Showing posts with label Emulators. Show all posts
Showing posts with label Emulators. Show all posts

Sunday, October 02, 2016

Arduino Liberates the Recreated ZX Spectrum

Leave a Comment
Following on from the previous posts, in some free time this week, I butchered my ZX81 / AZ15 Arduino keyboard sketch libraries, sliced in some code for a USB host shield and beat an Arduino Leonardo into submission. The end result a fully working Recreated ZX Spectrum keyboard.

As talked about previously, the Recreated Spectrum has two keyboard modes, one for PS2 functionality and one for Emulation. The odd thing here being that in Emulation mode, the Recreated Spectrum is not compatible with the vast majority of Spectrum Emulators (at least at the time of writing). Disregarding this; Emulation or Mode 'A' as it is labeled, is the easiest to target for any external hardware solution used in remapping the Recreated Spectrum's keys in order to provide a Normal Spectrum Emulator experiance. See the previous post for a deeper explanation.

Emulation mode was quite straightforward in it's implementation, being that all keys are registered when pressed or released, if in a slightly odd way. It was a simple matter of matching the codes the Recreated Spectrum passed out on each of these events with the actual keys.

Once Mode 'A' was covered, it was time to move onto Normal / PS2 mode (Mode 'B'). Normally to enter either Mode 'A' or Mode 'B', a slider switch on the keyboard needs to be placed in the correct position. I decided that if using an external device to do the decoding, this mechanism would become problematic. Having already decoded Mode 'A' for usage, adding a simple keyboard selection would be simpler way to manage switching into Mode 'B' or vice versa. As a result, Mode 'A' is permanently set to on, at the back of the Recreated Spectrum, "CAPS SHIFT" + "SYMBOL SHIFT" + "ENTER" now changes modes on the external Arduino Leonardo based solution.

All key-mappings as described in the Recreated Spectrum's manual and Cheat Card remain the same, with the exception of the Windows, and Print Screen Keys which have been left unmapped at this stage.

While I've used a Leonardo and full sized USB host shield initially, there is no reason why a Pro Micro and mini USB host adapter could not be used to do the same thing. In fact one of the next steps could well be to do just that, along with mating the whole arrangement with a Raspberry Pi in a custom Sinclair inspired case.

For those that feel like a little experimentation, feel free to mess around with some hot of the presses Recreated ZX Spectrum Arduino Sketches. I'll be making changes regularly and the latest changes will be on the 'Recreated Spectrum Project Files to Download' page.

Also, make sure you download and install the Arduino USB_Host_Shield_2.0 libraries from Github before compliling the Recreated Spectrum Project Files.


Keyboard in Action

Read More

Saturday, September 24, 2016

It's all in the Modes & Mappings of the Recreated ZX Spectrum Keys

1 comment
As mentioned last post, the keyboard works perfectly in the standard PS2 mode, it's the emulator 'layer' that has usage issues. If you switch to mode A (the emulator layer) on the keyboard, the key presses register anything but what you'd expect. There seemed to be some rather odd mapping going on, leaving the emulator mode a little useless.

There would seem to be 3 ways in which to adjust the Recreated Spectrum for usage with Emulators, other than the designated but pulled Elite variants.
  1. Patch an Emulator, or wait for support to be added. Apparently the some patches exist for the Fuse Emulator, adding compatibility for the Recreated Spectrum. That's no guarantee that other emulators will support it; I'd wager many gradually will, though you never know.
  2. Rip out the existing control board and build up a new version.
  3. Come up with an external hardware solution to adjust the key codes sent back to the host computer.
I guess option 1 would be the easiest, at least from a general user perspective, though unless your favorite emulator is open source, this could take a while. Option 2 is the most problematic, as to replace the board in a non case destroying way would be difficult (not impossible) to achieve. Frankly I don't want to ruin the look of the Recreated Spectrum with a hatchet job. That leaves option 3 as a pretty good alternative.

The PS2 mode would be the logical mode to tap into for signal / code modification, we need to see what the Spectrum keyboard actually sends out across the USB cable. To do this easily I pulled out an Arduino USB host shield to read the key codes are being sent. The results where interesting. Firstly, the standard PS2 layer (Mode B), was sending exactly what would be expected. With all letters and combinations of 'shift' and 'Alt' key presses being sent in a seemingly standard manner. I found one glaring issue that puts a stop to tapping into the PS2 mode however; the keyboard does not send any codes for the combination of "CAPS SHIFT + 9". What this means to those who have not already guessed, is that you can't set the keyboard to graphics mode.

So this leaves us with Mode 'A', the emulator layer. This is where things get interesting. A PS2 style keyboard will normally send codes stating when a key is pressed, and a similar code indicating that a particular key has been released. The codes the Recreated Spectrum is sending in emulator mode do not follow that pattern. Instead, when you press 'q', for example the keyboard sends the code for 'u' being pressed, however on release the keyboard send the code for 'v' being released. There is not a single key that sends just the expected letter. I've mapped the keys in the table below.


The Recreated ZX Spectrums Mode 'A' Key Mapping

