diff --git a/Factory_Tests/Feather_RP2040_Adalogger/Feather_RP2040_Adalogger.ino b/Factory_Tests/Feather_RP2040_Adalogger/Feather_RP2040_Adalogger.ino index ea74524b6..87c4f6c6e 100644 --- a/Factory_Tests/Feather_RP2040_Adalogger/Feather_RP2040_Adalogger.ino +++ b/Factory_Tests/Feather_RP2040_Adalogger/Feather_RP2040_Adalogger.ino @@ -2,7 +2,6 @@ // // SPDX-License-Identifier: MIT -#include #include #include "Adafruit_TestBed.h" diff --git a/runcpm-rp2350-hstx-usb/runcpm-pico/.metro_rp2350_tinyusb.test.only b/runcpm-rp2350-hstx-usb/runcpm-pico/.metro_rp2350_tinyusb.test.only new file mode 100644 index 000000000..e69de29bb diff --git a/runcpm-rp2350-hstx-usb/runcpm-pico/LICENSE b/runcpm-rp2350-hstx-usb/runcpm-pico/LICENSE new file mode 100644 index 000000000..f7a6865b8 --- /dev/null +++ b/runcpm-rp2350-hstx-usb/runcpm-pico/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Mockba the Borg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/runcpm-rp2350-hstx-usb/runcpm-pico/README.md b/runcpm-rp2350-hstx-usb/runcpm-pico/README.md new file mode 100644 index 000000000..bf8a5e7f7 --- /dev/null +++ b/runcpm-rp2350-hstx-usb/runcpm-pico/README.md @@ -0,0 +1,31 @@ + + +This is a port of runcpm to the raspberry pi pico. + +It is based on: + * [RunCPM](https://github.com/MockbaTheBorg/RunCPM/) + * [RunCPM_RPi_Pico](https://github.com/guidol70/RunCPM_RPi_Pico) + +It works on a Raspberry Pi Pico (or Pico W). It uses the internal flash +for storage, and can be mounted as USB storage. + +If your Pico is placed on the Pico DV carrier board, you also get a 100x30 +character screen to enjoy your CP/M output on! + +First, build for your device. You must + * Use the Philhower Pico Core + * In the Tools menu, select + * A flash size option that includes at least 512kB for filesystem + * USB Stack: Adafruit TinyUSB + +After it boots the first time, you need to + * Format the flash device on your host computer + * Create the folder "/A/0" + * Put something useful in that folder, such as [Zork](http://www.retroarchive.org/cpm/games/zork123_80.zip) + * Files must respect the "8.3" naming convention (8 letters filename + 3 letters extension) and be all uppercase + * Safely eject the drive, then reset the emulator. + * Now at the "A0>" prompt you can run "ZORK1". diff --git a/runcpm-rp2350-hstx-usb/runcpm-pico/abstraction_arduino.h b/runcpm-rp2350-hstx-usb/runcpm-pico/abstraction_arduino.h new file mode 100644 index 000000000..f16d86cd5 --- /dev/null +++ b/runcpm-rp2350-hstx-usb/runcpm-pico/abstraction_arduino.h @@ -0,0 +1,531 @@ +// SPDX-FileCopyrightText: 2023 Mockba the Borg +// +// SPDX-License-Identifier: MIT + +#ifndef ABSTRACT_H +#define ABSTRACT_H + +#ifdef PROFILE +#define printf(a, b) Serial.println(b) +#endif + +// #if defined ARDUINO_SAM_DUE || defined ADAFRUIT_GRAND_CENTRAL_M4 +#define HostOS 0x01 +// #endif + +#if !defined(FILE_TYPE) +#define FILE_TYPE FsFile +#endif + +#if defined CORE_TEENSY +#define HostOS 0x04 +#endif +#if defined ESP32 +#define HostOS 0x05 +#endif +#if defined _STM32_DEF_ +#define HostOS 0x06 +#endif + +/* Memory abstraction functions */ +/*===============================================================================*/ +bool _RamLoad(char* filename, uint16 address) { + FILE_TYPE f; + bool result = false; + + if (f = SD.open(filename, FILE_READ)) { + while (f.available()) + _RamWrite(address++, f.read()); + f.close(); + result = true; + } + return(result); +} + +/* Filesystem (disk) abstraction fuctions */ +/*===============================================================================*/ +FILE_TYPE rootdir, userdir; +#define FOLDERCHAR '/' + +typedef struct { + uint8 dr; + uint8 fn[8]; + uint8 tp[3]; + uint8 ex, s1, s2, rc; + uint8 al[16]; + uint8 cr, r0, r1, r2; +} CPM_FCB; + +typedef struct { + uint8 dr; + uint8 fn[8]; + uint8 tp[3]; + uint8 ex, s1, s2, rc; + uint8 al[16]; +} CPM_DIRENTRY; + +bool _sys_exists(uint8* filename) { + return(SD.exists((const char *)filename)); +} + +FILE_TYPE _sys_fopen_w(uint8* filename) { + return(SD.open((char*)filename, O_CREAT | O_WRITE)); +} + +int _sys_fputc(uint8 ch, FILE_TYPE& f) { + return(f.write(ch)); +} + +void _sys_fflush(FILE_TYPE& f) { + f.flush(); +} + +void _sys_fclose(FILE_TYPE& f) { + f.close(); +} + +int _sys_select(uint8* disk) { + uint8 result = FALSE; + FILE_TYPE f; + + digitalWrite(LED, HIGH ^ LEDinv); + if (f = SD.open((char*)disk, O_READ)) { + if (f.isDirectory()) + result = TRUE; + f.close(); + } + digitalWrite(LED, LOW ^ LEDinv); + return(result); +} + +long _sys_filesize(uint8* filename) { + long l = -1; + FILE_TYPE f; + + digitalWrite(LED, HIGH ^ LEDinv); + if (f = SD.open((char*)filename, O_RDONLY)) { + l = f.size(); + f.close(); + } + digitalWrite(LED, LOW ^ LEDinv); + return(l); +} + +int _sys_openfile(uint8* filename) { + FILE_TYPE f; + int result = 0; + + digitalWrite(LED, HIGH ^ LEDinv); + f = SD.open((char*)filename, O_READ); + if (f) { + f.close(); + result = 1; + } + digitalWrite(LED, LOW ^ LEDinv); + return(result); +} + +int _sys_makefile(uint8* filename) { + FILE_TYPE f; + int result = 0; + + digitalWrite(LED, HIGH ^ LEDinv); + f = SD.open((char*)filename, O_CREAT | O_WRITE); + if (f) { + f.close(); + result = 1; + } + digitalWrite(LED, LOW ^ LEDinv); + return(result); +} + +int _sys_deletefile(uint8* filename) { + digitalWrite(LED, HIGH ^ LEDinv); + return(SD.remove((char*)filename)); + digitalWrite(LED, LOW ^ LEDinv); +} + +int _sys_renamefile(uint8* filename, uint8* newname) { + FILE_TYPE f; + int result = 0; + + digitalWrite(LED, HIGH ^ LEDinv); + f = SD.open((char*)filename, O_WRITE | O_APPEND); + if (f) { + if (f.rename((char*)newname)) { + f.close(); + result = 1; + } + } + digitalWrite(LED, LOW ^ LEDinv); + return(result); +} + +#ifdef DEBUGLOG +void _sys_logbuffer(uint8* buffer) { +#ifdef CONSOLELOG + puts((char*)buffer); +#else + FILE_TYPE f; + uint8 s = 0; + while (*(buffer + s)) // Computes buffer size + ++s; + if (f = SD.open(LogName, O_CREAT | O_APPEND | O_WRITE)) { + f.write(buffer, s); + f.flush(); + f.close(); + } +#endif +} +#endif + +bool _sys_extendfile(char* fn, unsigned long fpos) +{ + uint8 result = true; + FILE_TYPE f; + unsigned long i; + + digitalWrite(LED, HIGH ^ LEDinv); + if (f = SD.open(fn, O_WRITE | O_APPEND)) { + if (fpos > f.size()) { + for (i = 0; i < f.size() - fpos; ++i) { + if (f.write((uint8)0) != 1) { + result = false; + break; + } + } + } + f.close(); + } else { + result = false; + } + digitalWrite(LED, LOW ^ LEDinv); + return(result); +} + +uint8 _sys_readseq(uint8* filename, long fpos) { + uint8 result = 0xff; + FILE_TYPE f; + uint8 bytesread; + uint8 dmabuf[BlkSZ]; + uint8 i; + + digitalWrite(LED, HIGH ^ LEDinv); + f = SD.open((char*)filename, O_READ); + if (f) { + if (f.seek(fpos)) { + for (i = 0; i < BlkSZ; ++i) + dmabuf[i] = 0x1a; + bytesread = f.read(&dmabuf[0], BlkSZ); + if (bytesread) { + for (i = 0; i < BlkSZ; ++i) + _RamWrite(dmaAddr + i, dmabuf[i]); + } + result = bytesread ? 0x00 : 0x01; + } else { + result = 0x01; + } + f.close(); + } else { + result = 0x10; + } + digitalWrite(LED, LOW ^ LEDinv); + return(result); +} + +uint8 _sys_writeseq(uint8* filename, long fpos) { + uint8 result = 0xff; + FILE_TYPE f; + + digitalWrite(LED, HIGH ^ LEDinv); + if (_sys_extendfile((char*)filename, fpos)) + f = SD.open((char*)filename, O_RDWR); + if (f) { + if (f.seek(fpos)) { + if (f.write(_RamSysAddr(dmaAddr), BlkSZ)) + result = 0x00; + } else { + result = 0x01; + } + f.close(); + } else { + result = 0x10; + } + digitalWrite(LED, LOW ^ LEDinv); + return(result); +} + +uint8 _sys_readrand(uint8* filename, long fpos) { + uint8 result = 0xff; + FILE_TYPE f; + uint8 bytesread; + uint8 dmabuf[BlkSZ]; + uint8 i; + long extSize; + + digitalWrite(LED, HIGH ^ LEDinv); + f = SD.open((char*)filename, O_READ); + if (f) { + if (f.seek(fpos)) { + for (i = 0; i < BlkSZ; ++i) + dmabuf[i] = 0x1a; + bytesread = f.read(&dmabuf[0], BlkSZ); + if (bytesread) { + for (i = 0; i < BlkSZ; ++i) + _RamWrite(dmaAddr + i, dmabuf[i]); + } + result = bytesread ? 0x00 : 0x01; + } else { + if (fpos >= 65536L * BlkSZ) { + result = 0x06; // seek past 8MB (largest file size in CP/M) + } else { + extSize = f.size(); + // round file size up to next full logical extent + extSize = ExtSZ * ((extSize / ExtSZ) + ((extSize % ExtSZ) ? 1 : 0)); + if (fpos < extSize) + result = 0x01; // reading unwritten data + else + result = 0x04; // seek to unwritten extent + } + } + f.close(); + } else { + result = 0x10; + } + digitalWrite(LED, LOW ^ LEDinv); + return(result); +} + +uint8 _sys_writerand(uint8* filename, long fpos) { + uint8 result = 0xff; + FILE_TYPE f; + + digitalWrite(LED, HIGH ^ LEDinv); + if (_sys_extendfile((char*)filename, fpos)) { + f = SD.open((char*)filename, O_RDWR); + } + if (f) { + if (f.seek(fpos)) { + if (f.write(_RamSysAddr(dmaAddr), BlkSZ)) + result = 0x00; + } else { + result = 0x06; + } + f.close(); + } else { + result = 0x10; + } + digitalWrite(LED, LOW ^ LEDinv); + return(result); +} + +static uint8 findNextDirName[13]; +static uint16 fileRecords = 0; +static uint16 fileExtents = 0; +static uint16 fileExtentsUsed = 0; +static uint16 firstFreeAllocBlock; + +uint8 _findnext(uint8 isdir) { + FILE_TYPE f; + uint8 result = 0xff; + bool isfile; + uint32 bytes; + + digitalWrite(LED, HIGH ^ LEDinv); + if (allExtents && fileRecords) { + _mockupDirEntry(); + result = 0; + } else { + while (f = userdir.openNextFile()) { + f.getName((char*)&findNextDirName[0], 13); + isfile = !f.isDirectory(); + bytes = f.size(); + f.close(); + if (!isfile) + continue; + _HostnameToFCBname(findNextDirName, fcbname); + if (match(fcbname, pattern)) { + if (isdir) { + // account for host files that aren't multiples of the block size + // by rounding their bytes up to the next multiple of blocks + if (bytes & (BlkSZ - 1)) { + bytes = (bytes & ~(BlkSZ - 1)) + BlkSZ; + } + fileRecords = bytes / BlkSZ; + fileExtents = fileRecords / BlkEX + ((fileRecords & (BlkEX - 1)) ? 1 : 0); + fileExtentsUsed = 0; + firstFreeAllocBlock = firstBlockAfterDir; + _mockupDirEntry(); + } else { + fileRecords = 0; + fileExtents = 0; + fileExtentsUsed = 0; + firstFreeAllocBlock = firstBlockAfterDir; + } + _RamWrite(tmpFCB, filename[0] - '@'); + _HostnameToFCB(tmpFCB, findNextDirName); + result = 0x00; + break; + } + } + } + digitalWrite(LED, LOW ^ LEDinv); + return(result); +} + +uint8 _findfirst(uint8 isdir) { + uint8 path[4] = { '?', FOLDERCHAR, '?', 0 }; + path[0] = filename[0]; + path[2] = filename[2]; + if (userdir) + userdir.close(); + userdir = SD.open((char*)path); // Set directory search to start from the first position + _HostnameToFCBname(filename, pattern); + fileRecords = 0; + fileExtents = 0; + fileExtentsUsed = 0; + return(_findnext(isdir)); +} + +uint8 _findnextallusers(uint8 isdir) { + uint8 result = 0xFF; + char dirname[13]; + bool done = false; + + while (!done) { + while (!userdir) { + userdir = rootdir.openNextFile(); + if (!userdir) { + done = true; + break; + } + userdir.getName(dirname, sizeof dirname); + if (userdir.isDirectory() && strlen(dirname) == 1 && isxdigit(dirname[0])) { + currFindUser = dirname[0] <= '9' ? dirname[0] - '0' : toupper(dirname[0]) - 'A' + 10; + break; + } + userdir.close(); + } + if (userdir) { + result = _findnext(isdir); + if (result) { + userdir.close(); + } else { + done = true; + } + } else { + result = 0xFF; + done = true; + } + } + return result; +} + +uint8 _findfirstallusers(uint8 isdir) { + uint8 path[2] = { '?', 0 }; + + path[0] = filename[0]; + if (rootdir) + rootdir.close(); + if (userdir) + userdir.close(); + rootdir = SD.open((char*)path); // Set directory search to start from the first position + strcpy((char*)pattern, "???????????"); + if (!rootdir) + return 0xFF; + fileRecords = 0; + fileExtents = 0; + fileExtentsUsed = 0; + return(_findnextallusers(isdir)); +} + +uint8 _Truncate(char* filename, uint8 rc) { + FILE_TYPE f; + int result = 0; + + digitalWrite(LED, HIGH ^ LEDinv); + f = SD.open((char*)filename, O_WRITE | O_APPEND); + if (f) { + if (f.truncate(rc * BlkSZ)) { + f.close(); + result = 1; + } + } + digitalWrite(LED, LOW ^ LEDinv); + return(result); +} + +void _MakeUserDir() { + uint8 dFolder = cDrive + 'A'; + uint8 uFolder = toupper(tohex(userCode)); + + uint8 path[4] = { dFolder, FOLDERCHAR, uFolder, 0 }; + + digitalWrite(LED, HIGH ^ LEDinv); + SD.mkdir((char*)path); + digitalWrite(LED, LOW ^ LEDinv); +} + +uint8 _sys_makedisk(uint8 drive) { + uint8 result = 0; + if (drive < 1 || drive>16) { + result = 0xff; + } else { + uint8 dFolder = drive + '@'; + uint8 disk[2] = { dFolder, 0 }; + digitalWrite(LED, HIGH ^ LEDinv); + if (!SD.mkdir((char*)disk)) { + result = 0xfe; + } else { + uint8 path[4] = { dFolder, FOLDERCHAR, '0', 0 }; + SD.mkdir((char*)path); + } + digitalWrite(LED, LOW ^ LEDinv); + } + + return(result); +} + +/* Hardware abstraction functions */ +/*===============================================================================*/ +void _HardwareOut(const uint32 Port, const uint32 Value) { + +} + +uint32 _HardwareIn(const uint32 Port) { + return 0; +} + +/* Console abstraction functions */ +/*===============================================================================*/ + +#include "arduino_hooks.h" + +int _kbhit(void) { + if (_kbhit_hook && _kbhit_hook()) { return true; } + return(Serial.available()); +} + +uint8 _getch(void) { + while(true) { + if(_kbhit_hook && _kbhit_hook()) { return _getch_hook(); } + if(Serial.available()) { return Serial.read(); } + } +} + +uint8 _getche(void) { + uint8 ch = _getch(); + _putch(ch); + return(ch); +} + +void _putch(uint8 ch) { + Serial.write(ch); + if(_putch_hook) _putch_hook(ch); +} + +void _clrscr(void) { + Serial.print("\e[H\e[J"); +} + +#endif diff --git a/runcpm-rp2350-hstx-usb/runcpm-pico/arduino_hooks.c b/runcpm-rp2350-hstx-usb/runcpm-pico/arduino_hooks.c new file mode 100644 index 000000000..be225ac5a --- /dev/null +++ b/runcpm-rp2350-hstx-usb/runcpm-pico/arduino_hooks.c @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2023 Jeff Epler for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "arduino_hooks.h" + +bool (*_kbhit_hook)(void); +uint8_t (*_getch_hook)(void); +void (*_putch_hook)(uint8_t ch); + diff --git a/runcpm-rp2350-hstx-usb/runcpm-pico/arduino_hooks.h b/runcpm-rp2350-hstx-usb/runcpm-pico/arduino_hooks.h new file mode 100644 index 000000000..a1baed74b --- /dev/null +++ b/runcpm-rp2350-hstx-usb/runcpm-pico/arduino_hooks.h @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2023 Jeff Epler for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +extern bool (*_kbhit_hook)(void); +extern uint8_t (*_getch_hook)(void); +extern void (*_putch_hook)(uint8_t ch); + diff --git a/runcpm-rp2350-hstx-usb/runcpm-pico/ccp.h b/runcpm-rp2350-hstx-usb/runcpm-pico/ccp.h new file mode 100644 index 000000000..38da86fc2 --- /dev/null +++ b/runcpm-rp2350-hstx-usb/runcpm-pico/ccp.h @@ -0,0 +1,891 @@ +// SPDX-FileCopyrightText: 2023 Mockba the Borg +// +// SPDX-License-Identifier: MIT + +#ifndef CCP_H +#define CCP_H + +// CP/M BDOS calls +#include "cpm.h" + +#define CmdFCB (BatchFCB + 48) // FCB for use by internal commands +#define ParFCB 0x005C // FCB for use by line parameters +#define SecFCB 0x006C // Secondary part of FCB for renaming files +#define Trampoline (CmdFCB + 36) // Trampoline for running external commands + +#define inBuf (BDOSjmppage - 256) // Input buffer location +#define cmdLen 125 // Maximum size of a command line (sz+rd+cmd+\0) + +#define defDMA 0x0080 // Default DMA address +#define defLoad 0x0100 // Default load address + +// CCP global variables +uint8 pgSize = 22; // for TYPE +uint8 curDrive = 0; // 0 -> 15 = A -> P .. Current drive for the CCP (same as RAM[DSKByte]) +uint8 parDrive = 0; // 0 -> 15 = A -> P .. Drive for the first file parameter +uint8 curUser = 0; // 0 -> 15 .. Current user area to access +bool sFlag = FALSE; // Submit Flag +uint8 sRecs = 0; // Number of records on the Submit file +uint8 prompt[8] = "\r\n >"; +uint16 pbuf, perr; +uint8 blen; // Actual size of the typed command line (size of the buffer) + +static const char *Commands[] = +{ + // Standard CP/M commands + "DIR", + "ERA", + "TYPE", + "SAVE", + "REN", + "USER", + + // Extra CCP commands + "CLS", + "DEL", + "EXIT", + "PAGE", + "VOL", + "?", + NULL +}; + +// Used to call BDOS from inside the CCP +uint16 _ccp_bdos(uint8 function, uint16 de) { + SET_LOW_REGISTER(BC, function); + DE = de; + _Bdos(); + + return (HL & 0xffff); +} // _ccp_bdos + +// Compares two strings (Atmel doesn't like strcmp) +uint8 _ccp_strcmp(char *stra, char *strb) { + while (*stra && *strb && (*stra == *strb)) { + ++stra; + ++strb; + } + return (*stra == *strb); +} // _ccp_strcmp + +// Gets the command ID number +uint8 _ccp_cnum(void) { + uint8 result = 255; + uint8 command[9]; + uint8 i = 0; + + if (!_RamRead(CmdFCB)) { // If a drive was set, then the command is external + while (i < 8 && _RamRead(CmdFCB + i + 1) != ' ') { + command[i] = _RamRead(CmdFCB + i + 1); + ++i; + } + command[i] = 0; + i = 0; + while (Commands[i]) { + if (_ccp_strcmp((char *)command, (char *)Commands[i])) { + result = i; + perr = defDMA + 2; + break; + } + ++i; + } + } + return (result); +} // _ccp_cnum + +// Returns true if character is a separator +uint8 _ccp_delim(uint8 ch) { + return (ch == 0 || ch == ' ' || ch == '=' || ch == '.' || ch == ':' || ch == ';' || ch == '<' || ch == '>'); +} + +// Prints the FCB filename +void _ccp_printfcb(uint16 fcb, uint8 compact) { + uint8 i, ch; + + ch = _RamRead(fcb); + if (ch && compact) { + _ccp_bdos( C_WRITE, ch + '@'); + _ccp_bdos( C_WRITE, ':'); + } + + for (i = 1; i < 12; ++i) { + ch = _RamRead(fcb + i); + if ((ch == ' ') && compact) + continue; + if (i == 9) + _ccp_bdos(C_WRITE, compact ? '.' : ' '); + _ccp_bdos(C_WRITE, ch); + } +} // _ccp_printfcb + +// Initializes the FCB +void _ccp_initFCB(uint16 address, uint8 size) { + uint8 i; + + for (i = 0; i < size; ++i) + _RamWrite(address + i, 0x00); + + for (i = 0; i < 11; ++i) + _RamWrite(address + 1 + i, 0x20); +} // _ccp_initFCB + +// Name to FCB +uint8 _ccp_nameToFCB(uint16 fcb) { + uint8 pad, plen, ch, n = 0; + + // Checks for a drive and places it on the Command FCB + if (_RamRead(pbuf + 1) == ':') { + ch = toupper(_RamRead(pbuf++)); + _RamWrite(fcb, ch - '@'); // Makes the drive 0x1-0xF for A-P + ++pbuf; // Points pbuf past the : + blen -= 2; + } + if (blen) { + ++fcb; + + plen = 8; + pad = ' '; + ch = toupper(_RamRead(pbuf)); + + while (blen && plen) { + if (_ccp_delim(ch)) + break; + ++pbuf; + --blen; + if (ch == '*') + pad = '?'; + if (pad == '?') { + ch = pad; + n = n | 0x80; // Name is not unique + } + --plen; + ++n; + _RamWrite(fcb++, ch); + ch = toupper(_RamRead(pbuf)); + } + + while (plen--) + _RamWrite(fcb++, pad); + plen = 3; + pad = ' '; + if (ch == '.') { + ++pbuf; + --blen; + } + + while (blen && plen) { + ch = toupper(_RamRead(pbuf)); + if (_ccp_delim(ch)) + break; + ++pbuf; + --blen; + if (ch == '*') + pad = '?'; + if (pad == '?') { + ch = pad; + n = n | 0x80; // Name is not unique + } + --plen; + ++n; + _RamWrite(fcb++, ch); + } + + while (plen--) + _RamWrite(fcb++, pad); + } + return (n); +} // _ccp_nameToFCB + +// Converts the ParFCB name to a number +uint16 _ccp_fcbtonum() { + uint8 ch; + uint16 n = 0; + uint8 pos = ParFCB + 1; + + while (TRUE) { + ch = _RamRead(pos++); + if ((ch < '0') || (ch > '9')) { + break; + } + n = (n * 10) + (ch - '0'); + } + return (n); +} // _ccp_fcbtonum + +// DIR command +void _ccp_dir(void) { + uint8 i; + uint8 dirHead[6] = "A: "; + uint8 dirSep[6] = " | "; + uint32 fcount = 0; // Number of files printed + uint32 ccount = 0; // Number of columns printed + + if (_RamRead(ParFCB + 1) == ' ') + for (i = 1; i < 12; ++i) + _RamWrite(ParFCB + i, '?'); + dirHead[0] = _RamRead(ParFCB) ? _RamRead(ParFCB) + '@' : prompt[2]; + + _puts("\r\n"); + if (!_SearchFirst(ParFCB, TRUE)) { + _puts((char *)dirHead); + _ccp_printfcb(tmpFCB, FALSE); + ++fcount; + ++ccount; + + while (!_SearchNext(ParFCB, TRUE)) { + if (!ccount) { + _puts( "\r\n"); + _puts( (char *)dirHead); + } else { + _puts((char *)dirSep); + } + _ccp_printfcb(tmpFCB, FALSE); + ++fcount; + ++ccount; + if (ccount > 3) + ccount = 0; + } + } else { + _puts("No file"); + } +} // _ccp_dir + +// ERA command +void _ccp_era(void) { + if (_ccp_bdos(F_DELETE, ParFCB)) + _puts("\r\nNo file"); +} // _ccp_era + +// TYPE command +uint8 _ccp_type(void) { + uint8 i, c, l = 0, error = TRUE; + uint16 a, p = 0; + + if (!_ccp_bdos(F_OPEN, ParFCB)) { + _puts("\r\n"); + + while (!_ccp_bdos(F_READ, ParFCB)) { + i = 128; + a = dmaAddr; + + while (i) { + c = _RamRead(a); + if (c == 0x1a) + break; + _ccp_bdos(C_WRITE, c); + if (c == 0x0a) { + ++l; + if (pgSize && (l == pgSize)) { + l = 0; + p = _ccp_bdos(C_READ, 0x0000); + if (p == 3) + break; + } + } + --i; + ++a; + } + if (p == 3) + break; + } + error = FALSE; + } + return (error); +} // _ccp_type + +// SAVE command +uint8 _ccp_save(void) { + uint8 error = TRUE; + uint16 pages = _ccp_fcbtonum(); + uint16 i, dma; + + if (pages < 256) { + error = FALSE; + + while (_RamRead(pbuf) == ' ' && blen) { // Skips any leading spaces + ++pbuf; + --blen; + } + _ccp_nameToFCB(SecFCB); // Loads file name onto the ParFCB + if (_ccp_bdos(F_MAKE, SecFCB)) { + _puts("Err: create"); + } else { + if (_ccp_bdos(F_OPEN, SecFCB)) { + _puts("Err: open"); + } else { + pages *= 2; // Calculates the number of CP/M blocks to write + dma = defLoad; + _puts("\r\n"); + + for (i = 0; i < pages; i++) { + _ccp_bdos( F_DMAOFF, dma); + _ccp_bdos( F_WRITE, SecFCB); + dma += 128; + _ccp_bdos( C_WRITE, '.'); + } + _ccp_bdos(F_CLOSE, SecFCB); + } + } + } + return (error); +} // _ccp_save + +// REN command +void _ccp_ren(void) { + uint8 ch, i; + + ++pbuf; + + _ccp_nameToFCB(SecFCB); + + for (i = 0; i < 12; ++i) { // Swap the filenames on the fcb + ch = _RamRead(ParFCB + i); + _RamWrite( ParFCB + i, _RamRead(SecFCB + i)); + _RamWrite( SecFCB + i, ch); + } + if (_ccp_bdos(F_RENAME, ParFCB)) + _puts("\r\nNo file"); +} // _ccp_ren + +// USER command +uint8 _ccp_user(void) { + uint8 error = TRUE; + + curUser = (uint8)_ccp_fcbtonum(); + if (curUser < 16) { + _ccp_bdos(F_USERNUM, curUser); + error = FALSE; + } + return (error); +} // _ccp_user + +// PAGE command +uint8 _ccp_page(void) { + uint8 error = TRUE; + uint16 r = _ccp_fcbtonum(); + + if (r < 256) { + pgSize = (uint8)r; + error = FALSE; + } + return (error); +} // _ccp_page + +// VOL command +uint8 _ccp_vol(void) { + uint8 error = FALSE; + uint8 letter = _RamRead(ParFCB) ? '@' + _RamRead(ParFCB) : 'A' + curDrive; + uint8 folder[5] = {letter, FOLDERCHAR, '0', FOLDERCHAR, 0}; + uint8 filename[13] = {letter, FOLDERCHAR, '0', FOLDERCHAR, 'I', 'N', 'F', 'O', '.', 'T', 'X', 'T', 0}; + uint8 bytesread; + uint8 i, j; + + _puts("\r\nVolumes on "); + _putcon(folder[0]); + _puts(":\r\n"); + + for (i = 0; i < 16; ++i) { + folder[2] = i < 10 ? i + 48 : i + 55; + if (_sys_exists(folder)) { + _putcon(i < 10 ? ' ' : '1'); + _putcon(i < 10 ? folder[2] : 38 + i); + _puts(": "); + filename[2] = i < 10 ? i + 48 : i + 55; + bytesread = (uint8)_sys_readseq(filename, 0); + if (!bytesread) { + for (j = 0; j < 128; ++j) { + if ((_RamRead(dmaAddr + j) < 32) || (_RamRead(dmaAddr + j) > 126)) + break; + _putcon(_RamRead(dmaAddr + j)); + } + } + _puts("\r\n"); + } + } + return (error); +} // _ccp_vol + +// ?/Help command +uint8 _ccp_hlp(void) { + _puts("\r\nCCP Commands:\r\n"); + _puts("\t? - Shows this list of commands\r\n"); + _puts("\tCLS - Clears the screen\r\n"); + _puts("\tDEL - Alias to ERA\r\n"); + _puts("\tEXIT - Terminates RunCPM\r\n"); + _puts("\tPAGE [] - Sets the page size for TYPE\r\n"); + _puts("\t or disables paging if no parameter passed\r\n"); + _puts("\tVOL [drive] - Shows the volume information\r\n"); + _puts("\t which comes from each volume's INFO.TXT"); + return(FALSE); +} +#ifdef HASLUA + +// External (.LUA) command +uint8 _ccp_lua(void) { + uint8 error = TRUE; + uint8 found, drive, user = 0; + uint16 loadAddr = defLoad; + + _RamWrite( CmdFCB + 9, 'L'); + _RamWrite( CmdFCB + 10, 'U'); + _RamWrite( CmdFCB + 11, 'A'); + + drive = _RamRead(CmdFCB); + found = !_ccp_bdos(F_OPEN, CmdFCB); // Look for the program on the FCB drive, current or specified + if (!found) { // If not found + if (!drive) { // and the search was on the default drive + _RamWrite(CmdFCB, 0x01); // Then look on drive A: user 0 + if (curUser) { + user = curUser; // Save the current user + _ccp_bdos(F_USERNUM, 0x0000); // then set it to 0 + } + found = !_ccp_bdos(F_OPEN, CmdFCB); + if (!found) { // If still not found then + if (curUser) { // If current user not = 0 + _RamWrite(CmdFCB, 0x00); // look on current drive user 0 + found = !_ccp_bdos(F_OPEN, CmdFCB); // and try again + } + } + } + } + if (found) { + _puts("\r\n"); + + _ccp_bdos(F_RUNLUA, CmdFCB); + if (user) { // If a user was selected + _ccp_bdos(F_USERNUM, curUser); // Set it back + user = 0; + } + _RamWrite(CmdFCB, drive); // Set the command FCB drive back to what it was + cDrive = oDrive; // And restore cDrive + error = FALSE; + } + if (user) // If a user was selected + _ccp_bdos(F_USERNUM, curUser); // Set it back + _RamWrite(CmdFCB, drive); // Set the command FCB drive back to what it was + + return (error); +} // _ccp_lua +#endif // ifdef HASLUA + +// External (.COM) command +uint8 _ccp_ext(void) { + bool error = TRUE, found = FALSE; + uint8 drive = 0, user = 0; + uint16 loadAddr = defLoad; + + bool wasBlank = (_RamRead(CmdFCB + 9) == ' '); + bool wasSUB = ((_RamRead(CmdFCB + 9) == 'S') && + (_RamRead(CmdFCB + 10) == 'U') && + (_RamRead(CmdFCB + 11) == 'B')); + + if (!wasSUB) { + if (wasBlank) { + _RamWrite(CmdFCB + 9, 'C'); //first look for a .COM file + _RamWrite(CmdFCB + 10, 'O'); + _RamWrite(CmdFCB + 11, 'M'); + } + + drive = _RamRead(CmdFCB); // Get the drive from the command FCB + found = !_ccp_bdos(F_OPEN, CmdFCB); // Look for the program on the FCB drive, current or specified + if (!found) { // If not found + if (!drive) { // and the search was on the default drive + _RamWrite(CmdFCB, 0x01); // Then look on drive A: user 0 + if (curUser) { + user = curUser; // Save the current user + _ccp_bdos(F_USERNUM, 0x0000); // then set it to 0 + } + found = !_ccp_bdos(F_OPEN, CmdFCB); + if (!found) { // If still not found then + if (curUser) { // If current user not = 0 + _RamWrite(CmdFCB, 0x00); // look on current drive user 0 + found = !_ccp_bdos(F_OPEN, CmdFCB); // and try again + } + } + } + } + if (!found) { + _RamWrite(CmdFCB, drive); // restore previous drive + _ccp_bdos(F_USERNUM, curUser); // restore to previous user + } + } + + //if .COM not found then look for a .SUB file + if ((wasBlank || wasSUB) && !found && !sFlag) { //don't auto-submit while executing a submit file + _RamWrite(CmdFCB + 9, 'S'); + _RamWrite(CmdFCB + 10, 'U'); + _RamWrite(CmdFCB + 11, 'B'); + + drive = _RamRead(CmdFCB); // Get the drive from the command FCB + found = !_ccp_bdos(F_OPEN, CmdFCB); // Look for the program on the FCB drive, current or specified + if (!found) { // If not found + if (!drive) { // and the search was on the default drive + _RamWrite(CmdFCB, 0x01); // Then look on drive A: user 0 + if (curUser) { + user = curUser; // Save the current user + _ccp_bdos(F_USERNUM, 0x0000); // then set it to 0 + } + found = !_ccp_bdos(F_OPEN, CmdFCB); + if (!found) { // If still not found then + if (curUser) { // If current user not = 0 + _RamWrite(CmdFCB, 0x00); // look on current drive user 0 + found = !_ccp_bdos(F_OPEN, CmdFCB); // and try again + } + } + } + } + if (!found) { + _RamWrite(CmdFCB, drive); // restore previous drive + _ccp_bdos(F_USERNUM, curUser); // restore to previous user + } + + if (found) { + //_puts(".SUB file found!\n"); + int i; + + //move FCB's (CmdFCB --> ParFCB --> SecFCB) + for (i = 0; i < 16; i++) { + //ParFCB to SecFCB + _RamWrite(SecFCB + i, _RamRead(ParFCB + i)); + //CmdFCB to ParFCB + _RamWrite(ParFCB + i, _RamRead(CmdFCB + i)); + } + // (Re)Initialize the CmdFCB + _ccp_initFCB(CmdFCB, 36); + + //put 'SUBMIT.COM' in CmdFCB + const char *str = "SUBMIT COM"; + int s = (int)strlen(str); + for (i = 0; i < s; i++) + _RamWrite(CmdFCB + i + 1, str[i]); + + //now try to find SUBMIT.COM file + found = !_ccp_bdos(F_OPEN, CmdFCB); // Look for the program on the FCB drive, current or specified + if (!found) { // If not found + if (!drive) { // and the search was on the default drive + _RamWrite(CmdFCB, 0x01); // Then look on drive A: user 0 + if (curUser) { + user = curUser; // Save the current user + _ccp_bdos(F_USERNUM, 0x0000); // then set it to 0 + } + found = !_ccp_bdos(F_OPEN, CmdFCB); + if (!found) { // If still not found then + if (curUser) { // If current user not = 0 + _RamWrite(CmdFCB, 0x00); // look on current drive user 0 + found = !_ccp_bdos(F_OPEN, CmdFCB); // and try again + } + } + } + } + if (found) { + //insert "@" into command buffer + //note: this is so the rest will be parsed correctly + blen = _RamRead(defDMA); + if (blen < cmdLen) { + blen++; + _RamWrite(defDMA, blen); + } + uint8 lc = '@'; + for (i = 0; i < blen; i++) { + uint8 nc = _RamRead(defDMA + 1 + i); + _RamWrite(defDMA + 1 + i, lc); + lc = nc; + } + } + } + } + + if (found) { // Program was found somewhere + _puts("\r\n"); + _ccp_bdos(F_DMAOFF, loadAddr); // Sets the DMA address for the loading + while (!_ccp_bdos(F_READ, CmdFCB)) { // Loads the program into memory + loadAddr += 128; + if (loadAddr == BDOSjmppage) { // Breaks if it reaches the end of TPA + _puts("\r\nNo Memory"); + break; + } + _ccp_bdos(F_DMAOFF, loadAddr); // Points the DMA offset to the next loadAddr + } + _ccp_bdos(F_DMAOFF, defDMA); // Points the DMA offset back to the default + + if (user) { // If a user was selected + _ccp_bdos(F_USERNUM, curUser); // Set it back + user = 0; + } + _RamWrite(CmdFCB, drive); // Set the command FCB drive back to what it was + cDrive = oDrive; // And restore cDrive + + // Place a trampoline to call the external command + // as it may return using RET instead of JP 0000h + loadAddr = Trampoline; + _RamWrite(loadAddr, CALL); // CALL 0100h + _RamWrite16(loadAddr + 1, defLoad); + _RamWrite(loadAddr + 3, JP); // JP USERF + _RamWrite16(loadAddr + 4, BIOSjmppage + B_USERF); + + Z80reset(); // Resets the Z80 CPU + SET_LOW_REGISTER(BC, _RamRead(DSKByte)); // Sets C to the current drive/user + PC = loadAddr; // Sets CP/M application jump point + SP = BDOSjmppage; // Sets the stack to the top of the TPA + + Z80run(); // Starts Z80 simulation + + error = FALSE; + } + + if (user) // If a user was selected + _ccp_bdos(F_USERNUM, curUser); // Set it back + _RamWrite(CmdFCB, drive); // Set the command FCB drive back to what it was + + return(error); +} // _ccp_ext + +// Prints a command error +void _ccp_cmdError() { + uint8 ch; + + _puts("\r\n"); + while ((ch = _RamRead(perr++))) { + if (ch == ' ') + break; + _ccp_bdos(C_WRITE, toupper(ch)); + } + _puts("?\r\n"); +} // _ccp_cmdError + +// Reads input, either from the $$$.SUB or console +void _ccp_readInput(void) { + uint8 i; + uint8 chars; + + if (sFlag) { // Are we running a submit? + if (!sRecs) { // Are we already counting? + _ccp_bdos(F_OPEN, BatchFCB); // Open the batch file + sRecs = _RamRead(BatchFCB + 15); // Gets its record count + } + --sRecs; // Counts one less + _RamWrite(BatchFCB + 32, sRecs); // And sets to be the next read + _ccp_bdos( F_DMAOFF, defDMA); // Reset current DMA + _ccp_bdos( F_READ, BatchFCB); // And reads the last sector + chars = _RamRead(defDMA); // Then moves it to the input buffer + + for (i = 0; i <= chars; ++i) + _RamWrite(inBuf + i + 1, _RamRead(defDMA + i)); + _RamWrite(inBuf + i + 1, 0); + _puts((char *)_RamSysAddr(inBuf + 2)); + if (!sRecs) { + _ccp_bdos(F_DELETE, BatchFCB); // Deletes the submit file + sFlag = FALSE; // and clears the submit flag + } + } else { + _ccp_bdos(C_READSTR, inBuf); // Reads the command line from console + if (Debug) + Z80run(); + } +} // _ccp_readInput + +// Main CCP code +void _ccp(void) { + uint8 i; + + sFlag = (bool)_ccp_bdos(DRV_ALLRESET, 0x0000); + _ccp_bdos(DRV_SET, curDrive); + + for (i = 0; i < 36; ++i) + _RamWrite(BatchFCB + i, _RamRead(tmpFCB + i)); + + while (TRUE) { + curDrive = (uint8)_ccp_bdos(DRV_GET, 0x0000); // Get current drive + curUser = (uint8)_ccp_bdos(F_USERNUM, 0x00FF); // Get current user + _RamWrite(DSKByte, (curUser << 4) + curDrive); // Set user/drive on addr DSKByte + + parDrive = curDrive; // Initially the parameter drive is the same as the current drive + + sprintf((char *) prompt, "\r\n%c%u%c", 'A' + curDrive, curUser, sFlag ? '$' : '>'); + _puts((char *)prompt); + + _RamWrite(inBuf, cmdLen); // Sets the buffer size to read the command line + _ccp_readInput(); + + blen = _RamRead(inBuf + 1); // Obtains the number of bytes read + + _ccp_bdos(F_DMAOFF, defDMA); // Reset current DMA + if (blen) { + _RamWrite(inBuf + 2 + blen, 0); // "Closes" the read buffer with a \0 + pbuf = inBuf + 2; // Points pbuf to the first command character + + while (_RamRead(pbuf) == ' ' && blen) { // Skips any leading spaces + ++pbuf; + --blen; + } + if (!blen) // There were only spaces + continue; + if (_RamRead(pbuf) == ';') // Found a comment line + continue; + + // parse for DU: command line shortcut + bool errorFlag = FALSE, continueFlag = FALSE; + uint8 ch, tDrive = 0, tUser = curUser, u = 0; + + for (i = 0; i < blen; i++) { + ch = toupper(_RamRead(pbuf + i)); + if ((ch >= 'A') && (ch <= 'P')) { + if (tDrive) { // if we've already specified a new drive + break; // not a DU: command + } else { + tDrive = ch - '@'; + } + } else if ((ch >= '0') && (ch <= '9')) { + tUser = u = (u * 10) + (ch - '0'); + } else if (ch == ':') { + if (i == blen - 1) { // if we at the end of the command line + if (tUser >= 16) { // if invalid user + errorFlag = TRUE; + break; + } + if (tDrive != 0) { + cDrive = oDrive = tDrive - 1; + _RamWrite(DSKByte, (_RamRead(DSKByte) & 0xf0) | cDrive); + _ccp_bdos(DRV_SET, cDrive); + if (Status) + curDrive = 0; + } + if (tUser != curUser) { + curUser = tUser; + _ccp_bdos(F_USERNUM, curUser); + } + continueFlag = TRUE; + } + break; + } else { // invalid character + break; // don't error; may be valid (non-DU:) command + } + } + if (errorFlag) { + _ccp_cmdError(); // print command error + continue; + } + if (continueFlag) { + continue; + } + _ccp_initFCB(CmdFCB, 36); // Initializes the command FCB + + perr = pbuf; // Saves the pointer in case there's an error + if (_ccp_nameToFCB(CmdFCB) > 8) { // Extracts the command from the buffer + _ccp_cmdError(); // Command name cannot be non-unique or have an extension + continue; + } + _RamWrite(defDMA, blen); // Move the command line at this point to 0x0080 + + for (i = 0; i < blen; ++i) + _RamWrite(defDMA + i + 1, toupper(_RamRead(pbuf + i))); + while (i++ < 127) // "Zero" the rest of the DMA buffer + _RamWrite(defDMA + i, 0); + _ccp_initFCB( ParFCB, 18); // Initializes the parameter FCB + _ccp_initFCB( SecFCB, 18); // Initializes the secondary FCB + + while (_RamRead(pbuf) == ' ' && blen) { // Skips any leading spaces + ++pbuf; + --blen; + } + _ccp_nameToFCB(ParFCB); // Loads the next file parameter onto the parameter FCB + + while (_RamRead(pbuf) == ' ' && blen) { // Skips any leading spaces + ++pbuf; + --blen; + } + _ccp_nameToFCB(SecFCB); // Loads the next file parameter onto the secondary FCB + + i = FALSE; // Checks if the command is valid and executes + + switch (_ccp_cnum()) { + // Standard CP/M commands + case 0: { // DIR + _ccp_dir(); + break; + } + + case 1: { // ERA + _ccp_era(); + break; + } + + case 2: { // TYPE + i = _ccp_type(); + break; + } + + case 3: { // SAVE + i = _ccp_save(); + break; + } + + case 4: { // REN + _ccp_ren(); + break; + } + + case 5: { // USER + i = _ccp_user(); + break; + } + + // Extra CCP commands + case 6: { // CLS + _clrscr(); + break; + } + + case 7: { // DEL is an alias to ERA + _ccp_era(); + break; + } + + case 8: { // EXIT + _puts( "Terminating RunCPM.\r\n"); + _puts( "CPU Halted.\r\n"); + Status = 1; + break; + } + + case 9: { // PAGE + i = _ccp_page(); + break; + } + + case 10: { // VOL + i = _ccp_vol(); + break; + } + + case 11: { // HELP + i = _ccp_hlp(); + break; + } + + // External/Lua commands + case 255: { // It is an external command + i = _ccp_ext(); +#ifdef HASLUA + if (i) + i = _ccp_lua(); +#endif // ifdef HASLUA + break; + } + + default: { + i = TRUE; + break; + } + } // switch + cDrive = oDrive = curDrive; // Restore cDrive and oDrive + if (i) + _ccp_cmdError(); + } + if ((Status == 1) || (Status == 2)) + break; + } + _puts("\r\n"); +} // _ccp + +#endif // ifndef CCP_H diff --git a/runcpm-rp2350-hstx-usb/runcpm-pico/console.h b/runcpm-rp2350-hstx-usb/runcpm-pico/console.h new file mode 100644 index 000000000..1e9cf168b --- /dev/null +++ b/runcpm-rp2350-hstx-usb/runcpm-pico/console.h @@ -0,0 +1,64 @@ +// SPDX-FileCopyrightText: 2023 Mockba the Borg +// +// SPDX-License-Identifier: MIT + +#ifndef CONSOLE_H +#define CONSOLE_H + +extern int _kbhit(void); +uint8_t _getch(void); +void _putch(uint8 ch); + +/* see main.c for definition */ + +uint8 mask8bit = 0x7f; // TO be used for masking 8 bit characters (XMODEM related) + // If set to 0x7f, RunCPM masks the 8th bit of characters sent + // to the console. This is the standard CP/M behavior. + // If set to 0xff, RunCPM passes 8 bit characters. This is + // required for XMODEM to work. + // Use the CONSOLE7 and CONSOLE8 programs to change this on the fly. + +uint8 _chready(void) // Checks if there's a character ready for input +{ + return(_kbhit() ? 0xff : 0x00); +} + +uint8 _getchNB(void) // Gets a character, non-blocking, no echo +{ + return(_kbhit() ? _getch() : 0x00); +} + +void _putcon(uint8 ch) // Puts a character +{ + _putch(ch & mask8bit); +} + +void _puts(const char* str) // Puts a \0 terminated string +{ + while (*str) + _putcon(*(str++)); +} + +void _puthex8(uint8 c) // Puts a HH hex string +{ + _putcon(tohex(c >> 4)); + _putcon(tohex(c & 0x0f)); +} + +void _puthex16(uint16 w) // puts a HHHH hex string +{ + _puthex8(w >> 8); + _puthex8(w & 0x00ff); +} + +void _putdec(uint16_t w) { + char buf[] = " 0"; + size_t i=sizeof(buf)-1; + while(w) { + assert(i > 0); + buf[--i] = '0' + (w % 10); + w /= 10; + } + _puts(buf); +} +#endif diff --git a/runcpm-rp2350-hstx-usb/runcpm-pico/cpm.h b/runcpm-rp2350-hstx-usb/runcpm-pico/cpm.h new file mode 100644 index 000000000..b580a3cc9 --- /dev/null +++ b/runcpm-rp2350-hstx-usb/runcpm-pico/cpm.h @@ -0,0 +1,1782 @@ +// SPDX-FileCopyrightText: 2023 Mockba the Borg +// +// SPDX-License-Identifier: MIT + +#ifndef CPM_H +#define CPM_H + +enum eBIOSFunc { +// CP/M 2.2 Stuff + B_BOOT = 0, + B_WBOOT = 3, + B_CONST = 6, + B_CONIN = 9, + B_CONOUT = 12, + B_LIST = 15, + B_AUXOUT = 18, + B_READER = 21, + B_HOME = 24, + B_SELDSK = 27, + B_SETTRK = 30, + B_SETSEC = 33, + B_SETDMA = 36, + B_READ = 39, + B_WRITE = 42, + B_LISTST = 45, + B_SECTRAN = 48, +// CP/M 3.0 Stuff + B_CONOST = 51, + B_AUXIST = 54, + B_AUXOST = 57, + B_DEVTBL = 60, + B_DEVINI = 63, + B_DRVTBL = 66, + B_MULTIO = 69, + B_FLUSH = 72, + B_MOVE = 75, + B_TIME = 78, + B_SELMEM = 81, + B_SETBNK = 84, + B_XMOVE = 87, + B_USERF = 90, // Used by internal CCP to return to prompt + B_RESERV1 = 93, + B_RESERV2 = 96 +}; + +enum eBDOSFunc { +// CP/M 2.2 Stuff + P_TERMCPM = 0, + C_READ = 1, + C_WRITE = 2, + A_READ = 3, + A_WRITE = 4, + L_WRITE = 5, + C_RAWIO = 6, + A_STATIN = 7, + A_STATOUT = 8, + C_WRITESTR = 9, + C_READSTR = 10, + C_STAT = 11, + S_BDOSVER = 12, + DRV_ALLRESET = 13, + DRV_SET = 14, + F_OPEN = 15, + F_CLOSE = 16, + F_SFIRST = 17, + F_SNEXT = 18, + F_DELETE = 19, + F_READ = 20, + F_WRITE = 21, + F_MAKE = 22, + F_RENAME = 23, + DRV_LOGINVEC = 24, + DRV_GET = 25, + F_DMAOFF = 26, + DRV_ALLOCVEC = 27, + DRV_SETRO = 28, + DRV_ROVEC = 29, + F_ATTRIB = 30, + DRV_PDB = 31, + F_USERNUM = 32, + F_READRAND = 33, + F_WRITERAND = 34, + F_SIZE = 35, + F_RANDREC = 36, + DRV_RESET = 37, + DRV_ACCESS = 38, // This is an MP/M function that is not supported under CP/M 3. + DRV_FREE = 39, // This is an MP/M function that is not supported under CP/M 3. + F_WRITEZF = 40, +// CP/M 3.0 Stuff + F_TESTWRITE = 41, + F_LOCKFILE = 42, + F_UNLOCKFILE = 43, + F_MULTISEC = 44, + F_ERRMODE = 45, + DRV_SPACE = 46, + P_CHAIN = 47, + DRV_FLUSH = 48, + S_SCB = 49, + S_BIOS = 50, + P_LOAD = 59, + S_RSX = 60, + F_CLEANUP = 98, + F_TRUNCATE = 99, + DRV_SETLABEL = 100, + DRV_GETLABEL = 101, + F_TIMEDATE = 102, + F_WRITEXFCB = 103, + T_SET = 104, + T_GET = 105, + F_PASSWD = 106, + S_SERIAL = 107, + P_CODE = 108, + C_MODE = 109, + C_DELIMIT = 110, + C_WRITEBLK = 111, + L_WRITEBLK = 112, + F_PARSE = 152, +// RunCPM Stuff + F_RUNLUA = 254 +}; + +/* see main.c for definition */ + +#define JP 0xc3 +#define CALL 0xcd +#define RET 0xc9 +#define INa 0xdb // Triggers a BIOS call +#define OUTa 0xd3 // Triggers a BDOS call + +/* set up full PUN and LST filenames to be on drive A: user 0 */ +#ifdef USE_PUN +char pun_file[17] = {'A', FOLDERCHAR, '0', FOLDERCHAR, 'P', 'U', 'N', '.', 'T', 'X', 'T', 0}; + +#endif // ifdef USE_PUN +#ifdef USE_LST +char lst_file[17] = {'A', FOLDERCHAR, '0', FOLDERCHAR, 'L', 'S', 'T', '.', 'T', 'X', 'T', 0}; + +#endif // ifdef USE_LST + +#ifdef PROFILE +unsigned long time_start = 0; +unsigned long time_now = 0; + +#endif // ifdef PROFILE + +void _PatchCPM(void) { + uint16 i; + + // ********** Patch CP/M page zero into the memory ********** + + /* BIOS entry point */ + _RamWrite(0x0000, JP); /* JP BIOS+3 (warm boot) */ + _RamWrite16(0x0001, BIOSjmppage + 3); + if (Status != 2) { + /* IOBYTE - Points to Console */ + _RamWrite( IOByte, 0x3D); + + /* Current drive/user - A:/0 */ + _RamWrite( DSKByte, 0x00); + } + /* BDOS entry point (0x0005) */ + _RamWrite(0x0005, JP); + _RamWrite16(0x0006, BDOSjmppage + 0x06); + + // ********** Patch CP/M Version into the memory so the CCP can see it + _RamWrite16(BDOSjmppage, 0x1600); + _RamWrite16(BDOSjmppage + 2, 0x0000); + _RamWrite16(BDOSjmppage + 4, 0x0000); + + // Patches in the BDOS jump destination + _RamWrite( BDOSjmppage + 6, JP); + _RamWrite16(BDOSjmppage + 7, BDOSpage); + + // Patches in the BDOS page content + _RamWrite( BDOSpage, INa); + _RamWrite( BDOSpage + 1, 0xFF); + _RamWrite( BDOSpage + 2, RET); + + // Patches in the BIOS jump destinations + for (i = 0; i < 99; i = i + 3) { + _RamWrite( BIOSjmppage + i, JP); + _RamWrite16(BIOSjmppage + i + 1, BIOSpage + i); + } + + // Patches in the BIOS page content + for (i = 0; i < 99; i = i + 3) { + _RamWrite( BIOSpage + i, OUTa); + _RamWrite( BIOSpage + i + 1, 0xFF); + _RamWrite( BIOSpage + i + 2, RET); + } + // ********** Patch CP/M (fake) Disk Paramater Block after the BDOS call entry ********** + i = DPBaddr; + _RamWrite( i++, 64); // spt - Sectors Per Track + _RamWrite( i++, 0); + _RamWrite( i++, 5); // bsh - Data allocation "Block Shift Factor" + _RamWrite( i++, 0x1F); // blm - Data allocation Block Mask + _RamWrite( i++, 1); // exm - Extent Mask + _RamWrite( i++, 0xFF); // dsm - Total storage capacity of the disk drive + _RamWrite( i++, 0x07); + _RamWrite( i++, 255); // drm - Number of the last directory entry + _RamWrite( i++, 3); + _RamWrite( i++, 0xFF); // al0 + _RamWrite( i++, 0x00); // al1 + _RamWrite( i++, 0); // cks - Check area Size + _RamWrite( i++, 0); + _RamWrite( i++, 0x02); // off - Number of system reserved tracks at the beginning of the ( logical ) disk + _RamWrite( i++, 0x00); + blockShift = _RamRead(DPBaddr + 2); + blockMask = _RamRead(DPBaddr + 3); + extentMask = _RamRead(DPBaddr + 4); + numAllocBlocks = _RamRead16(DPBaddr + 5) + 1; + extentsPerDirEntry = extentMask + 1; + + // ********** Patch CP/M (fake) Disk Parameter Header after the Disk Parameter Block ********** + _RamWrite( i++, 0); // Addr of the sector translation table + _RamWrite( i++, 0); + _RamWrite( i++, 0); // Workspace + _RamWrite( i++, 0); + _RamWrite( i++, 0); + _RamWrite( i++, 0); + _RamWrite( i++, 0); + _RamWrite( i++, 0); + _RamWrite( i++, 0x80); // Addr of the Sector Buffer + _RamWrite( i++, 0); + _RamWrite( i++, LOW_REGISTER(DPBaddr)); // Addr of the DPB Disk Parameter Block + _RamWrite( i++, HIGH_REGISTER(DPBaddr)); + _RamWrite( i++, 0); // Addr of the Directory Checksum Vector + _RamWrite( i++, 0); + _RamWrite( i++, 0); // Addr of the Allocation Vector + _RamWrite( i++, 0); + + // + + // figure out the number of the first allocation block + // after the directory for the phoney allocation block + // list in _findnext() + firstBlockAfterDir = 0; + i = 0x80; + + while (_RamRead(DPBaddr + 9) & i) { + firstBlockAfterDir++; + i >>= 1; + } + if (_RamRead(DPBaddr + 9) == 0xFF) { + i = 0x80; + + while (_RamRead(DPBaddr + 10) & i) { + firstBlockAfterDir++; + i >>= 1; + } + } + physicalExtentBytes = logicalExtentBytes * (extentMask + 1); +} // _PatchCPM + +#ifdef DEBUGLOG +uint8 LogBuffer[128]; + +void _logRegs(void) { + uint8 J, I; + uint8 Flags[9] = {'S', 'Z', '5', 'H', '3', 'P', 'N', 'C'}; + uint8 c = HIGH_REGISTER(AF); + + if ((c < 32) || (c > 126)) + c = 46; + + for (J = 0, I = LOW_REGISTER(AF); J < 8; ++J, I <<= 1) + Flags[J] = I & 0x80 ? Flags[J] : '.'; + sprintf((char *)LogBuffer, " BC:%04x DE:%04x HL:%04x AF:%02x(%c)|%s| IX:%04x IY:%04x SP:%04x PC:%04x\n", + WORD16(BC), WORD16(DE), WORD16(HL), HIGH_REGISTER(AF), c, Flags, WORD16(IX), WORD16(IY), WORD16(SP), WORD16(PC)); + _sys_logbuffer(LogBuffer); +} // _logRegs + +void _logMem(uint16 address, uint8 amount) { // Amount = number of 16 bytes lines, so 1 CP/M block = 8, not 128 + uint8 i, m, c, pos; + uint8 head = 8; + uint8 hexa[] = "0123456789ABCDEF"; + + for (i = 0; i < amount; ++i) { + pos = 0; + + for (m = 0; m < head; ++m) + LogBuffer[pos++] = ' '; + sprintf((char *)LogBuffer, " %04x: ", address); + + for (m = 0; m < 16; ++m) { + c = _RamRead(address++); + LogBuffer[pos++] = hexa[c >> 4]; + LogBuffer[pos++] = hexa[c & 0x0f]; + LogBuffer[pos++] = ' '; + LogBuffer[m + head + 48] = c > 31 && c < 127 ? c : '.'; + } + pos += 16; + LogBuffer[pos++] = 0x0a; + LogBuffer[pos++] = 0x00; + _sys_logbuffer(LogBuffer); + } +} // _logMem + +void _logChar(char *txt, uint8 c) { + uint8 asc[2]; + + asc[0] = c > 31 && c < 127 ? c : '.'; + asc[1] = 0; + sprintf((char *)LogBuffer, " %s = %02xh:%3d (%s)\n", txt, c, c, asc); + _sys_logbuffer(LogBuffer); +} // _logChar + +void _logBiosIn(uint8 ch) { +#ifdef LOGBIOS_NOT + if (ch == LOGBIOS_NOT) + return; +#endif // ifdef LOGBIOS_NOT +#ifdef LOGBIOS_ONLY + if (ch != LOGBIOS_ONLY) + return; +#endif // ifdef LOGBIOS_ONLY + static const char *BIOSCalls[33] = + { + "boot", "wboot", "const", "conin", "conout", "list", "punch/aux", "reader", "home", "seldsk", "settrk", "setsec", "setdma", + "read", "write", "listst", "sectran", "conost", "auxist", "auxost", "devtbl", "devini", "drvtbl", "multio", "flush", "move", + "time", "selmem", "setbnk", "xmove", "userf", "reserv1", "reserv2" + }; + int index = ch / 3; + + if (index < 18) { + sprintf((char *)LogBuffer, "\nBios call: %3d/%02xh (%s) IN:\n", ch, ch, BIOSCalls[index]); + _sys_logbuffer(LogBuffer); + } else { + sprintf((char *)LogBuffer, "\nBios call: %3d/%02xh IN:\n", ch, ch); + _sys_logbuffer(LogBuffer); + } + _logRegs(); +} // _logBiosIn + +void _logBiosOut(uint8 ch) { +#ifdef LOGBIOS_NOT + if (ch == LOGBIOS_NOT) + return; +#endif // ifdef LOGBIOS_NOT +#ifdef LOGBIOS_ONLY + if (ch != LOGBIOS_ONLY) + return; +#endif // ifdef LOGBIOS_ONLY + sprintf((char *)LogBuffer, " OUT:\n"); + _sys_logbuffer(LogBuffer); + _logRegs(); +} // _logBiosOut + +void _logBdosIn(uint8 ch) { +#ifdef LOGBDOS_NOT + if (ch == LOGBDOS_NOT) + return; +#endif // ifdef LOGBDOS_NOT +#ifdef LOGBDOS_ONLY + if (ch != LOGBDOS_ONLY) + return; +#endif // ifdef LOGBDOS_ONLY + uint16 address = 0; + uint8 size = 0; + + static const char *CPMCalls[41] = + { + "System Reset", "Console Input", "Console Output", "Reader Input", "Punch Output", "List Output", "Direct I/O", + "Get IOByte", "Set IOByte", "Print String", "Read Buffered", "Console Status", "Get Version", "Reset Disk", + "Select Disk", "Open File", "Close File", "Search First", "Search Next", "Delete File", "Read Sequential", + "Write Sequential", "Make File", "Rename File", "Get Login Vector", "Get Current Disk", "Set DMA Address", + "Get Alloc", "Write Protect Disk", "Get R/O Vector", "Set File Attr", "Get Disk Params", "Get/Set User", + "Read Random", "Write Random", "Get File Size", "Set Random Record", "Reset Drive", "N/A", "N/A", + "Write Random 0 fill" + }; + + if (ch < 41) { + sprintf((char *)LogBuffer, "\nBdos call: %3d/%02xh (%s) IN from 0x%04x:\n", ch, ch, CPMCalls[ch], _RamRead16(SP) - 3); + _sys_logbuffer(LogBuffer); + } else { + sprintf((char *)LogBuffer, "\nBdos call: %3d/%02xh IN from 0x%04x:\n", ch, ch, _RamRead16(SP) - 3); + _sys_logbuffer(LogBuffer); + } + _logRegs(); + + switch (ch) { + case 2: + case 4: + case 5: + case 6: { + _logChar("E", LOW_REGISTER(DE)); + break; + } + + case 9: + case 10: { + address = DE; + size = 8; + break; + } + + case 15: + case 16: + case 17: + case 18: + case 19: + case 22: + case 23: + case 30: + case 35: + case 36: { + address = DE; + size = 3; + break; + } + + case 20: + case 21: + case 33: + case 34: + case 40: { + address = DE; + size = 3; + _logMem(address, size); + sprintf((char *)LogBuffer, "\n"); + _sys_logbuffer(LogBuffer); + address = dmaAddr; + size = 8; + break; + } + + default: { + break; + } + } // switch + if (size) + _logMem(address, size); +} // _logBdosIn + +void _logBdosOut(uint8 ch) { +#ifdef LOGBDOS_NOT + if (ch == LOGBDOS_NOT) + return; +#endif // ifdef LOGBDOS_NOT +#ifdef LOGBDOS_ONLY + if (ch != LOGBDOS_ONLY) + return; +#endif // ifdef LOGBDOS_ONLY + uint16 address = 0; + uint8 size = 0; + + sprintf((char *)LogBuffer, " OUT:\n"); + _sys_logbuffer(LogBuffer); + _logRegs(); + + switch (ch) { + case 1: + case 3: + case 6: { + _logChar("A", HIGH_REGISTER(AF)); + break; + } + + case 10: { + address = DE; + size = 8; + break; + } + + case 20: + case 21: + case 33: + case 34: + case 40: { + address = DE; + size = 3; + _logMem(address, size); + sprintf((char *)LogBuffer, "\n"); + _sys_logbuffer(LogBuffer); + address = dmaAddr; + size = 8; + break; + } + + case 26: { + address = dmaAddr; + size = 8; + break; + } + + case 35: + case 36: { + address = DE; + size = 3; + break; + } + + default: { + break; + } + } // switch + if (size) + _logMem(address, size); +} // _logBdosOut +#endif // ifdef DEBUGLOG + +void _Bios(void) { + uint8 ch = LOW_REGISTER(PCX); + uint8 disk[2] = {'A', 0}; + +#ifdef DEBUGLOG + _logBiosIn(ch); +#endif + + switch (ch) { + case B_BOOT: { + Status = 1; // 0 - Ends RunCPM + break; + } + case B_WBOOT: { + Status = 2; // 1 - Back to CCP + break; + } + case B_CONST: { // 2 - Console status + SET_HIGH_REGISTER(AF, _chready()); + break; + } + case B_CONIN: { // 3 - Console input + SET_HIGH_REGISTER(AF, _getch()); +#ifdef DEBUG + if (HIGH_REGISTER(AF) == 4) + Debug = 1; +#endif // ifdef DEBUG + break; + } + case B_CONOUT: { // 4 - Console output + _putcon(LOW_REGISTER(BC)); + break; + } + case B_LIST: { // 5 - List output + break; + } + case B_AUXOUT: { // 6 - Aux/Punch output + break; + } + case B_READER: { // 7 - Reader input (returns 0x1a = device not implemented) + SET_HIGH_REGISTER(AF, 0x1a); + break; + } + case B_HOME: { // 8 - Home disk head + break; + } + case B_SELDSK: { // 9 - Select disk drive + disk[0] += LOW_REGISTER(BC); + HL = 0x0000; + if (_sys_select(&disk[0])) + HL = DPHaddr; + break; + } + case B_SETTRK: { // 10 - Set track number + break; + } + case B_SETSEC: { // 11 - Set sector number + break; + } + case B_SETDMA: { // 12 - Set DMA address + HL = BC; + dmaAddr = BC; + break; + } + case B_READ: { // 13 - Read selected sector + SET_HIGH_REGISTER(AF, 0x00); + break; + } + case B_WRITE: { // 14 - Write selected sector + SET_HIGH_REGISTER(AF, 0x00); + break; + } + case B_LISTST: { // 15 - Get list device status + SET_HIGH_REGISTER(AF, 0x0ff); + break; + } + case B_SECTRAN: { // 16 - Sector translate + HL = BC; // HL=BC=No translation (1:1) + break; + } + case B_CONOST: { // 17 - Return status of current screen output device + SET_HIGH_REGISTER(AF, 0x0ff); + break; + } + case B_AUXIST: { // 18 - Return status of current auxiliary input device + SET_HIGH_REGISTER(AF, 0x00); + break; + } + case B_AUXOST: { // 19 - Return status of current auxiliary output device + SET_HIGH_REGISTER(AF, 0x00); + break; + } + case B_DEVTBL: { // 20 - Return the address of the devices table, or 0 if not implemented + HL = 0x0000; + break; + } + case B_DEVINI: { // 21 - Reinitialise character device number C + break; + } + case B_DRVTBL: { // 22 - Return the address of the drive table + HL = 0x0FFFF; + break; + } + case B_MULTIO: { // 23 - Notify the BIOS of multi sector transfer + break; + } + case B_FLUSH: { // 24 - Write any pending data to disc + SET_HIGH_REGISTER(AF, 0x00); + break; + } + case B_MOVE: { // 25 - Move a block of memory + if (!isXmove) + srcBank = dstBank = curBank; + while (BC--) + RAM[HL++ * dstBank] = RAM[DE++ * srcBank]; + isXmove = FALSE; + break; + } + case B_TIME: { // 26 - Get/Set SCB time + break; + } + case B_SELMEM: { // 27 - Select memory bank + curBank = HIGH_REGISTER(AF); + break; + } + case B_SETBNK: { // 28 - Set the bank to be used for the next read/write sector operation + ioBank = HIGH_REGISTER(AF); + } + case B_XMOVE: { // 29 - Preload banks for MOVE + srcBank = LOW_REGISTER(BC); + dstBank = HIGH_REGISTER(BC); + isXmove = TRUE; + break; + } + case B_USERF: { // 30 - This allows programs ending in RET return to internal CCP + Status = 3; + break; + } + case B_RESERV1: + case B_RESERV2: { + break; + } + default: { +#ifdef DEBUG // Show unimplemented BIOS calls only when debugging + _puts("\r\nUnimplemented BIOS call.\r\n"); + _puts("C = 0x"); + _puthex8(ch); + _puts("\r\n"); +#endif // ifdef DEBUG + break; + } + } // switch +#ifdef DEBUGLOG + _logBiosOut(ch); +#endif +} // _Bios + +void _Bdos(void) { + uint16 i; + uint8 j, chr, ch = LOW_REGISTER(BC); + +#ifdef DEBUGLOG + _logBdosIn(ch); +#endif + + HL = 0x0000; // HL is reset by the BDOS + SET_LOW_REGISTER(BC, LOW_REGISTER(DE)); // C ends up equal to E + + switch (ch) { + /* + C = 0 : System reset + Doesn't return. Reloads CP/M + */ + case P_TERMCPM: { + Status = 2; // Same as call to "BOOT" + break; + } + + /* + C = 1 : Console input + Gets a char from the console + Returns: A=Char + */ + case C_READ: { + HL = _getche(); +#ifdef DEBUG + if (HL == 4) + Debug = 1; +#endif // ifdef DEBUG + break; + } + + /* + C = 2 : Console output + E = Char + Sends the char in E to the console + */ + case C_WRITE: { + _putcon(LOW_REGISTER(DE)); + break; + } + + /* + C = 3 : Auxiliary (Reader) input + Returns: A=Char + */ + case A_READ: { + HL = 0x1a; + break; + } + + /* + C = 4 : Auxiliary (Punch) output + */ + case A_WRITE: { +#ifdef USE_PUN + if (!pun_open) { + pun_dev = _sys_fopen_w((uint8 *)pun_file); + pun_open = TRUE; + } + if (pun_dev) { + _sys_fputc(LOW_REGISTER(DE), pun_dev); + } +#endif // ifdef USE_PUN + break; + } + + /* + C = 5 : Printer output + */ + case L_WRITE: { +#ifdef USE_LST + if (!lst_open) { + lst_dev = _sys_fopen_w((uint8 *)lst_file); + lst_open = TRUE; + } + if (lst_dev) + _sys_fputc(LOW_REGISTER(DE), lst_dev); +#endif // ifdef USE_LST + break; + } + + /* + C = 6 : Direct console IO + E = 0xFF : Checks for char available and returns it, or 0x00 if none (read) + ToDo E = 0xFE : Return console input status. Zero if no character is waiting, nonzero otherwise. (CPM3) + ToDo E = 0xFD : Wait until a character is ready, return it without echoing. (CPM3) + E = char : Outputs char (write) + Returns: A=Char or 0x00 (on read) + */ + case C_RAWIO: { + if (LOW_REGISTER(DE) == 0xff) { + HL = _getchNB(); +#ifdef DEBUG + if (HL == 4) + Debug = 1; +#endif // ifdef DEBUG + } else { + _putcon(LOW_REGISTER(DE)); + } + break; + } + + /* + C = 7 : Get IOBYTE (CPM2) + Gets the system IOBYTE + Returns: A = IOBYTE (CPM2) + ToDo REPLACE with + C = 7 : Auxiliary Input status (CPM3) + 0FFh is returned if the Auxiliary Input device has a character ready; otherwise 0 is returned. + Returns: A=0 or 0FFh (CPM3) + */ + case A_STATIN: { + HL = _RamRead(0x0003); + break; + } + + /* + C = 8 : Set IOBYTE (CPM2) + E = IOBYTE + Sets the system IOBYTE to E + ToDo REPLACE with + C = 8 : Auxiliary Output status (CPM3) + 0FFh is returned if the Auxiliary Output device is ready for characters; otherwise 0 is returned. + Returns: A=0 or 0FFh (CPM3) + */ + case A_STATOUT: { + _RamWrite(0x0003, LOW_REGISTER(DE)); + break; + } + + /* + C = 9 : Output string + DE = Address of string + Sends the $ terminated string pointed by (DE) to the screen + */ + case C_WRITESTR: { + while ((ch = _RamRead(DE++)) != '$') + _putcon(ch); + break; + } + + /* + C = 10 (0Ah) : Buffered input + DE = Address of buffer + ToDo DE = 0 Use DMA address (CPM3) AND + DE=address: DE=0: + buffer: DEFB size buffer: DEFB size + DEFB ? DEFB len + bytes bytes + Reads (DE) bytes from the console + Returns: A = Number of chars read + DE) = First char + */ + case C_READSTR: { + uint16 chrsMaxIdx = WORD16(DE); //index to max number of characters + uint16 chrsCntIdx = (chrsMaxIdx + 1) & 0xFFFF; //index to number of characters read + uint16 chrsIdx = (chrsCntIdx + 1) & 0xFFFF; //index to characters + //printf("\n\r chrsMaxIdx: %0X, chrsCntIdx: %0X", chrsMaxIdx, chrsCntIdx); + + static uint8 *last = 0; + if (!last) + last = (uint8*)calloc(1,256); //allocate one (for now!) + +#ifdef PROFILE + if (time_start != 0) { + time_now = millis(); + printf(": %ld\n", time_now - time_start); + time_start = 0; + } +#endif // ifdef PROFILE + uint8 chrsMax = _RamRead(chrsMaxIdx); // Gets the max number of characters that can be read + uint8 chrsCnt = 0; // this is the number of characters read + uint8 curCol = 0; //this is the cursor column (relative to where it started) + + while (chrsMax) { + // pre-backspace, retype & post backspace counts + uint8 preBS = 0, reType = 0, postBS = 0; + + chr = _getch(); //input a character + + if (chr == 1) { // ^A - Move cursor one character to the left + if (curCol > 0) { + preBS++; //backspace one + } else { + _putcon('\007'); //ring the bell + } + } + + if (chr == 2) { // ^B - Toggle between beginning & end of line + if (curCol) { + preBS = curCol; //move to beginning + } else { + reType = chrsCnt - curCol; //move to EOL + } + } + + if ((chr == 3) && (chrsCnt == 0)) { // ^C - Abort string input + _puts("^C"); + Status = 2; + break; + } + +#ifdef DEBUG + if (chr == 4) { // ^D - DEBUG + Debug = 1; + break; + } +#endif // ifdef DEBUG + + if (chr == 5) { // ^E - goto beginning of next line + _putcon('\n'); + preBS = curCol; + reType = postBS = chrsCnt; + } + + if (chr == 6) { // ^F - Move the cursor one character forward + if (curCol < chrsCnt) { + reType++; + } else { + _putcon('\007'); //ring the bell + } + } + + if (chr == 7) { // ^G - Delete character at cursor + if (curCol < chrsCnt) { + //delete this character from buffer + for (i = curCol, j = i + 1; j < chrsCnt; i++, j++) { + ch = _RamRead(((chrsIdx + j) & 0xFFFF)); + _RamWrite((chrsIdx + i) & 0xFFFF, ch); + } + reType = postBS = chrsCnt - curCol; + chrsCnt--; + } else { + _putcon('\007'); //ring the bell + } + } + + if (((chr == 0x08) || (chr == 0x7F))) { // ^H and DEL - Delete one character to left of cursor + if (curCol > 0) { //not at BOL + if (curCol < chrsCnt) { //not at EOL + //delete previous character from buffer + for (i = curCol, j = i - 1; i < chrsCnt; i++, j++) { + ch = _RamRead(((chrsIdx + i) & 0xFFFF)); + _RamWrite((chrsIdx + j) & 0xFFFF, ch); + } + preBS++; //pre-backspace one + //note: does one extra to erase EOL + reType = postBS = chrsCnt - curCol + 1; + } else { + preBS = reType = postBS = 1; + } + chrsCnt--; + } else { + _putcon('\007'); //ring the bell + } + } + + if ((chr == 0x0A) || (chr == 0x0D)) { // ^J and ^M - Ends editing +#ifdef PROFILE + time_start = millis(); +#endif + break; + } + + if (chr == 0x0B) { // ^K - Delete to EOL from cursor + if (curCol < chrsCnt) { + reType = postBS = chrsCnt - curCol; + chrsCnt = curCol; //truncate buffer to here + } else { + _putcon('\007'); //ring the bell + } + } + + if (chr == 18) { // ^R - Retype the command line + _puts("#\b\n"); + preBS = curCol; //backspace to BOL + reType = chrsCnt; //retype everything + postBS = chrsCnt - curCol; //backspace to cursor column + } + + if (chr == 21) { // ^U - delete all characters + _puts("#\b\n"); + preBS = curCol; //backspace to BOL + chrsCnt = 0; + } + + if (chr == 23) { // ^W - recall last command + if (!curCol) { //if at beginning of command line + uint8 lastCnt = last[0]; + if (lastCnt) { //and there's a last command + //restore last command + for (j = 0; j <= lastCnt; j++) { + _RamWrite((chrsCntIdx + j) & 0xFFFF, last[j]); + } + //retype to greater of chrsCnt & lastCnt + reType = (chrsCnt > lastCnt) ? chrsCnt : lastCnt; + chrsCnt = lastCnt; //this is the restored length + //backspace to end of restored command + postBS = reType - chrsCnt; + } else { + _putcon('\007'); //ring the bell + } + } else if (curCol < chrsCnt) { //if not at EOL + reType = chrsCnt - curCol; //move to EOL + } + } + + if (chr == 24) { // ^X - delete all character left of the cursor + if (curCol > 0) { + //move rest of line to beginning of line + for (i = 0, j = curCol; j < chrsCnt;i++, j++) { + ch = _RamRead(((chrsIdx + j) & 0xFFFF)); + _RamWrite((chrsIdx +i) & 0xFFFF, ch); + } + preBS = curCol; + reType = chrsCnt; + postBS = chrsCnt; + chrsCnt -= curCol; + } else { + _putcon('\007'); //ring the bell + } + } + + if ((chr >= 0x20) && (chr <= 0x7E)) { //valid character + if (curCol < chrsCnt) { + //move rest of buffer one character right + for (i = chrsCnt, j = i - 1; i > curCol; i--, j--) { + ch = _RamRead(((chrsIdx + j) & 0xFFFF)); + _RamWrite((chrsIdx + i) & 0xFFFF, ch); + } + } + //put the new character in the buffer + _RamWrite((chrsIdx + curCol) & 0xffff, chr); + + chrsCnt++; + reType = chrsCnt - curCol; + postBS = reType - 1; + } + + //pre-backspace + for (i = 0; i < preBS; i++) { + _putcon('\b'); + curCol--; + } + + //retype + for (i = 0; i < reType; i++) { + if (curCol < chrsCnt) { + ch = _RamRead(((chrsIdx + curCol) & 0xFFFF)); + } else { + ch = ' '; + } + _putcon(ch); + curCol++; + } + + //post-backspace + for (i = 0; i < postBS; i++) { + _putcon('\b'); + curCol--; + } + + if (chrsCnt == chrsMax) // Reached the maximum count + break; + } // while (chrsMax) + + // Save the number of characters read + _RamWrite(chrsCntIdx, chrsCnt); + + //if there are characters... + if (chrsCnt) { + //... then save this as last command + for (j = 0; j <= chrsCnt; j++) { + last[j] = _RamRead((chrsCntIdx + j) & 0xFFFF); + } + } +#if 0 + printf("\n\r chrsMaxIdx: %0X, chrsMax: %u, chrsCnt: %u", chrsMaxIdx, chrsMax, chrsCnt); + for (j = 0; j < chrsCnt + 2; j++) { + printf("\n\r chrsMaxIdx[%u]: %0.2x", j, last[j]); + } +#endif + _putcon('\r'); // Gives a visual feedback that read ended + break; + } + + /* + C = 11 (0Bh) : Get console status + Returns: A=0x00 or 0xFF + */ + case C_STAT: { + HL = _chready(); + break; + } + + /* + C = 12 (0Ch) : Get version number + Returns: B=H=system type, A=L=version number + */ + case S_BDOSVER: { + HL = 0x22; + break; + } + + /* + C = 13 (0Dh) : Reset disk system + */ + case DRV_ALLRESET: { + roVector = 0; // Make all drives R/W + loginVector = 0; + dmaAddr = 0x0080; + cDrive = 0; // userCode remains unchanged + HL = _CheckSUB(); // Checks if there's a $$$.SUB on the boot disk + break; + } + + /* + C = 14 (0Eh) : Select Disk + Returns: A=0x00 or 0xFF + */ + case DRV_SET: { + oDrive = cDrive; + cDrive = LOW_REGISTER(DE); + HL = _SelectDisk(LOW_REGISTER(DE) + 1); // +1 here is to allow SelectDisk to be used directly by disk.h as well + if (!HL) { + oDrive = cDrive; + } else { + if ((_RamRead(DSKByte) & 0x0f) == cDrive) { + cDrive = oDrive = 0; + _RamWrite(DSKByte, _RamRead(DSKByte) & 0xf0); + } else { + cDrive = oDrive; + } + } + break; + } + + /* + C = 15 (0Fh) : Open file + Returns: A=0x00 or 0xFF + */ + case F_OPEN: { + HL = _OpenFile(DE); + break; + } + + /* + C = 16 (10h) : Close file + */ + case F_CLOSE: { + HL = _CloseFile(DE); + break; + } + + /* + C = 17 (11h) : Search for first + */ + case F_SFIRST: { + HL = _SearchFirst(DE, TRUE); // TRUE = Creates a fake dir entry when finding the file + break; + } + + /* + C = 18 (12h) : Search for next + */ + case F_SNEXT: { + HL = _SearchNext(DE, TRUE); // TRUE = Creates a fake dir entry when finding the file + break; + } + + /* + C = 19 (13h) : Delete file + */ + case F_DELETE: { + HL = _DeleteFile(DE); + break; + } + + /* + C = 20 (14h) : Read sequential + DE = address of FCB + ToDo under CP/M 3 this can be a multiple of 128 bytes + Returns: A = return code + */ + case F_READ: { + HL = _ReadSeq(DE); + break; + } + + /* + C = 21 (15h) : Write sequential + DE = address of FCB + ToDo under CP/M 3 this can be a multiple of 128 bytes + Returns: A=return code + */ + case F_WRITE: { + HL = _WriteSeq(DE); + break; + } + + /* + C = 22 (16h) : Make file + */ + case F_MAKE: { + HL = _MakeFile(DE); + break; + } + + /* + C = 23 (17h) : Rename file + */ + case F_RENAME: { + HL = _RenameFile(DE); + break; + } + + /* + C = 24 (18h) : Return log-in vector (active drive map) + */ + case DRV_LOGINVEC: { + HL = loginVector; // (todo) improve this + break; + } + + /* + C = 25 (19h) : Return current disk + */ + case DRV_GET: { + HL = cDrive; + break; + } + + /* + C = 26 (1Ah) : Set DMA address + */ + case F_DMAOFF: { + dmaAddr = DE; + break; + } + + /* + C = 27 (1Bh) : Get ADDR(Alloc) + */ + case DRV_ALLOCVEC: { + HL = SCBaddr; + break; + } + + /* + C = 28 (1Ch) : Write protect current disk + */ + case DRV_SETRO: { + roVector = roVector | (1 << cDrive); + break; + } + + /* + C = 29 (1Dh) : Get R/O vector + */ + case DRV_ROVEC: { + HL = roVector; + break; + } + + /* + C = 30 (1Eh) : Set file attributes (does nothing) + */ + case F_ATTRIB: { + HL = 0; + break; + } + + /* + C = 31 (1Fh) : Get ADDR(Disk Parms) + */ + case DRV_PDB: { + HL = DPBaddr; + break; + } + + /* + C = 32 (20h) : Get/Set user code + */ + case F_USERNUM: { + if (LOW_REGISTER(DE) == 0xFF) { + HL = userCode; + } else { + _SetUser(DE); + } + break; + } + + /* + C = 33 (21h) : Read random + ToDo under CPM3, if A returns 0xFF, H returns hardware error + */ + case F_READRAND: { + HL = _ReadRand(DE); + break; + } + + /* + C = 34 (22h) : Write random + ToDo under CPM3, if A returns 0xFF, H returns hardware error + */ + case F_WRITERAND: { + HL = _WriteRand(DE); + break; + } + + /* + C = 35 (23h) : Compute file size + */ + case F_SIZE: { + HL = _GetFileSize(DE); + break; + } + + /* + C = 36 (24h) : Set random record + */ + case F_RANDREC: { + HL = _SetRandom(DE); + break; + } + + /* + C = 37 (25h) : Reset drive + */ + case DRV_RESET: { + roVector = roVector & ~DE; + break; + } + + /* ********* Function 38: Not supported by CP/M 2.2 ********* + ********* Function 39: Not supported by CP/M 2.2 ********* + ********* (todo) Function 40: Write random with zero fill ********* + */ + + /* + ToDo C = 38 (26h) : Access drives (CPM3) + This is an MP/M function that is not supported under CP/M 3. If called, the file + system returns a zero In register A indicating that the access request is successful. + */ + case DRV_ACCESS: { + break; + } + + /* + ToDo C = 39 (27h) : Free drives (CPM3) + This is an MP/M function that is not supported under CP/M 3. If called, the file + system returns a zero In register A indicating that the access request is successful. + */ + case DRV_FREE: { + break; + } + + /* + C = 40 (28h) : Write random with zero fill (we have no disk blocks, so just write random) + DE = address of FCB + Returns: A = return code + H = Physical Error + */ + case F_WRITEZF: { + HL = _WriteRand(DE); + break; + } + + /* + ToDo: C = 41 (29h) : Test and Write Record (CPM3) + DE = address of FCB + Returns: A = return code + H = Physical Error + */ + case F_TESTWRITE: { + break; + } + + /* + ToDo: C = 42 (2Ah) : Lock Record (CPM3) + DE = address of FCB + Returns: A = return code + H = Physical Error + */ + case F_LOCKFILE: { + break; + } + + /* + ToDo: C = 43 (2Bh) : Unlock Record (CPM3) + DE = address of FCB + Returns: A = return code + H = Physical Error + */ + case F_UNLOCKFILE: { + break; + } + + + /* + ToDo: C = 44 (2Ch) : Set number of records to read/write at once (CPM3) + E = Number of Sectors + Returns: A = return code (Returns A=0 if E was valid, 0FFh otherwise) + */ + case F_MULTISEC: { + break; + } + + /* + ToDo: C = 45 (2Dh) : Set BDOS Error Mode (CPM3) + E = BDOS Error Mode + E < 254 Compatibility mode; program is terminated and an error message printed. + E = 254 Error code is returned in H, error message is printed. + E = 255 Error code is returned in H, no error message is printed. + Returns: None + */ + case F_ERRMODE: { + break; + } + + /* + ToDo: C = 46 (2Eh) : Get Free Disk Space (CPM3) + E = Drive + Returns: A = return code + H = Physical Error + Binary result in the first 3 bytes of current DMA buffer + */ + case DRV_SPACE: { + break; + } + + /* + ToDo: C = 47 (2Fh) : Chain to program (CPM3) + E = Chain flag + Returns: None + */ + case P_CHAIN: { + break; + } + + /* + ToDo: C = 48 (30h) : Flush Bufers (CPM3) + E = Purge flag + Returns: A = return code + H = Physical Error + */ + case DRV_FLUSH: { + break; + } + + /* + ToDo: C = 49 (31h) : Get/Set System Control (CPM3) + DE = SCB PB Address + Returns: A = Returned Byte + HL = Returned Word + */ + case S_SCB: { + break; + } + + + /* + ToDo: C = 50 (32h) : Direct BIOS Calls (CPM3) + DE = BIOS PB Address + Returns: BIOS Return + */ + case S_BIOS: { + break; + } + + /* + ToDo: C = 59 (3Bh) : Load Overlay (CPM3) + DE = address of FCB + Returns: A = return code + H = Physical Error + */ + case P_LOAD: { + break; + } + + + /* + ToDo: C = 60 (3Ch) : Call Resident System Extension (RSX) (CPM3) + DE = RSX PB Address + Returns: A = return code + H = Physical Error + */ + case S_RSX: { + break; + } + + + /* + ToDo: C = 98 (62h) : Free Blocks (CPM3) + Returns: A = return code + H = Physical Error + */ + case F_CLEANUP: { + break; + } + + + /* + ToDo: C = 99 (63h) : Truncate File (CPM3) + DE = address of FCB + Returns: A = Directory code + H = Extended or Physical Error + */ + case F_TRUNCATE: { + break; + } + + + /* + ToDo: C = 100 (64h) : Set Directory Label (CPM3) + DE = address of FCB + Returns: A = Directory code + H = Extended or Physical Error + */ + case DRV_SETLABEL: { + break; + } + + + /* + ToDo: C = 101 (65h) : Return Directory Label Data (CPM3) + E = Drive + Returns: A = Directory Label Data Byte or 0xFF + H = Physical Error + */ + case DRV_GETLABEL: { + break; + } + + + /* + ToDo: C = 102 (66h) : Read File Date Stamps and Password Mode (CPM3) + DE = address of FCB + Returns: A = Directory code + H = Physical Error + */ + case F_TIMEDATE: { + break; + } + + + /* + ToDo: C = 103 (67h) : Write File XFCB (CPM3) + DE = address of FCB + Returns: A = Directory code + H = Physical Error + */ + case F_WRITEXFCB: { + break; + } + + + /* + ToDo: C = 104 (68h) : Set Date and Time (CPM3) + DE = Date and Time (DAT) Address + Returns: None + */ + case T_SET: { + break; + } + + + /* + ToDo: C = 105 (69h) : Get Date and Time (CPM3) + DE = Date and Time (DAT) Address + Returns: Date and Time (DAT) set + A = Seconds (in packed BCD format) + */ + case T_GET: { + break; + } + + + /* + ToDo: C = 106 (6Ah) : Set Default Password (CPM3) + DE = Password Addresss + Returns: None + */ + case F_PASSWD: { + break; + } + + + /* + ToDo: C = 107 (6Bh) : Return Serial Number (CPM3) + DE = Serial Number Field + Returns: Serial number field set + */ + case S_SERIAL: { + break; + } + + + /* + ToDo: C = 108 (6Ch) : Get/Set Program Return Code (CPM3) + DE = 0xFFFF (Get) or Program Return Code (Set) + Returns: HL = Program Return Code or (none) + */ + case P_CODE: { + break; + } + + + /* + ToDo: C = 109 (6Dh) : Get/Set Console Mode (CPM3) + DE = 0xFFFF (Get) or Console Mode (Set) + Returns: HL = Console Mode or (none) + */ + case C_MODE: { + break; + } + + + /* + ToDo: C = 110 (6Eh) : Get/Set Output Delimiter (CPM3) + DE = 0xFFFF (Get) or E = Delimiter (Set) + Returns: A = Output Delimiter or (none) + */ + case C_DELIMIT: { + break; + } + + + /* + ToDo: C = 111 (6Fh) : Print Block (CPM3) + DE = address of CCB + Returns: None + */ + case C_WRITEBLK: { + break; + } + + + /* + ToDo: C = 112 (70h) : List Block (CPM3) + DE = address of CCB + Returns: None + */ + case L_WRITEBLK: { + break; + } + + + /* + ToDo: C = 152 (98h) : List Block (CPM3) + DE = address of PFCB + Returns: HL = Return code + Parsed file control block + */ + case F_PARSE: { + break; + } + + +#if defined board_digital_io + + /* + C = 220 (DCh) : PinMode + */ + case 220: { + pinMode(HIGH_REGISTER(DE), LOW_REGISTER(DE)); + break; + } + + /* + C = 221 (DDh) : DigitalRead + */ + case 221: { + HL = digitalRead(HIGH_REGISTER(DE)); + break; + } + + /* + C = 222 (DEh) : DigitalWrite + */ + case 222: { + digitalWrite(HIGH_REGISTER(DE), LOW_REGISTER(DE)); + break; + } + + /* + C = 223 (DFh) : AnalogRead + */ + case 223: { + HL = analogRead(HIGH_REGISTER(DE)); + break; + } + +#endif // if defined board_digital_io +#if defined board_analog_io + + /* + C = 224 (E0h) : AnalogWrite + */ + case 224: { + analogWrite(HIGH_REGISTER(DE), LOW_REGISTER(DE)); + break; + } + +#endif // if defined board_analog_io + + /* + C = 230 (E6h) : Set 8 bit masking + */ + case 230: { + mask8bit = LOW_REGISTER(DE); + break; + } + + /* + C = 231 (E7h) : Host specific BDOS call + */ + case 231: { + HL = hostbdos(DE); + break; + } + + /* + C = 232 (E8h) : ESP32 specific BDOS call + */ +#if defined board_esp32 + case 232: { + HL = esp32bdos(DE); + break; + } + +#endif // if defined board_esp32 +#if defined board_stm32 + case 232: { + HL = stm32bdos(DE); + break; + } + +#endif // if defined board_stm32 + + /* + C = 249 (F9h) : MakeDisk + Makes a disk directory if not existent. + */ + case 249: { + HL = _MakeDisk(DE); + break; + } + + /* + C = 250 (FAh) : HostOS + Returns: A = 0x00 - Windows / 0x01 - Arduino / 0x02 - Posix / 0x03 - Dos / 0x04 - Teensy / 0x05 - ESP32 / 0x06 - STM32 + */ + case 250: { + HL = HostOS; + break; + } + + /* + C = 251 (FBh) : Version + Returns: A = 0xVv - Version in BCD representation: V.v + */ + case 251: { + HL = VersionBCD; + break; + } + + /* + C = 252 (FCh) : CCP version + Returns: A = 0x00-0x04 = DRI|CCPZ|ZCPR2|ZCPR3|Z80CCP / 0xVv = Internal version in BCD: V.v + */ + case 252: { + HL = VersionCCP; + break; + } + + /* + C = 253 (FDh) : CCP address + */ + case 253: { + HL = CCPaddr; + break; + } + +#ifdef HASLUA + + /* + C = 254 (FEh) : Run Lua file + */ + case 254: { + HL = _RunLua(DE); + break; + } + +#endif // ifdef HASLUA + + /* + Unimplemented calls get listed + */ + default: { +#ifdef DEBUG // Show unimplemented BDOS calls only when debugging + _puts("\r\nUnimplemented BDOS call.\r\n"); + _puts("C = 0x"); + _puthex8(ch); + _puts("\r\n"); +#endif // ifdef DEBUG + break; + } + } // switch + + // CP/M BDOS does this before returning + SET_HIGH_REGISTER( BC, HIGH_REGISTER(HL)); + SET_HIGH_REGISTER( AF, LOW_REGISTER(HL)); + +#ifdef DEBUGLOG + _logBdosOut(ch); +#endif +} // _Bdos + +#endif // ifndef CPM_H diff --git a/runcpm-rp2350-hstx-usb/runcpm-pico/cpu.h b/runcpm-rp2350-hstx-usb/runcpm-pico/cpu.h new file mode 100644 index 000000000..92b40993d --- /dev/null +++ b/runcpm-rp2350-hstx-usb/runcpm-pico/cpu.h @@ -0,0 +1,4637 @@ +// SPDX-FileCopyrightText: 2023 Mockba the Borg +// +// SPDX-License-Identifier: MIT + +#ifndef CPU_H +#define CPU_H + +/* see main.c for definition */ + +int32 PCX; /* external view of PC */ +int32 AF; /* AF register */ +int32 BC; /* BC register */ +int32 DE; /* DE register */ +int32 HL; /* HL register */ +int32 IX; /* IX register */ +int32 IY; /* IY register */ +int32 PC; /* program counter */ +int32 SP; /* SP register */ +int32 AF1; /* alternate AF register */ +int32 BC1; /* alternate BC register */ +int32 DE1; /* alternate DE register */ +int32 HL1; /* alternate HL register */ +int32 IFF; /* Interrupt Flip Flop */ +int32 IR; /* Interrupt (upper) / Refresh (lower) register */ +int32 Status = 0; /* Status of the CPU 0=running 1=end request 2=back to CCP */ +int32 Debug = 0; +int32 Break = -1; +int32 Step = -1; + +#ifdef iDEBUG +FILE* iLogFile; +char iLogBuffer[256]; +const char* iLogTxt; +#endif + +/* increase R by val (to correctly implement refresh counter) if enabled */ +#ifdef DO_INCR +#define INCR(val) IR = (IR & ~0x3f) | ((IR + (val)) & 0x3f) +#else +#define INCR(val) ; +#endif + +/* + Functions needed by the soft CPU implementation +*/ +void cpu_out(const uint32 Port, const uint32 Value) { + if (Port == 0xFF) { + _Bios(); + } else { + _HardwareOut(Port, Value); + } +} + +uint32 cpu_in(const uint32 Port) { + uint32 Result; + if (Port == 0xFF) { + _Bdos(); + Result = HIGH_REGISTER(AF); + } else { + Result = _HardwareIn(Port); + } + return(Result); +} + +/* Z80 Custom soft core */ + +/* simulator stop codes */ +#define STOP_HALT 0 /* HALT */ +#define STOP_IBKPT 1 /* breakpoint (program counter) */ +#define STOP_MEM 2 /* breakpoint (memory access) */ +#define STOP_INSTR 3 /* breakpoint (instruction access) */ +#define STOP_OPCODE 4 /* invalid operation encountered (8080, Z80, 8086) */ + +#define ADDRMASK 0xffff + +#define FLAG_C 1 +#define FLAG_N 2 +#define FLAG_P 4 +#define FLAG_H 16 +#define FLAG_Z 64 +#define FLAG_S 128 + +#define SETFLAG(f,c) (AF = (c) ? AF | FLAG_ ## f : AF & ~FLAG_ ## f) +#define TSTFLAG(f) ((AF & FLAG_ ## f) != 0) + +#define PARITY(x) parityTable[(x) & 0xff] + +#define SET_PVS(s) (((cbits >> 6) ^ (cbits >> 5)) & 4) +#define SET_PV (SET_PVS(sum)) +#define SET_PV2(x) ((temp == (x)) << 2) + +#define POP(x) { \ + uint32 y = RAM_PP(SP); \ + x = y + (RAM_PP(SP) << 8); \ +} + +#define JPC(cond) { \ + if (cond) { \ + PC = GET_WORD(PC); \ + } else { \ + PC += 2; \ + } \ +} + +#define CALLC(cond) { \ + if (cond) { \ + uint32 adrr = GET_WORD(PC); \ + PUSH(PC + 2); \ + PC = adrr; \ + } else { \ + PC += 2; \ + } \ +} + +/* the following tables precompute some common subexpressions +parityTable[i] 0..255 (number of 1's in i is odd) ? 0 : 4 +incTable[i] 0..256! (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0) << 4) +decTable[i] 0..255 (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0xf) << 4) | 2 +cbitsTable[i] 0..511 (i & 0x10) | ((i >> 8) & 1) +cbitsDup8Table[i] 0..511 (i & 0x10) | ((i >> 8) & 1) | ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) +cbitsDup16Table[i] 0..511 (i & 0x10) | ((i >> 8) & 1) | (i & 0x28) +cbits2Table[i] 0..511 (i & 0x10) | ((i >> 8) & 1) | 2 +rrcaTable[i] 0..255 ((i & 1) << 15) | ((i >> 1) << 8) | ((i >> 1) & 0x28) | (i & 1) +rraTable[i] 0..255 ((i >> 1) << 8) | ((i >> 1) & 0x28) | (i & 1) +addTable[i] 0..511 ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) +subTable[i] 0..255 ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) | 2 +andTable[i] 0..255 (i << 8) | (i & 0xa8) | ((i == 0) << 6) | 0x10 | parityTable[i] +xororTable[i] 0..255 (i << 8) | (i & 0xa8) | ((i == 0) << 6) | parityTable[i] +rotateShiftTable[i] 0..255 (i & 0xa8) | (((i & 0xff) == 0) << 6) | parityTable[i & 0xff] +incZ80Table[i] 0..256! (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0) << 4) | ((i == 0x80) << 2) +decZ80Table[i] 0..255 (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0xf) << 4) | ((i == 0x7f) << 2) | 2 +cbitsZ80Table[i] 0..511 (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) +cbitsZ80DupTable[i] 0..511 (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | (i & 0xa8) +cbits2Z80Table[i] 0..511 (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | 2 +cbits2Z80DupTable[i] 0..511 (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | 2 | (i & 0xa8) +negTable[i] 0..255 (((i & 0x0f) != 0) << 4) | ((i == 0x80) << 2) | 2 | (i != 0) +rrdrldTable[i] 0..255 (i << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) | parityTable[i] +cpTable[i] 0..255 (i & 0x80) | (((i & 0xff) == 0) << 6) +*/ + +/* parityTable[i] = (number of 1's in i is odd) ? 0 : 4, i = 0..255 */ +static const uint8 parityTable[256] = { + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, +}; + +/* incTable[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0) << 4), i = 0..256 */ +static const uint8 incTable[257] = { + 80, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, 80 +}; + +/* decTable[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0xf) << 4) | 2, i = 0..255 */ +static const uint8 decTable[256] = { + 66, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, +}; + +/* cbitsTable[i] = (i & 0x10) | ((i >> 8) & 1), i = 0..511 */ +static const uint8 cbitsTable[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +}; + +/* cbitsDup8Table[i] = (i & 0x10) | ((i >> 8) & 1) | ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6), i = 0..511 */ +static const uint16 cbitsDup8Table[512] = { + 0x0040,0x0100,0x0200,0x0300,0x0400,0x0500,0x0600,0x0700, + 0x0808,0x0908,0x0a08,0x0b08,0x0c08,0x0d08,0x0e08,0x0f08, + 0x1010,0x1110,0x1210,0x1310,0x1410,0x1510,0x1610,0x1710, + 0x1818,0x1918,0x1a18,0x1b18,0x1c18,0x1d18,0x1e18,0x1f18, + 0x2020,0x2120,0x2220,0x2320,0x2420,0x2520,0x2620,0x2720, + 0x2828,0x2928,0x2a28,0x2b28,0x2c28,0x2d28,0x2e28,0x2f28, + 0x3030,0x3130,0x3230,0x3330,0x3430,0x3530,0x3630,0x3730, + 0x3838,0x3938,0x3a38,0x3b38,0x3c38,0x3d38,0x3e38,0x3f38, + 0x4000,0x4100,0x4200,0x4300,0x4400,0x4500,0x4600,0x4700, + 0x4808,0x4908,0x4a08,0x4b08,0x4c08,0x4d08,0x4e08,0x4f08, + 0x5010,0x5110,0x5210,0x5310,0x5410,0x5510,0x5610,0x5710, + 0x5818,0x5918,0x5a18,0x5b18,0x5c18,0x5d18,0x5e18,0x5f18, + 0x6020,0x6120,0x6220,0x6320,0x6420,0x6520,0x6620,0x6720, + 0x6828,0x6928,0x6a28,0x6b28,0x6c28,0x6d28,0x6e28,0x6f28, + 0x7030,0x7130,0x7230,0x7330,0x7430,0x7530,0x7630,0x7730, + 0x7838,0x7938,0x7a38,0x7b38,0x7c38,0x7d38,0x7e38,0x7f38, + 0x8080,0x8180,0x8280,0x8380,0x8480,0x8580,0x8680,0x8780, + 0x8888,0x8988,0x8a88,0x8b88,0x8c88,0x8d88,0x8e88,0x8f88, + 0x9090,0x9190,0x9290,0x9390,0x9490,0x9590,0x9690,0x9790, + 0x9898,0x9998,0x9a98,0x9b98,0x9c98,0x9d98,0x9e98,0x9f98, + 0xa0a0,0xa1a0,0xa2a0,0xa3a0,0xa4a0,0xa5a0,0xa6a0,0xa7a0, + 0xa8a8,0xa9a8,0xaaa8,0xaba8,0xaca8,0xada8,0xaea8,0xafa8, + 0xb0b0,0xb1b0,0xb2b0,0xb3b0,0xb4b0,0xb5b0,0xb6b0,0xb7b0, + 0xb8b8,0xb9b8,0xbab8,0xbbb8,0xbcb8,0xbdb8,0xbeb8,0xbfb8, + 0xc080,0xc180,0xc280,0xc380,0xc480,0xc580,0xc680,0xc780, + 0xc888,0xc988,0xca88,0xcb88,0xcc88,0xcd88,0xce88,0xcf88, + 0xd090,0xd190,0xd290,0xd390,0xd490,0xd590,0xd690,0xd790, + 0xd898,0xd998,0xda98,0xdb98,0xdc98,0xdd98,0xde98,0xdf98, + 0xe0a0,0xe1a0,0xe2a0,0xe3a0,0xe4a0,0xe5a0,0xe6a0,0xe7a0, + 0xe8a8,0xe9a8,0xeaa8,0xeba8,0xeca8,0xeda8,0xeea8,0xefa8, + 0xf0b0,0xf1b0,0xf2b0,0xf3b0,0xf4b0,0xf5b0,0xf6b0,0xf7b0, + 0xf8b8,0xf9b8,0xfab8,0xfbb8,0xfcb8,0xfdb8,0xfeb8,0xffb8, + 0x0041,0x0101,0x0201,0x0301,0x0401,0x0501,0x0601,0x0701, + 0x0809,0x0909,0x0a09,0x0b09,0x0c09,0x0d09,0x0e09,0x0f09, + 0x1011,0x1111,0x1211,0x1311,0x1411,0x1511,0x1611,0x1711, + 0x1819,0x1919,0x1a19,0x1b19,0x1c19,0x1d19,0x1e19,0x1f19, + 0x2021,0x2121,0x2221,0x2321,0x2421,0x2521,0x2621,0x2721, + 0x2829,0x2929,0x2a29,0x2b29,0x2c29,0x2d29,0x2e29,0x2f29, + 0x3031,0x3131,0x3231,0x3331,0x3431,0x3531,0x3631,0x3731, + 0x3839,0x3939,0x3a39,0x3b39,0x3c39,0x3d39,0x3e39,0x3f39, + 0x4001,0x4101,0x4201,0x4301,0x4401,0x4501,0x4601,0x4701, + 0x4809,0x4909,0x4a09,0x4b09,0x4c09,0x4d09,0x4e09,0x4f09, + 0x5011,0x5111,0x5211,0x5311,0x5411,0x5511,0x5611,0x5711, + 0x5819,0x5919,0x5a19,0x5b19,0x5c19,0x5d19,0x5e19,0x5f19, + 0x6021,0x6121,0x6221,0x6321,0x6421,0x6521,0x6621,0x6721, + 0x6829,0x6929,0x6a29,0x6b29,0x6c29,0x6d29,0x6e29,0x6f29, + 0x7031,0x7131,0x7231,0x7331,0x7431,0x7531,0x7631,0x7731, + 0x7839,0x7939,0x7a39,0x7b39,0x7c39,0x7d39,0x7e39,0x7f39, + 0x8081,0x8181,0x8281,0x8381,0x8481,0x8581,0x8681,0x8781, + 0x8889,0x8989,0x8a89,0x8b89,0x8c89,0x8d89,0x8e89,0x8f89, + 0x9091,0x9191,0x9291,0x9391,0x9491,0x9591,0x9691,0x9791, + 0x9899,0x9999,0x9a99,0x9b99,0x9c99,0x9d99,0x9e99,0x9f99, + 0xa0a1,0xa1a1,0xa2a1,0xa3a1,0xa4a1,0xa5a1,0xa6a1,0xa7a1, + 0xa8a9,0xa9a9,0xaaa9,0xaba9,0xaca9,0xada9,0xaea9,0xafa9, + 0xb0b1,0xb1b1,0xb2b1,0xb3b1,0xb4b1,0xb5b1,0xb6b1,0xb7b1, + 0xb8b9,0xb9b9,0xbab9,0xbbb9,0xbcb9,0xbdb9,0xbeb9,0xbfb9, + 0xc081,0xc181,0xc281,0xc381,0xc481,0xc581,0xc681,0xc781, + 0xc889,0xc989,0xca89,0xcb89,0xcc89,0xcd89,0xce89,0xcf89, + 0xd091,0xd191,0xd291,0xd391,0xd491,0xd591,0xd691,0xd791, + 0xd899,0xd999,0xda99,0xdb99,0xdc99,0xdd99,0xde99,0xdf99, + 0xe0a1,0xe1a1,0xe2a1,0xe3a1,0xe4a1,0xe5a1,0xe6a1,0xe7a1, + 0xe8a9,0xe9a9,0xeaa9,0xeba9,0xeca9,0xeda9,0xeea9,0xefa9, + 0xf0b1,0xf1b1,0xf2b1,0xf3b1,0xf4b1,0xf5b1,0xf6b1,0xf7b1, + 0xf8b9,0xf9b9,0xfab9,0xfbb9,0xfcb9,0xfdb9,0xfeb9,0xffb9, +}; + +/* cbitsDup16Table[i] = (i & 0x10) | ((i >> 8) & 1) | (i & 0x28), i = 0..511 */ +static const uint8 cbitsDup16Table[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16,16,16,16,16,16,16,16,24,24,24,24,24,24,24,24, + 32,32,32,32,32,32,32,32,40,40,40,40,40,40,40,40, + 48,48,48,48,48,48,48,48,56,56,56,56,56,56,56,56, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16,16,16,16,16,16,16,16,24,24,24,24,24,24,24,24, + 32,32,32,32,32,32,32,32,40,40,40,40,40,40,40,40, + 48,48,48,48,48,48,48,48,56,56,56,56,56,56,56,56, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16,16,16,16,16,16,16,16,24,24,24,24,24,24,24,24, + 32,32,32,32,32,32,32,32,40,40,40,40,40,40,40,40, + 48,48,48,48,48,48,48,48,56,56,56,56,56,56,56,56, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16,16,16,16,16,16,16,16,24,24,24,24,24,24,24,24, + 32,32,32,32,32,32,32,32,40,40,40,40,40,40,40,40, + 48,48,48,48,48,48,48,48,56,56,56,56,56,56,56,56, + 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, + 17,17,17,17,17,17,17,17,25,25,25,25,25,25,25,25, + 33,33,33,33,33,33,33,33,41,41,41,41,41,41,41,41, + 49,49,49,49,49,49,49,49,57,57,57,57,57,57,57,57, + 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, + 17,17,17,17,17,17,17,17,25,25,25,25,25,25,25,25, + 33,33,33,33,33,33,33,33,41,41,41,41,41,41,41,41, + 49,49,49,49,49,49,49,49,57,57,57,57,57,57,57,57, + 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, + 17,17,17,17,17,17,17,17,25,25,25,25,25,25,25,25, + 33,33,33,33,33,33,33,33,41,41,41,41,41,41,41,41, + 49,49,49,49,49,49,49,49,57,57,57,57,57,57,57,57, + 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, + 17,17,17,17,17,17,17,17,25,25,25,25,25,25,25,25, + 33,33,33,33,33,33,33,33,41,41,41,41,41,41,41,41, + 49,49,49,49,49,49,49,49,57,57,57,57,57,57,57,57, +}; + +/* cbits2Table[i] = (i & 0x10) | ((i >> 8) & 1) | 2, i = 0..511 */ +static const uint8 cbits2Table[512] = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, +}; + +/* rrcaTable[i] = ((i & 1) << 15) | ((i >> 1) << 8) | ((i >> 1) & 0x28) | (i & 1), i = 0..255 */ +static const uint16 rrcaTable[256] = { + 0x0000,0x8001,0x0100,0x8101,0x0200,0x8201,0x0300,0x8301, + 0x0400,0x8401,0x0500,0x8501,0x0600,0x8601,0x0700,0x8701, + 0x0808,0x8809,0x0908,0x8909,0x0a08,0x8a09,0x0b08,0x8b09, + 0x0c08,0x8c09,0x0d08,0x8d09,0x0e08,0x8e09,0x0f08,0x8f09, + 0x1000,0x9001,0x1100,0x9101,0x1200,0x9201,0x1300,0x9301, + 0x1400,0x9401,0x1500,0x9501,0x1600,0x9601,0x1700,0x9701, + 0x1808,0x9809,0x1908,0x9909,0x1a08,0x9a09,0x1b08,0x9b09, + 0x1c08,0x9c09,0x1d08,0x9d09,0x1e08,0x9e09,0x1f08,0x9f09, + 0x2020,0xa021,0x2120,0xa121,0x2220,0xa221,0x2320,0xa321, + 0x2420,0xa421,0x2520,0xa521,0x2620,0xa621,0x2720,0xa721, + 0x2828,0xa829,0x2928,0xa929,0x2a28,0xaa29,0x2b28,0xab29, + 0x2c28,0xac29,0x2d28,0xad29,0x2e28,0xae29,0x2f28,0xaf29, + 0x3020,0xb021,0x3120,0xb121,0x3220,0xb221,0x3320,0xb321, + 0x3420,0xb421,0x3520,0xb521,0x3620,0xb621,0x3720,0xb721, + 0x3828,0xb829,0x3928,0xb929,0x3a28,0xba29,0x3b28,0xbb29, + 0x3c28,0xbc29,0x3d28,0xbd29,0x3e28,0xbe29,0x3f28,0xbf29, + 0x4000,0xc001,0x4100,0xc101,0x4200,0xc201,0x4300,0xc301, + 0x4400,0xc401,0x4500,0xc501,0x4600,0xc601,0x4700,0xc701, + 0x4808,0xc809,0x4908,0xc909,0x4a08,0xca09,0x4b08,0xcb09, + 0x4c08,0xcc09,0x4d08,0xcd09,0x4e08,0xce09,0x4f08,0xcf09, + 0x5000,0xd001,0x5100,0xd101,0x5200,0xd201,0x5300,0xd301, + 0x5400,0xd401,0x5500,0xd501,0x5600,0xd601,0x5700,0xd701, + 0x5808,0xd809,0x5908,0xd909,0x5a08,0xda09,0x5b08,0xdb09, + 0x5c08,0xdc09,0x5d08,0xdd09,0x5e08,0xde09,0x5f08,0xdf09, + 0x6020,0xe021,0x6120,0xe121,0x6220,0xe221,0x6320,0xe321, + 0x6420,0xe421,0x6520,0xe521,0x6620,0xe621,0x6720,0xe721, + 0x6828,0xe829,0x6928,0xe929,0x6a28,0xea29,0x6b28,0xeb29, + 0x6c28,0xec29,0x6d28,0xed29,0x6e28,0xee29,0x6f28,0xef29, + 0x7020,0xf021,0x7120,0xf121,0x7220,0xf221,0x7320,0xf321, + 0x7420,0xf421,0x7520,0xf521,0x7620,0xf621,0x7720,0xf721, + 0x7828,0xf829,0x7928,0xf929,0x7a28,0xfa29,0x7b28,0xfb29, + 0x7c28,0xfc29,0x7d28,0xfd29,0x7e28,0xfe29,0x7f28,0xff29, +}; + +/* rraTable[i] = ((i >> 1) << 8) | ((i >> 1) & 0x28) | (i & 1), i = 0..255 */ +static const uint16 rraTable[256] = { + 0x0000,0x0001,0x0100,0x0101,0x0200,0x0201,0x0300,0x0301, + 0x0400,0x0401,0x0500,0x0501,0x0600,0x0601,0x0700,0x0701, + 0x0808,0x0809,0x0908,0x0909,0x0a08,0x0a09,0x0b08,0x0b09, + 0x0c08,0x0c09,0x0d08,0x0d09,0x0e08,0x0e09,0x0f08,0x0f09, + 0x1000,0x1001,0x1100,0x1101,0x1200,0x1201,0x1300,0x1301, + 0x1400,0x1401,0x1500,0x1501,0x1600,0x1601,0x1700,0x1701, + 0x1808,0x1809,0x1908,0x1909,0x1a08,0x1a09,0x1b08,0x1b09, + 0x1c08,0x1c09,0x1d08,0x1d09,0x1e08,0x1e09,0x1f08,0x1f09, + 0x2020,0x2021,0x2120,0x2121,0x2220,0x2221,0x2320,0x2321, + 0x2420,0x2421,0x2520,0x2521,0x2620,0x2621,0x2720,0x2721, + 0x2828,0x2829,0x2928,0x2929,0x2a28,0x2a29,0x2b28,0x2b29, + 0x2c28,0x2c29,0x2d28,0x2d29,0x2e28,0x2e29,0x2f28,0x2f29, + 0x3020,0x3021,0x3120,0x3121,0x3220,0x3221,0x3320,0x3321, + 0x3420,0x3421,0x3520,0x3521,0x3620,0x3621,0x3720,0x3721, + 0x3828,0x3829,0x3928,0x3929,0x3a28,0x3a29,0x3b28,0x3b29, + 0x3c28,0x3c29,0x3d28,0x3d29,0x3e28,0x3e29,0x3f28,0x3f29, + 0x4000,0x4001,0x4100,0x4101,0x4200,0x4201,0x4300,0x4301, + 0x4400,0x4401,0x4500,0x4501,0x4600,0x4601,0x4700,0x4701, + 0x4808,0x4809,0x4908,0x4909,0x4a08,0x4a09,0x4b08,0x4b09, + 0x4c08,0x4c09,0x4d08,0x4d09,0x4e08,0x4e09,0x4f08,0x4f09, + 0x5000,0x5001,0x5100,0x5101,0x5200,0x5201,0x5300,0x5301, + 0x5400,0x5401,0x5500,0x5501,0x5600,0x5601,0x5700,0x5701, + 0x5808,0x5809,0x5908,0x5909,0x5a08,0x5a09,0x5b08,0x5b09, + 0x5c08,0x5c09,0x5d08,0x5d09,0x5e08,0x5e09,0x5f08,0x5f09, + 0x6020,0x6021,0x6120,0x6121,0x6220,0x6221,0x6320,0x6321, + 0x6420,0x6421,0x6520,0x6521,0x6620,0x6621,0x6720,0x6721, + 0x6828,0x6829,0x6928,0x6929,0x6a28,0x6a29,0x6b28,0x6b29, + 0x6c28,0x6c29,0x6d28,0x6d29,0x6e28,0x6e29,0x6f28,0x6f29, + 0x7020,0x7021,0x7120,0x7121,0x7220,0x7221,0x7320,0x7321, + 0x7420,0x7421,0x7520,0x7521,0x7620,0x7621,0x7720,0x7721, + 0x7828,0x7829,0x7928,0x7929,0x7a28,0x7a29,0x7b28,0x7b29, + 0x7c28,0x7c29,0x7d28,0x7d29,0x7e28,0x7e29,0x7f28,0x7f29, +}; + +/* addTable[i] = ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6), i = 0..511 */ +static const uint16 addTable[512] = { + 0x0040,0x0100,0x0200,0x0300,0x0400,0x0500,0x0600,0x0700, + 0x0808,0x0908,0x0a08,0x0b08,0x0c08,0x0d08,0x0e08,0x0f08, + 0x1000,0x1100,0x1200,0x1300,0x1400,0x1500,0x1600,0x1700, + 0x1808,0x1908,0x1a08,0x1b08,0x1c08,0x1d08,0x1e08,0x1f08, + 0x2020,0x2120,0x2220,0x2320,0x2420,0x2520,0x2620,0x2720, + 0x2828,0x2928,0x2a28,0x2b28,0x2c28,0x2d28,0x2e28,0x2f28, + 0x3020,0x3120,0x3220,0x3320,0x3420,0x3520,0x3620,0x3720, + 0x3828,0x3928,0x3a28,0x3b28,0x3c28,0x3d28,0x3e28,0x3f28, + 0x4000,0x4100,0x4200,0x4300,0x4400,0x4500,0x4600,0x4700, + 0x4808,0x4908,0x4a08,0x4b08,0x4c08,0x4d08,0x4e08,0x4f08, + 0x5000,0x5100,0x5200,0x5300,0x5400,0x5500,0x5600,0x5700, + 0x5808,0x5908,0x5a08,0x5b08,0x5c08,0x5d08,0x5e08,0x5f08, + 0x6020,0x6120,0x6220,0x6320,0x6420,0x6520,0x6620,0x6720, + 0x6828,0x6928,0x6a28,0x6b28,0x6c28,0x6d28,0x6e28,0x6f28, + 0x7020,0x7120,0x7220,0x7320,0x7420,0x7520,0x7620,0x7720, + 0x7828,0x7928,0x7a28,0x7b28,0x7c28,0x7d28,0x7e28,0x7f28, + 0x8080,0x8180,0x8280,0x8380,0x8480,0x8580,0x8680,0x8780, + 0x8888,0x8988,0x8a88,0x8b88,0x8c88,0x8d88,0x8e88,0x8f88, + 0x9080,0x9180,0x9280,0x9380,0x9480,0x9580,0x9680,0x9780, + 0x9888,0x9988,0x9a88,0x9b88,0x9c88,0x9d88,0x9e88,0x9f88, + 0xa0a0,0xa1a0,0xa2a0,0xa3a0,0xa4a0,0xa5a0,0xa6a0,0xa7a0, + 0xa8a8,0xa9a8,0xaaa8,0xaba8,0xaca8,0xada8,0xaea8,0xafa8, + 0xb0a0,0xb1a0,0xb2a0,0xb3a0,0xb4a0,0xb5a0,0xb6a0,0xb7a0, + 0xb8a8,0xb9a8,0xbaa8,0xbba8,0xbca8,0xbda8,0xbea8,0xbfa8, + 0xc080,0xc180,0xc280,0xc380,0xc480,0xc580,0xc680,0xc780, + 0xc888,0xc988,0xca88,0xcb88,0xcc88,0xcd88,0xce88,0xcf88, + 0xd080,0xd180,0xd280,0xd380,0xd480,0xd580,0xd680,0xd780, + 0xd888,0xd988,0xda88,0xdb88,0xdc88,0xdd88,0xde88,0xdf88, + 0xe0a0,0xe1a0,0xe2a0,0xe3a0,0xe4a0,0xe5a0,0xe6a0,0xe7a0, + 0xe8a8,0xe9a8,0xeaa8,0xeba8,0xeca8,0xeda8,0xeea8,0xefa8, + 0xf0a0,0xf1a0,0xf2a0,0xf3a0,0xf4a0,0xf5a0,0xf6a0,0xf7a0, + 0xf8a8,0xf9a8,0xfaa8,0xfba8,0xfca8,0xfda8,0xfea8,0xffa8, + 0x0040,0x0100,0x0200,0x0300,0x0400,0x0500,0x0600,0x0700, + 0x0808,0x0908,0x0a08,0x0b08,0x0c08,0x0d08,0x0e08,0x0f08, + 0x1000,0x1100,0x1200,0x1300,0x1400,0x1500,0x1600,0x1700, + 0x1808,0x1908,0x1a08,0x1b08,0x1c08,0x1d08,0x1e08,0x1f08, + 0x2020,0x2120,0x2220,0x2320,0x2420,0x2520,0x2620,0x2720, + 0x2828,0x2928,0x2a28,0x2b28,0x2c28,0x2d28,0x2e28,0x2f28, + 0x3020,0x3120,0x3220,0x3320,0x3420,0x3520,0x3620,0x3720, + 0x3828,0x3928,0x3a28,0x3b28,0x3c28,0x3d28,0x3e28,0x3f28, + 0x4000,0x4100,0x4200,0x4300,0x4400,0x4500,0x4600,0x4700, + 0x4808,0x4908,0x4a08,0x4b08,0x4c08,0x4d08,0x4e08,0x4f08, + 0x5000,0x5100,0x5200,0x5300,0x5400,0x5500,0x5600,0x5700, + 0x5808,0x5908,0x5a08,0x5b08,0x5c08,0x5d08,0x5e08,0x5f08, + 0x6020,0x6120,0x6220,0x6320,0x6420,0x6520,0x6620,0x6720, + 0x6828,0x6928,0x6a28,0x6b28,0x6c28,0x6d28,0x6e28,0x6f28, + 0x7020,0x7120,0x7220,0x7320,0x7420,0x7520,0x7620,0x7720, + 0x7828,0x7928,0x7a28,0x7b28,0x7c28,0x7d28,0x7e28,0x7f28, + 0x8080,0x8180,0x8280,0x8380,0x8480,0x8580,0x8680,0x8780, + 0x8888,0x8988,0x8a88,0x8b88,0x8c88,0x8d88,0x8e88,0x8f88, + 0x9080,0x9180,0x9280,0x9380,0x9480,0x9580,0x9680,0x9780, + 0x9888,0x9988,0x9a88,0x9b88,0x9c88,0x9d88,0x9e88,0x9f88, + 0xa0a0,0xa1a0,0xa2a0,0xa3a0,0xa4a0,0xa5a0,0xa6a0,0xa7a0, + 0xa8a8,0xa9a8,0xaaa8,0xaba8,0xaca8,0xada8,0xaea8,0xafa8, + 0xb0a0,0xb1a0,0xb2a0,0xb3a0,0xb4a0,0xb5a0,0xb6a0,0xb7a0, + 0xb8a8,0xb9a8,0xbaa8,0xbba8,0xbca8,0xbda8,0xbea8,0xbfa8, + 0xc080,0xc180,0xc280,0xc380,0xc480,0xc580,0xc680,0xc780, + 0xc888,0xc988,0xca88,0xcb88,0xcc88,0xcd88,0xce88,0xcf88, + 0xd080,0xd180,0xd280,0xd380,0xd480,0xd580,0xd680,0xd780, + 0xd888,0xd988,0xda88,0xdb88,0xdc88,0xdd88,0xde88,0xdf88, + 0xe0a0,0xe1a0,0xe2a0,0xe3a0,0xe4a0,0xe5a0,0xe6a0,0xe7a0, + 0xe8a8,0xe9a8,0xeaa8,0xeba8,0xeca8,0xeda8,0xeea8,0xefa8, + 0xf0a0,0xf1a0,0xf2a0,0xf3a0,0xf4a0,0xf5a0,0xf6a0,0xf7a0, + 0xf8a8,0xf9a8,0xfaa8,0xfba8,0xfca8,0xfda8,0xfea8,0xffa8, +}; + +/* subTable[i] = ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) | 2, i = 0..255 */ +static const uint16 subTable[256] = { + 0x0042,0x0102,0x0202,0x0302,0x0402,0x0502,0x0602,0x0702, + 0x080a,0x090a,0x0a0a,0x0b0a,0x0c0a,0x0d0a,0x0e0a,0x0f0a, + 0x1002,0x1102,0x1202,0x1302,0x1402,0x1502,0x1602,0x1702, + 0x180a,0x190a,0x1a0a,0x1b0a,0x1c0a,0x1d0a,0x1e0a,0x1f0a, + 0x2022,0x2122,0x2222,0x2322,0x2422,0x2522,0x2622,0x2722, + 0x282a,0x292a,0x2a2a,0x2b2a,0x2c2a,0x2d2a,0x2e2a,0x2f2a, + 0x3022,0x3122,0x3222,0x3322,0x3422,0x3522,0x3622,0x3722, + 0x382a,0x392a,0x3a2a,0x3b2a,0x3c2a,0x3d2a,0x3e2a,0x3f2a, + 0x4002,0x4102,0x4202,0x4302,0x4402,0x4502,0x4602,0x4702, + 0x480a,0x490a,0x4a0a,0x4b0a,0x4c0a,0x4d0a,0x4e0a,0x4f0a, + 0x5002,0x5102,0x5202,0x5302,0x5402,0x5502,0x5602,0x5702, + 0x580a,0x590a,0x5a0a,0x5b0a,0x5c0a,0x5d0a,0x5e0a,0x5f0a, + 0x6022,0x6122,0x6222,0x6322,0x6422,0x6522,0x6622,0x6722, + 0x682a,0x692a,0x6a2a,0x6b2a,0x6c2a,0x6d2a,0x6e2a,0x6f2a, + 0x7022,0x7122,0x7222,0x7322,0x7422,0x7522,0x7622,0x7722, + 0x782a,0x792a,0x7a2a,0x7b2a,0x7c2a,0x7d2a,0x7e2a,0x7f2a, + 0x8082,0x8182,0x8282,0x8382,0x8482,0x8582,0x8682,0x8782, + 0x888a,0x898a,0x8a8a,0x8b8a,0x8c8a,0x8d8a,0x8e8a,0x8f8a, + 0x9082,0x9182,0x9282,0x9382,0x9482,0x9582,0x9682,0x9782, + 0x988a,0x998a,0x9a8a,0x9b8a,0x9c8a,0x9d8a,0x9e8a,0x9f8a, + 0xa0a2,0xa1a2,0xa2a2,0xa3a2,0xa4a2,0xa5a2,0xa6a2,0xa7a2, + 0xa8aa,0xa9aa,0xaaaa,0xabaa,0xacaa,0xadaa,0xaeaa,0xafaa, + 0xb0a2,0xb1a2,0xb2a2,0xb3a2,0xb4a2,0xb5a2,0xb6a2,0xb7a2, + 0xb8aa,0xb9aa,0xbaaa,0xbbaa,0xbcaa,0xbdaa,0xbeaa,0xbfaa, + 0xc082,0xc182,0xc282,0xc382,0xc482,0xc582,0xc682,0xc782, + 0xc88a,0xc98a,0xca8a,0xcb8a,0xcc8a,0xcd8a,0xce8a,0xcf8a, + 0xd082,0xd182,0xd282,0xd382,0xd482,0xd582,0xd682,0xd782, + 0xd88a,0xd98a,0xda8a,0xdb8a,0xdc8a,0xdd8a,0xde8a,0xdf8a, + 0xe0a2,0xe1a2,0xe2a2,0xe3a2,0xe4a2,0xe5a2,0xe6a2,0xe7a2, + 0xe8aa,0xe9aa,0xeaaa,0xebaa,0xecaa,0xedaa,0xeeaa,0xefaa, + 0xf0a2,0xf1a2,0xf2a2,0xf3a2,0xf4a2,0xf5a2,0xf6a2,0xf7a2, + 0xf8aa,0xf9aa,0xfaaa,0xfbaa,0xfcaa,0xfdaa,0xfeaa,0xffaa, +}; + +/* andTable[i] = (i << 8) | (i & 0xa8) | ((i == 0) << 6) | 0x10 | parityTable[i], i = 0..255 */ +static const uint16 andTable[256] = { + 0x0054,0x0110,0x0210,0x0314,0x0410,0x0514,0x0614,0x0710, + 0x0818,0x091c,0x0a1c,0x0b18,0x0c1c,0x0d18,0x0e18,0x0f1c, + 0x1010,0x1114,0x1214,0x1310,0x1414,0x1510,0x1610,0x1714, + 0x181c,0x1918,0x1a18,0x1b1c,0x1c18,0x1d1c,0x1e1c,0x1f18, + 0x2030,0x2134,0x2234,0x2330,0x2434,0x2530,0x2630,0x2734, + 0x283c,0x2938,0x2a38,0x2b3c,0x2c38,0x2d3c,0x2e3c,0x2f38, + 0x3034,0x3130,0x3230,0x3334,0x3430,0x3534,0x3634,0x3730, + 0x3838,0x393c,0x3a3c,0x3b38,0x3c3c,0x3d38,0x3e38,0x3f3c, + 0x4010,0x4114,0x4214,0x4310,0x4414,0x4510,0x4610,0x4714, + 0x481c,0x4918,0x4a18,0x4b1c,0x4c18,0x4d1c,0x4e1c,0x4f18, + 0x5014,0x5110,0x5210,0x5314,0x5410,0x5514,0x5614,0x5710, + 0x5818,0x591c,0x5a1c,0x5b18,0x5c1c,0x5d18,0x5e18,0x5f1c, + 0x6034,0x6130,0x6230,0x6334,0x6430,0x6534,0x6634,0x6730, + 0x6838,0x693c,0x6a3c,0x6b38,0x6c3c,0x6d38,0x6e38,0x6f3c, + 0x7030,0x7134,0x7234,0x7330,0x7434,0x7530,0x7630,0x7734, + 0x783c,0x7938,0x7a38,0x7b3c,0x7c38,0x7d3c,0x7e3c,0x7f38, + 0x8090,0x8194,0x8294,0x8390,0x8494,0x8590,0x8690,0x8794, + 0x889c,0x8998,0x8a98,0x8b9c,0x8c98,0x8d9c,0x8e9c,0x8f98, + 0x9094,0x9190,0x9290,0x9394,0x9490,0x9594,0x9694,0x9790, + 0x9898,0x999c,0x9a9c,0x9b98,0x9c9c,0x9d98,0x9e98,0x9f9c, + 0xa0b4,0xa1b0,0xa2b0,0xa3b4,0xa4b0,0xa5b4,0xa6b4,0xa7b0, + 0xa8b8,0xa9bc,0xaabc,0xabb8,0xacbc,0xadb8,0xaeb8,0xafbc, + 0xb0b0,0xb1b4,0xb2b4,0xb3b0,0xb4b4,0xb5b0,0xb6b0,0xb7b4, + 0xb8bc,0xb9b8,0xbab8,0xbbbc,0xbcb8,0xbdbc,0xbebc,0xbfb8, + 0xc094,0xc190,0xc290,0xc394,0xc490,0xc594,0xc694,0xc790, + 0xc898,0xc99c,0xca9c,0xcb98,0xcc9c,0xcd98,0xce98,0xcf9c, + 0xd090,0xd194,0xd294,0xd390,0xd494,0xd590,0xd690,0xd794, + 0xd89c,0xd998,0xda98,0xdb9c,0xdc98,0xdd9c,0xde9c,0xdf98, + 0xe0b0,0xe1b4,0xe2b4,0xe3b0,0xe4b4,0xe5b0,0xe6b0,0xe7b4, + 0xe8bc,0xe9b8,0xeab8,0xebbc,0xecb8,0xedbc,0xeebc,0xefb8, + 0xf0b4,0xf1b0,0xf2b0,0xf3b4,0xf4b0,0xf5b4,0xf6b4,0xf7b0, + 0xf8b8,0xf9bc,0xfabc,0xfbb8,0xfcbc,0xfdb8,0xfeb8,0xffbc, +}; + +/* xororTable[i] = (i << 8) | (i & 0xa8) | ((i == 0) << 6) | parityTable[i], i = 0..255 */ +static const uint16 xororTable[256] = { + 0x0044,0x0100,0x0200,0x0304,0x0400,0x0504,0x0604,0x0700, + 0x0808,0x090c,0x0a0c,0x0b08,0x0c0c,0x0d08,0x0e08,0x0f0c, + 0x1000,0x1104,0x1204,0x1300,0x1404,0x1500,0x1600,0x1704, + 0x180c,0x1908,0x1a08,0x1b0c,0x1c08,0x1d0c,0x1e0c,0x1f08, + 0x2020,0x2124,0x2224,0x2320,0x2424,0x2520,0x2620,0x2724, + 0x282c,0x2928,0x2a28,0x2b2c,0x2c28,0x2d2c,0x2e2c,0x2f28, + 0x3024,0x3120,0x3220,0x3324,0x3420,0x3524,0x3624,0x3720, + 0x3828,0x392c,0x3a2c,0x3b28,0x3c2c,0x3d28,0x3e28,0x3f2c, + 0x4000,0x4104,0x4204,0x4300,0x4404,0x4500,0x4600,0x4704, + 0x480c,0x4908,0x4a08,0x4b0c,0x4c08,0x4d0c,0x4e0c,0x4f08, + 0x5004,0x5100,0x5200,0x5304,0x5400,0x5504,0x5604,0x5700, + 0x5808,0x590c,0x5a0c,0x5b08,0x5c0c,0x5d08,0x5e08,0x5f0c, + 0x6024,0x6120,0x6220,0x6324,0x6420,0x6524,0x6624,0x6720, + 0x6828,0x692c,0x6a2c,0x6b28,0x6c2c,0x6d28,0x6e28,0x6f2c, + 0x7020,0x7124,0x7224,0x7320,0x7424,0x7520,0x7620,0x7724, + 0x782c,0x7928,0x7a28,0x7b2c,0x7c28,0x7d2c,0x7e2c,0x7f28, + 0x8080,0x8184,0x8284,0x8380,0x8484,0x8580,0x8680,0x8784, + 0x888c,0x8988,0x8a88,0x8b8c,0x8c88,0x8d8c,0x8e8c,0x8f88, + 0x9084,0x9180,0x9280,0x9384,0x9480,0x9584,0x9684,0x9780, + 0x9888,0x998c,0x9a8c,0x9b88,0x9c8c,0x9d88,0x9e88,0x9f8c, + 0xa0a4,0xa1a0,0xa2a0,0xa3a4,0xa4a0,0xa5a4,0xa6a4,0xa7a0, + 0xa8a8,0xa9ac,0xaaac,0xaba8,0xacac,0xada8,0xaea8,0xafac, + 0xb0a0,0xb1a4,0xb2a4,0xb3a0,0xb4a4,0xb5a0,0xb6a0,0xb7a4, + 0xb8ac,0xb9a8,0xbaa8,0xbbac,0xbca8,0xbdac,0xbeac,0xbfa8, + 0xc084,0xc180,0xc280,0xc384,0xc480,0xc584,0xc684,0xc780, + 0xc888,0xc98c,0xca8c,0xcb88,0xcc8c,0xcd88,0xce88,0xcf8c, + 0xd080,0xd184,0xd284,0xd380,0xd484,0xd580,0xd680,0xd784, + 0xd88c,0xd988,0xda88,0xdb8c,0xdc88,0xdd8c,0xde8c,0xdf88, + 0xe0a0,0xe1a4,0xe2a4,0xe3a0,0xe4a4,0xe5a0,0xe6a0,0xe7a4, + 0xe8ac,0xe9a8,0xeaa8,0xebac,0xeca8,0xedac,0xeeac,0xefa8, + 0xf0a4,0xf1a0,0xf2a0,0xf3a4,0xf4a0,0xf5a4,0xf6a4,0xf7a0, + 0xf8a8,0xf9ac,0xfaac,0xfba8,0xfcac,0xfda8,0xfea8,0xffac, +}; + +/* rotateShiftTable[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | parityTable[i & 0xff], i = 0..255 */ +static const uint8 rotateShiftTable[256] = { + 68, 0, 0, 4, 0, 4, 4, 0, 8, 12, 12, 8, 12, 8, 8, 12, + 0, 4, 4, 0, 4, 0, 0, 4, 12, 8, 8, 12, 8, 12, 12, 8, + 32, 36, 36, 32, 36, 32, 32, 36, 44, 40, 40, 44, 40, 44, 44, 40, + 36, 32, 32, 36, 32, 36, 36, 32, 40, 44, 44, 40, 44, 40, 40, 44, + 0, 4, 4, 0, 4, 0, 0, 4, 12, 8, 8, 12, 8, 12, 12, 8, + 4, 0, 0, 4, 0, 4, 4, 0, 8, 12, 12, 8, 12, 8, 8, 12, + 36, 32, 32, 36, 32, 36, 36, 32, 40, 44, 44, 40, 44, 40, 40, 44, + 32, 36, 36, 32, 36, 32, 32, 36, 44, 40, 40, 44, 40, 44, 44, 40, + 128,132,132,128,132,128,128,132,140,136,136,140,136,140,140,136, + 132,128,128,132,128,132,132,128,136,140,140,136,140,136,136,140, + 164,160,160,164,160,164,164,160,168,172,172,168,172,168,168,172, + 160,164,164,160,164,160,160,164,172,168,168,172,168,172,172,168, + 132,128,128,132,128,132,132,128,136,140,140,136,140,136,136,140, + 128,132,132,128,132,128,128,132,140,136,136,140,136,140,140,136, + 160,164,164,160,164,160,160,164,172,168,168,172,168,172,172,168, + 164,160,160,164,160,164,164,160,168,172,172,168,172,168,168,172, +}; + +/* incZ80Table[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0) << 4) | ((i == 0x80) << 2), i = 0..256 */ +static const uint8 incZ80Table[257] = { + 80, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 148,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, 80, +}; + +/* decZ80Table[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0xf) << 4) | ((i == 0x7f) << 2) | 2, i = 0..255 */ +static const uint8 decZ80Table[256] = { + 66, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 62, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, +}; + +/* cbitsZ80Table[i] = (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1), i = 0..511 */ +static const uint8 cbitsZ80Table[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +}; + +/* cbitsZ80DupTable[i] = (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | (i & 0xa8), i = 0..511 */ +static const uint8 cbitsZ80DupTable[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 16, 16, 16, 16, 16, 16, 16, 24, 24, 24, 24, 24, 24, 24, 24, + 32, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 48, 48, 48, 48, 48, 48, 48, 48, 56, 56, 56, 56, 56, 56, 56, 56, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 16, 16, 16, 16, 16, 16, 16, 24, 24, 24, 24, 24, 24, 24, 24, + 32, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 48, 48, 48, 48, 48, 48, 48, 48, 56, 56, 56, 56, 56, 56, 56, 56, + 132,132,132,132,132,132,132,132,140,140,140,140,140,140,140,140, + 148,148,148,148,148,148,148,148,156,156,156,156,156,156,156,156, + 164,164,164,164,164,164,164,164,172,172,172,172,172,172,172,172, + 180,180,180,180,180,180,180,180,188,188,188,188,188,188,188,188, + 132,132,132,132,132,132,132,132,140,140,140,140,140,140,140,140, + 148,148,148,148,148,148,148,148,156,156,156,156,156,156,156,156, + 164,164,164,164,164,164,164,164,172,172,172,172,172,172,172,172, + 180,180,180,180,180,180,180,180,188,188,188,188,188,188,188,188, + 5, 5, 5, 5, 5, 5, 5, 5, 13, 13, 13, 13, 13, 13, 13, 13, + 21, 21, 21, 21, 21, 21, 21, 21, 29, 29, 29, 29, 29, 29, 29, 29, + 37, 37, 37, 37, 37, 37, 37, 37, 45, 45, 45, 45, 45, 45, 45, 45, + 53, 53, 53, 53, 53, 53, 53, 53, 61, 61, 61, 61, 61, 61, 61, 61, + 5, 5, 5, 5, 5, 5, 5, 5, 13, 13, 13, 13, 13, 13, 13, 13, + 21, 21, 21, 21, 21, 21, 21, 21, 29, 29, 29, 29, 29, 29, 29, 29, + 37, 37, 37, 37, 37, 37, 37, 37, 45, 45, 45, 45, 45, 45, 45, 45, + 53, 53, 53, 53, 53, 53, 53, 53, 61, 61, 61, 61, 61, 61, 61, 61, + 129,129,129,129,129,129,129,129,137,137,137,137,137,137,137,137, + 145,145,145,145,145,145,145,145,153,153,153,153,153,153,153,153, + 161,161,161,161,161,161,161,161,169,169,169,169,169,169,169,169, + 177,177,177,177,177,177,177,177,185,185,185,185,185,185,185,185, + 129,129,129,129,129,129,129,129,137,137,137,137,137,137,137,137, + 145,145,145,145,145,145,145,145,153,153,153,153,153,153,153,153, + 161,161,161,161,161,161,161,161,169,169,169,169,169,169,169,169, + 177,177,177,177,177,177,177,177,185,185,185,185,185,185,185,185, +}; + +/* cbits2Z80Table[i] = (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | 2, i = 0..511 */ +static const uint8 cbits2Z80Table[512] = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, +}; + +/* cbits2Z80DupTable[i] = (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | 2 | (i & 0xa8), i = 0..511 */ +static const uint8 cbits2Z80DupTable[512] = { + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 10, + 18, 18, 18, 18, 18, 18, 18, 18, 26, 26, 26, 26, 26, 26, 26, 26, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 42, + 50, 50, 50, 50, 50, 50, 50, 50, 58, 58, 58, 58, 58, 58, 58, 58, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 10, + 18, 18, 18, 18, 18, 18, 18, 18, 26, 26, 26, 26, 26, 26, 26, 26, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 42, + 50, 50, 50, 50, 50, 50, 50, 50, 58, 58, 58, 58, 58, 58, 58, 58, + 134,134,134,134,134,134,134,134,142,142,142,142,142,142,142,142, + 150,150,150,150,150,150,150,150,158,158,158,158,158,158,158,158, + 166,166,166,166,166,166,166,166,174,174,174,174,174,174,174,174, + 182,182,182,182,182,182,182,182,190,190,190,190,190,190,190,190, + 134,134,134,134,134,134,134,134,142,142,142,142,142,142,142,142, + 150,150,150,150,150,150,150,150,158,158,158,158,158,158,158,158, + 166,166,166,166,166,166,166,166,174,174,174,174,174,174,174,174, + 182,182,182,182,182,182,182,182,190,190,190,190,190,190,190,190, + 7, 7, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15, 15, 15, + 23, 23, 23, 23, 23, 23, 23, 23, 31, 31, 31, 31, 31, 31, 31, 31, + 39, 39, 39, 39, 39, 39, 39, 39, 47, 47, 47, 47, 47, 47, 47, 47, + 55, 55, 55, 55, 55, 55, 55, 55, 63, 63, 63, 63, 63, 63, 63, 63, + 7, 7, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15, 15, 15, + 23, 23, 23, 23, 23, 23, 23, 23, 31, 31, 31, 31, 31, 31, 31, 31, + 39, 39, 39, 39, 39, 39, 39, 39, 47, 47, 47, 47, 47, 47, 47, 47, + 55, 55, 55, 55, 55, 55, 55, 55, 63, 63, 63, 63, 63, 63, 63, 63, + 131,131,131,131,131,131,131,131,139,139,139,139,139,139,139,139, + 147,147,147,147,147,147,147,147,155,155,155,155,155,155,155,155, + 163,163,163,163,163,163,163,163,171,171,171,171,171,171,171,171, + 179,179,179,179,179,179,179,179,187,187,187,187,187,187,187,187, + 131,131,131,131,131,131,131,131,139,139,139,139,139,139,139,139, + 147,147,147,147,147,147,147,147,155,155,155,155,155,155,155,155, + 163,163,163,163,163,163,163,163,171,171,171,171,171,171,171,171, + 179,179,179,179,179,179,179,179,187,187,187,187,187,187,187,187, +}; + +/* negTable[i] = (((i & 0x0f) != 0) << 4) | ((i == 0x80) << 2) | 2 | (i != 0), i = 0..255 */ +static const uint8 negTable[256] = { + 2,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 7,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, +}; + +/* rrdrldTable[i] = (i << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) | parityTable[i], i = 0..255 */ +static const uint16 rrdrldTable[256] = { + 0x0044,0x0100,0x0200,0x0304,0x0400,0x0504,0x0604,0x0700, + 0x0808,0x090c,0x0a0c,0x0b08,0x0c0c,0x0d08,0x0e08,0x0f0c, + 0x1000,0x1104,0x1204,0x1300,0x1404,0x1500,0x1600,0x1704, + 0x180c,0x1908,0x1a08,0x1b0c,0x1c08,0x1d0c,0x1e0c,0x1f08, + 0x2020,0x2124,0x2224,0x2320,0x2424,0x2520,0x2620,0x2724, + 0x282c,0x2928,0x2a28,0x2b2c,0x2c28,0x2d2c,0x2e2c,0x2f28, + 0x3024,0x3120,0x3220,0x3324,0x3420,0x3524,0x3624,0x3720, + 0x3828,0x392c,0x3a2c,0x3b28,0x3c2c,0x3d28,0x3e28,0x3f2c, + 0x4000,0x4104,0x4204,0x4300,0x4404,0x4500,0x4600,0x4704, + 0x480c,0x4908,0x4a08,0x4b0c,0x4c08,0x4d0c,0x4e0c,0x4f08, + 0x5004,0x5100,0x5200,0x5304,0x5400,0x5504,0x5604,0x5700, + 0x5808,0x590c,0x5a0c,0x5b08,0x5c0c,0x5d08,0x5e08,0x5f0c, + 0x6024,0x6120,0x6220,0x6324,0x6420,0x6524,0x6624,0x6720, + 0x6828,0x692c,0x6a2c,0x6b28,0x6c2c,0x6d28,0x6e28,0x6f2c, + 0x7020,0x7124,0x7224,0x7320,0x7424,0x7520,0x7620,0x7724, + 0x782c,0x7928,0x7a28,0x7b2c,0x7c28,0x7d2c,0x7e2c,0x7f28, + 0x8080,0x8184,0x8284,0x8380,0x8484,0x8580,0x8680,0x8784, + 0x888c,0x8988,0x8a88,0x8b8c,0x8c88,0x8d8c,0x8e8c,0x8f88, + 0x9084,0x9180,0x9280,0x9384,0x9480,0x9584,0x9684,0x9780, + 0x9888,0x998c,0x9a8c,0x9b88,0x9c8c,0x9d88,0x9e88,0x9f8c, + 0xa0a4,0xa1a0,0xa2a0,0xa3a4,0xa4a0,0xa5a4,0xa6a4,0xa7a0, + 0xa8a8,0xa9ac,0xaaac,0xaba8,0xacac,0xada8,0xaea8,0xafac, + 0xb0a0,0xb1a4,0xb2a4,0xb3a0,0xb4a4,0xb5a0,0xb6a0,0xb7a4, + 0xb8ac,0xb9a8,0xbaa8,0xbbac,0xbca8,0xbdac,0xbeac,0xbfa8, + 0xc084,0xc180,0xc280,0xc384,0xc480,0xc584,0xc684,0xc780, + 0xc888,0xc98c,0xca8c,0xcb88,0xcc8c,0xcd88,0xce88,0xcf8c, + 0xd080,0xd184,0xd284,0xd380,0xd484,0xd580,0xd680,0xd784, + 0xd88c,0xd988,0xda88,0xdb8c,0xdc88,0xdd8c,0xde8c,0xdf88, + 0xe0a0,0xe1a4,0xe2a4,0xe3a0,0xe4a4,0xe5a0,0xe6a0,0xe7a4, + 0xe8ac,0xe9a8,0xeaa8,0xebac,0xeca8,0xedac,0xeeac,0xefa8, + 0xf0a4,0xf1a0,0xf2a0,0xf3a4,0xf4a0,0xf5a4,0xf6a4,0xf7a0, + 0xf8a8,0xf9ac,0xfaac,0xfba8,0xfcac,0xfda8,0xfea8,0xffac, +}; + +/* cpTable[i] = (i & 0x80) | (((i & 0xff) == 0) << 6), i = 0..255 */ +static const uint8 cpTable[256] = { + 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, +}; + +#if defined(DEBUG) || defined(iDEBUG) +static const char* Mnemonics[256] = +{ + "NOP", "LD BC,#h", "LD (BC),A", "INC BC", "INC B", "DEC B", "LD B,*h", "RLCA", + "EX AF,AF'", "ADD HL,BC", "LD A,(BC)", "DEC BC", "INC C", "DEC C", "LD C,*h", "RRCA", + "DJNZ @h", "LD DE,#h", "LD (DE),A", "INC DE", "INC D", "DEC D", "LD D,*h", "RLA", + "JR @h", "ADD HL,DE", "LD A,(DE)", "DEC DE", "INC E", "DEC E", "LD E,*h", "RRA", + "JR NZ,@h", "LD HL,#h", "LD (#h),HL", "INC HL", "INC H", "DEC H", "LD H,*h", "DAA", + "JR Z,@h", "ADD HL,HL", "LD HL,(#h)", "DEC HL", "INC L", "DEC L", "LD L,*h", "CPL", + "JR NC,@h", "LD SP,#h", "LD (#h),A", "INC SP", "INC (HL)", "DEC (HL)", "LD (HL),*h", "SCF", + "JR C,@h", "ADD HL,SP", "LD A,(#h)", "DEC SP", "INC A", "DEC A", "LD A,*h", "CCF", + "LD B,B", "LD B,C", "LD B,D", "LD B,E", "LD B,H", "LD B,L", "LD B,(HL)", "LD B,A", + "LD C,B", "LD C,C", "LD C,D", "LD C,E", "LD C,H", "LD C,L", "LD C,(HL)", "LD C,A", + "LD D,B", "LD D,C", "LD D,D", "LD D,E", "LD D,H", "LD D,L", "LD D,(HL)", "LD D,A", + "LD E,B", "LD E,C", "LD E,D", "LD E,E", "LD E,H", "LD E,L", "LD E,(HL)", "LD E,A", + "LD H,B", "LD H,C", "LD H,D", "LD H,E", "LD H,H", "LD H,L", "LD H,(HL)", "LD H,A", + "LD L,B", "LD L,C", "LD L,D", "LD L,E", "LD L,H", "LD L,L", "LD L,(HL)", "LD L,A", + "LD (HL),B", "LD (HL),C", "LD (HL),D", "LD (HL),E", "LD (HL),H", "LD (HL),L", "HALT", "LD (HL),A", + "LD A,B", "LD A,C", "LD A,D", "LD A,E", "LD A,H", "LD A,L", "LD A,(HL)", "LD A,A", + "ADD B", "ADD C", "ADD D", "ADD E", "ADD H", "ADD L", "ADD (HL)", "ADD A", + "ADC B", "ADC C", "ADC D", "ADC E", "ADC H", "ADC L", "ADC (HL)", "ADC A", + "SUB B", "SUB C", "SUB D", "SUB E", "SUB H", "SUB L", "SUB (HL)", "SUB A", + "SBC B", "SBC C", "SBC D", "SBC E", "SBC H", "SBC L", "SBC (HL)", "SBC A", + "AND B", "AND C", "AND D", "AND E", "AND H", "AND L", "AND (HL)", "AND A", + "XOR B", "XOR C", "XOR D", "XOR E", "XOR H", "XOR L", "XOR (HL)", "XOR A", + "OR B", "OR C", "OR D", "OR E", "OR H", "OR L", "OR (HL)", "OR A", + "CP B", "CP C", "CP D", "CP E", "CP H", "CP L", "CP (HL)", "CP A", + "RET NZ", "POP BC", "JP NZ,#h", "JP #h", "CALL NZ,#h", "PUSH BC", "ADD *h", "RST 00h", + "RET Z", "RET", "JP Z,#h", "PFX_CB", "CALL Z,#h", "CALL #h", "ADC *h", "RST 08h", + "RET NC", "POP DE", "JP NC,#h", "OUTA (*h)", "CALL NC,#h", "PUSH DE", "SUB *h", "RST 10h", + "RET C", "EXX", "JP C,#h", "INA (*h)", "CALL C,#h", "PFX_DD", "SBC *h", "RST 18h", + "RET PO", "POP HL", "JP PO,#h", "EX HL,(SP)", "CALL PO,#h", "PUSH HL", "AND *h", "RST 20h", + "RET PE", "LD PC,HL", "JP PE,#h", "EX DE,HL", "CALL PE,#h", "PFX_ED", "XOR *h", "RST 28h", + "RET P", "POP AF", "JP P,#h", "DI", "CALL P,#h", "PUSH AF", "OR *h", "RST 30h", + "RET M", "LD SP,HL", "JP M,#h", "EI", "CALL M,#h", "PFX_FD", "CP *h", "RST 38h" +}; + +static const char* MnemonicsCB[256] = +{ + "RLC B", "RLC C", "RLC D", "RLC E", "RLC H", "RLC L", "RLC (HL)", "RLC A", + "RRC B", "RRC C", "RRC D", "RRC E", "RRC H", "RRC L", "RRC (HL)", "RRC A", + "RL B", "RL C", "RL D", "RL E", "RL H", "RL L", "RL (HL)", "RL A", + "RR B", "RR C", "RR D", "RR E", "RR H", "RR L", "RR (HL)", "RR A", + "SLA B", "SLA C", "SLA D", "SLA E", "SLA H", "SLA L", "SLA (HL)", "SLA A", + "SRA B", "SRA C", "SRA D", "SRA E", "SRA H", "SRA L", "SRA (HL)", "SRA A", + "SLL B", "SLL C", "SLL D", "SLL E", "SLL H", "SLL L", "SLL (HL)", "SLL A", + "SRL B", "SRL C", "SRL D", "SRL E", "SRL H", "SRL L", "SRL (HL)", "SRL A", + "BIT 0,B", "BIT 0,C", "BIT 0,D", "BIT 0,E", "BIT 0,H", "BIT 0,L", "BIT 0,(HL)", "BIT 0,A", + "BIT 1,B", "BIT 1,C", "BIT 1,D", "BIT 1,E", "BIT 1,H", "BIT 1,L", "BIT 1,(HL)", "BIT 1,A", + "BIT 2,B", "BIT 2,C", "BIT 2,D", "BIT 2,E", "BIT 2,H", "BIT 2,L", "BIT 2,(HL)", "BIT 2,A", + "BIT 3,B", "BIT 3,C", "BIT 3,D", "BIT 3,E", "BIT 3,H", "BIT 3,L", "BIT 3,(HL)", "BIT 3,A", + "BIT 4,B", "BIT 4,C", "BIT 4,D", "BIT 4,E", "BIT 4,H", "BIT 4,L", "BIT 4,(HL)", "BIT 4,A", + "BIT 5,B", "BIT 5,C", "BIT 5,D", "BIT 5,E", "BIT 5,H", "BIT 5,L", "BIT 5,(HL)", "BIT 5,A", + "BIT 6,B", "BIT 6,C", "BIT 6,D", "BIT 6,E", "BIT 6,H", "BIT 6,L", "BIT 6,(HL)", "BIT 6,A", + "BIT 7,B", "BIT 7,C", "BIT 7,D", "BIT 7,E", "BIT 7,H", "BIT 7,L", "BIT 7,(HL)", "BIT 7,A", + "RES 0,B", "RES 0,C", "RES 0,D", "RES 0,E", "RES 0,H", "RES 0,L", "RES 0,(HL)", "RES 0,A", + "RES 1,B", "RES 1,C", "RES 1,D", "RES 1,E", "RES 1,H", "RES 1,L", "RES 1,(HL)", "RES 1,A", + "RES 2,B", "RES 2,C", "RES 2,D", "RES 2,E", "RES 2,H", "RES 2,L", "RES 2,(HL)", "RES 2,A", + "RES 3,B", "RES 3,C", "RES 3,D", "RES 3,E", "RES 3,H", "RES 3,L", "RES 3,(HL)", "RES 3,A", + "RES 4,B", "RES 4,C", "RES 4,D", "RES 4,E", "RES 4,H", "RES 4,L", "RES 4,(HL)", "RES 4,A", + "RES 5,B", "RES 5,C", "RES 5,D", "RES 5,E", "RES 5,H", "RES 5,L", "RES 5,(HL)", "RES 5,A", + "RES 6,B", "RES 6,C", "RES 6,D", "RES 6,E", "RES 6,H", "RES 6,L", "RES 6,(HL)", "RES 6,A", + "RES 7,B", "RES 7,C", "RES 7,D", "RES 7,E", "RES 7,H", "RES 7,L", "RES 7,(HL)", "RES 7,A", + "SET 0,B", "SET 0,C", "SET 0,D", "SET 0,E", "SET 0,H", "SET 0,L", "SET 0,(HL)", "SET 0,A", + "SET 1,B", "SET 1,C", "SET 1,D", "SET 1,E", "SET 1,H", "SET 1,L", "SET 1,(HL)", "SET 1,A", + "SET 2,B", "SET 2,C", "SET 2,D", "SET 2,E", "SET 2,H", "SET 2,L", "SET 2,(HL)", "SET 2,A", + "SET 3,B", "SET 3,C", "SET 3,D", "SET 3,E", "SET 3,H", "SET 3,L", "SET 3,(HL)", "SET 3,A", + "SET 4,B", "SET 4,C", "SET 4,D", "SET 4,E", "SET 4,H", "SET 4,L", "SET 4,(HL)", "SET 4,A", + "SET 5,B", "SET 5,C", "SET 5,D", "SET 5,E", "SET 5,H", "SET 5,L", "SET 5,(HL)", "SET 5,A", + "SET 6,B", "SET 6,C", "SET 6,D", "SET 6,E", "SET 6,H", "SET 6,L", "SET 6,(HL)", "SET 6,A", + "SET 7,B", "SET 7,C", "SET 7,D", "SET 7,E", "SET 7,H", "SET 7,L", "SET 7,(HL)", "SET 7,A" +}; + +static const char* MnemonicsED[256] = +{ + "DB EDh,00h", "DB EDh,01h", "DB EDh,02h", "DB EDh,03h", + "DB EDh,04h", "DB EDh,05h", "DB EDh,06h", "DB EDh,07h", + "DB EDh,08h", "DB EDh,09h", "DB EDh,0Ah", "DB EDh,0Bh", + "DB EDh,0Ch", "DB EDh,0Dh", "DB EDh,0Eh", "DB EDh,0Fh", + "DB EDh,10h", "DB EDh,11h", "DB EDh,12h", "DB EDh,13h", + "DB EDh,14h", "DB EDh,15h", "DB EDh,16h", "DB EDh,17h", + "DB EDh,18h", "DB EDh,19h", "DB EDh,1Ah", "DB EDh,1Bh", + "DB EDh,1Ch", "DB EDh,1Dh", "DB EDh,1Eh", "DB EDh,1Fh", + "DB EDh,20h", "DB EDh,21h", "DB EDh,22h", "DB EDh,23h", + "DB EDh,24h", "DB EDh,25h", "DB EDh,26h", "DB EDh,27h", + "DB EDh,28h", "DB EDh,29h", "DB EDh,2Ah", "DB EDh,2Bh", + "DB EDh,2Ch", "DB EDh,2Dh", "DB EDh,2Eh", "DB EDh,2Fh", + "DB EDh,30h", "DB EDh,31h", "DB EDh,32h", "DB EDh,33h", + "DB EDh,34h", "DB EDh,35h", "DB EDh,36h", "DB EDh,37h", + "DB EDh,38h", "DB EDh,39h", "DB EDh,3Ah", "DB EDh,3Bh", + "DB EDh,3Ch", "DB EDh,3Dh", "DB EDh,3Eh", "DB EDh,3Fh", + "IN B,(C)", "OUT (C),B", "SBC HL,BC", "LD (#h),BC", + "NEG", "RETN", "IM 0", "LD I,A", + "IN C,(C)", "OUT (C),C", "ADC HL,BC", "LD BC,(#h)", + "DB EDh,4Ch", "RETI", "DB EDh,4Eh", "LD R,A", + "IN D,(C)", "OUT (C),D", "SBC HL,DE", "LD (#h),DE", + "DB EDh,54h", "DB EDh,55h", "IM 1", "LD A,I", + "IN E,(C)", "OUT (C),E", "ADC HL,DE", "LD DE,(#h)", + "DB EDh,5Ch", "DB EDh,5Dh", "IM 2", "LD A,R", + "IN H,(C)", "OUT (C),H", "SBC HL,HL", "LD (#h),HL", + "DB EDh,64h", "DB EDh,65h", "DB EDh,66h", "RRD", + "IN L,(C)", "OUT (C),L", "ADC HL,HL", "LD HL,(#h)", + "DB EDh,6Ch", "DB EDh,6Dh", "DB EDh,6Eh", "RLD", + "IN F,(C)", "DB EDh,71h", "SBC HL,SP", "LD (#h),SP", + "DB EDh,74h", "DB EDh,75h", "DB EDh,76h", "DB EDh,77h", + "IN A,(C)", "OUT (C),A", "ADC HL,SP", "LD SP,(#h)", + "DB EDh,7Ch", "DB EDh,7Dh", "DB EDh,7Eh", "DB EDh,7Fh", + "DB EDh,80h", "DB EDh,81h", "DB EDh,82h", "DB EDh,83h", + "DB EDh,84h", "DB EDh,85h", "DB EDh,86h", "DB EDh,87h", + "DB EDh,88h", "DB EDh,89h", "DB EDh,8Ah", "DB EDh,8Bh", + "DB EDh,8Ch", "DB EDh,8Dh", "DB EDh,8Eh", "DB EDh,8Fh", + "DB EDh,90h", "DB EDh,91h", "DB EDh,92h", "DB EDh,93h", + "DB EDh,94h", "DB EDh,95h", "DB EDh,96h", "DB EDh,97h", + "DB EDh,98h", "DB EDh,99h", "DB EDh,9Ah", "DB EDh,9Bh", + "DB EDh,9Ch", "DB EDh,9Dh", "DB EDh,9Eh", "DB EDh,9Fh", + "LDI", "CPI", "INI", "OUTI", + "DB EDh,A4h", "DB EDh,A5h", "DB EDh,A6h", "DB EDh,A7h", + "LDD", "CPD", "IND", "OUTD", + "DB EDh,ACh", "DB EDh,ADh", "DB EDh,AEh", "DB EDh,AFh", + "LDIR", "CPIR", "INIR", "OTIR", + "DB EDh,B4h", "DB EDh,B5h", "DB EDh,B6h", "DB EDh,B7h", + "LDDR", "CPDR", "INDR", "OTDR", + "DB EDh,BCh", "DB EDh,BDh", "DB EDh,BEh", "DB EDh,BFh", + "DB EDh,C0h", "DB EDh,C1h", "DB EDh,C2h", "DB EDh,C3h", + "DB EDh,C4h", "DB EDh,C5h", "DB EDh,C6h", "DB EDh,C7h", + "DB EDh,C8h", "DB EDh,C9h", "DB EDh,CAh", "DB EDh,CBh", + "DB EDh,CCh", "DB EDh,CDh", "DB EDh,CEh", "DB EDh,CFh", + "DB EDh,D0h", "DB EDh,D1h", "DB EDh,D2h", "DB EDh,D3h", + "DB EDh,D4h", "DB EDh,D5h", "DB EDh,D6h", "DB EDh,D7h", + "DB EDh,D8h", "DB EDh,D9h", "DB EDh,DAh", "DB EDh,DBh", + "DB EDh,DCh", "DB EDh,DDh", "DB EDh,DEh", "DB EDh,DFh", + "DB EDh,E0h", "DB EDh,E1h", "DB EDh,E2h", "DB EDh,E3h", + "DB EDh,E4h", "DB EDh,E5h", "DB EDh,E6h", "DB EDh,E7h", + "DB EDh,E8h", "DB EDh,E9h", "DB EDh,EAh", "DB EDh,EBh", + "DB EDh,ECh", "DB EDh,EDh", "DB EDh,EEh", "DB EDh,EFh", + "DB EDh,F0h", "DB EDh,F1h", "DB EDh,F2h", "DB EDh,F3h", + "DB EDh,F4h", "DB EDh,F5h", "DB EDh,F6h", "DB EDh,F7h", + "DB EDh,F8h", "DB EDh,F9h", "DB EDh,FAh", "DB EDh,FBh", + "DB EDh,FCh", "DB EDh,FDh", "DB EDh,FEh", "DB EDh,FFh" +}; + +static const char* MnemonicsXX[256] = +{ + "NOP", "LD BC,#h", "LD (BC),A", "INC BC", "INC B", "DEC B", "LD B,*h", "RLCA", + "EX AF,AF'", "ADD I%,BC", "LD A,(BC)", "DEC BC", "INC C", "DEC C", "LD C,*h", "RRCA", + "DJNZ @h", "LD DE,#h", "LD (DE),A", "INC DE", "INC D", "DEC D", "LD D,*h", "RLA", + "JR @h", "ADD I%,DE", "LD A,(DE)", "DEC DE", "INC E", "DEC E", "LD E,*h", "RRA", + "JR NZ,@h", "LD I%,#h", "LD (#h),I%", "INC I%", "INC I%h", "DEC I%h", "LD I%h,*h", "DAA", + "JR Z,@h", "ADD I%,I%", "LD I%,(#h)", "DEC I%", "INC I%l", "DEC I%l", "LD I%l,*h", "CPL", + "JR NC,@h", "LD SP,#h", "LD (#h),A", "INC SP", "INC (I%+^h)", "DEC (I%+^h)", "LD (I%+^h),*h", "SCF", + "JR C,@h", "ADD I%,SP", "LD A,(#h)", "DEC SP", "INC A", "DEC A", "LD A,*h", "CCF", + "LD B,B", "LD B,C", "LD B,D", "LD B,E", "LD B,I%h", "LD B,I%l", "LD B,(I%+^h)", "LD B,A", + "LD C,B", "LD C,C", "LD C,D", "LD C,E", "LD C,I%h", "LD C,I%l", "LD C,(I%+^h)", "LD C,A", + "LD D,B", "LD D,C", "LD D,D", "LD D,E", "LD D,I%h", "LD D,I%l", "LD D,(I%+^h)", "LD D,A", + "LD E,B", "LD E,C", "LD E,D", "LD E,E", "LD E,I%h", "LD E,I%l", "LD E,(I%+^h)", "LD E,A", + "LD I%h,B", "LD I%h,C", "LD I%h,D", "LD I%h,E", "LD I%h,I%h", "LD I%h,I%l", "LD H,(I%+^h)", "LD I%h,A", + "LD I%l,B", "LD I%l,C", "LD I%l,D", "LD I%l,E", "LD I%l,I%h", "LD I%l,I%l", "LD L,(I%+^h)", "LD I%l,A", + "LD (I%+^h),B", "LD (I%+^h),C", "LD (I%+^h),D", "LD (I%+^h),E", "LD (I%+^h),H", "LD (I%+^h),L", "HALT", "LD (I%+^h),A", + "LD A,B", "LD A,C", "LD A,D", "LD A,E", "LD A,I%h", "LD A,I%l", "LD A,(I%+^h)", "LD A,A", + "ADD B", "ADD C", "ADD D", "ADD E", "ADD I%h", "ADD I%l", "ADD (I%+^h)", "ADD A", + "ADC B", "ADC C", "ADC D", "ADC E", "ADC I%h", "ADC I%l", "ADC (I%+^h)", "ADC,A", + "SUB B", "SUB C", "SUB D", "SUB E", "SUB I%h", "SUB I%l", "SUB (I%+^h)", "SUB A", + "SBC B", "SBC C", "SBC D", "SBC E", "SBC I%h", "SBC I%l", "SBC (I%+^h)", "SBC A", + "AND B", "AND C", "AND D", "AND E", "AND I%h", "AND I%l", "AND (I%+^h)", "AND A", + "XOR B", "XOR C", "XOR D", "XOR E", "XOR I%h", "XOR I%l", "XOR (I%+^h)", "XOR A", + "OR B", "OR C", "OR D", "OR E", "OR I%h", "OR I%l", "OR (I%+^h)", "OR A", + "CP B", "CP C", "CP D", "CP E", "CP I%h", "CP I%l", "CP (I%+^h)", "CP A", + "RET NZ", "POP BC", "JP NZ,#h", "JP #h", "CALL NZ,#h", "PUSH BC", "ADD *h", "RST 00h", + "RET Z", "RET", "JP Z,#h", "PFX_CB", "CALL Z,#h", "CALL #h", "ADC *h", "RST 08h", + "RET NC", "POP DE", "JP NC,#h", "OUTA (*h)", "CALL NC,#h", "PUSH DE", "SUB *h", "RST 10h", + "RET C", "EXX", "JP C,#h", "INA (*h)", "CALL C,#h", "PFX_DD", "SBC *h", "RST 18h", + "RET PO", "POP I%", "JP PO,#h", "EX I%,(SP)", "CALL PO,#h", "PUSH I%", "AND *h", "RST 20h", + "RET PE", "LD PC,I%", "JP PE,#h", "EX DE,I%", "CALL PE,#h", "PFX_ED", "XOR *h", "RST 28h", + "RET P", "POP AF", "JP P,#h", "DI", "CALL P,#h", "PUSH AF", "OR *h", "RST 30h", + "RET M", "LD SP,I%", "JP M,#h", "EI", "CALL M,#h", "PFX_FD", "CP *h", "RST 38h" +}; + +static const char* MnemonicsXCB[256] = +{ + "RLC B", "RLC C", "RLC D", "RLC E", "RLC H", "RLC L", "RLC (I%@h)", "RLC A", + "RRC B", "RRC C", "RRC D", "RRC E", "RRC H", "RRC L", "RRC (I%@h)", "RRC A", + "RL B", "RL C", "RL D", "RL E", "RL H", "RL L", "RL (I%@h)", "RL A", + "RR B", "RR C", "RR D", "RR E", "RR H", "RR L", "RR (I%@h)", "RR A", + "SLA B", "SLA C", "SLA D", "SLA E", "SLA H", "SLA L", "SLA (I%@h)", "SLA A", + "SRA B", "SRA C", "SRA D", "SRA E", "SRA H", "SRA L", "SRA (I%@h)", "SRA A", + "SLL B", "SLL C", "SLL D", "SLL E", "SLL H", "SLL L", "SLL (I%@h)", "SLL A", + "SRL B", "SRL C", "SRL D", "SRL E", "SRL H", "SRL L", "SRL (I%@h)", "SRL A", + "BIT 0,B", "BIT 0,C", "BIT 0,D", "BIT 0,E", "BIT 0,H", "BIT 0,L", "BIT 0,(I%@h)", "BIT 0,A", + "BIT 1,B", "BIT 1,C", "BIT 1,D", "BIT 1,E", "BIT 1,H", "BIT 1,L", "BIT 1,(I%@h)", "BIT 1,A", + "BIT 2,B", "BIT 2,C", "BIT 2,D", "BIT 2,E", "BIT 2,H", "BIT 2,L", "BIT 2,(I%@h)", "BIT 2,A", + "BIT 3,B", "BIT 3,C", "BIT 3,D", "BIT 3,E", "BIT 3,H", "BIT 3,L", "BIT 3,(I%@h)", "BIT 3,A", + "BIT 4,B", "BIT 4,C", "BIT 4,D", "BIT 4,E", "BIT 4,H", "BIT 4,L", "BIT 4,(I%@h)", "BIT 4,A", + "BIT 5,B", "BIT 5,C", "BIT 5,D", "BIT 5,E", "BIT 5,H", "BIT 5,L", "BIT 5,(I%@h)", "BIT 5,A", + "BIT 6,B", "BIT 6,C", "BIT 6,D", "BIT 6,E", "BIT 6,H", "BIT 6,L", "BIT 6,(I%@h)", "BIT 6,A", + "BIT 7,B", "BIT 7,C", "BIT 7,D", "BIT 7,E", "BIT 7,H", "BIT 7,L", "BIT 7,(I%@h)", "BIT 7,A", + "RES 0,B", "RES 0,C", "RES 0,D", "RES 0,E", "RES 0,H", "RES 0,L", "RES 0,(I%@h)", "RES 0,A", + "RES 1,B", "RES 1,C", "RES 1,D", "RES 1,E", "RES 1,H", "RES 1,L", "RES 1,(I%@h)", "RES 1,A", + "RES 2,B", "RES 2,C", "RES 2,D", "RES 2,E", "RES 2,H", "RES 2,L", "RES 2,(I%@h)", "RES 2,A", + "RES 3,B", "RES 3,C", "RES 3,D", "RES 3,E", "RES 3,H", "RES 3,L", "RES 3,(I%@h)", "RES 3,A", + "RES 4,B", "RES 4,C", "RES 4,D", "RES 4,E", "RES 4,H", "RES 4,L", "RES 4,(I%@h)", "RES 4,A", + "RES 5,B", "RES 5,C", "RES 5,D", "RES 5,E", "RES 5,H", "RES 5,L", "RES 5,(I%@h)", "RES 5,A", + "RES 6,B", "RES 6,C", "RES 6,D", "RES 6,E", "RES 6,H", "RES 6,L", "RES 6,(I%@h)", "RES 6,A", + "RES 7,B", "RES 7,C", "RES 7,D", "RES 7,E", "RES 7,H", "RES 7,L", "RES 7,(I%@h)", "RES 7,A", + "SET 0,B", "SET 0,C", "SET 0,D", "SET 0,E", "SET 0,H", "SET 0,L", "SET 0,(I%@h)", "SET 0,A", + "SET 1,B", "SET 1,C", "SET 1,D", "SET 1,E", "SET 1,H", "SET 1,L", "SET 1,(I%@h)", "SET 1,A", + "SET 2,B", "SET 2,C", "SET 2,D", "SET 2,E", "SET 2,H", "SET 2,L", "SET 2,(I%@h)", "SET 2,A", + "SET 3,B", "SET 3,C", "SET 3,D", "SET 3,E", "SET 3,H", "SET 3,L", "SET 3,(I%@h)", "SET 3,A", + "SET 4,B", "SET 4,C", "SET 4,D", "SET 4,E", "SET 4,H", "SET 4,L", "SET 4,(I%@h)", "SET 4,A", + "SET 5,B", "SET 5,C", "SET 5,D", "SET 5,E", "SET 5,H", "SET 5,L", "SET 5,(I%@h)", "SET 5,A", + "SET 6,B", "SET 6,C", "SET 6,D", "SET 6,E", "SET 6,H", "SET 6,L", "SET 6,(I%@h)", "SET 6,A", + "SET 7,B", "SET 7,C", "SET 7,D", "SET 7,E", "SET 7,H", "SET 7,L", "SET 7,(I%@h)", "SET 7,A" +}; + +static const char* CPMCalls[41] = +{ + "System Reset", "Console Input", "Console Output", "Reader Input", "Punch Output", "List Output", "Direct I/O", "Get IOByte", + "Set IOByte", "Print String", "Read Buffered", "Console Status", "Get Version", "Reset Disk", "Select Disk", "Open File", + "Close File", "Search First", "Search Next", "Delete File", "Read Sequential", "Write Sequential", "Make File", "Rename File", + "Get Login Vector", "Get Current Disk", "Set DMA Address", "Get Alloc", "Write Protect Disk", "Get R/O Vector", "Set File Attr", "Get Disk Params", + "Get/Set User", "Read Random", "Write Random", "Get File Size", "Set Random Record", "Reset Drive", "N/A", "N/A", "Write Random 0 fill" +}; + +int32 Watch = -1; +#endif + +/* Memory management */ +static uint8 GET_BYTE(uint32 Addr) { + return _RamRead(Addr & ADDRMASK); +} + +static void PUT_BYTE(uint32 Addr, uint32 Value) { + _RamWrite(Addr & ADDRMASK, Value); +} + +static uint16 GET_WORD(uint32 a) { + return GET_BYTE(a) | (GET_BYTE(a + 1) << 8); +} + +static void PUT_WORD(uint32 Addr, uint32 Value) { + _RamWrite(Addr, Value); + _RamWrite(++Addr, Value >> 8); +} + +#define RAM_MM(a) GET_BYTE(a--) +#define RAM_PP(a) GET_BYTE(a++) + +#define PUT_BYTE_PP(a,v) PUT_BYTE(a++, v) +#define PUT_BYTE_MM(a,v) PUT_BYTE(a--, v) +#define MM_PUT_BYTE(a,v) PUT_BYTE(--a, v) + +#define PUSH(x) do { \ + MM_PUT_BYTE(SP, (x) >> 8); \ + MM_PUT_BYTE(SP, x); \ +} while (0) + +/* Macros for the IN/OUT instructions INI/INIR/IND/INDR/OUTI/OTIR/OUTD/OTDR + +Pre condition +temp == value of register B at entry of the instruction +acu == value of transferred byte (IN or OUT) +Post condition +F is set correctly + +Use INOUTFLAGS_ZERO(x) for INIR/INDR/OTIR/OTDR where +x == (C + 1) & 0xff for INIR +x == L for OTIR and OTDR +x == (C - 1) & 0xff for INDR +Use INOUTFLAGS_NONZERO(x) for INI/IND/OUTI/OUTD where +x == (C + 1) & 0xff for INI +x == L for OUTI and OUTD +x == (C - 1) & 0xff for IND +*/ +#define INOUTFLAGS(syxz, x) \ + AF = (AF & 0xff00) | (syxz) | /* SF, YF, XF, ZF */ \ + ((acu & 0x80) >> 6) | /* NF */ \ + ((acu + (x)) > 0xff ? (FLAG_C | FLAG_H) : 0) | /* CF, HF */ \ + parityTable[((acu + (x)) & 7) ^ temp] /* PF */ + +#define INOUTFLAGS_ZERO(x) INOUTFLAGS(FLAG_Z, x) +#define INOUTFLAGS_NONZERO(x) \ + INOUTFLAGS((HIGH_REGISTER(BC) & 0xa8) | ((HIGH_REGISTER(BC) == 0) << 6), x) + +static inline void Z80reset(void) { + PC = 0; + IFF = 0; + IR = 0; + Status = 0; + Debug = 0; + Break = -1; + Step = -1; +} + +#ifdef DEBUG +void watchprint(uint16 pos) { + uint8 I, J; + _puts("\r\n"); + _puts(" Watch : "); _puthex16(Watch); + _puts(" = "); _puthex8(_RamRead(Watch)); _putcon(':'); _puthex8(_RamRead(Watch + 1)); + _puts(" / "); + for (J = 0, I = _RamRead(Watch); J < 8; ++J, I <<= 1) _putcon(I & 0x80 ? '1' : '0'); + _putcon(':'); + for (J = 0, I = _RamRead(Watch + 1); J < 8; ++J, I <<= 1) _putcon(I & 0x80 ? '1' : '0'); +} + +void memdump(uint16 pos) { + uint16 h = pos; + uint16 c = pos; + uint8 l, i; + uint8 ch = pos & 0xff; + + _puts(" "); + for (i = 0; i < 16; ++i) { + _puthex8(ch++ & 0x0f); + _puts(" "); + } + _puts("\r\n"); + _puts(" "); + for (i = 0; i < 16; ++i) + _puts("---"); + _puts("\r\n"); + for (l = 0; l < 16; ++l) { + _puthex16(h); + _puts(" : "); + for (i = 0; i < 16; ++i) { + _puthex8(_RamRead(h++)); + _puts(" "); + } + for (i = 0; i < 16; ++i) { + ch = _RamRead(c++); + _putcon(ch > 31 && ch < 127 ? ch : '.'); + } + _puts("\r\n"); + } +} + +uint8 Disasm(uint16 pos) { + const char* txt; + char jr; + uint8 ch = _RamRead(pos); + uint8 count = 1; + uint8 C = 0; + + switch (ch) { + case 0xCB: ++pos; txt = MnemonicsCB[_RamRead(pos++)]; count++; break; + case 0xED: ++pos; txt = MnemonicsED[_RamRead(pos++)]; count++; break; + case 0xDD: ++pos; C = 'X'; + if (_RamRead(pos) != 0xCB) { + txt = MnemonicsXX[_RamRead(pos++)]; ++count; + } else { + ++pos; txt = MnemonicsXCB[_RamRead(pos++)]; count += 2; + } + break; + case 0xFD: ++pos; C = 'Y'; + if (_RamRead(pos) != 0xCB) { + txt = MnemonicsXX[_RamRead(pos++)]; ++count; + } else { + ++pos; txt = MnemonicsXCB[_RamRead(pos++)]; count += 2; + } + break; + default: txt = Mnemonics[_RamRead(pos++)]; + } + while (*txt != 0) { + switch (*txt) { + case '*': + txt += 2; + ++count; + _puthex8(_RamRead(pos++)); + break; + case '^': + txt += 2; + ++count; + _puthex8(_RamRead(pos++)); + break; + case '#': + txt += 2; + count += 2; + _puthex8(_RamRead(pos + 1)); + _puthex8(_RamRead(pos)); + break; + case '@': + txt += 2; + ++count; + jr = _RamRead(pos++); + _puthex16(pos + jr); + break; + case '%': + _putch(C); + ++txt; + break; + default: + _putch(*txt); + ++txt; + } + } + + return(count); +} + +void Z80debug(void) { + uint8 ch = 0; + uint16 pos, l; + static const char Flags[9] = "SZ5H3PNC"; + uint8 J, I; + unsigned int bpoint; + uint8 loop = TRUE; + uint8 res = 0; + + _puts("\r\nDebug Mode - Press '?' for help"); + + while (loop) { + pos = PC; + _puts("\r\n"); + _puts("BC:"); _puthex16(BC); + _puts(" DE:"); _puthex16(DE); + _puts(" HL:"); _puthex16(HL); + _puts(" AF:"); _puthex16(AF); + _puts(" : ["); + for (J = 0, I = LOW_REGISTER(AF); J < 8; ++J, I <<= 1) _putcon(I & 0x80 ? Flags[J] : '.'); + _puts("]\r\n"); + _puts("IX:"); _puthex16(IX); + _puts(" IY:"); _puthex16(IY); + _puts(" SP:"); _puthex16(SP); + _puts(" PC:"); _puthex16(PC); + _puts(" : "); + + Disasm(pos); + + if (PC == 0x0005) { + if (LOW_REGISTER(BC) > 40) { + _puts(" (Unknown)"); + } else { + _puts(" ("); + _puts(CPMCalls[LOW_REGISTER(BC)]); + _puts(")"); + } + } + + if (Watch != -1) { + watchprint(Watch); + } + + _puts("\r\n"); + _puts("Command|? : "); + ch = _getch(); + if (ch > 21 && ch < 127) + _putch(ch); + switch (ch) { + case 't': + loop = FALSE; + break; + case 'c': + loop = FALSE; + _puts("\r\n"); + Debug = 0; + break; + case 'b': + _puts("\r\n"); memdump(BC); break; + case 'd': + _puts("\r\n"); memdump(DE); break; + case 'h': + _puts("\r\n"); memdump(HL); break; + case 'p': + _puts("\r\n"); memdump(PC & 0xFF00); break; + case 's': + _puts("\r\n"); memdump(SP & 0xFF00); break; + case 'x': + _puts("\r\n"); memdump(IX & 0xFF00); break; + case 'y': + _puts("\r\n"); memdump(IY & 0xFF00); break; + case 'a': + _puts("\r\n"); memdump(dmaAddr); break; + case 'l': + _puts("\r\n"); + I = 16; + l = pos; + while (I > 0) { + _puthex16(l); + _puts(" : "); + l += Disasm(l); + _puts("\r\n"); + --I; + } + break; + case 'B': + _puts(" Addr: "); + res=scanf("%04x", &bpoint); + if (res) { + Break = bpoint; + _puts("Breakpoint set to "); + _puthex16(Break); + _puts("\r\n"); + } + break; + case 'C': + Break = -1; + _puts(" Breakpoint cleared\r\n"); + break; + case 'D': + _puts(" Addr: "); + res=scanf("%04x", &bpoint); + if(res) + memdump(bpoint); + break; + case 'L': + _puts(" Addr: "); + res=scanf("%04x", &bpoint); + if (res) { + I = 16; + l = bpoint; + while (I > 0) { + _puthex16(l); + _puts(" : "); + l += Disasm(l); + _puts("\r\n"); + --I; + } + } + break; + case 'T': + loop = FALSE; + Step = pos + 3; // This only works correctly with CALL + // If the called function messes with the stack, this will fail as well. + Debug = 0; + break; + case 'W': + _puts(" Addr: "); + res=scanf("%04x", &bpoint); + if (res) { + Watch = bpoint; + _puts("Watch set to "); + _puthex16(Watch); + _puts("\r\n"); + } + break; + case '?': + _puts("\r\n"); + _puts("Lowercase commands:\r\n"); + _puts(" t - traces to the next instruction\r\n"); + _puts(" c - Continue execution\r\n"); + _puts(" b - Dumps memory pointed by (BC)\r\n"); + _puts(" d - Dumps memory pointed by (DE)\r\n"); + _puts(" h - Dumps memory pointed by (HL)\r\n"); + _puts(" p - Dumps the page (PC) points to\r\n"); + _puts(" s - Dumps the page (SP) points to\r\n"); + _puts(" x - Dumps the page (IX) points to\r\n"); + _puts(" y - Dumps the page (IY) points to\r\n"); + _puts(" a - Dumps memory pointed by dmaAddr\r\n"); + _puts(" l - Disassembles from current PC\r\n"); + _puts("Uppercase commands:\r\n"); + _puts(" B - Sets breakpoint at address\r\n"); + _puts(" C - Clears breakpoint\r\n"); + _puts(" D - Dumps memory at address\r\n"); + _puts(" L - Disassembles at address\r\n"); + _puts(" T - Steps over a call\r\n"); + _puts(" W - Sets a byte/word watch\r\n"); + break; + default: + _puts(" ???\r\n"); + } + } +} +#endif + +static inline void Z80run(void) { + uint32 temp = 0; + uint32 acu; + uint32 sum; + uint32 cbits; + uint32 op; + uint32 adr; + + /* main instruction fetch/decode loop */ + while (!Status) { /* loop until Status != 0 */ + +#ifdef DEBUG + if (PC == Break) { + _puts(":BREAK at "); + _puthex16(Break); + _puts(":"); + Debug = 1; + } + if (PC == Step) { + Debug = 1; + Step = -1; + } + if (Debug) + Z80debug(); +#endif + + PCX = PC; + INCR(1); /* Add one M1 cycle to refresh counter */ + +#ifdef iDEBUG + iLogFile = fopen("iDump.log", "a"); + switch (RAM[PCX & 0xffff]) { + case 0xCB: iLogTxt = MnemonicsCB[RAM[(PCX & 0xffff) + 1]]; break; + case 0xED: iLogTxt = MnemonicsED[RAM[(PCX & 0xffff) + 1]]; break; + case 0xDD: + case 0xFD: + if (RAM[PCX & 0xffff] == 0xCB) { + iLogTxt = MnemonicsXCB[RAM[(PCX & 0xffff) + 1]]; break; + } else { + iLogTxt = MnemonicsXX[RAM[(PCX & 0xffff) + 1]]; break; + } + default: iLogTxt = Mnemonics[RAM[PCX & 0xffff]]; + } + sprintf(iLogBuffer, "0x%04x : 0x%02x = %s\n", PCX, RAM[PCX & 0xffff], iLogTxt); + fputs(iLogBuffer, iLogFile); + fclose(iLogFile); +#endif + + switch (RAM_PP(PC)) { + + case 0x00: /* NOP */ + break; + + case 0x01: /* LD BC,nnnn */ + BC = GET_WORD(PC); + PC += 2; + break; + + case 0x02: /* LD (BC),A */ + PUT_BYTE(BC, HIGH_REGISTER(AF)); + break; + + case 0x03: /* INC BC */ + ++BC; + break; + + case 0x04: /* INC B */ + BC += 0x100; + temp = HIGH_REGISTER(BC); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); /* SET_PV2 uses temp */ + break; + + case 0x05: /* DEC B */ + BC -= 0x100; + temp = HIGH_REGISTER(BC); + AF = (AF & ~0xfe) | decTable[temp] | SET_PV2(0x7f); /* SET_PV2 uses temp */ + break; + + case 0x06: /* LD B,nn */ + SET_HIGH_REGISTER(BC, RAM_PP(PC)); + break; + + case 0x07: /* RLCA */ + AF = ((AF >> 7) & 0x0128) | ((AF << 1) & ~0x1ff) | + (AF & 0xc4) | ((AF >> 15) & 1); + break; + + case 0x08: /* EX AF,AF' */ + temp = AF; + AF = AF1; + AF1 = temp; + break; + + case 0x09: /* ADD HL,BC */ + HL &= ADDRMASK; + BC &= ADDRMASK; + sum = HL + BC; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(HL ^ BC ^ sum) >> 8]; + HL = sum; + break; + + case 0x0a: /* LD A,(BC) */ + SET_HIGH_REGISTER(AF, GET_BYTE(BC)); + break; + + case 0x0b: /* DEC BC */ + --BC; + break; + + case 0x0c: /* INC C */ + temp = LOW_REGISTER(BC) + 1; + SET_LOW_REGISTER(BC, temp); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); + break; + + case 0x0d: /* DEC C */ + temp = LOW_REGISTER(BC) - 1; + SET_LOW_REGISTER(BC, temp); + AF = (AF & ~0xfe) | decTable[temp & 0xff] | SET_PV2(0x7f); + break; + + case 0x0e: /* LD C,nn */ + SET_LOW_REGISTER(BC, RAM_PP(PC)); + break; + + case 0x0f: /* RRCA */ + AF = (AF & 0xc4) | rrcaTable[HIGH_REGISTER(AF)]; + break; + + case 0x10: /* DJNZ dd */ + if ((BC -= 0x100) & 0xff00) + PC += (int8)GET_BYTE(PC) + 1; + else + ++PC; + break; + + case 0x11: /* LD DE,nnnn */ + DE = GET_WORD(PC); + PC += 2; + break; + + case 0x12: /* LD (DE),A */ + PUT_BYTE(DE, HIGH_REGISTER(AF)); + break; + + case 0x13: /* INC DE */ + ++DE; + break; + + case 0x14: /* INC D */ + DE += 0x100; + temp = HIGH_REGISTER(DE); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); /* SET_PV2 uses temp */ + break; + + case 0x15: /* DEC D */ + DE -= 0x100; + temp = HIGH_REGISTER(DE); + AF = (AF & ~0xfe) | decTable[temp] | SET_PV2(0x7f); /* SET_PV2 uses temp */ + break; + + case 0x16: /* LD D,nn */ + SET_HIGH_REGISTER(DE, RAM_PP(PC)); + break; + + case 0x17: /* RLA */ + AF = ((AF << 8) & 0x0100) | ((AF >> 7) & 0x28) | ((AF << 1) & ~0x01ff) | + (AF & 0xc4) | ((AF >> 15) & 1); + break; + + case 0x18: /* JR dd */ + PC += (int8)GET_BYTE(PC) + 1; + break; + + case 0x19: /* ADD HL,DE */ + HL &= ADDRMASK; + DE &= ADDRMASK; + sum = HL + DE; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(HL ^ DE ^ sum) >> 8]; + HL = sum; + break; + + case 0x1a: /* LD A,(DE) */ + SET_HIGH_REGISTER(AF, GET_BYTE(DE)); + break; + + case 0x1b: /* DEC DE */ + --DE; + break; + + case 0x1c: /* INC E */ + temp = LOW_REGISTER(DE) + 1; + SET_LOW_REGISTER(DE, temp); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); + break; + + case 0x1d: /* DEC E */ + temp = LOW_REGISTER(DE) - 1; + SET_LOW_REGISTER(DE, temp); + AF = (AF & ~0xfe) | decTable[temp & 0xff] | SET_PV2(0x7f); + break; + + case 0x1e: /* LD E,nn */ + SET_LOW_REGISTER(DE, RAM_PP(PC)); + break; + + case 0x1f: /* RRA */ + AF = ((AF & 1) << 15) | (AF & 0xc4) | rraTable[HIGH_REGISTER(AF)]; + break; + + case 0x20: /* JR NZ,dd */ + if (TSTFLAG(Z)) + ++PC; + else + PC += (int8)GET_BYTE(PC) + 1; + break; + + case 0x21: /* LD HL,nnnn */ + HL = GET_WORD(PC); + PC += 2; + break; + + case 0x22: /* LD (nnnn),HL */ + PUT_WORD(GET_WORD(PC), HL); + PC += 2; + break; + + case 0x23: /* INC HL */ + ++HL; + break; + + case 0x24: /* INC H */ + HL += 0x100; + temp = HIGH_REGISTER(HL); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); /* SET_PV2 uses temp */ + break; + + case 0x25: /* DEC H */ + HL -= 0x100; + temp = HIGH_REGISTER(HL); + AF = (AF & ~0xfe) | decTable[temp] | SET_PV2(0x7f); /* SET_PV2 uses temp */ + break; + + case 0x26: /* LD H,nn */ + SET_HIGH_REGISTER(HL, RAM_PP(PC)); + break; + + case 0x27: /* DAA */ + acu = HIGH_REGISTER(AF); + temp = LOW_DIGIT(acu); + cbits = TSTFLAG(C); + if (TSTFLAG(N)) { /* last operation was a subtract */ + int hd = cbits || acu > 0x99; + if (TSTFLAG(H) || (temp > 9)) { /* adjust low digit */ + if (temp > 5) + SETFLAG(H, 0); + acu -= 6; + acu &= 0xff; + } + if (hd) + acu -= 0x160; /* adjust high digit */ + } else { /* last operation was an add */ + if (TSTFLAG(H) || (temp > 9)) { /* adjust low digit */ + SETFLAG(H, (temp > 9)); + acu += 6; + } + if (cbits || ((acu & 0x1f0) > 0x90)) + acu += 0x60; /* adjust high digit */ + } + AF = (AF & 0x12) | rrdrldTable[acu & 0xff] | ((acu >> 8) & 1) | cbits; + break; + + case 0x28: /* JR Z,dd */ + if (TSTFLAG(Z)) + PC += (int8)GET_BYTE(PC) + 1; + else + ++PC; + break; + + case 0x29: /* ADD HL,HL */ + HL &= ADDRMASK; + sum = HL + HL; + AF = (AF & ~0x3b) | cbitsDup16Table[sum >> 8]; + HL = sum; + break; + + case 0x2a: /* LD HL,(nnnn) */ + HL = GET_WORD(GET_WORD(PC)); + PC += 2; + break; + + case 0x2b: /* DEC HL */ + --HL; + break; + + case 0x2c: /* INC L */ + temp = LOW_REGISTER(HL) + 1; + SET_LOW_REGISTER(HL, temp); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); + break; + + case 0x2d: /* DEC L */ + temp = LOW_REGISTER(HL) - 1; + SET_LOW_REGISTER(HL, temp); + AF = (AF & ~0xfe) | decTable[temp & 0xff] | SET_PV2(0x7f); + break; + + case 0x2e: /* LD L,nn */ + SET_LOW_REGISTER(HL, RAM_PP(PC)); + break; + + case 0x2f: /* CPL */ + AF = (~AF & ~0xff) | (AF & 0xc5) | ((~AF >> 8) & 0x28) | 0x12; + break; + + case 0x30: /* JR NC,dd */ + if (TSTFLAG(C)) + ++PC; + else + PC += (int8)GET_BYTE(PC) + 1; + break; + + case 0x31: /* LD SP,nnnn */ + SP = GET_WORD(PC); + PC += 2; + break; + + case 0x32: /* LD (nnnn),A */ + PUT_BYTE(GET_WORD(PC), HIGH_REGISTER(AF)); + PC += 2; + break; + + case 0x33: /* INC SP */ + ++SP; + break; + + case 0x34: /* INC (HL) */ + temp = GET_BYTE(HL) + 1; + PUT_BYTE(HL, temp); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); + break; + + case 0x35: /* DEC (HL) */ + temp = GET_BYTE(HL) - 1; + PUT_BYTE(HL, temp); + AF = (AF & ~0xfe) | decTable[temp & 0xff] | SET_PV2(0x7f); + break; + + case 0x36: /* LD (HL),nn */ + PUT_BYTE(HL, RAM_PP(PC)); + break; + + case 0x37: /* SCF */ + AF = (AF & ~0x3b) | ((AF >> 8) & 0x28) | 1; + break; + + case 0x38: /* JR C,dd */ + if (TSTFLAG(C)) + PC += (int8)GET_BYTE(PC) + 1; + else + ++PC; + break; + + case 0x39: /* ADD HL,SP */ + HL &= ADDRMASK; + SP &= ADDRMASK; + sum = HL + SP; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(HL ^ SP ^ sum) >> 8]; + HL = sum; + break; + + case 0x3a: /* LD A,(nnnn) */ + SET_HIGH_REGISTER(AF, GET_BYTE(GET_WORD(PC))); + PC += 2; + break; + + case 0x3b: /* DEC SP */ + --SP; + break; + + case 0x3c: /* INC A */ + AF += 0x100; + temp = HIGH_REGISTER(AF); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); /* SET_PV2 uses temp */ + break; + + case 0x3d: /* DEC A */ + AF -= 0x100; + temp = HIGH_REGISTER(AF); + AF = (AF & ~0xfe) | decTable[temp] | SET_PV2(0x7f); /* SET_PV2 uses temp */ + break; + + case 0x3e: /* LD A,nn */ + SET_HIGH_REGISTER(AF, RAM_PP(PC)); + break; + + case 0x3f: /* CCF */ + AF = (AF & ~0x3b) | ((AF >> 8) & 0x28) | ((AF & 1) << 4) | (~AF & 1); + break; + + case 0x40: /* LD B,B */ + break; + + case 0x41: /* LD B,C */ + BC = (BC & 0xff) | ((BC & 0xff) << 8); + break; + + case 0x42: /* LD B,D */ + BC = (BC & 0xff) | (DE & ~0xff); + break; + + case 0x43: /* LD B,E */ + BC = (BC & 0xff) | ((DE & 0xff) << 8); + break; + + case 0x44: /* LD B,H */ + BC = (BC & 0xff) | (HL & ~0xff); + break; + + case 0x45: /* LD B,L */ + BC = (BC & 0xff) | ((HL & 0xff) << 8); + break; + + case 0x46: /* LD B,(HL) */ + SET_HIGH_REGISTER(BC, GET_BYTE(HL)); + break; + + case 0x47: /* LD B,A */ + BC = (BC & 0xff) | (AF & ~0xff); + break; + + case 0x48: /* LD C,B */ + BC = (BC & ~0xff) | ((BC >> 8) & 0xff); + break; + + case 0x49: /* LD C,C */ + break; + + case 0x4a: /* LD C,D */ + BC = (BC & ~0xff) | ((DE >> 8) & 0xff); + break; + + case 0x4b: /* LD C,E */ + BC = (BC & ~0xff) | (DE & 0xff); + break; + + case 0x4c: /* LD C,H */ + BC = (BC & ~0xff) | ((HL >> 8) & 0xff); + break; + + case 0x4d: /* LD C,L */ + BC = (BC & ~0xff) | (HL & 0xff); + break; + + case 0x4e: /* LD C,(HL) */ + SET_LOW_REGISTER(BC, GET_BYTE(HL)); + break; + + case 0x4f: /* LD C,A */ + BC = (BC & ~0xff) | ((AF >> 8) & 0xff); + break; + + case 0x50: /* LD D,B */ + DE = (DE & 0xff) | (BC & ~0xff); + break; + + case 0x51: /* LD D,C */ + DE = (DE & 0xff) | ((BC & 0xff) << 8); + break; + + case 0x52: /* LD D,D */ + break; + + case 0x53: /* LD D,E */ + DE = (DE & 0xff) | ((DE & 0xff) << 8); + break; + + case 0x54: /* LD D,H */ + DE = (DE & 0xff) | (HL & ~0xff); + break; + + case 0x55: /* LD D,L */ + DE = (DE & 0xff) | ((HL & 0xff) << 8); + break; + + case 0x56: /* LD D,(HL) */ + SET_HIGH_REGISTER(DE, GET_BYTE(HL)); + break; + + case 0x57: /* LD D,A */ + DE = (DE & 0xff) | (AF & ~0xff); + break; + + case 0x58: /* LD E,B */ + DE = (DE & ~0xff) | ((BC >> 8) & 0xff); + break; + + case 0x59: /* LD E,C */ + DE = (DE & ~0xff) | (BC & 0xff); + break; + + case 0x5a: /* LD E,D */ + DE = (DE & ~0xff) | ((DE >> 8) & 0xff); + break; + + case 0x5b: /* LD E,E */ + break; + + case 0x5c: /* LD E,H */ + DE = (DE & ~0xff) | ((HL >> 8) & 0xff); + break; + + case 0x5d: /* LD E,L */ + DE = (DE & ~0xff) | (HL & 0xff); + break; + + case 0x5e: /* LD E,(HL) */ + SET_LOW_REGISTER(DE, GET_BYTE(HL)); + break; + + case 0x5f: /* LD E,A */ + DE = (DE & ~0xff) | ((AF >> 8) & 0xff); + break; + + case 0x60: /* LD H,B */ + HL = (HL & 0xff) | (BC & ~0xff); + break; + + case 0x61: /* LD H,C */ + HL = (HL & 0xff) | ((BC & 0xff) << 8); + break; + + case 0x62: /* LD H,D */ + HL = (HL & 0xff) | (DE & ~0xff); + break; + + case 0x63: /* LD H,E */ + HL = (HL & 0xff) | ((DE & 0xff) << 8); + break; + + case 0x64: /* LD H,H */ + break; + + case 0x65: /* LD H,L */ + HL = (HL & 0xff) | ((HL & 0xff) << 8); + break; + + case 0x66: /* LD H,(HL) */ + SET_HIGH_REGISTER(HL, GET_BYTE(HL)); + break; + + case 0x67: /* LD H,A */ + HL = (HL & 0xff) | (AF & ~0xff); + break; + + case 0x68: /* LD L,B */ + HL = (HL & ~0xff) | ((BC >> 8) & 0xff); + break; + + case 0x69: /* LD L,C */ + HL = (HL & ~0xff) | (BC & 0xff); + break; + + case 0x6a: /* LD L,D */ + HL = (HL & ~0xff) | ((DE >> 8) & 0xff); + break; + + case 0x6b: /* LD L,E */ + HL = (HL & ~0xff) | (DE & 0xff); + break; + + case 0x6c: /* LD L,H */ + HL = (HL & ~0xff) | ((HL >> 8) & 0xff); + break; + + case 0x6d: /* LD L,L */ + break; + + case 0x6e: /* LD L,(HL) */ + SET_LOW_REGISTER(HL, GET_BYTE(HL)); + break; + + case 0x6f: /* LD L,A */ + HL = (HL & ~0xff) | ((AF >> 8) & 0xff); + break; + + case 0x70: /* LD (HL),B */ + PUT_BYTE(HL, HIGH_REGISTER(BC)); + break; + + case 0x71: /* LD (HL),C */ + PUT_BYTE(HL, LOW_REGISTER(BC)); + break; + + case 0x72: /* LD (HL),D */ + PUT_BYTE(HL, HIGH_REGISTER(DE)); + break; + + case 0x73: /* LD (HL),E */ + PUT_BYTE(HL, LOW_REGISTER(DE)); + break; + + case 0x74: /* LD (HL),H */ + PUT_BYTE(HL, HIGH_REGISTER(HL)); + break; + + case 0x75: /* LD (HL),L */ + PUT_BYTE(HL, LOW_REGISTER(HL)); + break; + + case 0x76: /* HALT */ +#ifdef DEBUG + _puts("\r\n::CPU HALTED::"); // A halt is a good indicator of broken code + _puts("Press any key..."); + _getch(); +#endif + --PC; + goto end_decode; + break; + + case 0x77: /* LD (HL),A */ + PUT_BYTE(HL, HIGH_REGISTER(AF)); + break; + + case 0x78: /* LD A,B */ + AF = (AF & 0xff) | (BC & ~0xff); + break; + + case 0x79: /* LD A,C */ + AF = (AF & 0xff) | ((BC & 0xff) << 8); + break; + + case 0x7a: /* LD A,D */ + AF = (AF & 0xff) | (DE & ~0xff); + break; + + case 0x7b: /* LD A,E */ + AF = (AF & 0xff) | ((DE & 0xff) << 8); + break; + + case 0x7c: /* LD A,H */ + AF = (AF & 0xff) | (HL & ~0xff); + break; + + case 0x7d: /* LD A,L */ + AF = (AF & 0xff) | ((HL & 0xff) << 8); + break; + + case 0x7e: /* LD A,(HL) */ + SET_HIGH_REGISTER(AF, GET_BYTE(HL)); + break; + + case 0x7f: /* LD A,A */ + break; + + case 0x80: /* ADD A,B */ + temp = HIGH_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x81: /* ADD A,C */ + temp = LOW_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x82: /* ADD A,D */ + temp = HIGH_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x83: /* ADD A,E */ + temp = LOW_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x84: /* ADD A,H */ + temp = HIGH_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x85: /* ADD A,L */ + temp = LOW_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x86: /* ADD A,(HL) */ + temp = GET_BYTE(HL); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x87: /* ADD A,A */ + cbits = 2 * HIGH_REGISTER(AF); + AF = cbitsDup8Table[cbits] | (SET_PVS(cbits)); + break; + + case 0x88: /* ADC A,B */ + temp = HIGH_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x89: /* ADC A,C */ + temp = LOW_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x8a: /* ADC A,D */ + temp = HIGH_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x8b: /* ADC A,E */ + temp = LOW_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x8c: /* ADC A,H */ + temp = HIGH_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x8d: /* ADC A,L */ + temp = LOW_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x8e: /* ADC A,(HL) */ + temp = GET_BYTE(HL); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x8f: /* ADC A,A */ + cbits = 2 * HIGH_REGISTER(AF) + TSTFLAG(C); + AF = cbitsDup8Table[cbits] | (SET_PVS(cbits)); + break; + + case 0x90: /* SUB B */ + temp = HIGH_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x91: /* SUB C */ + temp = LOW_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x92: /* SUB D */ + temp = HIGH_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x93: /* SUB E */ + temp = LOW_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x94: /* SUB H */ + temp = HIGH_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x95: /* SUB L */ + temp = LOW_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x96: /* SUB (HL) */ + temp = GET_BYTE(HL); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x97: /* SUB A */ + AF = 0x42; + break; + + case 0x98: /* SBC A,B */ + temp = HIGH_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x99: /* SBC A,C */ + temp = LOW_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x9a: /* SBC A,D */ + temp = HIGH_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x9b: /* SBC A,E */ + temp = LOW_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x9c: /* SBC A,H */ + temp = HIGH_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x9d: /* SBC A,L */ + temp = LOW_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x9e: /* SBC A,(HL) */ + temp = GET_BYTE(HL); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x9f: /* SBC A,A */ + cbits = -TSTFLAG(C); + AF = subTable[cbits & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PVS(cbits)); + break; + + case 0xa0: /* AND B */ + AF = andTable[((AF & BC) >> 8) & 0xff]; + break; + + case 0xa1: /* AND C */ + AF = andTable[((AF >> 8)& BC) & 0xff]; + break; + + case 0xa2: /* AND D */ + AF = andTable[((AF & DE) >> 8) & 0xff]; + break; + + case 0xa3: /* AND E */ + AF = andTable[((AF >> 8)& DE) & 0xff]; + break; + + case 0xa4: /* AND H */ + AF = andTable[((AF & HL) >> 8) & 0xff]; + break; + + case 0xa5: /* AND L */ + AF = andTable[((AF >> 8)& HL) & 0xff]; + break; + + case 0xa6: /* AND (HL) */ + AF = andTable[((AF >> 8)& GET_BYTE(HL)) & 0xff]; + break; + + case 0xa7: /* AND A */ + AF = andTable[(AF >> 8) & 0xff]; + break; + + case 0xa8: /* XOR B */ + AF = xororTable[((AF ^ BC) >> 8) & 0xff]; + break; + + case 0xa9: /* XOR C */ + AF = xororTable[((AF >> 8) ^ BC) & 0xff]; + break; + + case 0xaa: /* XOR D */ + AF = xororTable[((AF ^ DE) >> 8) & 0xff]; + break; + + case 0xab: /* XOR E */ + AF = xororTable[((AF >> 8) ^ DE) & 0xff]; + break; + + case 0xac: /* XOR H */ + AF = xororTable[((AF ^ HL) >> 8) & 0xff]; + break; + + case 0xad: /* XOR L */ + AF = xororTable[((AF >> 8) ^ HL) & 0xff]; + break; + + case 0xae: /* XOR (HL) */ + AF = xororTable[((AF >> 8) ^ GET_BYTE(HL)) & 0xff]; + break; + + case 0xaf: /* XOR A */ + AF = 0x44; + break; + + case 0xb0: /* OR B */ + AF = xororTable[((AF | BC) >> 8) & 0xff]; + break; + + case 0xb1: /* OR C */ + AF = xororTable[((AF >> 8) | BC) & 0xff]; + break; + + case 0xb2: /* OR D */ + AF = xororTable[((AF | DE) >> 8) & 0xff]; + break; + + case 0xb3: /* OR E */ + AF = xororTable[((AF >> 8) | DE) & 0xff]; + break; + + case 0xb4: /* OR H */ + AF = xororTable[((AF | HL) >> 8) & 0xff]; + break; + + case 0xb5: /* OR L */ + AF = xororTable[((AF >> 8) | HL) & 0xff]; + break; + + case 0xb6: /* OR (HL) */ + AF = xororTable[((AF >> 8) | GET_BYTE(HL)) & 0xff]; + break; + + case 0xb7: /* OR A */ + AF = xororTable[(AF >> 8) & 0xff]; + break; + + case 0xb8: /* CP B */ + temp = HIGH_REGISTER(BC); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + break; + + case 0xb9: /* CP C */ + temp = LOW_REGISTER(BC); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + break; + + case 0xba: /* CP D */ + temp = HIGH_REGISTER(DE); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + break; + + case 0xbb: /* CP E */ + temp = LOW_REGISTER(DE); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + break; + + case 0xbc: /* CP H */ + temp = HIGH_REGISTER(HL); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + break; + + case 0xbd: /* CP L */ + temp = LOW_REGISTER(HL); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + break; + + case 0xbe: /* CP (HL) */ + temp = GET_BYTE(HL); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + break; + + case 0xbf: /* CP A */ + SET_LOW_REGISTER(AF, (HIGH_REGISTER(AF) & 0x28) | 0x42); + break; + + case 0xc0: /* RET NZ */ + if (!(TSTFLAG(Z))) + POP(PC); + break; + + case 0xc1: /* POP BC */ + POP(BC); + break; + + case 0xc2: /* JP NZ,nnnn */ + JPC(!TSTFLAG(Z)); + break; + + case 0xc3: /* JP nnnn */ + JPC(1); + break; + + case 0xc4: /* CALL NZ,nnnn */ + CALLC(!TSTFLAG(Z)); + break; + + case 0xc5: /* PUSH BC */ + PUSH(BC); + break; + + case 0xc6: /* ADD A,nn */ + temp = RAM_PP(PC); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0xc7: /* RST 0 */ + PUSH(PC); + PC = 0; + break; + + case 0xc8: /* RET Z */ + if (TSTFLAG(Z)) + POP(PC); + break; + + case 0xc9: /* RET */ + POP(PC); + break; + + case 0xca: /* JP Z,nnnn */ + JPC(TSTFLAG(Z)); + break; + + case 0xcb: /* CB prefix */ + INCR(1); /* Add one M1 cycle to refresh counter */ + adr = HL; + switch ((op = GET_BYTE(PC)) & 7) { + + case 0: + ++PC; + acu = HIGH_REGISTER(BC); + break; + + case 1: + ++PC; + acu = LOW_REGISTER(BC); + break; + + case 2: + ++PC; + acu = HIGH_REGISTER(DE); + break; + + case 3: + ++PC; + acu = LOW_REGISTER(DE); + break; + + case 4: + ++PC; + acu = HIGH_REGISTER(HL); + break; + + case 5: + ++PC; + acu = LOW_REGISTER(HL); + break; + + case 6: + ++PC; + acu = GET_BYTE(adr); + break; + + case 7: + ++PC; + acu = HIGH_REGISTER(AF); + break; + } + switch (op & 0xc0) { + + case 0x00: /* shift/rotate */ + switch (op & 0x38) { + + case 0x00:/* RLC */ + temp = (acu << 1) | (acu >> 7); + cbits = temp & 1; + goto cbshflg1; + + case 0x08:/* RRC */ + temp = (acu >> 1) | (acu << 7); + cbits = temp & 0x80; + goto cbshflg1; + + case 0x10:/* RL */ + temp = (acu << 1) | TSTFLAG(C); + cbits = acu & 0x80; + goto cbshflg1; + + case 0x18:/* RR */ + temp = (acu >> 1) | (TSTFLAG(C) << 7); + cbits = acu & 1; + goto cbshflg1; + + case 0x20:/* SLA */ + temp = acu << 1; + cbits = acu & 0x80; + goto cbshflg1; + + case 0x28:/* SRA */ + temp = (acu >> 1) | (acu & 0x80); + cbits = acu & 1; + goto cbshflg1; + + case 0x30:/* SLIA */ + temp = (acu << 1) | 1; + cbits = acu & 0x80; + goto cbshflg1; + + case 0x38:/* SRL */ + temp = acu >> 1; + cbits = acu & 1; + cbshflg1: + AF = (AF & ~0xff) | rotateShiftTable[temp & 0xff] | !!cbits; + } + break; + + case 0x40: /* BIT */ + if (acu & (1 << ((op >> 3) & 7))) + AF = (AF & ~0xfe) | 0x10 | (((op & 0x38) == 0x38) << 7); + else + AF = (AF & ~0xfe) | 0x54; + if ((op & 7) != 6) + AF |= (acu & 0x28); + temp = acu; + break; + + case 0x80: /* RES */ + temp = acu & ~(1 << ((op >> 3) & 7)); + break; + + case 0xc0: /* SET */ + temp = acu | (1 << ((op >> 3) & 7)); + break; + } + switch (op & 7) { + + case 0: + SET_HIGH_REGISTER(BC, temp); + break; + + case 1: + SET_LOW_REGISTER(BC, temp); + break; + + case 2: + SET_HIGH_REGISTER(DE, temp); + break; + + case 3: + SET_LOW_REGISTER(DE, temp); + break; + + case 4: + SET_HIGH_REGISTER(HL, temp); + break; + + case 5: + SET_LOW_REGISTER(HL, temp); + break; + + case 6: + PUT_BYTE(adr, temp); + break; + + case 7: + SET_HIGH_REGISTER(AF, temp); + break; + } + break; + + case 0xcc: /* CALL Z,nnnn */ + CALLC(TSTFLAG(Z)); + break; + + case 0xcd: /* CALL nnnn */ + CALLC(1); + break; + + case 0xce: /* ADC A,nn */ + temp = RAM_PP(PC); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0xcf: /* RST 8 */ + PUSH(PC); + PC = 8; + break; + + case 0xd0: /* RET NC */ + if (!(TSTFLAG(C))) + POP(PC); + break; + + case 0xd1: /* POP DE */ + POP(DE); + break; + + case 0xd2: /* JP NC,nnnn */ + JPC(!TSTFLAG(C)); + break; + + case 0xd3: /* OUT (nn),A */ + cpu_out(RAM_PP(PC), HIGH_REGISTER(AF)); + break; + + case 0xd4: /* CALL NC,nnnn */ + CALLC(!TSTFLAG(C)); + break; + + case 0xd5: /* PUSH DE */ + PUSH(DE); + break; + + case 0xd6: /* SUB nn */ + temp = RAM_PP(PC); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0xd7: /* RST 10H */ + PUSH(PC); + PC = 0x10; + break; + + case 0xd8: /* RET C */ + if (TSTFLAG(C)) + POP(PC); + break; + + case 0xd9: /* EXX */ + temp = BC; + BC = BC1; + BC1 = temp; + temp = DE; + DE = DE1; + DE1 = temp; + temp = HL; + HL = HL1; + HL1 = temp; + break; + + case 0xda: /* JP C,nnnn */ + JPC(TSTFLAG(C)); + break; + + case 0xdb: /* IN A,(nn) */ + SET_HIGH_REGISTER(AF, cpu_in(RAM_PP(PC))); + break; + + case 0xdc: /* CALL C,nnnn */ + CALLC(TSTFLAG(C)); + break; + + case 0xdd: /* DD prefix */ + INCR(1); /* Add one M1 cycle to refresh counter */ + switch (RAM_PP(PC)) { + + case 0x09: /* ADD IX,BC */ + IX &= ADDRMASK; + BC &= ADDRMASK; + sum = IX + BC; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IX ^ BC ^ sum) >> 8]; + IX = sum; + break; + + case 0x19: /* ADD IX,DE */ + IX &= ADDRMASK; + DE &= ADDRMASK; + sum = IX + DE; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IX ^ DE ^ sum) >> 8]; + IX = sum; + break; + + case 0x21: /* LD IX,nnnn */ + IX = GET_WORD(PC); + PC += 2; + break; + + case 0x22: /* LD (nnnn),IX */ + PUT_WORD(GET_WORD(PC), IX); + PC += 2; + break; + + case 0x23: /* INC IX */ + ++IX; + break; + + case 0x24: /* INC IXH */ + IX += 0x100; + AF = (AF & ~0xfe) | incZ80Table[HIGH_REGISTER(IX)]; + break; + + case 0x25: /* DEC IXH */ + IX -= 0x100; + AF = (AF & ~0xfe) | decZ80Table[HIGH_REGISTER(IX)]; + break; + + case 0x26: /* LD IXH,nn */ + SET_HIGH_REGISTER(IX, RAM_PP(PC)); + break; + + case 0x29: /* ADD IX,IX */ + IX &= ADDRMASK; + sum = IX + IX; + AF = (AF & ~0x3b) | cbitsDup16Table[sum >> 8]; + IX = sum; + break; + + case 0x2a: /* LD IX,(nnnn) */ + IX = GET_WORD(GET_WORD(PC)); + PC += 2; + break; + + case 0x2b: /* DEC IX */ + --IX; + break; + + case 0x2c: /* INC IXL */ + temp = LOW_REGISTER(IX) + 1; + SET_LOW_REGISTER(IX, temp); + AF = (AF & ~0xfe) | incZ80Table[temp]; + break; + + case 0x2d: /* DEC IXL */ + temp = LOW_REGISTER(IX) - 1; + SET_LOW_REGISTER(IX, temp); + AF = (AF & ~0xfe) | decZ80Table[temp & 0xff]; + break; + + case 0x2e: /* LD IXL,nn */ + SET_LOW_REGISTER(IX, RAM_PP(PC)); + break; + + case 0x34: /* INC (IX+dd) */ + adr = IX + (int8)RAM_PP(PC); + temp = GET_BYTE(adr) + 1; + PUT_BYTE(adr, temp); + AF = (AF & ~0xfe) | incZ80Table[temp]; + break; + + case 0x35: /* DEC (IX+dd) */ + adr = IX + (int8)RAM_PP(PC); + temp = GET_BYTE(adr) - 1; + PUT_BYTE(adr, temp); + AF = (AF & ~0xfe) | decZ80Table[temp & 0xff]; + break; + + case 0x36: /* LD (IX+dd),nn */ + adr = IX + (int8)RAM_PP(PC); + PUT_BYTE(adr, RAM_PP(PC)); + break; + + case 0x39: /* ADD IX,SP */ + IX &= ADDRMASK; + SP &= ADDRMASK; + sum = IX + SP; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IX ^ SP ^ sum) >> 8]; + IX = sum; + break; + + case 0x44: /* LD B,IXH */ + SET_HIGH_REGISTER(BC, HIGH_REGISTER(IX)); + break; + + case 0x45: /* LD B,IXL */ + SET_HIGH_REGISTER(BC, LOW_REGISTER(IX)); + break; + + case 0x46: /* LD B,(IX+dd) */ + SET_HIGH_REGISTER(BC, GET_BYTE(IX + (int8)RAM_PP(PC))); + break; + + case 0x4c: /* LD C,IXH */ + SET_LOW_REGISTER(BC, HIGH_REGISTER(IX)); + break; + + case 0x4d: /* LD C,IXL */ + SET_LOW_REGISTER(BC, LOW_REGISTER(IX)); + break; + + case 0x4e: /* LD C,(IX+dd) */ + SET_LOW_REGISTER(BC, GET_BYTE(IX + (int8)RAM_PP(PC))); + break; + + case 0x54: /* LD D,IXH */ + SET_HIGH_REGISTER(DE, HIGH_REGISTER(IX)); + break; + + case 0x55: /* LD D,IXL */ + SET_HIGH_REGISTER(DE, LOW_REGISTER(IX)); + break; + + case 0x56: /* LD D,(IX+dd) */ + SET_HIGH_REGISTER(DE, GET_BYTE(IX + (int8)RAM_PP(PC))); + break; + + case 0x5c: /* LD E,IXH */ + SET_LOW_REGISTER(DE, HIGH_REGISTER(IX)); + break; + + case 0x5d: /* LD E,IXL */ + SET_LOW_REGISTER(DE, LOW_REGISTER(IX)); + break; + + case 0x5e: /* LD E,(IX+dd) */ + SET_LOW_REGISTER(DE, GET_BYTE(IX + (int8)RAM_PP(PC))); + break; + + case 0x60: /* LD IXH,B */ + SET_HIGH_REGISTER(IX, HIGH_REGISTER(BC)); + break; + + case 0x61: /* LD IXH,C */ + SET_HIGH_REGISTER(IX, LOW_REGISTER(BC)); + break; + + case 0x62: /* LD IXH,D */ + SET_HIGH_REGISTER(IX, HIGH_REGISTER(DE)); + break; + + case 0x63: /* LD IXH,E */ + SET_HIGH_REGISTER(IX, LOW_REGISTER(DE)); + break; + + case 0x64: /* LD IXH,IXH */ + break; + + case 0x65: /* LD IXH,IXL */ + SET_HIGH_REGISTER(IX, LOW_REGISTER(IX)); + break; + + case 0x66: /* LD H,(IX+dd) */ + SET_HIGH_REGISTER(HL, GET_BYTE(IX + (int8)RAM_PP(PC))); + break; + + case 0x67: /* LD IXH,A */ + SET_HIGH_REGISTER(IX, HIGH_REGISTER(AF)); + break; + + case 0x68: /* LD IXL,B */ + SET_LOW_REGISTER(IX, HIGH_REGISTER(BC)); + break; + + case 0x69: /* LD IXL,C */ + SET_LOW_REGISTER(IX, LOW_REGISTER(BC)); + break; + + case 0x6a: /* LD IXL,D */ + SET_LOW_REGISTER(IX, HIGH_REGISTER(DE)); + break; + + case 0x6b: /* LD IXL,E */ + SET_LOW_REGISTER(IX, LOW_REGISTER(DE)); + break; + + case 0x6c: /* LD IXL,IXH */ + SET_LOW_REGISTER(IX, HIGH_REGISTER(IX)); + break; + + case 0x6d: /* LD IXL,IXL */ + break; + + case 0x6e: /* LD L,(IX+dd) */ + SET_LOW_REGISTER(HL, GET_BYTE(IX + (int8)RAM_PP(PC))); + break; + + case 0x6f: /* LD IXL,A */ + SET_LOW_REGISTER(IX, HIGH_REGISTER(AF)); + break; + + case 0x70: /* LD (IX+dd),B */ + PUT_BYTE(IX + (int8)RAM_PP(PC), HIGH_REGISTER(BC)); + break; + + case 0x71: /* LD (IX+dd),C */ + PUT_BYTE(IX + (int8)RAM_PP(PC), LOW_REGISTER(BC)); + break; + + case 0x72: /* LD (IX+dd),D */ + PUT_BYTE(IX + (int8)RAM_PP(PC), HIGH_REGISTER(DE)); + break; + + case 0x73: /* LD (IX+dd),E */ + PUT_BYTE(IX + (int8)RAM_PP(PC), LOW_REGISTER(DE)); + break; + + case 0x74: /* LD (IX+dd),H */ + PUT_BYTE(IX + (int8)RAM_PP(PC), HIGH_REGISTER(HL)); + break; + + case 0x75: /* LD (IX+dd),L */ + PUT_BYTE(IX + (int8)RAM_PP(PC), LOW_REGISTER(HL)); + break; + + case 0x77: /* LD (IX+dd),A */ + PUT_BYTE(IX + (int8)RAM_PP(PC), HIGH_REGISTER(AF)); + break; + + case 0x7c: /* LD A,IXH */ + SET_HIGH_REGISTER(AF, HIGH_REGISTER(IX)); + break; + + case 0x7d: /* LD A,IXL */ + SET_HIGH_REGISTER(AF, LOW_REGISTER(IX)); + break; + + case 0x7e: /* LD A,(IX+dd) */ + SET_HIGH_REGISTER(AF, GET_BYTE(IX + (int8)RAM_PP(PC))); + break; + + case 0x84: /* ADD A,IXH */ + temp = HIGH_REGISTER(IX); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x85: /* ADD A,IXL */ + temp = LOW_REGISTER(IX); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x86: /* ADD A,(IX+dd) */ + adr = IX + (int8)RAM_PP(PC); + temp = GET_BYTE(adr); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x8c: /* ADC A,IXH */ + temp = HIGH_REGISTER(IX); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x8d: /* ADC A,IXL */ + temp = LOW_REGISTER(IX); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x8e: /* ADC A,(IX+dd) */ + adr = IX + (int8)RAM_PP(PC); + temp = GET_BYTE(adr); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x96: /* SUB (IX+dd) */ + adr = IX + (int8)RAM_PP(PC); + temp = GET_BYTE(adr); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0x94: /* SUB IXH */ + SETFLAG(C, 0);/* fall through, a bit less efficient but smaller code */ + + case 0x9c: /* SBC A,IXH */ + temp = HIGH_REGISTER(IX); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0x95: /* SUB IXL */ + SETFLAG(C, 0);/* fall through, a bit less efficient but smaller code */ + + case 0x9d: /* SBC A,IXL */ + temp = LOW_REGISTER(IX); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0x9e: /* SBC A,(IX+dd) */ + adr = IX + (int8)RAM_PP(PC); + temp = GET_BYTE(adr); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xa4: /* AND IXH */ + AF = andTable[((AF & IX) >> 8) & 0xff]; + break; + + case 0xa5: /* AND IXL */ + AF = andTable[((AF >> 8)& IX) & 0xff]; + break; + + case 0xa6: /* AND (IX+dd) */ + AF = andTable[((AF >> 8)& GET_BYTE(IX + (int8)RAM_PP(PC))) & 0xff]; + break; + + case 0xac: /* XOR IXH */ + AF = xororTable[((AF ^ IX) >> 8) & 0xff]; + break; + + case 0xad: /* XOR IXL */ + AF = xororTable[((AF >> 8) ^ IX) & 0xff]; + break; + + case 0xae: /* XOR (IX+dd) */ + AF = xororTable[((AF >> 8) ^ GET_BYTE(IX + (int8)RAM_PP(PC))) & 0xff]; + break; + + case 0xb4: /* OR IXH */ + AF = xororTable[((AF | IX) >> 8) & 0xff]; + break; + + case 0xb5: /* OR IXL */ + AF = xororTable[((AF >> 8) | IX) & 0xff]; + break; + + case 0xb6: /* OR (IX+dd) */ + AF = xororTable[((AF >> 8) | GET_BYTE(IX + (int8)RAM_PP(PC))) & 0xff]; + break; + + case 0xbc: /* CP IXH */ + temp = HIGH_REGISTER(IX); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xbd: /* CP IXL */ + temp = LOW_REGISTER(IX); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xbe: /* CP (IX+dd) */ + adr = IX + (int8)RAM_PP(PC); + temp = GET_BYTE(adr); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xcb: /* CB prefix */ + adr = IX + (int8)RAM_PP(PC); + switch ((op = GET_BYTE(PC)) & 7) { + + case 0: + ++PC; + acu = HIGH_REGISTER(BC); + break; + + case 1: + ++PC; + acu = LOW_REGISTER(BC); + break; + + case 2: + ++PC; + acu = HIGH_REGISTER(DE); + break; + + case 3: + ++PC; + acu = LOW_REGISTER(DE); + break; + + case 4: + ++PC; + acu = HIGH_REGISTER(HL); + break; + + case 5: + ++PC; + acu = LOW_REGISTER(HL); + break; + + case 6: + ++PC; + acu = GET_BYTE(adr); + break; + + case 7: + ++PC; + acu = HIGH_REGISTER(AF); + break; + } + switch (op & 0xc0) { + + case 0x00: /* shift/rotate */ + switch (op & 0x38) { + + case 0x00:/* RLC */ + temp = (acu << 1) | (acu >> 7); + cbits = temp & 1; + goto cbshflg2; + + case 0x08:/* RRC */ + temp = (acu >> 1) | (acu << 7); + cbits = temp & 0x80; + goto cbshflg2; + + case 0x10:/* RL */ + temp = (acu << 1) | TSTFLAG(C); + cbits = acu & 0x80; + goto cbshflg2; + + case 0x18:/* RR */ + temp = (acu >> 1) | (TSTFLAG(C) << 7); + cbits = acu & 1; + goto cbshflg2; + + case 0x20:/* SLA */ + temp = acu << 1; + cbits = acu & 0x80; + goto cbshflg2; + + case 0x28:/* SRA */ + temp = (acu >> 1) | (acu & 0x80); + cbits = acu & 1; + goto cbshflg2; + + case 0x30:/* SLIA */ + temp = (acu << 1) | 1; + cbits = acu & 0x80; + goto cbshflg2; + + case 0x38:/* SRL */ + temp = acu >> 1; + cbits = acu & 1; + cbshflg2: + AF = (AF & ~0xff) | rotateShiftTable[temp & 0xff] | !!cbits; + } + break; + + case 0x40: /* BIT */ + if (acu & (1 << ((op >> 3) & 7))) + AF = (AF & ~0xfe) | 0x10 | (((op & 0x38) == 0x38) << 7); + else + AF = (AF & ~0xfe) | 0x54; + if ((op & 7) != 6) + AF |= (acu & 0x28); + temp = acu; + break; + + case 0x80: /* RES */ + temp = acu & ~(1 << ((op >> 3) & 7)); + break; + + case 0xc0: /* SET */ + temp = acu | (1 << ((op >> 3) & 7)); + break; + } + switch (op & 7) { + + case 0: + SET_HIGH_REGISTER(BC, temp); + break; + + case 1: + SET_LOW_REGISTER(BC, temp); + break; + + case 2: + SET_HIGH_REGISTER(DE, temp); + break; + + case 3: + SET_LOW_REGISTER(DE, temp); + break; + + case 4: + SET_HIGH_REGISTER(HL, temp); + break; + + case 5: + SET_LOW_REGISTER(HL, temp); + break; + + case 6: + PUT_BYTE(adr, temp); + break; + + case 7: + SET_HIGH_REGISTER(AF, temp); + break; + } + break; + + case 0xe1: /* POP IX */ + POP(IX); + break; + + case 0xe3: /* EX (SP),IX */ + temp = IX; + POP(IX); + PUSH(temp); + break; + + case 0xe5: /* PUSH IX */ + PUSH(IX); + break; + + case 0xe9: /* JP (IX) */ + PC = IX; + break; + + case 0xf9: /* LD SP,IX */ + SP = IX; + break; + + default: /* ignore DD */ + --PC; + } + break; + + case 0xde: /* SBC A,nn */ + temp = RAM_PP(PC); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0xdf: /* RST 18H */ + PUSH(PC); + PC = 0x18; + break; + + case 0xe0: /* RET PO */ + if (!(TSTFLAG(P))) + POP(PC); + break; + + case 0xe1: /* POP HL */ + POP(HL); + break; + + case 0xe2: /* JP PO,nnnn */ + JPC(!TSTFLAG(P)); + break; + + case 0xe3: /* EX (SP),HL */ + temp = HL; + POP(HL); + PUSH(temp); + break; + + case 0xe4: /* CALL PO,nnnn */ + CALLC(!TSTFLAG(P)); + break; + + case 0xe5: /* PUSH HL */ + PUSH(HL); + break; + + case 0xe6: /* AND nn */ + AF = andTable[((AF >> 8)& RAM_PP(PC)) & 0xff]; + break; + + case 0xe7: /* RST 20H */ + PUSH(PC); + PC = 0x20; + break; + + case 0xe8: /* RET PE */ + if (TSTFLAG(P)) + POP(PC); + break; + + case 0xe9: /* JP (HL) */ + PC = HL; + break; + + case 0xea: /* JP PE,nnnn */ + JPC(TSTFLAG(P)); + break; + + case 0xeb: /* EX DE,HL */ + temp = HL; + HL = DE; + DE = temp; + break; + + case 0xec: /* CALL PE,nnnn */ + CALLC(TSTFLAG(P)); + break; + + case 0xed: /* ED prefix */ + INCR(1); /* Add one M1 cycle to refresh counter */ + switch (RAM_PP(PC)) { + + case 0x40: /* IN B,(C) */ + temp = cpu_in(LOW_REGISTER(BC)); + SET_HIGH_REGISTER(BC, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x41: /* OUT (C),B */ + cpu_out(LOW_REGISTER(BC), HIGH_REGISTER(BC)); + break; + + case 0x42: /* SBC HL,BC */ + HL &= ADDRMASK; + BC &= ADDRMASK; + sum = HL - BC - TSTFLAG(C); + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | + cbits2Z80Table[((HL ^ BC ^ sum) >> 8) & 0x1ff]; + HL = sum; + break; + + case 0x43: /* LD (nnnn),BC */ + PUT_WORD(GET_WORD(PC), BC); + PC += 2; + break; + + case 0x44: /* NEG */ + + case 0x4C: /* NEG, unofficial */ + + case 0x54: /* NEG, unofficial */ + + case 0x5C: /* NEG, unofficial */ + + case 0x64: /* NEG, unofficial */ + + case 0x6C: /* NEG, unofficial */ + + case 0x74: /* NEG, unofficial */ + + case 0x7C: /* NEG, unofficial */ + temp = HIGH_REGISTER(AF); + AF = ((~(AF & 0xff00) + 1) & 0xff00); /* AF = (-(AF & 0xff00) & 0xff00); */ + AF |= ((AF >> 8) & 0xa8) | (((AF & 0xff00) == 0) << 6) | negTable[temp]; + break; + + case 0x45: /* RETN */ + + case 0x55: /* RETN, unofficial */ + + case 0x5D: /* RETN, unofficial */ + + case 0x65: /* RETN, unofficial */ + + case 0x6D: /* RETN, unofficial */ + + case 0x75: /* RETN, unofficial */ + + case 0x7D: /* RETN, unofficial */ + IFF |= IFF >> 1; + POP(PC); + break; + + case 0x46: /* IM 0 */ + /* interrupt mode 0 */ + break; + + case 0x47: /* LD I,A */ + IR = (IR & 0xff) | (AF & ~0xff); + break; + + case 0x48: /* IN C,(C) */ + temp = cpu_in(LOW_REGISTER(BC)); + SET_LOW_REGISTER(BC, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x49: /* OUT (C),C */ + cpu_out(LOW_REGISTER(BC), LOW_REGISTER(BC)); + break; + + case 0x4a: /* ADC HL,BC */ + HL &= ADDRMASK; + BC &= ADDRMASK; + sum = HL + BC + TSTFLAG(C); + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | + cbitsZ80Table[(HL ^ BC ^ sum) >> 8]; + HL = sum; + break; + + case 0x4b: /* LD BC,(nnnn) */ + BC = GET_WORD(GET_WORD(PC)); + PC += 2; + break; + + case 0x4d: /* RETI */ + IFF |= IFF >> 1; + POP(PC); + break; + + case 0x4f: /* LD R,A */ + IR = (IR & ~0xff) | ((AF >> 8) & 0xff); + break; + + case 0x50: /* IN D,(C) */ + temp = cpu_in(LOW_REGISTER(BC)); + SET_HIGH_REGISTER(DE, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x51: /* OUT (C),D */ + cpu_out(LOW_REGISTER(BC), HIGH_REGISTER(DE)); + break; + + case 0x52: /* SBC HL,DE */ + HL &= ADDRMASK; + DE &= ADDRMASK; + sum = HL - DE - TSTFLAG(C); + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | + cbits2Z80Table[((HL ^ DE ^ sum) >> 8) & 0x1ff]; + HL = sum; + break; + + case 0x53: /* LD (nnnn),DE */ + PUT_WORD(GET_WORD(PC), DE); + PC += 2; + break; + + case 0x56: /* IM 1 */ + /* interrupt mode 1 */ + break; + + case 0x57: /* LD A,I */ + AF = (AF & 0x29) | (IR & ~0xff) | ((IR >> 8) & 0x80) | (((IR & ~0xff) == 0) << 6) | ((IFF & 2) << 1); + break; + + case 0x58: /* IN E,(C) */ + temp = cpu_in(LOW_REGISTER(BC)); + SET_LOW_REGISTER(DE, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x59: /* OUT (C),E */ + cpu_out(LOW_REGISTER(BC), LOW_REGISTER(DE)); + break; + + case 0x5a: /* ADC HL,DE */ + HL &= ADDRMASK; + DE &= ADDRMASK; + sum = HL + DE + TSTFLAG(C); + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | + cbitsZ80Table[(HL ^ DE ^ sum) >> 8]; + HL = sum; + break; + + case 0x5b: /* LD DE,(nnnn) */ + DE = GET_WORD(GET_WORD(PC)); + PC += 2; + break; + + case 0x5e: /* IM 2 */ + /* interrupt mode 2 */ + break; + + case 0x5f: /* LD A,R */ + AF = (AF & 0x29) | ((IR & 0xff) << 8) | (IR & 0x80) | + (((IR & 0xff) == 0) << 6) | ((IFF & 2) << 1); + break; + + case 0x60: /* IN H,(C) */ + temp = cpu_in(LOW_REGISTER(BC)); + SET_HIGH_REGISTER(HL, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x61: /* OUT (C),H */ + cpu_out(LOW_REGISTER(BC), HIGH_REGISTER(HL)); + break; + + case 0x62: /* SBC HL,HL */ + HL &= ADDRMASK; + sum = HL - HL - TSTFLAG(C); + AF = (AF & ~0xff) | (((sum & ADDRMASK) == 0) << 6) | + cbits2Z80DupTable[(sum >> 8) & 0x1ff]; + HL = sum; + break; + + case 0x63: /* LD (nnnn),HL */ + PUT_WORD(GET_WORD(PC), HL); + PC += 2; + break; + + case 0x67: /* RRD */ + temp = GET_BYTE(HL); + acu = HIGH_REGISTER(AF); + PUT_BYTE(HL, HIGH_DIGIT(temp) | (LOW_DIGIT(acu) << 4)); + AF = rrdrldTable[(acu & 0xf0) | LOW_DIGIT(temp)] | (AF & 1); + break; + + case 0x68: /* IN L,(C) */ + temp = cpu_in(LOW_REGISTER(BC)); + SET_LOW_REGISTER(HL, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x69: /* OUT (C),L */ + cpu_out(LOW_REGISTER(BC), LOW_REGISTER(HL)); + break; + + case 0x6a: /* ADC HL,HL */ + HL &= ADDRMASK; + sum = HL + HL + TSTFLAG(C); + AF = (AF & ~0xff) | (((sum & ADDRMASK) == 0) << 6) | + cbitsZ80DupTable[sum >> 8]; + HL = sum; + break; + + case 0x6b: /* LD HL,(nnnn) */ + HL = GET_WORD(GET_WORD(PC)); + PC += 2; + break; + + case 0x6f: /* RLD */ + temp = GET_BYTE(HL); + acu = HIGH_REGISTER(AF); + PUT_BYTE(HL, (LOW_DIGIT(temp) << 4) | LOW_DIGIT(acu)); + AF = rrdrldTable[(acu & 0xf0) | HIGH_DIGIT(temp)] | (AF & 1); + break; + + case 0x70: /* IN (C) */ + temp = cpu_in(LOW_REGISTER(BC)); + SET_LOW_REGISTER(temp, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x71: /* OUT (C),0 */ + cpu_out(LOW_REGISTER(BC), 0); + break; + + case 0x72: /* SBC HL,SP */ + HL &= ADDRMASK; + SP &= ADDRMASK; + sum = HL - SP - TSTFLAG(C); + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | + cbits2Z80Table[((HL ^ SP ^ sum) >> 8) & 0x1ff]; + HL = sum; + break; + + case 0x73: /* LD (nnnn),SP */ + PUT_WORD(GET_WORD(PC), SP); + PC += 2; + break; + + case 0x78: /* IN A,(C) */ + temp = cpu_in(LOW_REGISTER(BC)); + SET_HIGH_REGISTER(AF, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x79: /* OUT (C),A */ + cpu_out(LOW_REGISTER(BC), HIGH_REGISTER(AF)); + break; + + case 0x7a: /* ADC HL,SP */ + HL &= ADDRMASK; + SP &= ADDRMASK; + sum = HL + SP + TSTFLAG(C); + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | + cbitsZ80Table[(HL ^ SP ^ sum) >> 8]; + HL = sum; + break; + + case 0x7b: /* LD SP,(nnnn) */ + SP = GET_WORD(GET_WORD(PC)); + PC += 2; + break; + + case 0xa0: /* LDI */ + acu = RAM_PP(HL); + PUT_BYTE_PP(DE, acu); + acu += HIGH_REGISTER(AF); + AF = (AF & ~0x3e) | (acu & 8) | ((acu & 2) << 4) | + (((--BC & ADDRMASK) != 0) << 2); + break; + + case 0xa1: /* CPI */ + acu = HIGH_REGISTER(AF); + temp = RAM_PP(HL); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xfe) | (sum & 0x80) | (!(sum & 0xff) << 6) | + (((sum - ((cbits & 16) >> 4)) & 2) << 4) | (cbits & 16) | + ((sum - ((cbits >> 4) & 1)) & 8) | + ((--BC & ADDRMASK) != 0) << 2 | 2; + if ((sum & 15) == 8 && (cbits & 16) != 0) + AF &= ~8; + break; + + /* SF, ZF, YF, XF flags are affected by decreasing register B, as in DEC B. + NF flag A is copy of bit 7 of the value read from or written to an I/O port. + INI/INIR/IND/INDR use the C flag in stead of the L register. There is a + catch though, because not the value of C is used, but C + 1 if it's INI/INIR or + C - 1 if it's IND/INDR. So, first of all INI/INIR: + HF and CF Both set if ((HL) + ((C + 1) & 255) > 255) + PF The parity of (((HL) + ((C + 1) & 255)) & 7) xor B) */ + case 0xa2: /* INI */ + acu = cpu_in(LOW_REGISTER(BC)); + PUT_BYTE(HL, acu); + ++HL; + temp = HIGH_REGISTER(BC); + BC -= 0x100; + INOUTFLAGS_NONZERO((LOW_REGISTER(BC) + 1) & 0xff); + break; + + /* SF, ZF, YF, XF flags are affected by decreasing register B, as in DEC B. + NF flag A is copy of bit 7 of the value read from or written to an I/O port. + And now the for OUTI/OTIR/OUTD/OTDR instructions. Take state of the L + after the increment or decrement of HL; add the value written to the I/O port + to; call that k for now. If k > 255, then the CF and HF flags are set. The PF + flags is set like the parity of k bitwise and'ed with 7, bitwise xor'ed with B. + HF and CF Both set if ((HL) + L > 255) + PF The parity of ((((HL) + L) & 7) xor B) */ + case 0xa3: /* OUTI */ + acu = GET_BYTE(HL); + cpu_out(LOW_REGISTER(BC), acu); + ++HL; + temp = HIGH_REGISTER(BC); + BC -= 0x100; + INOUTFLAGS_NONZERO(LOW_REGISTER(HL)); + break; + + case 0xa8: /* LDD */ + acu = RAM_MM(HL); + PUT_BYTE_MM(DE, acu); + acu += HIGH_REGISTER(AF); + AF = (AF & ~0x3e) | (acu & 8) | ((acu & 2) << 4) | + (((--BC & ADDRMASK) != 0) << 2); + break; + + case 0xa9: /* CPD */ + acu = HIGH_REGISTER(AF); + temp = RAM_MM(HL); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xfe) | (sum & 0x80) | (!(sum & 0xff) << 6) | + (((sum - ((cbits & 16) >> 4)) & 2) << 4) | (cbits & 16) | + ((sum - ((cbits >> 4) & 1)) & 8) | + ((--BC & ADDRMASK) != 0) << 2 | 2; + if ((sum & 15) == 8 && (cbits & 16) != 0) + AF &= ~8; + break; + + /* SF, ZF, YF, XF flags are affected by decreasing register B, as in DEC B. + NF flag A is copy of bit 7 of the value read from or written to an I/O port. + INI/INIR/IND/INDR use the C flag in stead of the L register. There is a + catch though, because not the value of C is used, but C + 1 if it's INI/INIR or + C - 1 if it's IND/INDR. And last IND/INDR: + HF and CF Both set if ((HL) + ((C - 1) & 255) > 255) + PF The parity of (((HL) + ((C - 1) & 255)) & 7) xor B) */ + case 0xaa: /* IND */ + acu = cpu_in(LOW_REGISTER(BC)); + PUT_BYTE(HL, acu); + --HL; + temp = HIGH_REGISTER(BC); + BC -= 0x100; + INOUTFLAGS_NONZERO((LOW_REGISTER(BC) - 1) & 0xff); + break; + + case 0xab: /* OUTD */ + acu = GET_BYTE(HL); + cpu_out(LOW_REGISTER(BC), acu); + --HL; + temp = HIGH_REGISTER(BC); + BC -= 0x100; + INOUTFLAGS_NONZERO(LOW_REGISTER(HL)); + break; + + case 0xb0: /* LDIR */ + BC &= ADDRMASK; + if (BC == 0) + BC = 0x10000; + do { + INCR(2); /* Add two M1 cycles to refresh counter */ + acu = RAM_PP(HL); + PUT_BYTE_PP(DE, acu); + } while (--BC); + acu += HIGH_REGISTER(AF); + AF = (AF & ~0x3e) | (acu & 8) | ((acu & 2) << 4); + break; + + case 0xb1: /* CPIR */ + acu = HIGH_REGISTER(AF); + BC &= ADDRMASK; + if (BC == 0) + BC = 0x10000; + do { + INCR(1); /* Add one M1 cycle to refresh counter */ + temp = RAM_PP(HL); + op = --BC != 0; + sum = acu - temp; + } while (op && sum != 0); + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xfe) | (sum & 0x80) | (!(sum & 0xff) << 6) | + (((sum - ((cbits & 16) >> 4)) & 2) << 4) | + (cbits & 16) | ((sum - ((cbits >> 4) & 1)) & 8) | + op << 2 | 2; + if ((sum & 15) == 8 && (cbits & 16) != 0) + AF &= ~8; + break; + + case 0xb2: /* INIR */ + temp = HIGH_REGISTER(BC); + if (temp == 0) + temp = 0x100; + do { + INCR(1); /* Add one M1 cycle to refresh counter */ + acu = cpu_in(LOW_REGISTER(BC)); + PUT_BYTE(HL, acu); + ++HL; + } while (--temp); + temp = HIGH_REGISTER(BC); + SET_HIGH_REGISTER(BC, 0); + INOUTFLAGS_ZERO((LOW_REGISTER(BC) + 1) & 0xff); + break; + + case 0xb3: /* OTIR */ + temp = HIGH_REGISTER(BC); + if (temp == 0) + temp = 0x100; + do { + INCR(1); /* Add one M1 cycle to refresh counter */ + acu = GET_BYTE(HL); + cpu_out(LOW_REGISTER(BC), acu); + ++HL; + } while (--temp); + temp = HIGH_REGISTER(BC); + SET_HIGH_REGISTER(BC, 0); + INOUTFLAGS_ZERO(LOW_REGISTER(HL)); + break; + + case 0xb8: /* LDDR */ + BC &= ADDRMASK; + if (BC == 0) + BC = 0x10000; + do { + INCR(2); /* Add two M1 cycles to refresh counter */ + acu = RAM_MM(HL); + PUT_BYTE_MM(DE, acu); + } while (--BC); + acu += HIGH_REGISTER(AF); + AF = (AF & ~0x3e) | (acu & 8) | ((acu & 2) << 4); + break; + + case 0xb9: /* CPDR */ + acu = HIGH_REGISTER(AF); + BC &= ADDRMASK; + if (BC == 0) + BC = 0x10000; + do { + INCR(1); /* Add one M1 cycle to refresh counter */ + temp = RAM_MM(HL); + op = --BC != 0; + sum = acu - temp; + } while (op && sum != 0); + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xfe) | (sum & 0x80) | (!(sum & 0xff) << 6) | + (((sum - ((cbits & 16) >> 4)) & 2) << 4) | + (cbits & 16) | ((sum - ((cbits >> 4) & 1)) & 8) | + op << 2 | 2; + if ((sum & 15) == 8 && (cbits & 16) != 0) + AF &= ~8; + break; + + case 0xba: /* INDR */ + temp = HIGH_REGISTER(BC); + if (temp == 0) + temp = 0x100; + do { + INCR(1); /* Add one M1 cycle to refresh counter */ + acu = cpu_in(LOW_REGISTER(BC)); + PUT_BYTE(HL, acu); + --HL; + } while (--temp); + temp = HIGH_REGISTER(BC); + SET_HIGH_REGISTER(BC, 0); + INOUTFLAGS_ZERO((LOW_REGISTER(BC) - 1) & 0xff); + break; + + case 0xbb: /* OTDR */ + temp = HIGH_REGISTER(BC); + if (temp == 0) + temp = 0x100; + do { + INCR(1); /* Add one M1 cycle to refresh counter */ + acu = GET_BYTE(HL); + cpu_out(LOW_REGISTER(BC), acu); + --HL; + } while (--temp); + temp = HIGH_REGISTER(BC); + SET_HIGH_REGISTER(BC, 0); + INOUTFLAGS_ZERO(LOW_REGISTER(HL)); + break; + + default: /* ignore ED and following byte */ + break; + } + break; + + case 0xee: /* XOR nn */ + AF = xororTable[((AF >> 8) ^ RAM_PP(PC)) & 0xff]; + break; + + case 0xef: /* RST 28H */ + PUSH(PC); + PC = 0x28; + break; + + case 0xf0: /* RET P */ + if (!(TSTFLAG(S))) + POP(PC); + break; + + case 0xf1: /* POP AF */ + POP(AF); + break; + + case 0xf2: /* JP P,nnnn */ + JPC(!TSTFLAG(S)); + break; + + case 0xf3: /* DI */ + IFF = 0; + break; + + case 0xf4: /* CALL P,nnnn */ + CALLC(!TSTFLAG(S)); + break; + + case 0xf5: /* PUSH AF */ + PUSH(AF); + break; + + case 0xf6: /* OR nn */ + AF = xororTable[((AF >> 8) | RAM_PP(PC)) & 0xff]; + break; + + case 0xf7: /* RST 30H */ + PUSH(PC); + PC = 0x30; + break; + + case 0xf8: /* RET M */ + if (TSTFLAG(S)) + POP(PC); + break; + + case 0xf9: /* LD SP,HL */ + SP = HL; + break; + + case 0xfa: /* JP M,nnnn */ + JPC(TSTFLAG(S)); + break; + + case 0xfb: /* EI */ + IFF = 3; + break; + + case 0xfc: /* CALL M,nnnn */ + CALLC(TSTFLAG(S)); + break; + + case 0xfd: /* FD prefix */ + INCR(1); /* Add one M1 cycle to refresh counter */ + switch (RAM_PP(PC)) { + + case 0x09: /* ADD IY,BC */ + IY &= ADDRMASK; + BC &= ADDRMASK; + sum = IY + BC; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IY ^ BC ^ sum) >> 8]; + IY = sum; + break; + + case 0x19: /* ADD IY,DE */ + IY &= ADDRMASK; + DE &= ADDRMASK; + sum = IY + DE; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IY ^ DE ^ sum) >> 8]; + IY = sum; + break; + + case 0x21: /* LD IY,nnnn */ + IY = GET_WORD(PC); + PC += 2; + break; + + case 0x22: /* LD (nnnn),IY */ + temp = GET_WORD(PC); + PUT_WORD(temp, IY); + PC += 2; + break; + + case 0x23: /* INC IY */ + ++IY; + break; + + case 0x24: /* INC IYH */ + IY += 0x100; + AF = (AF & ~0xfe) | incZ80Table[HIGH_REGISTER(IY)]; + break; + + case 0x25: /* DEC IYH */ + IY -= 0x100; + AF = (AF & ~0xfe) | decZ80Table[HIGH_REGISTER(IY)]; + break; + + case 0x26: /* LD IYH,nn */ + SET_HIGH_REGISTER(IY, RAM_PP(PC)); + break; + + case 0x29: /* ADD IY,IY */ + IY &= ADDRMASK; + sum = IY + IY; + AF = (AF & ~0x3b) | cbitsDup16Table[sum >> 8]; + IY = sum; + break; + + case 0x2a: /* LD IY,(nnnn) */ + IY = GET_WORD(GET_WORD(PC)); + PC += 2; + break; + + case 0x2b: /* DEC IY */ + --IY; + break; + + case 0x2c: /* INC IYL */ + temp = LOW_REGISTER(IY) + 1; + SET_LOW_REGISTER(IY, temp); + AF = (AF & ~0xfe) | incZ80Table[temp]; + break; + + case 0x2d: /* DEC IYL */ + temp = LOW_REGISTER(IY) - 1; + SET_LOW_REGISTER(IY, temp); + AF = (AF & ~0xfe) | decZ80Table[temp & 0xff]; + break; + + case 0x2e: /* LD IYL,nn */ + SET_LOW_REGISTER(IY, RAM_PP(PC)); + break; + + case 0x34: /* INC (IY+dd) */ + adr = IY + (int8)RAM_PP(PC); + temp = GET_BYTE(adr) + 1; + PUT_BYTE(adr, temp); + AF = (AF & ~0xfe) | incZ80Table[temp]; + break; + + case 0x35: /* DEC (IY+dd) */ + adr = IY + (int8)RAM_PP(PC); + temp = GET_BYTE(adr) - 1; + PUT_BYTE(adr, temp); + AF = (AF & ~0xfe) | decZ80Table[temp & 0xff]; + break; + + case 0x36: /* LD (IY+dd),nn */ + adr = IY + (int8)RAM_PP(PC); + PUT_BYTE(adr, RAM_PP(PC)); + break; + + case 0x39: /* ADD IY,SP */ + IY &= ADDRMASK; + SP &= ADDRMASK; + sum = IY + SP; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IY ^ SP ^ sum) >> 8]; + IY = sum; + break; + + case 0x44: /* LD B,IYH */ + SET_HIGH_REGISTER(BC, HIGH_REGISTER(IY)); + break; + + case 0x45: /* LD B,IYL */ + SET_HIGH_REGISTER(BC, LOW_REGISTER(IY)); + break; + + case 0x46: /* LD B,(IY+dd) */ + SET_HIGH_REGISTER(BC, GET_BYTE(IY + (int8)RAM_PP(PC))); + break; + + case 0x4c: /* LD C,IYH */ + SET_LOW_REGISTER(BC, HIGH_REGISTER(IY)); + break; + + case 0x4d: /* LD C,IYL */ + SET_LOW_REGISTER(BC, LOW_REGISTER(IY)); + break; + + case 0x4e: /* LD C,(IY+dd) */ + SET_LOW_REGISTER(BC, GET_BYTE(IY + (int8)RAM_PP(PC))); + break; + + case 0x54: /* LD D,IYH */ + SET_HIGH_REGISTER(DE, HIGH_REGISTER(IY)); + break; + + case 0x55: /* LD D,IYL */ + SET_HIGH_REGISTER(DE, LOW_REGISTER(IY)); + break; + + case 0x56: /* LD D,(IY+dd) */ + SET_HIGH_REGISTER(DE, GET_BYTE(IY + (int8)RAM_PP(PC))); + break; + + case 0x5c: /* LD E,IYH */ + SET_LOW_REGISTER(DE, HIGH_REGISTER(IY)); + break; + + case 0x5d: /* LD E,IYL */ + SET_LOW_REGISTER(DE, LOW_REGISTER(IY)); + break; + + case 0x5e: /* LD E,(IY+dd) */ + SET_LOW_REGISTER(DE, GET_BYTE(IY + (int8)RAM_PP(PC))); + break; + + case 0x60: /* LD IYH,B */ + SET_HIGH_REGISTER(IY, HIGH_REGISTER(BC)); + break; + + case 0x61: /* LD IYH,C */ + SET_HIGH_REGISTER(IY, LOW_REGISTER(BC)); + break; + + case 0x62: /* LD IYH,D */ + SET_HIGH_REGISTER(IY, HIGH_REGISTER(DE)); + break; + + case 0x63: /* LD IYH,E */ + SET_HIGH_REGISTER(IY, LOW_REGISTER(DE)); + break; + + case 0x64: /* LD IYH,IYH */ + break; + + case 0x65: /* LD IYH,IYL */ + SET_HIGH_REGISTER(IY, LOW_REGISTER(IY)); + break; + + case 0x66: /* LD H,(IY+dd) */ + SET_HIGH_REGISTER(HL, GET_BYTE(IY + (int8)RAM_PP(PC))); + break; + + case 0x67: /* LD IYH,A */ + SET_HIGH_REGISTER(IY, HIGH_REGISTER(AF)); + break; + + case 0x68: /* LD IYL,B */ + SET_LOW_REGISTER(IY, HIGH_REGISTER(BC)); + break; + + case 0x69: /* LD IYL,C */ + SET_LOW_REGISTER(IY, LOW_REGISTER(BC)); + break; + + case 0x6a: /* LD IYL,D */ + SET_LOW_REGISTER(IY, HIGH_REGISTER(DE)); + break; + + case 0x6b: /* LD IYL,E */ + SET_LOW_REGISTER(IY, LOW_REGISTER(DE)); + break; + + case 0x6c: /* LD IYL,IYH */ + SET_LOW_REGISTER(IY, HIGH_REGISTER(IY)); + break; + + case 0x6d: /* LD IYL,IYL */ + break; + + case 0x6e: /* LD L,(IY+dd) */ + SET_LOW_REGISTER(HL, GET_BYTE(IY + (int8)RAM_PP(PC))); + break; + + case 0x6f: /* LD IYL,A */ + SET_LOW_REGISTER(IY, HIGH_REGISTER(AF)); + break; + + case 0x70: /* LD (IY+dd),B */ + PUT_BYTE(IY + (int8)RAM_PP(PC), HIGH_REGISTER(BC)); + break; + + case 0x71: /* LD (IY+dd),C */ + PUT_BYTE(IY + (int8)RAM_PP(PC), LOW_REGISTER(BC)); + break; + + case 0x72: /* LD (IY+dd),D */ + PUT_BYTE(IY + (int8)RAM_PP(PC), HIGH_REGISTER(DE)); + break; + + case 0x73: /* LD (IY+dd),E */ + PUT_BYTE(IY + (int8)RAM_PP(PC), LOW_REGISTER(DE)); + break; + + case 0x74: /* LD (IY+dd),H */ + PUT_BYTE(IY + (int8)RAM_PP(PC), HIGH_REGISTER(HL)); + break; + + case 0x75: /* LD (IY+dd),L */ + PUT_BYTE(IY + (int8)RAM_PP(PC), LOW_REGISTER(HL)); + break; + + case 0x77: /* LD (IY+dd),A */ + PUT_BYTE(IY + (int8)RAM_PP(PC), HIGH_REGISTER(AF)); + break; + + case 0x7c: /* LD A,IYH */ + SET_HIGH_REGISTER(AF, HIGH_REGISTER(IY)); + break; + + case 0x7d: /* LD A,IYL */ + SET_HIGH_REGISTER(AF, LOW_REGISTER(IY)); + break; + + case 0x7e: /* LD A,(IY+dd) */ + SET_HIGH_REGISTER(AF, GET_BYTE(IY + (int8)RAM_PP(PC))); + break; + + case 0x84: /* ADD A,IYH */ + temp = HIGH_REGISTER(IY); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x85: /* ADD A,IYL */ + temp = LOW_REGISTER(IY); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x86: /* ADD A,(IY+dd) */ + adr = IY + (int8)RAM_PP(PC); + temp = GET_BYTE(adr); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x8c: /* ADC A,IYH */ + temp = HIGH_REGISTER(IY); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x8d: /* ADC A,IYL */ + temp = LOW_REGISTER(IY); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x8e: /* ADC A,(IY+dd) */ + adr = IY + (int8)RAM_PP(PC); + temp = GET_BYTE(adr); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x96: /* SUB (IY+dd) */ + adr = IY + (int8)RAM_PP(PC); + temp = GET_BYTE(adr); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0x94: /* SUB IYH */ + SETFLAG(C, 0);/* fall through, a bit less efficient but smaller code */ + + case 0x9c: /* SBC A,IYH */ + temp = HIGH_REGISTER(IY); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0x95: /* SUB IYL */ + SETFLAG(C, 0);/* fall through, a bit less efficient but smaller code */ + + case 0x9d: /* SBC A,IYL */ + temp = LOW_REGISTER(IY); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0x9e: /* SBC A,(IY+dd) */ + adr = IY + (int8)RAM_PP(PC); + temp = GET_BYTE(adr); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xa4: /* AND IYH */ + AF = andTable[((AF & IY) >> 8) & 0xff]; + break; + + case 0xa5: /* AND IYL */ + AF = andTable[((AF >> 8)& IY) & 0xff]; + break; + + case 0xa6: /* AND (IY+dd) */ + AF = andTable[((AF >> 8)& GET_BYTE(IY + (int8)RAM_PP(PC))) & 0xff]; + break; + + case 0xac: /* XOR IYH */ + AF = xororTable[((AF ^ IY) >> 8) & 0xff]; + break; + + case 0xad: /* XOR IYL */ + AF = xororTable[((AF >> 8) ^ IY) & 0xff]; + break; + + case 0xae: /* XOR (IY+dd) */ + AF = xororTable[((AF >> 8) ^ GET_BYTE(IY + (int8)RAM_PP(PC))) & 0xff]; + break; + + case 0xb4: /* OR IYH */ + AF = xororTable[((AF | IY) >> 8) & 0xff]; + break; + + case 0xb5: /* OR IYL */ + AF = xororTable[((AF >> 8) | IY) & 0xff]; + break; + + case 0xb6: /* OR (IY+dd) */ + AF = xororTable[((AF >> 8) | GET_BYTE(IY + (int8)RAM_PP(PC))) & 0xff]; + break; + + case 0xbc: /* CP IYH */ + temp = HIGH_REGISTER(IY); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xbd: /* CP IYL */ + temp = LOW_REGISTER(IY); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xbe: /* CP (IY+dd) */ + adr = IY + (int8)RAM_PP(PC); + temp = GET_BYTE(adr); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xcb: /* CB prefix */ + adr = IY + (int8)RAM_PP(PC); + switch ((op = GET_BYTE(PC)) & 7) { + + case 0: + ++PC; + acu = HIGH_REGISTER(BC); + break; + + case 1: + ++PC; + acu = LOW_REGISTER(BC); + break; + + case 2: + ++PC; + acu = HIGH_REGISTER(DE); + break; + + case 3: + ++PC; + acu = LOW_REGISTER(DE); + break; + + case 4: + ++PC; + acu = HIGH_REGISTER(HL); + break; + + case 5: + ++PC; + acu = LOW_REGISTER(HL); + break; + + case 6: + ++PC; + acu = GET_BYTE(adr); + break; + + case 7: + ++PC; + acu = HIGH_REGISTER(AF); + break; + } + switch (op & 0xc0) { + + case 0x00: /* shift/rotate */ + switch (op & 0x38) { + + case 0x00:/* RLC */ + temp = (acu << 1) | (acu >> 7); + cbits = temp & 1; + goto cbshflg3; + + case 0x08:/* RRC */ + temp = (acu >> 1) | (acu << 7); + cbits = temp & 0x80; + goto cbshflg3; + + case 0x10:/* RL */ + temp = (acu << 1) | TSTFLAG(C); + cbits = acu & 0x80; + goto cbshflg3; + + case 0x18:/* RR */ + temp = (acu >> 1) | (TSTFLAG(C) << 7); + cbits = acu & 1; + goto cbshflg3; + + case 0x20:/* SLA */ + temp = acu << 1; + cbits = acu & 0x80; + goto cbshflg3; + + case 0x28:/* SRA */ + temp = (acu >> 1) | (acu & 0x80); + cbits = acu & 1; + goto cbshflg3; + + case 0x30:/* SLIA */ + temp = (acu << 1) | 1; + cbits = acu & 0x80; + goto cbshflg3; + + case 0x38:/* SRL */ + temp = acu >> 1; + cbits = acu & 1; + cbshflg3: + AF = (AF & ~0xff) | rotateShiftTable[temp & 0xff] | !!cbits; + } + break; + + case 0x40: /* BIT */ + if (acu & (1 << ((op >> 3) & 7))) + AF = (AF & ~0xfe) | 0x10 | (((op & 0x38) == 0x38) << 7); + else + AF = (AF & ~0xfe) | 0x54; + if ((op & 7) != 6) + AF |= (acu & 0x28); + temp = acu; + break; + + case 0x80: /* RES */ + temp = acu & ~(1 << ((op >> 3) & 7)); + break; + + case 0xc0: /* SET */ + temp = acu | (1 << ((op >> 3) & 7)); + break; + } + switch (op & 7) { + + case 0: + SET_HIGH_REGISTER(BC, temp); + break; + + case 1: + SET_LOW_REGISTER(BC, temp); + break; + + case 2: + SET_HIGH_REGISTER(DE, temp); + break; + + case 3: + SET_LOW_REGISTER(DE, temp); + break; + + case 4: + SET_HIGH_REGISTER(HL, temp); + break; + + case 5: + SET_LOW_REGISTER(HL, temp); + break; + + case 6: + PUT_BYTE(adr, temp); + break; + + case 7: + SET_HIGH_REGISTER(AF, temp); + break; + } + break; + + case 0xe1: /* POP IY */ + POP(IY); + break; + + case 0xe3: /* EX (SP),IY */ + temp = IY; + POP(IY); + PUSH(temp); + break; + + case 0xe5: /* PUSH IY */ + PUSH(IY); + break; + + case 0xe9: /* JP (IY) */ + PC = IY; + break; + + case 0xf9: /* LD SP,IY */ + SP = IY; + break; + + default: /* ignore FD */ + --PC; + } + break; + + case 0xfe: /* CP nn */ + temp = RAM_PP(PC); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + break; + + case 0xff: /* RST 38H */ + PUSH(PC); + PC = 0x38; + } + } +end_decode: + ; +} + + +#endif diff --git a/runcpm-rp2350-hstx-usb/runcpm-pico/disk.h b/runcpm-rp2350-hstx-usb/runcpm-pico/disk.h new file mode 100644 index 000000000..d8ee06c76 --- /dev/null +++ b/runcpm-rp2350-hstx-usb/runcpm-pico/disk.h @@ -0,0 +1,635 @@ +// SPDX-FileCopyrightText: 2023 Mockba the Borg +// +// SPDX-License-Identifier: MIT + +#ifndef DISK_H +#define DISK_H + +/* see main.c for definition */ + +#ifdef __linux__ +#include +#endif + +#ifdef __DJGPP__ +#include +#endif + +/* +Disk errors +*/ +#define errWRITEPROT 1 +#define errSELECT 2 + +#define RW (roVector & (1 << cDrive)) + +// Prints out a BDOS error +void _error(uint8 error) { + _puts("\r\nBdos Err on "); + _putcon('A' + cDrive); + _puts(": "); + switch (error) { + case errWRITEPROT: + _puts("R/O"); + break; + case errSELECT: + _puts("Select"); + break; + default: + _puts("\r\nCP/M ERR"); + break; + } + Status = _getch(); + _puts("\r\n"); + cDrive = oDrive = _RamRead(DSKByte) & 0x0f; + Status = 2; +} + +// Selects the disk to be used by the next disk function +int _SelectDisk(uint8 dr) { + uint8 result = 0xff; + uint8 disk[2] = { 'A', 0 }; + + if (!dr || dr == '?') { + dr = cDrive; // This will set dr to defDisk in case no disk is passed + } else { + --dr; // Called from BDOS, set dr back to 0=A: format + } + + disk[0] += dr; + if (_sys_select(&disk[0])) { + loginVector = loginVector | (1 << (disk[0] - 'A')); + result = 0x00; + } else { + _error(errSELECT); + } + + return(result); +} + +// Converts a FCB entry onto a host OS filename string +uint8 _FCBtoHostname(uint16 fcbaddr, uint8* filename) { + uint8 addDot = TRUE; + CPM_FCB* F = (CPM_FCB*)_RamSysAddr(fcbaddr); + uint8 i = 0; + uint8 unique = TRUE; + uint8 c; + + if (F->dr && F->dr != '?') { + *(filename++) = (F->dr - 1) + 'A'; + } else { + *(filename++) = cDrive + 'A'; + } + *(filename++) = FOLDERCHAR; + + *(filename++) = toupper(tohex(userCode)); + *(filename++) = FOLDERCHAR; + + if (F->dr != '?') { + while (i < 8) { + c = F->fn[i] & 0x7F; +#ifdef NOSLASH + if (c == '/') + c = '_'; +#endif + if (c > 32) + *(filename++) = toupper(c); + if (c == '?') + unique = FALSE; + ++i; + } + i = 0; + while (i < 3) { + c = F->tp[i] & 0x7F; + if (c > 32) { + if (addDot) { + addDot = FALSE; + *(filename++) = '.'; // Only add the dot if there's an extension + } +#ifdef NOSLASH + if (c == '/') + c = '_'; +#endif + *(filename++) = toupper(c); + } + if (c == '?') + unique = FALSE; + ++i; + } + } else { + for (i = 0; i < 8; ++i) + *(filename++) = '?'; + *(filename++) = '.'; + for (i = 0; i < 3; ++i) + *(filename++) = '?'; + unique = FALSE; + } + *filename = 0x00; + + return(unique); +} + +// Convers a host OS filename string onto a FCB entry +void _HostnameToFCB(uint16 fcbaddr, uint8* filename) { + CPM_FCB* F = (CPM_FCB*)_RamSysAddr(fcbaddr); + uint8 i = 0; + + ++filename; + if (*filename == FOLDERCHAR) { // Skips the drive and / if needed + filename += 3; + } else { + --filename; + } + + while (*filename != 0 && *filename != '.') { + F->fn[i] = toupper(*filename); + ++filename; + ++i; + } + while (i < 8) { + F->fn[i] = ' '; + ++i; + } + if (*filename == '.') + ++filename; + i = 0; + while (*filename != 0) { + F->tp[i] = toupper(*filename); + ++filename; + ++i; + } + while (i < 3) { + F->tp[i] = ' '; + ++i; + } +} + +// Converts a string name (AB.TXT) onto FCB name (AB TXT) +void _HostnameToFCBname(uint8* from, uint8* to) { + int i = 0; + + ++from; + if (*from == FOLDERCHAR) { // Skips the drive and / if needed + from += 3; + } else { + --from; + } + + while (*from != 0 && *from != '.') { + *to = toupper(*from); + ++to; ++from; ++i; + } + while (i < 8) { + *to = ' '; + ++to; ++i; + } + if (*from == '.') + ++from; + i = 0; + while (*from != 0) { + *to = toupper(*from); + ++to; ++from; ++i; + } + while (i < 3) { + *to = ' '; + ++to; ++i; + } + *to = 0; +} + +// Creates a fake directory entry for the current dmaAddr FCB +void _mockupDirEntry(void) { + CPM_DIRENTRY* DE = (CPM_DIRENTRY*)_RamSysAddr(dmaAddr); + uint8 blocks, i; + + for (i = 0; i < sizeof(CPM_DIRENTRY); ++i) + _RamWrite(dmaAddr + i, 0x00); // zero out directory entry + _HostnameToFCB(dmaAddr, (uint8*)findNextDirName); + + if (allUsers) { + DE->dr = currFindUser; // set user code for return + } else { + DE->dr = userCode; + } + // does file fit in a single directory entry? + if (fileExtents <= extentsPerDirEntry) { + if (fileExtents) { + DE->ex = (fileExtents - 1 + fileExtentsUsed) % (MaxEX + 1); + DE->s2 = (fileExtents - 1 + fileExtentsUsed) / (MaxEX + 1); + DE->rc = fileRecords - (BlkEX * (fileExtents - 1)); + } + blocks = (fileRecords >> blockShift) + ((fileRecords & blockMask) ? 1 : 0); + fileRecords = 0; + fileExtents = 0; + fileExtentsUsed = 0; + } else { // no, max out the directory entry + DE->ex = (extentsPerDirEntry - 1 + fileExtentsUsed) % (MaxEX + 1); + DE->s2 = (extentsPerDirEntry - 1 + fileExtentsUsed) / (MaxEX + 1); + DE->rc = BlkEX; + blocks = numAllocBlocks < 256 ? 16 : 8; + // update remaining records and extents for next call + fileRecords -= BlkEX * extentsPerDirEntry; + fileExtents -= extentsPerDirEntry; + fileExtentsUsed += extentsPerDirEntry; + } + // phoney up an appropriate number of allocation blocks + if (numAllocBlocks < 256) { + for (i = 0; i < blocks; ++i) + DE->al[i] = (uint8)firstFreeAllocBlock++; + } else { + for (i = 0; i < 2 * blocks; i += 2) { + DE->al[i] = firstFreeAllocBlock & 0xFF; + DE->al[i + 1] = firstFreeAllocBlock >> 8; + ++firstFreeAllocBlock; + } + } +} + +// Matches a FCB name to a search pattern +uint8 match(uint8* fcbname, uint8* pattern) { + uint8 result = 1; + uint8 i; + + for (i = 0; i < 12; ++i) { + if (*pattern == '?' || *pattern == *fcbname) { + ++pattern; ++fcbname; + continue; + } else { + result = 0; + break; + } + } + return(result); +} + +// Returns the size of a file +long _FileSize(uint16 fcbaddr) { + CPM_FCB* F = (CPM_FCB*)_RamSysAddr(fcbaddr); + long r, l = -1; + + if (!_SelectDisk(F->dr)) { + _FCBtoHostname(fcbaddr, &filename[0]); + l = _sys_filesize(filename); + r = l % BlkSZ; + if (r) + l = l + BlkSZ - r; + } + return(l); +} + +// Opens a file +uint8 _OpenFile(uint16 fcbaddr) { + CPM_FCB* F = (CPM_FCB*)_RamSysAddr(fcbaddr); + uint8 result = 0xff; + long len; + int32 i; + + if (!_SelectDisk(F->dr)) { + _FCBtoHostname(fcbaddr, &filename[0]); + if (_sys_openfile(&filename[0])) { + + len = _FileSize(fcbaddr) / BlkSZ; // Compute the len on the file in blocks + + F->s1 = 0x00; + F->s2 = 0x80; // set unmodified flag + + + F->rc = len > MaxRC ? MaxRC : (uint8)len; + for (i = 0; i < 16; ++i) // Clean up AL + F->al[i] = 0x00; + + result = 0x00; + } + } + return(result); +} + +// Closes a file +uint8 _CloseFile(uint16 fcbaddr) { + CPM_FCB* F = (CPM_FCB*)_RamSysAddr(fcbaddr); + uint8 result = 0xff; + + if (!_SelectDisk(F->dr)) { + if (!(F->s2 & 0x80)) { // if file is modified + if (!RW) { + _FCBtoHostname(fcbaddr, &filename[0]); + if (fcbaddr == BatchFCB) + _Truncate((char*)filename, F->rc); // Truncate $$$.SUB to F->rc CP/M records so SUBMIT.COM can work + result = 0x00; + } else { + _error(errWRITEPROT); + } + } else { + result = 0x00; + } + } + return(result); +} + +// Creates a file +uint8 _MakeFile(uint16 fcbaddr) { + CPM_FCB* F = (CPM_FCB*)_RamSysAddr(fcbaddr); + uint8 result = 0xff; + uint8 i; + + if (!_SelectDisk(F->dr)) { + if (!RW) { + _FCBtoHostname(fcbaddr, &filename[0]); + if (_sys_makefile(&filename[0])) { + F->ex = 0x00; // Makefile also initializes the FCB (file becomes "open") + F->s1 = 0x00; + F->s2 = 0x00; // newly created files are already modified + F->rc = 0x00; + for (i = 0; i < 16; ++i) // Clean up AL + F->al[i] = 0x00; + F->cr = 0x00; + result = 0x00; + } + } else { + _error(errWRITEPROT); + } + } + return(result); +} + +// Searches for the first directory file +uint8 _SearchFirst(uint16 fcbaddr, uint8 isdir) { + CPM_FCB* F = (CPM_FCB*)_RamSysAddr(fcbaddr); + uint8 result = 0xff; + + if (!_SelectDisk(F->dr)) { + _FCBtoHostname(fcbaddr, &filename[0]); + allUsers = F->dr == '?'; + allExtents = F->ex == '?'; + if (allUsers) { + result = _findfirstallusers(isdir); + } else { + result = _findfirst(isdir); + } + } + return(result); +} + +// Searches for the next directory file +uint8 _SearchNext(uint16 fcbaddr, uint8 isdir) { + CPM_FCB* F = (CPM_FCB*)_RamSysAddr(tmpFCB); + uint8 result = 0xff; + + if (!_SelectDisk(F->dr)) { + if (allUsers) { + result = _findnextallusers(isdir); + } else { + result = _findnext(isdir); + } + } + return(result); +} + +// Deletes a file +uint8 _DeleteFile(uint16 fcbaddr) { + CPM_FCB* F = (CPM_FCB*)_RamSysAddr(fcbaddr); +#if defined(USE_PUN) || defined(USE_LST) + CPM_FCB* T = (CPM_FCB*)_RamSysAddr(tmpFCB); +#endif + uint8 result = 0xff; + uint8 deleted = 0xff; + + if (!_SelectDisk(F->dr)) { + if (!RW) { + result = _SearchFirst(fcbaddr, FALSE); // FALSE = Does not create a fake dir entry when finding the file + while (result != 0xff) { +#ifdef USE_PUN + if (!strcmp((char*)T->fn, "PUN TXT") && pun_open) { + _sys_fclose(pun_dev); + pun_open = FALSE; + } +#endif +#ifdef USE_LST + if (!strcmp((char*)T->fn, "LST TXT") && lst_open) { + _sys_fclose(lst_dev); + lst_open = FALSE; + } +#endif + _FCBtoHostname(tmpFCB, &filename[0]); + if (_sys_deletefile(&filename[0])) + deleted = 0x00; + result = _SearchFirst(fcbaddr, FALSE); // FALSE = Does not create a fake dir entry when finding the file + } + } else { + _error(errWRITEPROT); + } + } + return(deleted); +} + +// Renames a file +uint8 _RenameFile(uint16 fcbaddr) { + CPM_FCB* F = (CPM_FCB*)_RamSysAddr(fcbaddr); + uint8 result = 0xff; + + if (!_SelectDisk(F->dr)) { + if (!RW) { + _RamWrite(fcbaddr + 16, _RamRead(fcbaddr)); // Prevents rename from moving files among folders + _FCBtoHostname(fcbaddr + 16, &newname[0]); + _FCBtoHostname(fcbaddr, &filename[0]); + if (_sys_renamefile(&filename[0], &newname[0])) + result = 0x00; + } else { + _error(errWRITEPROT); + } + } + return(result); +} + +// Sequential read +uint8 _ReadSeq(uint16 fcbaddr) { + CPM_FCB* F = (CPM_FCB*)_RamSysAddr(fcbaddr); + uint8 result = 0xff; + + long fpos = ((F->s2 & MaxS2) * BlkS2 * BlkSZ) + + (F->ex * BlkEX * BlkSZ) + + (F->cr * BlkSZ); + + if (!_SelectDisk(F->dr)) { + _FCBtoHostname(fcbaddr, &filename[0]); + result = _sys_readseq(&filename[0], fpos); + if (!result) { // Read succeeded, adjust FCB + ++F->cr; + if (F->cr > MaxCR) { + F->cr = 1; + ++F->ex; + } + if (F->ex > MaxEX) { + F->ex = 0; + ++F->s2; + } + if ((F->s2 & 0x7F) > MaxS2) + result = 0xfe; // (todo) not sure what to do + } + } + return(result); +} + +// Sequential write +uint8 _WriteSeq(uint16 fcbaddr) { + CPM_FCB* F = (CPM_FCB*)_RamSysAddr(fcbaddr); + uint8 result = 0xff; + + long fpos = ((F->s2 & MaxS2) * BlkS2 * BlkSZ) + + (F->ex * BlkEX * BlkSZ) + + (F->cr * BlkSZ); + + if (!_SelectDisk(F->dr)) { + if (!RW) { + _FCBtoHostname(fcbaddr, &filename[0]); + result = _sys_writeseq(&filename[0], fpos); + if (!result) { // Write succeeded, adjust FCB + F->s2 &= 0x7F; // reset unmodified flag + ++F->cr; + if (F->cr > MaxCR) { + F->cr = 1; + ++F->ex; + } + if (F->ex > MaxEX) { + F->ex = 0; + ++F->s2; + } + if (F->s2 > MaxS2) + result = 0xfe; // (todo) not sure what to do + } + } else { + _error(errWRITEPROT); + } + } + return(result); +} + +// Random read +uint8 _ReadRand(uint16 fcbaddr) { + CPM_FCB* F = (CPM_FCB*)_RamSysAddr(fcbaddr); + uint8 result = 0xff; + + int32 record = (F->r2 << 16) | (F->r1 << 8) | F->r0; + long fpos = record * BlkSZ; + + if (!_SelectDisk(F->dr)) { + _FCBtoHostname(fcbaddr, &filename[0]); + result = _sys_readrand(&filename[0], fpos); + if (result == 0 || result == 1 || result == 4) { + // adjust FCB unless error #6 (seek past 8MB - max CP/M file & disk size) + F->cr = record & 0x7F; + F->ex = (record >> 7) & 0x1f; + if (F->s2 & 0x80) { + F->s2 = ((record >> 12) & MaxS2) | 0x80; + } else { + F->s2 = (record >> 12) & MaxS2; + } + } + } + return(result); +} + +// Random write +uint8 _WriteRand(uint16 fcbaddr) { + CPM_FCB* F = (CPM_FCB*)_RamSysAddr(fcbaddr); + uint8 result = 0xff; + + int32 record = (F->r2 << 16) | (F->r1 << 8) | F->r0; + long fpos = record * BlkSZ; + + if (!_SelectDisk(F->dr)) { + if (!RW) { + _FCBtoHostname(fcbaddr, &filename[0]); + result = _sys_writerand(&filename[0], fpos); + if (!result) { // Write succeeded, adjust FCB + F->cr = record & 0x7F; + F->ex = (record >> 7) & 0x1f; + F->s2 = (record >> 12) & MaxS2; // resets unmodified flag + } + } else { + _error(errWRITEPROT); + } + } + return(result); +} + +// Returns the size of a CP/M file +uint8 _GetFileSize(uint16 fcbaddr) { + CPM_FCB* F = (CPM_FCB*)_RamSysAddr(fcbaddr); + uint8 result = 0xff; + int32 count = _FileSize(DE) >> 7; + + if (count != -1) { + F->r0 = count & 0xff; + F->r1 = (count >> 8) & 0xff; + F->r2 = (count >> 16) & 0xff; + } + return(result); +} + +// Set the next random record +uint8 _SetRandom(uint16 fcbaddr) { + CPM_FCB* F = (CPM_FCB*)_RamSysAddr(fcbaddr); + uint8 result = 0x00; + + int32 count = F->cr & 0x7f; + count += (F->ex & 0x1f) << 7; + count += (F->s2 & MaxS2) << 12; + + F->r0 = count & 0xff; + F->r1 = (count >> 8) & 0xff; + F->r2 = (count >> 16) & 0xff; + + return(result); +} + +// Sets the current user area +void _SetUser(uint8 user) { + userCode = user & 0x1f; // BDOS unoficially allows user areas 0-31 + // this may create folders from G-V if this function is called from an user program + // It is an unwanted behavior, but kept as BDOS does it +#ifdef NOHIGHUSER + if(userCode < 16) +#endif + _MakeUserDir(); // Creates the user dir (0-F[G-V]) if needed +} + +// Creates a disk directory folder +uint8 _MakeDisk(uint16 fcbaddr) { + CPM_FCB* F = (CPM_FCB*)_RamSysAddr(fcbaddr); + return(_sys_makedisk(F->dr)); +} + +// Checks if there's a temp submit file present +uint8 _CheckSUB(void) { + uint8 result; + uint8 oCode = userCode; // Saves the current user code (original BDOS does not do this) + _HostnameToFCB(tmpFCB, (uint8*)"$???????.???"); // The original BDOS in fact only looks for a file which start with $ +#ifdef BATCHA + _RamWrite(tmpFCB, 1); // Forces it to be checked on drive A: +#endif +#ifdef BATCH0 + userCode = 0; // Forces it to be checked on user 0 +#endif + result = (_SearchFirst(tmpFCB, FALSE) == 0x00) ? 0xff : 0x00; + userCode = oCode; // Restores the current user code + return(result); +} + +#ifdef HASLUA +// Executes a Lua script +uint8 _RunLua(uint16 fcbaddr) { + uint8 luascript[17]; + uint8 result = 0xff; + + if (_FCBtoHostname(fcbaddr, &luascript[0])) { // Script name must be unique + if (!_SearchFirst(fcbaddr, FALSE)) { // and must exist + result = _RunLuaScript((char*)&luascript[0]); + } + } + + return(result); +} +#endif + +#endif diff --git a/runcpm-rp2350-hstx-usb/runcpm-pico/globals.h b/runcpm-rp2350-hstx-usb/runcpm-pico/globals.h new file mode 100644 index 000000000..511c0ddf9 --- /dev/null +++ b/runcpm-rp2350-hstx-usb/runcpm-pico/globals.h @@ -0,0 +1,252 @@ +// SPDX-FileCopyrightText: 2023 Mockba the Borg +// +// SPDX-License-Identifier: MIT + +#ifndef GLOBALS_H +#define GLOBALS_H + +/* Some definitions needed globally */ +#ifdef __MINGW32__ +#include +#endif + +/* Definition for enabling incrementing the R register for each M1 cycle */ +#define DO_INCR + +/* Definitions for enabling PUN: and LST: devices */ +#define USE_PUN // The pun.txt and lst.txt files will appear on drive A: user 0 +#define USE_LST + +/* Definitions for file/console based debugging */ +//#define DEBUG // Enables the internal debugger (enabled by default on vstudio debug builds) +//#define iDEBUG // Enables instruction logging onto iDebug.log (for development debug only) +//#define DEBUGLOG // Writes extensive call trace information to RunCPM.log +//#define CONSOLELOG // Writes debug information to console instead of file +//#define LOGBIOS_NOT 01 // If defined will not log this BIOS function number +//#define LOGBIOS_ONLY 02 // If defines will log only this BIOS function number +//#define LOGBDOS_NOT 06 // If defined will not log this BDOS function number +//#define LOGBDOS_ONLY 22 // If defines will log only this BDOS function number +#define LogName "RunCPM.log" + +/* RunCPM version for the greeting header */ +#define VERSION "6.0" +#define VersionBCD 0x60 + +/* Definition of which CCP to use (must define only one) */ +#define CCP_INTERNAL // If this is defined, an internal CCP will emulated +//#define CCP_DR +//#define CCP_CCPZ +//#define CCP_ZCPR2 +//#define CCP_ZCPR3 +//#define CCP_Z80 + +/* Definition of the CCP memory information */ +// +#ifdef CCP_INTERNAL +#define CCPname "INTERNAL v3.0" // Will use the CCP from ccp.h +#define VersionCCP 0x30 // 0x10 and above reserved for Internal CCP +#define BatchFCB (tmpFCB + 48) +#define CCPaddr BDOSjmppage // Internal CCP has size 0 +#endif +// +#ifdef CCP_DR +#define CCPname "CCP-DR." STR(TPASIZE) "K" +#define VersionCCP 0x00 // Version to be used by INFO.COM +#define BatchFCB (CCPaddr + 0x7AC) // Position of the $$$.SUB fcb on this CCP +#define CCPaddr (BDOSjmppage-0x0800) // CCP memory address +#endif +// +#ifdef CCP_CCPZ +#define CCPname "CCP-CCPZ." STR(TPASIZE) "K" +#define VersionCCP 0x01 +#define BatchFCB (CCPaddr + 0x7A) // Position of the $$$.SUB fcb on this CCP +#define CCPaddr (BDOSjmppage-0x0800) +#endif +// +#ifdef CCP_ZCPR2 +#define CCPname "CCP-ZCP2." STR(TPASIZE) "K" +#define VersionCCP 0x02 +#define BatchFCB (CCPaddr + 0x5E) // Position of the $$$.SUB fcb on this CCP +#define CCPaddr (BDOSjmppage-0x0800) +#endif +// +#ifdef CCP_ZCPR3 +#define CCPname "CCP-ZCP3." STR(TPASIZE) "K" +#define VersionCCP 0x03 +#define BatchFCB (CCPaddr + 0x5E) // Position of the $$$.SUB fcb on this CCP +#define CCPaddr (BDOSjmppage-0x1000) +#endif +// +#ifdef CCP_Z80 +#define CCPname "CCP-Z80." STR(TPASIZE) "K" +#define VersionCCP 0x04 +#define BatchFCB (CCPaddr + 0x79E) // Position of the $$$.SUB fcb on this CCP +#define CCPaddr (BDOSjmppage-0x0800) +#endif +// +#ifndef CCPname +#error No CCP defined +#endif +// +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +// #define CCPHEAD "\r\nRunCPM Version " VERSION " (CP/M " STR(TPASIZE) "K)\r\n" +#define CCPHEAD "\r\nRunCPM [" TEXT_BOLD "v" VERSION TEXT_NORMAL "] => CCP:[" TEXT_BOLD CCPname TEXT_NORMAL "] TPA:" TEXT_BOLD STR(TPASIZE) "K" TEXT_NORMAL "\r\n" + +#define NOSLASH // Will translate '/' to '_' on filenames to prevent directory errors + +//#define HASLUA // Will enable Lua scripting (BDOS call 254) + // Should be passed externally per-platform with -DHASLUA + +//#define PROFILE // For measuring time taken to run a CP/M command + // This should be enabled only for debugging purposes when trying to improve emulation speed + +#define NOHIGHUSER // Prevents the creation of user folders above 'F' (15) by programs + // Original CP/M BDOS allows it, but I prefer to keep the folders clean + +/* Definition for CP/M 2.2 user number support */ + +#define BATCHA // If this is defined, the $$$.SUB will be looked for on drive A: +//#define BATCH0 // If this is defined, the $$$.SUB will be looked for on user area 0 + // The default behavior of DRI's CP/M 2.2 was to have $$$.SUB created on the current drive/user while looking for it + // on drive A: current user, which made it complicated to run SUBMITs when not logged to drive A: user 0 + +/* Some environment and type definitions */ + +#ifndef TRUE +#define FALSE 0 +#define TRUE 1 +#endif + +typedef signed char int8; +typedef signed short int16; +typedef signed int int32; +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; + +#define LOW_DIGIT(x) ((x) & 0xf) +#define HIGH_DIGIT(x) (((x) >> 4) & 0xf) +#define LOW_REGISTER(x) ((x) & 0xff) +#define HIGH_REGISTER(x) (((x) >> 8) & 0xff) + +#define SET_LOW_REGISTER(x, v) x = (((x) & 0xff00) | ((v) & 0xff)) +#define SET_HIGH_REGISTER(x, v) x = (((x) & 0xff) | (((v) & 0xff) << 8)) + +#define WORD16(x) ((x) & 0xffff) + +/* CP/M Page 0 definitions */ +#define IOByte 0x03 +#define DSKByte 0x04 + +/* CP/M disk definitions */ +#define BlkSZ 128 // CP/M block size +#define BlkEX 128 // Number of blocks on an extension +#define ExtSZ (BlkSZ * BlkEX) +#define BlkS2 4096 // Number of blocks on a S2 (module) +#define MaxEX 31 // Maximum value the EX field can take +#define MaxS2 15 // Maximum value the S2 (modules) field can take - Can be set to 63 to emulate CP/M Plus +#define MaxCR 128 // Maximum value the CR field can take +#define MaxRC 128 // Maximum value the RC field can take + +/* CP/M memory definitions */ +#define TPASIZE 64 // Can be 60 for CP/M 2.2 compatibility or more, up to 64 for extra memory + // Values other than 60 or 64 would require rebuilding the CCP + // For TPASIZE<60 CCP ORG = (SIZEK * 1024) - 0x0C00 + +#define BANKS 1 // Number of memory banks available +static uint8 curBank = 1; // Number of the current RAM bank in use (1-x, not 0-x) +static uint8 isXmove = FALSE; // Used by BIOS +static uint8 srcBank = 1; // Source bank for memory MOVE +static uint8 dstBank = 1; // Destination bank for memory MOVE +static uint8 ioBank = 1; // Destination bank for sector IO + +#define PAGESIZE 64 * 1024 // RAM(plus ROM) needs to be 64K to avoid compatibility issues +#define MEMSIZE PAGESIZE * BANKS // Total RAM size + +#if BANKS==1 +#define RAM_FAST // If this is defined, all RAM function calls become direct access (see below) + // This saves about 2K on the Arduino code and should bring speed improvements +#endif + +#ifdef RAM_FAST // Makes all function calls to memory access into direct RAM access (less calls / less code) + static uint8 RAM[MEMSIZE]; + #define _RamSysAddr(a) &RAM[a] + #define _RamRead(a) RAM[a] + #define _RamRead16(a) ((RAM[((a) & 0xffff) + 1] << 8) | RAM[(a) & 0xffff]) + #define _RamWrite(a, v) RAM[a] = v + #define _RamWrite16(a, v) RAM[a] = (v) & 0xff; RAM[(a) + 1] = (v) >> 8 +#endif + +// Size of the allocated pages (Minimum size = 1 page = 256 bytes) + +// BIOS Pages (512 bytes from the top of memory) +#define BIOSjmppage (PAGESIZE - 512) +#define BIOSpage (BIOSjmppage + 256) + +// BDOS Pages (depends on TPASIZE for external CCPs) +#if defined CCP_INTERNAL + #define BDOSjmppage (BIOSjmppage - 256) + #define BDOSpage (BDOSjmppage + 16) +#else + #define BDOSjmppage (TPASIZE * 1024) - 1024 + #define BDOSpage (BDOSjmppage + 256) +#endif + +#define DPBaddr (BIOSpage + 128) // Address of the Disk Parameter Block (Hardcoded in BIOS) +#define DPHaddr (DPBaddr + 15) // Address of the Disk Parameter Header + +#define SCBaddr (BDOSpage + 3) // Address of the System Control Block +#define tmpFCB (BDOSpage + 16) // Address of the temporary FCB + +/* Definition of global variables */ +static uint8 filename[17]; // Current filename in host filesystem format +static uint8 newname[17]; // New filename in host filesystem format +static uint8 fcbname[13]; // Current filename in CP/M format +static uint8 pattern[13]; // File matching pattern in CP/M format +static uint16 dmaAddr = 0x0080; // Current dmaAddr +static uint8 oDrive = 0; // Old selected drive +static uint8 cDrive = 0; // Currently selected drive +static uint8 userCode = 0; // Current user code +static uint16 roVector = 0; +static uint16 loginVector = 0; +static uint8 allUsers = FALSE; // true when dr is '?' in BDOS search first +static uint8 allExtents = FALSE; // true when ex is '?' in BDOS search first +static uint8 currFindUser = 0; // user number of current directory in BDOS search first on all user numbers +static uint8 blockShift; // disk allocation block shift +static uint8 blockMask; // disk allocation block mask +static uint8 extentMask; // disk extent mask +static uint16 firstBlockAfterDir; // first allocation block after directory +static uint16 numAllocBlocks; // # of allocation blocks on disk +static uint8 extentsPerDirEntry; // # of logical (16K) extents in a directory entry +#define logicalExtentBytes (16*1024UL) +static uint16 physicalExtentBytes;// # bytes described by 1 directory entry + +#define tohex(x) ((x) < 10 ? (x) + 48 : (x) + 87) + +/* Definition of externs to prevent precedence compilation errors */ +#ifdef __cplusplus // If building on Arduino +extern "C" +{ +#endif + +#ifndef RAM_FAST + extern uint8* _RamSysAddr(uint16 address); + extern void _RamWrite(uint16 address, uint8 value); +#endif + + extern void _Bdos(void); + extern void _Bios(void); + + extern void _HostnameToFCB(uint16 fcbaddr, uint8* filename); + extern void _HostnameToFCBname(uint8* from, uint8* to); + extern void _mockupDirEntry(void); + extern uint8 match(uint8* fcbname, uint8* pattern); + + extern void _puts(const char* str); + +#ifdef __cplusplus // If building on Arduino +} +#endif + +#endif diff --git a/runcpm-rp2350-hstx-usb/runcpm-pico/hardware/pico/metro_rp2350.h b/runcpm-rp2350-hstx-usb/runcpm-pico/hardware/pico/metro_rp2350.h new file mode 100644 index 000000000..e4d4b1503 --- /dev/null +++ b/runcpm-rp2350-hstx-usb/runcpm-pico/hardware/pico/metro_rp2350.h @@ -0,0 +1,341 @@ +// SPDX-FileCopyrightText: 2023 Mockba the Borg +// SPDX-FileCopyrightText: 2023 Jeff Epler for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "../../arduino_hooks.h" +#include "../../console.h" +#include +#include +#include +#include +#include + +DVHSTXText display(DVHSTX_PINOUT_DEFAULT); + +// USB Host object +Adafruit_USBH_Host USBHost; + +SdFat SD; +#if PIN_SPI1_SCK == PIN_SD_CLK +SdSpiConfig config(PIN_SD_DAT3_CS, DEDICATED_SPI, SD_SCK_MHZ(16), &SPI1); +#else +SdSpiConfig config(PIN_SD_DAT3_CS, DEDICATED_SPI, SD_SCK_MHZ(16)); +#endif + +static bool start1; + +// ========================================================================================= +// Define Board-Data +// GP25 green onboard LED +// ========================================================================================= +#define LED (13) +#define LEDinv (false) +#define board_pico +#define board_analog_io +#define board_digital_io + +queue_t kb_queue; + +void putch_display(uint8_t ch) { + if (ch == 8) { + auto x = display.getCursorX(); + if (x > 0) { + auto y = display.getCursorY(); + display.setCursor(--x, y); + display.drawPixel(x, y, ' '); + } + } else { + display.write(ch); + } +} + +uint8_t getch_usbhost(void) { + uint8_t result; + queue_remove_blocking(&kb_queue, &result); + return result; +} + +bool kbhit_usbhost(void) { return queue_get_level(&kb_queue) > 0; } + +bool port_init_early() { + if (!display.begin()) { + return false; + } + display.showCursor(); + _putch_hook = putch_display; + delay(10); + + queue_init_with_spinlock(&kb_queue, 1, 64, spin_lock_claim_unused(true)); + start1 = true; + _getch_hook = getch_usbhost; + _kbhit_hook = kbhit_usbhost; + + return true; +} + +bool port_flash_begin() { + if (!SD.begin(config)) { + return false; + } + return true; +} + +/************************************************************************* + * USB Host keyboard + *************************************************************************/ + +void setup1() { + while (!start1) + ; + +#ifdef PIN_5V_EN + pinMode(PIN_5V_EN, OUTPUT); + digitalWrite(PIN_5V_EN, PIN_5V_EN_STATE); +#endif + + pio_usb_configuration_t pio_cfg = PIO_USB_DEFAULT_CONFIG; + pio_cfg.pin_dp = PIN_USB_HOST_DP; + pio_cfg.tx_ch = dma_claim_unused_channel(true); + dma_channel_unclaim(pio_cfg.tx_ch); + + USBHost.configure_pio_usb(1, &pio_cfg); + + // run host stack on controller (rhport) 1 + // Note: For rp2040 pico-pio-usb, calling USBHost.begin() on core1 will have + // most of the host bit-banging processing works done in core1 to free up + // core0 for other works + USBHost.begin(1); + Serial.println("end of setup1"); +} + +int old_ascii = -1; +uint32_t repeat_timeout; +// this matches Linux default of 500ms to first repeat, 1/20s thereafter +const uint32_t default_repeat_time = 50; +const uint32_t initial_repeat_time = 500; + +void send_ascii(uint8_t code, uint32_t repeat_time = default_repeat_time) { + old_ascii = code; + repeat_timeout = millis() + repeat_time; + queue_try_add(&kb_queue, &code); // failure is ignored +} + +void loop1() { + uint32_t now = millis(); + uint32_t deadline = repeat_timeout - now; + if (old_ascii >= 0 && deadline > INT32_MAX) { + send_ascii(old_ascii); + deadline = repeat_timeout - now; + } else if (old_ascii < 0) { + deadline = UINT32_MAX; + } + tuh_task_ext(deadline, false); +} + +// Invoked when device with hid interface is mounted +// Report descriptor is also available for use. +// tuh_hid_parse_report_descriptor() can be used to parse common/simple enough +// descriptor. Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE, +// it will be skipped therefore report_desc = NULL, desc_len = 0 +void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, + uint8_t const *desc_report, uint16_t desc_len) { + (void)desc_report; + (void)desc_len; + uint16_t vid, pid; + tuh_vid_pid_get(dev_addr, &vid, &pid); + + Serial.printf("HID device address = %d, instance = %d is mounted\r\n", + dev_addr, instance); + Serial.printf("VID = %04x, PID = %04x\r\n", vid, pid); + + uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance); + if (itf_protocol == HID_ITF_PROTOCOL_KEYBOARD) { + Serial.printf("HID Keyboard\r\n"); + if (!tuh_hid_receive_report(dev_addr, instance)) { + Serial.printf("Error: cannot request to receive report\r\n"); + } + } +} + +// Invoked when device with hid interface is un-mounted +void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) { + Serial.printf("HID device address = %d, instance = %d is unmounted\r\n", + dev_addr, instance); +} + +#define FLAG_ALPHABETIC (1) +#define FLAG_SHIFT (2) +#define FLAG_NUMLOCK (4) +#define FLAG_CTRL (8) +#define FLAG_LUT (16) + +const char *const lut[] = { + "!@#$%^&*()", /* 0 - shifted numeric keys */ + "\r\x1b\10\t -=[]\\#;'`,./", /* 1 - symbol keys */ + "\n\x1b\177\t _+{}|~:\"~<>?", /* 2 - shifted */ + "\12\13\10\22", /* 3 - arrow keys RLDU */ + "/*-+\n1234567890.", /* 4 - keypad w/numlock */ + "/*-+\n\xff\2\xff\4\xff\3\xff\1\xff\xff.", /* 5 - keypad w/o numlock */ +}; + +struct keycode_mapper { + uint8_t first, last, code, flags; +} keycode_to_ascii[] = { + { + HID_KEY_A, + HID_KEY_Z, + 'a', + FLAG_ALPHABETIC, + }, + + { + HID_KEY_1, + HID_KEY_9, + 0, + FLAG_SHIFT | FLAG_LUT, + }, + { + HID_KEY_1, + HID_KEY_9, + '1', + 0, + }, + { + HID_KEY_0, + HID_KEY_0, + ')', + FLAG_SHIFT, + }, + { + HID_KEY_0, + HID_KEY_0, + '0', + 0, + }, + + {HID_KEY_ENTER, HID_KEY_ENTER, '\n', FLAG_CTRL}, + { + HID_KEY_ENTER, + HID_KEY_SLASH, + 2, + FLAG_SHIFT | FLAG_LUT, + }, + { + HID_KEY_ENTER, + HID_KEY_SLASH, + 1, + FLAG_LUT, + }, + + { + HID_KEY_F1, + HID_KEY_F1, + 0x1e, + 0, + }, // help key on xerox 820 kbd + + {HID_KEY_ARROW_RIGHT, HID_KEY_ARROW_UP, 3, FLAG_LUT}, + + {HID_KEY_KEYPAD_DIVIDE, HID_KEY_KEYPAD_DECIMAL, 4, FLAG_NUMLOCK | FLAG_LUT}, + {HID_KEY_KEYPAD_DIVIDE, HID_KEY_KEYPAD_DECIMAL, 5, FLAG_LUT}, +}; + +bool report_contains(const hid_keyboard_report_t &report, uint8_t key) { + for (int i = 0; i < 6; i++) { + if (report.keycode[i] == key) + return true; + } + return false; +} + +hid_keyboard_report_t old_report; + +static bool caps, num; +static uint8_t old_leds; +void process_event(uint8_t dev_addr, uint8_t instance, + const hid_keyboard_report_t &report) { + bool alt = report.modifier & 0x44; + bool shift = report.modifier & 0x22; + bool ctrl = report.modifier & 0x11; + uint8_t code = 0; + + if (report.keycode[0] == 1 && report.keycode[1] == 1) { + // keyboard says it has exceeded max kro + return; + } + + // something was pressed or release, so cancel any key repeat + old_ascii = -1; + + for (auto keycode : report.keycode) { + if (keycode == 0) + continue; + if (report_contains(old_report, keycode)) + continue; + + /* key is newly pressed */ + if (keycode == HID_KEY_NUM_LOCK) { + Serial.println("toggle numlock"); + num = !num; + } else if (keycode == HID_KEY_CAPS_LOCK) { + Serial.println("toggle capslock"); + caps = !caps; + } else { + for (const auto &mapper : keycode_to_ascii) { + if (!(keycode >= mapper.first && keycode <= mapper.last)) + continue; + if (mapper.flags & FLAG_SHIFT && !shift) + continue; + if (mapper.flags & FLAG_NUMLOCK && !num) + continue; + if (mapper.flags & FLAG_CTRL && !ctrl) + continue; + if (mapper.flags & FLAG_LUT) { + code = lut[mapper.code][keycode - mapper.first]; + } else { + code = keycode - mapper.first + mapper.code; + } + if (mapper.flags & FLAG_ALPHABETIC) { + if (shift ^ caps) { + code ^= ('a' ^ 'A'); + } + } + if (ctrl) + code &= 0x1f; + if (alt) + code ^= 0x80; + send_ascii(code, initial_repeat_time); + break; + } + } + } + + uint8_t leds = (caps << 1) | num; + if (leds != old_leds) { + old_leds = leds; + Serial.printf("Send LEDs report %d (dev:instance = %d:%d)\r\n", leds, + dev_addr, instance); + // no worky + auto r = tuh_hid_set_report(dev_addr, instance /*idx*/, 0 /*report_id*/, + HID_REPORT_TYPE_OUTPUT /*report_type*/, + &old_leds, sizeof(old_leds)); + Serial.printf("set_report() -> %d\n", (int)r); + } + old_report = report; +} + +// Invoked when received report from device via interrupt endpoint +void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, + uint8_t const *report, uint16_t len) { + if (len != sizeof(hid_keyboard_report_t)) { + Serial.printf("report len = %u NOT 8, probably something wrong !!\r\n", + len); + } else { + process_event(dev_addr, instance, *(hid_keyboard_report_t *)report); + } + // continue to request to receive report + if (!tuh_hid_receive_report(dev_addr, instance)) { + Serial.printf("Error: cannot request to receive report\r\n"); + } +} diff --git a/runcpm-rp2350-hstx-usb/runcpm-pico/host.h b/runcpm-rp2350-hstx-usb/runcpm-pico/host.h new file mode 100644 index 000000000..024f98e14 --- /dev/null +++ b/runcpm-rp2350-hstx-usb/runcpm-pico/host.h @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2023 Mockba the Borg +// +// SPDX-License-Identifier: MIT + +#ifndef HOST_H +#define HOST_H + +uint8 hostbdos(uint16 dmaaddr) { + return(0x00); +} + +#endif diff --git a/runcpm-rp2350-hstx-usb/runcpm-pico/main.c b/runcpm-rp2350-hstx-usb/runcpm-pico/main.c new file mode 100644 index 000000000..683ad7243 --- /dev/null +++ b/runcpm-rp2350-hstx-usb/runcpm-pico/main.c @@ -0,0 +1,115 @@ +// Copyright (c) 2016 - Marcelo Dantas +// SPDX-FileCopyrightText: 2023 Mockba the Borg +// +// SPDX-License-Identifier: MIT + +// Only build this code if not on Arduino +#ifndef ARDUINO + +/* globals.h must be the first file included - it defines the bare essentials */ +#include "globals.h" + +/* Any system specific includes should go here - this will define system functions used by the abstraction */ + +/* all the RunCPM includes must go after the system specific includes */ + +/* +abstraction.h - Adds all system dependent calls and definitions needed by RunCPM +This should be the only file modified for portability. Any other file +shoud be kept the same. +*/ + +#ifdef _WIN32 +#include "abstraction_vstudio.h" +#else +#include "abstraction_posix.h" +#endif + +// AUX: device configuration +#ifdef USE_PUN +FILE* pun_dev; +int pun_open = FALSE; +#endif + +// PRT: device configuration +#ifdef USE_LST +FILE* lst_dev; +int lst_open = FALSE; +#endif + +#include "ram.h" // ram.h - Implements the RAM +#include "console.h" // console.h - Defines all the console abstraction functions +#include "cpu.h" // cpu.h - Implements the emulated CPU +#include "disk.h" // disk.h - Defines all the disk access abstraction functions +#include "host.h" // host.h - Custom host-specific BDOS call +#include "cpm.h" // cpm.h - Defines the CPM structures and calls +#ifdef CCP_INTERNAL +#include "ccp.h" // ccp.h - Defines a simple internal CCP +#endif + +int main(int argc, char* argv[]) { + +#ifdef DEBUGLOG + _sys_deletefile((uint8*)LogName); +#endif + + _host_init(argc, &argv[0]); + _console_init(); + _clrscr(); + _puts(" CP/M Emulator v" VERSION " by Marcelo Dantas\r\n"); + _puts(" Built " __DATE__ " - " __TIME__ "\r\n"); +#ifdef HASLUA + _puts(" with Lua scripting support\r\n"); +#endif + _puts("-----------------------------------------\r\n"); + _puts("BIOS at 0x"); + _puthex16(BIOSjmppage); + _puts(" - "); + _puts("BDOS at 0x"); + _puthex16(BDOSjmppage); + _puts("\r\n"); + _puts("CCP " CCPname " at 0x"); + _puthex16(CCPaddr); + _puts("\r\n"); +#if BANKS > 1 + _puts("Banked Memory: "); + _puthex8(BANKS); + _puts(" banks\r\n"); +#endif + + while (TRUE) { + _puts(CCPHEAD); + _PatchCPM(); // Patches the CP/M entry points and other things in + Status = 0; +#ifdef CCP_INTERNAL + _ccp(); +#else + if (!_sys_exists((uint8*)CCPname)) { + _puts("Unable to load CP/M CCP.\r\nCPU halted.\r\n"); + break; + } + _RamLoad((uint8*)CCPname, CCPaddr); // Loads the CCP binary file into memory + Z80reset(); // Resets the Z80 CPU + SET_LOW_REGISTER(BC, _RamRead(DSKByte)); // Sets C to the current drive/user + PC = CCPaddr; // Sets CP/M application jump point + Z80run(); // Starts simulation +#endif + if (Status == 1) // This is set by a call to BIOS 0 - ends CP/M + break; +#ifdef USE_PUN + if (pun_dev) + _sys_fflush(pun_dev); +#endif +#ifdef USE_LST + if (lst_dev) + _sys_fflush(lst_dev); +#endif + } + + _puts("\r\n"); + _console_reset(); + + return(0); +} + +#endif diff --git a/runcpm-rp2350-hstx-usb/runcpm-pico/ram.h b/runcpm-rp2350-hstx-usb/runcpm-pico/ram.h new file mode 100644 index 000000000..3c7604bd7 --- /dev/null +++ b/runcpm-rp2350-hstx-usb/runcpm-pico/ram.h @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: 2023 Mockba the Borg +// +// SPDX-License-Identifier: MIT + +#ifndef RAM_H +#define RAM_H + +/* see main.c for definition */ + +#ifndef RAM_FAST +static uint8 RAM[MEMSIZE]; // Definition of the emulated RAM + +uint8* _RamSysAddr(uint16 address) { + if (address < CCPaddr) { + return(&RAM[address * curBank]); + } else { + return(&RAM[address]); + } +} + +uint8 _RamRead(uint16 address) { + if (address < CCPaddr) { + return(RAM[address * curBank]); + } else { + return(RAM[address]); + } +} + +uint16 _RamRead16(uint16 address) { + if (address < CCPaddr) { + return(RAM[address * curBank] + (RAM[(address * curBank) + 1] << 8)); + } else { + return(RAM[address] + (RAM[address + 1] << 8)); + } +} + +void _RamWrite(uint16 address, uint8 value) { + if (address < CCPaddr) { + RAM[address * curBank] = value; + } else { + RAM[address] = value; + } +} + +void _RamWrite16(uint16 address, uint16 value) { + // Z80 is a "little indian" (8 bit era joke) + _RamWrite(address, value & 0xff); + _RamWrite(address + 1, (value >> 8) & 0xff); +} +#endif + +#endif diff --git a/runcpm-rp2350-hstx-usb/runcpm-pico/resource.h b/runcpm-rp2350-hstx-usb/runcpm-pico/resource.h new file mode 100644 index 000000000..018f0cef8 --- /dev/null +++ b/runcpm-rp2350-hstx-usb/runcpm-pico/resource.h @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: 2023 Mockba the Borg +// +// SPDX-License-Identifier: MIT + +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by RunCPM.rc +// +#define IDI_ICON1 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/runcpm-rp2350-hstx-usb/runcpm-pico/runcpm-pico.ino b/runcpm-rp2350-hstx-usb/runcpm-pico/runcpm-pico.ino new file mode 100644 index 000000000..f6afb1844 --- /dev/null +++ b/runcpm-rp2350-hstx-usb/runcpm-pico/runcpm-pico.ino @@ -0,0 +1,227 @@ +// SPDX-FileCopyrightText: 2023 Mockba the Borg +// +// SPDX-License-Identifier: MIT + +// only AVR and ARM CPU +// #include + +// ========================================================================================= +// Guido Lehwalder's Code-Revision-Number +// ========================================================================================= +#define GL_REV "GL20230303.0" + +#include + +#include // One SD library to rule them all - Greinman SdFat from Library Manager + +#ifndef USE_VT100 +#define USE_VT100 (0) +#endif + +#if USE_VT100 +#define TEXT_BOLD "\033[1m" +#define TEXT_NORMAL "\033[0m" +#else +#define TEXT_BOLD "" +#define TEXT_NORMAL "" +#endif + +#include "globals.h" + +// ========================================================================================= +// Board definitions go into the "hardware" folder, if you use a board different +// than the Arduino DUE, choose/change a file from there and reference that file +// here +// ========================================================================================= + +// Metro RP2350 +#include "hardware/pico/metro_rp2350.h" + +#ifndef BOARD_TEXT +#define BOARD_TEXT USB_MANUFACTURER " " USB_PRODUCT +#endif + +#include "abstraction_arduino.h" +// Raspberry Pi Pico W(iFi) (LED = GPIO32) +// #include "hardware/pico/pico_w_sd_spi.h" + +// ========================================================================================= +// Delays for LED blinking +// ========================================================================================= +#define sDELAY 100 +#define DELAY 1200 + +// ========================================================================================= +// Serial port speed +// ========================================================================================= +#define SERIALSPD 115200 + +// ========================================================================================= +// PUN: device configuration +// ========================================================================================= +#ifdef USE_PUN +FILE_TYPE pun_dev; +int pun_open = FALSE; +#endif + +// ========================================================================================= +// LST: device configuration +// ========================================================================================= +#ifdef USE_LST +FILE_TYPE lst_dev; +int lst_open = FALSE; +#endif + +// (n.b. these are order sensitive. don't let clang-format reorder them!) +#include "ram.h" +#include "console.h" +#include "cpu.h" +#include "disk.h" +#include "host.h" +#include "cpm.h" +#ifdef CCP_INTERNAL +#include "ccp.h" +#endif + +void setup(void) { + pinMode(LED, OUTPUT); + digitalWrite(LED, LOW ^ LEDinv); + + // ========================================================================================= + // Serial Port Definition + // ========================================================================================= + // Serial =USB / Serial1 =UART0/COM1 / Serial2 =UART1/COM2 + + Serial1.setRX(1); // Pin 2 + Serial1.setTX(0); // Pin 1 + Serial1.begin(SERIALSPD); + + Serial2.setRX(5); // Pin 7 + Serial2.setTX(4); // Pin 6 + Serial2.begin(SERIALSPD); + + // or + + // Serial1.setRX(17); // Pin 22 + // Serial1.setTX(16); // Pin 21 + + // Serial2.setRX(21); // Pin 27 + // Serial2.setTX(20); // Pin 26 + // ========================================================================================= + + if (!port_init_early()) { + return; + } + +#if defined(WAIT_SERIAL) + // _clrscr(); + // _puts("Opening serial-port...\r\n"); + Serial.begin(SERIALSPD); + while (!Serial) { // Wait until serial is connected + digitalWrite(LED, HIGH ^ LEDinv); + delay(sDELAY); + digitalWrite(LED, LOW ^ LEDinv); + delay(DELAY); + } +#endif + +#ifdef DEBUGLOG + _sys_deletefile((uint8 *)LogName); +#endif + + // ========================================================================================= + // Printing the Startup-Messages + // ========================================================================================= + + _clrscr(); + + // if (bootup_press == 1) + // { _puts("Recognized " TEXT_BOLD "#" TEXT_NORMAL " key as pressed! + // :)\r\n\r\n"); + // } + + _puts("CP/M Emulator " TEXT_BOLD "v" VERSION "" TEXT_NORMAL + " by " TEXT_BOLD "Marcelo Dantas" TEXT_NORMAL "\r\n"); + _puts("----------------------------------------------\r\n"); + _puts(" running on [" TEXT_BOLD BOARD_TEXT TEXT_NORMAL "]\r\n"); + _puts("----------------------------------------------\r\n"); + + _puts("BIOS at [" TEXT_BOLD "0x"); + _puthex16(BIOSjmppage); + // _puts(" - "); + _puts("" TEXT_NORMAL "]\r\n"); + + _puts("BDOS at [" TEXT_BOLD "0x"); + _puthex16(BDOSjmppage); + _puts("" TEXT_NORMAL "]\r\n"); + + _puts("CCP " CCPname " at [" TEXT_BOLD "0x"); + _puthex16(CCPaddr); + _puts("" TEXT_NORMAL "]\r\n"); + +#if BANKS > 1 + _puts("Banked Memory [" TEXT_BOLD ""); + _puthex8(BANKS); + _puts("" TEXT_NORMAL "]banks\r\n"); +#endif + + // Serial.printf("Free Memory [" TEXT_BOLD "%d bytes" TEXT_NORMAL + // "]\r\n", freeMemory()); + + _puts("CPU-Clock [" TEXT_BOLD); + _putdec((clock_get_hz(clk_sys) + 500'000) / 1'000'000); + _puts(TEXT_NORMAL "] MHz\r\n"); + + _puts("Init Storage [ " TEXT_BOLD ""); + if (port_flash_begin()) { + _puts("OK " TEXT_NORMAL "]\r\n"); + _puts("----------------------------------------------"); + + if (VersionCCP >= 0x10 || SD.exists(CCPname)) { + while (true) { + _puts(CCPHEAD); + _PatchCPM(); + Status = 0; +#ifndef CCP_INTERNAL + if (!_RamLoad((char *)CCPname, CCPaddr)) { + _puts("Unable to load the CCP.\r\nCPU halted.\r\n"); + break; + } + Z80reset(); + SET_LOW_REGISTER(BC, _RamRead(DSKByte)); + PC = CCPaddr; + Z80run(); +#else + _ccp(); +#endif + if (Status == 1) + break; +#ifdef USE_PUN + if (pun_dev) + _sys_fflush(pun_dev); +#endif +#ifdef USE_LST + if (lst_dev) + _sys_fflush(lst_dev); +#endif + } + } else { + _puts("Unable to load CP/M CCP.\r\nCPU halted.\r\n"); + } + } else { + _puts("ERR " TEXT_NORMAL + "]\r\nUnable to initialize SD card.\r\nCPU halted.\r\n"); + } +} + +// if loop is reached, blink LED forever to signal error +void loop(void) { + digitalWrite(LED, HIGH ^ LEDinv); + delay(DELAY); + digitalWrite(LED, LOW ^ LEDinv); + delay(DELAY); + digitalWrite(LED, HIGH ^ LEDinv); + delay(DELAY); + digitalWrite(LED, LOW ^ LEDinv); + delay(DELAY * 4); +}