diff --git a/CMakeLists.txt b/CMakeLists.txt
index ca01e88..15cb768 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -9,6 +9,7 @@ project(${PROJECT} C CXX ASM)
 OPTION(TIME_SPARE "Set to true to measure idle time" OFF)
 OPTION(OVER_VOLT "Set to true to increase the Pico voltage" OFF)
 OPTION(HDMI_SOUND "Set to true to deliver sound over hdmi" OFF)
+OPTION(PICOZX_LCD "Set to true to enable LCD for PICOZX" OFF)
 
 # Look for board file first in local board directory
 set(PICO_BOARD_HEADER_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/boards)
@@ -36,6 +37,9 @@ endif()
 # e.g. cmake -DPICO_BOARD=picozxboard
 # Set to "picozxrealboard" for vga 222 with csync based on picozxreal board
 # e.g. cmake -DPICO_BOARD=picozxrealboard
+#
+# To enable HDMI sound add -DHDMI_SOUND
+# To enable LCD display in addition to VGA for picozx add -DPICOZX_LCD
 
 set(CMAKE_C_STANDARD 11)
 set(CMAKE_CXX_STANDARD 17)
@@ -80,16 +84,21 @@ set(ZX8X_SOURCES
 set(INI_SOURCES
     inih/ini.c)
 
+set(DISPLAY_COMMON_SOURCES
+    display/display_common.c)
+
 if ((${PICO_BOARD} STREQUAL "dviboard") OR (${PICO_BOARD} STREQUAL "olimexpcboard") OR (${PICO_BOARD} STREQUAL "wspizeroboard"))
     set(DISPLAY_SOURCES
         display/display_dvi.c
         display/tmds_double.S
-        display/tmds_double.h
-        display/tmds_chroma.S
-        display/tmds_chroma.h)
+        display/tmds_chroma.S)
 elseif ((${PICO_BOARD} STREQUAL "lcdws28board") OR (${PICO_BOARD} STREQUAL "lcdmakerboard"))
     set(DISPLAY_SOURCES
         display/display_lcd.c)
+elseif ((${PICO_BOARD} STREQUAL "picozxboard") AND (${PICOZX_LCD}))
+    set(DISPLAY_SOURCES
+        display/display_lcd.c
+        display/display_vga.c)
 else ()
     set(DISPLAY_SOURCES
         display/display_vga.c)
@@ -106,6 +115,7 @@ set(USB_SOURCES
 add_executable(${PROJECT}
                ${ZX8X_SOURCES}
                ${INI_SOURCES}
+               ${DISPLAY_COMMON_SOURCES}
                ${DISPLAY_SOURCES}
                ${USB_SOURCES})
 
@@ -181,8 +191,14 @@ else()
     elseif (${PICO_BOARD} STREQUAL "vgamaker222cboard")
         set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "picozx81_vgamaker222c")
     elseif (${PICO_BOARD} STREQUAL "picozxboard")
-        set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "picozx81_picozx")
-        elseif (${PICO_BOARD} STREQUAL "picozxrealboard")
+        if (${PICOZX_LCD})
+            pico_generate_pio_header(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/display/spi_lcd.pio)
+            target_compile_definitions(${PROJECT} PRIVATE -DPICOZX_LCD)
+            set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "picozx81_picozx_lcd")
+        else ()
+            set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "picozx81_picozx")
+        endif ()
+    elseif (${PICO_BOARD} STREQUAL "picozxrealboard")
         set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "picozx81_picozxreal")
     endif()
 
@@ -211,7 +227,7 @@ target_link_libraries(${PROJECT}
                       tinyusb_host
                       tinyusb_board)
 
-# Cannot use serial port with Pimoroni vga
+# Cannot use serial port with Pimoroni vga or picozx boards
 if ((${PICO_BOARD} STREQUAL "vgaboard") OR (${PICO_BOARD} STREQUAL "picozxboard") OR (${PICO_BOARD} STREQUAL "picozxrealboard"))
     pico_enable_stdio_uart(${PROJECT} 0)
 else()