KEY 1 2 3 4 5 6 7 8 9 0
PRESS a c e g I k m o q s
RELEASE b d f h j l n p r t
KEY Q W E R T Y U I O P
PRESS u w y A C E G I K M
RELEASE v x z B D F H J L N
KEY A S D F G H J K L ENTER
PRESS O Q S U W Y 0 2 4 6
RELEASE P R T V X Z 1 3 5 7
KEY CAPS SHIFT Z X C V B N M SYM SHIFT SPACE
PRESS 8 < - [ ; , / { ! %
RELEASE 9 > = ] : . ? } $ ^

As can be seen from the table, the key code order is layed out in a very neat and predictable manner. Also crucially, all key presses are detected in emulator mode, there are no dead keys combination problems such as in the "CAPS SHIFT + 9" issue discovered in PS2 mode. This all makes adding a man in the middle hardware solution relatively easy to implement.

The next step is to write some Arduino code to see just what we can achieve.

Read More

Sunday, August 28, 2016

AZ15 Raspberry PI 3 Upgrade

Leave a Comment
I've taken the opportunity to upgrade the heart of the AZ15 to a Raspberry PI 3, up from a PI 2. The neat thing about the last few models of PIs' has been the unchanging form factor with increasing capabilities.

There has been a noticeable speed boost between the PI 2 and PI 3, this has been most evident when using the ZEsarUX emulator. Previously ZEsarUX has seemed somewhat sluggish on the PI 2, leaving SZ81 as my emulator of choice. With the performance improvements on the PI 3, the choice of emulator is now wide open, a nice change indeed.

Also  the PI 3s' addition of baked in Bluetooth and WIFI has meant that a number of the USB ports formerly dedicated to these tasks are suddenly free. With the PI 2, I had dedicated one of the internal (to the AZ15) USB ports to a WIFI dongle, with the advent of the PI 3 new found connectivity option, I've instead inserted a small in stature and capacity USB flash drive for ZX81 ''P" file storage. Additionally with Bluetooth now available, wireless mice in particular are easy to add.

AZ15 With Bluetooth Mouse and WIFI

AZ15 Concept Image

A What's in the Box Shot


All the nice additions to the PI 3 bring the whole AZ15 project pretty damn close to the original concept image; thanks to the addition of a Bluetoooth mouse and the ever unseen WIFI.

So now it's time for a new glory shot, a shot which I've taken at as close to the same angles and lighting conditions as manageable.

What do you think?


Read More

Sunday, August 21, 2016

Brand New Version ZX81 Arduino Keyboard Sketches

Leave a Comment
This week brings major changes to the Arduino sketch, so big I'm calling this latest modification version 2.

The last update still left me somewhat dissatisfied with the keyboards performance in some emulators. The deficiencies were particularly apparent when playing games, where the keyboard would not respond smoothly and in a timely fashion. To remedy the response time problems, I went back and re-examined how the code was first put together.


When Originally written, I'd relied on the Keyboard.print function to send key presses, which worked fine during initial testing. Unfortunately that didn't work so well when incorporate the shift, graphics and function modes into the keyboard. Using the Ardunio keyboard Keyboard.press functionality solved that issue and all seemed to be okay for a while. There were still some problems that become noticeable in various emulators, and these I addressed with some debouncing routines (which culminated in the last code release).

To cut it all short the debouning routines were the completely wrong solution to the problem and as it turned out I'd completely overlooked a decent Keyboard.release method. After a mild dose of head scratching in the effort to remedy past errors, I'm releasing what I'm calling version 2 of the sketches.

The Version 2 removes the kludge of keyboard debouncing, and now thankfully we have a keyboard that functions just as well as that on any self respecting ZX81.

The command line / serial switches have, thanks to the changes, slimmed back down. The need for extra commands to set extended emulation behavior have been removed.


Command Line, Mode and Options Selection
BELLSound the LeoStick Piezo. Could be used to forward audio system notifications etc. 
BEEP OFFTurn off Keyboard Sounds. Sounds are off by default.
BEEP ONTurn on Keyboard Sounds. Similar to the keyboard clicks made by ZX Spectrum when typing. The keyboard only emits clicks in Standard mode. In Emulation mode sound is always off.
DEVICEReturns message "ZX81_KEYS" to the console. Useful if unsure you have the correct serial port. You must be monitoring incoming streams to get the return message. eg cat /dev/ttyACM0
EMULATORSwitch the keyboard into emulator mode.
STANDARDSwitch the keyboard into standard mode.

All versions of the keyboard sketches are avaliable from the Project Files page.
Read More

Sunday, August 14, 2016

Arduino Sketch Updates for Keyboard Debounce Timimgs

Leave a Comment
I've made some minor changes to the Arduino sketches over the weekend. The alterations affect the debounce timings, where I've hopefully improved keyboard response while in EMULATOR modes. The latest Code can be downloaded from Project Files page.

A new switch DBEM ON / OFF, for piping to the Arduino serial console, has been added. The switch is designed to set the STANDARD mode debounce timimgs to those use in EMULATOR mode. This is particularly useful if the emulator frequently requires the keyboard to be switched to STANDARD mode. I found that in particular ZEsarUX requires STANDARD mode to be set quite often to load files or change options etc.

The below example  sets the keyboard to EMULATOR mode, sounds the BELL and turns DBEM ON.

