return to ChibiAkumas.com Choose a Color Scheme: Dark
Print Mode


Learn Multi platform Z80 Assembly Programming... With Vampires!

Contents
Introduction
The Z80
Numbers in assembly

Lesson 1 - Getting started with winape
Lesson 2 - Memory copy, Symbol definitions, Loops and Conditional Jumps!
Lesson 3 - 'Case Statement' , 8 bit basic Maths, Writing to Ram and Reading from basic
Lesson 4 - Stack, Strings,Compiler Directives, Indirect registers, CPC Call

Appendix
Links & Resources for download

Introduction
Welcome to my Assembly programming tutorials, These will be split into parts, the first will teach you the bare basics of assembly language, then we'll jump into some simple programs, once you've learned the basics we're going to jump straight into real game development!

Using my 'Chibi Akumas' (Little Monsters) Multi platform shooter game as a foundation... I'll teach you how to  tweak, enhance, or completely rewrite my game into something of your own, with as much fun and as little theory as possible! As my game engine is open source, once you're feeling ready to go it alone, you can take as much or as little from my game to make your own as you want!

At first we'll begin learning on the Amstrad CPC, as soon as we've covered the basics and moved to the full 'game engine' you'll be able to develop for either the CPC, the MSX or the ZX spectrum as you prefer!

If you want to learn Z80 get the Cheatsheet! it has all the Z80 commands, and useful info on the CPC, Spectrum and MSX!
It will give you a quick reference when you're stuck or confused - and it's what the author used to develop ChibiAkumas!
Print it in color at high resolution on 2 sides of A4 for maximum performance!
The next few chapters are quite technical and confusing, but if you want, just skip them for now, and jump straight into the coding!
This tutorial is designed so you can do that!
You'll need to know the technical stuff explained below one day, but you can come back to it later when you feel you want to!


Feel the power... of the Z80!
The Z80 is a 4mhz 8 bit processor from the 1980's... now by modern standards, it's slow and ridiculously out of date, so why would you want to learn to develop for it?
Well, you can learn a lot about modern computer concepts from the simple Z80, and 4mhz is 4 million commands a second! Which is a heck of a lot when you know how to use them!  
These old 8 bits give a simple system with a lot of potential for the creative person... and while one person could never create a game up to the standards of the latest 'AAA' titles... you could very easily create a game that's as good or better than the best games of the 80's!


Whether you're a fan of the CPC, MSX or Spectrum, these 8 bits have all the power and potential for you to show what you can really do - and you'll learn things doing assembly that you would miss out on with years of C++ or Java development!

If you want to make a game with the latest graphics, of course go download Unity... but if you really want to be in control, and to understand everything that's happening in your code, Assembly gives you the power! No operating system, no drivers, you can take control of everything and make anything you want with it!

Assembly development can be confusing at first... but it has very few commands to learn, Everyone has to start simply, so try not to compare what you're doing to others... just look at what you're achieving, and knowing however 'simple' what you're doing is... it's something you made yourself!

What is the Z80 and what are 8 'bits'
The Z80 is an 8-Bit processor with a 16 bit Data bus!
What's 8 bit... well, one 'Bit' can be 1 or 0
four bits make a Nibble (0-15)
two nibbles (8 bits) make a byte (0-255)
two bytes (16 bits) make a word (0-65535)

And what is 65535? well that's 64 kilobytes ... in computers Kilo is 1024, because four bytes is 1024 bytes
64 kilobytes is the amount of memory a basic 8-bit system can access

Z80 is 8 bit so it's best at numbers less than 256... it can do numbers up to 65535 too more slowly... and really big numbers will be much harder to do! - we can design our game round small numbers so these limits aren't a problem.

You probably think 64 kilobytes doesn't sound much when a small game now takes 8 gigabytes, but that's 'cos modern games are sloppy, inefficient,  fat and lazy - like the basement dwelling losers who wrote them!!!
Z80 code is small, fast, and super efficient - with ASM you can do things in 1k that will amaze you!

Numbers in Assembly can be represented in different ways.
A 'Nibble' (half a byte) can be represented as Binary (0000-1111) , Decimal (0-15) or  Hexadecimal (0-F)... unfortunately, you'll need to learn all three for programming!

Also a letter can be a number... Capital 'A'  is stored in the computer as number 65!

Think of Hexadecimal as being the number system invented by someone wit h 15 fingers, ABCDEF are just numbers above 9!
Decimal is just the same, it only has 1 and 0.

In this guide, Binary will shown with a % symbol... eg %11001100 ... hexadecimal will be shown with & eg.. &FF.

Assemblers will use a symbol to denote a hexadecimal number, some use $FF or #FF or even 0x, but this guide uses & - as this is how hexadecimal is represented in CPC basic
All the code in this tutorial is designed for compiling with WinApe's assembler - if you're using something else you may need to change a few things!
But remember, whatever compiler you use, while the text based source code may need to be slightly different, the compiled "BYTES' will be the same!
Decimal 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ... 255
Binary 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111   11111111
Hexadecimal 0 1 2 3 4 5 6 7 8 9 A B C D E F   FF

Another way to think of binary is think what each digit is 'Worth' ... each digit in a number has it's own value... lets take a look at %11001100 in detail and add up it's total

Bit position 7 6 5 4 3 2 1 0
Digit Value (D) 128 64 32 16 8 4 2 1
Our number (N) 1 1 0 0 1 1 0 0
D x N 128 64 0 0 8 4 0 0
128+64+8+4= 204            So %11001100 = 204 !

If a binary number is small, it may be shown as %11 ... this is the same as %00000011
Also notice in the chart above, each bit has a number, the bit on the far right is no 0, and the far left is 7... don't worry about it now, but you will need it one day!

If you ever get confused, look at Windows Calculator, Switch to 'Programmer Mode' and  it has binary and Hexadecimal view, so you can change numbers from one form to another!
If you're an Excel fan, Look up the functions DEC2BIN and DEC2HEX... Excel has all the commands to you need to convert one thing to the other!

But wait! I said a Byte could go from 0-255 before, well what happens if you add 1 to 255? Well it overflows, and goes back to 0!...  The same happens if we add 2 to 254... if we add 2 to 255, we will end up with 1
this is actually usefull, as if we want to subtract a number, we can use this to work out what number to add to get the effect we want

Negative number -1 -2 -3 -5 -10 -20 -50 -254 -255
Equivalent Byte value 255 254 253 251 246 236 206 2 1
Equivalent Hex Byte Value FF FE FD FB F6 EC CE 2 1

All these number types can be confusing, but don't worry! Your Assembler will do the work for you!
You can type %11111111 ,  &FF , 255  or  -1  ... but the assembler knows these are all the same thing! Type whatever you prefer in your ode and the assembler will work out what that means and put the right data in the compiled code!

If you need more help, check out 'Convert.bas' on the tools.dsk in the sources file... this is a Hex/Bin calculator that's designed to help you see how binary and hexidecimal compare to decimal - it should help you understand how Hex & Bin work!

Working with numbers in the Z80
We know what numbers we can have on the Z80, but how do we work with them?
When we need to save our results, we will store them to that 64k of memory mentioned earlier, but the memory is some distance away from the processor... to be fast we want to use the memory built in to the processor.
This z80 memory is called the 'Registers'... there's not much of it, but its really fast - so try to use them to do as much as you can! Each Register can only store one byte (0-255)... but some registers can be paired ... so HL together are 16 bits, and can strore 0-65535!

  Normal Registers Shadow Registers
Accumulator
A A'
HighLow Memory Location H L H L
ByteCount B C B C
Destinaton D E D E
Indirect X IXH IXL

