Home > Z80 Assembly > Tutorial: ZX Spectrum Machine Code Game in 30 Minutes!

Tutorial: ZX Spectrum Machine Code Game in 30 Minutes!

This tutorial by Jon Kingsman (bigjon) originally appeared in a thread on WoSF. Reproduced with permission.

Roadrace game by bigjon, incorporating suggestions from Dr Beep, skoolkid and Matt B

Hi folks, I’m a machine code novice who coded a very small roadrace game to help me learn.

I reckon you can learn the basics of machine code in half an hour by coding this game step by step.

This tutorial assumes you have a working knowledge of ZX Spectrum basic, and the ZX Spin emulator.

Make yourself a large cup of tea – by the time you’ve drunk it, you be able to program in machine code!

CHAPTER 1 – Create a machine code function that returns the score to BASIC
Machine code programs are a series of bytes in the Spectrum’s memory.
In this chapter we will

  • – Use the Spin assembler to write a few bytes into the memory generating a score for our game.
  • – Write a BASIC program to run the machine code and print the score we have obtained from it.

Open ZX Spin.  Select Tools -> Z80 Assembler.
To run our roadrace game, we need to execute the following steps:

MAIN 	;label for main section of program as opposed to graphics data etc	
	;arrange to put our machine code at free, fast-running (over 32768) and memorable address in RAM
	org 33000  
	;initialise score
	;initialise road, car
PRINCIPALLOOP ;label for the loop in the game that will execute over and over
	;read keyboard
	;set new carposition
	;crash? if so, go to GAMEOVER.
	;print car
	;scroll road
	;random road left or right
	;jump back to PRINCIPALLOOP
GAMEOVER ;label for the cleaning up that needs to be done before returning to BASIC
	;return score to BASIC

Copy and paste the paragraph above into the Spin Assembler.It will appear as 15 lines of mainly grey text.

The text is grey because text after a ; is a comment. The assembler ignores it but it’s there for our benefit.

You can TAB comments over towards the right-hand side of the assembler page to make your code more readable.

The labels are in pink.The assembler won’t put anything in RAM for them but will use them as entry points to jump to.

In the assembler, do File -> Save as and type something like mc30mintut.asm into the save box.
We’ll do the first and last of these steps in this chapter, starting with the last one.

The assembly language instruction for ‘return to calling program’ (in our case a BASIC routine) is ‘ret’.

Click on the end of line 15, press enter to create line 16 and type ret
The word ret‘ appears in blue. This is Spin’s colour code for an instruction.

When the Spin assembler gets to the instruction ret it writes the byte 201 into the memory at an address we choose.

The computer knows that the first byte it meets will be an instruction byte.

It does something different for each byte from 0 to 255. There’s a list in Appendix A of the Spectrum Manual.

Some instruction bytes, like 201 for ret, are complete as is – no further info is needed to complete the action.

Some instruction bytes need one or two bytes of data afterwards for the computer to know what to do.

Some other instruction bytes need a further instruction byte to clarify the action required.

Now we’ll initialise the score, ready for the mc program to report it back to BASIC at the end of the game.

The computer does most of its work using 7 temporary byte-sized addresses called registers.

The first register deals with single bytes only (numbers 0 to 255), the other six are in pairs to deal with 0 to 65535.

The first (single-byte) register is called the A register, sometimes also referred to as the accumulator.

The other three register pairs are called BC, DE, and HL (H is for High byte, L is for Low byte)

Any machine code function called from basic will return the value from 0 to 65535 in the BC register.

We will write the value 0 into the BC register, ready to increase it by 1 each time the game goes round its principal loop.

At the beginning of line 4, type ld bc,0. ld is the instruction for load a value into a register or register pair.

The instruction byte for ld is different for each register or register pair that is loaded.

The instruction byte for ld bc is 1. The computer then expects 2 data bytes to give it a number from 0 to 65535.

In our case the two data bytes will be 0,0.  So the assembler will write 1,0,0,201 at address 33000 in RAM.