echo emulator bell dbem on> /dev/ttyACM0

Setting DBEM ON will also negate the use of the BEEP ON option. For more extra information on switches see the earlier blog entry AZ15 and A Tale of Two Features.


Command Line, Mode and Options Selection
BELLSound the LeoStick Piezo. Could be used to forward audio system notifications etc. 
BEEP OFFTurn off Keyboard Sounds. Sounds are off by default.
BEEP ONTurn on Keyboard Sounds. Similar to the keyboard clicks made by ZX Spectrum when typing. The keyboard only emits clicks in Standard mode. In Emulation mode sound is always off.
DBEM OFFUse normal STANDARD mode debounce timings. This is the default option.
DBEM ONUse EMULATOR keyboard debounce timings in STANDARD mode. Good to set if switching to STANDARD mode frequently while using an Emulator. Switch was added with ZEsarUX in mind in particular. Default if off.
DEVICEReturns message "ZX81_KEYS" to the console. Useful if unsure you have the correct serial port. You must be monitoring incoming streams to get the return message. eg cat /dev/ttyACM0
EMULATORSwitch the keyboard into emulator mode.
STANDARDSwitch the keyboard into standard mode.


Read More

Saturday, August 13, 2016

ZX81 Kiosk Mode for Raspberry Pi / AZ15 (Part 2)

Leave a Comment

In Part 1, we setup the AZ15 to boot into a kiosk mode that relied on X11. This is all well and good, but why use an X11 windows manager when we don't really need to.

All the major ZX81 Emulators available for use on the Raspberry Pi have SDL versions available, and SDL dosn't require an X11 windows manager. Booting right into an emulator will speed up the startup process and get us "programming" our ZX81 in not time at all.

All the hard work such as installing the sz81 emulator was covered in Part 1. This time around we only need to make some minor alterations.

Auto Login the PI User to the CLI


First off, a couple of minor config changes from the last blog entry. We need to set auto login to the CLI instead of the Desktop. From the X11 desktop (startx if you need to), access the graphical config utility using the “Preferences” > “Raspberry Pi Configuration” menu. Set the options as below:


Auto Run the ZX81 Emulator at Login


Next up add the following Code to the end of your /home/pi/.bashrc file.


# Run if tty1
if [ $(tty) == /dev/tty1 ]; then
 # *** Configure and Run the ZX81 emulator ***
 # If you have ZX81 P files / Roms in a handy Dirctory, cd to it first.
 # This obviously makes life easier later on.
 cd ~/Files/emulation/zx81

 #Set az15 keyboard options, if you haven't built a az15 the comment this out.
 echo emulator bell beep off dbem on> /dev/ttyACM0

 # Run the sz81 emulator in fullscreen mode
 sz81 -f -1024x768

 #Set az15 keyboard options, if you haven't built a az15 the comment this out.
 echo standard bell beep on dbem off> /dev/ttyACM0
fi


That's it, we can now boot right on into the emulator after the next "reboot".

A Little Extra Visual Config Enhancement


Normally when the Pi boots up a whole load of logging and informational messages scroll down the screen. While this is all very useful information (depending on your perspective), it's not exactly helping generate that 80s mircro-computer vibe.

So let's hide the information overload by adding some extra switches into the Raspberries "cmdline.txt" file.

sudo nano /boot/cmdline.txt

All the configuration options will be on the first line of the file, change the following details.

Find:
console=tty1

Change to:
console=tty3

Next, add the following commands to the end of the line.

loglevel=3 logo.nologo

If you reboot now, having made the above changes in combination with Part 1's instructions, then you should see something very similar to the video clip below. (You'll probably also notice I changed the Splash Screen).



Now the AZ15 has booted up, we're ready to do some serious programming, or the somewhat more likely scenario, play the excellent "Crozxy Road" from Bobs Stuff.




Read More

Wednesday, August 03, 2016

ZX81 Kiosk Mode for Raspberry Pi / AZ15 (Part 1)

Leave a Comment

The final piece to the ZX81 keyboard puzzle is to get the Raspberry Pi / AZ15 to boot like it's 1981, arriving straight at the Sinclair "K" prompt after being switched on. Booting won't be measured in seconds (actually it comes in at about 20 seconds) as it was in 1981, after all 35 years of progress takes extra time. Though to be fair, the loading of software after booting up a ZX81 was measured in aeons.

There are many ways to boot a Pi into a Kiosk like mode, I've gone with one of the easiest, simply hijacking the pre-existing Openbox Xsession. The process outlined below is applied on top of a default Raspbian Jessie installation.

Installing the sz81 Emulator


Fist up you'll need a ZX81 emulator, I've been using various sz81 versions to test the AZ15, and have found the last official release, sz81-2.1.7,  the most stable for my purposes.

- Download the official sz81-2.1.7 release of sz81 from http://sz81.sourceforge.net/
- Alternatively, experiment with the unofficial updates provided at http://rullf2.xs4all.nl/sz81/

After downloading and decompressing the sz81-2.1.7-source.tar.gz file, edit the makefile in the source directory, and change the installation type to system wide. Full details can be found in the sz81 REAME file.