Indirect Y IYH IYL

         
Program Counter   PC    
Stack Pointer   SP    
Refresh   R    
Interrupt point   I    

Each of the registers has a 'purpose' it is intended for... Of course you can use any register for anything you want! but they all have 'strengths' because many commands will only work with certain ones... and some commands may be slower or need more code if you use the wrong one!

A This is used during all our main calculatons - you'll use it all the time when adding up (Accumulation)!
HL This often stores memory locations, as there are a lot of special commands that use it to quickly read or write whatever memory locations.. it's also good at 16 bit maths, so if you want to add two numbers above 255, you'll probably need it!
BC These are often used as a bytecount or loop counter... sometimes you'll use B and C together, for a count of up to 65535, or just B on its own for up to 255...
DE Destination - if you're reading from one place and writing to another, you'll probably use HL as the source, and DE as the destination
IX Sometimes we want to get to memory by specifying a relative position - Indirect Registers allow us to do this ... for example if we have sprites, and each 4 bytes for X,Y,Width,Height.. just point IX to the start of the data for the sprite we want - and read the rest out as IX+1 , IX+2 etc...
Don't worry about this - we'll explain it later... IX is actually a pair of two registers  called IXH and IXL - we can use them alone for whatever we want - but they are slower!
IY IY works the same as IX
   
PC This is the place in memory that the Z80 is running - we dont' change this directly
SP This is the stack pointer - it's points to a big temporary store that we'll use for 'backing up' values we need to remember for a short while
R This is the Refresh register - the system uses it to know when to refresh the memory... don't change it ! you could mess something up - but it can be used for getting simple 'random' numbers!
I This is the Interrupt point... on the CPC and MSX it's pretty useless so you can use it as a 'temporary' store... but on the spectrum it's really important  and you'll have to leave it alone!


The shadow registers are a 'copy' of the main ones... on the CPC the system firmware uses these during 'Interrupts' so you can't have them!
But if we stop the interrupts then we can use them for even more power!
Interrupts do things like read the keyboard and update the system - but we canreplace the firmware and  take over these jobs ourselves and take total control of the Z80 so we have all the power!



Lesson 1 - Getting started with Winape!
For our Z80 development we'll be using the WINAPE Amstrad CPC Emulator - it's free and does everything you need for development in one place, so I highly recommend you use it.
Winape works great in Windows XP - so you can run it in a virtual machine if you are a Linux or Mac fan, and I believe it works with WINE too.

There's a video of this lesson,  just click the icon to the right to watch it ->

You may want to develop for the ZX spectrum or MSX, but you should start with 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 you dont't use widows, Winape works OK with WINE!


