From 47fcc4200af2983625ac9ed70dcd3ec65bf61039 Mon Sep 17 00:00:00 2001
From: ikjordan <38705261+ikjordan@users.noreply.github.com>
Date: Fri, 3 May 2024 18:56:35 +0100
Subject: [PATCH] Picozxreal (#33)
* start picozxreal
* Refactor
* First pass Bootsel
* Update PicoDVI
* No serial for picozxreal
* Testing zxpico with LCD
* Testing picozx variants
* Update Readme
* Update Readme
* Readme update
* Update Readme for back power
* Add more tested applications
* Add 3DMM image
* Update images
* Correct link
* add image credit
* SPI bus speed
---------
Co-authored-by: ikjordan
---
CMakeLists.txt | 28 ++++-
PicoDVI | 2 +-
README.md | 110 ++++++++++++-----
boards/picozxboard.h | 29 +++++
boards/wspizeroboard.h | 7 +-
buildall | 12 ++
display/display.h | 11 +-
display/display_common.c | 170 +++++++++++++++++++++++----
display/display_dvi.c | 36 +-----
display/display_lcd.c | 117 ++++++++++--------
display/display_priv.h | 36 ++++++
display/display_vga.c | 53 ++++-----
display/zx80bmp.h | 11 +-
display/zx81bmp.h | 11 +-
images/3dmonster.jpg | Bin 0 -> 302620 bytes
images/picozx_lcd.jpg | Bin 0 -> 300167 bytes
images/rgb_222_vga.png | Bin 0 -> 28129 bytes
images/zx81_spectrum.jpg | Bin 0 -> 200062 bytes
src/emuapi.cpp | 17 +++
src/emuapi.h | 3 +
src/emukeyboard.cpp | 81 +++++++++----
src/emukeyboard.h | 4 +
src/menu.cpp | 15 ++-
src/pico81.cpp | 5 +
src/sound.c | 15 ++-
uf2/picozx81_dvi.uf2 | Bin 514560 -> 514048 bytes
uf2/picozx81_dvi_hdmi_sound.uf2 | Bin 518144 -> 517632 bytes
uf2/picozx81_lcdmaker.uf2 | Bin 503808 -> 504832 bytes
uf2/picozx81_lcdws28.uf2 | Bin 503296 -> 503808 bytes
uf2/picozx81_olimexpc.uf2 | Bin 514560 -> 514048 bytes
uf2/picozx81_olimexpc_hdmi_sound.uf2 | Bin 518656 -> 518144 bytes
uf2/picozx81_picomitevga.uf2 | Bin 509440 -> 509440 bytes
uf2/picozx81_picozx.uf2 | Bin 509440 -> 509440 bytes
uf2/picozx81_picozx_lcd.uf2 | Bin 0 -> 552448 bytes
uf2/picozx81_picozxreal.uf2 | Bin 0 -> 509440 bytes
uf2/picozx81_vga.uf2 | Bin 508416 -> 508416 bytes
uf2/picozx81_vga332.uf2 | Bin 509440 -> 509440 bytes
uf2/picozx81_vgamaker222c.uf2 | Bin 510464 -> 510464 bytes
uf2/picozx81_wspizero_hdmi_sound.uf2 | Bin 518656 -> 518144 bytes
39 files changed, 565 insertions(+), 208 deletions(-)
create mode 100755 images/3dmonster.jpg
create mode 100755 images/picozx_lcd.jpg
create mode 100644 images/rgb_222_vga.png
create mode 100755 images/zx81_spectrum.jpg
create mode 100644 uf2/picozx81_picozx_lcd.uf2
create mode 100644 uf2/picozx81_picozxreal.uf2
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
+### 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)
+
+
+
+
+
+[PICOZX in a Spectrum case](https://www.pcbway.com/project/shareproject/)
+
+A ZX81 disguised as a Spectrum!
+
+
+
+
### 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)
@@ -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:
+
+
+
+
+#### 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
-#include "hardware/structs/bus_ctrl.h"
-#define MAX_FREE 4
-#define MAX_PEND 2
-
+#include
+#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> 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
-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; iwidth)>>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>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)<>3u)<>3u)<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 0000000000000000000000000000000000000000..b8c7fcb3b910f9a518a238f6b9ad01cde5a58c32
GIT binary patch
literal 302620
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
n8g&L-}mq65;eq
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