# Comment/uncomment these to choose an installation destination
# System wide installation
PREFIX?=/usr/local
BINDIR?=$(PREFIX)/bin
DOCDIR?=$(PREFIX)/share/doc/$(TARGET)
PACKAGE_DATA_DIR?=$(PREFIX)/share/$(TARGET)


Also, if not already installed grab, the SDL develpment libararies from the repositories, as these will be required before compiling the sz81 source.

sudo apt-get install libsdl1.2-dev

Then from the sz81 source directory, make and install sz81 itself.

make
sudo make install


Auto Login the PI User


Now that we have an emulator installed, we'll want it to execute on boot up.

If the Pi isn't already configured to Boot to the Desktop and / or Auto Login, you can access the graphical config utility using the “Preferences” > “Raspberry Pi Configuration” menu. Set the preferences as below:


If your Raspberry Pi wasn't previously configured to boot to the desktop, reboot it now before continuing, in order to test all is well so far.

Simple PI / ZX81 Emulator Kiosk


Raspbian Jessie ships with 3 pre-configured desktop environments:


  1. Default Xsession: An LDXE enironment using version 3 GTK+ of toolkit
  2. LDXE: An LDXE enironment using version 2 GTK+ of toolkit
  3. Openbox: Amazingly configurable Desktop


For our purposes we'll essentially hijack Openbox for auto logon and startup the sz81 emulator. Openbox is a very user configurable and we shall be ignoring most of that power co-opt it for our Kiosk style login.

Editing or create an OpenBox autostart file (using Geany or Nano from a console) in a sub-directory off the pi users home directory.

nano ~/.config/openbox/autostart


# *** Set some Openbox options ***
# Set a background colour to Black
BG=""
if which hsetroot >/dev/null; then
  BG=hsetroot
elif which esetroot >/dev/null; then
  BG=esetroot
elif which xsetroot >/dev/null; then
  BG=xsetroot
fi
test -z $BG || $BG -solid "#000000"

# *** Configure and Run the ZX81 emulator ***

# If you have ZX81 P files / Roms in a handy Dirctory, cd to it first.
# This obviously makes life easier later on.
cd ~/Files/emulation/zx81

#Set az15 keyboard options, if you haven't built a az15 the comment this out.
echo emulator bell beep off> /dev/ttyACM0

# Run the sz81 emulator in fullscreen mode
sz81 -f -1280x900

# *** On emulator Close exit Openbox ***

#Set az15 keyboard options, if you haven't built a az15 the comment this out.
echo standard bell beep on> /dev/ttyACM0

openbox --exit


Save the files and that's the basic kiosk like configuration completed.

Now log out of the default LXD environment. Once back to the logon screen, select Openbox from the drop down menu on the top left of the screen. If everything has gone to plan the sz81 emulator should startup.

If you now exit the sz81 emulator, you should once again be dropped back to the logon screen. This time select Restart from the power menu and the PI should boot directly into Openbox and open the emulator, as by default the last Desktop Environment will be selected on a system restart.

Retro Pixel Inducing Splash Screen

Setup a Splash Screen


Get into that 80s spirit with a Loading Screen. Technically there were no splash screens on a ZX81, unless you count the wavy lines that take over the screen when loading from tapes, but lets no spoil things. I've made up a rather retro and referential black and light grey image for the purpose, but use what ever image floats your boat.

Log onto the console and create a directory.

sudo mkdir /etc/images

Copy your chosen image into that directory, make sure your image is in the PNG format.

sudo cp /home/pi/"YOUR IMAGE".png /etc/images/az15-splash.png

Now we need a FrameBuffer Image viewer to run on boot.

sudo apt-get install fbi

Create a startup file and edit the contents.

sudo nano /etc/init.d/az15-splash-screen


#! /bin/sh
### BEGIN INIT INFO
# Provides:          az15-splash-screen
# Required-Start:
# Required-Stop:
# Should-Start:      
# Default-Start:     S
# Default-Stop:
# Short-Description: Show custom splashscreen
# Description:       Show custom splashscreen
### END INIT INFO

do_start () {
  /usr/bin/fbi -T 1 -noverbose -a /etc/images/az15-splash.png    
  exit 0
}

case "$1" in
  start|"")
    do_start
    ;;
  restart|reload|force-reload)
    echo "Error: argument '$1' not supported" >&2
    exit 3
    ;;
  stop)
    # No-op
    ;;
  status)
    exit 0
    ;;
  *)
    echo "Usage: az15-splash-screen [start|stop]" >&2
    exit 3
    ;;
esac

:


Now, make the file executable and get the system to recognise it as an init script.

sudo chmod a+x /etc/init.d/az15-splash-screen
sudo insserv /etc/init.d/az15-splash-screen

Reboot and you should have something very similar to the video clip below.


Credit Where It's Due Most


Much thanks to the following sites and blogs describing the exact or very similar processes, leading to an article that's boarding on plagiarism.

Run a ZX81 Emulator in "kiosk" mode on a Raspberry Pi
Building a Raspberry Pi Kiosk
Openbox Wiki Entry on Autostart


Read More

Wednesday, December 09, 2015

AZ15 and a Tale of two Features

Leave a Comment