To get started, Download Winape from this Link (I'm using V2.0b2)  and extract it to a folder,
Start Winape by clicking on the icon
You should see the emulator window open and show the amstrad CPC blue screen... the CPC starts straight away into Basic... and Amstrad's basic is great for testing ASM code!

Don't worry if you've never used a CPC before, this tutorial assumes you know nothing about basic or the CPC!

Now, Let's get straight into it and code something!

Click on the Assembler menu and select "Show Assembler" - or press F3 on your keyboard!
Select the File Menu, then New to create an empty ASM program
Type in all the lines shown to the right - Don't worry about what they do, We'll look at that in a bit!

Also, don't worry about the case as winape isn't case sensitive.... ORG &8000 is the same as org &8000
Select Assemble from the Assemble menu to compile the program!
You will see the Assembler output.  This shows the actual bytes of code that what you typed ends up as!

Check there are Zero Errors ... if there are then you've made a mistake typing!

Click on OK!

Now this is the magic of Winape - your code is now immediately in the emulators memory - so lets go run it!
Now go back to the blue screen... and type in Call &8000 and hit your enter key

Basic should return the message Ready

if something else happened, check your code matches the screenshot above!

What did it do? well we'll look at that in more detail next!
Congratulations! You just wrote and ran your first assembly program - and you now officially Kick Ass!
Now we'll look in detail at what the program did, and how to use Winapes debugger to look at it in detail!

Now you've had a a quick go at assembly, lets look in detail at what that program does! We'll look at each line of the program, and explain what it means.
 
org &8000 ORG sets the ORIGIN of the code in memory - the Z80 has a 16 bit address bus, so memory goes from &0000-&FFFF - but we can't just use anywhere, as some bits are used by other things - for example &C000-&FFFF on the CPC is used by the screen.
&8000 is a good 'safe area' on the Amstrad for your code to start
ld a,4 load A (the accumulator) with the number 4 - The Accumulator A is the main register used for calculations - registers are used for very short term memory storage
inc a INCrease A by 1... A will now contain 5
inc a INCrease A by 1... hopefully you can guess it now contains 6
ld b,10 Set B to 10 ... B is another register... it can't do as much as A, but it can do a lot... notice the destination is on the left, and the source (the number) is on the right... this is always the case with Z80 assembly
add b this adds B to A ... although A isn't mentioned in the command, since the Accumulator A is used for almost all maths any time a destination isn't mentioned, it will be A
this command could be written as ADD A,B... winape will compile this, but the "A," is superfluous, so you're better off learning not to need it
LD (&7000),A When Brackets () are used in assembly they define a MEMORY location... &7000 is the a number (&7000) is the content of that memory point!
So this command puts A into memory location &7000!
RET RET returns back to whatever called the program... in this case basic!
 
Ok, we've read over the code, lets use the debugger to see the Z80 run the code!

If you still have your assembler open, just click on its window
If you closed it, just click  Show Assembler from the Assembler menu
Click on the Grey Area next to the command "LD A,4"

A red blob will appear! This means before the Z80 runs the command "LD A,4" it will stop, and the Debugger will appear!
Select Assemble from the Assemble menu to recompile the program!
Type Call &8000 again into basic, and hit Enter
If you've done everything right, the debugger will immediately pop up! Lets take a look at what it offers!

At the top of the screen you can see the Compiled code... the line the Z80 is running is highlighted.

On the Right you can see all the Registers - you should recognize the names! remember our code uses A and B... A is the first half of AF and B is the first half of BC

In the middle of the screen you can see the Memory

At the bottom of the screen you can see the Rom and other debugger options... don't worry about them now!

In the bottom right is the Stack... we'll cover that very soon!
The Debugger has tons of options we're not going to use right now, but feel free to try them later! Try right clicking on things for more options!

You can even change the registers and memory by typing new values in! and remember - The worst you can do is crash your emulator, so there's no real harm you can do - so don't be afraid to mess!

If you crash your emulator, the memory will be erased, so you'll have to re-assemble your code from the Assembler before Call &8000 will work again!
Now Lets look at what your code does!

Press the F7 key on your keyboard (or the single step button on the main window)

Keep an eye on the A and B registers in the debugger!

You should see A change to 4... press F7 a few more times and watch what happens
keep pressing F7 again and again until you get to the RET command!

You can press it even more if you want, but remember... the debugger isn't just debugging your code, but the whole CPC... so you'll end up debugging the whole of basic! 

When your code has finished running we have one thing left to do....
Remember the program wrote to &7000 ?

Scroll through the memory browser to find &7000 - and you should see our final value for A  has been saved there!
You can open the debugger by selecting Pause from the Debug menu at any time,  or by pressing F7 - and just close it when you've seen enough and want your emulator to run normally!

Think back on what you've just done, You've written a program, compiled it, run it, and watched everything the program did at the CPU level in more detail than you've probably ever seen any computer work before! Not bad for a days work! - and this is just lesson 1!

Adding a few numbers may not seem much, but it's just the start, and you'll soon find you can make the computer do amazing thing!
Also, don't forget, once you understand the basics, you can use pieces of other people's code to do the work you don't want to! These tutorials will show you how to build on the open source code of the Chibi Akumas game to allow you to make big progress super-fast!

I highly recommend you type the programs in yourself, but you can download the source code with comments Here (Contains source for all lessons)



Lesson 2 - Memory copy, Symbol definitions, Loops and Conditional Jumps!
Now you've got Winape up and running, and had a go at programming, we can get on with learning some more commands!
We're still going to do simple things, but let's use the CPC's screen this time, so you can see the results of your code!

The CPC screen memory is at &C000 and takes up &4000 bytes!

Open up the assembler
     (Remember: Press F3 or select Show Assembler from the Assembler menu)
Create a new document
      (File... New)
Type in exactly what you see to the right!... there is a typo in this code  - so we're going to have to debug it

Once you've typed it in assemble it! 
     (Ctrl-F9 or Assemble from the Assemble menu)
Oh no! there's an error!... what a surprise!

Click OK to close the assembling window, and lets sort that error out!
The error that occurred will be show at the bottom of the screen... double click on it, and the cursor will jump to the error!

Whoops, we've put BD ... theres no such pair! correct the error to BC

Now reassemble the program, and you should get no errors!
Type Call &8000 and hit Enter

The screen will clear, and the top line or two will have some weird junk on it!


You probably think this is weird, but when we look at the code, This is exactly what we'd expect to happen... so lets break it down line by line!

Our code is at &8000 - that's where we call to run it org &8000
Set HL to &0000 - in this case HL is a source memory address ld hl,&0000
set DE to &C000 - in this case DE is a Destination, and &C000 is the start of the CPC screen ld de,&C000
set BC to &4000 - in this case BC is a byte count - and &4000 is 16k, the size of the CPC screen! ld bc,&4000
LDIR means " Load, Increment Repeat"... in effect it copies BC bytes from memory pos HL to DE
ldir
return to basic ret

So we've copied 16k from &0000 to the screen - and what is at &0000 - well some system junk, and any basic progs!
don't believe me?... type:
    10 print "moo"
    Call &8000
and you'll see some more junk appeared! that new junk is your program as it appears in memory!
Of course if you set DE to &0000 and HL to &C000 - you'd copy the screen to your system area - and your machine will hang!
it will also hang if you set BC to &5000... why? well when the destination gets to &FFFF it rolls over to &0000 - again overwriting your system area - but give it a go if you want, there's no harm crashing an emulated machine!

By default the CPC screen starts with &C000 at the 'top' but if you make basic scroll up and down, the 'Top' will move somewhere else, if you press the down cursor until the screen scrolls - then call &8000 again you'll see the junk appear somewhere else
You can always reset the cpc by typing "Mode 1" in basic (reset to screen mode 1)

If your crazy enough to try this on a different system, you'd need to make a few changes, The ZX Spectrum screen starts at &4000 not &C000, and the size is &1800
On the MSX the screen isn't in memory so you can't see the effect on screen - and you'll crash the system if you write to &F000 or higher, but you could set the size to &1000, and see the change in memory between &C000-&D000 with a debugger if you want!

Ok, you've had a quick look at LDIR,
Now lets try another more useful example,

Type in the example code to the right, and assemble it as before
Assuming you got no errors, lets run the program.

The ORG was different this time, so type Call &8100

The screen will clear!

What's interesting is that this simple ASM code is faster at clearing the screen than the firmware's own CLS command!... and there's a lot of tricks we can do to speed it up even more... but we'll leave them for another day!

What did that code do? well lets break it down!

This line defines a 'symbol' ... we're telling the assembler 'ScreenSize' EQUals &4000
A symbol is a constant in your code - basically every time the assembler sees 'ScreenSize' it will put in &4000
there's a couple of reasons to do this, firstly it's makes your code easier to read, and secondly, if you need to change it to &2000 - then you can just change this definition in one place, rather than all the places you've used it!
ScreenSize equ &4000
set the origin to &8100 (I'm using a different one, as all these examples are in the same 'lesson2.asm' in the download file) org &8100
Set the source HL to the start of the screen memory... yes, you read that right, the SOURCE ld hl,&C000
set the destination DE to one byte after the source... note that the assembler calculates what &C000+1 actually equals - not the Z80 ld de,&C000+1
set the Byte count to our defined symbol -1 (this will compile to ld bc,&3FFF) ld bc,ScreenSize-1
set the first byte of the source to 0 ld (hl),0
run our LDIR copy... now here's the trick...
at first LDIR copies the byte from HL (&C000 we just set to 0) to DE (&C001)
next  LDIR copies the byte from HL (now &C001 which IT just set to 0) to DE (now &C002)
after that  LDIR copies the byte from HL (now &C002 which IT just set to 0) to DE (now &C003)... and so on!
We tricked LDIR into copying it's own data, and acting as a quick(ish) FILL command!
ldir
return to basic ret

Mathematics in ASM code in Winape code is pretty dumb... it can't do brackets like 2*(5+1)... and it doesn't do multiplication first.... Usually you'd expect 5+1*2 to be equal 7... but in winape this equals 12!
Why? well winape does each command from left to right, so 5+1=6... then 6*2=12...it takes some getting used to, but it does work fine!

We have one last version of this program to try!

Type in the code to the right, and compile it... you should get no errors.

There's some new commands in here, but take a look at them in a minute!
Type Call &8200 and hit enter
The screen will do a strange 'fading clear'... Weird huh?

Ok, it's not very useful, but it teaches us a lot of good stuff!

Lets take a look at the code, and see why it does what it does!

Define our constant symbol "ScreenSize" as &4000 ScreenSize equ &4000
Start our program at &8200 org &8200
Set A to %00001111 - this sets all 4 pixels of a screen byte to cyan on the cpc screen ld a,%00001111
This is the definition of a label...
it's like a the constant "screensize" in that the assembler converts future mentions of 'FillAgain' to a number when it compiles... however unlike 'screensize' that was defined with 'equ'...  a label points to a memory location in the compiled code... if you take a look at the assembled code you'll see it is in the position &8202... that's because 'ld a,%00001111' takes 2 bytes
FillAgain:
set HL to the start of the screen ld hl,&C000
set DE to one byte later (the same as last time) ld de,&C000+1
set BC to ScreenSize -1 ... you should recognize all this from last time! ld bc,ScreenSize-1
load the first byte with A - remember it will set  all the pixels Cyan ld (hl),a
Just like last time LDIR will fill the whole screen ldir
Decrease A by 1 dec a
Compare A with 255... when A=0 and we do DEC A... A will become 255... cp 255
if A does not match 255 , jump back to label 'FillAgain'
The next command the Z80 will run will be 'ld hl,&C000'
jp nz, FillAgain
Now you know how to do a loop! in fact JP Z and JP NZ can be used like LOOPs, IF statements and even CASE statements!
Actually - you don't have a lot of choice as assembly has so few commands - but remember... all other programming languages compile down to Assembly - so anything Basic or C can do is possible in ASM - and ASM will always be fastest if you do it right!


So we've used a Label, and a jump (JP) to create a loop!
there are three kinds of Jump command that you should know!

Command Meaning Example
JP ## This is like a GOTO command in basic,  it will just jump to the label or memory address you specify every time. 
this command takes 3 bytes
JP &4000
JP c,## This is like an 'IF X THEN GOTO' command in basic. if condition is met the jump will occur, otherwise it will continue
otherwise program execution will continue... there is no such thing as an ELSE, but you can always immediately do another jump!
this command takes 3 bytes
JP Z,&4000
JR # do a Jump Relative to the current location...
This is a bit tricky, but all you need to know is it takes 1 byte less than JP, but can only jump nearby - so it saves memory, but you can't always use it!
JR label
JR c,# Same as above, a 2 byte relative jump - saves one byte over JP c,## - but can't always be used if you need to jump far away JR Z,label
djnz # This is a special  'quick small loop' command... it automatically Decreases B and Jumps if b is NotZero
DJNZ only takes up 2 bytes, so it's small if you only need a basic loop.
a jr jump can only jump to a label that is nearby - so if you get an error use this alternative which can jump anywhere
     dec b
     jp nz,label
DJNZ label
JP (HL) Jump to memory location in HL, this is quite advanced, so don't worry about it now... but you can load HL with a label, then use this to perform a jump if you need to JP (HL)


So those are the Jumps we have available, but some of them need a condition too!...
we have 4 main conditions we need to so lets take a look at an example - you don't need to type it in

load the accumulator with 4       LD a,4       
Compare A to 10 CP 10

There are 4 basic conditions we can use in this situation - it's annoying, but what the condition  officially means, and what it does in this case are different

Flag example Official meaning *** Basic program equivalent What it means when using CP
Carry JP C,label Carry  is used with bitshifts and addition - if a byte goes over 255, it will go back to zero, but Carry will be True A<CP if A < CP Value then C is true and JP C,label  will make the jump to label
NoCarry JP NC,label NC is true when there is no carry A>=CP if A > CP Value then NC is true and JP NC,label  will make the jump to label
Zero JP Z,label Z is true when the last mathematical operation resulted in zero A=CP if A=CP then Z is true and JP Z,label  will make the jump to label
NonZero JP NZ,label NZ is true when the last mathematical operation did not result in zero A<>CP or A!=CP if A<>CP then NZ is true and JP NZ,label  will make the jump to label
*** Note: while CP always works, not all mathematical commands affect all flags - see the cheatsheet for full details! - eg "inc hl" does not set the zero flag!

NC NC Z and NZ don't make a lot of sense for < > = and <> ... and they're a real pain to remember!
Z and NZ are = and <> ... so try to think of them as 'is the difference Zero - or Non Zero?'
C and NC are < and >= ... so think of them as Chibi (smaller) and NonChibi (bigger or equal)
They're on the Cheatsheet, but if you can come up with a way of remembering which is which - all the better!

Well done! You finished Lesson 2! But there's no reason to finish with these programs if you don't want to!
Try changing some of the numbers!
What happens if you change 'ld a,%00001111' to 'ld a,%11110000' or 'ld a,%11111111',
or change 'ScreenSize equ &4000' to 'ScreenSize equ &2000'...
Have some fun! and try lots of things!...
There's a video available describing more about how CPC & Spectrum screen memory work if you want to learn more now!



Lesson 3 - 'Case Statement' , 8 bit basic Maths, Writing to Ram and Reading from basic
We used a Jump to effect a loop last time, but sometimes we'll need to jump to different places depending on a value.

Also, lets take a look at how to do some everyday maths in 8 bit... and finally, we'll use a simple basic program to act as a 'frontend' to our assembly

Lets get straight into coding!

We're going to write a little Assembly calculator

Create a new assembly document, and type in the program to the right.

Compile it - you should get no errors!

You'll notice that the program takes it's input and output from 4 memory locations:

&9000 Command Num
&9001 Var 1
&9002 Var 2
&9003 Result

We're going to access these from a basic program - which we'll write now!
Type the program in to the right in basic

if you're not familiar with Basic, Don't worry about what the commands do, we'll look at them in a moment

ADD and SUB in assembly can add or subtract up to 255 from a register, but if you only need to add or subtract one, use INC or DEC, they increase or decrease a register by 1...
INC and DEC commands take only 1 byte, so they're faster than ADD and SUB... and they work on 16 bit registers like HL
ADD and SUB only work on the 8 Bit Accumulator, but we'll learn how to do 16 bit equivalents later!

CPC basic is really easy, just start typing the lines in after the emulator starts.
If you make a mistake it's easy to fix- for example to edit line 30, just type "Edit 30" and hit enter!
CPC basic also has a strange 'copy' command, which allows you to 'copy' text already onscreen... just hold down shift and use cursor keys to create a 'shadow cursor' and use Alt to copy the letters under the shadow cursor... on the CPC the 'Alt' key was marked 'Copy'

Type RUN to start the program and hit enter.

Enter the values for the Variables as 20 and 5, hit enter after each value

When asked if you want to Add or Subtract, Enter 1 for subtract

The result will be show onscreen!
Feel free to try other values in the program, but it only uses 8 bit registers, so it can only do up to 255, and can't do negative values right!
Right now it'll only do Addition and Subtraction because there aren't any built in Multiply or Divide commands on the Z80 - we're going to work around that next!

Lets take a look at the ASM code!

program starts at &8000 org &8000
Read memory position &9000 into A ld a,(&9000)
read two bytes into 16 bit register BC... on the Z80, 16 bit registers are stored in memory in reverse
So C is loaded from &9001 and B is loaded from &9002
ld bc,(&9001)
We want to do a 'Case statement' where we do different commands depending on A, but no such command exists!
No problem! we just do is lots of CP x commands and JR Z,label commands
Remember, CP # always compares with A... and JR Z will do a jump if A matches with the compared value #
cp 0
Command 0 is add, so if this is what the user selected jump to the MathAdd label jr z,MathAdd
Compare A to 1 cp 1
Command1 is subtract, so if this is what the user selected jump to the MathSub label jr z,MathSub
if we got here, then A was something weird, so set A to 0 ld a,0
This is our SaveResult label, if a command was run then it will finish here, if the user put a strange number in, execution will also end up here SaveResult:
Load the result (in A) to memory point &9003 ld (&9003),a
return to basic ret
   
The start of out subtract routine MathSub:
we need our result in A, so load from C (Val1) into it ld a,c
Subtract B (Val2) from A sub b
we've finished, so Jump to our saveresult label jr SaveResult
   
The start of our addition routine MathAdd:
we need our result in A, so load from C (Val1) into it ld a,c
Add B (Val2) to A add b
we've finished, so Jump to our saveresult label jr SaveResult
Sometimes in ASM there's a smaller, faster command that  has the exact same result as another!
For example, you can use "OR A" instead of "CP 0"... and "XOR A" instead of "LD A,0"
The result is identical, but you'll save some speed and memory!... it'll just look a bit odd in your code!
It's something you'll want to learn to use.. so why not give it a try now!

In case you're not familiar with CPC basic, lets take a look at the basic code!

Clear the screen CLS
INPUT asks the user a question, and stores the users response in a variable
So this will show "Val1?" onscreen, and store input from user into a
     Note: a/b/c in basic is nothing to do with A/B/C in ASM!
INPUT "Val1";a
Show "Val2?" onscreen, and store input from user into b INPUT "Val2";b
ask the user what command to run INPUT "0=Add,1=Sub";c
POKE writes a byte into memory, this writes our command number into memory point &9000... this is how we'll get our values from basic to ASM POKE (&9000),c
Store Value from A into &9001 POKE (&9001),a
Store Value from B into &9002 POKE (&9002),b
Call our ASM program CALL &8000
PEEK reads a byte from memory, allowing us to get the result our ASM program produced.
PRINT just shows it onscreen
PRINT PEEK(&9003)

Using basic to 'launch' and test your ASM code is a great way to develop quickly and ease testing
Using PEEK and POKE to get data to and from your program is a good solution, but you can also pass variables using the CALL command, but it's a little tricky, so we'll cover it later!

Multiply and Divide

We want to add Multiply and Divide commands, but unfotunately the Z80 does not have these commands! but we can simulate them by repeatedly using the ADD or SUBtract commands!

Add These Lines to the bottom of your code below MathAdd
Add These Lines to 'case' condition block

use EDIT 30 to edit the line showing the options, make it the same as This
Run the program!
You'll now be able to do multiplication, but only if the result is less than 255! You'll also see that negative numbers don't work!

If you try a number that ends up too high, or below zero, you'll get a strange number, that's because the numbers 'roll around' from zero back to 255

Later we'll upgrade the program to use 16 bit numbers, so we can go from -32768 to 32767!

Take a look at the Hexadecimal tutorial at the start of this document if you want to know more about negative numbers now!

You can get the sourcecode for this lesson (and all the others) in the sources.7z file... the basic code can be found on the included disk image!

Repeately adding or subtracting a number to 'fake' Multiplication or Division is silly and slow, but if you only need to Double or Halve a number you can use bit shifts... we'll cover them soon!
You want to try avoid needing Multiplication and division if you can in your code, so design your game not to need anything except halving and doubling... of course you can do x4 or x8 by doubling twice or three times!




Lesson 4 - Stack, Strings,Compiler Directives, Indirect registers, CPC Call
That was a nice little program, but we still have some basics to cover!

You'll have noticed there's not many Registers, and you'll often wish you had more. So what can you do if you have a value you need later, but you need to do something else first?

We need some temporary storage, and we have something called the stack for that!

Lets suppose we have a value in A ... we need the value again later, but we need all our registers now too... what can we do? Well that's what the stack is for, it's a temporary store!
The stack is like a letter tray - we can put an extra sheet on the top of - and take it off later, but we can't get one from the middle, so we always get the Last one we put in - this is known as LIFO - Last in First Out
We use PUSH and POP to push a new item onto the stack, and pop it off later.

Lets look at two examples, and see why we want the stack!

Suppose we have a Call 'PrintChar' which will print the character in A ... and another call 'DoStuff' which will change all the registers - how can we keep A the same before and after this 'DoStuff' command?  Well, we could save A to some temp memory - or let the stack take the slack!

don't type this in, it won't actually work!

Without the stack
ld a,'1'
LD (Temp),a
call  PrintChar
call DoStuff
ld a,(Temp)
call  PrintChar
Temp: db 0
With the stack
ld a,'1'
push af
   call PrintChar
   call DoStuff
pop af
call Printchar

Both these do the same thing, but  commands like LD (temp),a takes 3 bytes, and PUSH AF takes only 1.. and it's faster! also you no longer need that Temp: db 0 ... so that's another byte saved!

the stack always works in 16 bit - so even if you only want to save B - you'll have to PUSH BC - but don't worry , it's so fast you won't mind!
note:  the F in AF is the 'Flags' (the Z NZ C NC in comparisons) - they're saved with A when you do a push.

You can push lots of different things onto the stack, but remember they will come out in the same order... you can even do  PUSH BC then POP DE to copy BC into DE
The order is important! if you're unclear on how the stack works, you can use the Debugger in winape to step through your program, and see what the stack does as your program works!
seeing things happen step by step in the Z80 is a great way to see how things are happening
The Stack

Lets do an example of the stack!

Type in the program to the right!
Type Call &8000 and hit enter.

The screen will 'Pause' so press A

You should see A|x|A onscreen

Run the program again, and press a different letter!

What was the point of that? well lets take a look at what the program does!  I've colored the PUSH, POP commands so you can see what POP gets the matching value that was PUSHED

This is a definition pointing at a command in the CPC firmware - it will print A to the screen as a character PrintChar equ &BB5A
This is a definition pointing at a command in the CPC firmware - it will wait until a key is pressed and save it in A WaitChar  equ &BB06
Start of the program at &8000 org &8000
Get a character from the user -this is why the screen paused - lets assume the user pressed A call WaitChar
Print the character to the screen call PrintChar
Push the character onto the stack - in this example 'A' push af
Load a bar symbol into A     ld a,'|'
Push the bar onto the stack for later     push af
Print the bar to the screen         call PrintChar
Load an 'X' into A         ld a,'x'
Print the X to the screen         call PrintChar
pop an item off the stack... we will get the Bar we just pushed     pop af
Print the bar onto the screen     call PrintChar
pop an item off the stack, we get the character the user entered - in this example  'A' pop af
print the character ('A') to the screen call PrintChar
Return to basic ret

So we can push items onto the stack, and get them back later so long as we need them in the same order!  But the stack doesn't just operate for us.. the Z80 uses it too
When we do a 'CALL label' command, the current running address is pushed onto the stack - effectively the Z80 does 'Push PC    JP Label'
When we do a 'Ret' command... the Z80 effectively does 'pop PC'
Pc is the program counter - the current address the z80 is running... now this Push PC and Pop PC command don't really exist, but that's what the Z80 does - and you need to know this so you know why this program won't work:
Command What happens
Ld a,'Z' Accumulator set to character 'Z'
push af AF pushed onto the stack
Call ShowIT the address of the next command (ret) is pushed onto the stack
Ret end of the program
ShowIT:
pop AF we wanted to get  'Z' back - but we actually got the address of the command after Callit
call PrintChar We wanted to print 'Z" but we actually printed half the address!
ret we wanted to return from the call - but we stole that off the stack - return jumps to the AF value we pushed - and the computer will crash!

If you don't understand the stack yet, try making some test programs, or editing the one you just typed in!
The stack's used so much you'll see plenty of examples - and you'll soon get used to it!

The stack is the fastest way to read and write memory on the Z80 - if you're clever you can use it in a 'tricky way' to quickly do things like fill the screen.

We'll learn how to do it later - it's an advanced trick, but if you want to make the Z80 as fast as possible - that's how to do it!

Compiler Directives

We'll move on from the stack - Lets have a look at another little program! - we're going to use the previous PrintChar command to make a string printing routine.

Type in the program to the right.

Assemble it - you should get no errors

We'll explain what each line does after we run it!
Use Call &8100 to run the program
It will print a little two-line message to the screen!
Chage the first line - put a Semicolon ; at the start of it - this marks it as a 'comment' - which means it does nothing in the code

Assemble it - you should get no errors
Run the program again - The message has changed!

Writing somthing as simple as a print string routine may seem a pain - and you're probably wondering why the firware can't do it for you,
But writing your own routines is the best idea - firstly - you'll know exactly what they do, and you can change them later to add special functions - and more importantly - you can port them to other systems and they will work the same!
The less you use the system firware the better! your Z80 code will work the same on a CPC / Spectrum or MSX - firmware calls will not!

That program probably seems rather long, but there's lots of good stuff in there! Lets take a look at it line by line!

Define a symbol called 'ThinkPositive' and set it to 1 ThinkPositive equ 1
Define PrintChar, and point it to the memory address in the Amstrad Firmware that Prints ascii character A to screen PrintChar equ &BB5A
Start of our program - the two lines above are instructions to the assembler, they do no compile to anything the Z80 sees org &8100
load the address of the Introduction string into HL ld hl,Introduction
call our PrintString function call PrintString
Call our NewLine function call NewLine
Load the address of the Message into HL ld hl,Message
call our PrintString function call PrintString
Return to basic ret
Start of out Printstring function PrintString:
Load a Byte (character) into A  from the address HL we were given ld a,(hl)
Was the byte 255? cp 255
If it was (the difference is Zero) then we're reached the end of the string - so return to the calling program ret z
increase the HL address counter inc hl
call our PrintChar routine to show the letter in A onscreen call PrintChar
Repeat the procedure. jr PrintString
A label defining the address of our introduction message Introduction:
a string of letters, ending with 255 - the assembler will convert these to their equivalent bytes according to their Ascii code. db 'Thought of the day...',255
remember that 'ThinkPositive' symbol? well we're telling the assembler that we only want to do the following if we've defined it! ifdef ThinkPositive
A label 'Message' and a message that will compile when the IF statement above is true. Message:    db 'Z80 is Awesome!',255
If 'ThinkPositive' is not defined (for example - when we put a semicolon in front of it) else
A label 'Message' and a message that will compile when the IF statement above is false - Note: Normally you can't have two labels with the same name, BUT because the IF statement means only one will compile there ARE NOT TWO in the final program Message:    db '6510 sucks!',255
End of the Assembler directive endif
NewLine:
A newline command ld a,13
Load Character 13 into A (Carriage return) call PrintChar
Print it ld a,10
Load Character 10 into A (New Line)  call PrintChar
Print it ret

Feel free to try other values in the program, It's important to note that the IFDEF is actually changing the Compiled code -the 'message' that is not shown DOES NOT EXIST in the compiled data!
This allows you to compile multiple versions of your program - Chibi Akumas uses this to compile different builds of the game for CPC, ZX spectrum and MSX - and for different languages - all with one code base!


The Indirect registers IX and IY

Suppose we have some bytes of data we want to read from a 'bank' of data - but we want to read those bytes by specifying an offset relative to the start address - we can use the Indirect register IX or IY to do this - lets look at an example

Type in the program to the right, and compile it.

It's pretty long, but The bottom part is identical to your previously entered one, so just copy and paste it, from your last example - or just add the new code to the bottom of your old one.

If it's too long, you can always download the sources file and just run it from there.
Run the program by typing Call &8200
It prints a message inside two kinds of brackets

Don't underestimate calls from basic! A great way to make your first game is to write the logic and input routines in basic, and call out to assembly for things like drawing sprites and music!
The important thing is the result you achieve, not the method - so why not make things easy for yourself and do some of the work in basic - you can always convert it to ASM later once you've worked out exactly what you need to do!

Lets look at the program and see how it works!

Start of the program org &8200
Load the Indirect register IX with the address of the square brackets ld ix,SquareBrackets
Load the address of 'Message' into HL ld hl,Message
load DE with the address of the Printstring function ld de,PrintString
Call the DoBrackets function - we'll take a look at it in a second call DoBrackets
Call the Newline command call NewLine
Load the Indirect register IX with the address of the curly brackets ld ix,CurlyBrackets
Load the address of 'Message' into HL ld hl,Message
load DE with the address of the Printstring function ld de,PrintString
Call the DoBrackets function - we'll take a look at it in a second call DoBrackets
return to basic ret
Start of the Dobrackets function DoBrackets:
Load A from the address in IX (plus zero - so just IX)  ld a,(ix+0)
Print character A call PrintChar
Run the function DoCallDE - we'll look at it in a moment. Call DoCallDE
Load A from the address IX plus 1 - note this does not change the value in IX ld a,(ix+1)
Print character A call PrintChar
ret
The function DoCallDE DoCallDE:
Push DE onto the stack push de
Return will take two bytes off the stack, and continue execution from that point - effectively we have done the command Call (DE) - the Z80 has no such command, but we have simulated it here ret

The Chibi Akumas game uses IX to point to the Player settings - the Player routine gets the Life - position - health etc of the player via IX+... references
This allows the same routine to handle both players just by changing the IX reference when the function is called - the first time it points to Player1's data - the second time Player2's


Call with parameters

That example of IX was rather useless, but the IX register has a far more useful function on the CPC - we can use it to get data from the Call statement! Lets take a look at an example!
Type in the example to the right - it's rather long - if it's too much trouble, remember all the examples are in the downloadable sources file.

The Printstring function is the same as before - so you can just copy and paste it.
Try typing Call &8300

Because you didn't give a parameter You will see a 'usage message'

Type Call &8300,12345

You will see '12345' converted to 16bit hexadecimal!

Feel free to try it with some other numbers!

We've created a Decimal to Hexadecimal converter! and it gets its data straight from basic!

Lets take a look at the code and see how it works
org &8300
When the program starts CPC basic will store the number of parameters passed in A cp 1
If the user did not pass 1 parameter, show how to use the program jp nz,ShowUsage
Print an & symbol onscreen ld a,'&'
call PrintChar
Numbers are passed as 16 bit integers... The first parameter location is passed by basic in IX - because the data is passed in little Endian it's backwards, so we load the larger part from IX+1 into A ld a,(ix+1)
if the high byte is not zero, call our ShowHex function or a
call nz,ShowHex
Load the smaller part from IX+0 (IX) int A ld a,(ix+0)
Call ShowHex call ShowHex
return to basic ret
Show the usage message to the user - as the last command is a JP there is no need for a return command. ShowUsage:
    ld hl,ShowUsageMessage
jp PrintString
Message string we show the user if they used the program wrong ShowUsageMessage:
    defb "Usage: Call &8300,[16 bit number]",255
ShowHex function - this shows an 8 bit byte in Hex ShowHex:
we want to Divide the number in A by 16 ld b,16
Call our Divide function call MathDiv
The remainder is returned in A - we need it later - so we Push it now push af
Load the result of the divide - this is how many 16's there were in the byte - so this is the first symbol in the hex string ld a,c
Print the hex string call PrintHexChar
get back A - this is now the second digit with a pop pop af
jump to the PrintHexChar - because we don't use CALL there is no need for a return jp PrintHexChar
This function prints a single hex digit 0-F PrintHexChar:
Compare A to 10 cp 10
if A is less than 10 we need to print a digit. jr c,PrintHexCharNotAtoF
Add 7 to A - this is the Ascii difference between 9 and A add 7
PrintHexCharNotAtoF:
Add 48 (0) to the digit - this converts A to an Ascii character add 48
Print it jp PrintChar
This mathDiv function is different from lastweeks, it stores the result in C and the remainder in B MathDiv:
Reset C - it will store the result ld c,0
ifA is zero then return cp 0
ret z
MathDivAgain:
Subtract B from A sub b
increase C (the result counter) inc c
Repeat if we've not gone below zero jp nc,MathDivAgain
we've gone over zero, so add b again, so A contains the correct remainder add b
we've gone over zero, so decrease C to get the correct result dec c
ret

Using IX is great - but it only works on the CPC - the MSX can pass one variable (see the basic documentation) but other systems cannot really do this - just use the POKE function in the previous example instead!
Other than CALL commands IX functions are great for settings data - you can use them for passing references to sets of data that you want to access and alter 'randomly'


Appendix
Command Alternate form Meaning Example Notes
ADC r ADC A,r Add register r and the carry to A ADC B  
ADC # ADC A,# Add number # and the carry to A ADC 2  
ADC HL,rr   Add 16 bit register rr and the carry to HL ADC HL,BC  
ADD r ADD A,r Adds register r to A ADD B  
ADD # ADD A,# Adds number # to A ADD 5 remember ADD 254 is like -2
ADD HL,rr   Add 16 bit register rr to HL ADD HL,BC remember ADD HL,65534 is like -2
AND r AND A,r bitwise AND register r with A (result in A) AND B  
AND # AND A,# bitwise AND number # with A (result in A) AND %11100000 if A=%00111000... AND %11100000 will result in %00100000
BIT b,r   get bit b from register r (0 is far right bit) BIT 7,a  
CALL ##   Call address ## Call &4000 this is like GOSUB.... Note: CALL pushes PC onto the stack
CALL c,##   Call address ## if condition C is true Call Z,&4000 this is a THEN GOSUB statemenr
CCF   Invert carry flag (compliment carry flag) CCF if you want to clear the carry flag, do OR A ... it will set carry flag to 0 - alternatively SCF, CCF will do the same but is slower
CP r   Compare A to register r CP A this is your IF statement
CP #   Compare A to number # CP 255 Use OR A instead of CP 0 - it has the same effect but is smaller and faster!
CPD   Compare and decrease... CP (HL), DEC HL,DEC BC, PO set if BC=0 CPD  
CPDR   Compare and decrease, repeat ... CP (HL), DEC HL,DEC BC, until BC=0 or found CPDR  
CPI   Compare and increase... CP (HL), INC  HL,DEC BC, PO set if BC=0 CPI  
CPIR   Compare and increase, repeat ... CP (HL), INC HL,DEC BC, until BC=0 or found CPIR  
CPL   invert all bits of A (ones ComPLiment) CPL turn %11001100 to %00110011
DAA   Special command for Binary coded Decimal, update A DAA  
DEC r   decrease value in register r by one DEC B  
DEC rr   decrease value in register rr by one DEC HL  
DI   Disable interrupts DI do this if you need to use shadow registers
djnz #   Decrease B and Jump if NonZero... only jr jump, so must be close.. +-128 bytes away DJNZ label this is how you do a loop
EI   Enable interrupts EI  
EX (SP),HL   Exchange the bytes at (SP) with the value in HL EX (SP),HL  
EX AF,AF'   Exchange A & Flags with the shadow register A' & Flags' EX AF,AF'  
EX DE,HL   Exchange DE with HL EX DE,HL HL can do more than DE, so you may want to quicly swap them
EXX   Exchange BC,DE,HL with the shadow registers  BC',DE',HL' EXX Use this if you need these register values  later, but want to do another job now
HALT   Wait until interrupt occurs HALT force music to play... DI, HALT will force emulator to stop permanently - good for debugging a particular point in the code on MSX or SPECTRUM where defining breakpoints is hard
IM 0   Interrupt mode 0    
IM 1   Interrupt mode 1   on the MSX or CPC this is the only one you need... on spectrum the firmware uses this mode.
on MSX... IM 1 does a CALL &0038 every screen refresh (50hz)
on CPC... IM 1 does a CALL &0038 6 times during screen refresh (300hz)
IM 2   Interrupt mode 2   the only usable interrupt mode for our game on the ZX SPECTRUM
not as easy to use as IM 1... you need to use all the memory between &8000-&8183 and the I register - but the result is the same as  IM1 on the CPC/MSX
IN A,(#)   read from port # into A IN A,(3) Note: the Z80 on the ZX SPECTRUM & CPC are wired strangely  - this function does not work right
IN R,(C)   read from port C into A in A,(C). Note: the Z80 on the ZX SPECTRUM & CPC are wired strangely - they actually do in (B)
IN (C)   read from port C... but do nothing with it in (C). Note: the Z80 on the ZX SPECTRUM & CPC are wired strangely  - they actually do IN (B)
INC r   increase r by one INC B  
INC rr   increase rr by one INC HL  
IND   In (HL),(C)...dec HL... dec B IND Note: the Z80 on the ZX SPECTRUM & CPC are wired strangely - they actually do in (B) which is also the loopcounter, so this will malfunction
INDR   In (HL),(C)...dec HL... dec B until B=0 INDR Note: the Z80 on the ZX SPECTRUM & CPC are wired strangely - they actually do in (B) which is also the loopcounter, so this will malfunction
INI   In (HL),(C).. Inc HL... Dec B INI Note: the Z80 on the ZX SPECTRUM & CPC are wired strangely - they actually do in (B) which is also the loopcounter, so this will malfunction
INIR   In (HL),(C).. Inc HL... Dec B until B=0   Note: the Z80 on the ZX SPECTRUM & CPC are wired strangely - they actually do in (B) which is also the loopcounter, so this will malfunction
JP (HL)   Jump to the address stored in HL JP (HL) if HL=&4000 then this would be the same as JP &4000
JP ##   Jump to ## JP &4000 JP is like GOTO
JP c,##   Jump to ## if condition c is true    this is like a THEN GOTO statement
JR #   jump to # must be +- 128 bytes from current location JR label JR is faster than JP when FALSE (jump didn't occur) but slower when TRUE (Jump occurs)
JR saves one byte over JP - which is a lot on an 8 bit!
JR c,#   jump to # if condition c is true
must be +- 128 bytes from current location
JR z,label JR is faster than JP when FALSE (jump didn't occur) but slower when TRUE (Jump occurs)
JR saves one byte over JP - which is a lot on an 8 bit!
LD (rr),A   Load memory address in (rr) with value in A LD (BC),A  
LD (##),A   Load A into memory address ## LD (&4000),A  
LD (##),rr   load 16 bit regisers rr  into memory ## and ##+1 LD (&4000),BC bytes are loaded into memory in reverse order - though this only matters if you're doing clever things!, eg if you do:
LD BC,&1122... LD (&4000),BC
&4000 = 22    &4001=11
LD (##),SP   Load the Stack Pointer into ## LD (&4000),SP back up the stack pointer - you may want to use an alternate stack for a while or misuse the stack pointer for fast bulk reading or writing.
LD A,(rr)   Load A from memory location (rr) LD A,(BC)  
LD A,(##)   Load A from memory location ##   LD A,(&4000)
LD A,I   Load A from the Interrupt register   The interrupt register only has a few commands, you'll need this to get data out of I
LD A,R   Load A from the Refresh register   R constantly changes - you can use it for random numbers!
LD rr,(##)   load 16 bitr registers rr from memory (##) ld BC,(&4000)  
LD I,A   Set the Interrupt register I to A   This is the only way to get data into I
LD R,A   set Refresh register to A   NEVER USE THIS - I'm told messing with the refresh register can damage your computers memory!
LD SP,(##)   Loadthe stack pointer from memory location ##   Restore the stack pointer.
LD SP,HL   Set the Stack Pointer to HL    
ld r1,r2   Load r2 into r1 LD B,C you can't do LD HL,BC
instead do LD H,B... LD L,C
LD r,#   Load r with value #   note, rather than doing LD A,0 do XOR A... it's faster but the result is the same!
LD r,(ir+-#)   Load r from intirect register ir (IX or IY) +-# bytes LD B,(IY+4) IF IY=&4000
LD B,(IY+4)  would do the same as LD B,(&4004)
LD rr,##   loads 16 bit number ## into rr LD BC,&4000  
LDD   Load (DE),(HL)... DEC HL... DEC DE... DEC BC LDD Copy a range backwards with no repeat - faster to do this 10 times than to use LDDR with B=10
LDDR   Load (DE),(HL)... DEC HL... DEC DE... DEC BC... Repeat until B=0 LDDR Copy a range backwards
LDI   Load (DE),(HL)... INC HL... INC DE... DEC BC   Copy a range with no repeat - faster to do this 10 times than to use LIDR with B=10
LDIR   Load (DE),(HL)... INC HL... INC DE... DEC BC... Repeat until B=0   Copy a range ... can also be used to fill a range by setting DE=HL+1
NEG   NEG   Negates A... Turns 5 into -5 (251)
NOP   Does nothing   Can be used as a placeholder for self modyfying code
OR r OR A,r Bitwise OR r with A OR B  
OR # OR A,# Bitwise OR # with A OR %11100000 if A=%00111000... OR %11100000 will result in %11111000
OTDR   OUT (C),(HL)... DEC HL... DEC B... repeat until B=0   Note: the Z80 on the ZX SPECTRUM & CPC are wired strangely - ZX & CPC actually do OUT (B),(HL)... DEC HL... DEC B.. note B is loop counter and port! this is dangerous!
OTIR   OUT (C),(HL)... INC HL... DEC B... repeat until B=0   Note: the Z80 on the ZX SPECTRUM & CPC are wired strangely - ZX & CPC actually do OUT (B),(HL)... INC HL... DEC B.. note B is loop counter and port! this is dangerous!
OUT (#),A   OUTput A to port # (Does not work on CPC/XZ.. see notes)   Note: the Z80 on the ZX SPECTRUM & CPC are wired strangely - this command does not work
OUT (C),r   OUTput r to port C (B on CPC/XZ.. see notes) OUT (C),C ZX & CPC actually do OUT (B),r... this allows port and val BC to be set in one go by LD BC,nn
OUT (C),0   OUTput 0 to port C (B on CPC/XZ.. see notes) OUT (C),0 Note: the Z80 on the ZX SPECTRUM & CPC are wired strangely - they actually do OUT (B),0
OUTD   OUT (C),(HL)... DEC HL... DEC B   ZX & CPC actually do OUT (B),(HL)... DEC HL... DEC B.. note B is loop counter and port!
OUTI   OUT (C),(HL)... INC HL... DEC B   ZX & CPC actually do OUT (B),(HL)... INC HL... DEC B.. note B is loop counter and port!
POP rr   Pops 2 bytes off the stack and puts them in rr POP DE Much faster than reading DE using LD DE,(####) or LD D,(HL).. INC HL, LD E,(HL)
PUSH rr   Push 2 bytes from rr into the stack PUSH DE Much faster than writing DE using LD (####),DE or LD (HL),D.. INC HL, LD (HL),E
RES r,b   Reset bit b of register r (sets bit to zero)... Bit 0 is far right RES B,0 using AND # would need loading register into accumulator - this does not
RET   Return RET return... Note: RET pops PC off the stack
RET f   Return if condition f is true RET Z  
RETI   Return from Interrupt RETI You'll probably never need this
RETN   Return from Non maskable interrupt RETN You'll probably never need this
RL r   Rotate bits in r left using carry bit RL B RL A is faster than RL B
RLC r   Rotate Left and Copy bit 7 to bit 0 (wrapping the bits) RLC B doing RLC B on  %10011000 results in %00110001
RLD   Rotate Left 4 bit nibble at Destination (HL) using bits 0-3 of A as a carry RLD  
RR r   Rotate bits in register r Right with carry RR B RR A is faster than RR B
RRC r   Rotate Right  and Copy bit 0 to bit 7 (wrapping the bits) RRC B doing RRC B on  %00001101 results in %10000110
RRD   Rotate Right 4 bit nibble at Destination (HL) using bits 0-3 of A as a carry    
RST 0   Call &0000 RST 0 Rst's are one byte calls, they can save memory! you cant reconfigure them on SPECTRUM
RST 1-5   Call &0008 (1),   &0010 (2),   &0018 (3),   &0020 (4),   &0028 (5) RST 3  
RST 6   Call &0030   CPC defines this as spare for user configuration
RST 7   Call &0038 RST 7 RST 7 is called by the Z80 when an interrupt occurs... put your own interrupt handler here to take over interrupts from the firmware!
SBC r SBC A,r SuBtract register r and the Carry from A SBC B  
SBC # SBC A,# SuBtract # and the Carry from A SBC 3  
SBC HL,rr   Subtract 16 bit register rr from HL with the carry SBC HL,DE Note there is no SUB HL,rr command - so clear the carry and use this if you need to, or use cpl to flip all the bits in each of the two regisers in rr, then do INC rr  then ADD HL,rr
SCF   Set the Carry Flag    
SET b,r   set Bit b to 1 in register r (note bit 0 is at the far right( Set A,0 using OR # would need loading register into accumulator - this does not
SLA r   Shift Left r and Alter bit 0 to 0 SLA B %01111101 becomes %11111010
SLL r   Shift Left and Load bit 0 with 1 SLL B %01111101 becomes %11111011
SRA r   Shift Right r and Alter bit 7 to same as previous bit 7 SRA B %01111101 becomes %00111110
SRL r   Shift Right and Load bit 7 with 0 SRL B %01111101 becomes %10111110
SUB r SUB A,r Subtract register r from A SUB B  
SUB # SUB A,# Subtract number # from A SUB 5  
XOR r XOR A,r XOR (invert bits) in X with register r XOR B XOR A does the same as LD A,0 ... but is smaller and faster!
XOR #   XOR (invert bits) in X with number # (when bit in # is 1) XOR %11110000 if A=%00111000 and you do XOR %11110000 the result is %11001000
Note, as the Accumulator does most mathmatical operations you can just enter ADD 4 ... but ADD A,4 has the same meaning... the shorter form will be used in my guide
Note, On the CPC and ZX Spectrum due to the way the Z80 is wired OUT commands do not work as stated... OUT (C) will actually do OUT (B).. making commands like OTIR  that use B as a counter likely to cause hardware problems (It caused random disk writes to me!)... OUT (#) will not work at all... OUT  works normally on he MSX  OUT (C) will do Out (C) and Out (#) works as stated


My Resources for download
Cheatsheet  - A new improved version of the cheatsheet I use for Z80 programming, you'll need to print at high resolution in color - it's intended to be printed on two sides of A4 paper or card and laminated
Sources - The source code with comments for all the lessons on here!

Z80 Links
Zilog Z80 manual - The official manual , it's compelx, but if you need a definitive answer you'll find it here so this should be in your toolkit
Learn ASM in 28 days - I learned from this tutorial, it's aimed at the TI-83 calc, but that uses a Z80... if you don't like my tutorial, try this one!
Down to the silicon - Not remotely needed for programming, but this amazing technical breakdown of how the Z80 works is really something

Main Z80 systems:

Amstrad Links
Winape  - Not just the easiest to use CPC emulator, but the easiest Z80 platform for beginner ASM programmers!
www.cantrell.org.uk
  - A great source of CPC and ASM info.. My cheatsheet ASM list is based on the one from cantrell.org.uk
CRTC - Details of the amstrad CPC CRTC hardware
Amstrad Firmware guide - Pdf documenting the CPC firmware calls
CPC Firmware Guide - Detailed info on how the CPC hardware and firmware
Basic Manual - You'll want to know at least enough basic to do calls and operate the computer
CpcWiki - Web community full of helpful people!

Spectrum Links
Fuse - My Spectrum emulator of choice!
Spectrum 128k and Spectrum 48K reference - Great summary of the hardware - provides much of the info you'll want for ZX dev
Basic Manual - You'll want to know at least enough basic to do calls and operate the computer
Spectrum Computing Forum - Web community full of helpful people!

MSX Links
OpenMSX - The best MSX emulator - you'll need to find your own roms for disk system emulaton (For disk images I reccomend emulating the hb-f1xdj)
Grauw.nl
- great resources and samplecode for MSX development
MSX2 Technical Handbook - full breakdown of the MSX2 hardware - all the info you will need for development purposes
Basic Manual - You'll want to know at least enough basic to do calls and operate the computer
MSX.Org - Web community full of helpful people!

Other interesting Z80 systems:

Enterprise Links
EP128 - Enterprise 128 Emulator
Ep128.hu - The only site I've found so far with english Enterprise128 manuals for download!
HTML Technical manual - detailed technical documentation that's easy to read
My Enterprise 128 Site - or just get my Enterprise 128 sources+notes

TI-83 Links
WabbitEmu - Fine Ti-83 emulator - you'll need your own rom files from somewhere!
Learn ASM in 28 days - I learned from this tutorial, it's a great guide that covers the Ti-83





Visit www.ChibiAkumas.com to get my games and their source code! | Support me on patreon
||