We’ll assemble this code now. Do File -> Save, then File -> Assemble. Type 33000 into the Start Address box and click OK.

At the bottom window of the assembler you should see a report that says “No errors in 16 lines. 4 bytes generated”.

You can see the four bytes are now at memory address 33000 by clicking in the main Spin display window on Tools -> Debugger.

To run these four bytes of machine code, enter this one-line program in the main Spin display window:
10 PRINT AT 0,0; “Your score was “; USR 33000

Now RUN the program. Did you get “Your score was 0”? Congratulations – you have coded your first machine code program!

Do File -> Save in the main Spin display window and save as something like mc30mintut.sna. Here ends Chapter 1!

CHAPTER 2 – Display material on the screen.
There are two areas of the Spectrum’s memory which have a direct effect on the screen display.
The complicated way is the display file, from addresses 16384 to 22527, which stores a dash of 8 pixels per byte.

Try POKE-ing 255 into bytes within this range to see the funny order in which this memory area is mapped onto the screen.

The simple way is the attribute file, from 22528 to 23296, which affects an 8×8 pixel block per byte, in logical order.

In this chapter we will

  • Draw our ‘car’ by changing the paper colour of one character square to blue.
  • Draw our ‘road’ by using a loop to create two vertical stripes of black paper colour down the screen.

In the spin assembler line 5, delete the word ‘road’ in the comments.

At the beginning of line 5, type ld hl,23278. This points HL to the middle of the bottom row in the display file.

Insert line 6, ld a,8. This puts a blue PAPER colour into the A register. Why 8? See the BASIC manual chapter 16.

Insert line 7, ld (hl),a. The brackets round hl mean the load will be to the address in RAM that hl is pointing to.

Insert line 8, ld (32900),hl ;save car posn. We’ll store the attribute file address of the ‘car’ in some free bytes in RAM.

Now for the road. Insert line 4, ld hl,22537 ;initialise road. This points to a third of the way along the top line.

To save the road position, which we’ll need frequently, we’ll let the computer choose where to store it, on its ‘stack’.

Chapter 24 of the manual has a diagram showing where the machine stack is in the RAM.
To write to the stack we use push. To write from the stack we use pop. What goes on the stack first will come off last.

Insert line 5, push hl ;save road posn. Insert line 21, pop hl ;empty stack

To print a black road we need 0 in the accumulator (ch16 of the BASIC manual).

We could do ld a, 0 but this takes 2 bytes whereas xor a takes only one. Insert line 6, xor a.
xor compares the chosen register to the A register and puts a 1 in the A register for each bit that is different.

We’ll print the top line of the road. Two double squares of black with a 9-square gap between them.

Insert line 7, then copy and paste the following code:

	ld (hl),a
	inc hl ;inc increases the register by one, dec decreases it by one.
	ld (hl),a
	ld de,9 ;for a 9-space gap in the road.
	add hl,de ;add adds the registers together, so hl points to the right hand side of the road.
	ld (hl),a
	inc hl
	ld (hl),a

To get hl to point to the left hand verge on the next line, we need to move 21 bytes further in the attribute file.

Insert line 15, ld de, 21 ;point to left verge on next line
Insert line 16, add hl,de

To fill the screen with the road we will use machine code’s equivalent of a FOR-NEXT loop, djnz.
djnz stands for Decrement then Jump if Not Zero. We load the b register with the number of times we want to loop.

Insert line 7, ld b,24 ;print road verge on 24 lines.
Insert line 8, fillscreen – this is the label for our loop to jump back to.
Insert line 19, djnz fillscreen.

Because our routine will continue from the loop when b=0, we no longer need to initialise b as well as c to 0 in the next line.

Change line 20 ld bc, 0 to ld c,b.  This is one byte shorter.

Assemble and save. If you want to see the blue ‘car’, you’ll need to add something like 20 PAUSE 0 to your basic program.

CHAPTER 3 – move the car, test for collision.
Time to start playing the game! First we need to erase the car ready to move it if the player wants to.