Feature One: As seen on the GuardianWitness


A big thanks to the The Guardian and Matthew Holmes in particular, for featuring this humble Raspberry Pi, Arduino and ZX81 keyboard project in the article Best reader Raspberry Pi projects – and some of the most pointless. There are loads of other interesting Pi related constructions to peruse through, the article is a fun sample of all the ways people are finding to enjoy the Pi. I particularly enjoyed the Raspberry Pi hacked into a Holga 120 Film Camera, and its taking of photos old school.

The AZ15, as seen in all the best online newspapers.


Feature Two: New Keyboard Functionality


The nice thing about making something yourself is that you can continue to work on it, even if to all external appearances the project may seem complete.

Having used the AZ15 for almost two weeks now I decided to add a little extra functionality, addressing some minor usability issues, the main one being my forgetting to switch between Emulator and Standard keyboard modes. When using a ZX81 emulator with the keyboard, the button on the right side of the case is pressed, this changes the keyboard mode, and the LEDs on top visually tell you what mode you're in. This is all fine, except I keep forgetting to press the button.

The solution is simple enough, have the keyboard change modes automatically. Nicely the Raspberry Pi can talk to the LeoStick via serial link and vice versa. A couple of commands directed at the serial interface before launching an Emulator automatically selecting the correct keyboard mode and things soon become more user friendly.

For example, emulator mode can be selected and a notification bell passed with the following redirection to the serial port the LeoStick / Keyboard is attached to.

echo emulator bell > /dev/ttyACM0

Putting the above in a start script that launches the emulator and then sets all back to normal after exiting solves my main issues.



#!/bin/bash
# Put keyboard in Emulator Mode
echo emulator bell > /dev/ttyACM0
# Launch the sz80 emulator
sz81
# Set keyboard back to Standard Mode
echo standard > /dev/ttyACM0

If the USB port assignment changes around, "dmesg" could always be used to parse for the correct device.


Command Line, Mode and Options Selection
BELLSound the LeoStick Piezo. Could be used to forward audio system notifications etc. 
BEEP OFFTurn off Keyboard Sounds. Sounds are off by default.
BEEP ONTurn on Keyboard Sounds. Similar to the keyboard clicks made by ZX Spectrum when typing. The keyboard only emits clicks in Standard mode. In Emulation mode sound is always off.
DEVICEReturns message "ZX81_KEYS" to the console. Useful if unsure you have the correct serial port. You must be monitoring incoming streams to get the return message. eg cat /dev/ttyACM0
EMULATORSwitch the keyboard into emulator mode.
STANDARDSwitch the keyboard into standard mode.


The latest Arduino sketch can be downloaded here: http://zx81.dasteph.com/files/zx81usbkeyboard_20151205.tar.gz

Read More

Wednesday, November 04, 2015

Some More Arduino Code for the ZX81 Keyboard

4 comments
I've made  a fair number of changes to the ZX81 keyboard Arduino sketch. The most noticeable being that the code is now broken into multiple files / libraries; all feeling a little more C++ like. Of course real microcontroller programmers use C, but we wont discus that here.

In general the keyboard functions as earlier. The main physically tangible improvement being that the keyboard performs a whole lot better in ZX81 emulators. It's not perfect and it's definitely not ideal for playing real time games with, though It works as advertised when typing normally, ie. using the keyboard to program with.

Emulator comparability wise, the keyboard will work with  sz81 and ZEsarUX. I'd recommend using sz81, as the keyboard still functions best with that particular emulator. I've also had a go at using Fuse with the keyboard, and to the extent that a ZX81 keyboard can be used on ZX Spectrum it functions okay, though it is missing those couple of crucial extra keys for proper Spectrum emulation.

When using the keyboard in standard mode as a regular USB input device in Linux or with a Raspberry Pi there are no issues, unfortunately, as with the emulators, games that require SDL libraries suffer from the "keys not being registered when you'd like them to be" syndrome. This issue I'm afraid may be down to problems with the Arduino USB Keyboard library and beyond my immediate control.

Presented For Your Amusement, Code Caught in the Wild


Main Program (zx81usbkeyboard.ino)

As the name on the implies, here be the general program.


// **************************************************************************
// **** ZX81 USB Keyboard for Funtronics LeoStick (based on a Leonardo). ****
// **** zx81usbkeyboard.ino                                              ****
// **************************************************************************
// ** David Stephenson 2015-11-03  **
// **                              **
// ** Originally based on code by: **
// ** Dave Curran  2013-04-27      **
// ** Tony Smith 2014-02-15        **
// **********************************

 #include "Arduino.h"
 #include "zx81keyboard.h"
 #include "zx81modestate.h"
 
// ************************
// *** Global Variables ***
// ************************

// Setup Global Variables for keyboard.
ZxKeyBoard MyKeyboard;

// Setup LeoStick pins for mode switch and indicator LEDs.
ModeState MyModeState(A5,A0,A1,A2);

// Setup LeoStick pins for Keyboard rows and columns.
const byte bColPins[NUM_COLS] = {13,12,10,9,8};
const byte bRowPins[NUM_ROWS] = {7,6,5,4,3,2,1,0};


