├── .gitignore ├── .vscode ├── extensions.json └── settings.json ├── CMakeLists.txt ├── README.md ├── include ├── ClassProperties.h ├── File.h ├── FileSystem.h ├── MemoryPage.h ├── RamPage.h ├── RamVideoPage.h ├── ScreenArea.h ├── SpectrumScreenData.h ├── VideoController.h ├── ay3-8912-state.h ├── emulator.h ├── errorReadingFile.h ├── font8x8.h ├── keyboard.h ├── main_ROM.h ├── ps2Input.h ├── settings.h ├── volume.h ├── z80Emulator.h ├── z80Environment.h ├── z80Input.h ├── z80main.h └── z80snapshot.h ├── lib ├── z80_AW │ ├── LICENSE │ ├── README.md │ ├── z80.c │ └── z80_AW.h ├── z80_JLS │ ├── README.md │ ├── z80.cpp │ ├── z80.h │ └── z80operations.h ├── z80_LKF │ ├── README.txt │ ├── instructions.h │ ├── macros.h │ ├── tables.h │ ├── z80config.h │ ├── z80emu.c │ ├── z80emu.h │ └── z80user.h └── z80_ZEL │ ├── LICENSE │ ├── README.md │ ├── tables │ ├── cb_prefix.tab │ ├── dd_prefix.tab │ ├── ddcb_prefix.tab │ ├── ed_prefix.tab │ ├── fd_prefix.tab │ ├── fdcb_prefix.tab │ └── no_prefix.tab │ ├── z80.c │ ├── z80_instructions.c │ ├── z80_types.h │ └── zel │ ├── z80.h │ ├── z80_instruction_types.h │ └── z80_instructions.h ├── platformio.ini ├── sdkconfig ├── sdkconfig.pico32 ├── src ├── CMakeLists.txt ├── File.cpp ├── FileSystem.cpp ├── RamPage.cpp ├── RamVideoPage.cpp ├── ScreenArea.cpp ├── VideoController.cpp ├── ay3-8912-state.cpp ├── emulator.cpp ├── errorReadingFile.cpp ├── font8x8.cpp ├── main.cpp ├── main_ROM.c ├── ps2Input.cpp ├── volume.c ├── z80Emulator_AW.cpp ├── z80Emulator_JLS.cpp ├── z80Emulator_LKF.cpp ├── z80Emulator_ZEL.cpp ├── z80Environment.cpp ├── z80Input.cpp ├── z80main.cpp └── z80snapshot.cpp └── test └── README /.gitignore: -------------------------------------------------------------------------------- 1 | .pio/ 2 | /lib/FabGL/ 3 | /build/ 4 | .vscode/.browse.c_cpp.db* 5 | .vscode/c_cpp_properties.json 6 | .vscode/launch.json 7 | .vscode/ipch 8 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "platformio.platformio-ide" 6 | ], 7 | "unwantedRecommendations": [ 8 | "ms-vscode.cpptools-extension-pack" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "sstream": "cpp", 4 | "array": "cpp", 5 | "string": "cpp", 6 | "algorithm": "cpp", 7 | "atomic": "cpp", 8 | "*.tcc": "cpp", 9 | "cctype": "cpp", 10 | "cerrno": "cpp", 11 | "cfloat": "cpp", 12 | "climits": "cpp", 13 | "clocale": "cpp", 14 | "cmath": "cpp", 15 | "cstdarg": "cpp", 16 | "cstdbool": "cpp", 17 | "cstddef": "cpp", 18 | "cstdint": "cpp", 19 | "cstdio": "cpp", 20 | "cstdlib": "cpp", 21 | "cstring": "cpp", 22 | "ctime": "cpp", 23 | "cwchar": "cpp", 24 | "cwctype": "cpp", 25 | "deque": "cpp", 26 | "map": "cpp", 27 | "unordered_map": "cpp", 28 | "unordered_set": "cpp", 29 | "vector": "cpp", 30 | "exception": "cpp", 31 | "functional": "cpp", 32 | "system_error": "cpp", 33 | "tuple": "cpp", 34 | "type_traits": "cpp", 35 | "iterator": "cpp", 36 | "memory": "cpp", 37 | "numeric": "cpp", 38 | "random": "cpp", 39 | "fstream": "cpp", 40 | "initializer_list": "cpp", 41 | "iomanip": "cpp", 42 | "ios": "cpp", 43 | "iosfwd": "cpp", 44 | "istream": "cpp", 45 | "limits": "cpp", 46 | "locale": "cpp", 47 | "new": "cpp", 48 | "ostream": "cpp", 49 | "queue": "cpp", 50 | "stdexcept": "cpp", 51 | "streambuf": "cpp", 52 | "cinttypes": "cpp", 53 | "utility": "cpp", 54 | "typeinfo": "cpp", 55 | "z80_instructions.h": "c" 56 | }, 57 | "cmake.configureOnOpen": false, 58 | "idf.adapterTargetName": "esp32" 59 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16.0) 2 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 3 | project(Test) 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # esp32-z80emu 2 | Spectrum ZX 128K emulator on VGA-32 board 3 | 4 | ## Hardware 5 | [VGA32 v1.4 Board](https://www.lilygo.cc/en-ca/products/fabgl-vga32) 6 | 7 | ## What it can do 8 | * Emulate Spectrum ZX 128K 9 | * Load snapshot in .Z80 format from SD card 10 | * Save snapshot in .Z80 format to SD card 11 | * Output some sounds (partial support for AY3-8912) 12 | * Kempston mouse 13 | * Load ROMs from SD card (`/roms/128-0.rom`; `/roms/128-1.rom`. Fall back to OpenSE Basic if not present) 14 | * Not using any PSRAM 15 | 16 | ZX Spectrum Emulator on ESP32 19 | 20 | ## Third party software 21 | This project uses the following libraries: 22 | * (GPL v3.0) Display video using VGA, process PS/2 keyboard, and sound output: https://github.com/fdivitto/FabGL 23 | * (GPL v2.0) OpenSE Basic: https://spectrumcomputing.co.uk/index.php?cat=96&id=27510 24 | 25 | Choose one of the following for Z80 CPU emulator: 26 | * (One liner, for those who don't like reading long legal documents) Lin Ke-Fong's https://github.com/anotherlin/z80emu 27 | * (GPL v3.0) José Luis Sánchez's https://github.com/jsanchezv/z80cpp 28 | * (MIT) Steve Checkoway's https://github.com/stevecheckoway/libzel 29 | * (zlib License) Andre Weissflog's https://github.com/floooh/chips (this one seems too slow) 30 | 31 | ## Plans for the future / issues 32 | * Flickering in some games 33 | * Beeper 34 | * Support noise and envelope for AY3-8912 sound 35 | * The speed is 12% faster than it is supposed to be 36 | 37 | -------------------------------------------------------------------------------- /include/ClassProperties.h: -------------------------------------------------------------------------------- 1 | #ifndef __CLASSPROPERTIES_INCLUDED__ 2 | #define __CLASSPROPERTIES_INCLUDED__ 3 | 4 | // Magic from https://www.codeproject.com/Articles/12358/C-object-properties-with-no-run-time-or-memory-ove. 5 | template 6 | struct property 7 | { 8 | private: 9 | T* _this; 10 | V _value; 11 | public: 12 | property(T* this_):_this(this_) { } 13 | operator V() { return (_this->*_get)(); } 14 | void operator=(V i) { (_this->*_set)(i);} 15 | }; 16 | #define CLASS(NAME) typedef NAME ClassType 17 | #define PROPERTY(TYPE, NAME) \ 18 | property NAME 19 | 20 | #endif 21 | 22 | -------------------------------------------------------------------------------- /include/File.h: -------------------------------------------------------------------------------- 1 | #ifndef __FILE_H__ 2 | #define __FILE_H__ 3 | 4 | #include 5 | 6 | namespace zx 7 | { 8 | 9 | class File : public std::fstream 10 | { 11 | public: 12 | size_t read(uint8_t* buf, size_t size); 13 | bool seek(uint32_t off, ios_base::seekdir way); 14 | size_t write(const uint8_t *buf, size_t size); 15 | }; 16 | 17 | } 18 | 19 | #endif /* __FILE_H__ */ 20 | -------------------------------------------------------------------------------- /include/FileSystem.h: -------------------------------------------------------------------------------- 1 | #ifndef __SDCARD_H__ 2 | #define __SDCARD_H__ 3 | 4 | void FileSystemInitialize(); 5 | 6 | bool loadSnapshotSetup(const char* path); 7 | bool loadSnapshotLoop(); 8 | 9 | bool saveSnapshotSetup(const char* path); 10 | bool saveSnapshotLoop(); 11 | 12 | bool ReadFromFile(const char* fileName, uint8_t* buffer, size_t size); 13 | 14 | #endif /* __SDCARD_H__ */ 15 | -------------------------------------------------------------------------------- /include/MemoryPage.h: -------------------------------------------------------------------------------- 1 | #ifndef __MEMORYPAGE_INCLUDED__ 2 | #define __MEMORYPAGE_INCLUDED__ 3 | 4 | #include 5 | 6 | class MemoryPage 7 | { 8 | public: 9 | uint8_t virtual ReadByte(uint16_t addr) = 0; 10 | uint8_t operator[](uint16_t addr) 11 | { 12 | return this->ReadByte(addr); 13 | } 14 | void virtual WriteByte(uint16_t addr, uint8_t data) = 0; 15 | 16 | uint16_t virtual ReadWord(uint16_t addr) 17 | { 18 | return (this->ReadByte(addr + 1) << 8) | this->ReadByte(addr); 19 | } 20 | 21 | void virtual WriteWord(uint16_t addr, uint16_t data) 22 | { 23 | this->WriteByte(addr + 1, data >> 8); 24 | this->WriteByte(addr, data & 0xFF); 25 | } 26 | 27 | void virtual FromBuffer(void* buffer) = 0; 28 | void virtual ToBuffer(void* buffer) = 0; 29 | }; 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /include/RamPage.h: -------------------------------------------------------------------------------- 1 | #ifndef __RAMPAGE_INCLUDED__ 2 | #define __RAMPAGE_INCLUDED__ 3 | 4 | #include "MemoryPage.h" 5 | 6 | class RamPage: public MemoryPage 7 | { 8 | private: 9 | uint8_t* _data = nullptr; 10 | public: 11 | RamPage& operator=(void* allocatedRam); 12 | operator uint8_t*(); 13 | 14 | uint8_t virtual ReadByte(uint16_t addr) override; 15 | void virtual WriteByte(uint16_t addr, uint8_t data) override; 16 | void virtual FromBuffer(void* buffer) override; 17 | void virtual ToBuffer(void* buffer) override; 18 | }; 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /include/RamVideoPage.h: -------------------------------------------------------------------------------- 1 | #ifndef __RAMVIDEOPAGE_INCLUDED__ 2 | #define __RAMVIDEOPAGE_INCLUDED__ 3 | 4 | #include "MemoryPage.h" 5 | #include "SpectrumScreenData.h" 6 | 7 | class RamVideoPage: public MemoryPage 8 | { 9 | private: 10 | SpectrumScreenData* _videoRam = nullptr; 11 | uint8_t* _data = nullptr; 12 | 13 | public: 14 | void Initialize(SpectrumScreenData* _videoRam, void* allocatedRam); 15 | 16 | uint8_t virtual ReadByte(uint16_t addr) override; 17 | void virtual WriteByte(uint16_t addr, uint8_t data) override; 18 | void virtual FromBuffer(void* data) override; 19 | void virtual ToBuffer(void* buffer) override; 20 | }; 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /include/ScreenArea.h: -------------------------------------------------------------------------------- 1 | #ifndef _SCREENAREA_H 2 | #define _SCREENAREA_H 3 | 4 | #include "VideoController.h" 5 | 6 | class ScreenArea 7 | { 8 | private: 9 | VideoController* _videoController; 10 | uint16_t _xOffset; 11 | uint16_t _yOffset; 12 | uint16_t _Width; 13 | uint16_t _Height; 14 | uint8_t foreColor = 0xFF; 15 | uint8_t backColor = 0xFF; 16 | 17 | public: 18 | bool _isCursorVisible = false; 19 | uint16_t getX(); 20 | uint16_t getY(); 21 | 22 | ScreenArea(VideoController* videoController, 23 | uint16_t xOffset, uint16_t width, 24 | uint16_t yOffset, uint16_t height); 25 | 26 | void Clear(); 27 | void SetAttribute(uint8_t x, uint8_t y, uint8_t foreColor, uint8_t backColor); 28 | void SetCursorPosition(uint8_t x, uint8_t y); 29 | void ShowCursor(); 30 | void HideCursor(); 31 | void SetPrintAttribute(uint16_t attribute); 32 | void SetPrintAttribute(uint8_t foreColor, uint8_t backColor); 33 | void Print(const char* str); 34 | void PrintAt(uint8_t x, uint8_t y, const char* str); 35 | void PrintAlignRight(uint8_t y, const char *str); 36 | void PrintAlignCenter(uint8_t y, const char *str); 37 | }; 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /include/SpectrumScreenData.h: -------------------------------------------------------------------------------- 1 | #ifndef _SPECTRUMSCREENDATA_H 2 | #define _SPECTRUMSCREENDATA_H 3 | 4 | #include 5 | 6 | #define SPECTRUM_WIDTH 32 7 | #define SPECTRUM_HEIGHT 24 8 | 9 | typedef struct 10 | { 11 | uint8_t* Pixels; 12 | uint16_t* Attributes; 13 | } SpectrumScreenData; 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /include/VideoController.h: -------------------------------------------------------------------------------- 1 | #ifndef __VIDEOCONTROLLER__ 2 | #define __VIDEOCONTROLLER__ 3 | 4 | #include 5 | #include 6 | #include "fabgl.h" 7 | #include "Settings.h" 8 | #include "SpectrumScreenData.h" 9 | 10 | #define SPECTRUM_WIDTH_WITH_BORDER 36 11 | #define SPECTRUM_HEIGHT_WITH_BORDER 26 12 | 13 | using namespace std; 14 | 15 | class VideoController : public fabgl::VGADirectController 16 | { 17 | public: 18 | uint8_t _mode = 2; 19 | 20 | // Mode 1 21 | uint8_t IRAM_ATTR createRawPixel(uint8_t color); 22 | uint8_t* _fontData; 23 | uint8_t Characters[SCREEN_WIDTH * SCREEN_HEIGHT]; 24 | uint32_t* _defaultAttribute = nullptr; 25 | uint32_t** Attributes; 26 | uint16_t _leftOffset = 24; 27 | uint16_t _topOffset = 16; 28 | uint8_t cursor_x = 0; 29 | uint8_t cursor_y = 0; 30 | 31 | // Mode 2 32 | SpectrumScreenData* Settings; 33 | uint8_t* BorderColor; 34 | uint16_t _borderWidth = 32; 35 | uint16_t _borderHeight = 24; 36 | volatile uint32_t Frames = 0; 37 | 38 | VideoController(SpectrumScreenData* screenData); 39 | void Start(char const* modeline); 40 | void SetMode(uint8_t mode); 41 | 42 | // Mode 1 43 | void Print(const char* str); 44 | void print(char* str, uint8_t foreColor, uint8_t backColor); 45 | void SetCursorPosition(uint8_t x, uint8_t y); 46 | void ShowScreenshot(); 47 | void ShowScreenshot(const uint8_t* screenshot, uint8_t borderColor); 48 | void SetAttribute(uint8_t x, uint8_t y, uint8_t foreColor, uint8_t backColor); 49 | 50 | // Mode 2 51 | 52 | private: 53 | std::map _attrToAddr; 54 | std::map _addrToAttr; 55 | uint32_t* _spectrumAttributes; 56 | 57 | uint32_t* CreateAttribute(uint8_t foreColor, uint8_t backColor); 58 | void InitAttribute(uint32_t* attribute, uint8_t foreColor, uint8_t backColor); 59 | void cursorNext(); 60 | void print(char* str); 61 | void print(const char* str, uint8_t foreColor, uint8_t backColor); 62 | void printChar(uint16_t x, uint16_t y, uint16_t ch); 63 | void printChar(uint16_t x, uint16_t y, uint16_t ch, uint8_t foreColor, uint8_t backColor); 64 | void freeUnusedAttributes(); 65 | void prepareDebugScreen(); 66 | void showScreenshot(uint8_t* pixelData, uint16_t* attributes, uint8_t borderColor); 67 | }; 68 | 69 | #endif -------------------------------------------------------------------------------- /include/ay3-8912-state.h: -------------------------------------------------------------------------------- 1 | #ifndef _AY3_8912_STATE_H 2 | #define _AY3_8912_STATE_H 3 | 4 | #include 5 | #include "fabgl.h" 6 | 7 | namespace Sound 8 | { 9 | 10 | class Ay3_8912_state 11 | { 12 | public: 13 | // Registers 14 | uint8_t finePitchChannelA = 0xFF; 15 | uint8_t coarsePitchChannelA = 0xFF; 16 | uint8_t finePitchChannelB = 0xFF; 17 | uint8_t coarsePitchChannelB = 0xFF; 18 | uint8_t finePitchChannelC = 0xFF; 19 | uint8_t coarsePitchChannelC = 0xFF; 20 | uint8_t noisePitch = 0xFF; 21 | uint8_t mixer = 0xFF; 22 | uint8_t volumeChannelA = 0xFF; 23 | uint8_t volumeChannelB = 0xFF; 24 | uint8_t volumeChannelC = 0xFF; 25 | uint8_t envelopeFineDuration = 0xFF; 26 | uint8_t envelopeCoarseDuration = 0xFF; 27 | uint8_t envelopeShape = 0xFF; 28 | uint8_t ioPortA = 0xFF; 29 | 30 | // Status 31 | uint8_t selectedRegister = 0xFF; 32 | uint8_t channelVolume[3] = { 0xFF, 0xFF, 0xFF }; 33 | uint16_t channelFrequency[3] = { 0xFFFF, 0xFFFF, 0xFFFF }; 34 | 35 | void selectRegister(uint8_t registerNumber); 36 | void setRegisterData(uint8_t data); 37 | uint8_t getRegisterData(); 38 | 39 | void Initialize(); 40 | void StopSound(); 41 | void ResumeSound(); 42 | void Clear(); 43 | 44 | void AttachSoundGenerator(WaveformGenerator* soundGenerator); 45 | 46 | private: 47 | void updated(); 48 | }; 49 | 50 | } 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /include/emulator.h: -------------------------------------------------------------------------------- 1 | #ifndef __EMULATOR_H__ 2 | #define __EMULATOR_H__ 3 | 4 | #include "settings.h" 5 | #include "z80Emulator.h" 6 | 7 | extern uint8_t _buffer16K_1[]; 8 | extern uint8_t _buffer16K_2[]; 9 | 10 | void EmulatorTaskMain(void *unused); 11 | 12 | void showTitle(const char* title); 13 | void saveState(); 14 | void restoreState(); 15 | 16 | #endif /* __EMULATOR_H__ */ 17 | -------------------------------------------------------------------------------- /include/errorReadingFile.h: -------------------------------------------------------------------------------- 1 | #ifndef ERRORREADINGFILE_H 2 | #define ERRORREADINGFILE_H 3 | 4 | extern const unsigned char errorReadingFile[]; 5 | 6 | #endif -------------------------------------------------------------------------------- /include/font8x8.h: -------------------------------------------------------------------------------- 1 | #ifndef FONT8X8_H 2 | #define FONT8X8_H 3 | 4 | extern const unsigned char font8x8[]; 5 | 6 | #endif -------------------------------------------------------------------------------- /include/main_ROM.h: -------------------------------------------------------------------------------- 1 | #ifndef MAIN_ROM_H 2 | #define MAIN_ROM_H 3 | 4 | #include 5 | 6 | extern const uint8_t ROM[]; 7 | 8 | #endif -------------------------------------------------------------------------------- /include/ps2Input.h: -------------------------------------------------------------------------------- 1 | #ifndef _PS2INPUT_H_ 2 | #define _PS2INPUT_H_ 3 | 4 | /* Single Byte Key Codes */ 5 | #define KEY_NUM 0x77 6 | #define KEY_SCROLL 0x7E 7 | #define KEY_CAPS 0x58 8 | #define KEY_LEFTSHIFT 0x12 9 | #define KEY_RIGHTSHIFT 0x59 10 | #define KEY_LEFTCONTROL 0x14 11 | #define KEY_ALT 0x11 12 | #define KEY_ESC 0x76 13 | #define KEY_BACKSPACE 0x66 14 | #define KEY_TAB 0x0D 15 | #define KEY_ENTER 0x5A 16 | #define KEY_SPACEBAR 0x29 17 | #define KEY_KP0 0x70 18 | #define KEY_KP1 0x69 19 | #define KEY_KP2 0x72 20 | #define KEY_KP3 0x7A 21 | #define KEY_KP4 0x6B 22 | #define KEY_KP5 0x73 23 | #define KEY_KP6 0x74 24 | #define KEY_KP7 0x6C 25 | #define KEY_KP8 0x75 26 | #define KEY_KP9 0x7D 27 | #define KEY_KP_DOT 0x71 28 | #define KEY_KP_PLUS 0x79 29 | #define KEY_KP_MINUS 0x7B 30 | #define KEY_KP_TIMES 0x7C 31 | #define KEY_0 0X45 32 | #define KEY_1 0X16 33 | #define KEY_2 0X1E 34 | #define KEY_3 0X26 35 | #define KEY_4 0X25 36 | #define KEY_5 0X2E 37 | #define KEY_6 0X36 38 | #define KEY_7 0X3D 39 | #define KEY_8 0X3E 40 | #define KEY_9 0X46 41 | #define KEY_APOS 0X52 42 | #define KEY_COMMA 0X41 43 | #define KEY_MINUS 0X4E 44 | #define KEY_DOT 0X49 45 | #define KEY_DIV 0X4A 46 | /* Single quote or back apostrophe */ 47 | #define KEY_SINGLE 0X0E 48 | #define KEY_A 0X1C 49 | #define KEY_B 0X32 50 | #define KEY_C 0X21 51 | #define KEY_D 0X23 52 | #define KEY_E 0X24 53 | #define KEY_F 0X2B 54 | #define KEY_G 0X34 55 | #define KEY_H 0X33 56 | #define KEY_I 0X43 57 | #define KEY_J 0X3B 58 | #define KEY_K 0X42 59 | #define KEY_L 0X4B 60 | #define KEY_M 0X3A 61 | #define KEY_N 0X31 62 | #define KEY_O 0X44 63 | #define KEY_P 0X4D 64 | #define KEY_Q 0X15 65 | #define KEY_R 0X2D 66 | #define KEY_S 0X1B 67 | #define KEY_T 0X2C 68 | #define KEY_U 0X3C 69 | #define KEY_V 0X2A 70 | #define KEY_W 0X1D 71 | #define KEY_X 0X22 72 | #define KEY_Y 0X35 73 | #define KEY_Z 0X1A 74 | #define KEY_SEMI 0X4C 75 | #define KEY_BACK 0X5D 76 | #define KEY_OPEN_SQ 0X54 77 | #define KEY_CLOSE_SQ 0X5B 78 | #define KEY_EQUAL 0X55 79 | #define KEY_F1 0X05 80 | #define KEY_F2 0X06 81 | #define KEY_F3 0X04 82 | #define KEY_F4 0X0C 83 | #define KEY_F5 0X03 84 | #define KEY_F6 0X0B 85 | #define KEY_F7 0X83 86 | #define KEY_F8 0X0A 87 | #define KEY_F9 0X01 88 | #define KEY_F10 0X09 89 | #define KEY_F11 0X78 90 | #define KEY_F12 0X07 91 | #define KEY_KP_COMMA 0X6D 92 | 93 | /* Extended key codes E0 table for two byte codes */ 94 | /* PS2_CTRL and PS2_ALT Need using in any table for the right keys */ 95 | /* first is special case for PRTSCR not always used so ignored by decoding */ 96 | #define KEY_IGNORE 0xE012 97 | #define KEY_PRTSCR 0xE07C 98 | #define KEY_RIGHTCONTROL 0XE014 99 | /* Sometimes called windows key */ 100 | #define KEY_L_GUI 0xE01F 101 | #define KEY_R_GUI 0xE027 102 | #define KEY_MENU 0xE02F 103 | /* Break is CTRL + PAUSE generated inside keyboard */ 104 | #define KEY_BREAK 0xE07E 105 | #define KEY_HOME 0xE06C 106 | #define KEY_END 0xE069 107 | #define KEY_PGUP 0xE07D 108 | #define KEY_PGDN 0xE07A 109 | #define KEY_LEFTARROW 0xE06B 110 | #define KEY_RIGHTARROW 0xE074 111 | #define KEY_UPARROW 0xE075 112 | #define KEY_DOWNARROW 0xE072 113 | #define KEY_INSERT 0xE070 114 | #define KEY_DELETE 0xE071 115 | #define KEY_KP_ENTER 0xE05A 116 | #define KEY_KP_DIV 0xE04A 117 | #define KEY_NEXT_TR 0XE04D 118 | #define KEY_PREV_TR 0XE015 119 | #define KEY_STOP 0XE038 120 | #define KEY_PLAY 0XE034 121 | #define KEY_MUTE 0XE023 122 | #define KEY_VOL_UP 0XE032 123 | #define KEY_VOL_DN 0XE021 124 | #define KEY_MEDIA 0XE050 125 | #define KEY_EMAIL 0XE048 126 | #define KEY_CALC 0XE02B 127 | #define KEY_COMPUTER 0XE040 128 | 129 | #include "fabgl.h" 130 | 131 | void Ps2_Initialize(fabgl::PS2Controller* inputController); 132 | 133 | #ifdef __cplusplus 134 | extern "C" { 135 | #endif 136 | 137 | #include 138 | int32_t Ps2_GetScancode(); 139 | char Ps2_ConvertScancode(int32_t scanCode); 140 | 141 | bool Ps2_isMouseAvailable(); 142 | uint8_t Ps2_getMouseButtons(); 143 | uint8_t Ps2_getMouseX(); 144 | uint8_t Ps2_getMouseY(); 145 | 146 | #ifdef __cplusplus 147 | } 148 | #endif 149 | 150 | #endif 151 | -------------------------------------------------------------------------------- /include/settings.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __SETTINGS_H__ 3 | #define __SETTINGS_H__ 4 | 5 | #define ZX128K 6 | 7 | /////////////////////////////////////////////////////////////////////////////// 8 | // CPU core selection 9 | // 10 | // one of the following MUST be defined: 11 | // - CPU_LINKEFONG: use Lin Ke-Fong's core https://github.com/anotherlin/z80emu 12 | // - CPU_JLSANCHEZ: use José Luis Sánchez's core https://github.com/jsanchezv/z80cpp 13 | // - CPU_STEVECHECKOWAY: use Steve Checkoway's core https://github.com/stevecheckoway/libzel 14 | // - CPU_ANDREWEISSFLOG: use Andre Weissflog's core https://github.com/floooh/chips 15 | /////////////////////////////////////////////////////////////////////////////// 16 | //#define CPU_LINKEFONG 17 | #define CPU_JLSANCHEZ 18 | //#define CPU_STEVECHECKOWAY 19 | //#define CPU_ANDREWEISSFLOG 20 | 21 | // Do not undefine this. Current version doesn't support reading from flash 22 | #define SDCARD 23 | 24 | #define BEEPER 25 | #define BEEPER_PIN gpio_num_t::GPIO_NUM_25 26 | 27 | #define RESOLUTION VGA_640x480_60Hz 28 | #define SCREEN_WIDTH 80 29 | #define SCREEN_HEIGHT 60 30 | 31 | #define DEBUG_BAND_COLORS 0x2A10 32 | 33 | // SD Card 34 | #define PIN_NUM_MISO 2 35 | #define PIN_NUM_MOSI 12 36 | #define PIN_NUM_CLK 14 37 | #define PIN_NUM_CS gpio_num_t::GPIO_NUM_13 38 | #define SDCARD_PATH "/sdcard" 39 | 40 | // ESP_LOGx 41 | #define TAG "z80emu" 42 | 43 | #ifdef ZX128K 44 | #define TSTATES_PER_FRAME 70908 45 | #else 46 | #define TSTATES_PER_FRAME 69888 47 | #endif 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /include/volume.h: -------------------------------------------------------------------------------- 1 | #ifndef _VOLUME_H 2 | #define _VOLUME_H 3 | 4 | extern const unsigned char volume[]; 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /include/z80Emulator.h: -------------------------------------------------------------------------------- 1 | #ifndef __Z80EMULATOR_INCLUDED__ 2 | #define __Z80EMULATOR_INCLUDED__ 3 | 4 | // Abstraction for z80 Emulator 5 | 6 | #include 7 | #include "../include/emulator.h" 8 | #include "z80Environment.h" 9 | #include "ClassProperties.h" 10 | #undef F 11 | 12 | struct z80Emulator 13 | { 14 | private: 15 | Z80Environment* _environment; 16 | 17 | uint32_t get_TStates() { return this->_environment->TStates; } 18 | void set_TStates(uint32_t value) { this->_environment->TStates = value; } 19 | 20 | uint8_t get_A(); void set_A(uint8_t); 21 | uint8_t get_F(); void set_F(uint8_t); 22 | uint8_t get_B(); void set_B(uint8_t); 23 | uint8_t get_C(); void set_C(uint8_t); 24 | uint8_t get_D(); void set_D(uint8_t); 25 | uint8_t get_E(); void set_E(uint8_t); 26 | uint8_t get_H(); void set_H(uint8_t); 27 | uint8_t get_L(); void set_L(uint8_t); 28 | 29 | uint8_t get_I(); void set_I(uint8_t); 30 | uint8_t get_R(); void set_R(uint8_t); 31 | 32 | uint16_t get_AF(); void set_AF(uint16_t); 33 | uint16_t get_BC(); void set_BC(uint16_t); 34 | uint16_t get_DE(); void set_DE(uint16_t); 35 | uint16_t get_HL(); void set_HL(uint16_t); 36 | 37 | uint16_t get_AFx(); void set_AFx(uint16_t); 38 | uint16_t get_BCx(); void set_BCx(uint16_t); 39 | uint16_t get_DEx(); void set_DEx(uint16_t); 40 | uint16_t get_HLx(); void set_HLx(uint16_t); 41 | 42 | uint16_t get_IX(); void set_IX(uint16_t); 43 | uint16_t get_IY(); void set_IY(uint16_t); 44 | uint16_t get_SP(); void set_SP(uint16_t); 45 | 46 | uint16_t get_PC(); void set_PC(uint16_t); 47 | 48 | uint8_t get_IFF1(); void set_IFF1(uint8_t); 49 | uint8_t get_IFF2(); void set_IFF2(uint8_t); 50 | uint8_t get_IM(); void set_IM(uint8_t); 51 | 52 | public: 53 | void setup(Z80Environment* environment); 54 | void reset(); 55 | int emulate(int number_cycles); 56 | void interrupt(); 57 | 58 | CLASS(z80Emulator); 59 | 60 | PROPERTY(uint32_t, TStates); 61 | 62 | // 8 bit registers 63 | PROPERTY(uint8_t, A); // Accumulator 64 | PROPERTY(uint8_t, F); // Flags 65 | PROPERTY(uint8_t, B); 66 | PROPERTY(uint8_t, C); 67 | PROPERTY(uint8_t, D); 68 | PROPERTY(uint8_t, E); 69 | PROPERTY(uint8_t, H); 70 | PROPERTY(uint8_t, L); 71 | 72 | // Other registers 73 | PROPERTY(uint8_t, I); // Interrupt vector 74 | PROPERTY(uint8_t, R); // Refresh counter 75 | 76 | // 16 bit registers 77 | PROPERTY(uint16_t, AF); 78 | PROPERTY(uint16_t, BC); 79 | PROPERTY(uint16_t, DE); 80 | PROPERTY(uint16_t, HL); 81 | 82 | // Alternate (shadow) registers 83 | PROPERTY(uint16_t, AFx); 84 | PROPERTY(uint16_t, BCx); 85 | PROPERTY(uint16_t, DEx); 86 | PROPERTY(uint16_t, HLx); 87 | 88 | // Index registers 89 | PROPERTY(uint16_t, IX); 90 | PROPERTY(uint16_t, IY); 91 | PROPERTY(uint16_t, SP); 92 | 93 | // Program counter 94 | PROPERTY(uint16_t, PC); 95 | 96 | // Mode 97 | PROPERTY(uint8_t, IFF1); // Interrupt flip-flop 1 (its value cannot be read) 98 | PROPERTY(uint8_t, IFF2); // Interrupt flip-flop 2 99 | PROPERTY(uint8_t, IM); // Interrupt mode 100 | 101 | z80Emulator() : TStates(0), 102 | A(this), F(this), B(this), C(this), D(this), E(this), H(this), L(this), 103 | I(this), R(this), AF(this), BC(this), DE(this), HL(this), 104 | AFx(this), BCx(this), DEx(this), HLx(this), 105 | IX(this), IY(this), SP(this), PC(this), 106 | IFF1(this), IFF2(this), IM(this) 107 | { 108 | } 109 | }; 110 | 111 | #endif 112 | 113 | -------------------------------------------------------------------------------- /include/z80Environment.h: -------------------------------------------------------------------------------- 1 | #ifndef __Z80ENVIRONMENT_INCLUDED__ 2 | #define __Z80ENVIRONMENT_INCLUDED__ 3 | 4 | // Class for the Z80 enviroment (ROM, RAM, I/O) 5 | 6 | #include 7 | #include "MemoryPage.h" 8 | #include "RamPage.h" 9 | #include "ClassProperties.h" 10 | #include "RamVideoPage.h" 11 | 12 | typedef struct 13 | { 14 | union 15 | { 16 | uint8_t Bits; 17 | 18 | struct 19 | { 20 | uint8_t RamBank : 3; 21 | uint8_t ShadowScreen : 1; 22 | uint8_t RomSelect : 1; 23 | uint8_t PagingLock : 1; 24 | } __attribute__((packed)); 25 | }; 26 | } MemorySelect; 27 | 28 | class VideoController; 29 | 30 | class Z80Environment 31 | { 32 | private: 33 | uint8_t _borderColor; 34 | uint8_t get_BorderColor(); void set_BorderColor(uint8_t); 35 | 36 | RamPage _rom0; 37 | RamPage _rom1; 38 | 39 | RamPage _ram0; 40 | RamPage _ram1; 41 | RamPage _ram2; 42 | RamPage _ram3; 43 | RamPage _ram4; 44 | SpectrumScreenData _mainScreenData; 45 | RamVideoPage _ram5; 46 | RamPage _ram6; 47 | SpectrumScreenData _shadowScreenData; 48 | RamVideoPage _ram7; 49 | 50 | public: 51 | VideoController* Screen; 52 | RamPage* Rom[2]; 53 | MemoryPage* Ram[8]; 54 | MemorySelect MemoryState; 55 | 56 | // CPU Tstates elapsed in current frame 57 | uint32_t TStates; 58 | 59 | CLASS(Z80Environment); 60 | PROPERTY(uint8_t, BorderColor); 61 | 62 | Z80Environment(VideoController* screen); 63 | 64 | void Initialize(); 65 | 66 | void SetState(uint8_t memoryState); 67 | uint8_t ReadByte(uint16_t address); 68 | uint16_t ReadWord(uint16_t address); 69 | void WriteByte(uint16_t address, uint8_t data); 70 | void WriteWord(uint16_t address, uint16_t data); 71 | uint8_t Input(uint8_t portLow, uint8_t portHigh); 72 | void Output(uint8_t portLow, uint8_t portHigh, uint8_t data); 73 | 74 | static uint16_t FromSpectrumColor(uint8_t sinclairColor); 75 | static uint8_t ToSpectrumColor(uint16_t color); 76 | }; 77 | 78 | #endif 79 | 80 | -------------------------------------------------------------------------------- /include/z80Input.h: -------------------------------------------------------------------------------- 1 | #ifndef _Z80INPUT_H_ 2 | #define _Z80INPUT_H_ 3 | 4 | #include 5 | 6 | extern uint8_t indata[]; 7 | 8 | enum { 9 | ZX_KEY_SHIFT = 0, 10 | ZX_KEY_Z, 11 | ZX_KEY_X, 12 | ZX_KEY_C, 13 | ZX_KEY_V, 14 | 15 | ZX_KEY_A, 16 | ZX_KEY_S, 17 | ZX_KEY_D, 18 | ZX_KEY_F, 19 | ZX_KEY_G, 20 | 21 | ZX_KEY_Q, 22 | ZX_KEY_W, 23 | ZX_KEY_E, 24 | ZX_KEY_R, 25 | ZX_KEY_T, 26 | 27 | ZX_KEY_1, 28 | ZX_KEY_2, 29 | ZX_KEY_3, 30 | ZX_KEY_4, 31 | ZX_KEY_5, 32 | 33 | ZX_KEY_0, 34 | ZX_KEY_9, 35 | ZX_KEY_8, 36 | ZX_KEY_7, 37 | ZX_KEY_6, 38 | 39 | ZX_KEY_P, 40 | ZX_KEY_O, 41 | ZX_KEY_I, 42 | ZX_KEY_U, 43 | ZX_KEY_Y, 44 | 45 | ZX_KEY_ENTER, 46 | ZX_KEY_L, 47 | ZX_KEY_K, 48 | ZX_KEY_J, 49 | ZX_KEY_H, 50 | 51 | ZX_KEY_SPACE, 52 | ZX_KEY_SYM, 53 | ZX_KEY_M, 54 | ZX_KEY_N, 55 | ZX_KEY_B, 56 | 57 | ZX_KEY_LAST 58 | }; 59 | 60 | extern const uint8_t keyaddr[ZX_KEY_LAST]; 61 | extern const uint8_t keybuf[ZX_KEY_LAST]; 62 | 63 | bool OnKey(uint32_t scanCode, bool isKeyUp); 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /include/z80main.h: -------------------------------------------------------------------------------- 1 | #ifndef __ZXMAIN_INCLUDED__ 2 | #define __ZXMAIN_INCLUDED__ 3 | 4 | #include "z80Emulator.h" 5 | #include "z80Environment.h" 6 | #include "ay3-8912-state.h" 7 | 8 | extern Sound::Ay3_8912_state _ay3_8912; 9 | extern z80Emulator Z80cpu; 10 | 11 | void zx_setup(Z80Environment* spectrumScreen); 12 | int32_t zx_loop(); 13 | void zx_reset(); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /include/z80snapshot.h: -------------------------------------------------------------------------------- 1 | #ifndef __Z80SNAPSHOT_INCLUDED__ 2 | #define __Z80SNAPSHOT_INCLUDED__ 3 | 4 | #include 5 | #include "File.h" 6 | 7 | using namespace std; 8 | 9 | namespace zx 10 | { 11 | 12 | bool LoadZ80Snapshot(File* file, uint8_t buffer1[0x4000], uint8_t buffer2[0x4000]); 13 | bool LoadScreenFromZ80Snapshot(File* file, uint8_t buffer1[0x4000]); 14 | bool LoadScreenshot(File* file, uint8_t buffer1[0x4000]); 15 | bool SaveZ80Snapshot(File* file, uint8_t buffer1[0x4000], uint8_t buffer2[0x4000]); 16 | 17 | } 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /lib/z80_AW/LICENSE: -------------------------------------------------------------------------------- 1 | zlib/libpng license 2 | 3 | Copyright (c) 2018 Andre Weissflog 4 | This software is provided 'as-is', without any express or implied warranty. 5 | In no event will the authors be held liable for any damages arising from the 6 | use of this software. 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 1. The origin of this software must not be misrepresented; you must not 11 | claim that you wrote the original software. If you use this software in a 12 | product, an acknowledgment in the product documentation would be 13 | appreciated but is not required. 14 | 2. Altered source versions must be plainly marked as such, and must not 15 | be misrepresented as being the original software. 16 | 3. This notice may not be removed or altered from any source 17 | distribution. 18 | -------------------------------------------------------------------------------- /lib/z80_AW/README.md: -------------------------------------------------------------------------------- 1 | # chips 2 | 3 | [![Build Status](https://github.com/floooh/chips/workflows/build_and_test/badge.svg)](https://github.com/floooh/chips/actions) 4 | 5 | A toolbox of 8-bit chip-emulators, helper code and complete embeddable 6 | system emulators in dependency-free C headers (a subset of C99 that 7 | compiles on gcc, clang and cl.exe). 8 | 9 | Tests and example code is in a separate repo: https://github.com/floooh/chips-test 10 | 11 | The example emulators, compiled to WebAssembly: https://floooh.github.io/tiny8bit/ 12 | 13 | For schematics, manuals and research material, see: https://github.com/floooh/emu-info 14 | 15 | The USP of the chip emulators is that they communicate with the outside world through 16 | a 'pin bit mask': A 'tick' function takes an uint64_t as input where the bits 17 | represent the chip's in/out pins, the tick function inspects the pin 18 | bits, computes one tick, and returns a (potentially modified) pin bit mask. 19 | 20 | A complete emulated computer then more or less just wires those chip emulators 21 | together just like on a breadboard. 22 | 23 | In reality, most emulators are not quite as 'pure' (as this would affect performance 24 | too much or complicate the emulation): some chip emulators have a small number 25 | of callback functions and the adress decoding in the system emulators often 26 | take shortcuts instead of simulating the actual address decoding chips 27 | (with one exception: the lc80 emulator). 28 | -------------------------------------------------------------------------------- /lib/z80_AW/z80.c: -------------------------------------------------------------------------------- 1 | #define CHIPS_IMPL 2 | #include "z80_AW.h" -------------------------------------------------------------------------------- /lib/z80_JLS/README.md: -------------------------------------------------------------------------------- 1 | ## z80cpp 2 | #### Z80 core in C++ 3 | 4 | That's a port from Java to C++ of my [Z80Core](https://github.com/jsanchezv/Z80Core). 5 | 6 | To build: 7 | ``` 8 | mkdir build 9 | cd build 10 | cmake .. 11 | make 12 | ``` 13 | Then, you have an use case at dir *example*. 14 | 15 | The core have the same features of [Z80Core](https://github.com/jsanchezv/Z80Core): 16 | 17 | * Complete instruction set emulation 18 | * Emulates the undocumented bits 3 & 5 from flags register 19 | * Emulates the MEMPTR register (known as WZ in official Zilog documentation) 20 | * Strict execution order for every instruction 21 | * Precise timing for all instructions, totally decoupled from the core 22 | 23 | *jspeccy at gmail dot com* 24 | -------------------------------------------------------------------------------- /lib/z80_JLS/z80operations.h: -------------------------------------------------------------------------------- 1 | #ifndef Z80OPERATIONS_H 2 | #define Z80OPERATIONS_H 3 | 4 | #include 5 | 6 | class Z80operations { 7 | public: 8 | Z80operations(void) {}; 9 | 10 | virtual ~Z80operations() {}; 11 | 12 | /* Read opcode from RAM */ 13 | virtual uint8_t fetchOpcode(uint16_t address) = 0; 14 | 15 | /* Read/Write byte from/to RAM */ 16 | virtual uint8_t peek8(uint16_t address) = 0; 17 | virtual void poke8(uint16_t address, uint8_t value) = 0; 18 | 19 | /* Read/Write word from/to RAM */ 20 | virtual uint16_t peek16(uint16_t adddress) = 0; 21 | virtual void poke16(uint16_t address, RegisterPair word) = 0; 22 | 23 | /* In/Out byte from/to IO Bus */ 24 | virtual uint8_t inPort(uint16_t port) = 0; 25 | virtual void outPort(uint16_t port, uint8_t value) = 0; 26 | 27 | /* Put an address on bus lasting 'tstates' cycles */ 28 | virtual void addressOnBus(uint16_t address, int32_t wstates) = 0; 29 | 30 | /* Clocks needed for processing INT and NMI */ 31 | virtual void interruptHandlingTime(int32_t wstates) = 0; 32 | 33 | /* Callback to know when the INT signal is active */ 34 | virtual bool isActiveINT(void) = 0; 35 | 36 | #ifdef WITH_BREAKPOINT_SUPPORT 37 | /* Callback for notify at PC address */ 38 | virtual uint8_t breakpoint(uint16_t address, uint8_t opcode) = 0; 39 | #endif 40 | 41 | #ifdef WITH_EXEC_DONE 42 | /* Callback to notify that one instruction has ended */ 43 | virtual void execDone(void) = 0; 44 | #endif 45 | }; 46 | 47 | #endif // Z80OPERATIONS_H 48 | -------------------------------------------------------------------------------- /lib/z80_LKF/README.txt: -------------------------------------------------------------------------------- 1 | z80emu 2 | Version 1.1.3 3 | 4 | Copyright (c) 2012-2017 Lin Ke-Fong 5 | 6 | z80emu is a free portable Z80 processor emulator. Its performance is comparable 7 | to other Z80 emulators. It emulates all undocumented features as described in 8 | "The Undocumented Z80 Documented Version 0.91" and passes both the zexdoc and 9 | zexall Z80 instruction exerciser tests. Code is pure ANSI C. 10 | 11 | The Makefile will compile a sample program to run zexdoc.com and zexall.com. 12 | Only needed CP/M BIOS functions are emulated. See zextest.c for details. 13 | 14 | All structures and functions are documented in the code, please read the header 15 | files. To use z80emu in your programs, modify z80config.h to configure the 16 | emulator, and in z80user.h, write the memory read/write and port input/output 17 | macros to interface with your system to emulate. The macros for zextest.c are 18 | simple examples. You shouldn't have to modify z80emu.c or any other files. 19 | 20 | z80emu works a little bit differently from other emulators. Instead of a switch 21 | case for all opcodes, it converts them to "generic" instructions and then do 22 | the switch case. Registers and operands are then decoded during execution. All 23 | of this is done using several tables (see maketables.c). The code is more 24 | compact, which should make it fit inside L1 instruction cache. 25 | 26 | You may find further information regarding the Z80 on http://www.z80.info. This 27 | emulator is heavily based on the information found on this website. And I would 28 | like to thank all its contributors. In particular, Sean Young for "The 29 | Undocumented Z80 Documented" and Christian Dinu for "Decoding Z80 Opcodes". The 30 | zexdoc and zexall programs have been written by Frank D. Cringles. 31 | 32 | I would like to thank Hugo Clement, Guy Hermann, Julien Montmartin, and Pierre 33 | Rousseau for review and testing. 34 | 35 | Sebastien Katz, Pierre Rousseau, and Thibaut Mattern made me start this project 36 | as part of a Sega Master System console emulator. That was long ago, back at 37 | university. I hope I will complete a full emulator someday. 38 | 39 | Feel free to send bug reports, comments, and suggestions to: 40 | 41 | anotherlin@gmail.com 42 | 43 | Revision history: 44 | 45 | 1.1.3 (18th September 2017) 46 | 47 | Another gross mistake fixed in the interrupt handling code: In mode 2, the 48 | interrupt vector wasn't dereferenced! Thank you rootednode for finding and 49 | reporting this issue. 50 | 51 | 1.1.2 (20th June 2017) 52 | 53 | The implementation of the "IM" Interrupt Mode selection instruction was 54 | completely wrong. The zextests don't check it and it seems that it was never 55 | encountered in the few systems emulated so far. This bug has gone unnoticed 56 | until now. Thank you Chris Despoinidis for reporting that bug. 57 | 58 | 1.1.1 (28th February 2017) 59 | 60 | I made the wrong assumption that char is signed by default. It is in fact 61 | implementation (compiler) dependent. So explicit (signed char) casts are now 62 | used instead. Otherwise, with unsigned char compilers, the zextests will have 63 | a few failing CRC checks. In fact, relative jumps will also fail to emulate 64 | correctly. Thank you SmallRoomLabs for finding and reporting this issue, and 65 | Mark Allender for verifying it. 66 | 67 | 1.1.0 "first-app" (22th December 2016) 68 | 69 | Ten years already! But hopefully, work on the Sega Master System emulator has 70 | restarted. For the first application of z80emu, redesign its user API, the 71 | original being really bad. Add 2 header files for the user to customize instead 72 | of having to modify z80emu.h, update the documentation: make it clear what 73 | variables each macro can expect, compute the register decoding tables only once 74 | at reset, and add a (void *) context for interfacing with the system to 75 | emulate. All these changes should make the API better organized and easier to 76 | use. 77 | 78 | 1.0.2 (12th February 2014) 79 | 80 | Conditional relative jump (JR_DD_E) instructions such as "jr NZ, loop_label" 81 | were not decoded correctly, just a silly bug in a macro to extract bits. The 82 | zexdoc and zexall exercisers have excellent coverage of ALU instructions, but 83 | they both don't feature a single "JR" instruction! Hence this (very obvious) 84 | bug has gone unnoticed. Thank you Marcelo Dantas. 85 | 86 | 1.0.1 (14th November 2012) 87 | 88 | Original implementation of zextest used int64_t. In retrospect, this was a poor 89 | choice. Using double to count cycles is ANSI and has more than enough precision 90 | to do so. Thank you Chris Pressey. 91 | 92 | 1.0.0 (13th March 2012) 93 | 94 | Initial commit on github. Actually, the code has been almost entirely written 95 | during the summer of 2006. 96 | -------------------------------------------------------------------------------- /lib/z80_LKF/instructions.h: -------------------------------------------------------------------------------- 1 | /* instructions.h 2 | * Declaration of the "generic" instructions emulated. 3 | * 4 | * Copyright (c) 2012 Lin Ke-Fong 5 | * 6 | * This code is free, do whatever you want with it. 7 | */ 8 | 9 | /* Some "instructions" handle two opcodes hence they need their encodings to 10 | * be able to distinguish them. 11 | */ 12 | 13 | #define OPCODE_LD_A_I 0x57 14 | #define OPCODE_LD_I_A 0x47 15 | 16 | #define OPCODE_LDI 0xa0 17 | #define OPCODE_LDIR 0xb0 18 | #define OPCODE_CPI 0xa1 19 | #define OPCODE_CPIR 0xb1 20 | 21 | #define OPCODE_RLD 0x6f 22 | 23 | #if defined(Z80_CATCH_RETI) && defined(Z80_CATCH_RETN) 24 | # define OPCODE_RETI 0x4d 25 | #endif 26 | 27 | #define OPCODE_INI 0xa2 28 | #define OPCODE_INIR 0xb2 29 | #define OPCODE_OUTI 0xa3 30 | #define OPCODE_OTIR 0xb3 31 | 32 | /* Instruction numbers, opcodes are converted to these numbers using tables 33 | * generated by maketables.c. 34 | */ 35 | 36 | enum { 37 | 38 | /* 8-bit load group. */ 39 | 40 | LD_R_R, 41 | LD_R_N, 42 | 43 | LD_R_INDIRECT_HL, 44 | LD_INDIRECT_HL_R, 45 | LD_INDIRECT_HL_N, 46 | 47 | LD_A_INDIRECT_BC, 48 | LD_A_INDIRECT_DE, 49 | LD_A_INDIRECT_NN, 50 | LD_INDIRECT_BC_A, 51 | LD_INDIRECT_DE_A, 52 | LD_INDIRECT_NN_A, 53 | 54 | LD_A_I_LD_A_R, /* Handle "LD A, I" and "LD A, R". */ 55 | LD_I_A_LD_R_A, /* Handle "LD I, A" and "LD I, A". */ 56 | 57 | /* 16-bit load group. */ 58 | 59 | LD_RR_NN, 60 | 61 | LD_HL_INDIRECT_NN, 62 | LD_RR_INDIRECT_NN, 63 | LD_INDIRECT_NN_HL, 64 | LD_INDIRECT_NN_RR, 65 | 66 | LD_SP_HL, 67 | 68 | PUSH_SS, 69 | POP_SS, 70 | 71 | /* Exchange, block transfer, and search group. */ 72 | 73 | EX_DE_HL, 74 | EX_AF_AF_PRIME, 75 | EXX, 76 | EX_INDIRECT_SP_HL, 77 | 78 | LDI_LDD, /* Handle "LDI" and "LDD". */ 79 | LDIR_LDDR, /* Handle "LDIR" and "LDDR". */ 80 | 81 | CPI_CPD, /* Handle "CPI" and "CPD". */ 82 | CPIR_CPDR, /* Handle "CPIR" and "CPDR". */ 83 | 84 | /* 8-bit arithmetic and logical group. */ 85 | 86 | ADD_R, 87 | ADD_N, 88 | ADD_INDIRECT_HL, 89 | 90 | ADC_R, 91 | ADC_N, 92 | ADC_INDIRECT_HL, 93 | 94 | SUB_R, 95 | SUB_N, 96 | SUB_INDIRECT_HL, 97 | 98 | SBC_R, 99 | SBC_N, 100 | SBC_INDIRECT_HL, 101 | 102 | AND_R, 103 | AND_N, 104 | AND_INDIRECT_HL, 105 | 106 | XOR_R, 107 | XOR_N, 108 | XOR_INDIRECT_HL, 109 | 110 | OR_R, 111 | OR_N, 112 | OR_INDIRECT_HL, 113 | 114 | CP_R, 115 | CP_N, 116 | CP_INDIRECT_HL, 117 | 118 | INC_R, 119 | INC_INDIRECT_HL, 120 | DEC_R, 121 | DEC_INDIRECT_HL, 122 | 123 | /* 16-bit arithmetic group. */ 124 | 125 | ADD_HL_RR, 126 | 127 | ADC_HL_RR, 128 | SBC_HL_RR, 129 | 130 | INC_RR, 131 | DEC_RR, 132 | 133 | /* General-purpose arithmetic and CPU control group. */ 134 | 135 | DAA, 136 | 137 | CPL, 138 | NEG, 139 | 140 | CCF, 141 | SCF, 142 | 143 | NOP, 144 | HALT, 145 | 146 | DI, 147 | EI, 148 | 149 | IM_N, 150 | 151 | /* Rotate and shift group. */ 152 | 153 | RLCA, 154 | RLA, 155 | RRCA, 156 | RRA, 157 | 158 | RLC_R, 159 | RLC_INDIRECT_HL, 160 | RL_R, 161 | RL_INDIRECT_HL, 162 | RRC_R, 163 | RRC_INDIRECT_HL, 164 | RR_R, 165 | RR_INDIRECT_HL, 166 | SLA_R, 167 | SLA_INDIRECT_HL, 168 | SLL_R, 169 | SLL_INDIRECT_HL, 170 | SRA_R, 171 | SRA_INDIRECT_HL, 172 | SRL_R, 173 | SRL_INDIRECT_HL, 174 | 175 | RLD_RRD, /* Handle "RLD" and "RRD". */ 176 | 177 | /* Bit set, reset, and test group. */ 178 | 179 | BIT_B_R, 180 | BIT_B_INDIRECT_HL, 181 | SET_B_R, 182 | SET_B_INDIRECT_HL, 183 | RES_B_R, 184 | RES_B_INDIRECT_HL, 185 | 186 | /* Jump group. */ 187 | 188 | JP_NN, 189 | JP_CC_NN, 190 | JR_E, 191 | JR_DD_E, 192 | JP_HL, 193 | DJNZ_E, 194 | 195 | /* Call and return group. */ 196 | 197 | CALL_NN, 198 | CALL_CC_NN, 199 | RET, 200 | RET_CC, 201 | 202 | RETI_RETN, /* Handle "RETI" and "RETN". */ 203 | 204 | RST_P, 205 | 206 | /* Input and output group. */ 207 | 208 | IN_A_N, 209 | IN_R_C, /* Correctly handle undocumented "IN F, (C)" 210 | * instruction. 211 | */ 212 | 213 | INI_IND, /* Handle "INI" and "IND". */ 214 | INIR_INDR, /* Handle "INIR" and "INDR". */ 215 | 216 | OUT_N_A, 217 | OUT_C_R, /* Correctly handle undocumented "OUT (C), 0" 218 | * instruction. 219 | */ 220 | 221 | OUTI_OUTD, /* Handle "OUTI" and "OUTD".*/ 222 | OTIR_OTDR, /* Handle "OTIR" and "OTDR". */ 223 | 224 | /* Prefix group. */ 225 | 226 | CB_PREFIX, 227 | DD_PREFIX, 228 | FD_PREFIX, 229 | ED_PREFIX, 230 | 231 | /* Special instruction group. */ 232 | 233 | ED_UNDEFINED /* ED_UNDEFINED is used to catch undefined 234 | * 0xed prefixed opcodes. 235 | */ 236 | 237 | }; 238 | -------------------------------------------------------------------------------- /lib/z80_LKF/z80config.h: -------------------------------------------------------------------------------- 1 | /* z80config.h 2 | * Define or comment out macros in this file to configure the emulator. 3 | * 4 | * Copyright (c) 2016, 2017 Lin Ke-Fong 5 | * 6 | * This code is free, do whatever you want with it. 7 | */ 8 | 9 | #ifndef __Z80CONFIG_INCLUDED__ 10 | #define __Z80CONFIG_INCLUDED__ 11 | 12 | /* Define this macro if the host processor is big endian. */ 13 | 14 | /* #define Z80_BIG_ENDIAN */ 15 | 16 | /* Emulation can be speed up a little bit by emulating only the documented 17 | * flags. 18 | */ 19 | 20 | /* #define Z80_DOCUMENTED_FLAGS_ONLY */ 21 | 22 | /* HALT, DI, EI, RETI, and RETN instructions can be catched. When such an 23 | * instruction is catched, the emulator is stopped and the PC register points 24 | * at the opcode to be executed next. The catched instruction can be determined 25 | * from the Z80_STATE's status value. Keep in mind that no interrupt can be 26 | * accepted at the instruction right after a DI or EI on an actual processor. 27 | */ 28 | 29 | /* 30 | #define Z80_CATCH_HALT 31 | #define Z80_CATCH_DI 32 | #define Z80_CATCH_EI 33 | #define Z80_CATCH_RETI 34 | #define Z80_CATCH_RETN 35 | */ 36 | 37 | /* Undefined 0xed prefixed opcodes may be catched, otherwise they are treated 38 | * like NOP instructions. When one is catched, Z80_STATUS_ED_UNDEFINED is set 39 | * in Z80_STATE's status member and the PC register points at the 0xed prefix 40 | * before the undefined opcode. 41 | */ 42 | 43 | /* #define Z80_CATCH_ED_UNDEFINED */ 44 | 45 | /* The emulator cannot be stopped between prefixed opcodes. This can be a 46 | * problem if there is a long sequence of 0xdd and/or 0xfd prefixes. But if 47 | * Z80_PREFIX_FAILSAFE is defined, it will always be able to stop after at 48 | * least numbers_cycles are executed, in which case Z80_STATE's status is set 49 | * to Z80_STATUS_PREFIX. Note that if the memory where the opcodes are read, 50 | * has wait states (slow memory), then the additional cycles for a one byte 51 | * fetch (the non executed prefix) must be substracted. Even if it is safer, 52 | * most program won't need this feature. 53 | */ 54 | 55 | /* #define Z80_PREFIX_FAILSAFE */ 56 | 57 | /* By defining this macro, the emulator will always fetch the displacement or 58 | * address of a conditionnal jump or call instruction, even if the condition 59 | * is false and the fetch can be avoided. Define this macro if you need to 60 | * account for memory wait states on code read. 61 | */ 62 | 63 | /* #define Z80_FALSE_CONDITION_FETCH */ 64 | 65 | /* It may be possible to overwrite the opcode of the currently executing LDIR, 66 | * LDDR, INIR, or OTDR instruction. Define this macro if you need to handle 67 | * these pathological cases. 68 | */ 69 | 70 | /* #define Z80_HANDLE_SELF_MODIFYING_CODE */ 71 | 72 | /* For interrupt mode 2, bit 0 of the 16-bit address to the interrupt vector 73 | * can be masked to zero. Some documentation states that this bit is forced to 74 | * zero. For instance, Zilog's application note about interrupts, states that 75 | * "only 7 bits are required" and "the least significant bit is zero". Yet, 76 | * this is quite unclear, even from Zilog's manuals. So this is left as an 77 | * option. 78 | */ 79 | 80 | /* #define Z80_MASK_IM2_VECTOR_ADDRESS */ 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /lib/z80_LKF/z80emu.h: -------------------------------------------------------------------------------- 1 | /* z80emu.h 2 | * Main header of z80emu. Don't modify this file directly. Use z80config.h and 3 | * z80user.h to customize the emulator to your need. 4 | * 5 | * Copyright (c) 2012, 2016 Lin Ke-Fong 6 | * 7 | * This code is free, do whatever you want with it. 8 | */ 9 | 10 | #ifndef __Z80EMU_INCLUDED__ 11 | #define __Z80EMU_INCLUDED__ 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | #include "z80config.h" 18 | 19 | /* If Z80_STATE's status is non-zero, the emulation has been stopped for some 20 | * reason other than emulating the requested number of cycles. See z80config.h. 21 | */ 22 | 23 | enum { 24 | 25 | Z80_STATUS_HALT = 1, 26 | Z80_STATUS_DI, 27 | Z80_STATUS_EI, 28 | Z80_STATUS_RETI, 29 | Z80_STATUS_RETN, 30 | Z80_STATUS_ED_UNDEFINED, 31 | Z80_STATUS_PREFIX 32 | 33 | }; 34 | 35 | /* The main registers are stored inside Z80_STATE as an union of arrays named 36 | * registers. They are referenced using indexes. Words are stored in the 37 | * endianness of the host processor. The alternate set of word registers AF', 38 | * BC', DE', and HL' is stored in the alternates member of Z80_STATE, as an 39 | * array using the same ordering. 40 | */ 41 | 42 | #ifdef Z80_BIG_ENDIAN 43 | 44 | # define Z80_B 0 45 | # define Z80_C 1 46 | # define Z80_D 2 47 | # define Z80_E 3 48 | # define Z80_H 4 49 | # define Z80_L 5 50 | # define Z80_A 6 51 | # define Z80_F 7 52 | 53 | # define Z80_IXH 8 54 | # define Z80_IXL 9 55 | # define Z80_IYH 10 56 | # define Z80_IYL 11 57 | 58 | #else 59 | 60 | # define Z80_B 1 61 | # define Z80_C 0 62 | # define Z80_D 3 63 | # define Z80_E 2 64 | # define Z80_H 5 65 | # define Z80_L 4 66 | # define Z80_A 7 67 | # define Z80_F 6 68 | 69 | # define Z80_IXH 9 70 | # define Z80_IXL 8 71 | # define Z80_IYH 11 72 | # define Z80_IYL 10 73 | 74 | #endif 75 | 76 | #define Z80_BC 0 77 | #define Z80_DE 1 78 | #define Z80_HL 2 79 | #define Z80_AF 3 80 | 81 | #define Z80_IX 4 82 | #define Z80_IY 5 83 | #define Z80_SP 6 84 | 85 | /* Z80's flags. */ 86 | 87 | #define Z80_S_FLAG_SHIFT 7 88 | #define Z80_Z_FLAG_SHIFT 6 89 | #define Z80_Y_FLAG_SHIFT 5 90 | #define Z80_H_FLAG_SHIFT 4 91 | #define Z80_X_FLAG_SHIFT 3 92 | #define Z80_PV_FLAG_SHIFT 2 93 | #define Z80_N_FLAG_SHIFT 1 94 | #define Z80_C_FLAG_SHIFT 0 95 | 96 | #define Z80_S_FLAG (1 << Z80_S_FLAG_SHIFT) 97 | #define Z80_Z_FLAG (1 << Z80_Z_FLAG_SHIFT) 98 | #define Z80_Y_FLAG (1 << Z80_Y_FLAG_SHIFT) 99 | #define Z80_H_FLAG (1 << Z80_H_FLAG_SHIFT) 100 | #define Z80_X_FLAG (1 << Z80_X_FLAG_SHIFT) 101 | #define Z80_PV_FLAG (1 << Z80_PV_FLAG_SHIFT) 102 | #define Z80_N_FLAG (1 << Z80_N_FLAG_SHIFT) 103 | #define Z80_C_FLAG (1 << Z80_C_FLAG_SHIFT) 104 | 105 | #define Z80_P_FLAG_SHIFT Z80_PV_FLAG_SHIFT 106 | #define Z80_V_FLAG_SHIFT Z80_PV_FLAG_SHIFT 107 | #define Z80_P_FLAG Z80_PV_FLAG 108 | #define Z80_V_FLAG Z80_PV_FLAG 109 | 110 | /* Z80's three interrupt modes. */ 111 | 112 | enum { 113 | 114 | Z80_INTERRUPT_MODE_0, 115 | Z80_INTERRUPT_MODE_1, 116 | Z80_INTERRUPT_MODE_2 117 | 118 | }; 119 | 120 | /* Z80 processor's state. You may add your own members if needed. However, it 121 | * is rather suggested to use the context pointer passed to the emulation 122 | * functions for that purpose. See z80user.h. 123 | */ 124 | 125 | typedef struct Z80_STATE { 126 | 127 | int status; 128 | 129 | union { 130 | 131 | unsigned char byte[14]; 132 | unsigned short word[7]; 133 | 134 | } registers; 135 | 136 | unsigned short alternates[4]; 137 | 138 | int i, r, pc, iff1, iff2, im; 139 | 140 | /* Register decoding tables. */ 141 | 142 | void *register_table[16], 143 | *dd_register_table[16], 144 | *fd_register_table[16]; 145 | 146 | } Z80_STATE; 147 | 148 | /* Initialize processor's state to power-on default. */ 149 | 150 | extern void Z80Reset (Z80_STATE *state); 151 | 152 | /* Trigger an interrupt according to the current interrupt mode and return the 153 | * number of cycles elapsed to accept it. If maskable interrupts are disabled, 154 | * this will return zero. In interrupt mode 0, data_on_bus must be a single 155 | * byte opcode. 156 | */ 157 | 158 | extern int Z80Interrupt (Z80_STATE *state, 159 | int data_on_bus, 160 | void *context); 161 | 162 | /* Trigger a non maskable interrupt, then return the number of cycles elapsed 163 | * to accept it. 164 | */ 165 | 166 | extern int Z80NonMaskableInterrupt (Z80_STATE *state, void *context); 167 | 168 | /* Execute instructions as long as the number of elapsed cycles is smaller than 169 | * number_cycles, and return the number of cycles emulated. The emulator can be 170 | * set to stop early on some conditions (see z80config.h). The user macros 171 | * (see z80user.h) also control the emulation. 172 | */ 173 | 174 | extern int Z80Emulate (Z80_STATE *state, 175 | int number_cycles, 176 | void *context); 177 | 178 | #ifdef __cplusplus 179 | } 180 | #endif 181 | 182 | #endif 183 | -------------------------------------------------------------------------------- /lib/z80_LKF/z80user.h: -------------------------------------------------------------------------------- 1 | /* z80user.h 2 | * Add your code here to interface the emulated system with z80emu. See towards 3 | * the end of the file for an example for running zextest. 4 | * 5 | * Copyright (c) 2016, 2017 Lin Ke-Fong 6 | * 7 | * This code is free, do whatever you want with it. 8 | */ 9 | 10 | #ifndef __Z80USER_INCLUDED__ 11 | #define __Z80USER_INCLUDED__ 12 | 13 | #include 14 | #include "z80emu.h" 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | /* Write the following macros for memory access and input/output on the Z80. 21 | * 22 | * Z80_FETCH_BYTE() and Z80_FETCH_WORD() are used by the emulator to read the 23 | * code (opcode, constants, displacement, etc). The upper 16-bit of the address 24 | * parameters is undefined and must be reset to zero before actually reading 25 | * memory (use & 0xffff). The value x read, must be an unsigned 8-bit or 16-bit 26 | * value in the endianness of the host processor. 27 | * 28 | * Z80_READ_BYTE(), Z80_WRITE_BYTE(), Z80_READ_WORD(), and Z80_WRITE_WORD() 29 | * are used for general memory access. They obey the same rules as the code 30 | * reading macros. The upper bits of the value x to write may be non-zero. 31 | * Z80_READ_WORD_INTERRUPT() and Z80_WRITE_WORD_INTERRUPT() are same as 32 | * respectively Z80_READ_WORD() and Z80_WRITE_WORD(), except they are only used 33 | * for interrupt generation. 34 | * 35 | * Z80_INPUT_BYTE() and Z80_OUTPUT_BYTE() are for input and output. The upper 36 | * bits of the port number to read or write are always zero. The input byte x 37 | * must be an unsigned 8-bit value. The value x to write is an unsigned 8-bit 38 | * with its upper bits zeroed. 39 | * 40 | * All macros have access to the following three variables: 41 | * 42 | * state Pointer to the current Z80_STATE. Because the 43 | * instruction is currently executing, its members may not 44 | * be fully up to date, depending on when the macro is 45 | * called in the process. It is rather suggested to access 46 | * the state only when the emulator is stopped. 47 | * 48 | * elapsed_cycles Number of cycles emulated. If needed, you may add wait 49 | * states to it for slow memory accesses. Because the 50 | * macros are called during the execution of the current 51 | * instruction, this number is only precise up to the 52 | * previous one. 53 | * 54 | * context This is the (void *) context passed to the emulation 55 | * functions. 56 | * 57 | * Except for Z80_READ_WORD_INTERRUPT and Z80_WRITE_WORD_INTERRUPT, all macros 58 | * also have access to: 59 | * 60 | * number_cycles Number of cycles to emulate. After executing each 61 | * instruction, the emulator checks if elapsed_cycles is 62 | * greater or equal to number_cycles, and will stops if 63 | * so. Hence you may decrease or increase the value of 64 | * number_cycles to stop the emulation earlier or later. 65 | * In particular, if you set it to zero, the emulator will 66 | * stop after completion of the current instruction. 67 | * 68 | * registers Current register decoding table, use it to determine if 69 | * the current instruction is prefixed. It points on: 70 | * 71 | * state->dd_register_table for 0xdd prefixes; 72 | * state->fd_register_table for 0xfd prefixes; 73 | * state->register_table otherwise. 74 | * 75 | * pc Current PC register (upper bits are undefined), points 76 | * on the opcode, the displacement or constant to read for 77 | * Z80_FETCH_BYTE() and Z80_FETCH_WORD(), or on the next 78 | * instruction otherwise. 79 | * 80 | * Except for Z80_FETCH_BYTE(), Z80_FETCH_WORD(), Z80_READ_WORD_INTERRUPT, and 81 | * Z80_WRITE_WORD_INTERRUPT, all other macros can know which instruction is 82 | * currently executing: 83 | * 84 | * opcode Opcode of the currently executing instruction. 85 | * 86 | * instruction Type of the currently executing instruction, see 87 | * instructions.h for a list. 88 | */ 89 | 90 | typedef struct CONTEXT { 91 | uint8_t(*readbyte)(uint16_t); 92 | uint16_t(*readword)(uint16_t); 93 | void(*writebyte)(uint16_t, uint8_t); 94 | void(*writeword)(uint16_t, uint16_t); 95 | uint8_t(*input)(uint8_t, uint8_t); 96 | void(*output)(uint8_t, uint8_t, uint8_t); 97 | } CONTEXT; 98 | 99 | #define Z80_READ_BYTE(address, x) \ 100 | { \ 101 | (x) = ((CONTEXT*)context)->readbyte(address); \ 102 | } 103 | 104 | #define Z80_WRITE_BYTE(address, x) \ 105 | { \ 106 | ((CONTEXT*)context)->writebyte(address, x); \ 107 | } 108 | 109 | #define Z80_READ_WORD(address, x) \ 110 | { \ 111 | (x) = ((CONTEXT*)context)->readword(address); \ 112 | } 113 | 114 | #define Z80_WRITE_WORD(address, x) \ 115 | { \ 116 | ((CONTEXT*)context)->writeword(address, x); \ 117 | } 118 | 119 | #define Z80_INPUT_BYTE(portLow, portHigh, x) \ 120 | { \ 121 | (x) = ((CONTEXT*)context)->input(portLow, portHigh); \ 122 | } 123 | 124 | #define Z80_OUTPUT_BYTE(portLow, portHigh, x) \ 125 | { \ 126 | ((CONTEXT*)context)->output(portLow, portHigh, x); \ 127 | } 128 | 129 | #define Z80_FETCH_BYTE(address, x) Z80_READ_BYTE((address), (x)) 130 | 131 | #define Z80_FETCH_WORD(address, x) Z80_READ_WORD((address), (x)) 132 | 133 | #define Z80_READ_WORD_INTERRUPT(address, x) Z80_READ_WORD((address), (x)) 134 | 135 | #define Z80_WRITE_WORD_INTERRUPT(address, x) Z80_WRITE_WORD((address), (x)) 136 | 137 | #ifdef __cplusplus 138 | } 139 | #endif 140 | 141 | #endif 142 | -------------------------------------------------------------------------------- /lib/z80_ZEL/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008, 2017 Stephen Checkoway 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /lib/z80_ZEL/README.md: -------------------------------------------------------------------------------- 1 | # z80 Emulation Library 2 | 3 | I wrote this z80 emulator library in 2008 after being dissatisfied with other 4 | emulators for one reason or another. It aims to be cycle-accurate by modeling 5 | the number of T-cycles per instruction. See the [Z80 CPU User 6 | Manual](https://www.zilog.com/docs/z80/um0080.pdf) for details about T-cycles. 7 | 8 | ## Operation 9 | 10 | Libzel is very low level. It operates one instruction at a time, calling 11 | callbacks to read memory, perform I/O, and handle interrupts. No assumptions 12 | are made about memory layout or peripheral organization. 13 | 14 | None of the standard Zilog peripherals are implemented and they must be 15 | handled by the callbacks. 16 | 17 | ## Paper 18 | 19 | Libzel was used to emulate the Sequoia AVC Advantage voting machine as 20 | described in Checkoway et al.'s [_Can DREs Provide Long-Lasting Security? The 21 | Case of Return-Oriented Programming and the AVC 22 | Advantage_](https://www.cs.uic.edu/~s/papers/evt2009/). 23 | -------------------------------------------------------------------------------- /lib/z80_ZEL/tables/cb_prefix.tab: -------------------------------------------------------------------------------- 1 | { RLC_R, REG_B, INV, INV, 8, TYPE_NONE, "rlc b" }, // 00 2 | { RLC_R, REG_C, INV, INV, 8, TYPE_NONE, "rlc c" }, // 01 3 | { RLC_R, REG_D, INV, INV, 8, TYPE_NONE, "rlc d" }, // 02 4 | { RLC_R, REG_E, INV, INV, 8, TYPE_NONE, "rlc e" }, // 03 5 | { RLC_R, REG_H, INV, INV, 8, TYPE_NONE, "rlc h" }, // 04 6 | { RLC_R, REG_L, INV, INV, 8, TYPE_NONE, "rlc l" }, // 05 7 | { RLC_MRR, REG_HL, INV, INV, 15, TYPE_NONE, "rlc (hl)" }, // 06 8 | { RLC_R, REG_A, INV, INV, 8, TYPE_NONE, "rlc a" }, // 07 9 | { RRC_R, REG_B, INV, INV, 8, TYPE_NONE, "rrc b" }, // 08 10 | { RRC_R, REG_C, INV, INV, 8, TYPE_NONE, "rrc c" }, // 09 11 | { RRC_R, REG_D, INV, INV, 8, TYPE_NONE, "rrc d" }, // 0a 12 | { RRC_R, REG_E, INV, INV, 8, TYPE_NONE, "rrc e" }, // 0b 13 | { RRC_R, REG_H, INV, INV, 8, TYPE_NONE, "rrc h" }, // 0c 14 | { RRC_R, REG_L, INV, INV, 8, TYPE_NONE, "rrc l" }, // 0d 15 | { RRC_MRR, REG_HL, INV, INV, 15, TYPE_NONE, "rrc (hl)" }, // 0e 16 | { RRC_R, REG_A, INV, INV, 8, TYPE_NONE, "rrc a" }, // 0f 17 | { RL_R, REG_B, INV, INV, 8, TYPE_NONE, "rl b" }, // 10 18 | { RL_R, REG_C, INV, INV, 8, TYPE_NONE, "rl c" }, // 11 19 | { RL_R, REG_D, INV, INV, 8, TYPE_NONE, "rl d" }, // 12 20 | { RL_R, REG_E, INV, INV, 8, TYPE_NONE, "rl e" }, // 13 21 | { RL_R, REG_H, INV, INV, 8, TYPE_NONE, "rl h" }, // 14 22 | { RL_R, REG_L, INV, INV, 8, TYPE_NONE, "rl l" }, // 15 23 | { RL_MRR, REG_HL, INV, INV, 15, TYPE_NONE, "rl (hl)" }, // 16 24 | { RL_R, REG_A, INV, INV, 8, TYPE_NONE, "rl a" }, // 17 25 | { RR_R, REG_B, INV, INV, 8, TYPE_NONE, "rr b" }, // 18 26 | { RR_R, REG_C, INV, INV, 8, TYPE_NONE, "rr c" }, // 19 27 | { RR_R, REG_D, INV, INV, 8, TYPE_NONE, "rr d" }, // 1a 28 | { RR_R, REG_E, INV, INV, 8, TYPE_NONE, "rr e" }, // 1b 29 | { RR_R, REG_H, INV, INV, 8, TYPE_NONE, "rr h" }, // 1c 30 | { RR_R, REG_L, INV, INV, 8, TYPE_NONE, "rr l" }, // 1d 31 | { RR_MRR, REG_HL, INV, INV, 15, TYPE_NONE, "rr (hl)" }, // 1e 32 | { RR_R, REG_A, INV, INV, 8, TYPE_NONE, "rr a" }, // 1f 33 | { SLA_R, REG_B, INV, INV, 8, TYPE_NONE, "sla b" }, // 20 34 | { SLA_R, REG_C, INV, INV, 8, TYPE_NONE, "sla c" }, // 21 35 | { SLA_R, REG_D, INV, INV, 8, TYPE_NONE, "sla d" }, // 22 36 | { SLA_R, REG_E, INV, INV, 8, TYPE_NONE, "sla e" }, // 23 37 | { SLA_R, REG_H, INV, INV, 8, TYPE_NONE, "sla h" }, // 24 38 | { SLA_R, REG_L, INV, INV, 8, TYPE_NONE, "sla l" }, // 25 39 | { SLA_MRR, REG_HL, INV, INV, 15, TYPE_NONE, "sla (hl)" }, // 26 40 | { SLA_R, REG_A, INV, INV, 8, TYPE_NONE, "sla a" }, // 27 41 | { SRA_R, REG_B, INV, INV, 8, TYPE_NONE, "sra b" }, // 28 42 | { SRA_R, REG_C, INV, INV, 8, TYPE_NONE, "sra c" }, // 29 43 | { SRA_R, REG_D, INV, INV, 8, TYPE_NONE, "sra d" }, // 2a 44 | { SRA_R, REG_E, INV, INV, 8, TYPE_NONE, "sra e" }, // 2b 45 | { SRA_R, REG_H, INV, INV, 8, TYPE_NONE, "sra h" }, // 2c 46 | { SRA_R, REG_L, INV, INV, 8, TYPE_NONE, "sra l" }, // 2d 47 | { SRA_MRR, REG_HL, INV, INV, 15, TYPE_NONE, "sra (hl)" }, // 2e 48 | { SRA_R, REG_A, INV, INV, 8, TYPE_NONE, "sra a" }, // 2f 49 | { SLL_R, REG_B, INV, INV, 8, TYPE_NONE, "sll b" }, // 30 50 | { SLL_R, REG_C, INV, INV, 8, TYPE_NONE, "sll c" }, // 31 51 | { SLL_R, REG_D, INV, INV, 8, TYPE_NONE, "sll d" }, // 32 52 | { SLL_R, REG_E, INV, INV, 8, TYPE_NONE, "sll e" }, // 33 53 | { SLL_R, REG_H, INV, INV, 8, TYPE_NONE, "sll h" }, // 34 54 | { SLL_R, REG_L, INV, INV, 8, TYPE_NONE, "sll l" }, // 35 55 | { SLL_MRR, REG_HL, INV, INV, 15, TYPE_NONE, "sll (hl)" }, // 36 56 | { SLL_R, REG_A, INV, INV, 8, TYPE_NONE, "sll a" }, // 37 57 | { SRL_R, REG_B, INV, INV, 8, TYPE_NONE, "srl b" }, // 38 58 | { SRL_R, REG_C, INV, INV, 8, TYPE_NONE, "srl c" }, // 39 59 | { SRL_R, REG_D, INV, INV, 8, TYPE_NONE, "srl d" }, // 3a 60 | { SRL_R, REG_E, INV, INV, 8, TYPE_NONE, "srl e" }, // 3b 61 | { SRL_R, REG_H, INV, INV, 8, TYPE_NONE, "srl h" }, // 3c 62 | { SRL_R, REG_L, INV, INV, 8, TYPE_NONE, "srl l" }, // 3d 63 | { SRL_MRR, REG_HL, INV, INV, 15, TYPE_NONE, "srl (hl)" }, // 3e 64 | { SRL_R, REG_A, INV, INV, 8, TYPE_NONE, "srl a" }, // 3f 65 | { BIT_R, 0, REG_B, INV, 8, TYPE_NONE, "bit 0,b" }, // 40 66 | { BIT_R, 0, REG_C, INV, 8, TYPE_NONE, "bit 0,c" }, // 41 67 | { BIT_R, 0, REG_D, INV, 8, TYPE_NONE, "bit 0,d" }, // 42 68 | { BIT_R, 0, REG_E, INV, 8, TYPE_NONE, "bit 0,e" }, // 43 69 | { BIT_R, 0, REG_H, INV, 8, TYPE_NONE, "bit 0,h" }, // 44 70 | { BIT_R, 0, REG_L, INV, 8, TYPE_NONE, "bit 0,l" }, // 45 71 | { BIT_MRR, 0, REG_HL, INV, 12, TYPE_NONE, "bit 0,(hl)" }, // 46 72 | { BIT_R, 0, REG_A, INV, 8, TYPE_NONE, "bit 0,a" }, // 47 73 | { BIT_R, 1, REG_B, INV, 8, TYPE_NONE, "bit 1,b" }, // 48 74 | { BIT_R, 1, REG_C, INV, 8, TYPE_NONE, "bit 1,c" }, // 49 75 | { BIT_R, 1, REG_D, INV, 8, TYPE_NONE, "bit 1,d" }, // 4a 76 | { BIT_R, 1, REG_E, INV, 8, TYPE_NONE, "bit 1,e" }, // 4b 77 | { BIT_R, 1, REG_H, INV, 8, TYPE_NONE, "bit 1,h" }, // 4c 78 | { BIT_R, 1, REG_L, INV, 8, TYPE_NONE, "bit 1,l" }, // 4d 79 | { BIT_MRR, 1, REG_HL, INV, 12, TYPE_NONE, "bit 1,(hl)" }, // 4e 80 | { BIT_R, 1, REG_A, INV, 8, TYPE_NONE, "bit 1,a" }, // 4f 81 | { BIT_R, 2, REG_B, INV, 8, TYPE_NONE, "bit 2,b" }, // 50 82 | { BIT_R, 2, REG_C, INV, 8, TYPE_NONE, "bit 2,c" }, // 51 83 | { BIT_R, 2, REG_D, INV, 8, TYPE_NONE, "bit 2,d" }, // 52 84 | { BIT_R, 2, REG_E, INV, 8, TYPE_NONE, "bit 2,e" }, // 53 85 | { BIT_R, 2, REG_H, INV, 8, TYPE_NONE, "bit 2,h" }, // 54 86 | { BIT_R, 2, REG_L, INV, 8, TYPE_NONE, "bit 2,l" }, // 55 87 | { BIT_MRR, 2, REG_HL, INV, 12, TYPE_NONE, "bit 2,(hl)" }, // 56 88 | { BIT_R, 2, REG_A, INV, 8, TYPE_NONE, "bit 2,a" }, // 57 89 | { BIT_R, 3, REG_B, INV, 8, TYPE_NONE, "bit 3,b" }, // 58 90 | { BIT_R, 3, REG_C, INV, 8, TYPE_NONE, "bit 3,c" }, // 59 91 | { BIT_R, 3, REG_D, INV, 8, TYPE_NONE, "bit 3,d" }, // 5a 92 | { BIT_R, 3, REG_E, INV, 8, TYPE_NONE, "bit 3,e" }, // 5b 93 | { BIT_R, 3, REG_H, INV, 8, TYPE_NONE, "bit 3,h" }, // 5c 94 | { BIT_R, 3, REG_L, INV, 8, TYPE_NONE, "bit 3,l" }, // 5d 95 | { BIT_MRR, 3, REG_HL, INV, 12, TYPE_NONE, "bit 3,(hl)" }, // 5e 96 | { BIT_R, 3, REG_A, INV, 8, TYPE_NONE, "bit 3,a" }, // 5f 97 | { BIT_R, 4, REG_B, INV, 8, TYPE_NONE, "bit 4,b" }, // 60 98 | { BIT_R, 4, REG_C, INV, 8, TYPE_NONE, "bit 4,c" }, // 61 99 | { BIT_R, 4, REG_D, INV, 8, TYPE_NONE, "bit 4,d" }, // 62 100 | { BIT_R, 4, REG_E, INV, 8, TYPE_NONE, "bit 4,e" }, // 63 101 | { BIT_R, 4, REG_H, INV, 8, TYPE_NONE, "bit 4,h" }, // 64 102 | { BIT_R, 4, REG_L, INV, 8, TYPE_NONE, "bit 4,l" }, // 65 103 | { BIT_MRR, 4, REG_HL, INV, 12, TYPE_NONE, "bit 4,(hl)" }, // 66 104 | { BIT_R, 4, REG_A, INV, 8, TYPE_NONE, "bit 4,a" }, // 67 105 | { BIT_R, 5, REG_B, INV, 8, TYPE_NONE, "bit 5,b" }, // 68 106 | { BIT_R, 5, REG_C, INV, 8, TYPE_NONE, "bit 5,c" }, // 69 107 | { BIT_R, 5, REG_D, INV, 8, TYPE_NONE, "bit 5,d" }, // 6a 108 | { BIT_R, 5, REG_E, INV, 8, TYPE_NONE, "bit 5,e" }, // 6b 109 | { BIT_R, 5, REG_H, INV, 8, TYPE_NONE, "bit 5,h" }, // 6c 110 | { BIT_R, 5, REG_L, INV, 8, TYPE_NONE, "bit 5,l" }, // 6d 111 | { BIT_MRR, 5, REG_HL, INV, 12, TYPE_NONE, "bit 5,(hl)" }, // 6e 112 | { BIT_R, 5, REG_A, INV, 8, TYPE_NONE, "bit 5,a" }, // 6f 113 | { BIT_R, 6, REG_B, INV, 8, TYPE_NONE, "bit 6,b" }, // 70 114 | { BIT_R, 6, REG_C, INV, 8, TYPE_NONE, "bit 6,c" }, // 71 115 | { BIT_R, 6, REG_D, INV, 8, TYPE_NONE, "bit 6,d" }, // 72 116 | { BIT_R, 6, REG_E, INV, 8, TYPE_NONE, "bit 6,e" }, // 73 117 | { BIT_R, 6, REG_H, INV, 8, TYPE_NONE, "bit 6,h" }, // 74 118 | { BIT_R, 6, REG_L, INV, 8, TYPE_NONE, "bit 6,l" }, // 75 119 | { BIT_MRR, 6, REG_HL, INV, 12, TYPE_NONE, "bit 6,(hl)" }, // 76 120 | { BIT_R, 6, REG_A, INV, 8, TYPE_NONE, "bit 6,a" }, // 77 121 | { BIT_R, 7, REG_B, INV, 8, TYPE_NONE, "bit 7,b" }, // 78 122 | { BIT_R, 7, REG_C, INV, 8, TYPE_NONE, "bit 7,c" }, // 79 123 | { BIT_R, 7, REG_D, INV, 8, TYPE_NONE, "bit 7,d" }, // 7a 124 | { BIT_R, 7, REG_E, INV, 8, TYPE_NONE, "bit 7,e" }, // 7b 125 | { BIT_R, 7, REG_H, INV, 8, TYPE_NONE, "bit 7,h" }, // 7c 126 | { BIT_R, 7, REG_L, INV, 8, TYPE_NONE, "bit 7,l" }, // 7d 127 | { BIT_MRR, 7, REG_HL, INV, 12, TYPE_NONE, "bit 7,(hl)" }, // 7e 128 | { BIT_R, 7, REG_A, INV, 8, TYPE_NONE, "bit 7,a" }, // 7f 129 | { RES_R, 0, REG_B, INV, 8, TYPE_NONE, "res 0,b" }, // 80 130 | { RES_R, 0, REG_C, INV, 8, TYPE_NONE, "res 0,c" }, // 81 131 | { RES_R, 0, REG_D, INV, 8, TYPE_NONE, "res 0,d" }, // 82 132 | { RES_R, 0, REG_E, INV, 8, TYPE_NONE, "res 0,e" }, // 83 133 | { RES_R, 0, REG_H, INV, 8, TYPE_NONE, "res 0,h" }, // 84 134 | { RES_R, 0, REG_L, INV, 8, TYPE_NONE, "res 0,l" }, // 85 135 | { RES_MRR, 0, REG_HL, INV, 15, TYPE_NONE, "res 0,(hl)" }, // 86 136 | { RES_R, 0, REG_A, INV, 8, TYPE_NONE, "res 0,a" }, // 87 137 | { RES_R, 1, REG_B, INV, 8, TYPE_NONE, "res 1,b" }, // 88 138 | { RES_R, 1, REG_C, INV, 8, TYPE_NONE, "res 1,c" }, // 89 139 | { RES_R, 1, REG_D, INV, 8, TYPE_NONE, "res 1,d" }, // 8a 140 | { RES_R, 1, REG_E, INV, 8, TYPE_NONE, "res 1,e" }, // 8b 141 | { RES_R, 1, REG_H, INV, 8, TYPE_NONE, "res 1,h" }, // 8c 142 | { RES_R, 1, REG_L, INV, 8, TYPE_NONE, "res 1,l" }, // 8d 143 | { RES_MRR, 1, REG_HL, INV, 15, TYPE_NONE, "res 1,(hl)" }, // 8e 144 | { RES_R, 1, REG_A, INV, 8, TYPE_NONE, "res 1,a" }, // 8f 145 | { RES_R, 2, REG_B, INV, 8, TYPE_NONE, "res 2,b" }, // 90 146 | { RES_R, 2, REG_C, INV, 8, TYPE_NONE, "res 2,c" }, // 91 147 | { RES_R, 2, REG_D, INV, 8, TYPE_NONE, "res 2,d" }, // 92 148 | { RES_R, 2, REG_E, INV, 8, TYPE_NONE, "res 2,e" }, // 93 149 | { RES_R, 2, REG_H, INV, 8, TYPE_NONE, "res 2,h" }, // 94 150 | { RES_R, 2, REG_L, INV, 8, TYPE_NONE, "res 2,l" }, // 95 151 | { RES_MRR, 2, REG_HL, INV, 15, TYPE_NONE, "res 2,(hl)" }, // 96 152 | { RES_R, 2, REG_A, INV, 8, TYPE_NONE, "res 2,a" }, // 97 153 | { RES_R, 3, REG_B, INV, 8, TYPE_NONE, "res 3,b" }, // 98 154 | { RES_R, 3, REG_C, INV, 8, TYPE_NONE, "res 3,c" }, // 99 155 | { RES_R, 3, REG_D, INV, 8, TYPE_NONE, "res 3,d" }, // 9a 156 | { RES_R, 3, REG_E, INV, 8, TYPE_NONE, "res 3,e" }, // 9b 157 | { RES_R, 3, REG_H, INV, 8, TYPE_NONE, "res 3,h" }, // 9c 158 | { RES_R, 3, REG_L, INV, 8, TYPE_NONE, "res 3,l" }, // 9d 159 | { RES_MRR, 3, REG_HL, INV, 15, TYPE_NONE, "res 3,(hl)" }, // 9e 160 | { RES_R, 3, REG_A, INV, 8, TYPE_NONE, "res 3,a" }, // 9f 161 | { RES_R, 4, REG_B, INV, 8, TYPE_NONE, "res 4,b" }, // a0 162 | { RES_R, 4, REG_C, INV, 8, TYPE_NONE, "res 4,c" }, // a1 163 | { RES_R, 4, REG_D, INV, 8, TYPE_NONE, "res 4,d" }, // a2 164 | { RES_R, 4, REG_E, INV, 8, TYPE_NONE, "res 4,e" }, // a3 165 | { RES_R, 4, REG_H, INV, 8, TYPE_NONE, "res 4,h" }, // a4 166 | { RES_R, 4, REG_L, INV, 8, TYPE_NONE, "res 4,l" }, // a5 167 | { RES_MRR, 4, REG_HL, INV, 15, TYPE_NONE, "res 4,(hl)" }, // a6 168 | { RES_R, 4, REG_A, INV, 8, TYPE_NONE, "res 4,a" }, // a7 169 | { RES_R, 5, REG_B, INV, 8, TYPE_NONE, "res 5,b" }, // a8 170 | { RES_R, 5, REG_C, INV, 8, TYPE_NONE, "res 5,c" }, // a9 171 | { RES_R, 5, REG_D, INV, 8, TYPE_NONE, "res 5,d" }, // aa 172 | { RES_R, 5, REG_E, INV, 8, TYPE_NONE, "res 5,e" }, // ab 173 | { RES_R, 5, REG_H, INV, 8, TYPE_NONE, "res 5,h" }, // ac 174 | { RES_R, 5, REG_L, INV, 8, TYPE_NONE, "res 5,l" }, // ad 175 | { RES_MRR, 5, REG_HL, INV, 15, TYPE_NONE, "res 5,(hl)" }, // ae 176 | { RES_R, 5, REG_A, INV, 8, TYPE_NONE, "res 5,a" }, // af 177 | { RES_R, 6, REG_B, INV, 8, TYPE_NONE, "res 6,b" }, // b0 178 | { RES_R, 6, REG_C, INV, 8, TYPE_NONE, "res 6,c" }, // b1 179 | { RES_R, 6, REG_D, INV, 8, TYPE_NONE, "res 6,d" }, // b2 180 | { RES_R, 6, REG_E, INV, 8, TYPE_NONE, "res 6,e" }, // b3 181 | { RES_R, 6, REG_H, INV, 8, TYPE_NONE, "res 6,h" }, // b4 182 | { RES_R, 6, REG_L, INV, 8, TYPE_NONE, "res 6,l" }, // b5 183 | { RES_MRR, 6, REG_HL, INV, 15, TYPE_NONE, "res 6,(hl)" }, // b6 184 | { RES_R, 6, REG_A, INV, 8, TYPE_NONE, "res 6,a" }, // b7 185 | { RES_R, 7, REG_B, INV, 8, TYPE_NONE, "res 7,b" }, // b8 186 | { RES_R, 7, REG_C, INV, 8, TYPE_NONE, "res 7,c" }, // b9 187 | { RES_R, 7, REG_D, INV, 8, TYPE_NONE, "res 7,d" }, // ba 188 | { RES_R, 7, REG_E, INV, 8, TYPE_NONE, "res 7,e" }, // bb 189 | { RES_R, 7, REG_H, INV, 8, TYPE_NONE, "res 7,h" }, // bc 190 | { RES_R, 7, REG_L, INV, 8, TYPE_NONE, "res 7,l" }, // bd 191 | { RES_MRR, 7, REG_HL, INV, 15, TYPE_NONE, "res 7,(hl)" }, // be 192 | { RES_R, 7, REG_A, INV, 8, TYPE_NONE, "res 7,a" }, // bf 193 | { SET_R, 0, REG_B, INV, 8, TYPE_NONE, "set 0,b" }, // c0 194 | { SET_R, 0, REG_C, INV, 8, TYPE_NONE, "set 0,c" }, // c1 195 | { SET_R, 0, REG_D, INV, 8, TYPE_NONE, "set 0,d" }, // c2 196 | { SET_R, 0, REG_E, INV, 8, TYPE_NONE, "set 0,e" }, // c3 197 | { SET_R, 0, REG_H, INV, 8, TYPE_NONE, "set 0,h" }, // c4 198 | { SET_R, 0, REG_L, INV, 8, TYPE_NONE, "set 0,l" }, // c5 199 | { SET_MRR, 0, REG_HL, INV, 15, TYPE_NONE, "set 0,(hl)" }, // c6 200 | { SET_R, 0, REG_A, INV, 8, TYPE_NONE, "set 0,a" }, // c7 201 | { SET_R, 1, REG_B, INV, 8, TYPE_NONE, "set 1,b" }, // c8 202 | { SET_R, 1, REG_C, INV, 8, TYPE_NONE, "set 1,c" }, // c9 203 | { SET_R, 1, REG_D, INV, 8, TYPE_NONE, "set 1,d" }, // ca 204 | { SET_R, 1, REG_E, INV, 8, TYPE_NONE, "set 1,e" }, // cb 205 | { SET_R, 1, REG_H, INV, 8, TYPE_NONE, "set 1,h" }, // cc 206 | { SET_R, 1, REG_L, INV, 8, TYPE_NONE, "set 1,l" }, // cd 207 | { SET_MRR, 1, REG_HL, INV, 15, TYPE_NONE, "set 1,(hl)" }, // ce 208 | { SET_R, 1, REG_A, INV, 8, TYPE_NONE, "set 1,a" }, // cf 209 | { SET_R, 2, REG_B, INV, 8, TYPE_NONE, "set 2,b" }, // d0 210 | { SET_R, 2, REG_C, INV, 8, TYPE_NONE, "set 2,c" }, // d1 211 | { SET_R, 2, REG_D, INV, 8, TYPE_NONE, "set 2,d" }, // d2 212 | { SET_R, 2, REG_E, INV, 8, TYPE_NONE, "set 2,e" }, // d3 213 | { SET_R, 2, REG_H, INV, 8, TYPE_NONE, "set 2,h" }, // d4 214 | { SET_R, 2, REG_L, INV, 8, TYPE_NONE, "set 2,l" }, // d5 215 | { SET_MRR, 2, REG_HL, INV, 15, TYPE_NONE, "set 2,(hl)" }, // d6 216 | { SET_R, 2, REG_A, INV, 8, TYPE_NONE, "set 2,a" }, // d7 217 | { SET_R, 3, REG_B, INV, 8, TYPE_NONE, "set 3,b" }, // d8 218 | { SET_R, 3, REG_C, INV, 8, TYPE_NONE, "set 3,c" }, // d9 219 | { SET_R, 3, REG_D, INV, 8, TYPE_NONE, "set 3,d" }, // da 220 | { SET_R, 3, REG_E, INV, 8, TYPE_NONE, "set 3,e" }, // db 221 | { SET_R, 3, REG_H, INV, 8, TYPE_NONE, "set 3,h" }, // dc 222 | { SET_R, 3, REG_L, INV, 8, TYPE_NONE, "set 3,l" }, // dd 223 | { SET_MRR, 3, REG_HL, INV, 15, TYPE_NONE, "set 3,(hl)" }, // de 224 | { SET_R, 3, REG_A, INV, 8, TYPE_NONE, "set 3,a" }, // df 225 | { SET_R, 4, REG_B, INV, 8, TYPE_NONE, "set 4,b" }, // e0 226 | { SET_R, 4, REG_C, INV, 8, TYPE_NONE, "set 4,c" }, // e1 227 | { SET_R, 4, REG_D, INV, 8, TYPE_NONE, "set 4,d" }, // e2 228 | { SET_R, 4, REG_E, INV, 8, TYPE_NONE, "set 4,e" }, // e3 229 | { SET_R, 4, REG_H, INV, 8, TYPE_NONE, "set 4,h" }, // e4 230 | { SET_R, 4, REG_L, INV, 8, TYPE_NONE, "set 4,l" }, // e5 231 | { SET_MRR, 4, REG_HL, INV, 15, TYPE_NONE, "set 4,(hl)" }, // e6 232 | { SET_R, 4, REG_A, INV, 8, TYPE_NONE, "set 4,a" }, // e7 233 | { SET_R, 5, REG_B, INV, 8, TYPE_NONE, "set 5,b" }, // e8 234 | { SET_R, 5, REG_C, INV, 8, TYPE_NONE, "set 5,c" }, // e9 235 | { SET_R, 5, REG_D, INV, 8, TYPE_NONE, "set 5,d" }, // ea 236 | { SET_R, 5, REG_E, INV, 8, TYPE_NONE, "set 5,e" }, // eb 237 | { SET_R, 5, REG_H, INV, 8, TYPE_NONE, "set 5,h" }, // ec 238 | { SET_R, 5, REG_L, INV, 8, TYPE_NONE, "set 5,l" }, // ed 239 | { SET_MRR, 5, REG_HL, INV, 15, TYPE_NONE, "set 5,(hl)" }, // ee 240 | { SET_R, 5, REG_A, INV, 8, TYPE_NONE, "set 5,a" }, // ef 241 | { SET_R, 6, REG_B, INV, 8, TYPE_NONE, "set 6,b" }, // f0 242 | { SET_R, 6, REG_C, INV, 8, TYPE_NONE, "set 6,c" }, // f1 243 | { SET_R, 6, REG_D, INV, 8, TYPE_NONE, "set 6,d" }, // f2 244 | { SET_R, 6, REG_E, INV, 8, TYPE_NONE, "set 6,e" }, // f3 245 | { SET_R, 6, REG_H, INV, 8, TYPE_NONE, "set 6,h" }, // f4 246 | { SET_R, 6, REG_L, INV, 8, TYPE_NONE, "set 6,l" }, // f5 247 | { SET_MRR, 6, REG_HL, INV, 15, TYPE_NONE, "set 6,(hl)" }, // f6 248 | { SET_R, 6, REG_A, INV, 8, TYPE_NONE, "set 6,a" }, // f7 249 | { SET_R, 7, REG_B, INV, 8, TYPE_NONE, "set 7,b" }, // f8 250 | { SET_R, 7, REG_C, INV, 8, TYPE_NONE, "set 7,c" }, // f9 251 | { SET_R, 7, REG_D, INV, 8, TYPE_NONE, "set 7,d" }, // fa 252 | { SET_R, 7, REG_E, INV, 8, TYPE_NONE, "set 7,e" }, // fb 253 | { SET_R, 7, REG_H, INV, 8, TYPE_NONE, "set 7,h" }, // fc 254 | { SET_R, 7, REG_L, INV, 8, TYPE_NONE, "set 7,l" }, // fd 255 | { SET_MRR, 7, REG_HL, INV, 15, TYPE_NONE, "set 7,(hl)" }, // fe 256 | { SET_R, 7, REG_A, INV, 8, TYPE_NONE, "set 7,a" }, // ff 257 | -------------------------------------------------------------------------------- /lib/z80_ZEL/tables/ed_prefix.tab: -------------------------------------------------------------------------------- 1 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 00 2 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 01 3 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 02 4 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 03 5 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 04 6 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 05 7 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 06 8 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 07 9 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 08 10 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 09 11 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 0a 12 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 0b 13 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 0c 14 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 0d 15 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 0e 16 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 0f 17 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 10 18 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 11 19 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 12 20 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 13 21 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 14 22 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 15 23 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 16 24 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 17 25 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 18 26 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 19 27 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 1a 28 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 1b 29 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 1c 30 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 1d 31 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 1e 32 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 1f 33 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 20 34 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 21 35 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 22 36 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 23 37 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 24 38 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 25 39 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 26 40 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 27 41 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 28 42 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 29 43 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 2a 44 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 2b 45 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 2c 46 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 2d 47 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 2e 48 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 2f 49 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 30 50 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 31 51 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 32 52 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 33 53 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 34 54 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 35 55 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 36 56 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 37 57 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 38 58 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 39 59 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 3a 60 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 3b 61 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 3c 62 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 3d 63 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 3e 64 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 3f 65 | { IN_R_R, REG_B, REG_C, INV, 12, TYPE_NONE, "in b,(c)" }, // 40 66 | { OUT_R_R, REG_C, REG_B, INV, 12, TYPE_NONE, "out (c),b" }, // 41 67 | { SBC_RR_RR, REG_HL, REG_BC, INV, 15, TYPE_NONE, "sbc hl,bc" }, // 42 68 | { LD_MNN_RR, INV, REG_BC, INV, 20, TYPE_IMM_NN, "ld (%04hXh),bc" }, // 43 69 | { NEG, INV, INV, INV, 8, TYPE_NONE, "neg" }, // 44 70 | { RETN, INV, INV, INV, 14, TYPE_NONE, "retn" }, // 45 71 | { IM, 0, INV, INV, 8, TYPE_NONE, "im 0" }, // 46 72 | { LD_R_R, REG_I, REG_A, INV, 9, TYPE_NONE, "ld i,a" }, // 47 73 | { IN_R_R, REG_C, REG_C, INV, 12, TYPE_NONE, "in c,(c)" }, // 48 74 | { OUT_R_R, REG_C, REG_C, INV, 12, TYPE_NONE, "out (c),c" }, // 49 75 | { ADC_RR_RR, REG_HL, REG_BC, INV, 15, TYPE_NONE, "adc hl,bc" }, // 4a 76 | { LD_RR_MNN, REG_BC, INV, INV, 20, TYPE_IMM_NN, "ld bc,(%04hXh)" }, // 4b 77 | { NEG, INV, INV, INV, 8, TYPE_NONE, "neg" }, // 4c 78 | { RETI, INV, INV, INV, 14, TYPE_NONE, "reti" }, // 4d 79 | { IM, 0, INV, INV, 8, TYPE_NONE, "im 0" }, // 4e 80 | { LD_R_R, REG_R, REG_A, INV, 9, TYPE_NONE, "ld r,a" }, // 4f 81 | { IN_R_R, REG_D, REG_C, INV, 12, TYPE_NONE, "in d,(c)" }, // 50 82 | { OUT_R_R, REG_C, REG_D, INV, 12, TYPE_NONE, "out (c),d" }, // 51 83 | { SBC_RR_RR, REG_HL, REG_DE, INV, 15, TYPE_NONE, "sbc hl,de" }, // 52 84 | { LD_MNN_RR, INV, REG_DE, INV, 20, TYPE_IMM_NN, "ld (%04hXh),de" }, // 53 85 | { NEG, INV, INV, INV, 8, TYPE_NONE, "neg" }, // 54 86 | { RETN, INV, INV, INV, 14, TYPE_NONE, "retn" }, // 55 87 | { IM, 1, INV, INV, 8, TYPE_NONE, "im 1" }, // 56 88 | { LD_R_R, REG_A, REG_I, INV, 9, TYPE_NONE, "ld a,i" }, // 57 89 | { IN_R_R, REG_E, REG_C, INV, 12, TYPE_NONE, "in e,(c)" }, // 58 90 | { OUT_R_R, REG_C, REG_E, INV, 12, TYPE_NONE, "out (c),e" }, // 59 91 | { ADC_RR_RR, REG_HL, REG_DE, INV, 15, TYPE_NONE, "adc hl,de" }, // 5a 92 | { LD_RR_MNN, REG_DE, INV, INV, 20, TYPE_IMM_NN, "ld de,(%04hXh)" }, // 5b 93 | { NEG, INV, INV, INV, 8, TYPE_NONE, "neg" }, // 5c 94 | { RETN, INV, INV, INV, 14, TYPE_NONE, "retn" }, // 5d 95 | { IM, 2, INV, INV, 8, TYPE_NONE, "im 2" }, // 5e 96 | { LD_R_R, REG_A, REG_R, INV, 9, TYPE_NONE, "ld a,r" }, // 5f 97 | { IN_R_R, REG_H, REG_C, INV, 12, TYPE_NONE, "in h,(c)" }, // 60 98 | { OUT_R_R, REG_C, REG_H, INV, 12, TYPE_NONE, "out (c),h" }, // 61 99 | { SBC_RR_RR, REG_HL, REG_HL, INV, 15, TYPE_NONE, "sbc hl,hl" }, // 62 100 | { LD_MNN_RR, INV, REG_HL, INV, 16, TYPE_IMM_NN, "ld (%04hXh),hl" }, // 63 101 | { NEG, INV, INV, INV, 8, TYPE_NONE, "neg" }, // 64 102 | { RETN, INV, INV, INV, 14, TYPE_NONE, "retn" }, // 65 103 | { IM, 0, INV, INV, 8, TYPE_NONE, "im 0" }, // 66 104 | { RRD, INV, INV, INV, 18, TYPE_NONE, "rrd" }, // 67 105 | { IN_R_R, REG_L, REG_C, INV, 12, TYPE_NONE, "in l,(c)" }, // 68 106 | { OUT_R_R, REG_C, REG_L, INV, 12, TYPE_NONE, "out (c),l" }, // 69 107 | { ADC_RR_RR, REG_HL, REG_HL, INV, 15, TYPE_NONE, "adc hl,hl" }, // 6a 108 | { LD_RR_MNN, REG_HL, INV, INV, 16, TYPE_IMM_NN, "ld hl,(%04hXh)" }, // 6b 109 | { NEG, INV, INV, INV, 8, TYPE_NONE, "neg" }, // 6c 110 | { RETN, INV, INV, INV, 14, TYPE_NONE, "retn" }, // 6d 111 | { IM, 0, INV, INV, 8, TYPE_NONE, "im 0" }, // 6e 112 | { RLD, INV, INV, INV, 18, TYPE_NONE, "rld" }, // 6f 113 | { IN_R_R, REG_F, REG_C, INV, 12, TYPE_NONE, "in f,(c)" }, // 70 114 | { OUT_R, REG_C, 0, INV, 12, TYPE_NONE, "out (c),0" }, // 71 115 | { SBC_RR_RR, REG_HL, REG_SP, INV, 15, TYPE_NONE, "sbc hl,sp" }, // 72 116 | { LD_MNN_RR, INV, REG_SP, INV, 20, TYPE_IMM_NN, "ld (%04hXh),sp" }, // 73 117 | { NEG, INV, INV, INV, 8, TYPE_NONE, "neg" }, // 74 118 | { RETN, INV, INV, INV, 14, TYPE_NONE, "retn" }, // 75 119 | { IM, 1, INV, INV, 8, TYPE_NONE, "im 1" }, // 76 120 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 77 121 | { IN_R_R, REG_A, REG_C, INV, 12, TYPE_NONE, "in a,(c)" }, // 78 122 | { OUT_R_R, REG_C, REG_A, INV, 12, TYPE_NONE, "out (c),a" }, // 79 123 | { ADC_RR_RR, REG_HL, REG_SP, INV, 15, TYPE_NONE, "adc hl,sp" }, // 7a 124 | { LD_RR_MNN, REG_SP, INV, INV, 20, TYPE_IMM_NN, "ld sp,(%04hXh)" }, // 7b 125 | { NEG, INV, INV, INV, 8, TYPE_NONE, "neg" }, // 7c 126 | { RETN, INV, INV, INV, 14, TYPE_NONE, "retn" }, // 7d 127 | { IM, 2, INV, INV, 8, TYPE_NONE, "im 2" }, // 7e 128 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 7f 129 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 80 130 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 81 131 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 82 132 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 83 133 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 84 134 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 85 135 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 86 136 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 87 137 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 88 138 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 89 139 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 8a 140 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 8b 141 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 8c 142 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 8d 143 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 8e 144 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 8f 145 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 90 146 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 91 147 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 92 148 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 93 149 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 94 150 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 95 151 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 96 152 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 97 153 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 98 154 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 99 155 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 9a 156 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 9b 157 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 9c 158 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 9d 159 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 9e 160 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // 9f 161 | { LDI, INV, INV, INV, 16, TYPE_NONE, "ldi" }, // a0 162 | { CPI, INV, INV, INV, 16, TYPE_NONE, "cpi" }, // a1 163 | { INI, INV, INV, INV, 16, TYPE_NONE, "ini" }, // a2 164 | { OUTI, INV, INV, INV, 16, TYPE_NONE, "outi" }, // a3 165 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // a4 166 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // a5 167 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // a6 168 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // a7 169 | { LDD, INV, INV, INV, 16, TYPE_NONE, "ldd" }, // a8 170 | { CPD, INV, INV, INV, 16, TYPE_NONE, "cpd" }, // a9 171 | { IND, INV, INV, INV, 16, TYPE_NONE, "ind" }, // aa 172 | { OUTD, INV, INV, INV, 16, TYPE_NONE, "outd" }, // ab 173 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // ac 174 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // ad 175 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // ae 176 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // af 177 | { LDIR, INV, INV, 16, 21, TYPE_NONE, "ldir" }, // b0 178 | { CPIR, INV, INV, 16, 21, TYPE_NONE, "cpir" }, // b1 179 | { INIR, INV, INV, 16, 21, TYPE_NONE, "inir" }, // b2 180 | { OTIR, INV, INV, 16, 21, TYPE_NONE, "otir" }, // b3 181 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // b4 182 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // b5 183 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // b6 184 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // b7 185 | { LDDR, INV, INV, 16, 21, TYPE_NONE, "lddr" }, // b8 186 | { CPDR, INV, INV, 16, 21, TYPE_NONE, "cpdr" }, // b9 187 | { INDR, INV, INV, 16, 21, TYPE_NONE, "indr" }, // ba 188 | { OTDR, INV, INV, 16, 21, TYPE_NONE, "otdr" }, // bb 189 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // bc 190 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // bd 191 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // be 192 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // bf 193 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // c0 194 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // c1 195 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // c2 196 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // c3 197 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // c4 198 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // c5 199 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // c6 200 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // c7 201 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // c8 202 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // c9 203 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // ca 204 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // cb 205 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // cc 206 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // cd 207 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // ce 208 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // cf 209 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // d0 210 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // d1 211 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // d2 212 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // d3 213 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // d4 214 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // d5 215 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // d6 216 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // d7 217 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // d8 218 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // d9 219 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // da 220 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // db 221 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // dc 222 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // dd 223 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // de 224 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // df 225 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // e0 226 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // e1 227 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // e2 228 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // e3 229 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // e4 230 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // e5 231 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // e6 232 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // e7 233 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // e8 234 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // e9 235 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // ea 236 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // eb 237 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // ec 238 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // ed 239 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // ee 240 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // ef 241 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // f0 242 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // f1 243 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // f2 244 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // f3 245 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // f4 246 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // f5 247 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // f6 248 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // f7 249 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // f8 250 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // f9 251 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // fa 252 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // fb 253 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // fc 254 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // fd 255 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // fe 256 | { NOP, INV, INV, INV, 8, TYPE_NONE, "nop" }, // ff 257 | -------------------------------------------------------------------------------- /lib/z80_ZEL/z80_instructions.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2008, 2017 Stephen Checkoway 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to deal 5 | * in the Software without restriction, including without limitation the rights 6 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | * copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | */ 21 | #include /* for snprintf */ 22 | #include 23 | #include /* Need registers */ 24 | #include 25 | 26 | #include "z80_types.h" 27 | 28 | const InstructionTemplate Unprefixed[] = 29 | { 30 | #include "tables/no_prefix.tab" 31 | }; 32 | const InstructionTemplate CB_Prefixed[] = 33 | { 34 | #include "tables/cb_prefix.tab" 35 | }; 36 | const InstructionTemplate DD_Prefixed[] = 37 | { 38 | #include "tables/dd_prefix.tab" 39 | }; 40 | const InstructionTemplate DDCB_Prefixed[] = 41 | { 42 | #include "tables/ddcb_prefix.tab" 43 | }; 44 | const InstructionTemplate ED_Prefixed[] = 45 | { 46 | #include "tables/ed_prefix.tab" 47 | }; 48 | const InstructionTemplate FD_Prefixed[] = 49 | { 50 | #include "tables/fd_prefix.tab" 51 | }; 52 | const InstructionTemplate FDCB_Prefixed[] = 53 | { 54 | #include "tables/fdcb_prefix.tab" 55 | }; 56 | 57 | int IF_ID( Instruction *inst, word address, ReadMemFunction ReadMem, void *data ) 58 | { 59 | byte opcode, opcode2 = 0; 60 | int length = 1; 61 | opcode = ReadMem( address, data ); 62 | inst->additional_tstates = 0; 63 | inst->offset = 0; 64 | inst->immediate = 0; 65 | inst->r_increment = 1; 66 | if( opcode == 0xdd || opcode == 0xfd ) 67 | { 68 | ++length; 69 | ++inst->r_increment; 70 | opcode2 = ReadMem( ++address, data ); 71 | while( opcode2 == 0xdd || opcode2 == 0xfd ) 72 | { 73 | inst->additional_tstates += 4; 74 | opcode = opcode2; 75 | ++length; 76 | ++inst->r_increment; 77 | opcode2 = ReadMem( ++address, data ); 78 | } 79 | /* If cocode2 is 0xed, then the prefix should be 80 | * completely ignored apart from the time it took to 81 | * read and the length. */ 82 | if( opcode2 == 0xed ) 83 | opcode = opcode2; 84 | } 85 | 86 | switch( opcode ) 87 | { 88 | case 0xcb: 89 | ++length; 90 | ++inst->r_increment; 91 | opcode = ReadMem( ++address, data ); 92 | inst->IT = &CB_Prefixed[opcode]; 93 | return length; // No immediates/offset 94 | case 0xdd: 95 | if( opcode2 == 0xcb ) 96 | { 97 | length += 2; 98 | inst->offset = (sbyte)ReadMem( ++address, data ); 99 | opcode = ReadMem( ++address, data ); 100 | inst->IT = &DDCB_Prefixed[opcode]; 101 | return length; // No immediates and offset is done 102 | } 103 | inst->IT = &DD_Prefixed[opcode2]; 104 | break; // immediates 105 | case 0xed: 106 | ++length; 107 | ++inst->r_increment; 108 | opcode = ReadMem( ++address, data ); 109 | inst->IT = &ED_Prefixed[opcode]; 110 | break; // immediates 111 | case 0xfd: 112 | if( opcode2 == 0xcb ) 113 | { 114 | length += 2; 115 | inst->offset = (sbyte)ReadMem( ++address, data ); 116 | opcode = ReadMem( ++address, data ); 117 | inst->IT = &FDCB_Prefixed[opcode]; 118 | return length; // No immediates and offset is done 119 | } 120 | inst->IT = &FD_Prefixed[opcode2]; 121 | break; // immediates 122 | default: 123 | inst->IT = &Unprefixed[opcode]; 124 | break; 125 | } 126 | switch( inst->IT->operand_types ) 127 | { 128 | case TYPE_NONE: 129 | break; 130 | case TYPE_IMM_N: 131 | ++length; 132 | inst->immediate = ReadMem( ++address, data ); 133 | break; 134 | case TYPE_OFFSET: 135 | case TYPE_DISP: 136 | ++length; 137 | inst->offset = (sbyte)ReadMem( ++address, data ); 138 | break; 139 | case TYPE_IMM_NN: 140 | length += 2; 141 | inst->immediate = ReadMem( ++address, data ); 142 | inst->immediate |= ReadMem( ++address, data ) << 8; 143 | break; 144 | case TYPE_OFFSET_IMM_N: 145 | length += 2; 146 | inst->offset = (sbyte)ReadMem( ++address, data ); 147 | inst->immediate = ReadMem( ++address, data ); 148 | break; 149 | } 150 | return length; 151 | } 152 | 153 | void DisassembleInstruction( const Instruction *inst, char *buffer ) 154 | { 155 | char c; 156 | int v; 157 | 158 | switch( inst->IT->operand_types ) 159 | { 160 | case TYPE_NONE: 161 | strncpy( buffer, inst->IT->format, 25 ); 162 | buffer[24] = '\0'; 163 | break; 164 | case TYPE_IMM_N: 165 | case TYPE_IMM_NN: 166 | snprintf( buffer, 25, inst->IT->format, inst->immediate ); 167 | break; 168 | case TYPE_OFFSET: 169 | if( inst->offset >= 0 ) 170 | { 171 | c = '+'; 172 | v = inst->offset; 173 | } 174 | else 175 | { 176 | c = '-'; 177 | v = -inst->offset; 178 | } 179 | snprintf( buffer, 25, inst->IT->format, c, v ); 180 | break; 181 | case TYPE_DISP: 182 | if( inst->offset >= -2 ) 183 | { 184 | c = '+'; 185 | v = inst->offset+2; 186 | } 187 | else 188 | { 189 | c = '-'; 190 | v = -inst->offset - 2; 191 | } 192 | snprintf( buffer, 25, inst->IT->format, c, v ); 193 | break; 194 | case TYPE_OFFSET_IMM_N: 195 | if( inst->offset >= 0 ) 196 | { 197 | c = '+'; 198 | v = inst->offset; 199 | } 200 | else 201 | { 202 | c = '-'; 203 | v = -inst->offset; 204 | } 205 | snprintf( buffer, 25, inst->IT->format, c, v, inst->immediate ); 206 | break; 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /lib/z80_ZEL/z80_types.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2008, 2017 Stephen Checkoway 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to deal 5 | * in the Software without restriction, including without limitation the rights 6 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | * copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | */ 21 | 22 | /*! \file 23 | * 24 | * Types used by the z80. 25 | * \author Stephen Checkoway 26 | * \version 0.1 27 | * \date 2008 28 | */ 29 | #ifndef ZEL_Z80_TYPES_H 30 | #define ZEL_Z80_TYPES_H 31 | 32 | #ifdef __cplusplus 33 | extern "C" { 34 | #endif 35 | 36 | /*! A single byte. */ 37 | typedef uint8_t byte; 38 | /*! A signed byte used for displacement. */ 39 | typedef int8_t sbyte; 40 | /*! A 16 bit z80 word. */ 41 | typedef uint16_t word; 42 | 43 | typedef struct Z80_t 44 | { 45 | /* C guarantees consecutive layout */ 46 | word word_reg[13]; 47 | byte *byte_reg; 48 | bool iff1; 49 | bool iff2; 50 | bool can_handle_interrupt; 51 | int interrupt_mode; 52 | bool interrupt; 53 | bool nmi; 54 | bool halt; 55 | bool restart_io; 56 | 57 | byte (*ReadMem)(word, bool, Z80); 58 | void (*WriteMem)(word, byte, Z80); 59 | byte (*ReadInterruptData)(word, Z80); 60 | byte (*ReadIO)(word, Z80); 61 | void (*WriteIO)(word, byte, Z80); 62 | void (*InterruptComplete)(Z80); 63 | void (*ControlFlow)(word, word, ControlFlowType, Z80); 64 | } Z80_t; 65 | 66 | #ifdef __cplusplus 67 | } 68 | #endif 69 | #endif 70 | -------------------------------------------------------------------------------- /lib/z80_ZEL/zel/z80.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2008, 2017 Stephen Checkoway 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to deal 5 | * in the Software without restriction, including without limitation the rights 6 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | * copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | */ 21 | 22 | /*! \file 23 | * 24 | * Create and run a z80 processor instance. 25 | * \author Stephen Checkoway 26 | * \version 0.1 27 | * \date 2008, 2017 28 | */ 29 | #ifndef ZEL_Z80_H 30 | #define ZEL_Z80_H 31 | 32 | #include 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #else 36 | #include 37 | #endif 38 | 39 | /*! Opaque type representing a z80 processor. */ 40 | typedef struct Z80_t *Z80; 41 | 42 | /* NOTE: Changing the order of these requires changing z80_instructions.h! */ 43 | /*! 16 bit paired z80 registers. The registers ending in P are the 44 | * primed registers. 45 | */ 46 | enum 47 | { 48 | REG_BC, //!< z80 register bc. 49 | REG_DE, //!< z80 register de. 50 | REG_HL, //!< z80 register hl. 51 | REG_AF, //!< z80 register af. 52 | REG_IX, //!< z80 register ix. 53 | REG_IY, //!< z80 register iy. 54 | REG_PC, //!< z80 register pc. 55 | REG_SP, //!< z80 register sp. 56 | REG_BCP, //!< z80 register bc'. 57 | REG_DEP, //!< z80 register de'. 58 | REG_HLP, //!< z80 register hl'. 59 | REG_AFP, //!< z80 register af'. 60 | REG_IR, //!< z80 register ir. 61 | NUM_REG, //!< Number of 16 bit z80 paired registers. 62 | }; 63 | 64 | /*! Enumerated value describing the control flow of the processor. 65 | * \sa Z80FunctionBlock 66 | */ 67 | typedef enum 68 | { 69 | CF_CALL, //!< Call instruction. 70 | CF_JUMP, //!< Jump instruction. 71 | CF_RETURN, //!< Return instruction. 72 | CF_RETURN_I, //!< Return from interrupt instruction. 73 | CF_RETURN_N, //!< Return from nonmaskable interrupt instruction. 74 | CF_RESTART, //!< Restart instruction. 75 | CF_INTERRUPT, //!< Maskable interrupt. 76 | CF_NMI, //!< Nonmaskable interrupt. 77 | CF_HALT, //!< Halt instruction. 78 | } ControlFlowType; 79 | 80 | /*! A block of callbacks used by Z80_New() to control how the 81 | * z80 interracts with its peripherials. 82 | * \headerfile z80.h zel/z80.h 83 | */ 84 | typedef struct 85 | { 86 | /*! Read a byte of memory. 87 | * \param addr The address to read. 88 | * \param inst True if the z80 is reading instructions. 89 | * \param cpu The \c Z80 instance making the read call. 90 | * \return The byte from memory. 91 | */ 92 | uint8_t (*ReadMem)(uint16_t addr, bool inst, Z80 cpu); 93 | /*! Write a byte of memory. 94 | * \param addr The address to write. 95 | * \param val The byte to write. 96 | * \param cpu The \c Z80 instance making the write call. 97 | */ 98 | void (*WriteMem)(uint16_t addr, uint8_t val, Z80 cpu); 99 | /*! Read the interrupt data. 100 | * \param n Read the \a n th byte of data. 101 | * \param cpu The \c Z80 instance making the read call. 102 | */ 103 | uint8_t (*ReadInterruptData)(uint16_t n, Z80 cpu); 104 | /*! Read a byte from an I/O port. 105 | * \param addr The contents of the address bus during the 106 | * request. The low 8 bits specify the port. 107 | * \param cpu The \c Z80 instance making the read call. 108 | * \return The byte from the I/O port. 109 | */ 110 | uint8_t (*ReadIO)(uint16_t addr, Z80 cpu); 111 | /*! Write a byte from an I/O port. 112 | * \param addr The contents of the address bus during the 113 | * request. The low 8 bits specify the port. 114 | * \param val The byte to write. 115 | * \param cpu The \c Z80 instance making the read call. 116 | */ 117 | void (*WriteIO)(uint16_t addr, uint8_t val, Z80 cpu); 118 | /*! Notify the peripherials that a return from interrupt 119 | * instruction has occured. 120 | * \param cpu The \c Z80 instance performing the notification. 121 | */ 122 | void (*InterruptComplete)(Z80 cpu); 123 | /*! Optional notification of control flow. This can be set to 124 | * \c NULL if notification is not desired. 125 | * \param pc The address of the current instruction. 126 | * \param target The target address of the instruction. For 127 | * example, the jump target. 128 | * \param type The type of control flow. 129 | * \param cpu The \c Z80 instance performing the notification. 130 | */ 131 | void (*ControlFlow)(uint16_t pc, uint16_t target, ControlFlowType type, Z80 cpu); 132 | } Z80FunctionBlock; 133 | 134 | /*! Create a new \c Z80 instance using the callbacks specified in \a blk. 135 | * \param blk A pointer to a block of callbacks. Only 136 | * Z80FunctionBlock.ControlFlow() may be \c NULL. 137 | * \return The new \c Z80 instance. 138 | */ 139 | Z80 Z80_New( const Z80FunctionBlock *blk ); 140 | 141 | /*! Frees the memory associated with \a cpu. 142 | * \param cpu The \c Z80 instance to free. 143 | */ 144 | void Z80_Free( Z80 cpu ); 145 | 146 | /*! Perform a single step of the processor cpu. 147 | * \param outPC If non\c NULL, \a *outPC is set to the program counter after 148 | * the current instruction is executed. 149 | * \param cpu The \c Z80 instance to step. 150 | * \return The number of clock ticks that have elapsed while executing 151 | * the instruction. 152 | */ 153 | int Z80_Step( uint16_t *outPC, Z80 cpu ); 154 | 155 | /*! Check if \a cpu has halted. 156 | * \param cpu The \c Z80 instance. 157 | * \return \c true if \a cpu has halted. 158 | */ 159 | bool Z80_HasHalted( Z80 cpu ); 160 | 161 | /*! Get a 16 bit paired register. 162 | * \param reg The register to get. 163 | * \param cpu The \c Z80 instance. 164 | * \return The contents of the register specified by \a reg. 165 | */ 166 | uint16_t Z80_GetReg( int reg, Z80 cpu ); 167 | 168 | /*! Set a 16 bit paired register. 169 | * \param reg The register to set. 170 | * \param value The value to assign to the register. 171 | * \param cpu The \c Z80 instance. 172 | */ 173 | void Z80_SetReg( int reg, uint16_t value, Z80 cpu ); 174 | 175 | /*! Disassemble the z80 instruction pointed to by \a address into \a buffer. 176 | * \param address The address of the beginning the instruction. 177 | * \param buffer A pointer to at least 25 bytes of storage. If buffer 178 | * is \c NULL, then the instruction is not disassembled and only the 179 | * length is returned. 180 | * \param cpu The \c Z80 instance. The Z80FunctionBlock.ReadMem() 181 | * function will be called to read the instructions. 182 | * \return The length of the instruction in bytes. 183 | */ 184 | int Z80_Disassemble( uint16_t address, char *buffer, Z80 cpu ); 185 | 186 | /*! Simulate the NMI pin going active. This causes the z80 to jump to 187 | * the nmi handler. 188 | * \param cpu The Z80 instance. 189 | */ 190 | void Z80_RaiseNMI( Z80 cpu ); 191 | 192 | /*! Simulate the interrupt pin going active. If interrupts are not 193 | * disabled, then the z80 handles the interrupt according to the 194 | * interrupt mode. 195 | * \param cpu The Z80 instance. 196 | */ 197 | void Z80_RaiseInterrupt( Z80 cpu ); 198 | 199 | /*! Cause the z80 to reissue the I/O instruction. This only has an 200 | * effect when called during the Z80FunctionBlock.ReadIO() or 201 | * Z80FunctionBlock.WriteIO() callbacks. It is to enable debugging by 202 | * breaking on I/O. 203 | * \param cpu The \c Z80 instance. 204 | */ 205 | void Z80_RestartIO( Z80 cpu ); 206 | 207 | /*! Clear the halt condition. Causes the processor to continue 208 | * fetching instructions rather than performing NOPs. 209 | * \param cpu The \c Z80 instance. 210 | */ 211 | void Z80_ClearHalt( Z80 cpu ); 212 | 213 | #ifdef __cplusplus 214 | } 215 | #endif 216 | 217 | #endif 218 | -------------------------------------------------------------------------------- /lib/z80_ZEL/zel/z80_instruction_types.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2008, 2017 Stephen Checkoway 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to deal 5 | * in the Software without restriction, including without limitation the rights 6 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | * copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | */ 21 | 22 | /*! \file 23 | * 24 | * z80 instruction types. 25 | * \author Stephen Checkoway 26 | * \version 0.1 27 | * \date 2008, 2017 28 | */ 29 | #ifndef ZEL_Z80_INSTRUCTION_TYPES_H 30 | #define ZEL_Z80_INSTRUCTION_TYPES_H 31 | 32 | #ifdef __cplusplus 33 | extern "C" { 34 | #endif 35 | 36 | typedef enum 37 | { 38 | ADC_RR_RR, //!< adc 39 | ADC_R_I, //!< adc 40 | ADC_R_MRR, //!< adc 41 | ADC_R_N, //!< adc 42 | ADC_R_R, //!< adc 43 | ADD_RR_RR, //!< add 44 | ADD_R_I, //!< add 45 | ADD_R_MRR, //!< add 46 | ADD_R_N, //!< add 47 | ADD_R_R, //!< add 48 | AND_I, //!< and 49 | AND_MRR, //!< and 50 | AND_N, //!< and 51 | AND_R, //!< and 52 | BIT_I, //!< bit 53 | BIT_MRR, //!< bit 54 | BIT_R, //!< bit 55 | CALL_C_MNN, //!< call 56 | CALL_MNN, //!< call 57 | CCF, //!< ccf 58 | CPD, //!< cpd 59 | CPDR, //!< cpdr 60 | CPI, //!< cpi 61 | CPIR, //!< cpir 62 | CPL, //!< cpl 63 | CP_I, //!< cp 64 | CP_MRR, //!< cp 65 | CP_N, //!< cp 66 | CP_R, //!< cp 67 | DAA, //!< daa 68 | DEC_I, //!< dec 69 | DEC_MRR, //!< dec 70 | DEC_R, //!< dec 71 | DEC_RR, //!< dec 72 | DI, //!< di 73 | DJNZ, //!< djnz 74 | EI, //!< ei 75 | EXX, //!< exx 76 | EX_MRR_RR, //!< ex 77 | EX_RR_RR, //!< ex 78 | HALT, //!< halt 79 | IM, //!< im 80 | INC_I, //!< inc 81 | INC_MRR, //!< inc 82 | INC_R, //!< inc 83 | INC_RR, //!< inc 84 | IND, //!< ind 85 | INDR, //!< indr 86 | INI, //!< ini 87 | INIR, //!< inir 88 | IN_R_MN, //!< in 89 | IN_R_R, //!< in 90 | JP_C_MNN, //!< jp 91 | JP_MNN, //!< jp 92 | JP_MRR, //!< jp 93 | JR, //!< jr 94 | JR_C, //!< jr 95 | LDD, //!< ldd 96 | LDDR, //!< lddr 97 | LDI, //!< ldi 98 | LDIR, //!< ldir 99 | LD_I_N, //!< ld 100 | LD_I_R, //!< ld 101 | LD_MNN_R, //!< ld 102 | LD_MNN_RR, //!< ld 103 | LD_MRR_N, //!< ld 104 | LD_MRR_R, //!< ld 105 | LD_RR_MNN, //!< ld 106 | LD_RR_NN, //!< ld 107 | LD_RR_RR, //!< ld 108 | LD_R_I, //!< ld 109 | LD_R_MNN, //!< ld 110 | LD_R_MRR, //!< ld 111 | LD_R_N, //!< ld 112 | LD_R_R, //!< ld 113 | NEG, //!< neg 114 | NOP, //!< nop 115 | OR_I, //!< or 116 | OR_MRR, //!< or 117 | OR_N, //!< or 118 | OR_R, //!< or 119 | OTDR, //!< otdr 120 | OTIR, //!< otir 121 | OUTD, //!< outd 122 | OUTI, //!< outi 123 | OUT_MN_R, //!< out 124 | OUT_R, //!< out 125 | OUT_R_R, //!< out 126 | POP_RR, //!< pop 127 | PUSH_RR, //!< push 128 | RES_I, //!< res 129 | RES_MRR, //!< res 130 | RES_R, //!< res 131 | RET, //!< ret 132 | RETI, //!< reti 133 | RETN, //!< retn 134 | RET_C, //!< ret 135 | RLA, //!< rla 136 | RLCA, //!< rlca 137 | RLC_I, //!< rlc 138 | RLC_MRR, //!< rlc 139 | RLC_R, //!< rlc 140 | RLD, //!< rld 141 | RL_I, //!< rl 142 | RL_MRR, //!< rl 143 | RL_R, //!< rl 144 | RRA, //!< rra 145 | RRCA, //!< rrca 146 | RRC_I, //!< rrc 147 | RRC_MRR, //!< rrc 148 | RRC_R, //!< rrc 149 | RRD, //!< rrd 150 | RR_I, //!< rr 151 | RR_MRR, //!< rr 152 | RR_R, //!< rr 153 | RST, //!< rst 154 | SBC_RR_RR, //!< sbc 155 | SBC_R_I, //!< sbc 156 | SBC_R_MRR, //!< sbc 157 | SBC_R_N, //!< sbc 158 | SBC_R_R, //!< sbc 159 | SCF, //!< scf 160 | SET_I, //!< set 161 | SET_MRR, //!< set 162 | SET_R, //!< set 163 | SLA_I, //!< sla 164 | SLA_MRR, //!< sla 165 | SLA_R, //!< sla 166 | SLL_I, //!< sll 167 | SLL_MRR, //!< sll 168 | SLL_R, //!< sll 169 | SRA_I, //!< sra 170 | SRA_MRR, //!< sra 171 | SRA_R, //!< sra 172 | SRL_I, //!< srl 173 | SRL_MRR, //!< srl 174 | SRL_R, //!< srl 175 | SUB_I, //!< sub 176 | SUB_MRR, //!< sub 177 | SUB_N, //!< sub 178 | SUB_R, //!< sub 179 | XOR_I, //!< xor 180 | XOR_MRR, //!< xor 181 | XOR_N, //!< xor 182 | XOR_R, //!< xor 183 | } InstructionType; 184 | 185 | #ifdef __cplusplus 186 | } 187 | #endif 188 | 189 | #endif 190 | -------------------------------------------------------------------------------- /lib/z80_ZEL/zel/z80_instructions.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2008, 2017 Stephen Checkoway 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to deal 5 | * in the Software without restriction, including without limitation the rights 6 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | * copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | */ 21 | 22 | /*! \file 23 | * 24 | * Functions for decoding and disassembling z80 instructions. 25 | * \author Stephen Checkoway 26 | * \version 0.1 27 | * \date 2008, 2017 28 | */ 29 | #ifndef ZEL_Z80_INSTRUCTIONS_H 30 | #define ZEL_Z80_INSTRUCTIONS_H 31 | 32 | #define _GNU_SOURCE 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #ifdef __cplusplus 39 | extern "C" { 40 | #else 41 | #include 42 | #endif 43 | 44 | #include 45 | 46 | /*! The type of a z80 instruction. Many z80 instructions are similar 47 | * and share a common type. For example, add a,b and 48 | * add a,c have the same type. 49 | * 50 | * The type of each instruction can be decoded as follows. Given an 51 | * instruction \c FOO_BAR_BAZ, \c FOO is the mnemonic, \c BAR is the 52 | * type of the first operand and \c BAZ is the type of the second. The 53 | * operand types are as follows: 54 | * - \c N 8 bit immediate 55 | * - \c NN 16 bit immediate 56 | * - \c I 8 bit signed displacement from an index register 57 | * - \c R 8 bit register 58 | * - \c RR 16 bit paired register 59 | * - \c MRR 16 bit paired register used as an address 60 | * - \c MNN 16 bit immediate used as an address 61 | */ 62 | 63 | #ifndef BYTE_ORDER 64 | #error define BYTE_ORDER 65 | #endif 66 | #if BYTE_ORDER == BIG_ENDIAN 67 | /*! Register values for the 8 bit registers which comprise the 16 bit 68 | * paired registers. 69 | */ 70 | enum 71 | { 72 | REG_B, //!< z80 register b. 73 | REG_C, //!< z80 register c. 74 | REG_D, //!< z80 register d. 75 | REG_E, //!< z80 register e. 76 | REG_H, //!< z80 register h. 77 | REG_L, //!< z80 register l. 78 | REG_A, //!< z80 register a. 79 | REG_F, //!< z80 register f. 80 | REG_IXH, //!< z80 register ixh. 81 | REG_IXL, //!< z80 register ixl. 82 | REG_IYH, //!< z80 register iyh. 83 | REG_IYL, //!< z80 register iyl. 84 | REG_PCH, //!< z80 register pch. 85 | REG_PCL, //!< z80 register pcl. 86 | /* No REG_SPH */ 87 | /* No REG_SPL */ 88 | /* No REG_BP */ 89 | /* No REG_CP */ 90 | /* No REG_DP */ 91 | /* No REG_EP */ 92 | /* No REG_HP */ 93 | /* No REG_LP */ 94 | /* No REG_AP */ 95 | /* No REG_FP */ 96 | REG_I = 24, //!< z80 register I. Interrupt page address register. 97 | REG_R, //!< z80 register r. Memory refresh register. 98 | INV = 0xff, //!< Invalid. 99 | }; 100 | #elif BYTE_ORDER == LITTLE_ENDIAN 101 | /*! Register values for the 8 bit registers which comprise the 16 bit 102 | * paired registers. 103 | */ 104 | enum 105 | { 106 | REG_C, //!< z80 register c. 107 | REG_B, //!< z80 register b. 108 | REG_E, //!< z80 register e. 109 | REG_D, //!< z80 register d. 110 | REG_L, //!< z80 register l. 111 | REG_H, //!< z80 register h. 112 | REG_F, //!< z80 register f. 113 | REG_A, //!< z80 register a. 114 | REG_IXL, //!< z80 register ixl. 115 | REG_IXH, //!< z80 register ixh. 116 | REG_IYL, //!< z80 register iyl. 117 | REG_IYH, //!< z80 register iyh. 118 | REG_PCL, //!< z80 register pcl. 119 | REG_PCH, //!< z80 register pch. 120 | /* No single byte regs */ 121 | REG_R = 24, //!< z80 register r. Memory refresh register. 122 | REG_I, //!< z80 register I. Interrupt page address register. 123 | INV = 0xff, //!< Invalid. 124 | }; 125 | #else 126 | #error What endianness are you using? 127 | #endif 128 | 129 | /*! Condition flag bits as stored in register \c f. 130 | * The flags \c x and \c y are listed as unspecified by the z80 CPU 131 | * User's Manual but they are modified by a number of instructions. 132 | */ 133 | enum 134 | { 135 | FLAG_C, //!< Carry flag. 136 | FLAG_N, //!< Add/subtract flag. 137 | FLAG_P, //!< Parity/overflow flag (P/V). 138 | FLAG_X, //!< Flag 3. 139 | FLAG_H, //!< Half-carry flag. 140 | FLAG_Y, //!< Flag 5. 141 | FLAG_Z, //!< Zero flag. 142 | FLAG_S, //!< Sign flag. 143 | }; 144 | 145 | /*! Condition flags. The condition is met if the corresponding flag is 146 | * set appropriately. 147 | */ 148 | enum 149 | { 150 | COND_NZ = -FLAG_Z-1, //!< Flag Z is reset. 151 | COND_Z = FLAG_Z, //!< Flag Z is set. 152 | COND_NC = -FLAG_C-1, //!< Flag C is reset. 153 | COND_C = FLAG_C, //!< Flag C is set. 154 | COND_PO = -FLAG_P-1, //!< Flag P/V is reset. 155 | COND_PE = FLAG_P, //!< Flag P/V is set. 156 | COND_P = -FLAG_S-1, //!< Flag S is reset. 157 | COND_M = FLAG_S, //!< Flag S is set. 158 | }; 159 | 160 | /* Exactly two instructions contain both an offset and an immediate: 161 | * dd36 d n: ld (ix+d),n 162 | * fd36 d n: ld (iy+d),n */ 163 | /*! Describes the layout of the operands for an instruction. */ 164 | enum 165 | { 166 | TYPE_NONE, //!< No operands. 167 | TYPE_IMM_N, //!< 8 bit immediate. 168 | TYPE_IMM_NN, //!< 16 bit immediate. 169 | TYPE_OFFSET, //!< 8 bit signed offset. 170 | TYPE_DISP, //!< 8 bit signed offset - 2. 171 | TYPE_OFFSET_IMM_N, //!< 8 bit signed offset and 8 bit immediate. 172 | }; 173 | 174 | // 4 (3) words native sized words on 32 (64) bit machine. 175 | /*! Uniform template for the instruction tables. 176 | * \headerfile z80_instructions.h zel/z80_instructions.h 177 | */ 178 | typedef struct 179 | { 180 | InstructionType type; //!< Type of the instruction. 181 | int16_t operand1; //!< Type of the first operand, if any. 182 | int16_t operand2; //!< Type of the second operand, if any. 183 | int16_t extra; //!< Type of third operand or tstates when a branch is taken, if applicable. 184 | uint8_t tstates; //!< Base number of clock ticks the instruction takes. 185 | uint8_t operand_types; //!< Operand layout in memory for the instruction. 186 | const char *format; //!< Format specifier string for disassembly. 187 | } InstructionTemplate; 188 | 189 | /*! Completely describes an instruction. 190 | * \headerfile z80_instructions.h zel/z80_instructions.h 191 | */ 192 | typedef struct 193 | { 194 | const InstructionTemplate *IT; //!< Template for the instruction. 195 | unsigned int immediate; //!< Immediate value, if any. 196 | unsigned int additional_tstates;//!< Additional clock ticks. 197 | unsigned int r_increment; //!< Amount by which the \c r register is incremented. 198 | int offset; //!< Offset, if any. 199 | } Instruction; 200 | 201 | 202 | /*! Instruction table for unprefixed instructions. */ 203 | extern const InstructionTemplate Unprefixed[256]; 204 | /*! Instruction table for cb prefixed instructions. */ 205 | extern const InstructionTemplate CB_Prefixed[256]; 206 | /*! Instruction table for dd prefixed instructions. */ 207 | extern const InstructionTemplate DD_Prefixed[256]; 208 | /*! Instruction table for ddcb prefixed instructions. */ 209 | extern const InstructionTemplate DDCB_Prefixed[256]; 210 | /*! Instruction table for ed prefixed instructions. */ 211 | extern const InstructionTemplate ED_Prefixed[256]; 212 | /*! Instruction table for fd prefixed instructions. */ 213 | extern const InstructionTemplate FD_Prefixed[256]; 214 | /*! Instruction table for fdcb prefixed instructions. */ 215 | extern const InstructionTemplate FDCB_Prefixed[256]; 216 | 217 | /*! Memory reading callback. 218 | * \param addr The address to read. 219 | * \param data Callback data from IF_ID(). 220 | * \return The value at address \a addr. 221 | */ 222 | typedef uint8_t (*ReadMemFunction)(uint16_t addr, void *data); 223 | 224 | /*! Instruction fetch and instruction decode. Fetchs and decodes the 225 | * instruction pointed to by \a address into \a *inst. 226 | * \param inst Pointer to an \c Instruction. \a *inst is set to the 227 | * decoded instruction. 228 | * \param address The address of the instruction. 229 | * \param ReadMem Called repeatedly to get bytes of the instruction. 230 | * \a data is passed as the \c data argument. 231 | * \param data Arbitrary data passed to the \a ReadMem callback. 232 | * \return The length of the instruction. 233 | */ 234 | int IF_ID( Instruction *inst, uint16_t address, ReadMemFunction ReadMem, void *data ); 235 | 236 | /*! Disassemble the instruction pointed to by \a inst into \a buffer. 237 | * \param inst Pointer to an \c Instruction. 238 | * \param buffer Buffer into which the disassembly is written. It must 239 | * be large enough to hold 25 bytes. 240 | */ 241 | void DisassembleInstruction( const Instruction *inst, char *buffer ); 242 | 243 | #ifdef __cplusplus 244 | } 245 | #endif 246 | 247 | #endif 248 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [env:pico32] 12 | platform = espressif32@5.3.0 13 | board = pico32 14 | framework = espidf 15 | ;lib_deps = fdivitto/FabGL@1.0.9 16 | lib_deps = https://github.com/fdivitto/FabGL.git#v1.0.8 17 | build_flags = -std=c++11 -O2 18 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file was automatically generated for projects 2 | # without default 'CMakeLists.txt' file. 3 | 4 | FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.*) 5 | 6 | idf_component_register(SRCS ${app_sources}) 7 | -------------------------------------------------------------------------------- /src/File.cpp: -------------------------------------------------------------------------------- 1 | #include "File.h" 2 | #include "settings.h" 3 | #include "esp_log.h" 4 | 5 | using namespace zx; 6 | 7 | size_t File::read(uint8_t* buf, size_t size) 8 | { 9 | int pos1 = this->tellg(); 10 | reinterpret_cast(this)->read((char *)buf, size); 11 | int pos2 = this->tellg(); 12 | return pos2 - pos1; 13 | } 14 | 15 | size_t File::write(const uint8_t *buf, size_t size) 16 | { 17 | int pos1 = this->tellp(); 18 | reinterpret_cast(this)->write((char *)buf, size); 19 | int pos2 = this->tellp(); 20 | return pos2 - pos1; 21 | } 22 | 23 | bool File::seek(uint32_t off, ios_base::seekdir way) 24 | { 25 | this->seekg(off, way); 26 | return !this->bad(); 27 | } 28 | -------------------------------------------------------------------------------- /src/FileSystem.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "esp_vfs_fat.h" 5 | #include "esp_log.h" 6 | 7 | #include "settings.h" 8 | #include "FileSystem.h" 9 | #include "Emulator.h" 10 | #include "ps2Input.h" 11 | #include "z80main.h" 12 | #include "z80snapshot.h" 13 | #include "ScreenArea.h" 14 | #include "errorReadingFile.h" 15 | #include "File.h" 16 | 17 | using namespace zx; 18 | 19 | #define DEBUG_COLUMNS 42 20 | #define DEBUG_ROWS 58 21 | #define FILE_COLUMNS 3 22 | #define FILE_COLUMNWIDTH (DEBUG_COLUMNS / FILE_COLUMNS) 23 | #define MAX_LFN 90 24 | #define FORE_COLOR (DEBUG_BAND_COLORS >> 8) 25 | #define BACK_COLOR (DEBUG_BAND_COLORS & 0xFF) 26 | 27 | extern VideoController* Screen; 28 | extern ScreenArea DebugScreen; 29 | 30 | typedef TCHAR FileName[MAX_LFN + 1]; 31 | 32 | static FileName* _fileNames = (FileName*)_buffer16K_2; 33 | static int16_t _selectedFile = 0; 34 | static int16_t _fileCount; 35 | static bool _loadingSnapshot = false; 36 | static bool _savingSnapshot = false; 37 | static char* _snapshotName = ((char*)_buffer16K_1) + MAX_LFN; 38 | 39 | static char _rootFolder[MAX_LFN]; 40 | static int _rootFolderLength; 41 | 42 | static esp_vfs_fat_sdmmc_mount_config_t _mount_config; 43 | static sdmmc_host_t _host = SDSPI_HOST_DEFAULT(); 44 | static sdspi_device_config_t _slot_config; 45 | static sdmmc_card_t* _card = nullptr; 46 | 47 | void FileSystemInitialize() 48 | { 49 | ESP_LOGI(TAG, "Initializing SD card"); 50 | 51 | _mount_config = { 52 | .format_if_mount_failed = false, 53 | .max_files = 1, 54 | .allocation_unit_size = 16 * 1024 55 | }; 56 | 57 | spi_host_device_t hostID = spi_host_device_t(_host.slot); 58 | 59 | spi_bus_config_t bus_cfg = {}; 60 | bus_cfg.mosi_io_num = PIN_NUM_MOSI; 61 | bus_cfg.miso_io_num = PIN_NUM_MISO; 62 | bus_cfg.sclk_io_num = PIN_NUM_CLK; 63 | bus_cfg.quadwp_io_num = -1; 64 | bus_cfg.quadhd_io_num = -1; 65 | bus_cfg.max_transfer_sz = 4000; 66 | 67 | esp_err_t ret = spi_bus_initialize(SPI2_HOST, &bus_cfg, SDSPI_DEFAULT_DMA); 68 | if (ret != ESP_OK) { 69 | ESP_LOGE(TAG, "Failed to initialize bus."); 70 | } 71 | 72 | _slot_config = SDSPI_DEVICE_CONFIG_DEFAULT(); 73 | _slot_config.gpio_cs = PIN_NUM_CS; 74 | _slot_config.host_id = hostID; 75 | } 76 | 77 | static FRESULT mount() 78 | { 79 | return esp_vfs_fat_sdspi_mount(SDCARD_PATH, &_host, &_slot_config, &_mount_config, &_card) == ESP_OK ? FR_OK : FR_NOT_READY; 80 | } 81 | 82 | static void unmount() 83 | { 84 | if (_card != nullptr) 85 | { 86 | esp_vfs_fat_sdcard_unmount(SDCARD_PATH, _card); 87 | _card = nullptr; 88 | } 89 | } 90 | 91 | static void GetFileCoord(uint16_t fileIndex, uint8_t* x, uint8_t* y) 92 | { 93 | *x = fileIndex / (DEBUG_ROWS - 1) * (FILE_COLUMNWIDTH + 1); 94 | *y = 1 + fileIndex % (DEBUG_ROWS - 1); 95 | } 96 | 97 | static TCHAR* GetFileName(TCHAR* fileName) 98 | { 99 | TCHAR* result = (TCHAR*)_buffer16K_1; 100 | strncpy(result, _rootFolder, _rootFolderLength); 101 | strncpy(result + _rootFolderLength, fileName, MAX_LFN); 102 | 103 | return result; 104 | } 105 | 106 | static TCHAR* FileExtension(TCHAR* fileName) 107 | { 108 | TCHAR* result = (TCHAR*)_buffer16K_1; 109 | memcpy(result, fileName, MAX_LFN); 110 | result[MAX_LFN] = '\0'; 111 | 112 | result[MAX_LFN - 1] = '\0'; 113 | char* extension = strrchr(result, '.'); 114 | if (extension != nullptr) 115 | { 116 | for(int i = 0; extension[i]; i++) 117 | { 118 | extension[i] = tolower(extension[i]); 119 | } 120 | } 121 | else 122 | { 123 | result[0] = '\0'; 124 | extension = result; 125 | } 126 | 127 | return extension; 128 | } 129 | 130 | static TCHAR* TruncateFileName(TCHAR* fileName) 131 | { 132 | int maxLength = FILE_COLUMNWIDTH + 1; 133 | TCHAR* result = (TCHAR*) _buffer16K_1; 134 | strncpy(result, fileName, maxLength); 135 | 136 | result[maxLength - 1] = '\0'; 137 | TCHAR* extension = strrchr(result, '.'); 138 | if (extension != nullptr) 139 | { 140 | *extension = '\0'; 141 | } 142 | 143 | return result; 144 | } 145 | 146 | static void noScreenshot() 147 | { 148 | // Error reading selected file 149 | Screen->ShowScreenshot(errorReadingFile, 0); 150 | } 151 | 152 | static void SetSelection(uint8_t selectedFile) 153 | { 154 | if (_fileCount == 0) 155 | { 156 | return; 157 | } 158 | 159 | _selectedFile = selectedFile; 160 | 161 | uint8_t x, y; 162 | GetFileCoord(selectedFile, &x, &y); 163 | for (uint8_t i = x; i < x + FILE_COLUMNWIDTH; i++) 164 | { 165 | DebugScreen.SetAttribute(i, y, BACK_COLOR, FORE_COLOR); // inverse 166 | } 167 | 168 | FRESULT fr; 169 | 170 | // Show screenshot for the selected file 171 | fr = mount(); 172 | if (fr == FR_OK) 173 | { 174 | File file; 175 | bool scrFileFound = false; 176 | 177 | TCHAR* fileName = GetFileName(_fileNames[selectedFile]); 178 | 179 | // Try to open file with the same name and .SCR extension 180 | TCHAR* scrFileName = (TCHAR*)&_buffer16K_1[_rootFolderLength + MAX_LFN + 1]; 181 | strncpy(scrFileName, fileName, MAX_LFN + 1); 182 | TCHAR* extension = strrchr(scrFileName, '.'); 183 | if (extension != nullptr) 184 | { 185 | strncpy(extension, ".scr", 5); 186 | file.open(scrFileName, ios_base::in); 187 | if (file.is_open()) 188 | { 189 | if (!LoadScreenshot(&file, _buffer16K_1)) 190 | { 191 | noScreenshot(); 192 | } 193 | file.close(); 194 | scrFileFound = true; 195 | } 196 | } 197 | 198 | if (!scrFileFound) 199 | { 200 | file.open(fileName, ios_base::in); 201 | if (file.is_open()) 202 | { 203 | if (!LoadScreenFromZ80Snapshot(&file, _buffer16K_1)) 204 | { 205 | noScreenshot(); 206 | } 207 | file.close(); 208 | _ay3_8912.Clear(); 209 | } 210 | } 211 | 212 | unmount(); 213 | } 214 | } 215 | 216 | static void loadSnapshot(const TCHAR* fileName) 217 | { 218 | FRESULT fr = mount(); 219 | if (fr == FR_OK) 220 | { 221 | File file; 222 | file.open(fileName, ios_base::in); 223 | if (file.is_open()) 224 | { 225 | LoadZ80Snapshot(&file, _buffer16K_1, _buffer16K_2); 226 | file.close(); 227 | } 228 | 229 | unmount(); 230 | } 231 | } 232 | 233 | static bool saveSnapshot(const TCHAR* fileName) 234 | { 235 | bool result = false; 236 | FRESULT fr = mount(); 237 | if (fr == FR_OK) 238 | { 239 | File file; 240 | file.open(fileName, ios_base::out); 241 | if (file.is_open()) 242 | { 243 | result = SaveZ80Snapshot(&file, _buffer16K_1, _buffer16K_2); 244 | file.close(); 245 | } 246 | 247 | unmount(); 248 | } 249 | 250 | return result; 251 | } 252 | 253 | static int fileCompare(const void* a, const void* b) 254 | { 255 | TCHAR* file1 = (TCHAR*)_buffer16K_1; 256 | for (int i = 0; i <= MAX_LFN; i++){ 257 | file1[i] = tolower(((TCHAR*)a)[i]); 258 | } 259 | 260 | TCHAR* file2 = (TCHAR*)&_buffer16K_1[MAX_LFN + 2]; 261 | for (int i = 0; i <= MAX_LFN; i++){ 262 | file2[i] = tolower(((TCHAR*)b)[i]); 263 | } 264 | 265 | return strncmp(file1, file2, MAX_LFN + 1); 266 | } 267 | 268 | bool saveSnapshotSetup(const char* path) 269 | { 270 | string rootFolder = string(SDCARD_PATH); 271 | rootFolder.append(path); 272 | strcpy(_rootFolder, rootFolder.c_str()); 273 | _rootFolderLength = rootFolder.length(); 274 | 275 | DebugScreen.SetPrintAttribute(FORE_COLOR, BACK_COLOR); 276 | DebugScreen.Clear(); 277 | 278 | showTitle("Save snapshot. ENTER, ESC, BS"); 279 | 280 | FRESULT fr = mount(); 281 | if (fr != FR_OK) 282 | { 283 | return false; 284 | } 285 | 286 | // Unmount file system 287 | unmount(); 288 | 289 | DebugScreen.PrintAt(0, 2, "Enter file name:"); 290 | DebugScreen.SetCursorPosition(0, 3); 291 | DebugScreen.ShowCursor(); 292 | memset(_snapshotName, 0, MAX_LFN + 1); 293 | _savingSnapshot = true; 294 | 295 | return true; 296 | } 297 | 298 | bool saveSnapshotLoop() 299 | { 300 | if (!_savingSnapshot) 301 | { 302 | return false; 303 | } 304 | 305 | int32_t scanCode = Ps2_GetScancode(); 306 | if (scanCode == 0 || (scanCode & 0xFF00) == 0xF000) 307 | { 308 | return true; 309 | } 310 | 311 | scanCode = ((scanCode & 0xFF0000) >> 8 | (scanCode & 0xFF)); 312 | uint8_t x = DebugScreen.getX(); 313 | TCHAR* fileName; 314 | switch (scanCode) 315 | { 316 | case KEY_BACKSPACE: 317 | if (x > 0) 318 | { 319 | DebugScreen.PrintAt(x - 1, DebugScreen.getY(), " "); 320 | DebugScreen.SetCursorPosition(x - 1, DebugScreen.getY()); 321 | _snapshotName[x] = '\0'; 322 | } 323 | break; 324 | 325 | case KEY_ENTER: 326 | case KEY_KP_ENTER: 327 | DebugScreen.HideCursor(); 328 | DebugScreen.PrintAt(0, 5, "Saving... "); 329 | fileName = GetFileName(_snapshotName); 330 | strcat(fileName,".z80"); 331 | if (saveSnapshot(fileName)) 332 | { 333 | _savingSnapshot = false; 334 | restoreState(); 335 | return false; 336 | } 337 | else 338 | { 339 | DebugScreen.SetPrintAttribute(0x0310); // red on blue 340 | DebugScreen.PrintAt(0, 5, "Error saving file"); 341 | DebugScreen.SetPrintAttribute(0x3F10); // white on blue 342 | DebugScreen.SetCursorPosition(x, 3); 343 | DebugScreen.ShowCursor(); 344 | } 345 | break; 346 | 347 | case KEY_ESC: 348 | _savingSnapshot = false; 349 | restoreState(); 350 | return false; 351 | 352 | default: 353 | char character = Ps2_ConvertScancode(scanCode); 354 | if (x < FILE_COLUMNWIDTH && character != '\0' 355 | && character != '\\' && character != '/' && character != ':' 356 | && character != '*' && character != '?' && character != '"' 357 | && character != '<' && character != '>' && character != '|') 358 | { 359 | char* text = (char*)_buffer16K_1; 360 | text[0] = character; 361 | _snapshotName[x] = character; 362 | text[1] = '\0'; 363 | DebugScreen.Print(text); 364 | } 365 | break; 366 | } 367 | 368 | return true; 369 | } 370 | 371 | bool loadSnapshotSetup(const char* path) 372 | { 373 | string rootFolder = string(SDCARD_PATH); 374 | rootFolder.append(path); 375 | strcpy(_rootFolder, rootFolder.c_str()); 376 | _rootFolderLength = rootFolder.length(); 377 | 378 | saveState(); 379 | 380 | DebugScreen.SetPrintAttribute(0x3F10); // white on blue 381 | DebugScreen.Clear(); 382 | 383 | showTitle("Loading files, please wait..."); 384 | 385 | FRESULT fr = mount(); 386 | if (fr != FR_OK) 387 | { 388 | return false; 389 | } 390 | 391 | uint8_t maxFileCount = (DEBUG_ROWS - 1) * FILE_COLUMNS; 392 | _fileCount = 0; 393 | bool result = true; 394 | 395 | FF_DIR folder; 396 | FILINFO fileInfo; 397 | fr = f_opendir(&folder, (const TCHAR*) "/"); 398 | if (fr == FR_OK) 399 | { 400 | int fileIndex = 0; 401 | while (fileIndex < maxFileCount) 402 | { 403 | fr = f_readdir(&folder, &fileInfo); 404 | if (fr != FR_OK || fileInfo.fname[0] == 0) 405 | { 406 | result = _fileCount > 0; 407 | break; 408 | } 409 | 410 | if (fileInfo.fattrib & AM_DIR) 411 | { 412 | continue; 413 | } 414 | 415 | // *.z80 416 | if (strncmp(FileExtension((TCHAR*)fileInfo.fname), ".z80", 4) != 0) 417 | { 418 | continue; 419 | } 420 | 421 | memcpy(_fileNames[fileIndex], fileInfo.fname, MAX_LFN + 1); 422 | 423 | _fileCount++; 424 | fileIndex++; 425 | } 426 | } 427 | else 428 | { 429 | result = false; 430 | } 431 | 432 | // Sort files alphabetically 433 | if (_fileCount > 0) 434 | { 435 | qsort(_fileNames, _fileCount, MAX_LFN + 1, fileCompare); 436 | 437 | for (int y = 1; y < DEBUG_ROWS; y++) 438 | { 439 | DebugScreen.PrintAt(FILE_COLUMNWIDTH, y, "\xB3"); // │ 440 | DebugScreen.PrintAt(FILE_COLUMNWIDTH * 2 + 1, y, "\xB3"); // │ 441 | } 442 | 443 | uint8_t x, y; 444 | for (int fileIndex = 0; fileIndex < _fileCount; fileIndex++) 445 | { 446 | GetFileCoord(fileIndex, &x, &y); 447 | DebugScreen.PrintAt(x, y, TruncateFileName(_fileNames[fileIndex])); 448 | } 449 | 450 | SetSelection(_selectedFile); 451 | } 452 | 453 | // Unmount file system 454 | unmount(); 455 | 456 | if (result) 457 | { 458 | _loadingSnapshot = true; 459 | } 460 | 461 | showTitle("Load snapshot. ENTER, ESC, \x18, \x19, \x1A, \x1B"); // ↑, ↓, →, ← 462 | 463 | return result; 464 | } 465 | 466 | bool loadSnapshotLoop() 467 | { 468 | if (!_loadingSnapshot) 469 | { 470 | return false; 471 | } 472 | 473 | int32_t scanCode = Ps2_GetScancode(); 474 | if (scanCode == 0 || (scanCode & 0xFF00) == 0xF000) 475 | { 476 | return true; 477 | } 478 | 479 | uint8_t previousSelection = _selectedFile; 480 | 481 | scanCode &= 0xFFFF; 482 | switch (scanCode) 483 | { 484 | case KEY_UPARROW: 485 | if (_selectedFile > 0) 486 | { 487 | _selectedFile--; 488 | } 489 | break; 490 | 491 | case KEY_DOWNARROW: 492 | if (_selectedFile < _fileCount - 1) 493 | { 494 | _selectedFile++; 495 | } 496 | break; 497 | 498 | case KEY_LEFTARROW: 499 | if (_selectedFile >= DEBUG_ROWS - 1) 500 | { 501 | _selectedFile -= DEBUG_ROWS - 1; 502 | } 503 | break; 504 | 505 | case KEY_RIGHTARROW: 506 | if (_selectedFile + DEBUG_ROWS <= _fileCount) 507 | { 508 | _selectedFile += DEBUG_ROWS - 1; 509 | } 510 | break; 511 | 512 | case KEY_ENTER: 513 | case KEY_KP_ENTER: 514 | loadSnapshot(GetFileName(_fileNames[_selectedFile])); 515 | _loadingSnapshot = false; 516 | restoreState(); 517 | return false; 518 | 519 | case KEY_ESC: 520 | _loadingSnapshot = false; 521 | restoreState(); 522 | return false; 523 | } 524 | 525 | if (previousSelection == _selectedFile) 526 | { 527 | return true; 528 | } 529 | 530 | uint8_t x, y; 531 | GetFileCoord(previousSelection, &x, &y); 532 | for (uint8_t i = x; i < x + FILE_COLUMNWIDTH; i++) 533 | { 534 | DebugScreen.SetAttribute(i, y, FORE_COLOR, BACK_COLOR); 535 | } 536 | 537 | SetSelection(_selectedFile); 538 | 539 | return true; 540 | } 541 | 542 | bool ReadFromFile(const char* fileName, uint8_t* buffer, size_t size) 543 | { 544 | FRESULT fr = mount(); 545 | if (fr != FR_OK) 546 | { 547 | return false; 548 | } 549 | 550 | string rootFolder = string(SDCARD_PATH); 551 | strcpy(_rootFolder, rootFolder.c_str()); 552 | _rootFolderLength = rootFolder.length(); 553 | TCHAR* filePath = GetFileName((TCHAR*)fileName); 554 | 555 | bool result = false; 556 | File file; 557 | file.open(filePath, ios_base::in); 558 | if (file.is_open()) 559 | { 560 | size_t bytesRead = file.read(buffer, size); 561 | result = (bytesRead == size); 562 | file.close(); 563 | } 564 | 565 | unmount(); 566 | 567 | return result; 568 | } -------------------------------------------------------------------------------- /src/RamPage.cpp: -------------------------------------------------------------------------------- 1 | #include "RamPage.h" 2 | 3 | #include 4 | 5 | RamPage& RamPage::operator=(void* allocatedRam) 6 | { 7 | this->_data = (uint8_t*)allocatedRam; 8 | return *this; 9 | } 10 | 11 | RamPage::operator uint8_t*() 12 | { 13 | return this->_data; 14 | } 15 | 16 | uint8_t RamPage::ReadByte(uint16_t addr) 17 | { 18 | return this->_data[addr]; 19 | } 20 | 21 | void RamPage::WriteByte(uint16_t addr, uint8_t data) 22 | { 23 | this->_data[addr] = data; 24 | } 25 | 26 | void RamPage::FromBuffer(void* buffer) 27 | { 28 | memcpy(this->_data, buffer, 0x4000); 29 | } 30 | 31 | void RamPage::ToBuffer(void* buffer) 32 | { 33 | memcpy(buffer, this->_data, 0x4000); 34 | } 35 | -------------------------------------------------------------------------------- /src/RamVideoPage.cpp: -------------------------------------------------------------------------------- 1 | #include "RamVideoPage.h" 2 | 3 | #include 4 | #include "z80Environment.h" 5 | 6 | void RamVideoPage::Initialize(SpectrumScreenData* videoRam, void* allocatedRam) 7 | { 8 | this->_videoRam = videoRam; 9 | this->_data = (uint8_t*)allocatedRam; 10 | } 11 | 12 | uint8_t RamVideoPage::ReadByte(uint16_t addr) 13 | { 14 | switch (addr) 15 | { 16 | case 0x0000 ... 0x17FF: 17 | // Screen pixels 18 | return this->_videoRam->Pixels[addr]; 19 | case 0x1800 ... 0x1AFF: 20 | // Screen Attributes 21 | return Z80Environment::ToSpectrumColor(this->_videoRam->Attributes[addr - (uint16_t)0x1800]); 22 | case 0x1B00 ... 0x3FFF: 23 | return this->_data[addr - (uint16_t)0x1B00]; 24 | default: 25 | return 0xFF; 26 | } 27 | } 28 | 29 | void RamVideoPage::WriteByte(uint16_t addr, uint8_t data) 30 | { 31 | switch (addr) 32 | { 33 | case 0x0000 ... 0x17FF: 34 | // Screen pixels 35 | this->_videoRam->Pixels[addr] = data; 36 | break; 37 | case 0x1800 ... 0x1AFF: 38 | // Screen Attributes 39 | this->_videoRam->Attributes[addr - (uint16_t)0x1800] = Z80Environment::FromSpectrumColor(data); 40 | break; 41 | case 0x1B00 ... 0x3FFF: 42 | this->_data[addr - (uint16_t)0x1B00] = data; 43 | break; 44 | } 45 | } 46 | 47 | void RamVideoPage::FromBuffer(void* data) 48 | { 49 | // Screen pixels 50 | memcpy(this->_videoRam->Pixels, data, 0x1800); 51 | 52 | // Screen Attributes 53 | for (uint32_t i = 0x1800; i < 0x1B00; i++) 54 | { 55 | this->WriteByte(i, ((uint8_t*)data)[i]); 56 | } 57 | 58 | // The rest 59 | memcpy(this->_data, &((uint8_t*)data)[0x1B00], 0x2500); 60 | } 61 | 62 | void RamVideoPage::ToBuffer(void* buffer) 63 | { 64 | uint8_t* data = (uint8_t*)buffer; 65 | 66 | // Screen pixels 67 | memcpy(buffer, this->_videoRam->Pixels, 0x1800); 68 | 69 | // Screen Attributes 70 | for (uint32_t i = 0x1800; i < 0x1B00; i++) 71 | { 72 | data[i] = this->ReadByte(i); 73 | } 74 | 75 | // The rest 76 | memcpy(&data[0x1B00], this->_data, 0x2500); 77 | } 78 | -------------------------------------------------------------------------------- /src/ScreenArea.cpp: -------------------------------------------------------------------------------- 1 | #include "ScreenArea.h" 2 | 3 | ScreenArea::ScreenArea(VideoController* videoController, 4 | uint16_t xOffset, uint16_t width, 5 | uint16_t yOffset, uint16_t height) 6 | { 7 | this->_videoController = videoController; 8 | this->_xOffset = xOffset; 9 | this->_Width = width; 10 | this->_yOffset = yOffset; 11 | this->_Height = height; 12 | } 13 | 14 | void ScreenArea::Clear() 15 | { 16 | this->HideCursor(); 17 | for (int y = this->_yOffset; y < this->_yOffset + this->_Height; y++) 18 | { 19 | for (int x = this->_xOffset; x < this->_xOffset + this->_Width; x++) 20 | { 21 | int i = y * SCREEN_WIDTH + x; 22 | this->_videoController->Characters[i] = ' '; 23 | this->_videoController->Attributes[i] = this->_videoController->_defaultAttribute; 24 | } 25 | } 26 | } 27 | 28 | void ScreenArea::SetPrintAttribute(uint16_t attribute) 29 | { 30 | this->foreColor = attribute >> 8; 31 | this->backColor = attribute & 0xFF; 32 | } 33 | 34 | void ScreenArea::SetPrintAttribute(uint8_t foreColor, uint8_t backColor) 35 | { 36 | this->foreColor = foreColor; 37 | this->backColor = backColor; 38 | } 39 | 40 | void ScreenArea::SetAttribute(uint8_t x, uint8_t y, uint8_t foreColor, uint8_t backColor) 41 | { 42 | this->_videoController->SetAttribute(x + this->_xOffset, y + this->_yOffset, foreColor, backColor); 43 | } 44 | 45 | void ScreenArea::PrintAt(uint8_t x, uint8_t y, const char* str) 46 | { 47 | this->SetCursorPosition(x, y); 48 | this->Print(str); 49 | } 50 | 51 | void ScreenArea::PrintAlignCenter(uint8_t y, const char *str) 52 | { 53 | uint8_t leftX = (this->_Width - strlen(str)) / 2; 54 | this->PrintAt(leftX, y, str); 55 | } 56 | 57 | uint16_t ScreenArea::getX() 58 | { 59 | if (this->_videoController->cursor_x < this->_xOffset) 60 | { 61 | return 0; 62 | } 63 | else 64 | { 65 | return this->_videoController->cursor_x - this->_xOffset; 66 | } 67 | } 68 | 69 | uint16_t ScreenArea::getY() 70 | { 71 | if (this->_videoController->cursor_y < this->_yOffset) 72 | { 73 | return 0; 74 | } 75 | else 76 | { 77 | return this->_videoController->cursor_y - this->_yOffset; 78 | } 79 | } 80 | 81 | void ScreenArea::ShowCursor() 82 | { 83 | if (!this->_isCursorVisible) 84 | { 85 | this->_isCursorVisible = true; 86 | this->SetAttribute(this->getX(), this->getY(), this->backColor, this->foreColor); 87 | } 88 | } 89 | 90 | void ScreenArea::HideCursor() 91 | { 92 | if (this->_isCursorVisible) 93 | { 94 | this->_isCursorVisible = false; 95 | this->SetAttribute(this->getX(), this->getY(), this->foreColor, this->backColor); 96 | } 97 | } 98 | 99 | void ScreenArea::Print(const char* str) 100 | { 101 | if (this->_isCursorVisible) 102 | { 103 | this->SetAttribute(this->getX(), this->getY(), this->foreColor, this->backColor); 104 | } 105 | this->_videoController->print((char*)str, this->foreColor, this->backColor); 106 | if (this->_isCursorVisible) 107 | { 108 | this->SetAttribute(this->getX(), this->getY(), this->backColor, this->foreColor); 109 | } 110 | } 111 | 112 | void ScreenArea::SetCursorPosition(uint8_t x, uint8_t y) 113 | { 114 | if (this->_isCursorVisible) 115 | { 116 | this->SetAttribute(this->getX(), this->getY(), this->foreColor, this->backColor); 117 | } 118 | this->_videoController->SetCursorPosition(x + this->_xOffset, y + this->_yOffset); 119 | if (this->_isCursorVisible) 120 | { 121 | this->SetAttribute(this->getX(), this->getY(), this->backColor, this->foreColor); 122 | } 123 | } -------------------------------------------------------------------------------- /src/ay3-8912-state.cpp: -------------------------------------------------------------------------------- 1 | #include "ay3-8912-state.h" 2 | #include "volume.h" 3 | #include "settings.h" 4 | 5 | using namespace fabgl; 6 | 7 | static SoundGenerator _soundGenerator(TSTATES_PER_FRAME * 60 / 256, GPIO_AUTO, SoundGenMethod::Auto); 8 | static SquareWaveformGenerator _channel[3]; 9 | 10 | #ifdef ZX128K 11 | // CPU clock is 3.5469MHz, 125000 * 3.5469 / 4 = 110840 12 | #define FREQUENCY_REFERENCE 110840 13 | #else 14 | // CPU clock is 3.5Hz, 125000 * 3.5 / 4 = 109375 15 | #define FREQUENCY_REFERENCE 109375 16 | #endif 17 | 18 | inline int fixPitch(int p) 19 | { 20 | int fixed = (p > 0) ? (FREQUENCY_REFERENCE / p) : 0; 21 | if (fixed > 15000) fixed = 15000; 22 | return fixed; 23 | } 24 | 25 | namespace Sound 26 | { 27 | 28 | void Ay3_8912_state::Initialize() 29 | { 30 | _soundGenerator.setVolume(126); 31 | _soundGenerator.play(true); 32 | for (int8_t channel = 0; channel < 3; channel++) 33 | { 34 | _soundGenerator.attach(&_channel[channel]); 35 | _channel[channel].enable(true); 36 | _channel[channel].setVolume(0); 37 | } 38 | } 39 | 40 | void Ay3_8912_state::AttachSoundGenerator(WaveformGenerator* soundGenerator) 41 | { 42 | _soundGenerator.attach(soundGenerator); 43 | } 44 | 45 | void Ay3_8912_state::StopSound() 46 | { 47 | _soundGenerator.play(false); 48 | } 49 | 50 | void Ay3_8912_state::ResumeSound() 51 | { 52 | _soundGenerator.play(true); 53 | } 54 | 55 | void Ay3_8912_state::Clear() 56 | { 57 | this->finePitchChannelA = 0xFF; 58 | this->coarsePitchChannelA = 0xFF; 59 | this->finePitchChannelB = 0xFF; 60 | this->coarsePitchChannelB = 0xFF; 61 | this->finePitchChannelC = 0xFF; 62 | this->coarsePitchChannelC = 0xFF; 63 | this->noisePitch = 0xFF; 64 | this->mixer = 0xFF; 65 | this->volumeChannelA = 0xFF; 66 | this->volumeChannelB = 0xFF; 67 | this->volumeChannelC = 0xFF; 68 | this->envelopeFineDuration = 0xFF; 69 | this->envelopeCoarseDuration = 0xFF; 70 | this->envelopeShape = 0xFF; 71 | this->ioPortA = 0xFF; 72 | 73 | // Status 74 | this->selectedRegister = 0xFF; 75 | for (int channel = 0; channel < 3; channel++) 76 | { 77 | this->channelVolume[channel] = 0xFF; 78 | this->channelFrequency[channel] = 0xFFFF; 79 | _channel[channel].setVolume(0); 80 | } 81 | } 82 | 83 | void Ay3_8912_state::updated() 84 | { 85 | uint16_t oldChannelFrequency[3]; 86 | uint8_t oldChannelVolume[3]; 87 | 88 | for (int8_t channel = 0; channel < 3; channel++) 89 | { 90 | uint8_t channelVolume = this->channelVolume[channel]; 91 | oldChannelFrequency[channel] = channelVolume ? this->channelFrequency[channel] : 0xFFFF; 92 | oldChannelVolume[channel] = channelVolume; 93 | } 94 | 95 | int16_t pitch; 96 | 97 | switch (this->selectedRegister) 98 | { 99 | case 0: 100 | case 1: 101 | pitch = ((this->coarsePitchChannelA << 8) | this->finePitchChannelA) & 0x0FFF; 102 | this->channelFrequency[0] = pitch; 103 | break; 104 | case 2: 105 | case 3: 106 | pitch = ((this->coarsePitchChannelB << 8) | this->finePitchChannelB) & 0x0FFF; 107 | this->channelFrequency[1] = pitch; 108 | break; 109 | case 4: 110 | case 5: 111 | pitch = ((this->coarsePitchChannelC << 8) | this->finePitchChannelC) & 0x0FFF; 112 | this->channelFrequency[2] = pitch; 113 | break; 114 | case 6: 115 | // noisePitch - ignored for now 116 | break; 117 | case 7: 118 | this->channelVolume[0] = (this->mixer & 0x01) ? 0 : volume[this->volumeChannelA & 0x0F]; 119 | this->channelVolume[1] = (this->mixer & 0x02) ? 0 : volume[this->volumeChannelB & 0x0F]; 120 | this->channelVolume[2] = (this->mixer & 0x04) ? 0 : volume[this->volumeChannelC & 0x0F]; 121 | break; 122 | case 8: 123 | this->channelVolume[0] = (this->mixer & 0x01) ? 0 : volume[this->volumeChannelA & 0x0F]; 124 | break; 125 | case 9: 126 | this->channelVolume[1] = (this->mixer & 0x02) ? 0 : volume[this->volumeChannelB & 0x0F]; 127 | break; 128 | case 10: 129 | this->channelVolume[2] = (this->mixer & 0x04) ? 0 : volume[this->volumeChannelC & 0x0F]; 130 | break; 131 | case 11: 132 | // envelopeFineDuration - ignored for now 133 | break; 134 | case 12: 135 | // envelopeCoarseDuration - ignored for now 136 | break; 137 | case 13: 138 | // envelopeShape - ignored for now 139 | break; 140 | case 14: 141 | // ioPortA - ignored for now 142 | break; 143 | } 144 | 145 | for (int8_t channel = 0; channel < 3; channel++) 146 | { 147 | if (this->channelVolume[channel] != oldChannelVolume[channel]) 148 | { 149 | _channel[channel].setVolume(this->channelVolume[channel]); 150 | } 151 | 152 | if (this->channelVolume[channel] == 0) 153 | { 154 | _channel[channel].setVolume(0); 155 | 156 | continue; 157 | } 158 | 159 | if (this->channelFrequency[channel] != oldChannelFrequency[channel]) 160 | { 161 | _channel[channel].setFrequency(fixPitch(this->channelFrequency[channel])); 162 | } 163 | } 164 | } 165 | 166 | void Ay3_8912_state::selectRegister(uint8_t registerNumber) 167 | { 168 | this->selectedRegister = registerNumber; 169 | } 170 | 171 | void Ay3_8912_state::setRegisterData(uint8_t data) 172 | { 173 | switch (this->selectedRegister) 174 | { 175 | case 0: 176 | this->finePitchChannelA = data; 177 | break; 178 | case 1: 179 | this->coarsePitchChannelA = data; 180 | break; 181 | case 2: 182 | this->finePitchChannelB = data; 183 | break; 184 | case 3: 185 | this->coarsePitchChannelB = data; 186 | break; 187 | case 4: 188 | this->finePitchChannelC = data; 189 | break; 190 | case 5: 191 | this->coarsePitchChannelC = data; 192 | break; 193 | case 6: 194 | this->noisePitch = data; 195 | break; 196 | case 7: 197 | this->mixer = data; 198 | break; 199 | case 8: 200 | this->volumeChannelA = data; 201 | break; 202 | case 9: 203 | this->volumeChannelB = data; 204 | break; 205 | case 10: 206 | this->volumeChannelC = data; 207 | break; 208 | case 11: 209 | this->envelopeFineDuration = data; 210 | break; 211 | case 12: 212 | this->envelopeCoarseDuration = data; 213 | break; 214 | case 13: 215 | this->envelopeShape = data; 216 | break; 217 | case 14: 218 | this->ioPortA = data; 219 | break; 220 | default: 221 | // invalid register - do nothing 222 | return; 223 | } 224 | 225 | this->updated(); 226 | } 227 | 228 | uint8_t Ay3_8912_state::getRegisterData() 229 | { 230 | switch (this->selectedRegister) 231 | { 232 | case 0: 233 | return this->finePitchChannelA; 234 | case 1: 235 | return this->coarsePitchChannelA; 236 | case 2: 237 | return this->finePitchChannelB; 238 | case 3: 239 | return this->coarsePitchChannelB; 240 | case 4: 241 | return this->finePitchChannelC; 242 | case 5: 243 | return this->coarsePitchChannelC; 244 | case 6: 245 | return this->noisePitch; 246 | case 7: 247 | return this->mixer; 248 | case 8: 249 | return this->volumeChannelA; 250 | case 9: 251 | return this->volumeChannelB; 252 | case 10: 253 | return this->volumeChannelC; 254 | case 11: 255 | return this->envelopeFineDuration; 256 | case 12: 257 | return this->envelopeCoarseDuration; 258 | case 13: 259 | return this->envelopeShape; 260 | case 14: 261 | return this->ioPortA; 262 | default: 263 | return 0; 264 | } 265 | } 266 | 267 | } 268 | -------------------------------------------------------------------------------- /src/emulator.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "esp_log.h" 5 | 6 | #include "settings.h" 7 | #include "Emulator.h" 8 | #include "VideoController.h" 9 | #include "z80Environment.h" 10 | #include "ps2Input.h" 11 | #include "z80main.h" 12 | #include "FileSystem.h" 13 | #include "keyboard.h" 14 | #include "z80snapshot.h" 15 | #include "main_ROM.h" 16 | #include "ScreenArea.h" 17 | 18 | using namespace fabgl; 19 | 20 | // Temporary buffers 21 | uint8_t _buffer16K_1[0x4000]; 22 | uint8_t _buffer16K_2[0x4000]; 23 | 24 | // Screen 25 | static SpectrumScreenData _spectrumScreenData; 26 | static VideoController _screen(&_spectrumScreenData); 27 | VideoController* Screen = &_screen; 28 | ScreenArea HelpScreen(Screen, 29 | 3, SPECTRUM_WIDTH_WITH_BORDER - 3, 30 | SPECTRUM_HEIGHT_WITH_BORDER + 4, SCREEN_HEIGHT - (SPECTRUM_HEIGHT_WITH_BORDER + 7)); 31 | ScreenArea DebugScreen(Screen, 32 | SPECTRUM_WIDTH_WITH_BORDER + 2, SCREEN_WIDTH - (SPECTRUM_WIDTH_WITH_BORDER + 3), 33 | 1, SCREEN_HEIGHT - 2); 34 | 35 | // Z80State 36 | Z80Environment Environment(Screen); 37 | 38 | static PS2Controller* InputController; 39 | 40 | static bool _savingSnapshot = false; 41 | 42 | static void startKeyboard() 43 | { 44 | Mouse::quickCheckHardware(); 45 | InputController = new PS2Controller(); 46 | InputController->begin(PS2Preset::KeyboardPort0_MousePort1); 47 | Ps2_Initialize(InputController); 48 | } 49 | 50 | static void hideRegisters() 51 | { 52 | for (int y = 8; y < 12; y++) 53 | { 54 | HelpScreen.PrintAlignCenter(y, " "); 55 | } 56 | } 57 | 58 | static void showRegisters() 59 | { 60 | //DebugScreen.SetPrintAttribute(DEBUG_BAND_COLORS); 61 | //DebugScreen.Clear(); 62 | //showTitle("Registers. ESC - clear"); 63 | 64 | char* buf = (char*)_buffer16K_1; 65 | 66 | sprintf(buf, "PC %04x AF %04x AF' %04x I %02x", 67 | (uint16_t)Z80cpu.PC, (uint16_t)Z80cpu.AF, (uint16_t)Z80cpu.AFx, (uint16_t)Z80cpu.I); 68 | HelpScreen.PrintAlignCenter(8, buf); 69 | sprintf(buf, "SP %04x BC %04x BC' %04x R %02x", 70 | (uint16_t)Z80cpu.SP, (uint16_t)Z80cpu.BC, (uint16_t)Z80cpu.BCx, (uint16_t)Z80cpu.R); 71 | HelpScreen.PrintAlignCenter(9, buf); 72 | sprintf(buf, "IX %04x DE %04x DE' %04x IM %x", 73 | (uint16_t)Z80cpu.IX, (uint16_t)Z80cpu.DE, (uint16_t)Z80cpu.DEx, (uint16_t)Z80cpu.IM); 74 | HelpScreen.PrintAlignCenter(10, buf); 75 | sprintf(buf, "IY %04x HL %04x HL' %04x ", 76 | (uint16_t)Z80cpu.IY, (uint16_t)Z80cpu.HL, (uint16_t)Z80cpu.HLx); 77 | HelpScreen.PrintAlignCenter(11, buf); 78 | } 79 | 80 | void saveState() 81 | { 82 | _ay3_8912.StopSound(); 83 | Screen->ShowScreenshot(); 84 | Screen->SetMode(1); 85 | } 86 | 87 | static void showHelp() 88 | { 89 | HelpScreen.HideCursor(); 90 | HelpScreen.SetPrintAttribute(DEBUG_BAND_COLORS); 91 | HelpScreen.Clear(); 92 | 93 | int y = 0; 94 | HelpScreen.PrintAt(0, y++, "F1 - pause"); 95 | #ifdef SDCARD 96 | HelpScreen.PrintAt(0, y++, "F2 - save snapshot to SD card"); 97 | HelpScreen.PrintAt(0, y++, "F3 - load snapshot from SD card"); 98 | #else 99 | HelpScreen.PrintAt(0, y++, "F3 - load snapshot from flash"); 100 | #endif 101 | HelpScreen.PrintAt(0, y++, "F5 - reset"); 102 | HelpScreen.PrintAt(0, y++, "F10 - show keyboard layout"); 103 | } 104 | 105 | void restoreState() 106 | { 107 | Screen->SetMode(2); 108 | _ay3_8912.ResumeSound(); 109 | } 110 | 111 | static void showErrorMessage(const char* errorMessage) 112 | { 113 | DebugScreen.SetPrintAttribute(0x0310); // red on blue 114 | DebugScreen.PrintAlignCenter(2, errorMessage); 115 | DebugScreen.SetPrintAttribute(DEBUG_BAND_COLORS); 116 | } 117 | 118 | static bool ReadRomFromFiles() 119 | { 120 | if ((uint8_t*)*Environment.Rom[0] == (uint8_t*)ROM) 121 | { 122 | *Environment.Rom[0] = (uint8_t*)malloc(0x4000); 123 | #ifdef ZX128K 124 | *Environment.Rom[1] = (uint8_t*)malloc(0x4000); 125 | #endif 126 | } 127 | 128 | bool result; 129 | #ifdef ZX128K 130 | result = ReadFromFile("/roms/128-0.rom", (uint8_t*)*Environment.Rom[0], 0x4000); 131 | if (result) 132 | { 133 | result = ReadFromFile("/roms/128-1.rom", (uint8_t*)*Environment.Rom[1], 0x4000); 134 | } 135 | #else 136 | result = ReadFromFile("/roms/48.rom", (uint8_t*)*Environment.Rom[0], 0x4000); 137 | #endif 138 | 139 | if (!result) 140 | { 141 | free((uint8_t*)*Environment.Rom[0]); 142 | #ifdef ZX128K 143 | free((uint8_t*)*Environment.Rom[1]); 144 | #endif 145 | *Environment.Rom[0] = (uint8_t*)ROM; 146 | *Environment.Rom[1] = (uint8_t*)ROM; 147 | } 148 | 149 | return result; 150 | } 151 | 152 | static void ResetSystem() 153 | { 154 | if (*Environment.Rom[0] != (uint8_t*)ROM) 155 | { 156 | free((uint8_t*)*Environment.Rom[0]); 157 | #ifdef ZX128K 158 | free((uint8_t*)*Environment.Rom[1]); 159 | #endif 160 | *Environment.Rom[0] = (uint8_t*)ROM; 161 | *Environment.Rom[1] = (uint8_t*)ROM; 162 | } 163 | 164 | ReadRomFromFiles(); 165 | restoreState(); 166 | zx_reset(); 167 | } 168 | 169 | static void showKeyboardSetup() 170 | { 171 | Screen->ShowScreenshot(spectrumKeyboard, 0); 172 | Screen->SetMode(1); 173 | DebugScreen.Clear(); 174 | } 175 | 176 | void showTitle(const char* title) 177 | { 178 | DebugScreen.SetPrintAttribute(0x3F00); // white on black 179 | DebugScreen.PrintAlignCenter(0, title); 180 | DebugScreen.SetPrintAttribute(DEBUG_BAND_COLORS); 181 | } 182 | 183 | static bool processSpecialKey(int32_t scanCode) 184 | { 185 | switch (scanCode) 186 | { 187 | case KEY_F1: 188 | saveState(); 189 | showRegisters(); 190 | DebugScreen.Clear(); 191 | break; 192 | 193 | #ifdef SDCARD 194 | case KEY_F2: 195 | hideRegisters(); 196 | if (!saveSnapshotSetup("/")) 197 | { 198 | #ifdef SDCARD 199 | showErrorMessage("Cannot initialize SD card"); 200 | #else 201 | showErrorMessage("Cannot initialize flash file system"); 202 | #endif 203 | } 204 | else 205 | { 206 | saveState(); 207 | _savingSnapshot = true; 208 | } 209 | break; 210 | #endif 211 | 212 | case KEY_F3: 213 | hideRegisters(); 214 | if (!loadSnapshotSetup("/")) 215 | { 216 | #ifdef SDCARD 217 | showErrorMessage("Error when loading from SD card"); 218 | #else 219 | showErrorMessage("Error when loading from flash"); 220 | #endif 221 | } 222 | break; 223 | 224 | case KEY_F5: 225 | ResetSystem(); 226 | break; 227 | 228 | case KEY_F10: 229 | hideRegisters(); 230 | showKeyboardSetup(); 231 | break; 232 | 233 | default: 234 | return false; 235 | } 236 | 237 | return true; 238 | } 239 | 240 | static bool pausedLoop() 241 | { 242 | if (Screen->_mode == 2) 243 | { 244 | return false; 245 | } 246 | 247 | if (saveSnapshotLoop()) 248 | { 249 | return true; 250 | } 251 | 252 | if (loadSnapshotLoop()) 253 | { 254 | return true; 255 | } 256 | 257 | int32_t scanCode = Ps2_GetScancode(); 258 | if (scanCode == 0 || (scanCode & 0xFF00) != 0x00) 259 | { 260 | return true; 261 | } 262 | 263 | if (!processSpecialKey(scanCode)) 264 | { 265 | restoreState(); 266 | return false; 267 | } 268 | 269 | return true; 270 | } 271 | 272 | void EmulatorTaskMain(void *unused) 273 | { 274 | FileSystemInitialize(); 275 | 276 | // Setup 277 | startKeyboard(); 278 | zx_setup(&Environment); 279 | Environment.Initialize(); 280 | 281 | ReadRomFromFiles(); 282 | 283 | Screen->Start(RESOLUTION); 284 | 285 | showHelp(); 286 | 287 | uint32_t freeHeap32 = heap_caps_get_free_size(MALLOC_CAP_32BIT); 288 | uint32_t freeHeap8 = heap_caps_get_free_size(MALLOC_CAP_8BIT); 289 | ESP_LOGI(TAG, "Free heap 32BIT: %d, free heap 8BIT: %d", freeHeap32 - freeHeap8, freeHeap8); 290 | 291 | // Loop 292 | while (true) 293 | { 294 | vTaskDelay(1); // important to avoid task watchdog timeouts 295 | 296 | if (pausedLoop()) 297 | { 298 | continue; 299 | } 300 | 301 | int32_t result = zx_loop(); 302 | processSpecialKey(result); 303 | } 304 | } 305 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "freertos/FreeRTOS.h" 2 | #include "esp_log.h" 3 | #include "settings.h" 4 | #include "emulator.h" 5 | // #include 6 | // #include 7 | // #include "fabutils.h" 8 | 9 | extern "C" void app_main() 10 | { 11 | uint32_t freeHeap32 = heap_caps_get_free_size(MALLOC_CAP_32BIT); 12 | uint32_t freeHeap8 = heap_caps_get_free_size(MALLOC_CAP_8BIT); 13 | ESP_LOGI(TAG, "Free heap 32BIT: %d, free heap 8BIT: %d", freeHeap32 - freeHeap8, freeHeap8); 14 | 15 | EmulatorTaskMain(nullptr); 16 | } 17 | -------------------------------------------------------------------------------- /src/ps2Input.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "ps2Input.h" 5 | 6 | using namespace fabgl; 7 | 8 | static Keyboard* _keyboard; 9 | static Mouse* _mouse; 10 | 11 | static bool _isLeftShiftPressed; 12 | static bool _isRightShiftPressed; 13 | 14 | // Used to convert virtual key back to scan code 15 | static std::map _virtualKeyMap; 16 | 17 | void Ps2_Initialize(PS2Controller* inputController) 18 | { 19 | _keyboard = inputController->keyboard(); 20 | _mouse = inputController->mouse(); 21 | _mouse->setupAbsolutePositioner(0x100 << 1, 0x100 << 1, false); 22 | 23 | // Used to convert virtual key back to scan code 24 | const KeyboardLayout* layout = _keyboard->getLayout(); 25 | for (VirtualKeyDef keyDef: layout->scancodeToVK) 26 | { 27 | _virtualKeyMap[keyDef.virtualKey] = keyDef.scancode; 28 | } 29 | for (VirtualKeyDef keyDef: layout->exScancodeToVK) 30 | { 31 | _virtualKeyMap[keyDef.virtualKey] = 0xE000 | keyDef.scancode; 32 | } 33 | for (AltVirtualKeyDef keyDef: layout->alternateVK) 34 | { 35 | _virtualKeyMap[keyDef.virtualKey] = _virtualKeyMap[keyDef.reqVirtualKey]; 36 | } 37 | } 38 | 39 | bool Ps2_isMouseAvailable() 40 | { 41 | bool result = _mouse != nullptr && _mouse->isMouseAvailable(); 42 | if (result && _mouse->deltaAvailable()) 43 | { 44 | MouseDelta delta; 45 | _mouse->getNextDelta(&delta); 46 | _mouse->updateAbsolutePosition(&delta); 47 | } 48 | return result; 49 | } 50 | 51 | uint8_t Ps2_getMouseButtons() 52 | { 53 | uint8_t result = 0xFF; 54 | MouseButtons buttons = _mouse->status().buttons; 55 | result &= buttons.right ? 0xFE : 0xFF; 56 | result &= buttons.left ? 0xFD : 0xFF; 57 | result &= buttons.middle ? 0xFB : 0xFF; 58 | return result; 59 | } 60 | 61 | uint8_t Ps2_getMouseX() 62 | { 63 | return (uint8_t)(_mouse->status().X >> 1); 64 | } 65 | 66 | uint8_t Ps2_getMouseY() 67 | { 68 | return 0xFF - (uint8_t)(_mouse->status().Y >> 1); 69 | } 70 | 71 | int32_t Ps2_GetScancode() 72 | { 73 | if (!_keyboard->virtualKeyAvailable()) 74 | { 75 | return 0; 76 | } 77 | 78 | bool keyDown; 79 | VirtualKey virtualKey = _keyboard->getNextVirtualKey(&keyDown, 0); 80 | 81 | /* 82 | Serial.print("virtualKey = "); 83 | Serial.print((int)virtualKey); 84 | Serial.print(","); 85 | Serial.println((int)keyDown); 86 | */ 87 | 88 | auto pos = _virtualKeyMap.find(virtualKey); 89 | if (pos == _virtualKeyMap.end()) 90 | { 91 | return 0; 92 | } 93 | 94 | uint32_t result = pos->second; 95 | if (!keyDown) 96 | { 97 | result = ((result << 8) & 0xFF0000) | 0xF000 | (result & 0xFF); 98 | } 99 | 100 | return result; 101 | } 102 | 103 | char Ps2_ConvertScancode(int32_t scanCode) 104 | { 105 | char result; 106 | 107 | switch (scanCode) 108 | { 109 | case KEY_A: 110 | result = 'a'; 111 | break; 112 | case KEY_B: 113 | result = 'b'; 114 | break; 115 | case KEY_C: 116 | result = 'c'; 117 | break; 118 | case KEY_D: 119 | result = 'd'; 120 | break; 121 | case KEY_E: 122 | result = 'e'; 123 | break; 124 | case KEY_F: 125 | result = 'f'; 126 | break; 127 | case KEY_G: 128 | result = 'g'; 129 | break; 130 | case KEY_H: 131 | result = 'h'; 132 | break; 133 | case KEY_I: 134 | result = 'i'; 135 | break; 136 | case KEY_J: 137 | result = 'j'; 138 | break; 139 | case KEY_K: 140 | result = 'k'; 141 | break; 142 | case KEY_L: 143 | result = 'l'; 144 | break; 145 | case KEY_M: 146 | result = 'm'; 147 | break; 148 | case KEY_N: 149 | result = 'n'; 150 | break; 151 | case KEY_O: 152 | result = 'o'; 153 | break; 154 | case KEY_P: 155 | result = 'p'; 156 | break; 157 | case KEY_Q: 158 | result = 'q'; 159 | break; 160 | case KEY_R: 161 | result = 'r'; 162 | break; 163 | case KEY_S: 164 | result = 's'; 165 | break; 166 | case KEY_T: 167 | result = 't'; 168 | break; 169 | case KEY_U: 170 | result = 'u'; 171 | break; 172 | case KEY_V: 173 | result = 'v'; 174 | break; 175 | case KEY_W: 176 | result = 'w'; 177 | break; 178 | case KEY_X: 179 | result = 'x'; 180 | break; 181 | case KEY_Y: 182 | result = 'y'; 183 | break; 184 | case KEY_Z: 185 | result = 'z'; 186 | break; 187 | case KEY_0: 188 | result = '0'; 189 | break; 190 | case KEY_1: 191 | result = '1'; 192 | break; 193 | case KEY_2: 194 | result = '2'; 195 | break; 196 | case KEY_3: 197 | result = '3'; 198 | break; 199 | case KEY_4: 200 | result = '4'; 201 | break; 202 | case KEY_5: 203 | result = '5'; 204 | break; 205 | case KEY_6: 206 | result = '6'; 207 | break; 208 | case KEY_7: 209 | result = '7'; 210 | break; 211 | case KEY_8: 212 | result = '8'; 213 | break; 214 | case KEY_9: 215 | result = '9'; 216 | break; 217 | case KEY_BACKSPACE: 218 | result = '\b'; 219 | break; 220 | case KEY_SPACEBAR: 221 | result = ' '; 222 | break; 223 | case KEY_COMMA: 224 | result = ','; 225 | break; 226 | case KEY_MINUS: 227 | result = '-'; 228 | break; 229 | case KEY_DOT: 230 | result = '.'; 231 | break; 232 | case KEY_DIV: 233 | result = '/'; 234 | break; 235 | case KEY_SINGLE: 236 | result = '`'; 237 | break; 238 | case KEY_APOS: 239 | result = '\''; 240 | break; 241 | case KEY_SEMI: 242 | result = ';'; 243 | break; 244 | case KEY_BACK: 245 | result = '\\'; 246 | break; 247 | case KEY_OPEN_SQ: 248 | result = '['; 249 | break; 250 | case KEY_CLOSE_SQ: 251 | result = ']'; 252 | break; 253 | case KEY_EQUAL: 254 | result = '='; 255 | break; 256 | default: 257 | result = '\0'; 258 | break; 259 | } 260 | 261 | if (_isLeftShiftPressed || _isRightShiftPressed) 262 | { 263 | switch (scanCode) 264 | { 265 | case KEY_0: 266 | result = ')'; 267 | break; 268 | case KEY_1: 269 | result = '!'; 270 | break; 271 | case KEY_2: 272 | result = '@'; 273 | break; 274 | case KEY_3: 275 | result = '#'; 276 | break; 277 | case KEY_4: 278 | result = '$'; 279 | break; 280 | case KEY_5: 281 | result = '%'; 282 | break; 283 | case KEY_6: 284 | result = '^'; 285 | break; 286 | case KEY_7: 287 | result = '&'; 288 | break; 289 | case KEY_8: 290 | result = '*'; 291 | break; 292 | case KEY_9: 293 | result = '('; 294 | break; 295 | case KEY_COMMA: 296 | result = '<'; 297 | break; 298 | case KEY_MINUS: 299 | result = '_'; 300 | break; 301 | case KEY_DOT: 302 | result = '>'; 303 | break; 304 | case KEY_DIV: 305 | result = '?'; 306 | break; 307 | case KEY_SINGLE: 308 | result = '~'; 309 | break; 310 | case KEY_APOS: 311 | result = '"'; 312 | break; 313 | case KEY_SEMI: 314 | result = ':'; 315 | break; 316 | case KEY_BACK: 317 | result = '|'; 318 | break; 319 | case KEY_OPEN_SQ: 320 | result = '{'; 321 | break; 322 | case KEY_CLOSE_SQ: 323 | result = '}'; 324 | break; 325 | case KEY_EQUAL: 326 | result = '+'; 327 | break; 328 | default: 329 | result = toupper(result); 330 | break; 331 | } 332 | } 333 | 334 | return result; 335 | } 336 | -------------------------------------------------------------------------------- /src/volume.c: -------------------------------------------------------------------------------- 1 | #include "volume.h" 2 | 3 | const unsigned char volume[] = { 4 | 0, 8, 17, 25, 34, 42, 51, 59, 68, 76, 85, 93, 102, 110, 119, 127 5 | }; 6 | -------------------------------------------------------------------------------- /src/z80Emulator_AW.cpp: -------------------------------------------------------------------------------- 1 | // Z80 Emulator by Lin Ke-Fong 2 | 3 | #include "z80Emulator.h" 4 | 5 | #ifdef CPU_ANDREWEISSFLOG 6 | 7 | #include "z80_AW.h" 8 | 9 | static z80_t _zxCpu; 10 | static Z80Environment* env; 11 | 12 | extern "C" uint64_t cpu_tick(int num, uint64_t pins, void* user_data); 13 | 14 | void z80Emulator::setup(Z80Environment* environment) 15 | { 16 | env = environment; 17 | z80_desc_t init = { .tick_cb = cpu_tick, .user_data = nullptr }; 18 | z80_init(&_zxCpu, &init); 19 | } 20 | 21 | void z80Emulator::reset() 22 | { 23 | z80_reset(&_zxCpu); 24 | } 25 | 26 | int z80Emulator::emulate(int number_cycles) 27 | { 28 | return z80_exec(&_zxCpu, number_cycles); 29 | } 30 | 31 | void z80Emulator::interrupt() 32 | { 33 | _zxCpu.pins |= Z80_INT; 34 | } 35 | 36 | uint8_t z80Emulator::get_A() { return z80_a(&_zxCpu); } 37 | void z80Emulator::set_A(uint8_t value) { z80_set_a(&_zxCpu, value); } 38 | 39 | uint8_t z80Emulator::get_F() { return z80_f(&_zxCpu); } 40 | void z80Emulator::set_F(uint8_t value) { z80_set_f(&_zxCpu, value); } 41 | 42 | uint8_t z80Emulator::get_B() { return z80_b(&_zxCpu); } 43 | void z80Emulator::set_B(uint8_t value) { z80_set_b(&_zxCpu, value); } 44 | 45 | uint8_t z80Emulator::get_C() { return z80_c(&_zxCpu); } 46 | void z80Emulator::set_C(uint8_t value) { z80_set_c(&_zxCpu, value); } 47 | 48 | uint8_t z80Emulator::get_D() { return z80_d(&_zxCpu); } 49 | void z80Emulator::set_D(uint8_t value) { z80_set_d(&_zxCpu, value); } 50 | 51 | uint8_t z80Emulator::get_H() { return z80_h(&_zxCpu); } 52 | void z80Emulator::set_H(uint8_t value) { z80_set_h(&_zxCpu, value); } 53 | 54 | uint8_t z80Emulator::get_L() { return z80_l(&_zxCpu); } 55 | void z80Emulator::set_L(uint8_t value) { z80_set_l(&_zxCpu, value); } 56 | 57 | uint8_t z80Emulator::get_I() { return z80_i(&_zxCpu); } 58 | void z80Emulator::set_I(uint8_t value) { z80_set_i(&_zxCpu, value); } 59 | 60 | uint8_t z80Emulator::get_R() { return z80_r(&_zxCpu); } 61 | void z80Emulator::set_R(uint8_t value) { z80_set_r(&_zxCpu, value); } 62 | 63 | uint16_t z80Emulator::get_AF() { return z80_af(&_zxCpu); } 64 | void z80Emulator::set_AF(uint16_t value) { z80_set_af(&_zxCpu, value); } 65 | 66 | uint16_t z80Emulator::get_BC() { return z80_bc(&_zxCpu); } 67 | void z80Emulator::set_BC(uint16_t value) { z80_set_bc(&_zxCpu, value); } 68 | 69 | uint16_t z80Emulator::get_DE() { return z80_de(&_zxCpu); } 70 | void z80Emulator::set_DE(uint16_t value) { z80_set_de(&_zxCpu, value); } 71 | 72 | uint16_t z80Emulator::get_HL() { return z80_hl(&_zxCpu); } 73 | void z80Emulator::set_HL(uint16_t value) { z80_set_hl(&_zxCpu, value); } 74 | 75 | uint16_t z80Emulator::get_AFx() { return z80_af_(&_zxCpu); } 76 | void z80Emulator::set_AFx(uint16_t value) { z80_set_af_(&_zxCpu, value); } 77 | 78 | uint16_t z80Emulator::get_BCx() { return z80_bc_(&_zxCpu); } 79 | void z80Emulator::set_BCx(uint16_t value) { z80_set_bc_(&_zxCpu, value); } 80 | 81 | uint16_t z80Emulator::get_DEx() { return z80_de_(&_zxCpu); } 82 | void z80Emulator::set_DEx(uint16_t value) { z80_set_de_(&_zxCpu, value); } 83 | 84 | uint16_t z80Emulator::get_HLx() { return z80_hl_(&_zxCpu); } 85 | void z80Emulator::set_HLx(uint16_t value) { z80_set_hl_(&_zxCpu, value); } 86 | 87 | uint16_t z80Emulator::get_IX() { return z80_ix(&_zxCpu); } 88 | void z80Emulator::set_IX(uint16_t value) { z80_set_ix(&_zxCpu, value); } 89 | 90 | uint16_t z80Emulator::get_IY() { return z80_iy(&_zxCpu); } 91 | void z80Emulator::set_IY(uint16_t value) { z80_set_iy(&_zxCpu, value); } 92 | 93 | uint16_t z80Emulator::get_SP() { return z80_sp(&_zxCpu); } 94 | void z80Emulator::set_SP(uint16_t value) {z80_set_sp(&_zxCpu, value); } 95 | 96 | uint16_t z80Emulator::get_PC() { return z80_pc(&_zxCpu); } 97 | void z80Emulator::set_PC(uint16_t value) { z80_set_pc(&_zxCpu, value); } 98 | 99 | uint8_t z80Emulator::get_IFF1() { return z80_iff1(&_zxCpu); } 100 | void z80Emulator::set_IFF1(uint8_t value) { z80_set_iff1(&_zxCpu, value); } 101 | 102 | uint8_t z80Emulator::get_IFF2() { return z80_iff2(&_zxCpu); } 103 | void z80Emulator::set_IFF2(uint8_t value) { z80_set_iff2(&_zxCpu, value); } 104 | 105 | uint8_t z80Emulator::get_IM() { return z80_im(&_zxCpu); } 106 | void z80Emulator::set_IM(uint8_t value) { z80_set_im(&_zxCpu, value); } 107 | 108 | extern "C" uint64_t cpu_tick(int num, uint64_t pins, void* user_data) 109 | { 110 | if (pins & Z80_MREQ) 111 | { 112 | if (pins & Z80_RD) 113 | { 114 | Z80_SET_DATA(pins, env->ReadByte(Z80_GET_ADDR(pins))); 115 | } 116 | else if (pins & Z80_WR) 117 | { 118 | env->WriteByte(Z80_GET_ADDR(pins), Z80_GET_DATA(pins)); 119 | } 120 | } 121 | else if (pins & Z80_IORQ) 122 | { 123 | if (pins & Z80_RD) 124 | { 125 | uint16_t port = Z80_GET_ADDR(pins); 126 | Z80_SET_DATA(pins, env->Input(port & 0xFF, port >> 8)); 127 | } 128 | else if (pins & Z80_WR) 129 | { 130 | uint16_t port = Z80_GET_ADDR(pins); 131 | env->Output(port & 0xFF, port >> 8, Z80_GET_DATA(pins)); 132 | } 133 | } 134 | return pins; 135 | } 136 | 137 | #endif -------------------------------------------------------------------------------- /src/z80Emulator_JLS.cpp: -------------------------------------------------------------------------------- 1 | // Z80 Emulator by Lin Ke-Fong 2 | 3 | #include "z80Emulator.h" 4 | 5 | #ifdef CPU_JLSANCHEZ 6 | 7 | #include "z80.h" 8 | #include "z80operations.h" 9 | 10 | class Operations : public Z80operations 11 | { 12 | private: 13 | // Delay Contention: for emulating CPU slowing due to sharing bus with ULA 14 | // NOTE: Only 48K spectrum contention implemented. This function must be called 15 | // only when dealing with affected memory (use ADDRESS_IN_LOW_RAM macro) 16 | static uint8_t delayContention(uint32_t currentTstates); 17 | 18 | public: 19 | Z80Environment* _environment; 20 | 21 | uint8_t fetchOpcode(uint16_t address) override; 22 | uint8_t peek8(uint16_t address) override; 23 | void poke8(uint16_t address, uint8_t value) override; 24 | uint16_t peek16(uint16_t adddress) override; 25 | void poke16(uint16_t address, RegisterPair word) override; 26 | uint8_t inPort(uint16_t port) override; 27 | void outPort(uint16_t port, uint8_t value) override; 28 | void addressOnBus(uint16_t address, int32_t wstates) override; 29 | void interruptHandlingTime(int32_t wstates) override; 30 | bool isActiveINT(void) override; 31 | }; 32 | 33 | static Operations z80Operations; 34 | static Z80 z80(&z80Operations); 35 | 36 | static bool interruptPending = false; 37 | 38 | void z80Emulator::setup(Z80Environment* environment) 39 | { 40 | z80Operations._environment = environment; 41 | } 42 | 43 | void z80Emulator::reset() 44 | { 45 | z80.reset(); 46 | } 47 | 48 | int z80Emulator::emulate(int number_cycles) 49 | { 50 | z80Operations._environment->TStates = 0; 51 | while (z80Operations._environment->TStates < number_cycles) 52 | { 53 | z80.execute(); 54 | } 55 | 56 | return number_cycles; 57 | } 58 | 59 | void z80Emulator::interrupt() 60 | { 61 | interruptPending = true; 62 | } 63 | 64 | uint8_t z80Emulator::get_A() { return z80.getRegA(); } 65 | void z80Emulator::set_A(uint8_t value) { z80.setRegA(value); } 66 | 67 | uint8_t z80Emulator::get_F() { return z80.getFlags(); } 68 | void z80Emulator::set_F(uint8_t value) { z80.setFlags(value); } 69 | 70 | uint8_t z80Emulator::get_B() { return z80.getRegB(); } 71 | void z80Emulator::set_B(uint8_t value) { z80.setRegB(value); } 72 | 73 | uint8_t z80Emulator::get_C() { return z80.getRegC(); } 74 | void z80Emulator::set_C(uint8_t value) { z80.setRegC(value); } 75 | 76 | uint8_t z80Emulator::get_D() { return z80.getRegD(); } 77 | void z80Emulator::set_D(uint8_t value) { z80.setRegD(value); } 78 | 79 | uint8_t z80Emulator::get_H() { return z80.getRegH(); } 80 | void z80Emulator::set_H(uint8_t value) { z80.setRegH(value); } 81 | 82 | uint8_t z80Emulator::get_L() { return z80.getRegL(); } 83 | void z80Emulator::set_L(uint8_t value) { z80.setRegL(value); } 84 | 85 | uint8_t z80Emulator::get_I() { return z80.getRegI(); } 86 | void z80Emulator::set_I(uint8_t value) { z80.setRegI(value); } 87 | 88 | uint8_t z80Emulator::get_R() { return z80.getRegR(); } 89 | void z80Emulator::set_R(uint8_t value) { z80.setRegR(value); } 90 | 91 | uint16_t z80Emulator::get_AF() { return z80.getRegAF(); } 92 | void z80Emulator::set_AF(uint16_t value) { z80.setRegAF(value); } 93 | 94 | uint16_t z80Emulator::get_BC() { return z80.getRegBC(); } 95 | void z80Emulator::set_BC(uint16_t value) { z80.setRegBC(value); } 96 | 97 | uint16_t z80Emulator::get_DE() { return z80.getRegDE(); } 98 | void z80Emulator::set_DE(uint16_t value) { z80.setRegDE(value); } 99 | 100 | uint16_t z80Emulator::get_HL() { return z80.getRegHL(); } 101 | void z80Emulator::set_HL(uint16_t value) { z80.setRegHL(value); } 102 | 103 | uint16_t z80Emulator::get_AFx() { return z80.getRegAFx(); } 104 | void z80Emulator::set_AFx(uint16_t value) { z80.setRegAFx(value); } 105 | 106 | uint16_t z80Emulator::get_BCx() { return z80.getRegBCx(); } 107 | void z80Emulator::set_BCx(uint16_t value) { z80.setRegBCx(value); } 108 | 109 | uint16_t z80Emulator::get_DEx() { return z80.getRegDEx(); } 110 | void z80Emulator::set_DEx(uint16_t value) { z80.setRegDEx(value); } 111 | 112 | uint16_t z80Emulator::get_HLx() { return z80.getRegHLx(); } 113 | void z80Emulator::set_HLx(uint16_t value) { z80.setRegHLx(value); } 114 | 115 | uint16_t z80Emulator::get_IX() { return z80.getRegIX(); } 116 | void z80Emulator::set_IX(uint16_t value) { z80.setRegIX(value); } 117 | 118 | uint16_t z80Emulator::get_IY() { return z80.getRegIY(); } 119 | void z80Emulator::set_IY(uint16_t value) { z80.setRegIY(value); } 120 | 121 | uint16_t z80Emulator::get_SP() { return z80.getRegSP(); } 122 | void z80Emulator::set_SP(uint16_t value) { z80.setRegSP(value); } 123 | 124 | uint16_t z80Emulator::get_PC() { return z80.getRegPC(); } 125 | void z80Emulator::set_PC(uint16_t value) { z80.setRegPC(value); } 126 | 127 | uint8_t z80Emulator::get_IFF1() { return z80.isIFF1() ? 1 : 0; } 128 | void z80Emulator::set_IFF1(uint8_t value) { z80.setIFF1(value == 1); } 129 | 130 | uint8_t z80Emulator::get_IFF2() { return z80.isIFF2() ? 1 : 0; } 131 | void z80Emulator::set_IFF2(uint8_t value) { z80.setIFF2(value == 1); } 132 | 133 | uint8_t z80Emulator::get_IM() { return (uint8_t)z80.getIM(); } 134 | void z80Emulator::set_IM(uint8_t value) { z80.setIM((Z80::IntMode)value); } 135 | 136 | 137 | #define ADDRESS_IN_LOW_RAM(addr) (1 == (addr >> 14)) 138 | 139 | /////////////////////////////////////////////////////////////////////////////// 140 | // 141 | // delay contention: emulates wait states introduced by the ULA (graphic chip) 142 | // whenever there is a memory access to contended memory (shared between ULA and CPU). 143 | // detailed info: https://worldofspectrum.org/faq/reference/48kreference.htm#ZXSpectrum 144 | // from paragraph which starts with "The 50 Hz interrupt is synchronized with..." 145 | // if you only read from https://worldofspectrum.org/faq/reference/48kreference.htm#Contention 146 | // without reading the previous paragraphs about line timings, it may be confusing. 147 | // 148 | inline uint8_t Operations::delayContention(uint32_t currentTstates) 149 | { 150 | // sequence of wait states 151 | static uint8_t wait_states[8] = { 6, 5, 4, 3, 2, 1, 0, 0 }; 152 | 153 | // delay states one t-state BEFORE the first pixel to be drawn 154 | currentTstates += 1; 155 | 156 | // each line spans 224 t-states 157 | int line = currentTstates / 224; 158 | 159 | // only the 192 lines between 64 and 255 have graphic data, the rest is border 160 | if (line < 64 || line >= 256) return 0; 161 | 162 | // only the first 128 t-states of each line correspond to a graphic data transfer 163 | // the remaining 96 t-states correspond to border 164 | int halfpix = currentTstates % 224; 165 | if (halfpix >= 128) return 0; 166 | 167 | int modulo = halfpix % 8; 168 | return wait_states[modulo]; 169 | } 170 | 171 | /* Read opcode from RAM */ 172 | uint8_t Operations::fetchOpcode(uint16_t address) { 173 | // 3 clocks to fetch opcode from RAM and 1 execution clock 174 | if (ADDRESS_IN_LOW_RAM(address)) 175 | { 176 | this->_environment->TStates += Operations::delayContention(this->_environment->TStates); 177 | } 178 | 179 | this->_environment->TStates += 4; 180 | return this->_environment->ReadByte(address); 181 | } 182 | 183 | /* Read/Write byte from/to RAM */ 184 | uint8_t Operations::peek8(uint16_t address) { 185 | // 3 clocks for read byte from RAM 186 | if (ADDRESS_IN_LOW_RAM(address)) 187 | { 188 | this->_environment->TStates += Operations::delayContention(this->_environment->TStates); 189 | } 190 | 191 | this->_environment->TStates += 3; 192 | return this->_environment->ReadByte(address); 193 | } 194 | void Operations::poke8(uint16_t address, uint8_t value) { 195 | // 3 clocks for write byte to RAM 196 | if (ADDRESS_IN_LOW_RAM(address)) 197 | { 198 | this->_environment->TStates += Operations::delayContention(this->_environment->TStates); 199 | } 200 | 201 | this->_environment->TStates += 3; 202 | this->_environment->WriteByte(address, value); 203 | } 204 | 205 | /* Read/Write word from/to RAM */ 206 | uint16_t Operations::peek16(uint16_t address) { 207 | // Order matters, first read lsb, then read msb, don't "optimize" 208 | uint8_t lsb = this->peek8(address); 209 | uint8_t msb = this->peek8(address + 1); 210 | return (msb << 8) | lsb; 211 | } 212 | void Operations::poke16(uint16_t address, RegisterPair word) { 213 | // Order matters, first write lsb, then write msb, don't "optimize" 214 | this->poke8(address, word.byte8.lo); 215 | this->poke8(address + 1, word.byte8.hi); 216 | } 217 | 218 | /* In/Out byte from/to IO Bus */ 219 | uint8_t Operations::inPort(uint16_t port) { 220 | // 3 clocks for read byte from bus 221 | this->_environment->TStates += 3; 222 | uint8_t hiport = port >> 8; 223 | uint8_t loport = port & 0xFF; 224 | return this->_environment->Input(loport, hiport); 225 | } 226 | void Operations::outPort(uint16_t port, uint8_t value) { 227 | // 4 clocks for write byte to bus 228 | this->_environment->TStates += 4; 229 | uint8_t hiport = port >> 8; 230 | uint8_t loport = port & 0xFF; 231 | this->_environment->Output(loport, hiport, value); 232 | } 233 | 234 | /* Put an address on bus lasting 'tstates' cycles */ 235 | void Operations::addressOnBus(uint16_t address, int32_t wstates){ 236 | // Additional clocks to be added on some instructions 237 | if (ADDRESS_IN_LOW_RAM(address)) 238 | { 239 | for (int idx = 0; idx < wstates; idx++) 240 | { 241 | this->_environment->TStates += Operations::delayContention(this->_environment->TStates) + 1; 242 | } 243 | } 244 | else 245 | { 246 | this->_environment->TStates += wstates; 247 | } 248 | } 249 | 250 | /* Clocks needed for processing INT and NMI */ 251 | void Operations::interruptHandlingTime(int32_t wstates) 252 | { 253 | this->_environment->TStates += wstates; 254 | } 255 | 256 | /* Callback to know when the INT signal is active */ 257 | bool Operations::isActiveINT(void) 258 | { 259 | if (!interruptPending) return false; 260 | interruptPending = false; 261 | return true; 262 | } 263 | 264 | #endif -------------------------------------------------------------------------------- /src/z80Emulator_LKF.cpp: -------------------------------------------------------------------------------- 1 | // Z80 Emulator by Lin Ke-Fong 2 | 3 | #include "z80Emulator.h" 4 | 5 | #ifdef CPU_LINKEFONG 6 | 7 | #include "z80emu.h" 8 | #include "z80user.h" 9 | 10 | static Z80_STATE z80_state; 11 | static Z80_STATE* state = &z80_state; 12 | static CONTEXT context; 13 | static Z80Environment* env; 14 | 15 | extern "C" 16 | { 17 | uint8_t readbyte(uint16_t addr); 18 | uint16_t readword(uint16_t addr); 19 | void writebyte(uint16_t addr, uint8_t data); 20 | void writeword(uint16_t addr, uint16_t data); 21 | uint8_t input(uint8_t portLow, uint8_t portHigh); 22 | void output(uint8_t portLow, uint8_t portHigh, uint8_t data); 23 | } 24 | 25 | void z80Emulator::setup(Z80Environment* environment) 26 | { 27 | env = environment; 28 | context.readbyte = readbyte; 29 | context.readword = readword; 30 | context.writeword = writeword; 31 | context.writebyte = writebyte; 32 | context.input = input; 33 | context.output = output; 34 | } 35 | 36 | void z80Emulator::reset() 37 | { 38 | Z80Reset(state); 39 | } 40 | 41 | int z80Emulator::emulate(int number_cycles) 42 | { 43 | return Z80Emulate(state, number_cycles, &context); 44 | } 45 | 46 | void z80Emulator::interrupt() 47 | { 48 | Z80Interrupt(state, 0xff, &context); 49 | } 50 | 51 | uint8_t z80Emulator::get_A() { return state->registers.byte[Z80_A]; } 52 | void z80Emulator::set_A(uint8_t value) { state->registers.byte[Z80_A] = value; } 53 | 54 | uint8_t z80Emulator::get_F() { return state->registers.byte[Z80_F]; } 55 | void z80Emulator::set_F(uint8_t value) { state->registers.byte[Z80_F] = value; } 56 | 57 | uint8_t z80Emulator::get_B() { return state->registers.byte[Z80_B]; } 58 | void z80Emulator::set_B(uint8_t value) { state->registers.byte[Z80_B] = value; } 59 | 60 | uint8_t z80Emulator::get_C() { return state->registers.byte[Z80_C]; } 61 | void z80Emulator::set_C(uint8_t value) { state->registers.byte[Z80_C] = value; } 62 | 63 | uint8_t z80Emulator::get_D() { return state->registers.byte[Z80_D]; } 64 | void z80Emulator::set_D(uint8_t value) { state->registers.byte[Z80_D] = value; } 65 | 66 | uint8_t z80Emulator::get_H() { return state->registers.byte[Z80_H]; } 67 | void z80Emulator::set_H(uint8_t value) { state->registers.byte[Z80_H] = value; } 68 | 69 | uint8_t z80Emulator::get_L() { return state->registers.byte[Z80_L]; } 70 | void z80Emulator::set_L(uint8_t value) { state->registers.byte[Z80_L] = value; } 71 | 72 | uint8_t z80Emulator::get_I() { return (uint8_t)state->i; } 73 | void z80Emulator::set_I(uint8_t value) { state->i = value; } 74 | 75 | uint8_t z80Emulator::get_R() { return (uint8_t)state->r; } 76 | void z80Emulator::set_R(uint8_t value) { state->r = value; } 77 | 78 | uint16_t z80Emulator::get_AF() { return state->registers.word[Z80_AF]; } 79 | void z80Emulator::set_AF(uint16_t value) { state->registers.word[Z80_AF] = value; } 80 | 81 | uint16_t z80Emulator::get_BC() { return state->registers.word[Z80_BC]; } 82 | void z80Emulator::set_BC(uint16_t value) { state->registers.word[Z80_BC] = value; } 83 | 84 | uint16_t z80Emulator::get_DE() { return state->registers.word[Z80_DE]; } 85 | void z80Emulator::set_DE(uint16_t value) { state->registers.word[Z80_DE] = value; } 86 | 87 | uint16_t z80Emulator::get_HL() { return state->registers.word[Z80_HL]; } 88 | void z80Emulator::set_HL(uint16_t value) { state->registers.word[Z80_HL] = value; } 89 | 90 | uint16_t z80Emulator::get_AFx() { return state->alternates[Z80_AF]; } 91 | void z80Emulator::set_AFx(uint16_t value) { state->alternates[Z80_AF] = value; } 92 | 93 | uint16_t z80Emulator::get_BCx() { return state->alternates[Z80_BC]; } 94 | void z80Emulator::set_BCx(uint16_t value) { state->alternates[Z80_BC] = value; } 95 | 96 | uint16_t z80Emulator::get_DEx() { return state->alternates[Z80_DE]; } 97 | void z80Emulator::set_DEx(uint16_t value) { state->alternates[Z80_DE] = value; } 98 | 99 | uint16_t z80Emulator::get_HLx() { return state->alternates[Z80_HL]; } 100 | void z80Emulator::set_HLx(uint16_t value) { state->alternates[Z80_HL] = value; } 101 | 102 | uint16_t z80Emulator::get_IX() { return state->registers.word[Z80_IX]; } 103 | void z80Emulator::set_IX(uint16_t value) { state->registers.word[Z80_IX] = value; } 104 | 105 | uint16_t z80Emulator::get_IY() { return state->registers.word[Z80_IY]; } 106 | void z80Emulator::set_IY(uint16_t value) { state->registers.word[Z80_IY] = value; } 107 | 108 | uint16_t z80Emulator::get_SP() { return state->registers.word[Z80_SP]; } 109 | void z80Emulator::set_SP(uint16_t value) { state->registers.word[Z80_SP] = value; } 110 | 111 | uint16_t z80Emulator::get_PC() { return (uint16_t)state->pc; } 112 | void z80Emulator::set_PC(uint16_t value) { state->pc = value; } 113 | 114 | uint8_t z80Emulator::get_IFF1() { return (uint8_t)state->iff1; } 115 | void z80Emulator::set_IFF1(uint8_t value) { state->iff1 = value; } 116 | 117 | uint8_t z80Emulator::get_IFF2() { return (uint8_t)state->iff2; } 118 | void z80Emulator::set_IFF2(uint8_t value) { state->iff2 = value; } 119 | 120 | uint8_t z80Emulator::get_IM() { return (uint8_t)state->im; } 121 | void z80Emulator::set_IM(uint8_t value) { state->im = value; } 122 | 123 | extern "C" uint8_t readbyte(uint16_t addr) 124 | { 125 | return env->ReadByte(addr); 126 | } 127 | 128 | extern "C" uint16_t readword(uint16_t addr) 129 | { 130 | return env->ReadWord(addr); 131 | } 132 | 133 | extern "C" void writebyte(uint16_t addr, uint8_t data) 134 | { 135 | env->WriteByte(addr, data); 136 | } 137 | 138 | extern "C" void writeword(uint16_t addr, uint16_t data) 139 | { 140 | env->WriteWord(addr, data); 141 | } 142 | 143 | extern "C" uint8_t input(uint8_t portLow, uint8_t portHigh) 144 | { 145 | return env->Input(portLow, portHigh); 146 | } 147 | 148 | extern "C" void output(uint8_t portLow, uint8_t portHigh, uint8_t data) 149 | { 150 | env->Output(portLow, portHigh, data); 151 | } 152 | 153 | #endif -------------------------------------------------------------------------------- /src/z80Emulator_ZEL.cpp: -------------------------------------------------------------------------------- 1 | // Z80 Emulator by Steve Checkoway 2 | 3 | #include "z80Emulator.h" 4 | 5 | #ifdef CPU_STEVECHECKOWAY 6 | 7 | #include "zel/z80_instructions.h" 8 | #include "zel/z80.h" 9 | #include "z80_types.h" 10 | 11 | static Z80 _zxCpu; 12 | static Z80Environment* env; 13 | 14 | extern "C" uint8_t ReadMem(uint16_t addr, bool inst, Z80 cpu); 15 | extern "C" void WriteMem(uint16_t addr, uint8_t val, Z80 cpu); 16 | extern "C" uint8_t ReadInterruptData(uint16_t n, Z80 cpu); 17 | extern "C" uint8_t ReadIO(uint16_t addr, Z80 cpu); 18 | extern "C" void WriteIO(uint16_t addr, uint8_t val, Z80 cpu); 19 | extern "C" void InterruptComplete(Z80 cpu); 20 | 21 | void z80Emulator::setup(Z80Environment* environment) 22 | { 23 | env = environment; 24 | 25 | Z80FunctionBlock functionBlock; 26 | functionBlock.ReadMem = ReadMem; 27 | functionBlock.WriteMem = WriteMem; 28 | functionBlock.ReadInterruptData = ReadInterruptData; 29 | functionBlock.ReadIO = ReadIO; 30 | functionBlock.WriteIO = WriteIO; 31 | functionBlock.InterruptComplete = InterruptComplete; 32 | functionBlock.ControlFlow = nullptr; 33 | 34 | _zxCpu = Z80_New(&functionBlock); 35 | } 36 | 37 | void z80Emulator::reset() 38 | { 39 | // set AF to 0xFFFF, all other regs are undefined 40 | Z80_SetReg(REG_AF, 0xFFFF, _zxCpu); 41 | 42 | // set SP to 0xFFFF, PC to 0x0000 43 | Z80_SetReg(REG_SP, 0xFFFF, _zxCpu); 44 | Z80_SetReg(REG_PC, 0x0000, _zxCpu); 45 | 46 | // IFF1 and IFF2 are off 47 | _zxCpu->iff1 = false; 48 | _zxCpu->iff2 = false; 49 | 50 | // IM is set to 0 51 | _zxCpu->interrupt_mode = 0; 52 | 53 | // after power-on or reset, R is set to 0 54 | this->set_R(0); 55 | 56 | Z80_ClearHalt(_zxCpu); 57 | } 58 | 59 | int z80Emulator::emulate(int number_cycles) 60 | { 61 | int cycles = 0; 62 | while (cycles < number_cycles) 63 | { 64 | cycles += Z80_Step(nullptr, _zxCpu); 65 | } 66 | 67 | return cycles; 68 | } 69 | 70 | void z80Emulator::interrupt() 71 | { 72 | Z80_RaiseInterrupt(_zxCpu); 73 | } 74 | 75 | uint8_t z80Emulator::get_A() { return _zxCpu->byte_reg[REG_A]; } 76 | void z80Emulator::set_A(uint8_t value) { _zxCpu->byte_reg[REG_A] = value; } 77 | 78 | uint8_t z80Emulator::get_F() { return _zxCpu->byte_reg[REG_F]; } 79 | void z80Emulator::set_F(uint8_t value) { _zxCpu->byte_reg[REG_F] = value; } 80 | 81 | uint8_t z80Emulator::get_B() { return _zxCpu->byte_reg[REG_B]; } 82 | void z80Emulator::set_B(uint8_t value) { _zxCpu->byte_reg[REG_B] = value; } 83 | 84 | uint8_t z80Emulator::get_C() { return _zxCpu->byte_reg[REG_C]; } 85 | void z80Emulator::set_C(uint8_t value) { _zxCpu->byte_reg[REG_C] = value; } 86 | 87 | uint8_t z80Emulator::get_D() { return _zxCpu->byte_reg[REG_D]; } 88 | void z80Emulator::set_D(uint8_t value) { _zxCpu->byte_reg[REG_D] = value; } 89 | 90 | uint8_t z80Emulator::get_H() { return _zxCpu->byte_reg[REG_H]; } 91 | void z80Emulator::set_H(uint8_t value) { _zxCpu->byte_reg[REG_H] = value; } 92 | 93 | uint8_t z80Emulator::get_L() { return _zxCpu->byte_reg[REG_L]; } 94 | void z80Emulator::set_L(uint8_t value) { _zxCpu->byte_reg[REG_L] = value; } 95 | 96 | uint8_t z80Emulator::get_I() { return _zxCpu->byte_reg[REG_I]; } 97 | void z80Emulator::set_I(uint8_t value) { _zxCpu->byte_reg[REG_I] = value; } 98 | 99 | uint8_t z80Emulator::get_R() { return _zxCpu->byte_reg[REG_R]; } 100 | void z80Emulator::set_R(uint8_t value) { _zxCpu->byte_reg[REG_R] = value; } 101 | 102 | uint16_t z80Emulator::get_AF() { return Z80_GetReg(REG_AF, _zxCpu); } 103 | void z80Emulator::set_AF(uint16_t value) { Z80_SetReg(REG_AF, value, _zxCpu); } 104 | 105 | uint16_t z80Emulator::get_BC() { return Z80_GetReg(REG_BC, _zxCpu); } 106 | void z80Emulator::set_BC(uint16_t value) { Z80_SetReg(REG_BC, value, _zxCpu); } 107 | 108 | uint16_t z80Emulator::get_DE() { return Z80_GetReg(REG_DE, _zxCpu); } 109 | void z80Emulator::set_DE(uint16_t value) { Z80_SetReg(REG_DE, value, _zxCpu); } 110 | 111 | uint16_t z80Emulator::get_HL() { return Z80_GetReg(REG_HL, _zxCpu);; } 112 | void z80Emulator::set_HL(uint16_t value) { Z80_SetReg(REG_HL, value, _zxCpu); } 113 | 114 | uint16_t z80Emulator::get_AFx() { return Z80_GetReg(REG_AFP, _zxCpu); } 115 | void z80Emulator::set_AFx(uint16_t value) { Z80_SetReg(REG_AFP, value, _zxCpu); } 116 | 117 | uint16_t z80Emulator::get_BCx() { return Z80_GetReg(REG_BCP, _zxCpu); } 118 | void z80Emulator::set_BCx(uint16_t value) { Z80_SetReg(REG_BCP, value, _zxCpu); } 119 | 120 | uint16_t z80Emulator::get_DEx() { return Z80_GetReg(REG_DEP, _zxCpu);; } 121 | void z80Emulator::set_DEx(uint16_t value) { Z80_SetReg(REG_DEP, value, _zxCpu); } 122 | 123 | uint16_t z80Emulator::get_HLx() { return Z80_GetReg(REG_HLP, _zxCpu); } 124 | void z80Emulator::set_HLx(uint16_t value) { Z80_SetReg(REG_HLP, value, _zxCpu); } 125 | 126 | uint16_t z80Emulator::get_IX() { return Z80_GetReg(REG_IX, _zxCpu);; } 127 | void z80Emulator::set_IX(uint16_t value) { Z80_SetReg(REG_IX, value, _zxCpu); } 128 | 129 | uint16_t z80Emulator::get_IY() { return Z80_GetReg(REG_IY, _zxCpu);; } 130 | void z80Emulator::set_IY(uint16_t value) { Z80_SetReg(REG_IY, value, _zxCpu); } 131 | 132 | uint16_t z80Emulator::get_SP() { return Z80_GetReg(REG_SP, _zxCpu); } 133 | void z80Emulator::set_SP(uint16_t value) { Z80_SetReg(REG_SP, value, _zxCpu); } 134 | 135 | uint16_t z80Emulator::get_PC() { return Z80_GetReg(REG_PC, _zxCpu); } 136 | void z80Emulator::set_PC(uint16_t value) { Z80_SetReg(REG_PC, value, _zxCpu); } 137 | 138 | uint8_t z80Emulator::get_IFF1() { return _zxCpu->iff1 ? 1 : 0; } 139 | void z80Emulator::set_IFF1(uint8_t value) { _zxCpu->iff1 = (value == 1); } 140 | 141 | uint8_t z80Emulator::get_IFF2() { return _zxCpu->iff2 ? 1 : 0; } 142 | void z80Emulator::set_IFF2(uint8_t value) { _zxCpu->iff2 = (value == 1); } 143 | 144 | uint8_t z80Emulator::get_IM() { return (uint8_t)_zxCpu->interrupt_mode; } 145 | void z80Emulator::set_IM(uint8_t value) { _zxCpu->interrupt_mode = value; } 146 | 147 | 148 | /*! Read a byte of memory. 149 | * \param addr The address to read. 150 | * \param inst True if the z80 is reading instructions. 151 | * \param cpu The \c Z80 instance making the read call. 152 | * \return The byte from memory. 153 | */ 154 | extern "C" uint8_t ReadMem(uint16_t addr, bool inst, Z80 cpu) 155 | { 156 | return env->ReadByte(addr); 157 | } 158 | 159 | /*! Write a byte of memory. 160 | * \param addr The address to write. 161 | * \param val The byte to write. 162 | * \param cpu The \c Z80 instance making the write call. 163 | */ 164 | extern "C" void WriteMem(uint16_t addr, uint8_t val, Z80 cpu) 165 | { 166 | env->WriteByte(addr, val); 167 | } 168 | 169 | /*! Read the interrupt data. 170 | * \param n Read the \a n th byte of data. 171 | * \param cpu The \c Z80 instance making the read call. 172 | */ 173 | extern "C" uint8_t ReadInterruptData(uint16_t n, Z80 cpu) 174 | { 175 | return 0xFF; 176 | } 177 | 178 | /*! Read a byte from an I/O port. 179 | * \param addr The contents of the address bus during the 180 | * request. The low 8 bits specify the port. 181 | * \param cpu The \c Z80 instance making the read call. 182 | * \return The byte from the I/O port. 183 | */ 184 | extern "C" uint8_t ReadIO(uint16_t addr, Z80 cpu) 185 | { 186 | return env->Input(addr &0xFF, addr >> 8); 187 | } 188 | 189 | /*! Write a byte from an I/O port. 190 | * \param addr The contents of the address bus during the 191 | * request. The low 8 bits specify the port. 192 | * \param val The byte to write. 193 | * \param cpu The \c Z80 instance making the read call. 194 | */ 195 | extern "C" void WriteIO(uint16_t addr, uint8_t val, Z80 cpu) 196 | { 197 | env->Output(addr &0xFF, addr >> 8, val); 198 | } 199 | 200 | /*! Notify the peripherials that a return from interrupt 201 | * instruction has occured. 202 | * \param cpu The \c Z80 instance performing the notification. 203 | */ 204 | extern "C" void InterruptComplete(Z80 cpu) 205 | { 206 | 207 | } 208 | 209 | #endif -------------------------------------------------------------------------------- /src/z80Environment.cpp: -------------------------------------------------------------------------------- 1 | #include "esp_log.h" 2 | #include "soc/rtc_io_reg.h" 3 | #include "fabgl.h" 4 | 5 | #include "settings.h" 6 | #include "z80Environment.h" 7 | #include "z80input.h" 8 | #include "ps2Input.h" 9 | #include "ay3-8912-state.h" 10 | #include "main_ROM.h" 11 | #include "VideoController.h" 12 | 13 | Sound::Ay3_8912_state _ay3_8912; 14 | static uint8_t zx_data = 0; 15 | 16 | static uint8_t _ram0Buffer[0x4000]; 17 | static uint8_t _ram2Buffer[0x4000]; 18 | static uint8_t _ram5Pixels[SPECTRUM_WIDTH * SPECTRUM_HEIGHT * 8]; 19 | static uint16_t _ram5Attributes[SPECTRUM_WIDTH * SPECTRUM_HEIGHT]; 20 | static uint8_t _ram5Buffer[0x2500]; 21 | 22 | Z80Environment::Z80Environment(VideoController* screen) 23 | : BorderColor(this) 24 | { 25 | this->Screen = screen; 26 | this->Screen->BorderColor = &this->_borderColor; 27 | 28 | this->Rom[0] = &this->_rom0; 29 | this->Rom[1] = &this->_rom1; 30 | 31 | this->Ram[0] = &this->_ram0; 32 | this->Ram[1] = &this->_ram1; 33 | this->Ram[2] = &this->_ram2; 34 | this->Ram[3] = &this->_ram3; 35 | this->Ram[4] = &this->_ram4; 36 | this->Ram[5] = &this->_ram5; 37 | this->Ram[6] = &this->_ram6; 38 | this->Ram[7] = &this->_ram7; 39 | } 40 | 41 | void Z80Environment::Initialize() 42 | { 43 | ESP_LOGI(TAG, "Z80Environment::Initialize()"); 44 | 45 | // Due to a technical limitation, the maximum statically allocated DRAM usage is 160KB 46 | // The remaining 160KB (for a total of 320KB of DRAM) can only be allocated at runtime as heap 47 | 48 | this->_rom0 = (uint8_t*)ROM; 49 | this->_rom1 = (uint8_t*)ROM; 50 | 51 | this->_ram0 = _ram0Buffer; 52 | this->_ram2 = _ram2Buffer; 53 | 54 | this->_mainScreenData.Pixels = _ram5Pixels; 55 | this->_mainScreenData.Attributes = _ram5Attributes; 56 | SpectrumScreenData* settings = this->Screen->Settings; 57 | settings->Attributes = this->_mainScreenData.Attributes; 58 | settings->Pixels = this->_mainScreenData.Pixels; 59 | this->_ram5.Initialize(&this->_mainScreenData, _ram5Buffer); 60 | 61 | #ifdef ZX128K 62 | this->_ram1 = (uint8_t*)malloc(0x4000); 63 | this->_ram3 = (uint8_t*)malloc(0x4000); 64 | this->_ram4 = (uint8_t*)malloc(0x4000); 65 | this->_ram6 = (uint8_t*)malloc(0x4000); 66 | 67 | this->_shadowScreenData.Pixels = (uint8_t*)malloc(SPECTRUM_WIDTH * SPECTRUM_HEIGHT * 8); 68 | this->_shadowScreenData.Attributes = (uint16_t*)malloc(SPECTRUM_WIDTH * SPECTRUM_HEIGHT * 2); 69 | this->_ram7.Initialize(&this->_shadowScreenData, (uint8_t*)malloc(0x2500)); 70 | #endif 71 | 72 | _ay3_8912.Initialize(); 73 | 74 | #ifdef BEEPER 75 | gpio_config_t io_conf = {}; 76 | io_conf.intr_type = GPIO_INTR_DISABLE; 77 | io_conf.mode = GPIO_MODE_OUTPUT; 78 | io_conf.pin_bit_mask = 1ULL<Rom[this->MemoryState.RomSelect]->ReadByte(addr); 91 | break; 92 | case 0x4000 ... 0x7FFF: 93 | // Always bank 5 94 | offset = addr - (uint16_t)0x4000; 95 | res = this->_ram5[offset]; 96 | break; 97 | case 0x8000 ... 0xBFFF: 98 | // Always bank 2 99 | offset = addr - (uint16_t)0x8000; 100 | res = this->_ram2[offset]; 101 | break; 102 | case 0xC000 ... 0xFFFF: 103 | // Selected page 104 | offset = addr - (uint16_t)0xC000; 105 | res = this->Ram[this->MemoryState.RamBank]->ReadByte(offset); 106 | break; 107 | } 108 | 109 | return res; 110 | } 111 | 112 | uint16_t Z80Environment::ReadWord(uint16_t addr) 113 | { 114 | return this->ReadByte(addr) | (this->ReadByte(addr + 1) << 8); 115 | } 116 | 117 | void Z80Environment::WriteByte(uint16_t addr, uint8_t data) 118 | { 119 | uint16_t offset; 120 | switch (addr) 121 | { 122 | case 0x0000 ... 0x3fff: 123 | // Cannot write to ROM 124 | break; 125 | case 0x4000 ... 0x7FFF: 126 | // Always bank 5 127 | offset = addr - (uint16_t)0x4000; 128 | this->_ram5.WriteByte(offset, data); 129 | break; 130 | case 0x8000 ... 0xBFFF: 131 | // Always bank 2 132 | offset = addr - (uint16_t)0x8000; 133 | this->_ram2.WriteByte(offset, data); 134 | break; 135 | case 0xC000 ... 0xFFFF: 136 | // Selected page 137 | offset = addr - (uint16_t)0xC000; 138 | this->Ram[this->MemoryState.RamBank]->WriteByte(offset, data); 139 | break; 140 | } 141 | } 142 | 143 | void Z80Environment::WriteWord(uint16_t addr, uint16_t data) 144 | { 145 | this->WriteByte(addr, (uint8_t)data); 146 | this->WriteByte(addr + 1, (uint8_t)(data >> 8)); 147 | } 148 | 149 | uint8_t Z80Environment::Input(uint8_t portLow, uint8_t portHigh) 150 | { 151 | if (portLow == 0xFE) 152 | { 153 | // Keyboard 154 | 155 | switch (portHigh) 156 | { 157 | case 0xFE: 158 | case 0xFD: 159 | case 0xFB: 160 | case 0xF7: 161 | case 0xEF: 162 | case 0xDF: 163 | case 0xBF: 164 | case 0x7F: 165 | return indata[portHigh - 0x7F]; 166 | case 0x00: 167 | { 168 | uint8_t result = indata[0xFE - 0x7F]; 169 | result &= indata[0xFD - 0x7F]; 170 | result &= indata[0xFB - 0x7F]; 171 | result &= indata[0xF7 - 0x7F]; 172 | result &= indata[0xEF - 0x7F]; 173 | result &= indata[0xDF - 0x7F]; 174 | result &= indata[0xBF - 0x7F]; 175 | result &= indata[0x7F - 0x7F]; 176 | return result; 177 | } 178 | } 179 | } 180 | 181 | // Sound (AY-3-8912) 182 | if (portLow == 0xFD) 183 | { 184 | switch (portHigh) 185 | { 186 | case 0xFF: 187 | return _ay3_8912.getRegisterData(); 188 | } 189 | } 190 | 191 | // Kempston Mouse 192 | if (portLow == 0xDF && Ps2_isMouseAvailable()) 193 | { 194 | switch (portHigh) 195 | { 196 | case 0xFA: 197 | return Ps2_getMouseButtons(); 198 | case 0xFB: 199 | return Ps2_getMouseX(); 200 | case 0xFF: 201 | return Ps2_getMouseY(); 202 | } 203 | } 204 | 205 | uint8_t data = zx_data; 206 | data |= (0xe0); /* Set bits 5-7 - as reset above */ 207 | data &= ~0x40; 208 | return data; 209 | } 210 | 211 | void Z80Environment::SetState(uint8_t memoryState) 212 | { 213 | if (this->MemoryState.PagingLock != 0) 214 | { 215 | // Locked, don't change 216 | return; 217 | } 218 | 219 | this->MemoryState.Bits = memoryState; 220 | } 221 | 222 | void Z80Environment::Output(uint8_t portLow, uint8_t portHigh, uint8_t data) 223 | { 224 | switch (portLow) 225 | { 226 | case 0xFE: 227 | { 228 | // border color (no bright colors) 229 | uint8_t borderColor = (data & 0x07); 230 | if ((indata[0x20] & 0x07) != borderColor) 231 | { 232 | this->BorderColor = borderColor; 233 | } 234 | 235 | #ifdef BEEPER 236 | uint8_t sound = (data & 0x10); 237 | if ((indata[0x20] & 0x10) != sound) 238 | { 239 | //_beeperGenerator.setState(sound != 0, this->TStates); 240 | gpio_set_level(BEEPER_PIN, sound >> 4 ? 1 : 0); 241 | } 242 | #endif 243 | 244 | indata[0x20] = data; 245 | } 246 | break; 247 | 248 | case 0xF5: 249 | { 250 | // Sound (AY-3-8912) 251 | switch (portHigh) 252 | { 253 | case 0xC0: 254 | _ay3_8912.selectRegister(data); 255 | break; 256 | } 257 | } 258 | break; 259 | 260 | case 0xFD: 261 | { 262 | switch (portHigh) 263 | { 264 | // Sound (AY-3-8912) 265 | case 0xFF: 266 | // Not sure if this one is correct 267 | _ay3_8912.selectRegister(data); 268 | break; 269 | case 0xBF: 270 | _ay3_8912.setRegisterData(data); 271 | break; 272 | 273 | case 0x7F: 274 | MemorySelect originalState = this->MemoryState; 275 | this->SetState(data); 276 | if (originalState.ShadowScreen != this->MemoryState.ShadowScreen) 277 | { 278 | if (this->MemoryState.ShadowScreen == 1) 279 | { 280 | this->Screen->Settings->Pixels = this->_shadowScreenData.Pixels; 281 | this->Screen->Settings->Attributes = this->_shadowScreenData.Attributes; 282 | } 283 | else 284 | { 285 | this->Screen->Settings->Pixels = this->_mainScreenData.Pixels; 286 | this->Screen->Settings->Attributes = this->_mainScreenData.Attributes; 287 | } 288 | } 289 | 290 | break; 291 | } 292 | } 293 | break; 294 | 295 | default: 296 | zx_data = data; 297 | break; 298 | } 299 | } 300 | 301 | uint8_t Z80Environment::get_BorderColor() 302 | { 303 | return Z80Environment::ToSpectrumColor(this->_borderColor); 304 | } 305 | void Z80Environment::set_BorderColor(uint8_t borderColor) 306 | { 307 | this->_borderColor = Z80Environment::FromSpectrumColor(borderColor) >> 8; 308 | } 309 | 310 | uint16_t Z80Environment::FromSpectrumColor(uint8_t sinclairColor) 311 | { 312 | // Sinclair: Flash-Bright-PaperG-PaperR-PaperB-InkG-InkR-InkB 313 | // 7 6 5 4 3 2 1 0 314 | // Our colors: 00-PaperB01-PaperG01-PaperR01 : 00-InkB01-InkG01-InkR01 315 | // 54 32 10 : 54 32 10 316 | 317 | bool bright = ((sinclairColor & 0B01000000) != 0); 318 | 319 | uint16_t ink = ((sinclairColor & 0B00000100) << 9); // InkG 320 | ink |= ((sinclairColor & 0B00000010) << 8); // InkR 321 | ink |= ((sinclairColor & 0B00000001) << 13); // InkB 322 | if (bright) 323 | { 324 | ink |= (ink >> 1); 325 | } 326 | 327 | uint16_t paper = ((sinclairColor & 0B00100000) >> 2); // PaperG 328 | paper |= ((sinclairColor & 0B00010000) >> 3); // PaperR 329 | paper |= ((sinclairColor & 0B00001000) << 2); // PaperB 330 | if (bright) 331 | { 332 | paper |= (paper >> 1); 333 | } 334 | 335 | uint16_t result = ink | paper; 336 | 337 | if (bright) 338 | { 339 | // This is only needed to correctly read back "bright black" color 340 | result |= 0x4000; 341 | } 342 | 343 | if ((sinclairColor & 0B10000000) != 0) 344 | { 345 | // Blink 346 | result |= 0x8000; 347 | } 348 | 349 | return result; 350 | } 351 | 352 | uint8_t Z80Environment::ToSpectrumColor(uint16_t color) 353 | { 354 | // Our colors: 00-PaperB01-PaperG01-PaperR01 : 00-InkB01-InkG01-InkR01 355 | // 54 32 10 : 54 32 10 356 | // Sinclair: Flash-Bright-PaperG-PaperR-PaperB-InkG-InkR-InkB 357 | // 7 6 5 4 3 2 1 0 358 | 359 | uint8_t result = 0; 360 | 361 | if ((color & 0x0080) != 0) 362 | { 363 | // Flash, need to swap bytes 364 | color = __builtin_bswap16(color); 365 | } 366 | 367 | if ((color & 0x4000) != 0) 368 | { 369 | // Bright 370 | result |= 0B01000000; 371 | } 372 | 373 | if ((color & 0x8000) != 0) 374 | { 375 | // Flash 376 | result |= 0B10000000; 377 | } 378 | 379 | result |= ((color & 0B00100000) >> 2); // PaperB 380 | result |= ((color & 0B00000010) << 3); // PaperR 381 | result |= ((color & 0B00001000) << 2); // PaperG 382 | 383 | color >>= 8; 384 | result |= ((color & 0B00100000) >> 5); // InkB 385 | result |= (color & 0B00000010); // InkR 386 | result |= ((color & 0B00001000) >> 1); // InkG 387 | 388 | return result; 389 | } -------------------------------------------------------------------------------- /src/z80Input.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "z80input.h" 3 | #include "ps2Input.h" 4 | 5 | uint8_t indata[128]; 6 | 7 | const uint8_t keyaddr[ZX_KEY_LAST] = { 8 | 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, // ZX_KEY_SHIFT, ZX_KEY_Z, ZX_KEY_X, ZX_KEY_C, ZX_KEY_V 9 | 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, // ZX_KEY_A, ZX_KEY_S, ZX_KEY_D, ZX_KEY_F, ZX_KEY_G 10 | 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, // ZX_KEY_Q, ZX_KEY_W, ZX_KEY_E, ZX_KEY_R, ZX_KEY_T 11 | 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, // ZX_KEY_1, ZX_KEY_2, ZX_KEY_3, ZX_KEY_4, ZX_KEY_5 12 | 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, // ZX_KEY_0, ZX_KEY_9, ZX_KEY_8, ZX_KEY_7, ZX_KEY_6 13 | 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, // ZX_KEY_P, ZX_KEY_O, ZX_KEY_I, ZX_KEY_U, ZX_KEY_Y 14 | 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, // ZX_KEY_ENTER, ZX_KEY_L, ZX_KEY_K, ZX_KEY_J, ZX_KEY_H 15 | 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, // ZX_KEY_SPACE, ZX_KEY_SYM, ZX_KEY_M, ZX_KEY_N, ZX_KEY_B 16 | }; 17 | 18 | const uint8_t keybuf[ZX_KEY_LAST] = { 19 | 0x01, 0x02, 0x04, 0x08, 0x10, // ZX_KEY_SHIFT, ZX_KEY_Z, ZX_KEY_X, ZX_KEY_C, ZX_KEY_V 20 | 0x01, 0x02, 0x04, 0x08, 0x10, // ZX_KEY_A, ZX_KEY_S, ZX_KEY_D, ZX_KEY_F, ZX_KEY_G 21 | 0x01, 0x02, 0x04, 0x08, 0x10, // ZX_KEY_Q, ZX_KEY_W, ZX_KEY_E, ZX_KEY_R, ZX_KEY_T 22 | 0x01, 0x02, 0x04, 0x08, 0x10, // ZX_KEY_1, ZX_KEY_2, ZX_KEY_3, ZX_KEY_4, ZX_KEY_5 23 | 0x01, 0x02, 0x04, 0x08, 0x10, // ZX_KEY_0, ZX_KEY_9, ZX_KEY_8, ZX_KEY_7, ZX_KEY_6 24 | 0x01, 0x02, 0x04, 0x08, 0x10, // ZX_KEY_P, ZX_KEY_O, ZX_KEY_I, ZX_KEY_U, ZX_KEY_Y 25 | 0x01, 0x02, 0x04, 0x08, 0x10, // ZX_KEY_ENTER, ZX_KEY_L, ZX_KEY_K, ZX_KEY_J, ZX_KEY_H 26 | 0x01, 0x02, 0x04, 0x08, 0x10, // ZX_KEY_SPACE, ZX_KEY_SYM, ZX_KEY_M, ZX_KEY_N, ZX_KEY_B 27 | }; 28 | 29 | #define ON_KEY(k, isKeyUp) isKeyUp ? indata[keyaddr[k] - 0x7F] |= keybuf[k] : indata[keyaddr[k] - 0x7F] &= ~keybuf[k] 30 | 31 | bool OnKey(uint32_t scanCode, bool isKeyUp) 32 | { 33 | switch (scanCode) 34 | { 35 | case KEY_LEFTSHIFT: 36 | case KEY_RIGHTSHIFT: 37 | ON_KEY(ZX_KEY_SHIFT, isKeyUp); 38 | break; 39 | case KEY_LEFTCONTROL: 40 | case KEY_RIGHTCONTROL: 41 | ON_KEY(ZX_KEY_SYM, isKeyUp); 42 | break; 43 | case KEY_ENTER: 44 | case KEY_KP_ENTER: 45 | ON_KEY(ZX_KEY_ENTER, isKeyUp); 46 | break; 47 | 48 | case KEY_SPACEBAR: 49 | ON_KEY(ZX_KEY_SPACE, isKeyUp); 50 | break; 51 | case KEY_0: 52 | ON_KEY(ZX_KEY_0, isKeyUp); 53 | break; 54 | case KEY_1: 55 | ON_KEY(ZX_KEY_1, isKeyUp); 56 | break; 57 | case KEY_2: 58 | ON_KEY(ZX_KEY_2, isKeyUp); 59 | break; 60 | case KEY_3: 61 | ON_KEY(ZX_KEY_3, isKeyUp); 62 | break; 63 | case KEY_4: 64 | ON_KEY(ZX_KEY_4, isKeyUp); 65 | break; 66 | case KEY_5: 67 | ON_KEY(ZX_KEY_5, isKeyUp); 68 | break; 69 | case KEY_6: 70 | ON_KEY(ZX_KEY_6, isKeyUp); 71 | break; 72 | case KEY_7: 73 | ON_KEY(ZX_KEY_7, isKeyUp); 74 | break; 75 | case KEY_8: 76 | ON_KEY(ZX_KEY_8, isKeyUp); 77 | break; 78 | case KEY_9: 79 | ON_KEY(ZX_KEY_9, isKeyUp); 80 | break; 81 | 82 | case KEY_A: 83 | ON_KEY(ZX_KEY_A, isKeyUp); 84 | break; 85 | case KEY_B: 86 | ON_KEY(ZX_KEY_B, isKeyUp); 87 | break; 88 | case KEY_C: 89 | ON_KEY(ZX_KEY_C, isKeyUp); 90 | break; 91 | case KEY_D: 92 | ON_KEY(ZX_KEY_D, isKeyUp); 93 | break; 94 | case KEY_E: 95 | ON_KEY(ZX_KEY_E, isKeyUp); 96 | break; 97 | case KEY_F: 98 | ON_KEY(ZX_KEY_F, isKeyUp); 99 | break; 100 | case KEY_G: 101 | ON_KEY(ZX_KEY_G, isKeyUp); 102 | break; 103 | case KEY_H: 104 | ON_KEY(ZX_KEY_H, isKeyUp); 105 | break; 106 | case KEY_I: 107 | ON_KEY(ZX_KEY_I, isKeyUp); 108 | break; 109 | case KEY_J: 110 | ON_KEY(ZX_KEY_J, isKeyUp); 111 | break; 112 | case KEY_K: 113 | ON_KEY(ZX_KEY_K, isKeyUp); 114 | break; 115 | case KEY_L: 116 | ON_KEY(ZX_KEY_L, isKeyUp); 117 | break; 118 | case KEY_M: 119 | ON_KEY(ZX_KEY_M, isKeyUp); 120 | break; 121 | case KEY_N: 122 | ON_KEY(ZX_KEY_N, isKeyUp); 123 | break; 124 | case KEY_O: 125 | ON_KEY(ZX_KEY_O, isKeyUp); 126 | break; 127 | case KEY_P: 128 | ON_KEY(ZX_KEY_P, isKeyUp); 129 | break; 130 | case KEY_Q: 131 | ON_KEY(ZX_KEY_Q, isKeyUp); 132 | break; 133 | case KEY_R: 134 | ON_KEY(ZX_KEY_R, isKeyUp); 135 | break; 136 | case KEY_S: 137 | ON_KEY(ZX_KEY_S, isKeyUp); 138 | break; 139 | case KEY_T: 140 | ON_KEY(ZX_KEY_T, isKeyUp); 141 | break; 142 | case KEY_U: 143 | ON_KEY(ZX_KEY_U, isKeyUp); 144 | break; 145 | case KEY_V: 146 | ON_KEY(ZX_KEY_V, isKeyUp); 147 | break; 148 | case KEY_W: 149 | ON_KEY(ZX_KEY_W, isKeyUp); 150 | break; 151 | case KEY_X: 152 | ON_KEY(ZX_KEY_X, isKeyUp); 153 | break; 154 | case KEY_Y: 155 | ON_KEY(ZX_KEY_Y, isKeyUp); 156 | break; 157 | case KEY_Z: 158 | ON_KEY(ZX_KEY_Z, isKeyUp); 159 | break; 160 | 161 | // "Convenience" buttons 162 | 163 | case KEY_MINUS: 164 | case KEY_KP_MINUS: 165 | ON_KEY(ZX_KEY_SYM, isKeyUp); 166 | ON_KEY(ZX_KEY_J, isKeyUp); 167 | break; 168 | case KEY_EQUAL: 169 | ON_KEY(ZX_KEY_SYM, isKeyUp); 170 | ON_KEY(ZX_KEY_L, isKeyUp); 171 | break; 172 | case KEY_COMMA: 173 | ON_KEY(ZX_KEY_SYM, isKeyUp); 174 | ON_KEY(ZX_KEY_N, isKeyUp); 175 | break; 176 | case KEY_DOT: 177 | case KEY_KP_DOT: 178 | ON_KEY(ZX_KEY_SYM, isKeyUp); 179 | ON_KEY(ZX_KEY_M, isKeyUp); 180 | break; 181 | case KEY_DIV: 182 | case KEY_KP_DIV: 183 | ON_KEY(ZX_KEY_SYM, isKeyUp); 184 | ON_KEY(ZX_KEY_V, isKeyUp); 185 | break; 186 | case KEY_SEMI: 187 | ON_KEY(ZX_KEY_SYM, isKeyUp); 188 | ON_KEY(ZX_KEY_O, isKeyUp); 189 | break; 190 | case KEY_KP_TIMES: 191 | ON_KEY(ZX_KEY_SYM, isKeyUp); 192 | ON_KEY(ZX_KEY_B, isKeyUp); 193 | break; 194 | case KEY_KP_PLUS: 195 | ON_KEY(ZX_KEY_SYM, isKeyUp); 196 | ON_KEY(ZX_KEY_K, isKeyUp); 197 | break; 198 | case KEY_BACKSPACE: 199 | ON_KEY(ZX_KEY_SHIFT, isKeyUp); 200 | ON_KEY(ZX_KEY_0, isKeyUp); 201 | break; 202 | case KEY_LEFTARROW: 203 | ON_KEY(ZX_KEY_SHIFT, isKeyUp); 204 | ON_KEY(ZX_KEY_5, isKeyUp); 205 | break; 206 | case KEY_RIGHTARROW: 207 | ON_KEY(ZX_KEY_SHIFT, isKeyUp); 208 | ON_KEY(ZX_KEY_8, isKeyUp); 209 | break; 210 | case KEY_UPARROW: 211 | ON_KEY(ZX_KEY_SHIFT, isKeyUp); 212 | ON_KEY(ZX_KEY_7, isKeyUp); 213 | break; 214 | case KEY_DOWNARROW: 215 | ON_KEY(ZX_KEY_SHIFT, isKeyUp); 216 | ON_KEY(ZX_KEY_6, isKeyUp); 217 | break; 218 | default: 219 | return false; 220 | } 221 | 222 | return true; 223 | } 224 | -------------------------------------------------------------------------------- /src/z80main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "z80main.h" 5 | #include "z80input.h" 6 | #include "z80Environment.h" 7 | #include "VideoController.h" 8 | #include "ps2Input.h" 9 | #include "ay3-8912-state.h" 10 | 11 | //#define BEEPER 12 | 13 | z80Emulator Z80cpu; 14 | extern Sound::Ay3_8912_state _ay3_8912; 15 | 16 | static uint16_t _attributeCount; 17 | static int _total; 18 | static int _next_total = 0; 19 | static uint8_t frames = 0; 20 | static uint32_t _ticks = 0; 21 | static VideoController* _spectrumScreen; 22 | 23 | void zx_setup(Z80Environment* environment) 24 | { 25 | _spectrumScreen = environment->Screen; 26 | _attributeCount = SPECTRUM_HEIGHT * SPECTRUM_WIDTH; 27 | 28 | Z80cpu.setup(environment); 29 | zx_reset(); 30 | } 31 | 32 | void zx_reset() 33 | { 34 | _ay3_8912.Clear(); 35 | memset(indata, 0xFF, 128); 36 | *_spectrumScreen->BorderColor = 0x2A; 37 | Z80cpu.reset(); 38 | } 39 | 40 | int32_t zx_loop() 41 | { 42 | int32_t result = -1; 43 | 44 | _total += Z80cpu.emulate(_next_total - _total); 45 | 46 | if (_total >= _next_total) 47 | { 48 | _next_total += TSTATES_PER_FRAME; 49 | 50 | // flash every 32 frames 51 | frames++; 52 | if (frames > 31) 53 | { 54 | frames = 0; 55 | for (int i = 0; i < _attributeCount; i++) 56 | { 57 | uint16_t color = _spectrumScreen->Settings->Attributes[i]; 58 | if ((color & 0x8080) != 0) 59 | { 60 | _spectrumScreen->Settings->Attributes[i] = __builtin_bswap16(color); 61 | } 62 | } 63 | } 64 | 65 | // Keyboard input 66 | int32_t scanCode = Ps2_GetScancode(); 67 | if (scanCode > 0) 68 | { 69 | if ((scanCode & 0xFF00) == 0xF000) 70 | { 71 | // key up 72 | 73 | scanCode = ((scanCode & 0xFF0000) >> 8 | (scanCode & 0xFF)); 74 | 75 | if (!OnKey(scanCode, true)) 76 | { 77 | result = scanCode; 78 | } 79 | } 80 | else 81 | { 82 | // key down 83 | 84 | OnKey(scanCode, false); 85 | } 86 | } 87 | 88 | Z80cpu.interrupt(); 89 | 90 | // delay 91 | //while (_spectrumScreen->Frames < _ticks) 92 | //{ 93 | //} 94 | 95 | _ticks = _spectrumScreen->Frames + 1; 96 | } 97 | 98 | return result; 99 | } 100 | -------------------------------------------------------------------------------- /test/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for PlatformIO Unit Testing and project tests. 3 | 4 | Unit Testing is a software testing method by which individual units of 5 | source code, sets of one or more MCU program modules together with associated 6 | control data, usage procedures, and operating procedures, are tested to 7 | determine whether they are fit for use. Unit testing finds problems early 8 | in the development cycle. 9 | 10 | More information about PlatformIO Unit Testing: 11 | - https://docs.platformio.org/page/plus/unit-testing.html 12 | --------------------------------------------------------------------------------