diff --git a/library.deps b/library.deps index c25f99062..facc430c7 100644 --- a/library.deps +++ b/library.deps @@ -1 +1 @@ -depends=Adafruit ILI9341, Adafruit BusIO, SD, Adafruit NeoPixel, Adafruit VS1053 Library, Adafruit BluefruitLE nRF51, Adafruit seesaw Library, Ethernet, Adafruit IO Arduino, FastLED, Adafruit LiquidCrystal, Adafruit SoftServo, TinyWireM, Adafruit AM radio library, WaveHC, Adafruit LED Backpack Library, MAX31850 OneWire, Adafruit VC0706 Serial Camera Library, RTClib, Adafruit SleepyDog Library, Adafruit Thermal Printer Library, Adafruit Zero I2S Library, Adafruit EPD, Adafruit SSD1351 library, Adafruit FONA Library, Adafruit Motor Shield V2 Library, Adafruit NeoMatrix, Adafruit Soundboard library, Adafruit Circuit Playground, ArduinoJson, Adafruit TCS34725, Adafruit Pixie, Adafruit GPS Library, TinyGPS, WiFi101, Adafruit DotStar, Adafruit Si7021 Library, Adafruit WS2801 Library, Mouse, Keyboard, Time, IRremote, Adafruit LSM9DS0 Library, Adafruit Arcada Library, MIDIUSB, PubSubClient, Adafruit LIS2MDL, Adafruit NeoPXL8, Adafruit MCP23017 Arduino Library, Adafruit MLX90640, LiquidCrystal, Adafruit NeoTrellis M4 Library, RGB matrix Panel, Adafruit MLX90614 Library, Adafruit RGB LCD Shield Library, MAX6675 library, Adafruit MP3, Adafruit Keypad, Adafruit Arcada GifDecoder, Keypad, Neosegment, Encoder, Adafruit TiCoServo, Adafruit Trellis Library, FauxmoESP, Adafruit LSM303 Accel, Adafruit LSM303DLH Mag, Adafruit LSM303DLHC, CapacitiveSensor, Adafruit Zero PDM Library, Adafruit DMA neopixel library, elapsedMillis, DST RTC, Adafruit SHARP Memory Display, Adafruit SPIFlash, BSEC Software Library, WiiChuck, Adafruit DPS310, Adafruit AHTX0, RotaryEncoder, Adafruit MCP9808 Library, LSM303, Adafruit Protomatter, Adafruit IS31FL3741 Library, Sensirion I2C SCD4x, Adafruit TestBed, Bounce2, Adafruit AHRS, Adafruit DRV2605 Library, STM32duino VL53L4CD, PicoDVI - Adafruit Fork, Adafruit MMA8451 Library, Adafruit TSC2007, GFX Library for Arduino, Adafruit PyCamera Library, Adafruit ADG72x, Adafruit BNO055, Adafruit SHT4x Library, Adafruit VCNL4200 Library, Adafruit GC9A01A +depends=Adafruit ILI9341, Adafruit BusIO, SD, Adafruit NeoPixel, Adafruit VS1053 Library, Adafruit BluefruitLE nRF51, Adafruit seesaw Library, Ethernet, Adafruit IO Arduino, FastLED, Adafruit LiquidCrystal, Adafruit SoftServo, TinyWireM, Adafruit AM radio library, WaveHC, Adafruit LED Backpack Library, MAX31850 OneWire, Adafruit VC0706 Serial Camera Library, RTClib, Adafruit SleepyDog Library, Adafruit Thermal Printer Library, Adafruit Zero I2S Library, Adafruit EPD, Adafruit SSD1351 library, Adafruit FONA Library, Adafruit Motor Shield V2 Library, Adafruit NeoMatrix, Adafruit Soundboard library, Adafruit Circuit Playground, ArduinoJson, Adafruit TCS34725, Adafruit Pixie, Adafruit GPS Library, TinyGPS, WiFi101, Adafruit DotStar, Adafruit Si7021 Library, Adafruit WS2801 Library, Mouse, Keyboard, Time, IRremote, Adafruit LSM9DS0 Library, Adafruit Arcada Library, MIDIUSB, PubSubClient, Adafruit LIS2MDL, Adafruit NeoPXL8, Adafruit MCP23017 Arduino Library, Adafruit MLX90640, LiquidCrystal, Adafruit NeoTrellis M4 Library, RGB matrix Panel, Adafruit MLX90614 Library, Adafruit RGB LCD Shield Library, MAX6675 library, Adafruit MP3, Adafruit Keypad, Adafruit Arcada GifDecoder, Keypad, Neosegment, Encoder, Adafruit TiCoServo, Adafruit Trellis Library, FauxmoESP, Adafruit LSM303 Accel, Adafruit LSM303DLH Mag, Adafruit LSM303DLHC, CapacitiveSensor, Adafruit Zero PDM Library, Adafruit DMA neopixel library, elapsedMillis, DST RTC, Adafruit SHARP Memory Display, Adafruit SPIFlash, BSEC Software Library, WiiChuck, Adafruit DPS310, Adafruit AHTX0, RotaryEncoder, Adafruit MCP9808 Library, LSM303, Adafruit Protomatter, Adafruit IS31FL3741 Library, Sensirion I2C SCD4x, Adafruit TestBed, Bounce2, Adafruit AHRS, Adafruit DRV2605 Library, STM32duino VL53L4CD, PicoDVI - Adafruit Fork, Adafruit MMA8451 Library, Adafruit TSC2007, GFX Library for Arduino, Adafruit PyCamera Library, Adafruit ADG72x, Adafruit BNO055, Adafruit SHT4x Library, Adafruit VCNL4200 Library, Adafruit GC9A01A, Adafruit DVI HSTX diff --git a/runcpm-rp2040-dvi-usb/keyboard-copro/keyboard-copro.ino b/runcpm-rp2040-dvi-usb/keyboard-copro/keyboard-copro.ino index 1bfc7feb9..263a842a7 100644 --- a/runcpm-rp2040-dvi-usb/keyboard-copro/keyboard-copro.ino +++ b/runcpm-rp2040-dvi-usb/keyboard-copro/keyboard-copro.ino @@ -6,6 +6,9 @@ #include "pio_usb.h" #include "Adafruit_TinyUSB.h" #include "pico/stdlib.h" +#include "Adafruit_dvhstx.h" + +DVHSTXText3 display(DVHSTX_PINOUT_DEFAULT); // Pin D+ for host, D- = D+ + 1 #ifndef PIN_USB_HOST_DP @@ -28,16 +31,22 @@ Adafruit_USBH_Host USBHost; SerialPIO pio_serial(1 /* RX of the sibling board */, SerialPIO::NOPIN); void setup() { +// ensure text generation interrupt takes place on core0 +display.begin(); +display.println("Hello hstx\n"); } void loop() { } void setup1() { - +while(!display) ; +delay(10); +display.println("Hello hstx in setup1\n"); // override tools menu CPU frequency setting - set_sys_clock_khz(120'000, true); + //set_sys_clock_khz(264'000, true); +Serial.begin(115200); #if 0 while ( !Serial ) delay(10); // wait for native usb Serial.println("Core1 setup to run TinyUSB host with pio-usb"); @@ -50,6 +59,9 @@ void setup1() { 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 @@ -59,6 +71,8 @@ void setup1() { // this `begin` is a void function, no way to check for failure! pio_serial.begin(115200); +display.println("end of setup1\n"); +display.show_cursor(); } int old_ascii = -1; @@ -70,16 +84,21 @@ 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; - if (code > 32 && code < 127) { - Serial.printf("'%c'\r\n", code); + if (code >= 32 && code < 127) { + display.printf("%c", code); } else { - Serial.printf("'\\x%02x'\r\n", code); + display.printf("\\x%02x", code); } pio_serial.write(code); } void loop1() { +static bool last_serial; + if (!last_serial && Serial) { +last_serial = true; +Serial.println("Hello host serial"); + } uint32_t now = millis(); uint32_t deadline = repeat_timeout - now; if (old_ascii >= 0 && deadline > INT32_MAX) { @@ -166,12 +185,12 @@ bool report_contains(const hid_keyboard_report_t &report, uint8_t key) { 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; - bool caps = old_report.reserved & 1; - bool num = old_report.reserved & 2; uint8_t code = 0; if (report.keycode[0] == 1 && report.keycode[1] == 1) { @@ -188,8 +207,10 @@ void process_event(uint8_t dev_addr, uint8_t instance, const hid_keyboard_report /* 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) { @@ -219,15 +240,15 @@ void process_event(uint8_t dev_addr, uint8_t instance, const hid_keyboard_report } } - uint8_t leds = (caps | (num << 1)); - if (leds != old_report.reserved) { + 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*/, &leds, sizeof(leds)); + 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; - old_report.reserved = leds; } // Invoked when received report from device via interrupt endpoint 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..43c85ed73 --- /dev/null +++ b/runcpm-rp2350-hstx-usb/runcpm-pico/hardware/pico/metro_rp2350.h @@ -0,0 +1,343 @@ +// 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 + +DVHSTXText3 display(DVHSTX_PINOUT_DEFAULT); + +// USB Host object +Adafruit_USBH_Host USBHost; + +#define SD_CS_PIN 39 + +SdFat SD; +#if defined(ADAFRUIT_METRO_RP2340) +SdSpiConfig config(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(16), &SPI1); +#else +SdSpiConfig config(SD_CS_PIN, 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); +}