// ******************
// *** Main Setup ***
// ******************
void setup() {
  
 // Set all Keyboard pins as inputs and activate pull-ups.
 for (byte bColCount = 0 ; bColCount < NUM_COLS ; bColCount++)
 {
  pinMode(bColPins[bColCount], INPUT);
  digitalWrite(bColPins[bColCount], HIGH);
 }
 
 // Set all Keyboard pins as inputs.
 for (byte bRowCount = 0 ; bRowCount < NUM_ROWS ; bRowCount++)
 {
  pinMode(bRowPins[bRowCount], INPUT);
 }
 
 // initialize control over the keyboard.
 //Serial.begin(9600);
 Keyboard.begin();
 
}
 
// ************************
// *** Main loop        ***
// *** Lets get Busy-ah ***
//*************************
void loop() {

 bool boShifted = false;
 byte bKeyPressed = 0;
 
 // Set Keyboard Mode.
 KEYMODES eMyKeyMode = MyModeState.GetMode();
 KEYSTATES eMyKeyState = MyModeState.GetState();
 
 // Check for the Shift key being pressed.
 pinMode(bRowPins[SHIFT_ROW], OUTPUT);
 
 if (digitalRead(bColPins[SHIFT_COL]) == LOW) boShifted = true;
 
 pinMode(bRowPins[SHIFT_ROW], INPUT);
 
 for (byte bRow = 0 ; bRow < NUM_ROWS ; bRow++){
  // Run through the rows, turn them on.
  
  pinMode(bRowPins[bRow], OUTPUT);
  digitalWrite(bRowPins[bRow], LOW);
  
  for (byte bCol = 0 ; bCol < NUM_COLS ; bCol++){
   if (digitalRead(bColPins[bCol]) == LOW){
    if (boShifted && eMyKeyMode == STANDARD){
     // Select correct Keyboard layout for current state For STANDARD mode. 
     // Shift alters the Current state selection. eg. gets Red Shift symbols in Normal State. 
     switch (eMyKeyState) {
      case NORMAL:
       bKeyPressed = MyKeyboard.bKeyPress(bRow, bCol, NORMAL_SHIFTED, eMyKeyMode);
       if (bKeyPressed == KEY_RETURN) { 
        eMyKeyState = FUNCTION;
        bKeyPressed = 0;
        MyKeyboard.KeyDisable(bRow, bCol);
       }
       if (bKeyPressed == '9') {
        eMyKeyState = GRAPHICS;
        bKeyPressed = 0;
        MyKeyboard.KeyDisable(bRow, bCol);
       }
      break;
       
      case FUNCTION:
       bKeyPressed = MyKeyboard.bKeyPress(bRow, bCol, NORMAL_SHIFTED, eMyKeyMode);
       if (bKeyPressed == KEY_RETURN) { 
        eMyKeyState = NORMAL;
        bKeyPressed = 0;
        MyKeyboard.KeyDisable(bRow, bCol);
       }
       if (bKeyPressed == '9') {
        eMyKeyState = GRAPHICS;
        bKeyPressed = 0;
        MyKeyboard.KeyDisable(bRow, bCol);
       }
      break;
       
      case GRAPHICS:
       bKeyPressed = MyKeyboard.bKeyPress(bRow, bCol, GRAPHICS_SHIFTED, eMyKeyMode);
       if (bKeyPressed == KEY_RETURN) { 
        eMyKeyState = FUNCTION;
        bKeyPressed = 0;
        MyKeyboard.KeyDisable(bRow, bCol);
       }
       if (bKeyPressed == '9') {
        eMyKeyState = NORMAL;
        bKeyPressed = 0;
        MyKeyboard.KeyDisable(bRow, bCol);
       }
      break;
       
     }
    } else {
     // Emulator keyboard, the keyboard states are controlled by a ZX81 emulator.
     // Keyboard mimics unaltered PS2 Keyboard presses as expected by an emulator.
     bKeyPressed = MyKeyboard.bKeyPress(bRow, bCol, eMyKeyState, eMyKeyMode);
    }
    
    if (bKeyPressed > 0 ) {
     //Serial.write(bKeyPressed);
     if (eMyKeyMode == EMULATOR){
      if (bKeyPressed && boShifted) Keyboard.press(KEY_LEFT_SHIFT);
      Keyboard.press(bKeyPressed);
      // Many Emulators don't use standard OS keyboard routines and require a pause
      // for key to register. There is some variance, but 100ms seems to cover most.  
      delay(DEBOUNCE_DELAY/2);
     } else {
      if (eMyKeyState == GRAPHICS && bKeyPressed > 96 && bKeyPressed < 123){
       if (boShifted){
        Keyboard.press(KEY_LEFT_ALT);
       } else {
        Keyboard.press(KEY_LEFT_CTRL);
       }
      }
      Keyboard.press(bKeyPressed);
      // Some audio feedback if not in EMULATOR mode.
      // Kill the line if you hate beepy beep beeps.
      tone(11, 31, 20);
     }
     
     Keyboard.releaseAll();
    }
    
   } else {
    MyKeyboard.KeyPressReset(bRow, bCol);
   }
  }
  pinMode(bRowPins[bRow], INPUT);
 }
 
 digitalWrite(bRowPins[SHIFT_ROW], LOW);
 
 // Update LED panel and check for Mode change switch press.
 MyModeState.SetState(eMyKeyState);
 
 }