diff --git a/PicoDVI b/PicoDVI
index 7845b09..ee6d372 160000
--- a/PicoDVI
+++ b/PicoDVI
@@ -1 +1 @@
-Subproject commit 7845b0928f0275806dde5ac4ab425dbafe7c9f2d
+Subproject commit ee6d3728791057a09b4b556e5f47a4613c7f8055
diff --git a/README.md b/README.md
index 084025a..16cf07c 100644
--- a/README.md
+++ b/README.md
@@ -40,6 +40,9 @@
 + [Waveshare Pico-ResTouch-LCD-2.8](https://www.waveshare.com/wiki/Pico-ResTouch-LCD-2.8)
 + [Cytron Maker Pi Pico](https://www.cytron.io/p-maker-pi-pico) with 320 by 240 LCD and RGB222 VGA displays
 + [PICOZX](https://hackaday.io/project/186039-pico-zx-spectrum-128k) All in one board with VGA output, built in keyboard and 9-pin joystick port
++ [PICOZX with LCD](https://hackaday.io/project/186039-pico-zx-spectrum-128k) All in one board with VGA and LCD output, built in keyboard and 9-pin joystick port
++ [PICOZX for ZX-Spectrum case](https://www.pcbway.com/project/shareproject/PICOZX_motherboard_for_ZX_Spectrum_original_case_5bbde8be.html) All in one board with VGA output, designed to be put in a ZX-Spectrum case with keyboard and 9-pin joystick port
+
 ## Examples
 ### Installed in a reproduction case
 The following images are taken with permission from a thread on [SinclairZXWorld](https://sinclairzxworld.com/viewtopic.php?f=3&t=5071&start=20) and show how user `computergui` has used picozx81 together with a case created by user `Spinnetti` to create a replica ZX80
@@ -80,6 +83,22 @@ To the right can be seen a status page, illustrating some of the configurable op
 <img src="images/lcd_3_2.jpg" width="95%" />
 </p>
 
+### Dedicated hardware
+[PICOZX with VGA and LCD output](https://hackaday.io/project/186039-pico-zx-spectrum-128k) running 3D Monster Maze
+
+Pictures supplied by [zzapmort](https://www.ebay.co.uk/usr/zzapmort)
+<p align="middle">
+<img src="images/3dmonster.jpg" width="49%" />
+<img src="images/picozx_lcd.jpg" width="49%" />
+</p>
+
+[PICOZX in a Spectrum case](https://www.pcbway.com/project/shareproject/)
+
+A ZX81 disguised as a Spectrum!
+<p align="middle">
+<img src="images/zx81_spectrum.jpg" width="60%" />
+</p>
+
 ### Chroma 80 and Chroma 81 Emulation
 [ColourAttrModeTest](http://www.fruitcake.plus.com/Sinclair/ZX81/Chroma/Files/Programs/ColourAttrModeTest.zip) and [HiRes ZX-Galaxians](http://zx81.eu5.org/files/soft/toddy/HR-Galax.zip)
 <p align="middle">
@@ -113,6 +132,8 @@ Click on the uf2 name corresponding to your board in the table below to download
 | Cytron Maker + 320x240 LCD | [`picozx81_lcdmaker.uf2`](https://github.com/ikjordan/picozx81/releases/latest/download/picozx81_lcdmaker.uf2)|
 | Cytron Maker + VGA 222 CSYNC | [`picozx81_vgamaker222c.uf2`](https://github.com/ikjordan/picozx81/releases/latest/download/picozx81_vgamaker222c.uf2)|
 | PICOZX | [`picozx81_picozx.uf2`](https://github.com/ikjordan/picozx81/blob/main/uf2/picozx81_picozx.uf2)|
+| PICOZX with LCD | [`picozx81_picozx_lcd.uf2`](https://github.com/ikjordan/picozx81/blob/main/uf2/picozx81_picozx_lcd.uf2)|
+| PICOZX for ZX-Spectrum case | [`picozx81_picozxreal.uf2`](https://github.com/ikjordan/picozx81/blob/main/uf2/picozx81_picozxreal.uf2)|
 
 1. Connect your Board to your display using a VGA or HDMI cable, as appropriate for your board
 2. Connect the Pico to your PC using a USB cable, whilst pressing the **BOOTSEL** button on the Pico. Use the micro USB connector on the Pico, *not* the micro USB cable on your board
@@ -199,6 +220,7 @@ Six extra options apply across all programs and can only be set in the `[default
 | AllFiles| When set, all files are initially displayed when the [Load Menu](#f2---load) is selected. When off only files with extensions `.p`, `.o`, `.81`, `.80` and `.p81` are initially displayed|Off|
 | MenuBorder | Enables a border area (in characters) for the [Load](#f2---load) and [Pause](#f4---pause) menus, useful when using a display with overscan. Range 0 to 2| 1 |
 | NinePinJoystick | When set to `on` Enables reading a 9 pin joystick, if supported in hardware | Off |
+| VGA | When set to `on` enables VGA output for the PICOZX + LCD board | off |
 
 **Notes:**
 1. By default the European ZX81 generates frames slightly faster than 50Hz (50.65 Hz). Setting `FiveSevenSix` to `Match` enables a display mode slightly faster than the 50Hz TV standard, so that better synchronisation between the frame generates by the emulator and frames sent to the monitor can be achieved. If there are issues with a TV or monitor locking to 50.65 Hz, then `FiveSevenSix` can be set to `On` to generate an exact 50 Hz frame rate
@@ -340,6 +362,8 @@ Testing the emulator has been a great way to experience some classic ZX81 games
   + Runs in the right number of frames (1863) when NTSC not selected
 + [Artic Galaxians](https://www.zx81stuff.org.uk/zx81/tape/Galaxians)
   + The one lo-res game that *had* to work correctly!
++ [3D Monster Maze](http://www.zx81stuff.org.uk/zx81/tape/3DMonsterMaze)
+  + An iconic ZX81 game
 + [ZXTEST2](https://sinclairzxworld.com/viewtopic.php?f=6&t=685&p=6120&hilit=zxtest.zip#p6120)
   + This creates an image that periodically moves up and down by 1 pixel on a real ZX81. The emulator replicates this behaviour
 + [Maxtxt](https://bodo4all.fortunecity.ws/zx/maxdemo.html)
@@ -406,13 +430,17 @@ Set `WRX` to `Off` and `CHR128` to `on`
 + [Hot-Z II 64K](http://www.pictureviewerpro.com/hosting/zx81/download/zx81/Hot-Z_II.zip)
     + Requires at least 32kB of RAM. Runs correctly with `M1NOT` set to `On`. As expected, the emulated ZX81 crashes if `M1NOT` is set to `off`
 ### Chroma 81
-+ To enable chroma support set LowRAM on, and Memory to 48kB
+To enable chroma support set LowRAM on, and Memory to 48kB
 + [Celebration](http://www.fruitcake.plus.com/Sinclair/ZX81/NewSoftware/Celebration.htm)
 + [Against The Elements](http://www.fruitcake.plus.com/Sinclair/ZX81/NewSoftware/AgainstTheElements.htm)
++ [Colour 3D Monster Maze](http://www.fruitcake.plus.com/Sinclair/ZX81/Chroma/ChromaInterface_Software_NativeColour.htm)
 + [Attribute Mode Test Program](http://www.fruitcake.plus.com/Sinclair/ZX81/Chroma/ChromaInterface_Software_NativeColour.htm)
-+ [HiRes Galaxians](http://zx81.eu5.org/files/soft/toddy/HR-Galax.zip). Ensure WRX RAM is disabled
-+ [Chroma Slideshow](http://www.fruitcake.plus.com/Sinclair/ZX81/Chroma/ChromaInterface_Software_NativeColour.htm). This works well with `FrameSync` set to `Interlaced`. Note that the program loads a series of image files. A config entry with `FrameSync` set to `Interlaced` needs to be created for each image file
-+ [ROCK CRUSH 80](http://www.fruitcake.plus.com/Sinclair/ZX80/FlickerFree/ZX80_RockCrush80.htm). This is a ZX80 game, but the `.p81` file will also run on the ZX81 
++ [HiRes Galaxians](http://zx81.eu5.org/files/soft/toddy/HR-Galax.zip)
+  + Ensure WRX RAM is disabled
++ [Chroma Slideshow](http://www.fruitcake.plus.com/Sinclair/ZX81/Chroma/ChromaInterface_Software_NativeColour.htm)
+  + This works well with `FrameSync` set to `Interlaced`. Note that the program loads a series of image files. A config entry with `FrameSync` set to `Interlaced` needs to be created for each image file
++ [ROCK CRUSH 80](http://www.fruitcake.plus.com/Sinclair/ZX80/FlickerFree/ZX80_RockCrush80.htm)
+  + This is a ZX80 game, but the `.p81` file will also run on the ZX81
 ### 16kB Demos
 These really show off the capabilities of the ZX81 and are a good test that the emulator is accurate
 #### Without WRX RAM
@@ -482,7 +510,9 @@ This will be named `picozx81_vga.uf2`
 | Pimoroni VGA |`cmake -DPICO_BOARD=vgaboard ..` | `picozx81_vga.uf2`|
 | Custom 332 VGA (similar to MCUME)|`cmake -DPICO_BOARD=vga332board ..`| `picozx81_vga332.uf2`|
 | Cytron Maker based 222 VGA with CSYNC (similar to PICOZX)|`cmake -DPICO_BOARD=vgamaker222cboard ..`| `picozx81_vgamaker222c.uf2`|
-| PICOZX |`cmake -DPICO_BOARD=picozxboard ..`| `picozx81_picozx.uf2`|
+| PICOZX without LCD |`cmake -DPICO_BOARD=picozxboard ..`| `picozx81_picozx.uf2`|
+| PICOZX with LCD |`cmake -DPICOZX_LCD=ON -DPICO_BOARD=picozxboard ..`| `picozx81_picozx_lcd.uf2`|
+| PICOZX in Spectrum case |`cmake -DPICO_BOARD=picozxrealboard ..`| `picozx81_picozxreal.uf2`|
 | Pimoroni DVI with HDMI sound|`cmake -DHDMI_SOUND=ON -DPICO_BOARD=dviboard ..` | `picozx81_dvi_hdmi_sound.uf2`|
 | Olimex PICO DVI with HDMI sound|`cmake -DHDMI_SOUND=ON -DPICO_BOARD=olimexpcboard ..` | `picozx81_olimexpc_hdmi_sound.uf2`|
 | Wavesare PiZero with HDMI sound|`cmake -DHDMI_SOUND=ON -DPICO_BOARD=wspizeroboard ..` | `picozx81_wspizero_hdmi_sound.uf2`|
@@ -495,9 +525,47 @@ This will be named `picozx81_vga.uf2`
 6. Upload the `uf2` file to the pico
 7. Populate a micro SD Card with files you wish to run. Optionally add `config.ini` files to the SD Card. See [here](examples) for examples of config files
 
-## Using the Cytron Maker with a LCD
+# Extra Information
+## General
++ The original intention of the emulator was to provide an authentic '80s feel. It emulated the hardware that was advertised in the early '80s i.e. QS UDG, Sound, joystick, hi-res mono graphics. It has now been extended to provide emulation of some of the amazing ZX81 developments of recent years, such as [Chroma 81](http://www.fruitcake.plus.com/Sinclair/ZX81/Chroma/ChromaInterface.htm). It supports the loading and saving of memory blocks, using a syntax similar to ZXpand
++ The ["Big Bang"](https://www.sinclairzxworld.com/viewtopic.php?t=2986) ROM is supported, as this accelerates BASIC execution, and runs on the original ZX81 hardware
++ Program debug support is limited to that provided by the ZX81 "in period", i.e. non-existent. It is recommended that one of the PC or Linux based ZX81 emulators with single step and breakpoint support are used to debug Z80 assembly programs
++ To achieve a full speed emulation the Pico is overclocked to 250MHz (640x480) and 270MHz (720x576). There is a very slight risk that this may damage the Pico. However many other applications run the Pico at this frequency. By default the stock voltage is used (1.1V), this has been successfully tested on multiple Picos. If the emulator appears unstable it can be built to use 1.2V, add `-DOVER_CLOCK` to the cmake command
++ The Pico only has 1 USB port. The Pimoroni, Olimex and Waveshare PiZero boards can be powered through a second on board USB power connector, allowing a keyboard to be connected to the Pico using an OTG adaptor
++ To connect more than one peripheral (e.g. a keyboard and joystick) at the same time, a powered USB OTG hub is required. These 3 hubs have been successfully tested. [1](https://www.amazon.co.uk/dp/B083WML1XB), [2](https://www.amazon.co.uk/dp/B078M3Z84Z), [3](https://www.amazon.co.uk/dp/B07Z4RHJ2D). Plug the hub directly into the USB port on the Pico. The USB-A connector on the PICOZX boards can also be used  
+**Note:** Testing has shown that all of these hubs can support OTG and power delivery to the Pico simultaneously
++ On rare occasion, some USB keyboards and joysticks fail to be detected when connected via powered hubs. A re-boot of the Pico often results in successful detection
+## Board Specific
+### Waveshare PiZero
++ The Waveshare PiZero has two USB-C connectors. Use the connector closest to the HDMI connector to provide power. Connect a keyboard to the other USB port using an OTG cable. If necessary, a female micro USB to male USB C adaptor can be used
++ The board can be back powered by some TVs. This can cause the board to not start correctly. If this happens either connect the power before attaching the HDMI cable, or press the reset button on the board
+### PicoMiteVGA
++ The PicoMiteVGA board has a PS/2 keyboard socket. Currently this is not supported, a USB keyboard must be used
++ PicomiteVGA only supports 1 level of Red and Blue, so it cannot display the full range of colours that Chroma can generate
++ Some versions of the PicoMiteVGA board have a jumper to select between RGB and GRN mode. Select RGB mode
+### PICOZX
++ PICOZX has a bank of 4 extra keys below the SD Card. These act as function keys. `Menu` maps to `F1`, `Reload` to `F2` etc. If shift is pressed 4 is added to the function number. e.g. `shift` + `Menu` gives `F5` (and so displays the keyboard)
++ The PICOZX + LCD code generates LCD outpur by default. To enable VGA output either hold down the the Fire button at start-up, or enable `VGA` in the config file
++ The PICOZX + LCD shares outputs with VGA. If the board is configured for VGA output, the intensity of the backlight of the LCD will vary with the contents of the VGA display
++ The PICOZX for the ZX-Spectrum case has two extra buttons on the back (in addition to a reset button). Without shift these two buttons will generate `F2` and `F5`. With shift they will generate `F3` and `F6`
++ Use the double shift mechanism to access all menus when using the PICOZX family
++ To enter BOOTSEL mode on the PICOZX for the ZX-Spectrum press and hold the `R` key then press the `F2` menu key. This allows new firmware to be loaded without needing to press the BOOTSEL key on the pico
+### Waveshare Pico-ResTouch-LCD-2.8
++ The Waveshare Pico-ResTouch-LCD-2.8 board has a touch controller, but the emulator does not support its use
+### Olimex RP2040-PICO-PC
++ The Olimex RP2040-PICO-PC board does not supply 5v to DVI pin 18. This may result in the board not being detected by some TVs. If necessary short the SJ1 connector so 5V is supplied
++ If SJ1 is shorted the board may be back powered by some TVs. This can cause the board to not start correctly. If this happens either connect the power before attaching the HDMI cable, or press the reset button on the board
+
+### Cytron Maker
++ The Cytron Maker Pi Pico has an onboard piezo buzzer. The audio quality is poor, but it can be used instead of speakers. If the buzzer is enabled (using the switch on the maker board) ensure that ACB Stereo is disabled
++ The vgamaker222c build requires the following connections:
+<p align="middle">
+<img src="images/rgb_222_vga.png" width="50%" />
+</p>
+
+#### Use of LCD displays
 The Maker board can be used with a range of 320 by 240 LCDs, controlled over the SPI bus, with controllers from either the ILI9341 or ST7789 families. The boards are configured using entries in the `default` section of the config file
-### Wiring
+
 The LCD should be connected to the Maker board as follows:
 | Function | Name |Pico GPIO Pin|
 | --- | --- | --- |
@@ -512,7 +580,7 @@ The LCD should be connected to the Maker board as follows:
 
 Touchscreen functionality is not supported. Any pins used for the touchscreen do not need to be connected
 
-### LCD Configuration Options
+## LCD Configuration Options
 All options are set in the `[default]` section of the `config.ini` file in the root directory of the SD Card.
 | Item | Description | Default Value |
 | --- | --- | --- |
@@ -526,7 +594,8 @@ All options are set in the `[default]` section of the `config.ini` file in the r
 
  **Notes:**
  1. If the configuration appears correct for a display, but no image appears, or the image is not stable, it could be that the display cannot support the SPI bus speed required to display every frame, or that cross talk is occuring between the wires connecting the display. In this case set `LCDFrameSkip = True`
- 2. If `LCDFrameSkip` equals `True`, then if `FrameSync` is set to `Interlaced` it will be interpreted as `On`
+ 2. It is recommended to use the Cytron maker board with the Pico pre-soldered, as this version can support higher bus speeds. If the version with a socket for a Pico is used, then the LCD display may not function correctly unless `LCDFrameSkip` is set to `True`
+ 3. If `LCDFrameSkip` equals `True`, then if `FrameSync` is set to `Interlaced` it will be interpreted as `On`
 
 ### Configuration of Tested LCDs
 
@@ -564,24 +633,6 @@ To enable the nine pin joystick set `NinePinJoystick` to `On` in the `[default]`
 
 It is not necessary to create a `NinePinJoystick` entry to use the joystick port on the picozx board
 
-# Extra Information
-+ The original intention of the emulator was to provide an authentic '80s feel. It emulated the hardware that was advertised in the early '80s i.e. QS UDG, Sound, joystick, hi-res mono graphics. It has now been extended to provide emulation of some of the amazing ZX81 developments of recent years, such as [Chroma 81](http://www.fruitcake.plus.com/Sinclair/ZX81/Chroma/ChromaInterface.htm). It supports the loading and saving of memory blocks, using a syntax similar to ZXpand
-+ The ["Big Bang"](https://www.sinclairzxworld.com/viewtopic.php?t=2986) ROM is supported, as this accelerates BASIC execution, and runs on the original ZX81 hardware
-+ Program debug support is limited to that provided by the ZX81 "in period", i.e. non-existent. It is recommended that one of the PC or Linux based ZX81 emulators with single step and breakpoint support are used to debug Z80 assembly programs
-+ To achieve a full speed emulation the Pico is overclocked to 250MHz (640x480) and 270MHz (720x576). There is a very slight risk that this may damage the Pico. However many other applications run the Pico at this frequency. By default the stock voltage is used (1.1V), this has been successfully tested on multiple Picos. If the emulator appears unstable it can be built to use 1.2V, add `-DOVER_CLOCK` to the cmake command
-+ The Pico only has 1 USB port. The Pimoroni and Olimex boards can be powered through a second on board USB power connector, allowing a keyboard to be connected to the Pico using an OTG adaptor
-+ To connect more than one peripheral (e.g. a keyboard and joystick) at the same time, a powered USB OTG hub is required. These 3 hubs have been successfully tested. [1](https://www.amazon.co.uk/dp/B083WML1XB), [2](https://www.amazon.co.uk/dp/B078M3Z84Z), [3](https://www.amazon.co.uk/dp/B07Z4RHJ2D). Plug the hub directly into the USB port on the Pico, not the USB power connector on the Pimoroni board  
-**Note:** Testing has shown that all of these hubs can support OTG and power delivery to the Pico simultaneously
-+ The Waveshare PiZero has two USB-C connectors. Use the connector closest to the HDMI connector to provide power. Connect a keyboard to the other USB port using an OTG cable. If necessary, a female micro USB to male USB C adaptor can be used
-+ On rare occasion, some USB keyboards and joysticks fail to be detected when connected via powered hubs. A re-boot of the Pico often results in successful detection
-+ The PicoMiteVGA board has a PS/2 keyboard socket. Currently this is not supported, a USB keyboard must be used
-+ PicomiteVGA only supports 1 level of Red and Blue, so it cannot display the full range of colours that Chroma can generate
-+ Some versions of the PicoMiteVGA board have a jumper to select between RGB and GRN mode. Select RGB mode
-+ The Waveshare Pico-ResTouch-LCD-2.8 board has a touch controller, but the emulator does not support its use
-+ The Olimex RP2040-PICO-PC board does not supply 5v to DVI pin 18. This may result in the board not being detected by some TVs. If necessary short the SJ1 connector so 5V is supplied
-+ The Cytron Maker Pi Pico has an onboard piezo buzzer. The audio quality is poor, but it can be used instead of speakers. If the buzzer is enabled (using the switch on the maker board) ensure that ACB Stereo is disabled 
-+ In an ideal world the latest versions of the excellent sz81 or EightyOne emulators would have been ported. An initial port showed that they are too processor intensive for an (overclocked) ARM M0+. An earlier version of sz81 ([2.1.8](https://github.com/ikjordan/sz81_2_1_8)) was used as a basis, with some Z80 timing corrections and back porting of the 207 tstate counter code from the latest sz81 (2.3.12). See [here](#applications-tested) for a list of applications tested
-
 # Developer Notes
 ## Acknowledgements
 One intention of this project was to show what can be quickly achieved by leveraging other open source projects. The following excellent Open source projects have been used for source code and inspiration:
@@ -605,12 +656,15 @@ To access the function menus from a ZX80/81 keyboard the `doubleshift` configura
 The picozx board does support keyboard and joystick. This is achieved by using every available GPIO pin, and using VGA222 with CSYNC, together with mono audio
 
 ## Performance and constraints
+In an ideal world the latest versions of the excellent sz81 or EightyOne emulators would have been ported. An initial port showed that they are too processor intensive for an (overclocked) ARM M0+. An earlier version of sz81 ([2.1.8](https://github.com/ikjordan/sz81_2_1_8)) was used as a basis, with some Z80 timing corrections and back porting of the 207 tstate counter code from the latest sz81 (2.3.12). See [here](#applications-tested) for a list of applications tested
+
 The initial port from sz81 2.3.12 onto the Pico ran at approximately 10% of real time speed. Use of the Z80 emulator originally written for xz80 by Ian Collier, plus optimisation of the ZX81 memory access, display and plot routines allows the emulator to run at 100% of real time speed. The display of a full 320 by 240 image in real time (e.g. [MaxDemo](https://bodo4all.fortunecity.ws/zx/maxdemo.html)) uses approximately 90% of the available CPU clock cycles. An overclock to 250MHz is required
 
 Corrections to the tstate timings were made for `ld a,n; ld c,n; ld e,n; ld l,n; set n,(hl); res n,(hl);`
 ## Possible Future Developments
 + Support for USB gamepads as well as joysticks
 + Add vsync (TV) based sound
++ Emulate `Real-time` loading and saving from tape, with sound and graphic effects
 + Move to a Pi Zero to greatly increase processing power and use [circle](https://github.com/rsta2/circle) for fast boot times
 ## Comparison to MCUME
 [MCUME](https://github.com/Jean-MarcHarvengt/MCUME/) demonstrated that a Raspberry Pi Pico based ZX80/81 emulator was feasible. The custom VGA RGB 332 board type is similar to the hardware required for MCUME
@@ -622,7 +676,7 @@ This emulator offers the following over MCUME:
 + Ability to load a program without reset
 + Support for Hi-res and pseudo Hi-res graphics
 + Support for multiple DVI, VGA and LCD boards
-+ Support for Chroma 80 and Chroma 81 
++ Support for Chroma 80 and Chroma 81
 + Support for programs which use more than 32 columns or 24 rows of characters
 + ZonX and QS Sound emulation
 + Emulated QS UDG
diff --git a/boards/picozxboard.h b/boards/picozxboard.h
index 999ba54..711ea72 100644
--- a/boards/picozxboard.h
+++ b/boards/picozxboard.h
@@ -70,6 +70,35 @@
 #define CP_SHIFT 19
 #define CP_JOIN(a) ((a & 0xF) | ((a >> 3) & (0x7 << 4)))
 #define TP 2800
+
+#ifdef PICOZX_LCD
+#define PICOZXBOARD_LCD_DC_PIN  6
+#define PICOZXBOARD_LCD_CS_PIN  5
+#define PICOZXBOARD_LCD_CMD_PIN 3     // MOSI
+#define PICOZXBOARD_LCD_BL_PIN  4
+#define PICOZXBOARD_LCD_CLK_PIN 2
+
+#ifndef PICO_LCD_DC_PIN
+#define PICO_LCD_DC_PIN PICOZXBOARD_LCD_DC_PIN
+#endif
+
+#ifndef PICO_LCD_CS_PIN
+#define PICO_LCD_CS_PIN PICOZXBOARD_LCD_CS_PIN
+#endif
+
+#ifndef PICO_LCD_BL_PIN
+#define PICO_LCD_BL_PIN PICOZXBOARD_LCD_BL_PIN
+#endif
+
+#ifndef PICO_LCD_CMD_PIN
+#define PICO_LCD_CMD_PIN PICOZXBOARD_LCD_CMD_PIN
+#endif
+
+#ifndef PICO_LCD_CLK_PIN
+#define PICO_LCD_CLK_PIN PICOZXBOARD_LCD_CLK_PIN
+#endif
+#endif
+
 #define PICO_PICOZX_BOARD
 
 // Maker board has a Pico on it, so default anything we haven't set above
diff --git a/boards/wspizeroboard.h b/boards/wspizeroboard.h
index 6b33f58..dbb063a 100644
--- a/boards/wspizeroboard.h
+++ b/boards/wspizeroboard.h
@@ -7,8 +7,7 @@
 #define _BOARDS_WSPIZEROBOARD_H
 
 // For board detection
-#define RASPBERRYPI_BOARDS_WSPIZEROBOARD
-
+#define WAVESHARE_BOARDS_PIZEROBOARD
 
 #define WSPIZEROBOARD_SD_CLK_PIN 18
 #define WSPIZEROBOARD_SD_CMD_PIN 19
@@ -58,6 +57,10 @@
 #define PICO_AUDIO_PWM_R_PIN WSPIZEROBOARD_PWM_R_PIN
 #endif
 
+#ifndef PICO_FLASH_SIZE_BYTES
+#define PICO_FLASH_SIZE_BYTES (16 * 1024 * 1024)
+#endif
+
 #define NINEPIN_JOYSTICK
 
 #define NINEPIN_UP      11
diff --git a/buildall b/buildall
index 4ae42ad..d65e286 100755
--- a/buildall
+++ b/buildall
@@ -20,6 +20,17 @@ build_uf2_hdmi()
   make -j4
   cp ./${uf2} ../uf2/${uf2}
 }
+
+build_uf2_lcd()
+{
+  board=$1"board .."
+  uf2="picozx81_"$1"_lcd.uf2"
+
+  cmake -DPICOZX_LCD=ON -DPICO_BOARD=${board}
+  make -j4
+  cp ./${uf2} ../uf2/${uf2}
+}
+
 # Create the directories
 mkdir -p build
 mkdir -p uf2
@@ -39,3 +50,4 @@ build_uf2 picozxreal
 build_uf2_hdmi dvi
 build_uf2_hdmi olimexpc
 build_uf2_hdmi wspizero
+build_uf2_lcd picozx
diff --git a/display/display.h b/display/display.h
index 6efa14e..e3bf607 100644
--- a/display/display.h
+++ b/display/display.h
@@ -32,6 +32,10 @@ typedef struct
     } info;
 } DisplayExtraInfo_T;
 
+#ifdef PICOZX_LCD
+extern bool useLCD;
+#endif
+
 /*
     fivesevensix    if true request resolution 720x576, else 640x480
     match           if true request refresh frequency of PAL ZX81 (50.65 Hz)
@@ -41,10 +45,15 @@ typedef struct
     stridebit       The actual number of bits between start of adjacent lines (pixel width + bufferbits)
     info            Extra information for HDMI audio and LCD displays
  */
+
+/* Display specific, defined for all display types */
 extern uint displayInitialise(bool fiveSevenSix, bool match, uint16_t minBuffByte, uint16_t* pixelWidth,
                               uint16_t* pixelHeight, uint16_t* strideBit, DisplayExtraInfo_T* info);
 extern void displayStart(void);
 
+extern bool displayShowKeyboard(bool zx81);
+
+/* Common */
 extern void displayGetFreeBuffer(uint8_t** buff);
 extern void displayBuffer(uint8_t* buff, bool sync, bool free, bool chroma);
 extern void displayGetCurrentBuffer(uint8_t** buff);
@@ -55,7 +64,6 @@ extern void displaySetInterlace(bool on);
 extern void displayBlank(bool black);
 extern bool displayIsBlank(bool* isBlack);
 
-extern bool displayShowKeyboard(bool zx81);
 extern bool displayHideKeyboard(void);
 
 #ifdef PICO_SPI_LCD_SD_SHARE
@@ -66,6 +74,7 @@ extern void displayGrantSPIBus(void);
 #ifdef SOUND_HDMI
 void getAudioRing(audio_ring_t** ring);
 #endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/display/display_common.c b/display/display_common.c
index 19dbde6..af18ea9 100644
--- a/display/display_common.c
+++ b/display/display_common.c
@@ -1,52 +1,59 @@
 /*
- * Common display code for VGA and DVI
+ * Common display code for VGA, DVI and LCD
  */
 #include <stdio.h>
-#include "hardware/structs/bus_ctrl.h"
-#define MAX_FREE 4
-#define MAX_PEND 2
-
+#include <stdlib.h>
+#include "pico/multicore.h"
+#include "display.h"
+#include "display_priv.h"
+#include "zx80bmp.h"
+#include "zx81bmp.h"
+
+// Structure for chroma buffers
 typedef struct
 {
     uint8_t* buff;
     bool     used;
 } chroma_t;
 
+mutex_t next_frame_mutex;
+semaphore_t display_initialised;
+
+bool blank = true;
+uint16_t blank_colour = BLACK;
+
+uint8_t* curr_buff = 0;      // buffer being displayed
+uint8_t* cbuffer = 0;        // Chroma buffer
+
+const KEYBOARD_PIC* keyboard = &ZX81KYBD;
+bool showKeyboard = false;
+
+// Number of display buffers
+#define MAX_FREE 4
+#define MAX_PEND 2
+
 // Note: Need 4 buffers, as the ZX81 produces rates at greater than 50 Hz
 // so 2 frames can be created in one time slice, and 1 emulated time slice
 // may complete in 14ms, therefore a backlog of 2 frames is valid, no_skip
 // when nominally frame matched
-static  bool blank = true;
-static  uint16_t blank_colour = BLACK;
-
-static semaphore_t display_initialised;
-static mutex_t next_frame_mutex;
 
-static uint8_t* curr_buff = 0;      // buffer being displayed
-static uint8_t* last_buff = 0;      // previously displayed buffer (interlace mode only)
 static uint8_t* pend_buff[MAX_PEND] = {0, 0};           // Buffers queued for display
 static uint8_t* free_buff[MAX_FREE] = {0, 0, 0, 0};     // Buffers available to be claimed
-
-static uint8_t* cbuffer = 0;        // Chroma buffer
-
-static uint8_t* index_to_display[MAX_FREE] = {0, 0, 0, 0};
 static chroma_t chroma[MAX_FREE] = { {0, false}, {0, false}, {0,false}, {0,false} };
+static uint8_t* index_to_display[MAX_FREE] = {0, 0, 0, 0};
+static uint8_t* last_buff = 0;      // previously displayed buffer (interlace mode only)
+
 static uint8_t free_count = 0;
 static uint8_t pend_count = 0;
-static uint16_t stride = 0;
 static bool interlace = false;
 static bool no_skip = false;
 
-static bool showKeyboard = false;
-
 //
 // Private interface
 //
-static void core1_main();
 static inline void freeAllPending(void);
 static inline void freeLast(void);
 static inline void swapCurrAndLast(void);
-static inline void newFrame(void);
 static inline void displayGetChromaBufferUsed(uint8_t** chroma_buff, uint8_t* buff);
 static inline void set_chroma_used(uint8_t* buff, bool used);
 
@@ -54,6 +61,34 @@ static inline void set_chroma_used(uint8_t* buff, bool used);
 // Public functions
 //
 
+void displayAllocateBuffers(uint16_t minBuffByte, uint16_t stride, uint16_t height)
+{
+    // Allocate the buffers
+    for (int i=0; i<MAX_FREE; ++i)
+    {
+        free_buff[i] = (uint8_t*)malloc(minBuffByte + stride * height)
+                         + minBuffByte;
+
+        // Store original index, so that can map a chroma buffer, if necessary
+        index_to_display[i] = free_buff[i];
+    }
+
+    // Allocate chroma buffers
+    for (int i=0; i<MAX_FREE; ++i)
+    {
+        chroma[i].buff = (uint8_t*)malloc(minBuffByte + stride * height)
+                        + minBuffByte;
+
+        if (!chroma[i].buff)
+        {
+            printf("Insufficient memory for chroma - aborting\n");
+            exit(-1);
+        }
+    }
+    free_count = MAX_FREE;
+
+}
+
 /* Set the interlace state */
 void __not_in_flash_func(displaySetInterlace)(bool on)
 {
@@ -270,21 +305,28 @@ bool displayHideKeyboard(void)
     return ret;
 }
 
-//
-// Private functions
-//
 void displayStartCommon(void)
 {
     // create a semaphore to be posted when video init is complete
     sem_init(&display_initialised, 0, 1);
 
     // launch all the video on core 1, so it isn't affected by USB handling on core 0
+#ifdef PICOZX_LCD
+    if (useLCD)
+    {
+        multicore_launch_core1(core1_main_lcd);
+    }
+    else
+    {
+        multicore_launch_core1(core1_main_vga);
+    }
+#else
     multicore_launch_core1(core1_main);
-
+#endif
     sem_acquire_blocking(&display_initialised);
 }
 
-static inline void __not_in_flash_func(newFrame)(void)
+void __not_in_flash_func(newFrame)(void)
 {
     mutex_enter_blocking(&next_frame_mutex);
     if (!blank)
@@ -367,6 +409,10 @@ static inline void __not_in_flash_func(newFrame)(void)
     mutex_exit(&next_frame_mutex);
 }
 
+//
+// Private functions
+//
+
 /* Indicate whether chroma is enabled for the supplied pixel buffer */
 static inline void set_chroma_used(uint8_t* buff, bool used)
 {
@@ -408,3 +454,75 @@ static inline void __not_in_flash_func(swapCurrAndLast)(void)
         last_buff = tmp_buff;
     }
 }
+
+// Allow selection between VGA and LCD for ZXPICO board
+#ifdef PICOZX_LCD
+#undef WHITE
+#undef BLUE
+#undef YELLOW
+#undef RED
+#undef BLACK
+
+#define WHITE  0xfff
+#define BLUE   0x00f
+#define YELLOW 0xff0
+#define RED    0xf00
+#define BLACK  0x000
+
+#undef ZX80_KEYBOARD
+#define ZX80_KEYBOARD ZX80KYBD_LCD
+#undef ZX81_KEYBOARD
+#define ZX81_KEYBOARD ZX81KYBD_LCD
+#include "zx80bmp.h"
+#include "zx81bmp.h"
+
+extern uint displayInitialiseLCD(bool fiveSevenSix, bool match, uint16_t minBuffByte, uint16_t* pixelWidth,
+                                 uint16_t* pixelHeight, uint16_t* strideBit, DisplayExtraInfo_T* info);
+extern void displayStartLCD(void);
+extern bool displayShowKeyboardLCD(bool zx81);
+
+extern uint displayInitialiseVGA(bool fiveSevenSix, bool match, uint16_t minBuffByte, uint16_t* pixelWidth,
+                                 uint16_t* pixelHeight, uint16_t* strideBit, DisplayExtraInfo_T* info);
+extern void displayStartVGA(void);
+extern bool displayShowKeyboardVGA(bool zx81);
+
+uint displayInitialise(bool fiveSevenSix, bool match, uint16_t minBuffByte, uint16_t* pixelWidth,
+                       uint16_t* pixelHeight, uint16_t* strideBit, DisplayExtraInfo_T* info)
+{
+    if (useLCD)
+    {
+        return displayInitialiseLCD(fiveSevenSix, match, minBuffByte, pixelWidth,
+                                    pixelHeight, strideBit, info);
+    }
+    else
+    {
+        return displayInitialiseVGA(fiveSevenSix, match, minBuffByte, pixelWidth,
+                                    pixelHeight, strideBit, info);
+    }
+}
+
+void displayStart(void)
+{
+    if (useLCD)
+    {
+        return displayStartLCD();
+    }
+    else
+    {
+        return displayStartVGA();
+    }
+}
+
+bool displayShowKeyboard(bool zx81)
+{
+    if (useLCD)
+    {
+        return displayShowKeyboardLCD(zx81);
+    }
+    else
+    {
+        return displayShowKeyboardVGA(zx81);
+    }
+}
+
+#endif
diff --git a/display/display_dvi.c b/display/display_dvi.c
index 966ac47..62d243f 100644
--- a/display/display_dvi.c
+++ b/display/display_dvi.c
@@ -2,6 +2,7 @@
 #include "pico/stdlib.h"
 #include "hardware/clocks.h"
 #include "hardware/irq.h"
+#include "hardware/structs/bus_ctrl.h"
 #include "pico/multicore.h"
 #include "dvi.h"
 #include "dvi_serialiser.h"
@@ -9,8 +10,7 @@
 #include "tmds_double.h"
 #include "tmds_chroma.h"
 #include "display.h"
-#include "zx80bmp.h"
-#include "zx81bmp.h"
+#include "display_priv.h"
 
 #define DVI_TIMING dvi_timing_720x576p_51hz
 #define __dvi_const(x) __not_in_flash_func(x)
@@ -46,11 +46,11 @@ static uint16_t PIXEL_WIDTH = 0;
 static uint16_t CHARACTER_WIDTH = 0;
 static uint16_t HEIGHT = 0;
 
-static const KEYBOARD_PIC* keyboard = &ZX81KYBD;
 static uint16_t keyboard_x = 0;
 static uint16_t keyboard_y = 0;
 static uint16_t keyboard_right = 0;
 static uint16_t keyboard_to_fill = 0;
+static uint16_t stride = 0;
 
 #ifdef SOUND_HDMI
 static const int hdmi_n[3] = {4096, 6272, 6144};
@@ -59,8 +59,6 @@ static uint16_t  rate  = 32000;     // Default audio rate
 audio_sample_t      audio_buffer[AUDIO_BUFFER_SIZE];
 #endif
 
-#include "display_common.c"
-
 //
 // Private interface
 //
@@ -93,32 +91,10 @@ uint displayInitialise(bool fiveSevenSix, bool match, uint16_t minBuffByte, uint
     CHARACTER_WIDTH = PIXEL_WIDTH >> 3;
 
     // Is padding requested? For DVI can pad a single byte
-    stride = minBuffByte + (PIXEL_WIDTH >> 3);
+    stride = minBuffByte + CHARACTER_WIDTH;
 
     // Allocate the buffers
-    for (int i=0; i<MAX_FREE; ++i)
-    {
-        free_buff[i] = (uint8_t*)malloc(minBuffByte + stride * HEIGHT)
-                         + minBuffByte;
-
-        // Store original index, so that can map a chroma buffer, if necessary
-        index_to_display[i] = free_buff[i];
-    }
-
-    // Allocate chroma buffers
-    for (int i=0; i<MAX_FREE; ++i)
-    {
-        chroma[i].buff = (uint8_t*)malloc(minBuffByte + stride * HEIGHT)
-                        + minBuffByte;
-
-        if (!chroma[i].buff)
-        {
-            printf("Insufficient memory for chroma - aborting\n");
-            exit(-1);
-        }
-    }
-
-    free_count = MAX_FREE;
+    displayAllocateBuffers(minBuffByte, stride, HEIGHT);
 
     // Return the values
     *pixelWidth = PIXEL_WIDTH;
@@ -297,7 +273,7 @@ static void __not_in_flash_func(render_loop)()
     }
 }
 
-static void core1_main()
+void core1_main()
 {
     dvi_register_irqs_this_core(&dvi0, DMA_IRQ_0);
     dvi_start(&dvi0);
diff --git a/display/display_lcd.c b/display/display_lcd.c
index 52d20e7..ce19012 100644
--- a/display/display_lcd.c
+++ b/display/display_lcd.c
@@ -9,13 +9,11 @@
 #include "sdcard.h"
 
 #include "display.h"
-#include "zx80bmp.h"
-#include "zx81bmp.h"
+#include "display_priv.h"
 
 #define PIXEL_WIDTH 320
 #define HEIGHT      240
 
-static const KEYBOARD_PIC* keyboard = &ZX81KYBD;
 static uint16_t keyboard_x = 0;
 static uint16_t keyboard_y = 0;
 static uint16_t keyboard_right = 0;
@@ -41,9 +39,18 @@ static bool rotate = false;     // Rotate display by 180 degrees
 static bool reflect = false;    // Change horizontal scan direction
 static bool bgr = false;        // use bgr instead of rgb
 
+static uint16_t stride;
+
+// Defined here to allow for case where VGA and LCD on same hardware
+#define BLACK_LCD  0x0000
+#define BLUE_LCD   0x000f
+#define RED_LCD    0x0f00
+#define YELLOW_LCD 0x0ff0
+#define WHITE_LCD  0x0fff
+
 // Do not make const - as want to keep in RAM
 static uint16_t colour_table[16] = {
-    0x0000,
+    BLACK_LCD,
     0x000a,
     0x0a00,
     0x0a0a,
@@ -51,18 +58,16 @@ static uint16_t colour_table[16] = {
     0x00aa,
     0x0aa0,
     0x0aaa,
-    0x0000,
-    0x000f,
-    0x0f00,
+    BLACK_LCD,
+    BLUE_LCD,
+    RED_LCD,
     0x0f0f,
     0x00f0,
     0x00ff,
-    0x0ff0,
-    0x0fff
+    YELLOW_LCD,
+    WHITE_LCD
 };
 
-#include "display_common.c"
-
 //
 // Private interface
 //
@@ -79,7 +84,13 @@ static inline void lcd_start_pixels(void);
 //
 #include <stdio.h>
 
-uint displayInitialise(bool fiveSevenSix, bool match, uint16_t minBuffByte, uint16_t* pixelWidth,
+#ifndef PICOZX_LCD
+uint displayInitialise
+#else
+bool useLCD = false;
+uint displayInitialiseLCD
+#endif
+                      (bool fiveSevenSix, bool match, uint16_t minBuffByte, uint16_t* pixelWidth,
                        uint16_t* pixelHeight, uint16_t* strideBit, DisplayExtraInfo_T* info)
 {
     if (info)
@@ -113,29 +124,7 @@ uint displayInitialise(bool fiveSevenSix, bool match, uint16_t minBuffByte, uint
     stride = minBuffByte + (PIXEL_WIDTH >> 3);
 
     // Allocate the buffers
-    for (int i=0; i<MAX_FREE; ++i)
-    {
-        free_buff[i] = (uint8_t*)malloc(minBuffByte + stride * HEIGHT)
-                         + minBuffByte;
-
-        // Store original index, so that can map a chroma buffer, if necessary
-        index_to_display[i] = free_buff[i];
-    }
-
-    // Allocate chroma buffers
-    for (int i=0; i<MAX_FREE; ++i)
-    {
-        chroma[i].buff = (uint8_t*)malloc(minBuffByte + stride * HEIGHT)
-                        + minBuffByte;
-
-        if (!chroma[i].buff)
-        {
-            printf("Insufficient memory for chroma - aborting\n");
-            exit(-1);
-        }
-    }
-
-    free_count = MAX_FREE;
+    displayAllocateBuffers(minBuffByte, stride, HEIGHT);
 
     // Return the values
     *pixelWidth = PIXEL_WIDTH;
@@ -148,7 +137,11 @@ uint displayInitialise(bool fiveSevenSix, bool match, uint16_t minBuffByte, uint
     return CLOCK_SPEED_KHZ;
 }
 
+#ifndef PICOZX_LCD
 void displayStart(void)
+#else
+void displayStartLCD(void)
+#endif
 {
     printf("Invert %s\n", invert ? "True": "false");
     printf("Skip %s\n", skip ? "True": "false");
@@ -159,13 +152,26 @@ void displayStart(void)
     displayStartCommon();
 }
 
+#ifndef PICOZX_LCD
 bool displayShowKeyboard(bool zx81)
+#else
+bool displayShowKeyboardLCD(bool zx81)
+#endif
 {
     bool previous = showKeyboard;
 
     if (!showKeyboard)
     {
-        keyboard = zx81 ? &ZX81KYBD : &ZX80KYBD;
+#ifdef PICOZX_LCD
+        if (useLCD)
+        {
+            keyboard = zx81 ? &ZX81KYBD_LCD : &ZX80KYBD_LCD;
+        }
+        else
+#endif
+        {
+            keyboard = zx81 ? &ZX81KYBD : &ZX80KYBD;
+        }
         keyboard_x = (PIXEL_WIDTH - keyboard->width)>>1;
         keyboard_y = (HEIGHT - keyboard->height)>>1;
         keyboard_right = (keyboard_x & 0xffe0) + keyboard->width;
@@ -276,8 +282,6 @@ static inline void lcd_start_pixels(void)
 
 static void __not_in_flash_func(render_loop)()
 {
-    lcd_start_pixels();
-
     while (true)
     {
         sem_acquire_blocking(&frame_sync);
@@ -290,6 +294,8 @@ static void __not_in_flash_func(render_loop)()
             // Ensure LCD has bus
             gpio_put(PICO_LCD_CS_PIN, 0);
 #endif
+            lcd_start_pixels();
+
             // 1 pixel generates a 12 bit word - so 2 pixels are 3 bytes
             for (uint y = 0; y < HEIGHT; ++y)
             {
@@ -304,8 +310,11 @@ static void __not_in_flash_func(render_loop)()
                     if (blank)
                     {
                         // 32 pixels of blank
+#ifndef PICOZX_LCD
                         uint32_t twoblank = (blank_colour << 12) | blank_colour;
-
+#else
+                        uint32_t twoblank = (blank_colour == WHITE) ? (WHITE_LCD << 12) | WHITE_LCD : (BLACK_LCD << 12) | BLACK_LCD;
+#endif
                         for (int i=0; i<(keyboard_x>>1); ++i)
                         {
                             spi_lcd_put((twoblank >> 16) & 0xff);
@@ -342,8 +351,8 @@ static void __not_in_flash_func(render_loop)()
                         for (int x = 0; x < (keyboard_x >> 3); ++x)
                         {
                             uint8_t byte = linebuf[x];
-                            uint16_t foreground = cbuff ? colour_table[clinebuf[x] & 0xf] : BLACK;
-                            uint16_t background = cbuff ? colour_table[clinebuf[x] >> 4] : WHITE;
+                            uint16_t foreground = cbuff ? colour_table[clinebuf[x] & 0xf] : BLACK_LCD;
+                            uint16_t background = cbuff ? colour_table[clinebuf[x] >> 4] : WHITE_LCD;
 
                             int count = 7;
 
@@ -378,8 +387,8 @@ static void __not_in_flash_func(render_loop)()
                         for (int x=((PIXEL_WIDTH - keyboard_x) >> 3); x<(PIXEL_WIDTH >> 3); ++x)
                         {
                             uint8_t byte = linebuf[x];
-                            uint16_t foreground = cbuff ? colour_table[clinebuf[x] & 0xf] : BLACK;
-                            uint16_t background = cbuff ? colour_table[clinebuf[x] >> 4] : WHITE;
+                            uint16_t foreground = cbuff ? colour_table[clinebuf[x] & 0xf] : BLACK_LCD;
+                            uint16_t background = cbuff ? colour_table[clinebuf[x] >> 4] : WHITE_LCD;
 
                             int count = 7;
 
@@ -400,8 +409,11 @@ static void __not_in_flash_func(render_loop)()
                 {
                     if (blank)
                     {
+#ifndef PICOZX_LCD
                         uint32_t twobits = (blank_colour << 12) | blank_colour;
-
+#else
+                        uint32_t twobits = (blank_colour == WHITE) ? (WHITE_LCD << 12) | WHITE_LCD : (BLACK_LCD << 12) | BLACK_LCD;
+#endif
                         for (int x=0; (x<PIXEL_WIDTH>>1); x++)
                         {
                             spi_lcd_put((twobits >> 16) & 0xff);
@@ -415,8 +427,8 @@ static void __not_in_flash_func(render_loop)()
                         for (int x = 0; x < (PIXEL_WIDTH >> 3); ++x)
                         {
                             uint8_t byte = linebuf[x];
-                            uint16_t foreground = cbuff ? colour_table[clinebuf[x] & 0xf] : BLACK;
-                            uint16_t background = cbuff ? colour_table[clinebuf[x] >> 4] : WHITE;
+                            uint16_t foreground = cbuff ? colour_table[clinebuf[x] & 0xf] : BLACK_LCD;
+                            uint16_t background = cbuff ? colour_table[clinebuf[x] >> 4] : WHITE_LCD;
                             int count = 7;
 
                             for (int j=0; j<4; j++)
@@ -445,7 +457,11 @@ static void __not_in_flash_func(render_loop)()
     }
 }
 
-static void core1_main()
+#if (defined PICOZX_LCD)
+void core1_main_lcd(void)
+#else
+void core1_main(void)
+#endif
 {
     sm = pio_claim_unused_sm(pio, true);    // Will panic if no sm available
 
@@ -469,13 +485,20 @@ static void core1_main()
     spi_lcd_program_init(pio, sm, offset, PICO_LCD_CMD_PIN, PICO_LCD_CLK_PIN, (SERIAL_CLK_DIV * (skip ? 2: 1)));
 #endif
     gpio_init(PICO_LCD_DC_PIN);
+#ifdef PICO_LCD_RS_PIN
     gpio_init(PICO_LCD_RS_PIN);
+#endif
     gpio_init(PICO_LCD_BL_PIN);
     gpio_set_dir(PICO_LCD_DC_PIN, GPIO_OUT);
+
+#ifdef PICO_LCD_RS_PIN
     gpio_set_dir(PICO_LCD_RS_PIN, GPIO_OUT);
+#endif
     gpio_set_dir(PICO_LCD_BL_PIN, GPIO_OUT);
 
+#ifdef PICO_LCD_RS_PIN
     gpio_put(PICO_LCD_RS_PIN, 1);
+#endif
     lcd_init();
     gpio_put(PICO_LCD_BL_PIN, 1);
 
diff --git a/display/display_priv.h b/display/display_priv.h
index 5236992..281d939 100644
--- a/display/display_priv.h
+++ b/display/display_priv.h
@@ -3,6 +3,7 @@
 #if !(defined (PICO_DVI_BOARD) || defined (PICO_OLIMEXPC_BOARD) || defined (PICO_WSPIZERO_BOARD) || defined (PICO_LCD_CS_PIN))
 #include "pico/scanvideo.h"
 #endif
+#include "pico/sync.h"
 
 typedef struct
 {
@@ -13,6 +14,41 @@ typedef struct
   uint8_t  __attribute__((aligned(4)))  pixel_data[];
 } KEYBOARD_PIC;
 
+// Keyboard display
+extern const KEYBOARD_PIC ZX80KYBD;
+extern const KEYBOARD_PIC ZX81KYBD;
+#ifdef PICOZX_LCD
+extern const KEYBOARD_PIC ZX80KYBD_LCD;
+extern const KEYBOARD_PIC ZX81KYBD_LCD;
+#endif
+extern const KEYBOARD_PIC* keyboard;
+extern bool showKeyboard;
+
+// Synchronisation primitives
+extern mutex_t next_frame_mutex;
+extern semaphore_t display_initialised;
+
+// Display blank screen
+extern bool blank;
+extern uint16_t blank_colour;
+
+// Buffers to display
+extern uint8_t* curr_buff;            // main display buffer being displayed
+extern uint8_t* cbuffer;              // chroma buffer being displayed
+
+// Functions used by display specific driver
+#if (defined PICOZX_LCD)
+extern void core1_main_vga(void);
+extern void core1_main_lcd(void);
+
+#else
+extern void core1_main(void);
+#endif
+extern void displayAllocateBuffers(uint16_t minBuffByte, uint16_t width, uint16_t height);
+extern void displayStartCommon(void);
+extern void newFrame(void);
+
+
 #if (defined PICO_VGA_BOARD)
 #define PICO_SCANVIDEO_PIXEL_FROM_RGB(r, g, b) ((((b)>>3u)<<PICO_SCANVIDEO_PIXEL_BSHIFT)|(((g)>>3u)<<PICO_SCANVIDEO_PIXEL_GSHIFT)|(((r)>>3u)<<PICO_SCANVIDEO_PIXEL_RSHIFT))
 #elif (defined PICO_PICOMITEVGA_BOARD)
diff --git a/display/display_vga.c b/display/display_vga.c
index 0433e9a..0b8c097 100644
--- a/display/display_vga.c
+++ b/display/display_vga.c
@@ -7,8 +7,7 @@
 #include "pico/multicore.h"
 #include "pico/sync.h"
 #include "display.h"
-#include "zx80bmp.h"
-#include "zx81bmp.h"
+#include "display_priv.h"
 
 // Union to allow variable length data manipulation
 typedef union
@@ -24,10 +23,11 @@ static uint16_t HEIGHT = 0;
 
 static const uint16_t MIN_RUN = 3;
 
-static const KEYBOARD_PIC* keyboard = &ZX81KYBD;
 static uint16_t keyboard_x = 0;
 static uint16_t keyboard_y = 0;
 
+static uint16_t stride = 0;
+
 // Do not make const - as want to keep in RAM
 static uint16_t colour_table[16] = {
     PICO_SCANVIDEO_PIXEL_FROM_RGB(0x00, 0x00, 0x00),
@@ -152,7 +152,6 @@ const scanvideo_mode_t vga_mode_360x288_51 =
 
 static const scanvideo_mode_t* video_mode = 0;
 
-#include "display_common.c"
 //
 // Private interface
 //
@@ -163,14 +162,18 @@ static int32_t populate_keyboard_line(int linenum, uint16_t bcolour, uint32_t* b
 static int32_t populate_mixed_line(uint8_t* display_line, uint8_t* colour_line, int linenum, uint32_t* buff);
 static void render_loop();
 static Fill_u expand_display(uint8_t disp, uint8_t colours);
-static void core1_main();
 
 //
 // Public functions
 //
 
     // Determine the video mode
-uint displayInitialise(bool fiveSevenSix, bool match, uint16_t minBuffByte, uint16_t* pixelWidth,
+#ifndef PICOZX_LCD
+uint displayInitialise
+#else
+uint displayInitialiseVGA
+#endif
+                      (bool fiveSevenSix, bool match, uint16_t minBuffByte, uint16_t* pixelWidth,
                        uint16_t* pixelHeight, uint16_t* strideBit, DisplayExtraInfo_T* info)
 {
     (void)info;
@@ -188,29 +191,7 @@ uint displayInitialise(bool fiveSevenSix, bool match, uint16_t minBuffByte, uint
     stride = minBuffByte + BYTE_WIDTH;
 
     // Allocate the buffers
-    for (int i=0; i<MAX_FREE; ++i)
-    {
-        free_buff[i] = (uint8_t*)malloc(minBuffByte + stride * HEIGHT)
-                         + minBuffByte;
-
-        // Store original index, so that can map a chroma buffer, if necessary
-        index_to_display[i] = free_buff[i];
-    }
-
-    // Allocate chroma buffers
-    for (int i=0; i<MAX_FREE; ++i)
-    {
-        chroma[i].buff = (uint8_t*)malloc(minBuffByte + stride * HEIGHT)
-                        + minBuffByte;
-
-        if (!chroma[i].buff)
-        {
-            printf("Insufficient memory for chroma - aborting\n");
-            exit(-1);
-        }
-    }
-
-    free_count = MAX_FREE;
+    displayAllocateBuffers(minBuffByte, stride, HEIGHT);
 
     // Return the values
     *pixelWidth = PIXEL_WIDTH;
@@ -223,12 +204,20 @@ uint displayInitialise(bool fiveSevenSix, bool match, uint16_t minBuffByte, uint
     return video_mode->default_timing->clock_freq / 100;
 }
 
+#ifndef PICOZX_LCD
 void displayStart(void)
+#else
+void displayStartVGA(void)
+#endif
 {
     displayStartCommon();
 }
 
+#ifndef PICOZX_LCD
 bool displayShowKeyboard(bool zx81)
+#else
+bool displayShowKeyboardVGA(bool zx81)
+#endif
 {
     bool previous = showKeyboard;
 
@@ -467,7 +456,11 @@ static Fill_u __not_in_flash_func(expand_display)(uint8_t disp, uint8_t colours)
     }
     return result;
 }
-static void core1_main()
+#if (defined PICOZX_LCD)
+void core1_main_vga(void)
+#else
+void core1_main(void)
+#endif
 {
     scanvideo_setup(video_mode);
     scanvideo_timing_enable(true);
diff --git a/display/zx80bmp.h b/display/zx80bmp.h
index 4b1f323..85da3e3 100644
--- a/display/zx80bmp.h
+++ b/display/zx80bmp.h
@@ -1,8 +1,12 @@
-#ifndef _ZX80BMP_H_
-#define _ZX80BMP_H_
+// No protection guard, as need to include twice from same file
 #include "display_priv.h"
 
-const KEYBOARD_PIC ZX80KYBD =
+// To allow multiple definitions
+#ifndef ZX80_KEYBOARD
+#define ZX80_KEYBOARD ZX80KYBD
+#endif
+
+const KEYBOARD_PIC ZX80_KEYBOARD =
 {
 256, 136, 2,
 {
@@ -1102,4 +1106,3 @@ const KEYBOARD_PIC ZX80KYBD =
 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff 
 }
 };
-#endif
\ No newline at end of file
diff --git a/display/zx81bmp.h b/display/zx81bmp.h
index 7035db3..c04233a 100644
--- a/display/zx81bmp.h
+++ b/display/zx81bmp.h
@@ -1,8 +1,12 @@
-#ifndef _ZX81BMP_H_
-#define _ZX81BMP_H_
+// No protection guard, as need to include twice from same file
 #include "display_priv.h"
 
-const KEYBOARD_PIC ZX81KYBD =
+// To allow multiple definitions
+#ifndef ZX81_KEYBOARD
+#define ZX81_KEYBOARD ZX81KYBD
+#endif
+
+const KEYBOARD_PIC ZX81_KEYBOARD =
 {
 256, 136, 2,
 {
@@ -1102,4 +1106,3 @@ const KEYBOARD_PIC ZX81KYBD =
 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff 
 }
 };
-#endif
\ No newline at end of file
diff --git a/images/3dmonster.jpg b/images/3dmonster.jpg
new file mode 100755
index 0000000..b8c7fcb
Binary files /dev/null and b/images/3dmonster.jpg differ
diff --git a/images/picozx_lcd.jpg b/images/picozx_lcd.jpg
new file mode 100755
index 0000000..449a147
Binary files /dev/null and b/images/picozx_lcd.jpg differ
diff --git a/images/rgb_222_vga.png b/images/rgb_222_vga.png
new file mode 100644
index 0000000..52c4b7e
Binary files /dev/null and b/images/rgb_222_vga.png differ
diff --git a/images/zx81_spectrum.jpg b/images/zx81_spectrum.jpg
new file mode 100755
index 0000000..8d2fdb5
Binary files /dev/null and b/images/zx81_spectrum.jpg differ
diff --git a/src/emuapi.cpp b/src/emuapi.cpp
index c12c746..4da00bb 100755
--- a/src/emuapi.cpp
+++ b/src/emuapi.cpp
@@ -210,6 +210,7 @@ typedef struct
   bool lcdRotate;
   bool lcdReflect;
   bool lcdBGR;
+  bool vga;
   bool ninePinJoystick;
 } Configuration_T;
 
@@ -254,6 +255,11 @@ bool emu_ACBRequested(void)
 #endif
 }
 
+bool emu_ACBPossible(void)
+{
+    return (AUDIO_PIN_L != AUDIO_PIN_R);
+}
+
 bool emu_ZX80Requested(void)
 {
   return (specific.computer == ZX80);
@@ -403,6 +409,11 @@ bool emu_lcdBGRRequested(void)
   return specific.lcdBGR;
 }
 
+bool emu_vgaRequested(void)
+{
+  return specific.vga;
+}
+
 const char* emu_GetDirectory(void)
 {
   return dirPath;
@@ -855,6 +866,11 @@ static int handler(void *user, const char *section, const char *name,
         // Defaults to off
         c->conf->lcdBGR = isEnabled(value);
       }
+      else if (!strcasecmp(name, "VGA"))
+      {
+        // Defaults to off
+        c->conf->vga = isEnabled(value);
+      }
 #endif
       else
       {
@@ -924,6 +940,7 @@ void emu_ReadDefaultValues(void)
     general.lcdRotate = false;
     general.lcdskipFrame = false;
     general.lcdBGR = false;
+    general.vga = false;
     general.ninePinJoystick = false;
 #ifdef PICO_LCDWS28_BOARD
     general.lcdInvertColour = true;
diff --git a/src/emuapi.h b/src/emuapi.h
index 14afea1..8912344 100755
--- a/src/emuapi.h
+++ b/src/emuapi.h
@@ -64,6 +64,7 @@ extern bool emu_NinePinJoystickRequested(void);
 extern int emu_MenuBorderRequested(void);
 extern uint16_t emu_VTol(void);
 extern bool emu_ACBRequested(void);
+extern bool emu_ACBPossible(void);
 extern bool emu_Centre(void);
 extern int emu_CentreX(void);
 extern int emu_CentreY(void);
@@ -75,6 +76,8 @@ extern bool emu_lcdRotateRequested(void);
 extern bool emu_lcdReflectRequested(void);
 extern bool emu_lcdBGRRequested(void);
 
+extern bool emu_vgaRequested(void);
+
 extern FrameSync_T emu_FrameSyncRequested(void);
 extern FiveSevenSix_T emu_576Requested(void);
 
diff --git a/src/emukeyboard.cpp b/src/emukeyboard.cpp
index 962236b..10c2162 100644
--- a/src/emukeyboard.cpp
+++ b/src/emukeyboard.cpp
@@ -11,6 +11,11 @@
 #if ((defined PICO_PICOZX_BOARD) || (defined PICO_PICOZXREAL_BOARD))
 #include "hardware/clocks.h"
 
+#if (defined PICO_PICOZXREAL_BOARD)
+#include "pico/bootrom.h"
+#endif
+
+static void gp_keyboard_initialise(void);
 static inline void device_keyscan_row(void);
 static bool timer_callback(repeating_timer_t *rt);
 
@@ -47,7 +52,7 @@ static uint8_t kbits[2][RN][CN] =
 #else
     {
         {
-            { HID_KEY_ENTER, HID_KEY_ARROW_LEFT, HID_KEY_ARROW_UP, HID_KEY_ARROW_RIGHT, HID_KEY_ARROW_DOWN, HID_KEY_F2, HID_KEY_F3, HID_KEY_F4 },
+            { HID_KEY_ENTER, HID_KEY_ARROW_LEFT, HID_KEY_ARROW_UP, HID_KEY_ARROW_RIGHT, HID_KEY_ARROW_DOWN, HID_KEY_F5, HID_KEY_F2, 0 },
             { HID_KEY_B, HID_KEY_H, HID_KEY_V, HID_KEY_Y, HID_KEY_6, HID_KEY_G, HID_KEY_T, HID_KEY_5 },
             { HID_KEY_N, HID_KEY_J, HID_KEY_C, HID_KEY_U, HID_KEY_7, HID_KEY_F, HID_KEY_R, HID_KEY_4 },
             { HID_KEY_M, HID_KEY_K, HID_KEY_X, HID_KEY_I, HID_KEY_8, HID_KEY_D, HID_KEY_E, HID_KEY_3 },
@@ -55,7 +60,7 @@ static uint8_t kbits[2][RN][CN] =
             { HID_KEY_SPACE, HID_KEY_ENTER, 0, HID_KEY_P, HID_KEY_0, HID_KEY_A, HID_KEY_Q, HID_KEY_1 }
         },
         {
-            { HID_KEY_ENTER, HID_KEY_ARROW_LEFT, HID_KEY_ARROW_UP, HID_KEY_ARROW_RIGHT, HID_KEY_ARROW_DOWN, HID_KEY_F5, HID_KEY_F6, HID_KEY_F7 },
+            { HID_KEY_ENTER, HID_KEY_ARROW_LEFT, HID_KEY_ARROW_UP, HID_KEY_ARROW_RIGHT, HID_KEY_ARROW_DOWN, HID_KEY_F3, HID_KEY_F6, 0 },
             { HID_KEY_B, HID_KEY_H, HID_KEY_V, HID_KEY_Y, HID_KEY_6, HID_KEY_G, HID_KEY_T, HID_KEY_5 },
             { HID_KEY_N, HID_KEY_J, HID_KEY_C, HID_KEY_U, HID_KEY_7, HID_KEY_F, HID_KEY_R, HID_KEY_4 },
             { HID_KEY_M, HID_KEY_K, HID_KEY_X, HID_KEY_I, HID_KEY_8, HID_KEY_D, HID_KEY_E, HID_KEY_3 },
@@ -72,29 +77,14 @@ void emu_KeyboardInitialise(uint8_t* keyboard)
 
 #if ((defined PICO_PICOZX_BOARD) || (defined PICO_PICOZXREAL_BOARD))
     static bool first = true;
+
+    gp_keyboard_initialise();
+
     if (first)
     {
-        // Only initialise the device keyboard once
+        // Only initialise the device keyboard timer once
         first = false;
-        for(int i = 0; i < RN; ++i)
-        {
-            gpio_init(rp[i]);
-            gpio_set_dir(rp[i], GPIO_IN);
-            gpio_disable_pulls(rp[i]);
-        }
 
-        for(int i = 0; i < CN; ++i)
-        {
-            gpio_init(cp[i]);
-            gpio_set_dir(cp[i], GPIO_IN);
-            gpio_pull_up(cp[i]);
-        }
-
-        // Set the first row to output, ready for first read
-        gpio_set_dir(rp[0], GPIO_OUT);
-        gpio_put(rp[0], 0);
-
-        // Set a timer to read device keyboard
         // if a read is missed, no need to catch up, so use positive time
         if (!add_repeating_timer_us(period, timer_callback, NULL, &timer))
         {
@@ -126,7 +116,7 @@ void emu_KeyboardScan(void* data)
         if (++used == 6) return;
     }
 
-    // Check main keys attched to row 0
+    // Check main keys attached to row 0
     if (rs[0] & 0x40)
     {
         report->keycode[used] = HID_KEY_PERIOD;
@@ -168,6 +158,13 @@ void emu_KeyboardScan(void* data)
             report->keycode[used] = kbits[set][0][j];
             if (++used == 6) return;
         }
+
+        // Check to see if menu button has been pressed, together with R key
+        if ((rs[0] & 0x40) && (rs[2] & 0x40))
+        {
+            // Reset the board, so new firmware can be loaded
+            reset_usb_boot(0, 0);
+        }
     }
 
     // Process remaining rows - have to skip shift
@@ -175,7 +172,7 @@ void emu_KeyboardScan(void* data)
     {
         for (int j=0; j<CN; ++j)
         {
-            if (rs[i] & (0x1<<j) & kbits[set][i][j])
+            if ((rs[i] & (0x1<<j)) && (kbits[set][i][j] !=0))
             {
                 report->keycode[used] = kbits[set][i][j];
                 if (++used == 6) return;
@@ -187,6 +184,44 @@ void emu_KeyboardScan(void* data)
 }
 
 #if ((defined PICO_PICOZX_BOARD) || (defined PICO_PICOZXREAL_BOARD))
+bool emu_KeyboardFire(void)
+{
+    // Ensure keyboard and joypad is initialised
+    gp_keyboard_initialise();
+
+    // Read the first line, which contains the fire button
+    device_keyscan_row();
+
+    return ((rs[0] & 0x01) != 0);
+}
+
+static void gp_keyboard_initialise(void)
+{
+    static bool first = true;
+    if (first)
+    {
+        // Only initialise the device keyboard once
+        first = false;
+        for(int i = 0; i < RN; ++i)
+        {
+            gpio_init(rp[i]);
+            gpio_set_dir(rp[i], GPIO_IN);
+            gpio_disable_pulls(rp[i]);
+        }
+
+        for(int i = 0; i < CN; ++i)
+        {
+            gpio_init(cp[i]);
+            gpio_set_dir(cp[i], GPIO_IN);
+            gpio_pull_up(cp[i]);
+        }
+
+        // Set the first row to output, ready for first read
+        gpio_set_dir(rp[0], GPIO_OUT);
+        gpio_put(rp[0], 0);
+    }
+}
+
 static inline void  __not_in_flash_func(device_keyscan_row)(void)
 {
     static uint32_t ri = 0;
diff --git a/src/emukeyboard.h b/src/emukeyboard.h
index 1ebfa01..a98e64b 100644
--- a/src/emukeyboard.h
+++ b/src/emukeyboard.h
@@ -10,6 +10,10 @@ extern void emu_KeyboardInitialise(uint8_t* keyboard);
 extern bool emu_KeyboardUpdate(uint8_t* special);
 extern void emu_KeyboardScan(void* data);
 
+#ifdef PICO_PICOZX_BOARD
+extern bool emu_KeyboardFire(void);
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/menu.cpp b/src/menu.cpp
index 6bdcd3d..3e72e7c 100644
--- a/src/menu.cpp
+++ b/src/menu.cpp
@@ -661,7 +661,8 @@ bool modifyMenu(void)
                     }
                     else if (field == PSTEREOACB)
                     {
-                        modify.stereo = ! modify.stereo;
+                        if (emu_ACBPossible())
+                            modify.stereo = ! modify.stereo;
                     }
 #endif
                     showModify(field, &modify);
@@ -1112,7 +1113,10 @@ static void showModify(PositionF6_T pos, ModifyF6_T* modify)
     writeString((modify->sound == AY_TYPE_QUICKSILVA) ? "QUICKSILVA" : (modify->sound == AY_TYPE_ZONX) ? "ZonX      " : "None      ", rhs , lcount + PositionF6_T::PSOUNDTYPE);
 
     writeInvertString("Stereo:", lhs, lcount + PositionF6_T::PSTEREOACB, pos == PositionF6_T::PSTEREOACB);
-    writeString(modify->stereo ? "ON-ACB" : "OFF   ", rhs , lcount + PositionF6_T::PSTEREOACB);
+    if (emu_ACBPossible())
+        writeString(modify->stereo ? "ON-ACB" : "OFF   ", rhs , lcount + PositionF6_T::PSTEREOACB);
+    else
+        writeString("N/A", rhs , lcount + PositionF6_T::PSTEREOACB);
 #endif
 }
 
@@ -1167,11 +1171,18 @@ static void showReboot(FiveSevenSix_T mode)
     writeString("      REBOOT THE PICO", lhs - 1, lcount + 6);
 
     writeInvertString("Resolution:", lhs, lcount + 12, true);
+#ifndef PICOZX_LCD
 #ifndef PICO_LCD_CS_PIN
     writeString((mode == OFF) ? "640x480x60  " : (mode == MATCH) ? "720x568x50.6" : "720x568x50  ", rhs, lcount + 12);
 #else
     writeString((mode == OFF) ? "320x240x60  " : (mode == MATCH) ? "320x240x50.6" : "320x240x50  ", rhs, lcount + 12);
 #endif
+#else
+    if (useLCD)
+        writeString((mode == OFF) ? "320x240x60  " : (mode == MATCH) ? "320x240x50.6" : "320x240x50  ", rhs, lcount + 12);
+    else
+        writeString((mode == OFF) ? "640x480x60  " : (mode == MATCH) ? "720x568x50.6" : "720x568x50  ", rhs, lcount + 12);
+#endif
 }
 
 static void showSave(const uint8_t* name, uint len, uint cursor, uint col, uint row)
diff --git a/src/pico81.cpp b/src/pico81.cpp
index 335bc8f..52208da 100755
--- a/src/pico81.cpp
+++ b/src/pico81.cpp
@@ -23,6 +23,11 @@ int main(void)
     // Initialise sd card and read config
     emu_init();
 
+#ifdef PICOZX_LCD
+    // Determine which display type to use
+    useLCD = !(emu_KeyboardFire() || emu_vgaRequested());
+#endif
+
     // Initialise display to get clock, using config
     uint clock = emu_VideoInit();
 
diff --git a/src/sound.c b/src/sound.c
index 2c6b518..0daebb0 100644
--- a/src/sound.c
+++ b/src/sound.c
@@ -45,9 +45,10 @@
 #include "common.h"
 #include "sound.h"
 #include "Z80.h"
+#include "iopins.h"
 
 /* configuration */
-const int sound_stereo=1;
+const int sound_stereo=1;  /* Always generate two channels */
 
 int sound_enabled=0;
 int sound_freq=0;
@@ -166,7 +167,8 @@ if(!sound_stereo)
 }
 else
 {
-  sound_stereo_acb = acb;
+  // As stereo is hard coded to 1...
+  sound_stereo_acb = (AUDIO_PIN_L != AUDIO_PIN_R) ? acb : 0;
 }
 
 if (reset)
@@ -357,10 +359,15 @@ for(f=0,ptr=buff;f<sound_framesiz;f++,ptr+=channels)
     ptr[1]=*ptr;
 
 #if ((!defined (I2S)) && (!defined (SOUND_HDMI)))
+#ifdef PICO_PICOZXREAL_BOARD
+#define PWM_SOUND_REDUCE 3
+#else
+#define PWM_SOUND_REDUCE 4
+#endif
   // Correct to PWM
-  *ptr = (*ptr>>4) + ZEROSOUND;
+  *ptr = (*ptr>>PWM_SOUND_REDUCE) + ZEROSOUND;
   if (sound_stereo)
-    ptr[1] = (ptr[1]>>4) + ZEROSOUND;
+    ptr[1] = (ptr[1]>>PWM_SOUND_REDUCE) + ZEROSOUND;
 #endif
   /* update noise RNG/filter */
   ay_noise_tick+=ay_tick_incr;
diff --git a/uf2/picozx81_dvi.uf2 b/uf2/picozx81_dvi.uf2
index a4fac87..d5f32cc 100644
Binary files a/uf2/picozx81_dvi.uf2 and b/uf2/picozx81_dvi.uf2 differ
diff --git a/uf2/picozx81_dvi_hdmi_sound.uf2 b/uf2/picozx81_dvi_hdmi_sound.uf2
index 02e3ae9..3505c85 100644
Binary files a/uf2/picozx81_dvi_hdmi_sound.uf2 and b/uf2/picozx81_dvi_hdmi_sound.uf2 differ
diff --git a/uf2/picozx81_lcdmaker.uf2 b/uf2/picozx81_lcdmaker.uf2
index 5b67cb6..d22d19f 100644
Binary files a/uf2/picozx81_lcdmaker.uf2 and b/uf2/picozx81_lcdmaker.uf2 differ
diff --git a/uf2/picozx81_lcdws28.uf2 b/uf2/picozx81_lcdws28.uf2
index 52d5862..53e115c 100644
Binary files a/uf2/picozx81_lcdws28.uf2 and b/uf2/picozx81_lcdws28.uf2 differ
diff --git a/uf2/picozx81_olimexpc.uf2 b/uf2/picozx81_olimexpc.uf2
index 53293e0..3df4939 100644
Binary files a/uf2/picozx81_olimexpc.uf2 and b/uf2/picozx81_olimexpc.uf2 differ
diff --git a/uf2/picozx81_olimexpc_hdmi_sound.uf2 b/uf2/picozx81_olimexpc_hdmi_sound.uf2
index d919b93..ef7b6c7 100644
Binary files a/uf2/picozx81_olimexpc_hdmi_sound.uf2 and b/uf2/picozx81_olimexpc_hdmi_sound.uf2 differ
diff --git a/uf2/picozx81_picomitevga.uf2 b/uf2/picozx81_picomitevga.uf2
index c7fb7e3..fbf3a60 100644
Binary files a/uf2/picozx81_picomitevga.uf2 and b/uf2/picozx81_picomitevga.uf2 differ
diff --git a/uf2/picozx81_picozx.uf2 b/uf2/picozx81_picozx.uf2
index b89b265..3ff8533 100644
Binary files a/uf2/picozx81_picozx.uf2 and b/uf2/picozx81_picozx.uf2 differ
diff --git a/uf2/picozx81_picozx_lcd.uf2 b/uf2/picozx81_picozx_lcd.uf2
new file mode 100644
index 0000000..2836599
Binary files /dev/null and b/uf2/picozx81_picozx_lcd.uf2 differ
diff --git a/uf2/picozx81_picozxreal.uf2 b/uf2/picozx81_picozxreal.uf2
new file mode 100644
index 0000000..2b3dce0
Binary files /dev/null and b/uf2/picozx81_picozxreal.uf2 differ
diff --git a/uf2/picozx81_vga.uf2 b/uf2/picozx81_vga.uf2
index 8087d36..6933361 100644
Binary files a/uf2/picozx81_vga.uf2 and b/uf2/picozx81_vga.uf2 differ
diff --git a/uf2/picozx81_vga332.uf2 b/uf2/picozx81_vga332.uf2
index c78484f..a8e0992 100644
Binary files a/uf2/picozx81_vga332.uf2 and b/uf2/picozx81_vga332.uf2 differ
diff --git a/uf2/picozx81_vgamaker222c.uf2 b/uf2/picozx81_vgamaker222c.uf2
index 9ad597c..d200ec7 100644
Binary files a/uf2/picozx81_vgamaker222c.uf2 and b/uf2/picozx81_vgamaker222c.uf2 differ
diff --git a/uf2/picozx81_wspizero_hdmi_sound.uf2 b/uf2/picozx81_wspizero_hdmi_sound.uf2
index 0d6ea6b..33f08a4 100644
Binary files a/uf2/picozx81_wspizero_hdmi_sound.uf2 and b/uf2/picozx81_wspizero_hdmi_sound.uf2 differ