Insert line 26, then copy and paste the following code:

	ld hl,(32900) ;retrieve car posn
	ld a,56 ;erase car
	ld (hl),a

Before we read the keyboard we will lock the keyboard for most of the game, and unlock it only when we want to read the keys.

The instruction to lock the keyboard is di = ‘disable interrupts. Its opposite is ei = ‘enable interrupts’.

Replace line 3 with di. Insert line 29, ei. Insert line 31, di. Insert line 41, ei

To read the keys we use the IN ports – see ch23 of the BASIC manual – to read the left and right half of the bottom row.

We load bc with the port number and use the instruction cp (compare) to see if the number has dropped to show a keypress.

Delete line30 and replace with the following code:

	ld bc,65278 ;read keyboard caps to v
	in a,(c)
	cp 191
	jr nz, moveright
	inc l
moveright
	ld bc,32766 ;read keyboard space to b
	in a,(c)
	cp 191
	jr nz, dontmove
	dec l
dontmove

jr nz stands for jump relative if not zero. It skips over the instruction to increment / decrement the car position.

Replace line 43 with the following to see if we bump into the oncoming road 32 bytes (1 screen) down the attribute file:

	ld (32900),hl ;store car posn
	ld de, 32 ;new carposn
	xor a  ;set carry flag to 0
	sbc hl,de
	ld a,(hl) ;crash?
	or a
	jr z,gameover
	ld a,8  ;print car
	ld (hl),a

We’d like to sub hl,de but there’s no such instruction so we use sbc, subtract with carry, and set the carry flag to zero.

or compares the register to the a register bit by bit and leaves a 1 in the a register for each bit that is 1 in either.

If all the digits are zero, then the zero flag will be set, so we can use or a to test for a black paper colour.

Delete line 53. Delete line 53 again!

To clean up the score at GAMEOVER insert line21, push bc; save score. Replace line 57 with pop bc;retrieve score

To cycle round the game before GAMEOVER change line 55 to jp PRINCIPALLOOP.

Assemble, save, and run. You’ll need to deliberately crash to get out!

CHAPTER 4 – scroll and move the road, keep score, adjust speed.
To scroll the road down the screen we copy the screen attribute bytes to the line beneath 736 times.

We use the instruction lddr, which stand for LoaD ((hl) to (de)),Decrement (hl and de) and Repeat (until bc is zero).

Replace line 53 with the following:

	ld hl,23263 ;scroll road
	ld de,23295
	ld bc,736
	lddr
	pop bc  ;retrieve score

To add 1 to the score and save it ready for GAMEOVER, insert the following into line 59:

inc bc ;add 1 to score
push bc ;save score

To move the road randomly left or right on the top line we use the following algorithm –
Choose a location in ROM where the are 256 random looking bytes and add the low byte of the score in bc to it.

If it is odd, lower the road position in hl by one. If it is even, increase by one.

(To test the last bit for odd and even we use ‘and 1’ which “masks” the last bit and sets the zero flag if it is 0).

Check to see if the road has reached the edge of the screen and bump it away if it has.
Print the new road top line like we did in chapter 2.

Replace line 58 with the following hefty chunk of code:

	pop hl  ;retrieve road posn
	push hl  ;save road posn
	ld a,56  ;delete old road
	ld (hl),a
	inc hl
	ld (hl),a
	ld de,9
	add hl,de
	ld (hl),a
	inc hl
	ld (hl),a
	;random road left or right
	ld hl,14000 ;source of random bytes in ROM
	ld d,0
	ld e,c
	add hl, de
	ld a,(hl)
	pop hl  ;retrieve road posn
	dec hl  ;move road posn 1 left
	and 1
	jr z, roadleft
	inc hl
	inc hl
roadleft
	ld a,l  ;check left
	cp 255
	jr nz, checkright
	inc hl
	inc hl
checkright
	ld a,l
	cp 21
	jr nz, newroadposn
	dec hl
	dec hl
newroadposn
	push hl  ;save road posn
	xor a  ;print new road
	ld (hl),a
	inc hl
	ld (hl),a
	ld de,9
	add hl,de
	ld (hl),a
	inc hl
	ld (hl),a

