Reading Mice and Joysticks on the Spectrum Next using C

Today’s lesson in curing headaches by banging your head on the wall until they stop involves joysticks and mice. This is a bit technical, so ensure your seat back and tray table are in the upright and locked position. Please keep all arms inside the ride at all times.

Chapter 1 – Down the Rabbit Hole

If you remember, last time I told you the Next is a fairly straightforward piece of hardware. There isn’t a complex operating system in the way, programmers just need to poke at memory locations to communicate with the machine.

Reading things like joysticks and mice should be easy, right? The joystick attached to a Spectrum is nothing more than a plastic box with five switches in it. It isn’t anywhere near as complicated as a USB controller. Even the NES joypad was more complex with a shift register inside it. PS, don’t use the grey Sinclair plug on an old school joystick, it shorts out the Next’s power supply and causes unpleasant side effects.

The Joystick

A PowerPlay Cruiser Joystick for the ZX Spectrum

Don’t look too closely, you might catch something!

Let’s start with something easy. The joystick. Here’s mine, isn’t it wonderful. This is the same joystick I bought when I was a kid and it’s been plugged into most computers I’ve owned over the years.

According to the Next user manual on P273 there is a control register for the joypad – port 31 (0x1F)

76543210Desc
       XRight
      X Left
     X  Down
    X   Up
   X    Fire

A simple BASIC program can show this working

10 PRINT IN(31)
20 GO TO 10Code language: PHP (php)

Pressing joystick buttons will show UP generates 8, DOWN 4, FIRE 16, and so on.

Reading the joystick in C

This is equally straight forward. A convenient header file called input.h has been created for us. No need to talk directly to ports, it’s been done for us.

uint16_t joystick;

joystick = in_stick_kempston();
if (joystick & IN_STICK_UP) {
    // Up
}
if (joystick & IN_STICK_DOWN) {
    // Down
}
if (joystick & IN_STICK_LEFT) {
    // Left
}
if (joystick & IN_STICK_RIGHT) {
    // Right
}
if (joystick & IN_STICK_FIRE)
{
    // Fire
}Code language: JavaScript (javascript)

That’s quite easy. The mouse must be equally simple, right?

Chapter 2 – You are in a maze of twisty little passages, all alike

If we look on the same page of the manual, ports 64479 (0xFBDF) , 65503 (0xFFDF) and 64223 (0xFADF) are temptingly called “Kempston Mouse X Position”, “Y Position” and “Mouse Button Status”.

We just have to read those, right, like the joystick? Well, sort of… if you read the manual carefully, it tells us the X and Y positions are returned as bytes between 0 and 255 that wrap when they reach their boundaries.

It is pitch black. You are likely to be eaten by a Grue

The Spectrum’s screen is 256×192 pixels, and the Next has a few larger screen sizes.

The important thing to realise is the numbers are just that – you move the mouse, the numbers change. They’re not screen co-ordinates. Oh no. They also depend highly on the resolution of the mouse. A low resolution mouse changes those numbers less frequently than a high resolution mouse.

Let’s look at some Z80 assembly code

It turns out that back in 2003 someone wrote a Kempston mouse driver for the Spectrum. It’s widely linked on the Internet, and since then nobody seems to have bothered doing anything. The mouse is a strange object in the 8 bit world, and rarely used.

If you’re ever bored, go download the source and have a look at it. Maybe you’ll figure out its logic better than I can, and you’ll work out why it seems to use a few very subtle tricks to calculate “is the mouse moving left/right”.

The Spectrum Next mouse driver

Fortunately, the Spectrum Next includes a mouse driver. It’s dead easy to use in BASIC. There’s even an example included on the SD card. Guess what, it uses a (modified) version of the Kempston mouse driver from before.

.install /nextos/mouse.drv
DRIVER 126,1 TO %b,x,y

This will fill in the variables X and Y with the mouse screen co-ordinates.

The driver is for BASIC though. We’re not using BASIC, we’re using C…

Accessing Spectrum Next drivers in C

Fortunately there is a way to access drivers in C. It’s not really documented though, I was pointed in the direction of some C source that talked to the ESP8266 WiFi board, and after a lot of trial and error, fudged together some working code.

#include <arch/zxn.h>
#include <arch/zxn/esxdos.h>
static struct esx_drvapi mouse;
intrinsic_ei();	// You need to enable IM1 interrupts, I have no idea really what this means!
// If you’re using IM2 interrupt service routines, you’re on your own...
// Repeat this section of code every time
    mouse.call.driver = 126;	
    mouse.call.function = 1;	
    mouse.de = 0;  
    mouse.hl = 0; 
    if(esx_m_drvapi(&mouse)) {
        // error
    }
// down to hereCode language: PHP (php)

After this, mouse.de and mouse.hl contain the screen co-ordinates of the mouse pointer, and mouse.bc contains the state of the buttons. You can tell this is straight out of some system level assembly API, can’t you!

Edit your autoexec.bas to load the driver

If you run that code, you’ll notice it doesn’t work! It turns out that “drivers” on the Spectrum Next are not permanent things. Think of them more like DOS drivers that need loading every time the machine boots.

I’m sure there is a way to load a driver from C, but by this time I’d given up. I just edited my machine’s AUTOEXEC.BAS file to contain

10 .install /nextos/mouse.drv

And made sure to SAVE AUTOEXEC.BAS LINE 10 to make it run at boot.

Caution, contains sharp edges

The more astute of you will realise that the mouse wraps at screen edges in a rather annoying manner, and that moving the mouse too quickly makes it sometimes reverse direction and generally act a bit drunk.

Fixing or coping with these problems is left as an exercise to the reader. Hint – you can’t check if an unsigned 8 bit integer is smaller than zero, or larger than 255…

What’s next?

The next thing to do on my Spectrum Next, is to create some more gamey type content. We’re going to hit the sprite system again, with a bit more purpose – multiple sprites, collision detection and some sort of sprite pooling.