A Tricky Bug to Find


I was most of the way through writing Castle Escape before I ran it on real hardware. Until that point, I had been using emulators to develop it. Mostly FUSE as it runs on both Linux and Windows and supports most of the Spectrum flavors, features (like floating bus), peripherals, and also has built-in screen capture (great for making videos). But, I began to pine for real Spectrum hardware. I found there are many clones of the Spectrum hardware available, I decide on the Superfo Harlequin 128 and as it turned out Superfo lives just down the road from me and had a circuit board going cheap. I was off to the races. I created my BOMs and ordered a sack-load of parts from DigiKey and Mouser, and some from EBay (the AY-3-8910/12 devices are difficult to get hold of these days) and spent about a week or so soldering it all together. Finally, I can run my game on real hardware!

Loading

The Harlequin 128 has switch selectable options such as 48K/128K timings or NTSC/PAL composite output. Since I'm in the U.S. I flicked the switches to NTSC and the timings to 128K, connected it to may Samsung LCD TV and turned on the power.... IT'S ALIVE! My beautiful creation was ALIVE! The picture was a little fuzzy from the composite output but everything seemed to work. Not bad for someone with a software background.

Now to load the game... But how? I hadn't really thought that far ahead, I was so excited by the prospect of having a 'real' Spectrum I hadn't figured out how I was going to load my game. So, a few hours of research later and I had converted the TAP image to a WAV file, copied it to my iPhone via iCloud and was able to play it into the Spectrum. After fiddling with the volume for 5 minutes I was able to successfully load the loading screen and finally the game. Woohoo! The music was a little fast, NTSC (60Hz) vs. PAL (50Hz) timing but other than that it was running. Time to have a play.

Blank Screen

I excitedly hit the button to play the game and the first level was displayed... for a fraction of a second, then the screen went blank?! The game music was still playing and it seemed I could move the player around and collect items, so it wasn't completely broken, but still, something was very wrong. My first thought was, it must have loaded incorrectly. I hit the reset button so I could load it again, and as I did so, the screen came back, although frozen because the reset button was being pressed. Very strange. I loaded the game a second, and third time, with the same results each time. The one consistent behavior was, when I held the reset button, the screen would re-appear. Since the game ran flawlessly on the emulator I was beginning to suspect the fancy new hardware I had built.

Another Game

Before diving down the hardware rabbit hole, I decided it would be a good idea to try a couple of other games to see if they would run correctly. Big mistake. I started off testing some classics I used to play and spent the next 4 hours reliving my childhood. My conclusion, probably not the hardware, which means it must be my game, but at least that's a domain I understood.

The Clues

I had the following clues to help me track down this problem:

  • All menus, sound and keyboard input worked correctly
  • When game play starts
    • the playing screen appears for a fraction of a second before it disappeared
    • game play seemed to continue
  • When reset is pressed, the playing screen would re-appear

This pointed me to something in the main game loop. So, process of elimination, I started to comment out routines and see what happened. Eventually, I tracked it down to the scrolling message routine. Upon closer inspection, I noticed this routine used an undocumented Z80 instruction, 'out (c), 0'. Just for test purposes, I changed this instruction to, 'xor a; out (c), a', and et voila. With my game now working, I was curious to find out why the undocumented 'out' instruction caused a problem.

NMOS vs. CMOS

N-type metal-oxide-semiconductor (NMOS) vs. complementary metal-oxide semiconductor (CMOS)? These are 2 different types of semiconductor technology. Older technologies, like 1980's Z80 CPU's were NMOS unlike my brand-spanking new Z80 CPU which is CMOS. I had assumed they were functionally the same, however there are some differences, especially where the undocumented Z80 instructions are concerned. Below are 2 differences which I am now aware of...

  • NMOS
    • OUT (C),0 --> OUT (C),0
    • LD A,R/LD A,I --> P/V flag is cleared if interrupt is received during the execution of these commands (bug)
  • CMOS
    • OUT (C),0 --> OUT (C),255
    • LD A,R/LD A,I --> no bug

My bug was due to my code using 'out (c), 0' where 'bc' is the Spectrum memory mapping register. Instead of mapping in RAM bank 0 the instruction was setting all of the bits in the mapping register which caused the screen to blank since screen 1 was being mapped in. Pressing reset, reset this register and screen 0 was displayed. It turns out, 'out (c), 0' isn't what the instruction does at all. The instruction is actually, 'out (c), (hl)'. This is explained at the site below...

https://groups.google.com/g/comp.os.cpm/c/HfSTFpaIkuU/m/KotvMWu3bZoJ?pli=1

Moral

The moral of this story is, don't use undocumented instructions unless you know, exactly, how they work.

NOTE:

The FUSE emulator actually has an option to emulate a CMOS Z80 CPU. So, going forward I can check for these type of issues ;).

Get Castle Escape (ZX Spectrum 48K-128K)

Buy Now$1.99 USD or more

Leave a comment

Log in with itch.io to leave a comment.