The last thing we need to do to have a playable game is slow down our blindingly fast machine code.

Insert the following into line 106 (as extension material, you could adjust the figure in bc with a keypress to ‘brake’):

;wait routine
	ld bc,$1fff ;max waiting time
wait
	dec bc
	ld a,b
	or c
	jr nz, wait

Save, assemble, and run – and that’s it! Has your tea gone cold yet?

A full listing follows, with my email address at the end for your comments and suggestions:

main
	org 33000
	di
	ld hl, 22537 ;initialise road
	push hl  ;save road posn
	xor a
	ld b,24
fillscreen
	ld (hl),a
	inc hl
	ld (hl),a
	ld de,9
	add hl,de
	ld (hl),a
	inc hl
	ld (hl),a
	ld de,21
	add hl,de
	djnz fillscreen
	ld c,b  ;initialise score
	push bc  ;save score
	ld hl,23278 ;initialise car
	ld a,8
	ld (hl),a
	ld (32900),hl ;save car posn
principalloop
	ld hl,(32900) ;retrieve car posn
	ld a,56  ;erase car
	ld (hl),a
	ei
	ld bc,65278 ;read keyboard caps to v
	in a,(c)
	cp 191
	jr nz, moveright
	inc l
moveright
	ld bc,32766 ;read keyboard space to b
	in a,(c)
	cp 191
	jr nz, dontmove
	dec l
dontmove
	di
	ld (32900),hl ;store car posn
	ld de, 32 ;new carposn
	xor a  ;set carry flag to 0
	sbc hl,de
	ld a,(hl) ;crash?
	or a
	jr z,gameover
	ld a,8  ;print car
	ld (hl),a
	ld hl,23263 ;scroll road
	ld de,23295
	ld bc,736
	lddr
	pop bc  ;retrieve score
	pop hl  ;retrieve road posn
	push hl  ;save road posn
	ld a,56  ;delete old road
	ld (hl),a
	inc hl
	ld (hl),a
	ld de,9
	add hl,de
	ld (hl),a
	inc hl
	ld (hl),a
	;random road left or right
	ld hl,14000 ;source of random bytes in ROM
	ld d,0
	ld e,c
	add hl, de
	ld a,(hl)
	pop hl  ;retrieve road posn
	dec hl  ;move road posn 1 left
	and 1
	jr z, roadleft
	inc hl
	inc hl
roadleft
	ld a,l  ;check left
	cp 255
	jr nz, checkright
	inc hl
	inc hl
checkright
	ld a,l
	cp 21
	jr nz, newroadposn
	dec hl
	dec hl
newroadposn
	push hl  ;save road posn
	xor a  ;print new road
	ld (hl),a
	inc hl
	ld (hl),a
	ld de,9
	add hl,de
	ld (hl),a
	inc hl
	ld (hl),a
	inc bc  ;add 1 to score
	push bc  ;save score
	;wait routine
	ld bc,$1fff ;max waiting time
wait
	dec bc
	ld a,b
	or c
	jr nz, wait
	jp principalloop
gameover
	pop bc  ;retrieve score
	pop hl  ;empty stack
	ei
	ret; game and tutorial written by Jon Kingsman ('bigjon', 'bj'). electronic mail gmail.com - atsign - jon.kingsman (reversed)
