Tutorial: Hello, world! in Z80 Assembly Language

 



I'm feeling kind of nostalgic today so I thought I'd write Hello, world! in Z80 assembly for the ZX Spectrum! The last time I wrote any Z80 assembly was when I was 14 so around 36 years ago! I may be a little rusty!

Here it is:



    
org $8000
  ld bc, TEXT

LOOP
  ld a, (bc)
  cp 0
  jr z, EXIT
  rst $10
  inc bc
  jr LOOP



EXIT

  ret



TEXT

  defb "Hello, world!"
  defb 13, 0
    

How this works line by line:

org $8000 - this line puts the program into memory location $8000

ld bc,TEXT - ld stands for load. We load register bc with the memory location of what comes after the label TEXT

LOOP - the beginning of our printing loop. We will be printing each letter at a time.

ld a,(bc) - now we load register a with the content of register bc. As register a is a single register and can only take a single byte at a time, then the content of bc loaded into register a will be the letter H (the first letter in Hello, world!)

cp 0 - stands for compare 0. We check if register a = 0. It doesn't. It currently equals H.

jr z, EXIT - jr z stands for jump if zero. Therefore if cp 0  does equal 0 then jump to the EXIT label.

rst $10 - rst stands for restart, but it's easier to think of it as "call" as we are now going to call the inbuilt routine at memory location $10. This routine prints the content of register a to the screen. We will now have the letter H printed to the screen!

inc bc - inc means increase. Currently bc points to the address in memory where Hello World! is stored. At this moment in the loop it is pointing to the memory location for the letter H. We need it to point to the next letter, e, so we tell it to increase by one. Register bc will now point to the address for letter e.

jr LOOP - jump to LOOP. We now start the loop again only this time with the letter e about to be printed.

EXIT - the label to begin the routine that will stop the program.

ret - stands for return. Return will go back to the point where the routine was called. As no other routine called this routine, then return will end this routine completely and return to the BASIC interpreter.

TEXT - the label that stores the Hello, world! string.

defb "Hello World!" - defb stands for define bytes, and is simply for holding variables and stuff. In this instance it holds the string "Hello, world!"

defb 13,0 - This is the important line that will cause the printing LOOP to end. The loop will print all the characters of Hello, world! and then come to the number 13. This is a carriage return instruction. This instructs the print routine at $10 to move one line down the screen. You could have another defb after this with another message like "This is my first assembly routine" and the print loop would print that on the next line. However, in this example, we follow the carriage return with 0. The print loop runs into the zero and jumps straight to EXIT and closes the program.


To run the program you would first need to type it into a program called an assembler, assemble it and run it with (and this is where my memory gets a bit fuzzy!) the BASIC command RANDOMIZE USR 32768

Why 32768? That's decimal for $8000. RANDOMIZE USR stands for randomise user sub routine, which just means run the code at memory location 32768.

You will then see Hello World! on your screen. Something like this:



And there you have it. Of course it's easier in BASIC:



10 PRINT "Hello, world!"

but not half as fun!

edit: I made a slight error but luckily u/spectrumero from Reddit was on hand to fix it:

; add this at the top of the routine after org $8000

hello_world:
   ld a, 2               ; channel number
   call 0x1601           ; set output channel for rst 16
What this does: 

 ld a, 2 - loads register a with the value 2. 

call 0x1601 - this sets the output channel to 2. This means that the message will be displayed on the screen itself and not be overwritten by your command prompt (the flashing K) when the program returns.

Comments

Popular posts from this blog

La Isla Bonita

A Trip to Glastonbury