Keyboard Class (zx81keyboard.h)

Main guts of the keyboard setup and initial handling is in here.


// ***********************
// ** zx81keyboard.h    **
// ** Class Definitions **
// ** For ZX81 keyboard **
// ***********************

enum KEYMODES {EMULATOR, STANDARD};
enum KEYSTATES {NORMAL, NORMAL_SHIFTED, GRAPHICS, GRAPHICS_SHIFTED, FUNCTION};

#define NUM_ROWS 8
#define NUM_COLS 5 

#define SHIFT_COL 4
#define SHIFT_ROW 5
//#define FUNCTION_COL 4
//#define FUNCTION_ROW 6
//#define GRAPHICS_COL 3
//#define GRAPHICS_ROW 2

#define DEBOUNCE_DELAY 200 //debounce countdown value.
#define DEBOUNCE_DELAY_REPEAT 600

// Defines a single key and its 4 possible Major KEYSTATES: NORMAL, NORMAL_SHIFTED, GRAPHICS, GRAPHICS_SHIFTED
// Returns 5 KEYSTATES, adds FUNCTION: Caps Lock).
class ZxKey  {
 private:
  // Standard Keyboard Characters for Normal PC Use
  byte bNormal;   // Standard ZX keyboard only in lower case. EMULATOR KEYMODE should use bNormal only.
  // Extra Modes for using keyboard as a normal(ish) PS2 / USB device.
  byte bNormalShifted; // Red Symbols & Words (replaced by Symbols)  
  byte bGraphics;   // Standard ZX keyboard but should be used with CTL characters. Numbers = F1 - F10 etc
  byte bGraphicsShifted; // Standard ZX keyboard but should be used with ALT characters. Numbers = F11 - F12, 5 = home, 6 PGUP etc 
  
  // Monitor Keys last press / activity.
  short iDebounceCount = DEBOUNCE_DELAY;
  short iDebounceCountHighest = iDebounceCount;

  byte KeyValue(KEYSTATES eKeystate) {
   // Get Keyboard character for correct Key state.
   byte bKeyValue = 0;

   switch (eKeystate) {
    case FUNCTION:
     bKeyValue = bNormal;
     // Adjust for Capital Letters
     if (bKeyValue > 96 && bKeyValue < 123){
      bKeyValue = bKeyValue - 32;
     }
     break;
     
    case NORMAL_SHIFTED:
     bKeyValue = bNormalShifted;
     break;
     
    case GRAPHICS:
     bKeyValue = bGraphics;
     break;
     
    case GRAPHICS_SHIFTED:
     bKeyValue = bGraphicsShifted;
     break;
     
    default:
     // Standard normal and Emulator mode keyboard.
     // Shift key modifier used later to select functions etc in a ZX81 Emulator.
     bKeyValue = bNormal;
     break;
   }
   return bKeyValue;
  }
  
 public:
  ZxKey (byte, byte, byte, byte);
   
  byte KeyPressDetected(KEYSTATES eKeystate, KEYMODES eKeymode){
   byte bKeyValue = 0;
   iDebounceCount--;
   if (iDebounceCount == 0){
    switch (iDebounceCountHighest) {
     case DEBOUNCE_DELAY:
      iDebounceCount = DEBOUNCE_DELAY_REPEAT;
      iDebounceCountHighest = iDebounceCount;
     break;
     
     case DEBOUNCE_DELAY_REPEAT:
      if (eKeymode == EMULATOR){
       iDebounceCount = DEBOUNCE_DELAY;
      } else {
       iDebounceCount = DEBOUNCE_DELAY / 2;
      }
     break;
     
     default:
      iDebounceCount = DEBOUNCE_DELAY;
     break;
    }
    bKeyValue = KeyValue(eKeystate);
   }
   return bKeyValue;
  }
  
  void KeyPressReset(){
   iDebounceCount = DEBOUNCE_DELAY;
   iDebounceCountHighest = iDebounceCount;
  }
 
  void KeyDisable(){
   iDebounceCount = -1;
  }
};


// ZxKey constructor
ZxKey::ZxKey (byte Normal, byte NormalShifted, byte Graphics, byte GraphicsShifted){
 bNormal = Normal;
 bNormalShifted = NormalShifted;
 bGraphics = Graphics;
 bGraphicsShifted = GraphicsShifted;
}