Categories: Z80 Assembly Tags: , ,
  1. paines
    July 27, 2010 at 6:11 pm

    Thank you very much for your work reorganising the Tutorial and making it better to read an follow.

    BR

  2. July 27, 2010 at 7:53 pm

    You are welcome! 🙂

  3. Mat
    December 28, 2010 at 1:44 am

    I have tried the spectrum tutorial ‘ZX Spectrum Machine Code in 30 Minutes chapter one and I have followed the instructions the program assembles and puts everything it should into memeory starting at 33000. However, when i try to run the program with the pRINT command it says:
    2 variable not found, 10,1

    why would it not find the variable?

  4. December 28, 2010 at 11:45 am

    Assuming your assembly program reads like this if you’ve followed the instructions (I removed the comments and the labels here for clarity):
    org 33000
    ld bc, 0
    ret

    This BASIC command works well enough. I just tried it. 🙂
    10 PRINT AT 10,0;”Your score is: “;USR 33000

    If you’re sure you’ve done the above then something else is going wrong and I’ll need more details:
    a) Which spectrum model are you using to enter the program? (I tried it in 48k)
    b) What does your assembly program look like?
    c) What does your BASIC program look like?

    Screenshots will also help.

    • Mat
      December 30, 2010 at 3:31 pm

      Many thanks for your reply,

      In the end I discovered what the problem was: being unfamiliar with the spectrum I was typing out the USR rather than tapping the key, so the code was not being read.

  5. Paul
    January 15, 2012 at 3:19 am

    Hi I am attemping to follow your tutorial, it might be me being stupid but I am struggling to see which lines need inserting where e.g. in Chapter 2 you say Insert line 6, ld a,8. This puts a blue PAPER colour into the A register etc but the line numbers dont match up to the line numbers in the listing you provide at the start, do i just count lines with code or include blank lines too?

    Am i just being stupid?

    Thanks

    Paul

    • January 15, 2012 at 9:36 am

      Hi Paul,

      Ignore the blank lines. There’s a full listing at the end of the article, so you can refer to it if you’re ever confused about what goes where. 🙂

      • Speccy32
        September 11, 2019 at 11:29 am

        is there a hex version for FUSE (It has no assembler and using REM statements gets me nowhere. also i ran the code in PC z80 and got errors in lines 2,32 and countless others. Maybe you could publish a listing for that specific assembler?

        • September 11, 2019 at 12:31 pm

          Sorry, there is no hex or PC z80 specific version. I recommend using an assembler like PASMO, or Zeus to type in the listing.

  6. Anonymous
    May 24, 2012 at 7:02 pm

    Nice tutorial! One very minor observation; you have repeatedly written the value 65355 instead of 65535

    • May 31, 2012 at 1:54 pm

      Lol. This has now been corrected in the text. Thanks!

  7. October 1, 2012 at 4:08 pm

    This is in some serious need of expanding and cleaning up to make it easier for people like me (completely new to z80 programming) to follow. Other than that, it’s awesomely great!

  8. October 1, 2012 at 4:39 pm

    Oh, nevermind about the cleanup – the tutorial becomes much easier to understand if you delete the blank lines in ZX Spin after you copy-paste.

  9. Anonymous
    December 17, 2012 at 12:02 am

    It would be great if you explain it by a video on youtube.. I’m getting “2 variable not found, 10,1” error, I’m very on zx, Thanks!

  10. Todd
    May 30, 2013 at 3:45 am

    This is brilliant. After 30 years, I’m finally beginning to grasp machine code. Thank you.

  11. Ry
    October 8, 2013 at 6:10 am

    Should this code work with Zasm? I don’t have windows so don’t use Spin, would be nice if I could use Zasm instead.

    • October 8, 2013 at 12:48 pm

      Yes, the code should work with any assembler as it doesn’t use any non-standard assembler specific directives.

  12. Guy
    October 8, 2013 at 3:53 pm

    The references to line numbers do make this difficult to follow. My line numbers don’t seem to be correct so I have to just make a best guess and which line you meant.

    • October 8, 2013 at 5:25 pm

      Yes, I see what you mean. Perhaps a text editor with line numbering might help to type in the assembly code which could perhaps be imported to Zasm? I haven’t used ZASM so I don’t know if it’s even possible. There is a full assembly listing of the source code at the end of the article. You can also refer to it in case you get stuck and wonder which line goes where.

  13. Manuel Elias
    July 17, 2014 at 10:34 pm

    I am still in the beginning of the tutorial, in the part where it is said:

    To run these four bytes of machine code, enter this one-line program in the main Spin display window:
    10 PRINT AT 0,0; “Your score was “; USR 33000

    So… I have:
    1. Copy and pasted the code in the 1st box with code into the Spin Assembler (that I called in ZX Spin in Tools -> Z80 Assembler…).
    2. In the assembler, I did File -> Save to a file named mc30mintut.asm
    3. I have Clicked st the end of line 15, press enter to create line 16 and typeed ret
    4. At the beginning of line 4, I typed ld bc,0
    5. I did File -> Save, then File -> Assemble. Typed 33000 into the Start Address box and clicked OK.
    6. At the bottom window of the assembler I have seen (as expected) a report saying “No errors in 16 lines. 4 bytes generated”.
    7. I have seen the four bytes at memory address 33000 not by clicking in the main Spin display window on Tools -> Debugger (as indicated – I think by mistake) but in Program -> Debug.

    Now the problem…
    Where exactly in the SPIN debugger should I write???
    10 PRINT AT 0,0; “Your score was “; USR 33000

    If I put the above on the assembler code, it returns error… so I suppose that this should be placed somewhere where I can place commands to be run in real-time with the program I just typed in memory… but where is this?

    • July 17, 2014 at 10:54 pm

      Hi,

      You have to enter it in the Basic editor. The main emulator window.

      Cheers!
      Arjun

  14. Andrej
    December 1, 2014 at 1:56 pm

    Hello.

    I’m glad to see spectrum is still alive :). I startet with it in good old 1981 ( ZX81). In a short while I became a master programmer in BASIC code. I never got to know assembly code. I tried a few times, but lost my grip on it. ( I was 10 years old at the time lol ). Time went by and forgot about the spectrum and now when I look at this code it gets so clear and easy. Well, in the years I got to know pretty much every language ( cobol, fortran, C, C#, C++, Java, PHP, HTML…) so I guess I get the idea about this language. Anyway, I’m glad my childhood dreams are still alive. Keep it up, nevertheless thats the begining of personal computer age as we know it.

    Regards, Andrej

    • December 2, 2014 at 4:54 pm

      Hi Andrej,
      Glad to hear from another specchum! I hope to keep the blog updated as and when I can. Thanks for the encouragement!

      Arjun

  15. btizef200
    December 7, 2014 at 5:04 am

    Thanks for this, truly useful for the nostalgic minds such as mine. I was having trouble getting the program to run (variable not found) but realised you can’t just type in “USR”, you need to specify the command using the correct key press (doh!). The keyboard helper built into SPIN helped me to get the code running. Thought that was worth a mention in case anyone else is having trouble. Thanks again, i intend to work through many chapters! 🙂

    • December 8, 2014 at 1:15 pm

      You are welcome!

  16. chris Mills
    March 21, 2015 at 2:17 am

    good times lol. my score was 2242. nice tutorial. was great fun. time to dust off my 48k beast and show my kids dad can program lol. thanks

  17. Vlad
    June 20, 2015 at 9:29 pm

    Just awesome! was struggling for so long with the assembler (I think zeus if I remember correctly) in my first computer – a ZX spectrum replica (around 14 years old) and lacked any real documentation on it so never fully understood it back then. Your tutorial makes it look so easy, who thought I would get the hang of it after 20 years!
    Very nicely explained, just fantastic!
    Thank you!

  18. September 23, 2015 at 4:44 pm

    Machine type in Spin was set to 48k and reading port 65278 and 32766 returned $ff if the keyboard untouched, so the car did not moved at all. Changed machine type to 48k+ and the ports started to return 191 and everithing was OK. Nice tutorial.
    RATOS, Romania – Senior Z80 Programmer.

  19. Stu
    December 28, 2015 at 4:07 pm

    Great post!
    Will this work on the new Spectrum remake?

    • December 29, 2015 at 11:26 am

      Not sure I quite follow. Which Specrum remake do you mean? The Vega? It should work just fine as far as I know.

      • Stu
        December 30, 2015 at 12:25 am

        http://www.recreatedzxspectrum.com

        My bad, the recreated Spectrum is just a wireless keyboard that looks like the original Spectrum. The code actually runs on an emulator, so I assume it should work as you say.

        I’ll give it a go and find out!

  20. February 18, 2016 at 6:26 pm

    Thanks for this! Enjoyed getting it running, and managed to add braking. I’m trying to get sound now, but it stops the keyboard working for some reason. I’m doing this at the start of the principal loop:

    ld hl,497 ; load pitch into hl
    ld de,2 ; load duration into de;
    call 949 ; call the process to play the sound

    Anything wrong with that? I don’t think the prior values in hl and de were needed but could be wrong.

    • May 16, 2016 at 5:57 pm

      Hi James,

      I tried out what you did and faced no issues whatsoever. Both sound and keyboard are working fine. Are you sure you didn’t change anything else and forgot about it?

      • September 6, 2016 at 4:28 pm

        Thanks for looking, but it seems to be working for me too now!
        It may have been an issue with the emulator – I was using Fuse on mac before, but in RealSpec on Windows it seems to work fine.

  21. Chucky H
    November 11, 2016 at 12:07 pm

    It has been around 33 years since I first experienced ownership of a ZX81 then upgraded to a Spectrum 48K Rubber keyboard version… and eventually upgraded again to a SAM Coupe. I still have my 48K Speccy and the Sam Coupe. I was quite good with BASIC on all machines, but Assembler code seemed way too difficult to understand… now I am considering trying to learn Z80 machine code programming via PC emulator. I hope to use your tutorial as a good starting area to help me understand the commands etc. Thank you. I’m from New Zealand 🙂

    • November 11, 2016 at 8:43 pm

      Good luck with assembly! Looking forward to seeing what you come up with. 🙂

  22. David
    December 9, 2018 at 4:11 am

    Stumbled across this and just had to dig out the emulator. I’ve worked as a developer in my time, and owned Spectrums (and SAM Coupes) as a teenager, but never managed to crack assembly language properly. Took about 60 minutes (I had a pint of beer, rather than tea, but it’s not finished yet). I also love the fact that it’s a complete game in 164 bytes…

    Constructive feedback: I think some of your line references are slightly out in the tutorial (editing the code moves things around, so the line numbers at the end aren’t what they are during the lessons). Also, chapter 4 misses a couple of lines around updating the score – I found them in the final listing, so worked it out.

    I’ve had a fun and nostalgic evening – many thanks!

    • August 25, 2019 at 7:20 pm

      Thanks for pointing that out. I’ve edited the listing to reflect the line numbers correctly.

  23. sam pedigo
    October 3, 2019 at 2:02 pm

    how do i run this in the FUSE emulator? I’ve already written it in notepad but running it just gives 3 white dots, any ideas?

    • November 18, 2021 at 12:22 am

      Use ZX Spin.

    • November 19, 2021 at 5:02 pm

      Your other option is to use a cross-assembler like pasmo or sjasmplus or zeus to assemble the listing and generate a file that can be executed on the spectrum. There is also the option of using a proper modern IDE like this: https://dotneteer.github.io/kliveide/ Take your pick!

  24. Dave
    June 24, 2020 at 8:43 am

    This is by far the best/easiest tutorial i have found on this subject. However I got to the end but i still can’t see any graphics (no car or road etc, just grey screen). I’m really new to Speccy (other than owning one as a kid in the 80s), what BASIC program must I write to get the assembly code of the road to start working? (sorry for the noob question, this is my first assembly project and first BASIC project)

  25. November 14, 2021 at 9:27 am

    Excellent tutorial on Z80 assembly language and a great place for beginners to start! I made a YouTube video using this program as a learning example. You can watch it here:

    • November 18, 2021 at 12:21 am

      search for Spriteworx on YouTube.

  1. October 5, 2012 at 2:33 am
  2. January 25, 2013 at 11:39 pm
  3. February 10, 2015 at 1:37 am
  4. May 15, 2015 at 10:34 am
  5. November 29, 2017 at 12:47 am
  6. December 29, 2019 at 4:45 am

Leave a reply to nekkoru Cancel reply