// Defines entire keyboard, includes ZxKey class.
class ZxKeyBoard {
 private:
  // Setup 4 versions of keyboard states into keyboard.
  // Keyboard mapped for US, might need changing for other configurations eg. UK keyboard.
  ZxKey keyMap[NUM_ROWS][NUM_COLS] = 
  {
   {{'5',KEY_LEFT_ARROW,KEY_F5,KEY_HOME},{'4','%',KEY_F4,KEY_INSERT},{'3','#',KEY_F3,KEY_ESC},{'2','@',KEY_F2,KEY_F12},{'1','!',KEY_F1,KEY_F11}},
   {{'t','_','t','t'},{'r','&','r','r'},{'e','^','e','e'},{'w','`','w','w'},{'q','~','q','q'}},
   {{'6',KEY_DOWN_ARROW,KEY_F6,KEY_PAGE_DOWN},{'7',KEY_UP_ARROW,KEY_F7,KEY_PAGE_UP},{'8',KEY_RIGHT_ARROW,KEY_F8,KEY_END},{'9','9',KEY_F9,'9'},{'0',KEY_BACKSPACE,KEY_F10,'0'}},
   {{'g','\\','g','g'},{'f','}','f','f'},{'d','{','d','d'},{'s',']','s','s'},{'a','[','a','a'}},
   {{'y','|','y','y'},{'u','$','u','u'},{'i','(','i','i'},{'o',')','o','o'},{'p','"','p','p'}},
   {{'v','/','v','v'},{'c','?','c','c'},{'x',';','x','x'},{'z',':','z','z'},{0,0,0,0}},
   {{'h','\'','h','h'},{'j','-','j','j'},{'k','+','k','k'},{'l','=','l','l'},{KEY_RETURN,KEY_RETURN,KEY_RETURN,KEY_RETURN}},
   {{'b','*','b','b'},{'n','<','n','n'},{'m','>','m','m'},{'.',',','.','.'},{' ',KEY_TAB,'£',' '}}
  };
  
 public:
  // Return from ZxKey matching specified state.
  byte bKeyPress(byte bRow, byte bCol, KEYSTATES eKeystate, KEYMODES eKeymode){
   return keyMap[bRow][bCol].KeyPressDetected(eKeystate, eKeymode);
  }
  
  void KeyPressReset(byte bRow, byte bCol){;
   keyMap[bRow][bCol].KeyPressReset();
  }
  
  void KeyDisable(byte bRow, byte bCol){;
   keyMap[bRow][bCol].KeyDisable();
  }
};

Mode Switch & LED Class (zx81modestate.h)

This ones still a work in progress, I'm wanting to add some more functionality to the Mode selection button at some point. Works well enough for the moment.


// *****************************
// ** zx81modestate.h         **
// ** Class Definitions       **
// ** For Mode Switch  & LEDs **
// *****************************

// Defines LED / Mode switch panel and its behaviour.
// Three LEDs are configured to report on keyboard mode and states.
// 
// In STANDARD mode:
//  Left LED on = GRAPHICS state.
//  Middle LED on = FUNCTION state.
//  Right LED on = NORMAL state.
//
// In EMULATOR mode:
//  Left and Right LEDs = on.

class ModeState {
private:
 KEYMODES eKeyboardMode = STANDARD;
 KEYSTATES eKeyboardState = NORMAL;
 byte bModeSwitchPin;
 byte bGraphicsPin;
 byte bFunctionPin;
 byte bModePin;
 
 bool boDebounce = false;
 
 byte SetMode(){
  if (digitalRead(bModeSwitchPin) == HIGH && boDebounce == false) {
   //Serial.println("high ");
   if (eKeyboardMode == STANDARD){
    eKeyboardMode = EMULATOR;
   } else {
    eKeyboardMode = STANDARD;
   }
   
   delay(DEBOUNCE_DELAY_REPEAT);
   boDebounce = true;
  } else if (digitalRead(bModeSwitchPin) == LOW) {
   boDebounce = false;
  }
 }
 
public:
 ModeState (byte, byte, byte, byte);
 
 byte SetState(KEYSTATES eKeystate){
  
  eKeyboardState = eKeystate;
  
  // Check and set mode if Mode switch is pressed
  SetMode();
  
  if (eKeyboardMode != EMULATOR){
   switch (eKeyboardState) {
    
    case FUNCTION:
     digitalWrite(bGraphicsPin, LOW);
     digitalWrite(bFunctionPin, HIGH);
     digitalWrite(bModePin, LOW);
     break;
     
    case GRAPHICS:
     digitalWrite(bGraphicsPin, HIGH);
     digitalWrite(bFunctionPin, LOW);
     digitalWrite(bModePin, LOW);
     break;
     
    default:
     digitalWrite(bGraphicsPin, LOW);
     digitalWrite(bFunctionPin, LOW);
     digitalWrite(bModePin, HIGH);
     eKeyboardState = NORMAL;
     break;
   }
  } else {
   digitalWrite(bGraphicsPin, HIGH);
   digitalWrite(bFunctionPin, LOW);
   digitalWrite(bModePin, HIGH);
   eKeyboardState = NORMAL;
  }
 }
 
 KEYMODES GetMode(){
  return eKeyboardMode;
 }
 
 KEYSTATES GetState(){
  return eKeyboardState;
 }
};

// ModeState constructor.
ModeState::ModeState (byte ModeSwitchPin, byte GraphicsPin, byte FunctionPin, byte ModePin){
 
 bModeSwitchPin = ModeSwitchPin;
 bGraphicsPin = GraphicsPin;
 bFunctionPin = FunctionPin;
 bModePin = ModePin;
 
 pinMode(bModeSwitchPin, INPUT);
 pinMode(bGraphicsPin, OUTPUT);
 pinMode(bFunctionPin, OUTPUT);
 pinMode(bModePin, OUTPUT);
 
}
Read More