From f1075bd15b78a8909cac91c9ddbe872ab0feada9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Iborra?= Date: Thu, 26 Oct 2023 03:07:49 +0200 Subject: [PATCH 01/30] New HWinfo panel, SD firmware updater --- ESPecpart2.csv | 6 +- include/ESPectrum.h | 2 + include/OSDMain.h | 3 + include/messages.h | 12 +- platformio.ini | 2 +- sdkconfig.ESPectrum | 28 ++-- sdkconfig.ESPectrum.old | 14 +- src/ESPectrum.cpp | 19 +-- src/OSDMain.cpp | 360 ++++++++++++++++++++++++++++------------ src/Video.cpp | 2 +- 10 files changed, 298 insertions(+), 150 deletions(-) diff --git a/ESPecpart2.csv b/ESPecpart2.csv index cda74de8..fff8818b 100644 --- a/ESPecpart2.csv +++ b/ESPecpart2.csv @@ -1,6 +1,6 @@ # Name, Type, SubType, Offset, Size, Flags nvs, data, nvs, 0x9000, 0x5000, otadata, data, ota, 0xe000, 0x2000, -48k, app, ota_0, 0x10000, 0x80000, -128k, app, ota_1, 0x90000, 0x80000, -spiffs, data, spiffs, 0x110000,0x2F0000, +48k, app, ota_0, 0x10000, 0xC0000, +128k, app, ota_1, 0xD0000, 0xC0000, +spiffs, data, spiffs, 0x190000,0x270000, diff --git a/include/ESPectrum.h b/include/ESPectrum.h index 4889d288..fc4ca598 100644 --- a/include/ESPectrum.h +++ b/include/ESPectrum.h @@ -109,6 +109,8 @@ class ESPectrum // static TaskHandle_t loopTaskHandle; + static TaskHandle_t audioTaskHandle; + static bool ps2kbd2; static bool trdos; diff --git a/include/OSDMain.h b/include/OSDMain.h index 0da4e3c6..f3d5052b 100644 --- a/include/OSDMain.h +++ b/include/OSDMain.h @@ -75,6 +75,7 @@ class OSD static void drawOSD(bool bottom_info); static void drawStats(char *line1, char *line2); static void do_OSD(fabgl::VirtualKey KeytoESP); + static void HWInfo(); // // Error static void errorPanel(string errormsg); @@ -115,6 +116,8 @@ class OSD static void esp_hard_reset(); + static esp_err_t updateFirmware(FILE *firmware); + }; #endif // ESPECTRUM_OSD_H diff --git a/include/messages.h b/include/messages.h index d6cceb39..b96c1563 100644 --- a/include/messages.h +++ b/include/messages.h @@ -41,7 +41,7 @@ visit https://zxespectrum.speccy.org/contacto #define MSG_LOADING_Z80 "Loading Z80 file" #define MSG_SAVE_CONFIG "Saving config file" #define MSG_VGA_INIT "Initializing VGA" -#define EMU_VERSION " v1.0rc3 " +#define EMU_VERSION " v1.0pr " // Error #define ERROR_TITLE " !!! ERROR - CLIVE MEDITATION !!! " @@ -78,6 +78,10 @@ static const char *OSD_PAUSE[2] = { OSD_PAUSE_EN,OSD_PAUSE_ES }; #define OSD_TAPE_SELECT_ERR_ES "TAP no seleccionado" static const char *OSD_TAPE_SELECT_ERR[2] = { OSD_TAPE_SELECT_ERR_EN,OSD_TAPE_SELECT_ERR_ES }; +#define OSD_FIRMW_ERR_EN "Problem updating firmware." +#define OSD_FIRMW_ERR_ES "Error actualizando firmware." +static const char *OSD_FIRMW_ERR[2] = { OSD_FIRMW_ERR_EN,OSD_FIRMW_ERR_ES}; + #define MENU_SNA_TITLE_EN "Select Snapshot" #define MENU_SNA_TITLE_ES "Elija snapshot" static const char *MENU_SNA_TITLE[2] = { MENU_SNA_TITLE_EN,MENU_SNA_TITLE_ES }; @@ -163,7 +167,8 @@ static const char *MENU_MAIN[2] = { MENU_MAIN_EN,MENU_MAIN_ES }; "Aspect ratio\t>\n"\ "PS/2 Joystick\t>\n"\ "Language\t>\n"\ - "Other\t>\n" + "Other\t>\n"\ + "Firmware update\n" #define MENU_OPTIONS_ES \ "Menu opciones\n"\ "Almacenamiento\t>\n"\ @@ -171,7 +176,8 @@ static const char *MENU_MAIN[2] = { MENU_MAIN_EN,MENU_MAIN_ES }; "Rel. aspecto\t>\n"\ "Joystick PS/2\t>\n"\ "Idioma\t>\n"\ - "Otros\t>\n" + "Otros\t>\n"\ + "Actualizar firmware\n" static const char *MENU_OPTIONS[2] = { MENU_OPTIONS_EN,MENU_OPTIONS_ES }; #define MENU_ASPECT_EN \ diff --git a/platformio.ini b/platformio.ini index 6bb000bf..2a0a51ee 100644 --- a/platformio.ini +++ b/platformio.ini @@ -22,7 +22,7 @@ monitor_speed = 115200 monitor_filters = direct esp32_exception_decoder -board_build.partitions = ESPecpart.csv +board_build.partitions = ESPecpart2.csv extra_scripts = download_fs.py build_flags = -w diff --git a/sdkconfig.ESPectrum b/sdkconfig.ESPectrum index 8105053b..2f6de82d 100644 --- a/sdkconfig.ESPectrum +++ b/sdkconfig.ESPectrum @@ -43,13 +43,13 @@ CONFIG_BOOTLOADER_OFFSET_IN_FLASH=0x1000 # CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_DEBUG is not set CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_PERF=y # CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_NONE is not set -CONFIG_BOOTLOADER_LOG_LEVEL_NONE=y +# CONFIG_BOOTLOADER_LOG_LEVEL_NONE is not set # CONFIG_BOOTLOADER_LOG_LEVEL_ERROR is not set # CONFIG_BOOTLOADER_LOG_LEVEL_WARN is not set -# CONFIG_BOOTLOADER_LOG_LEVEL_INFO is not set +CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y # CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG is not set # CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE is not set -CONFIG_BOOTLOADER_LOG_LEVEL=0 +CONFIG_BOOTLOADER_LOG_LEVEL=3 # CONFIG_BOOTLOADER_SPI_CUSTOM_WP_PIN is not set CONFIG_BOOTLOADER_SPI_WP_PIN=7 CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V=y @@ -340,11 +340,13 @@ CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192 CONFIG_ESP_IPC_TASK_STACK_SIZE=1024 CONFIG_ESP_IPC_USES_CALLERS_PRIORITY=y CONFIG_ESP_MINIMAL_SHARED_STACK_SIZE=2048 -# CONFIG_ESP_CONSOLE_UART_DEFAULT is not set +CONFIG_ESP_CONSOLE_UART_DEFAULT=y # CONFIG_ESP_CONSOLE_UART_CUSTOM is not set -CONFIG_ESP_CONSOLE_NONE=y +# CONFIG_ESP_CONSOLE_NONE is not set +CONFIG_ESP_CONSOLE_UART=y CONFIG_ESP_CONSOLE_MULTIPLE_UART=y -CONFIG_ESP_CONSOLE_UART_NUM=-1 +CONFIG_ESP_CONSOLE_UART_NUM=0 +CONFIG_ESP_CONSOLE_UART_BAUDRATE=115200 # CONFIG_ESP_INT_WDT is not set # CONFIG_ESP_TASK_WDT is not set # CONFIG_ESP_PANIC_HANDLER_IRAM is not set @@ -1105,13 +1107,13 @@ CONFIG_CU_DIAGNOSTICS_COLOR_ALWAYS=y # Deprecated options for backward compatibility CONFIG_TOOLPREFIX="xtensa-esp32-elf-" -CONFIG_LOG_BOOTLOADER_LEVEL_NONE=y +# CONFIG_LOG_BOOTLOADER_LEVEL_NONE is not set # CONFIG_LOG_BOOTLOADER_LEVEL_ERROR is not set # CONFIG_LOG_BOOTLOADER_LEVEL_WARN is not set -# CONFIG_LOG_BOOTLOADER_LEVEL_INFO is not set +CONFIG_LOG_BOOTLOADER_LEVEL_INFO=y # CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG is not set # CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE is not set -CONFIG_LOG_BOOTLOADER_LEVEL=0 +CONFIG_LOG_BOOTLOADER_LEVEL=3 # CONFIG_APP_ROLLBACK_ENABLE is not set # CONFIG_FLASH_ENCRYPTION_ENABLED is not set CONFIG_FLASHMODE_QIO=y @@ -1176,10 +1178,12 @@ CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32 CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2304 CONFIG_MAIN_TASK_STACK_SIZE=8192 CONFIG_IPC_TASK_STACK_SIZE=1024 -# CONFIG_CONSOLE_UART_DEFAULT is not set +CONFIG_CONSOLE_UART_DEFAULT=y # CONFIG_CONSOLE_UART_CUSTOM is not set -CONFIG_ESP_CONSOLE_UART_NONE=y -CONFIG_CONSOLE_UART_NUM=-1 +# CONFIG_ESP_CONSOLE_UART_NONE is not set +CONFIG_CONSOLE_UART=y +CONFIG_CONSOLE_UART_NUM=0 +CONFIG_CONSOLE_UART_BAUDRATE=115200 # CONFIG_INT_WDT is not set # CONFIG_TASK_WDT is not set # CONFIG_EVENT_LOOP_PROFILING is not set diff --git a/sdkconfig.ESPectrum.old b/sdkconfig.ESPectrum.old index 9108c7e6..205c466b 100644 --- a/sdkconfig.ESPectrum.old +++ b/sdkconfig.ESPectrum.old @@ -43,13 +43,13 @@ CONFIG_BOOTLOADER_OFFSET_IN_FLASH=0x1000 # CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_DEBUG is not set CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_PERF=y # CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_NONE is not set -CONFIG_BOOTLOADER_LOG_LEVEL_NONE=y +# CONFIG_BOOTLOADER_LOG_LEVEL_NONE is not set # CONFIG_BOOTLOADER_LOG_LEVEL_ERROR is not set # CONFIG_BOOTLOADER_LOG_LEVEL_WARN is not set -# CONFIG_BOOTLOADER_LOG_LEVEL_INFO is not set +CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y # CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG is not set # CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE is not set -CONFIG_BOOTLOADER_LOG_LEVEL=0 +CONFIG_BOOTLOADER_LOG_LEVEL=3 # CONFIG_BOOTLOADER_SPI_CUSTOM_WP_PIN is not set CONFIG_BOOTLOADER_SPI_WP_PIN=7 CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V=y @@ -340,13 +340,11 @@ CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192 CONFIG_ESP_IPC_TASK_STACK_SIZE=1024 CONFIG_ESP_IPC_USES_CALLERS_PRIORITY=y CONFIG_ESP_MINIMAL_SHARED_STACK_SIZE=2048 -CONFIG_ESP_CONSOLE_UART_DEFAULT=y +# CONFIG_ESP_CONSOLE_UART_DEFAULT is not set # CONFIG_ESP_CONSOLE_UART_CUSTOM is not set -# CONFIG_ESP_CONSOLE_NONE is not set -CONFIG_ESP_CONSOLE_UART=y +CONFIG_ESP_CONSOLE_NONE=y CONFIG_ESP_CONSOLE_MULTIPLE_UART=y -CONFIG_ESP_CONSOLE_UART_NUM=0 -CONFIG_ESP_CONSOLE_UART_BAUDRATE=115200 +CONFIG_ESP_CONSOLE_UART_NUM=-1 # CONFIG_ESP_INT_WDT is not set # CONFIG_ESP_TASK_WDT is not set # CONFIG_ESP_PANIC_HANDLER_IRAM is not set diff --git a/src/ESPectrum.cpp b/src/ESPectrum.cpp index 3fce48f0..b193fecd 100644 --- a/src/ESPectrum.cpp +++ b/src/ESPectrum.cpp @@ -95,7 +95,7 @@ int ESPectrum::TapeNameScroller = 0; // bool ESPectrum::Audio_restart = false; QueueHandle_t audioTaskQueue; -TaskHandle_t audioTaskHandle; +TaskHandle_t ESPectrum::audioTaskHandle; uint8_t *param; //======================================================================================= @@ -485,9 +485,6 @@ void ESPectrum::setup() // Create Audio task audioTaskQueue = xQueueCreate(1, sizeof(uint8_t *)); // Latest parameter = Core. In ESPIF, main task runs on core 0 by default. In Arduino, loop() runs on core 1. - - // xTaskCreatePinnedToCore(&ESPectrum::audioTask, "audioTask", 2048, NULL, configMAX_PRIORITIES - 1, &audioTaskHandle, 1); - // xTaskCreatePinnedToCore(&ESPectrum::audioTask, "audioTask", 1536, NULL, configMAX_PRIORITIES - 1, &audioTaskHandle, 1); xTaskCreatePinnedToCore(&ESPectrum::audioTask, "audioTask", 1024, NULL, configMAX_PRIORITIES - 1, &audioTaskHandle, 1); // AY Sound @@ -653,13 +650,8 @@ bool IRAM_ATTR ESPectrum::readKbd(fabgl::VirtualKeyItem *Nextkey) { if(ps2kbd2) PS2Controller.keybjoystick()->setLEDs(false, false, Config::CursorAsJoy); Config::save("CursorAsJoy"); - } - #ifdef RAM_INFO_KEY - else if (Nextkey->vk == fabgl::VK_GRAVEACCENT) { // Show mem info - showMemInfo(); r = false; } - #endif } return r; @@ -713,7 +705,7 @@ void IRAM_ATTR ESPectrum::processKeyboard() { KeytoESP = NextKey.vk; Kdown = NextKey.down; - if ((Kdown) && (((KeytoESP >= fabgl::VK_F1) && (KeytoESP <= fabgl::VK_F12)) || (KeytoESP == fabgl::VK_PAUSE))) { + if ((Kdown) && (((KeytoESP >= fabgl::VK_F1) && (KeytoESP <= fabgl::VK_F12)) || (KeytoESP == fabgl::VK_PAUSE) || (KeytoESP == fabgl::VK_GRAVEACCENT ))) { OSD::do_OSD(KeytoESP); return; } @@ -1002,10 +994,13 @@ void IRAM_ATTR ESPectrum::processKeyboard() { if (!bitRead(ZXKeyb::ZXcols[2],1)) { OSD::do_OSD(fabgl::VK_F12); } else - if (!bitRead(ZXKeyb::ZXcols[5],0)) { + if (!bitRead(ZXKeyb::ZXcols[5],0)) { // P -> Pause OSD::do_OSD(fabgl::VK_PAUSE); } else - if (!bitRead(ZXKeyb::ZXcols[1],1)) { + if (!bitRead(ZXKeyb::ZXcols[5],2)) { // I -> Info + OSD::do_OSD(fabgl::VK_GRAVEACCENT); + } else + if (!bitRead(ZXKeyb::ZXcols[1],1)) { // S -> Screen capture CaptureToBmp(); } else zxDelay = 0; diff --git a/src/OSDMain.cpp b/src/OSDMain.cpp index 8c83e6e3..aefe93fa 100644 --- a/src/OSDMain.cpp +++ b/src/OSDMain.cpp @@ -50,6 +50,9 @@ visit https://zxespectrum.speccy.org/contacto #ifndef ESP32_SDL2_WRAPPER #include "esp_system.h" #include "esp_ota_ops.h" +#include "esp_efuse.h" +#include "soc/efuse_reg.h" + #include "fabgl.h" #include "soc/rtc_wdt.h" @@ -279,26 +282,12 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP) { ZXKeyb::process(); - if ((!bitRead(ZXKeyb::ZXcols[6], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 0))) { // ENTER + if ((!bitRead(ZXKeyb::ZXcols[6], 0)) || (!bitRead(ZXKeyb::ZXcols[7], 0)) || (!bitRead(ZXKeyb::ZXcols[5], 0))) { // ENTER, BREAK, P if (zxDelay == 0) { ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAUSE, true, false); ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAUSE, false, false); zxDelay = REPDEL; } - } else - if ((!bitRead(ZXKeyb::ZXcols[7], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 1))) { // BREAK - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAUSE, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAUSE, false, false); - zxDelay = REPDEL; - } - } else - if (!bitRead(ZXKeyb::ZXcols[5], 0)) { // P (Pause) - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAUSE, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAUSE, false, false); - zxDelay = REPDEL; - } } } @@ -322,6 +311,11 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP) { } + } + else if (KeytoESP == fabgl::VK_GRAVEACCENT) { // Show mem info + + OSD::HWInfo(); + } else if (KeytoESP == fabgl::VK_F2) { menu_level = 0; @@ -444,57 +438,18 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP) { click(); } else if (KeytoESP == fabgl::VK_F9) { // Volume down - - // #ifdef TESTING_CODE - - // ESPectrum::target--; - - // // // Check if destination file exists - // // struct stat st; - // // if (stat("/sd/s/1942.z80", &st) == 0) { - // // //printf("Exists!\n"); - // // } - - // // FILE *f = fopen("/sd/s/1942.z80", "r"); - // // if (f == NULL) { - // // printf("Null file!\n"); - // // } else fclose(f); - - // #else - if (ESPectrum::aud_volume>-16) { click(); ESPectrum::aud_volume--; pwm_audio_set_volume(ESPectrum::aud_volume); } - - // #endif - - // VIDEO::tStatesScreen += 224; - // VIDEO::tStatesScreen += 1; - // printf("%d\n",VIDEO::tStatesScreen); - } else if (KeytoESP == fabgl::VK_F10) { // Volume up - - // #ifdef TESTING_CODE - - // ESPectrum::target++; - - // #else - if (ESPectrum::aud_volume<0) { click(); ESPectrum::aud_volume++; pwm_audio_set_volume(ESPectrum::aud_volume); } - - // VIDEO::tStatesScreen -= 224; - // VIDEO::tStatesScreen -= 1; - // printf("%d\n",VIDEO::tStatesScreen); - - // #endif - } // else if (KeytoESP == fabgl::VK_F9) { // ESPectrum::ESPoffset -= 5; @@ -503,7 +458,6 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP) { // ESPectrum::ESPoffset += 5; // } else if (KeytoESP == fabgl::VK_F11) { // Hard reset - // Hard if (Config::ram_file != NO_RAM_FILE) { Config::ram_file = NO_RAM_FILE; #ifdef SNAPSHOT_LOAD_LAST @@ -514,38 +468,13 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP) { ESPectrum::reset(); } else if (KeytoESP == fabgl::VK_F12) { // ESP32 reset - - // // Switch boot partition - // string splabel; - // esp_err_t chg_ota; - // const esp_partition_t *ESPectrum_partition = NULL; - - // ESPectrum_partition = esp_ota_get_running_partition(); - // if (ESPectrum_partition->label=="128k") splabel = "48k"; else splabel= "128k"; - - // ESPectrum_partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP,ESP_PARTITION_SUBTYPE_ANY,splabel.c_str()); - // chg_ota = esp_ota_set_boot_partition(ESPectrum_partition); - // ESP host reset #ifndef SNAPSHOT_LOAD_LAST Config::ram_file = NO_RAM_FILE; Config::save("ram"); #endif esp_hard_reset(); - } - // else if (KeytoESP == fabgl::VK_F12) { - // // Switch boot partition - // esp_err_t chg_ota; - // const esp_partition_t *ESPectrum_partition = NULL; - - // //ESPectrum_partition = esp_ota_get_next_update_partition(NULL); - // ESPectrum_partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP,ESP_PARTITION_SUBTYPE_ANY,"128k"); - // chg_ota = esp_ota_set_boot_partition(ESPectrum_partition); - - // if (chg_ota == ESP_OK ) esp_hard_reset(); - - // } else if (KeytoESP == fabgl::VK_F1) { menu_curopt = 1; @@ -1179,6 +1108,20 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP) { break; } } + } else if (options_num == 7) { + // Open firmware file + FILE *firmware = fopen("/sd/firmware.bin", "rb"); + if (firmware == NULL) { + osdCenteredMsg("No firmware file found.", LEVEL_WARN, 2000); + return; + } else { + esp_err_t res = updateFirmware(firmware); + fclose(firmware); + string errMsg = OSD_FIRMW_ERR[Config::lang]; + errMsg += " Code = " + to_string(res); + osdCenteredMsg(errMsg, LEVEL_ERROR, 3000); + } + return; } else { menu_curopt = 5; break; @@ -1203,26 +1146,12 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP) { ZXKeyb::process(); - if ((!bitRead(ZXKeyb::ZXcols[6], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 0))) { // ENTER + if ((!bitRead(ZXKeyb::ZXcols[6], 0)) || (!bitRead(ZXKeyb::ZXcols[7], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 1))) { // ENTER, BREAK, 0, 9 if (zxDelay == 0) { ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, true, false); ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, false, false); zxDelay = REPDEL; } - } else - if ((!bitRead(ZXKeyb::ZXcols[7], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 1))) { // BREAK - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_ESCAPE, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_ESCAPE, false, false); - zxDelay = REPDEL; - } - } else - if (!bitRead(ZXKeyb::ZXcols[1], 1)) { // S (Capture screen) - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PRINTSCREEN, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PRINTSCREEN, false, false); - zxDelay = REPDEL; - } } } @@ -1338,26 +1267,12 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP) { ZXKeyb::process(); - if ((!bitRead(ZXKeyb::ZXcols[6], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 0))) { // ENTER + if ((!bitRead(ZXKeyb::ZXcols[6], 0)) || (!bitRead(ZXKeyb::ZXcols[7], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 1))) { // ENTER, BREAK, 0, 9 if (zxDelay == 0) { ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, true, false); ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, false, false); zxDelay = REPABOUT; } - } else - if ((!bitRead(ZXKeyb::ZXcols[7], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 1))) { // BREAK - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_ESCAPE, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_ESCAPE, false, false); - zxDelay = REPABOUT; - } - } else - if (!bitRead(ZXKeyb::ZXcols[1], 1)) { // S (Capture screen) - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PRINTSCREEN, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PRINTSCREEN, false, false); - zxDelay = REPABOUT; - } } } @@ -1512,3 +1427,228 @@ string OSD::rowGet(string menu, unsigned short row) { } return ""; } + +void OSD::HWInfo() { + + fabgl::VirtualKeyItem Nextkey; + + click(); + + // Draw Hardware and memory info + drawOSD(true); + osdAt(2, 0); + + VIDEO::vga.setTextColor(OSD::zxColor(7, 0), OSD::zxColor(1, 0)); + + // Get chip information + esp_chip_info_t chip_info; + esp_chip_info(&chip_info); + + VIDEO::vga.print(" Hardware info\n"); + VIDEO::vga.print(" --------------------------------------\n"); + + // string chipmodel[6]={"","ESP32","ESP32-S2","","ESP32-S3","ESP32-C3"}; + // string textout = " Chip model : " + chipmodel[chip_info.model] + "\n"; + // VIDEO::vga.print(textout.c_str()); + + // Chip models for ESP32 + string textout = " Chip model : "; + uint32_t chip_ver = esp_efuse_get_pkg_ver(); + uint32_t pkg_ver = chip_ver & 0x7; + switch (pkg_ver) { + case EFUSE_RD_CHIP_VER_PKG_ESP32D0WDQ6 : + if (chip_info.revision == 3) + textout += "ESP32-D0WDQ6-V3"; + else + textout += "ESP32-D0WDQ6"; + break; + case EFUSE_RD_CHIP_VER_PKG_ESP32D0WDQ5 : + if (chip_info.revision == 3) + textout += "ESP32-D0WD-V3"; + else + textout += "ESP32-D0WD"; + break; + case EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5 : + textout += "ESP32-D2WD"; + break; + case EFUSE_RD_CHIP_VER_PKG_ESP32PICOD2 : + textout += "ESP32-PICO-D2"; + break; + case EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4 : + textout += "ESP32-PICO-D4"; + break; + case EFUSE_RD_CHIP_VER_PKG_ESP32PICOV302 : + textout += "ESP32-PICO-V3-02"; + break; + case /*EFUSE_RD_CHIP_VER_PKG_ESP32D0WDR2V3*/ 7 : // Not defined in ESP-IDF version we're using + textout += "ESP32-D0WDR2-V3"; + break; + default: + textout += "Unknown"; + } + textout += "\n"; + VIDEO::vga.print(textout.c_str()); + + textout = " Chip cores : " + to_string(chip_info.cores) + "\n"; + VIDEO::vga.print(textout.c_str()); + + textout = " Chip revision : " + to_string(chip_info.revision) + "\n"; + VIDEO::vga.print(textout.c_str()); + + textout = " Flash size : " + to_string(spi_flash_get_chip_size() / (1024 * 1024)) + (chip_info.features & CHIP_FEATURE_EMB_FLASH ? "MB embedded" : "MB external") + "\n"; + VIDEO::vga.print(textout.c_str()); + + multi_heap_info_t info; + heap_caps_get_info(&info, MALLOC_CAP_SPIRAM); + uint32_t psramsize = info.total_free_bytes + info.total_allocated_bytes; + // textout = " PSRAM size : " + to_string(info.total_free_bytes + info.total_allocated_bytes) + "\n"; + textout = " PSRAM size : " + ( psramsize == 0 ? "N/A or disabled" : to_string(psramsize) + " MB") + "\n"; + VIDEO::vga.print(textout.c_str()); + + textout = " IDF Version : " + (string)(esp_get_idf_version()) + "\n"; + VIDEO::vga.print(textout.c_str()); + + VIDEO::vga.print("\n Memory info\n"); + VIDEO::vga.print(" --------------------------------------\n"); + + heap_caps_get_info(&info, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); // internal RAM, memory capable to store data or to create new task + textout = " Total free bytes : " + to_string(info.total_free_bytes) + "\n"; + VIDEO::vga.print(textout.c_str()); + + textout = " Minimum free ever : " + to_string(info.minimum_free_bytes) + "\n"; + VIDEO::vga.print(textout.c_str()); + + textout = " Largest free block : " + to_string(info.largest_free_block) + "\n"; + VIDEO::vga.print(textout.c_str()); + + textout = " Free (MALLOC_CAP_32BIT) : " + to_string(heap_caps_get_free_size(MALLOC_CAP_32BIT)) + "\n"; + VIDEO::vga.print(textout.c_str()); + + UBaseType_t wm; + wm = uxTaskGetStackHighWaterMark(ESPectrum::audioTaskHandle); + textout = " Audio Task Stack HWM : " + to_string(wm) + "\n"; + VIDEO::vga.print(textout.c_str()); + + // wm = uxTaskGetStackHighWaterMark(loopTaskHandle); + // printf("Loop Task Stack HWM: %u\n", wm); + + wm = uxTaskGetStackHighWaterMark(VIDEO::videoTaskHandle); + textout = " Video Task Stack HWM : " + (Config::videomode ? to_string(wm) : "N/A") + "\n"; + VIDEO::vga.print(textout.c_str()); + + // Wait for key + + zxDelay = REPDEL; + + while (1) { + + if (ZXKeyb::Exists) { + + ZXKeyb::process(); + + if ((!bitRead(ZXKeyb::ZXcols[6], 0)) || (!bitRead(ZXKeyb::ZXcols[7], 0)) || (!bitRead(ZXKeyb::ZXcols[5], 2))) { // ENTER, BREAK or I + if (zxDelay == 0) { + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, true, false); + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, false, false); + zxDelay = REPDEL; + } + } + + } + + ESPectrum::readKbdJoy(); + + if (ESPectrum::PS2Controller.keyboard()->virtualKeyAvailable()) { + ESPectrum::PS2Controller.keyboard()->getNextVirtualKey(&Nextkey); + if(!Nextkey.down) continue; + if (Nextkey.vk == fabgl::VK_RETURN || Nextkey.vk == fabgl::VK_ESCAPE || Nextkey.vk == fabgl::VK_GRAVEACCENT) { + click(); + break; + } + } + + vTaskDelay(5 / portTICK_PERIOD_MS); + + if (zxDelay > 0) zxDelay--; + + } + +} + +// // Switch boot partition +// string splabel; +// esp_err_t chg_ota; +// const esp_partition_t *ESPectrum_partition = NULL; + +// ESPectrum_partition = esp_ota_get_running_partition(); +// if (ESPectrum_partition->label=="128k") splabel = "48k"; else splabel= "128k"; + +// ESPectrum_partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP,ESP_PARTITION_SUBTYPE_ANY,splabel.c_str()); +// chg_ota = esp_ota_set_boot_partition(ESPectrum_partition); +// if (chg_ota == ESP_OK ) esp_hard_reset(); + +#define FWBUFFSIZE 4096 + +esp_err_t OSD::updateFirmware(FILE *firmware) { + +char ota_write_data[FWBUFFSIZE + 1] = { 0 }; + +// get the currently running partition +const esp_partition_t *partition = esp_ota_get_running_partition(); +if (partition == NULL) { + return ESP_ERR_NOT_FOUND; +} + +// Grab next update target +const esp_partition_t *target = esp_ota_get_next_update_partition(NULL); +if (target == NULL) { + return ESP_ERR_NOT_FOUND; +} + +// printf("Running partition type %d subtype %d at offset 0x%x.\n", partition->type, partition->subtype, partition->address); +// printf("Target partition type %d subtype %d at offset 0x%x.\n", target->type, target->subtype, target->address); + +osdCenteredMsg("Erasing destination partition.", LEVEL_INFO,0); + +esp_ota_handle_t ota_handle; +esp_err_t result = esp_ota_begin(target, OTA_SIZE_UNKNOWN, &ota_handle); +if (result != ESP_OK) { + return result; +} + +size_t bytesread; +uint32_t byteswritten = 0; + +osdCenteredMsg(" Flashing new firmware. ", LEVEL_INFO,0); + +while (1) { + bytesread = fread(ota_write_data, 1, 0x1000 , firmware); + result = esp_ota_write(ota_handle,(const void *) ota_write_data, bytesread); + if (result != ESP_OK) { + return result; + } + byteswritten += bytesread; + // printf("Bytes written: %d\n",byteswritten); + if (feof(firmware)) break; +} + +result = esp_ota_end(ota_handle); +if (result != ESP_OK) +{ + // printf("esp_ota_end failed, err=0x%x.\n", result); + return result; +} + +result = esp_ota_set_boot_partition(target); +if (result != ESP_OK) { + // printf("esp_ota_set_boot_partition failed, err=0x%x.\n", result); + return result; +} + +osdCenteredMsg("Flashing complete. Rebooting.", LEVEL_INFO, 0); +delay(1000); + +// Firmware written: reboot +OSD::esp_hard_reset(); + +} \ No newline at end of file diff --git a/src/Video.cpp b/src/Video.cpp index a9145d23..7d74fc71 100644 --- a/src/Video.cpp +++ b/src/Video.cpp @@ -219,7 +219,7 @@ TaskHandle_t VIDEO::videoTaskHandle; void VIDEO::Init() { if (Config::videomode) { - xTaskCreatePinnedToCore(&VIDEO::vgataskinit, "videoTask", 1536, NULL, configMAX_PRIORITIES - 2, &videoTaskHandle, 1); + xTaskCreatePinnedToCore(&VIDEO::vgataskinit, "videoTask", 1024, NULL, configMAX_PRIORITIES - 2, &videoTaskHandle, 1); // Wait for vertical sync to ensure vga.init is done for (;;) { if (ESPectrum::vsync) break; From f87c1feb37bf0d3f084f1d25536e4da006703bb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Iborra?= Date: Thu, 26 Oct 2023 03:21:13 +0200 Subject: [PATCH 02/30] Flash messages localization --- include/messages.h | 12 ++++++++++++ src/OSDMain.cpp | 6 +++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/include/messages.h b/include/messages.h index b96c1563..e97dc54b 100644 --- a/include/messages.h +++ b/include/messages.h @@ -78,6 +78,18 @@ static const char *OSD_PAUSE[2] = { OSD_PAUSE_EN,OSD_PAUSE_ES }; #define OSD_TAPE_SELECT_ERR_ES "TAP no seleccionado" static const char *OSD_TAPE_SELECT_ERR[2] = { OSD_TAPE_SELECT_ERR_EN,OSD_TAPE_SELECT_ERR_ES }; +#define OSD_FIRMW_BEGIN_EN "Erasing destination partition." +#define OSD_FIRMW_BEGIN_ES "Borrando particion de destino." +static const char *OSD_FIRMW_BEGIN[2] = { OSD_FIRMW_BEGIN_EN,OSD_FIRMW_BEGIN_ES}; + +#define OSD_FIRMW_WRITE_EN " Flashing new firmware. " +#define OSD_FIRMW_WRITE_ES " Grabando nuevo firmware. " +static const char *OSD_FIRMW_WRITE[2] = { OSD_FIRMW_WRITE_EN,OSD_FIRMW_WRITE_ES}; + +#define OSD_FIRMW_END_EN "Flashing complete. Rebooting." +#define OSD_FIRMW_END_ES " Completado. Reiniciando. " +static const char *OSD_FIRMW_END[2] = { OSD_FIRMW_END_EN,OSD_FIRMW_END_ES}; + #define OSD_FIRMW_ERR_EN "Problem updating firmware." #define OSD_FIRMW_ERR_ES "Error actualizando firmware." static const char *OSD_FIRMW_ERR[2] = { OSD_FIRMW_ERR_EN,OSD_FIRMW_ERR_ES}; diff --git a/src/OSDMain.cpp b/src/OSDMain.cpp index aefe93fa..7d09e9d2 100644 --- a/src/OSDMain.cpp +++ b/src/OSDMain.cpp @@ -1608,7 +1608,7 @@ if (target == NULL) { // printf("Running partition type %d subtype %d at offset 0x%x.\n", partition->type, partition->subtype, partition->address); // printf("Target partition type %d subtype %d at offset 0x%x.\n", target->type, target->subtype, target->address); -osdCenteredMsg("Erasing destination partition.", LEVEL_INFO,0); +osdCenteredMsg(OSD_FIRMW_BEGIN[Config::lang], LEVEL_INFO,0); esp_ota_handle_t ota_handle; esp_err_t result = esp_ota_begin(target, OTA_SIZE_UNKNOWN, &ota_handle); @@ -1619,7 +1619,7 @@ if (result != ESP_OK) { size_t bytesread; uint32_t byteswritten = 0; -osdCenteredMsg(" Flashing new firmware. ", LEVEL_INFO,0); +osdCenteredMsg(OSD_FIRMW_WRITE[Config::lang], LEVEL_INFO,0); while (1) { bytesread = fread(ota_write_data, 1, 0x1000 , firmware); @@ -1645,7 +1645,7 @@ if (result != ESP_OK) { return result; } -osdCenteredMsg("Flashing complete. Rebooting.", LEVEL_INFO, 0); +osdCenteredMsg(OSD_FIRMW_END[Config::lang], LEVEL_INFO, 0); delay(1000); // Firmware written: reboot From a1913c071a7338fcc09c1bd6e660b89b031e60bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Iborra?= Date: Wed, 15 Nov 2023 13:35:03 +0100 Subject: [PATCH 03/30] 1st version of folders, CRT centering and more --- include/CPU.h | 3 - include/Config.h | 11 + include/FileUtils.h | 28 +- include/OSDMain.h | 14 +- include/Video.h | 24 +- include/ZXKeyb.h | 17 +- include/hardpins.h | 3 +- include/messages.h | 8 +- sdkconfig.ESPectrum | 2 +- sdkconfig.ESPectrum.old | 10 +- src/CPU.cpp | 5 +- src/Config.cpp | 111 ++ src/ESP32Lib/Graphics/Font.h | 2 +- src/ESP32Lib/Graphics/Graphics.h | 6 +- src/ESP32Lib/VGA/VGA.cpp | 143 +-- src/ESP32Lib/VGA/VGA.h | 15 +- src/ESPectrum.cpp | 77 +- src/FileUtils.cpp | 186 +-- src/Font.cpp | 461 ++++++++ src/OSDMain.cpp | 1804 +++++++++++++++--------------- src/OSDMenu.cpp | 1042 +++++++++-------- src/Snapshot.cpp | 55 + src/Tape.cpp | 11 +- src/Video.cpp | 51 +- src/ZXKeyb.cpp | 128 +-- src/wd1793.cpp | 2 + 26 files changed, 2554 insertions(+), 1665 deletions(-) diff --git a/include/CPU.h b/include/CPU.h index ed5b0425..0c39acfd 100644 --- a/include/CPU.h +++ b/include/CPU.h @@ -83,9 +83,6 @@ class CPU static uint8_t IntStart; static uint8_t IntEnd; - // Frames elapsed - static uint32_t framecnt; - }; static const unsigned char DRAM_ATTR wait_st[243] = { diff --git a/include/Config.h b/include/Config.h index 764ffa30..4c70ff89 100644 --- a/include/Config.h +++ b/include/Config.h @@ -70,6 +70,17 @@ class Config static uint8_t AluTiming; static uint8_t ps2_dev2; static bool CursorAsJoy; + static int8_t CenterH; + static int8_t CenterV; + static string SNA_Path; + static string TAP_Path; + static string DSK_Path; + static uint16_t SNA_begin_row; + static uint16_t SNA_focus; + static uint16_t TAP_begin_row; + static uint16_t TAP_focus; + static uint16_t DSK_begin_row; + static uint16_t DSK_focus; // config persistence static void load(); diff --git a/include/FileUtils.h b/include/FileUtils.h index ae758e52..fc2c87a1 100644 --- a/include/FileUtils.h +++ b/include/FileUtils.h @@ -48,6 +48,17 @@ using namespace std; // Defines #define ASCII_NL 10 +#define DISK_SNAFILE 0 +#define DISK_TAPFILE 1 +#define DISK_DSKFILE 2 + +struct DISK_FTYPE { + string fileExts; + string indexFilename; + int begin_row; + int focus; +}; + class FileUtils { public: @@ -59,8 +70,8 @@ class FileUtils // static void sanitizeFilename(String filename); // in-place // static File safeOpenFileRead(String filename); // static string getFileEntriesFromDir(string path); - static int DirToFile(string Dir, string fileExts); - static void Mergefiles(string fpath, int chunk_cnt); + static void DirToFile(string Dir, uint8_t ftype /*string fileExts*/); + static void Mergefiles(string fpath, uint8_t ftype, int chunk_cnt); // static uint16_t countFileEntriesFromDir(String path); // static string getSortedFileList(string fileDir); static bool hasSNAextension(string filename); @@ -71,11 +82,8 @@ class FileUtils static string SNA_Path; // Current SNA path on the SD (for future folder support) static string TAP_Path; // Current TAP path on the SD (for future folder support) - static string DSK_Path; // Current DSK path on the SD (for future folder support) - - static int curSNAFile; // Current SNA file index on browser - static int curTAPFile; // Current TAP file index on browser - static int curDSKFile; // Current DSK file index on browser + static string DSK_Path; // Current DSK path on the SD (for future folder support) + static DISK_FTYPE fileTypes[3]; private: friend class Config; @@ -100,8 +108,8 @@ class FileUtils #define DISK_SNA_DIR "/s" #define DISK_TAP_DIR "/t" #define DISK_DSK_DIR "/d" -#define DISK_SCR_DIR "/c" -#define DISK_PSNA_DIR "/p" +#define DISK_SCR_DIR "/.c" +#define DISK_PSNA_DIR "/.p" #define DISK_PSNA_FILE "persist" #define NO_RAM_FILE "none" @@ -110,7 +118,7 @@ class FileUtils #define SNA_128K_SIZE1 131103 #define SNA_128K_SIZE2 147487 -#define MAX_FNAMES_PER_CHUNK 256 +#define MAX_FNAMES_PER_CHUNK 128 // inline utility functions for uniform access to file/memory // and making it easy to to implement SNA/Z80 functions diff --git a/include/OSDMain.h b/include/OSDMain.h index f3d5052b..17c30bfb 100644 --- a/include/OSDMain.h +++ b/include/OSDMain.h @@ -74,7 +74,7 @@ class OSD static void osdAt(uint8_t row, uint8_t col); static void drawOSD(bool bottom_info); static void drawStats(char *line1, char *line2); - static void do_OSD(fabgl::VirtualKey KeytoESP); + static void do_OSD(fabgl::VirtualKey KeytoESP, uint8_t SHIFT); static void HWInfo(); // // Error @@ -84,22 +84,18 @@ class OSD static void osdCenteredMsg(string msg, uint8_t warn_level, uint16_t millispause); // // Menu - // static void newMenu(string new_menu); - // static void menuRecalc(); static unsigned short menuRealRowFor(uint8_t virtual_row_num); static bool menuIsSub(uint8_t virtual_row_num); static void menuPrintRow(uint8_t virtual_row_num, uint8_t line_type); - // static void menuDraw(); static void menuRedraw(); - // static string getArchMenu(); - // static string getRomsetMenu(string arch); static void WindowDraw(); static unsigned short menuRun(string new_menu); - static string menuFile(string new_menu, string title, string extensions, int currentFile); + static string menuFile(string &fdir, string title, uint8_t ftype, uint8_t mfcols, uint8_t mfrows); static int menuTape(string title); static void menuScroll(bool up); - // static void filemenuDraw(); - static void filemenuRedraw(string title); + static void filemenuRedraw(string title, uint8_t ftype); + static void filemenuPrintRow(uint8_t virtual_row_num, uint8_t line_type); + static void filemenuScrollBar(uint8_t ftype); static void tapemenuRedraw(string title); static void PrintRow(uint8_t virtual_row_num, uint8_t line_type); static void menuAt(short int row, short int col); diff --git a/include/Video.h b/include/Video.h index 9b251f7b..1b59fb55 100644 --- a/include/Video.h +++ b/include/Video.h @@ -72,20 +72,21 @@ class VIDEO /////////////////////////////////////////////////////////////////////////////////////////////////////// // Common static void NoVideo(unsigned int statestoadd, bool contended); + static void IRAM_ATTR EndFrame(); static void IRAM_ATTR Blank(unsigned int statestoadd, bool contended); - static void Flush(); // For flushing video buffer as fast as possible after HALT + static void IRAM_ATTR Flush(); // For flushing video buffer as fast as possible after HALT // 48 / 128 - static void TopBorder_Blank(unsigned int statestoadd, bool contended); - static void TopBorder(unsigned int statestoadd, bool contended); - static void MainScreen_Blank(unsigned int statestoadd, bool contended); - static void MainScreenLB(unsigned int statestoadd, bool contended); + static void IRAM_ATTR TopBorder_Blank(unsigned int statestoadd, bool contended); + static void IRAM_ATTR TopBorder(unsigned int statestoadd, bool contended); + static void IRAM_ATTR MainScreen_Blank(unsigned int statestoadd, bool contended); + static void IRAM_ATTR MainScreenLB(unsigned int statestoadd, bool contended); static void MainScreen(unsigned int statestoadd, bool contended); static void MainScreen_OSD(unsigned int statestoadd, bool contended); - static void MainScreenRB(unsigned int statestoadd, bool contended); - static void BottomBorder_Blank(unsigned int statestoadd, bool contended); - static void BottomBorder(unsigned int statestoadd, bool contended); - static void BottomBorder_OSD(unsigned int statestoadd, bool contended); + static void IRAM_ATTR MainScreenRB(unsigned int statestoadd, bool contended); + static void IRAM_ATTR BottomBorder_Blank(unsigned int statestoadd, bool contended); + static void IRAM_ATTR BottomBorder(unsigned int statestoadd, bool contended); + static void IRAM_ATTR BottomBorder_OSD(unsigned int statestoadd, bool contended); // Pentagon static void IRAM_ATTR TopBorder_Blank_Pentagon(unsigned int statestoadd, bool contended); @@ -121,6 +122,9 @@ class VIDEO static uint8_t tStatesPerLine; static int tStatesScreen; + // static unsigned int tstateDraw; // Drawing start point (in Tstates) + // static unsigned int linedraw_cnt; + static uint8_t flashing; static uint8_t flash_ctr; @@ -137,6 +141,8 @@ class VIDEO static int VsyncFinetune[2]; + static uint32_t framecnt; // Frames elapsed + }; static unsigned int is169; diff --git a/include/ZXKeyb.h b/include/ZXKeyb.h index 39073473..56eace0d 100644 --- a/include/ZXKeyb.h +++ b/include/ZXKeyb.h @@ -38,25 +38,22 @@ visit https://zxespectrum.speccy.org/contacto #include -class ZXKeyb -{ -public: +class ZXKeyb { - // setup pins for physical keyboard - static void setup(); +public: - // process physical keyboard - static void process(); + static void setup(); // setup pins for physical keyboard + static void process(); // process physical keyboard static uint8_t ZXcols[8]; - - static uint8_t PrevFkeyOSD; - static bool Exists; + // static uint8_t PrevFkeyOSD; private: + static void putRows(uint8_t row_pattern); static uint8_t getCols(); + }; #endif // ZXKEYB_h diff --git a/include/hardpins.h b/include/hardpins.h index 7d9f6ba3..2fe99f09 100644 --- a/include/hardpins.h +++ b/include/hardpins.h @@ -38,8 +38,7 @@ visit https://zxespectrum.speccy.org/contacto #include "hardconfig.h" -// Pin definition for LILYGO TTGO VGA32 - +// Audio Out #define SPEAKER_PIN 25 // Storage mode: pins for external SD card (LILYGO TTGO VGA32 Board and ESPectrum Board) diff --git a/include/messages.h b/include/messages.h index e97dc54b..fe959c81 100644 --- a/include/messages.h +++ b/include/messages.h @@ -249,11 +249,11 @@ static const char *MENU_PERSIST_SAVE[2] = { MENU_PERSIST_SAVE_EN, MENU_PERSIST_S static const char *MENU_PERSIST_LOAD[2] = { MENU_PERSIST_LOAD_EN, MENU_PERSIST_LOAD_ES }; #define MENU_STORAGE_EN "Storage\n"\ - "Flash tape load\t>\n"\ - "Refresh directories\n" + "Flash tape load\t>\n" + // "Refresh directories\n" #define MENU_STORAGE_ES "Almacenamiento\n"\ - "Carga rapida cinta\t>\n"\ - "Refrescar directorios\n" + "Carga rapida cinta\t>\n" + // "Refrescar directorios\n" // static const char *MENU_STORAGE[2] = { MENU_STORAGE_EN, MENU_STORAGE_ES }; #define MENU_FLASHLOAD_EN "Flash load\n"\ diff --git a/sdkconfig.ESPectrum b/sdkconfig.ESPectrum index 2f6de82d..14f4c843 100644 --- a/sdkconfig.ESPectrum +++ b/sdkconfig.ESPectrum @@ -526,7 +526,7 @@ CONFIG_FATFS_CODEPAGE=437 # CONFIG_FATFS_LFN_NONE is not set CONFIG_FATFS_LFN_HEAP=y # CONFIG_FATFS_LFN_STACK is not set -CONFIG_FATFS_MAX_LFN=28 +CONFIG_FATFS_MAX_LFN=52 CONFIG_FATFS_API_ENCODING_ANSI_OEM=y # CONFIG_FATFS_API_ENCODING_UTF_16 is not set # CONFIG_FATFS_API_ENCODING_UTF_8 is not set diff --git a/sdkconfig.ESPectrum.old b/sdkconfig.ESPectrum.old index 205c466b..6ee20ebb 100644 --- a/sdkconfig.ESPectrum.old +++ b/sdkconfig.ESPectrum.old @@ -340,11 +340,13 @@ CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192 CONFIG_ESP_IPC_TASK_STACK_SIZE=1024 CONFIG_ESP_IPC_USES_CALLERS_PRIORITY=y CONFIG_ESP_MINIMAL_SHARED_STACK_SIZE=2048 -# CONFIG_ESP_CONSOLE_UART_DEFAULT is not set +CONFIG_ESP_CONSOLE_UART_DEFAULT=y # CONFIG_ESP_CONSOLE_UART_CUSTOM is not set -CONFIG_ESP_CONSOLE_NONE=y +# CONFIG_ESP_CONSOLE_NONE is not set +CONFIG_ESP_CONSOLE_UART=y CONFIG_ESP_CONSOLE_MULTIPLE_UART=y -CONFIG_ESP_CONSOLE_UART_NUM=-1 +CONFIG_ESP_CONSOLE_UART_NUM=0 +CONFIG_ESP_CONSOLE_UART_BAUDRATE=115200 # CONFIG_ESP_INT_WDT is not set # CONFIG_ESP_TASK_WDT is not set # CONFIG_ESP_PANIC_HANDLER_IRAM is not set @@ -524,7 +526,7 @@ CONFIG_FATFS_CODEPAGE=437 # CONFIG_FATFS_LFN_NONE is not set CONFIG_FATFS_LFN_HEAP=y # CONFIG_FATFS_LFN_STACK is not set -CONFIG_FATFS_MAX_LFN=28 +CONFIG_FATFS_MAX_LFN=58 CONFIG_FATFS_API_ENCODING_ANSI_OEM=y # CONFIG_FATFS_API_ENCODING_UTF_16 is not set # CONFIG_FATFS_API_ENCODING_UTF_8 is not set diff --git a/src/CPU.cpp b/src/CPU.cpp index f69396d7..35425d43 100644 --- a/src/CPU.cpp +++ b/src/CPU.cpp @@ -47,7 +47,6 @@ visit https://zxespectrum.speccy.org/contacto uint32_t CPU::tstates = 0; uint64_t CPU::global_tstates = 0; uint32_t CPU::statesInFrame = 0; -uint32_t CPU::framecnt = 0; uint8_t CPU::latetiming = 0; uint8_t CPU::IntStart = 0; uint8_t CPU::IntEnd = 0; @@ -118,7 +117,9 @@ void IRAM_ATTR CPU::loop() global_tstates += statesInFrame; // increase global Tstates tstates -= statesInFrame; - framecnt++; + #ifndef NO_VIDEO + VIDEO::EndFrame(); + #endif } diff --git a/src/Config.cpp b/src/Config.cpp index 6d52533c..f78a6db3 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -69,6 +69,17 @@ uint8_t Config::joystick = 1; // 0 -> Cursor, 1 -> Kempston uint8_t Config::AluTiming = 0; uint8_t Config::ps2_dev2 = 0; // Second port PS/2 device: 0 -> None, 1 -> PS/2 keyboard, 2 -> PS/2 Mouse (TO DO) bool Config::CursorAsJoy = false; +int8_t Config::CenterH = 0; +int8_t Config::CenterV = 0; +string Config::SNA_Path = "/"; +string Config::TAP_Path = "/"; +string Config::DSK_Path = "/"; +uint16_t Config::SNA_begin_row = 1; +uint16_t Config::SNA_focus = 1; +uint16_t Config::TAP_begin_row = 1; +uint16_t Config::TAP_focus = 1; +uint16_t Config::DSK_begin_row = 1; +uint16_t Config::DSK_focus = 1; // erase control characters (in place) static inline void erase_cntrl(std::string &s) { @@ -258,6 +269,73 @@ void Config::load() { free(str_data); } + err = nvs_get_i8(handle, "CenterH", &Config::CenterH); + if (err == ESP_OK) { + // printf("PS2Dev2:%u\n",Config::ps2_dev2); + } + + err = nvs_get_i8(handle, "CenterV", &Config::CenterV); + if (err == ESP_OK) { + // printf("PS2Dev2:%u\n",Config::ps2_dev2); + } + + err = nvs_get_str(handle, "SNA_Path", NULL, &required_size); + if (err == ESP_OK) { + str_data = (char *)malloc(required_size); + nvs_get_str(handle, "SNA_Path", str_data, &required_size); + // printf("SNA_Path:%s\n",str_data); + SNA_Path = str_data; + free(str_data); + } + + err = nvs_get_str(handle, "TAP_Path", NULL, &required_size); + if (err == ESP_OK) { + str_data = (char *)malloc(required_size); + nvs_get_str(handle, "TAP_Path", str_data, &required_size); + // printf("TAP_Path:%s\n",str_data); + TAP_Path = str_data; + free(str_data); + } + + err = nvs_get_str(handle, "DSK_Path", NULL, &required_size); + if (err == ESP_OK) { + str_data = (char *)malloc(required_size); + nvs_get_str(handle, "DSK_Path", str_data, &required_size); + // printf("DSK_Path:%s\n",str_data); + DSK_Path = str_data; + free(str_data); + } + + err = nvs_get_u16(handle, "SNA_begin_row", &Config::SNA_begin_row); + if (err == ESP_OK) { + // printf("SNA_begin_row:%u\n",Config::SNA_begin_row); + } + + err = nvs_get_u16(handle, "TAP_begin_row", &Config::TAP_begin_row); + if (err == ESP_OK) { + // printf("TAP_begin_row:%u\n",Config::TAP_begin_row); + } + + err = nvs_get_u16(handle, "DSK_begin_row", &Config::DSK_begin_row); + if (err == ESP_OK) { + // printf("begin_row:%u\n",Config::DSK_begin_row); + } + + err = nvs_get_u16(handle, "SNA_focus", &Config::SNA_focus); + if (err == ESP_OK) { + // printf("SNA_focus:%u\n",Config::SNA_focus); + } + + err = nvs_get_u16(handle, "TAP_focus", &Config::TAP_focus); + if (err == ESP_OK) { + // printf("TAP_focus:%u\n",Config::TAP_focus); + } + + err = nvs_get_u16(handle, "DSK_focus", &Config::DSK_focus); + if (err == ESP_OK) { + // printf("DSK_focus:%u\n",Config::DSK_focus); + } + // Close nvs_close(handle); } @@ -341,6 +419,39 @@ void Config::save(string value) { if((value=="CursorAsJoy") || (value=="all")) nvs_set_str(handle,"CursorAsJoy", CursorAsJoy ? "true" : "false"); + if((value=="CenterH") || (value=="all")) + nvs_set_i8(handle,"CenterH",Config::CenterH); + + if((value=="CenterV") || (value=="all")) + nvs_set_i8(handle,"CenterV",Config::CenterV); + + if((value=="SNA_Path") || (value=="all")) + nvs_set_str(handle,"SNA_Path",Config::SNA_Path.c_str()); + + if((value=="TAP_Path") || (value=="all")) + nvs_set_str(handle,"TAP_Path",Config::TAP_Path.c_str()); + + if((value=="DSK_Path") || (value=="all")) + nvs_set_str(handle,"DSK_Path",Config::DSK_Path.c_str()); + + if((value=="SNA_begin_row") || (value=="all")) + nvs_set_u16(handle,"SNA_begin_row",Config::SNA_begin_row); + + if((value=="TAP_begin_row") || (value=="all")) + nvs_set_u16(handle,"TAP_begin_row",Config::TAP_begin_row); + + if((value=="DSK_begin_row") || (value=="all")) + nvs_set_u16(handle,"DSK_begin_row",Config::DSK_begin_row); + + if((value=="SNA_focus") || (value=="all")) + nvs_set_u16(handle,"SNA_focus",Config::SNA_focus); + + if((value=="TAP_focus") || (value=="all")) + nvs_set_u16(handle,"TAP_focus",Config::TAP_focus); + + if((value=="DSK_focus") || (value=="all")) + nvs_set_u16(handle,"DSK_focus",Config::DSK_focus); + // printf("Committing updates in NVS ... "); err = nvs_commit(handle); diff --git a/src/ESP32Lib/Graphics/Font.h b/src/ESP32Lib/Graphics/Font.h index a559d207..a9fc41f7 100644 --- a/src/ESP32Lib/Graphics/Font.h +++ b/src/ESP32Lib/Graphics/Font.h @@ -19,7 +19,7 @@ class Font const unsigned char *pixels; const int charWidth; const int charHeight; - Font(int charWidth, int charHeight, const unsigned char *pixels, int firstChar = 32, int charCount = 96) + Font(int charWidth, int charHeight, const unsigned char *pixels, int firstChar = 32, int charCount = 144 ) // 96 :firstChar(firstChar), charCount(charCount), pixels(pixels), diff --git a/src/ESP32Lib/Graphics/Graphics.h b/src/ESP32Lib/Graphics/Graphics.h index a4b1f2ee..ac112731 100644 --- a/src/ESP32Lib/Graphics/Graphics.h +++ b/src/ESP32Lib/Graphics/Graphics.h @@ -145,7 +145,8 @@ class Graphics: public ImageDrawer { if (!font) return; - if (!font->valid(ch)) + // if (!font->valid(ch)) + if (!(ch >= 32 && ch < 176)) return; const unsigned char *pix = &font->pixels[font->charWidth * font->charHeight * (ch - font->firstChar)]; for (int py = 0; py < font->charHeight; py++) @@ -160,7 +161,8 @@ class Graphics: public ImageDrawer { if (!font) return; - if (font->valid(ch)) + // if (font->valid(ch)) + if (ch >= 32 && ch < 176) drawChar(cursorX, cursorY, ch); else drawChar(cursorX, cursorY, ' '); diff --git a/src/ESP32Lib/VGA/VGA.cpp b/src/ESP32Lib/VGA/VGA.cpp index 7c8304da..74c5f3fd 100644 --- a/src/ESP32Lib/VGA/VGA.cpp +++ b/src/ESP32Lib/VGA/VGA.cpp @@ -52,8 +52,12 @@ const Mode VGA::MODE360x200_50_PENTAGON(18, 36, 54, 360, 31, 3, 33, 600, 3, 1524 // 48K // ¡PENDING FINE TUNE SYNC TESTS! -> 48K 50hz CRT, SYNC @ 19968 micros, DAPR AUDIO LAG TEST -> CLEAN TONE -const Mode VGA::MODE320x240_TV_48(38, 32, 58, 320, 28, 3, 41, 240, 1, 6999999, 1, 1, 0,128,6,13,0,0,8,15); // 15625 / 50.0801282 -const Mode VGA::MODE360x200_TV_48(18, 32, 38, 360, 48, 3, 61, 200, 1, 6999999, 1, 1, 0,128,6,13,0,0,8,15); // 15625 / 50.0801282 +// const Mode VGA::MODE320x240_TV_48(38, 32, 58, 320, 28, 3, 41, 240, 1, 6999999, 1, 1, 0,128,6,13,0,0,8,15); // 15625 / 50.0801282 +// const Mode VGA::MODE360x200_TV_48(18, 32, 38, 360, 48, 3, 61, 200, 1, 6999999, 1, 1, 0,128,6,13,0,0,8,15); // 15625 / 50.0801282 + +// Front and Back porches adjusted to fit with other models +const Mode VGA::MODE320x240_TV_48(42, 32, 62, 320, 28, 3, 40, 240, 1, 7102163, 1, 1, 59,167,6,13,0,0,6,12); // 15575 / 50.0801282 +const Mode VGA::MODE360x200_TV_48(22, 32, 42, 360, 48, 3, 60, 200, 1, 7102163, 1, 1, 59,167,6,13,0,0,6,12); // 15575 / 50.0801282 // 128K @@ -76,8 +80,9 @@ const Mode VGA::videomodes[3][3][2]={ VGA::VGA(const int i2sIndex) : I2S(i2sIndex) { - lineBufferCount = 8; - dmaBufferDescriptors = 0; + // lineBufferCount = 8; + // dmaBufferDescriptors = 0; + } bool VGA::init(const Mode &mode, const int *pinMap, const int bitCount, const int clockPin) @@ -101,30 +106,30 @@ bool VGA::init(const Mode &mode, const int *pinMap, const int bitCount, const in return true; } -void VGA::setLineBufferCount(int lineBufferCount) -{ - this->lineBufferCount = lineBufferCount; -} +// void VGA::setLineBufferCount(int lineBufferCount) +// { +// this->lineBufferCount = lineBufferCount; +// } void VGA::allocateLineBuffers() { - allocateLineBuffers(lineBufferCount); +// allocateLineBuffers(lineBufferCount); } -/// simple ringbuffer of blocks of size bytes each -void VGA::allocateLineBuffers(const int lines) -{ - dmaBufferDescriptorCount = lines; - dmaBufferDescriptors = DMABufferDescriptor::allocateDescriptors(dmaBufferDescriptorCount); - int bytes = (mode.hFront + mode.hSync + mode.hBack + mode.hRes) * bytesPerSample(); - for (int i = 0; i < dmaBufferDescriptorCount; i++) - { - dmaBufferDescriptors[i].setBuffer(DMABufferDescriptor::allocateBuffer(bytes, true), bytes); //front porch + hsync + back porch + pixels - if (i) - dmaBufferDescriptors[i - 1].next(dmaBufferDescriptors[i]); - } - dmaBufferDescriptors[dmaBufferDescriptorCount - 1].next(dmaBufferDescriptors[0]); -} +// /// simple ringbuffer of blocks of size bytes each +// void VGA::allocateLineBuffers(const int lines) +// { +// dmaBufferDescriptorCount = lines; +// dmaBufferDescriptors = DMABufferDescriptor::allocateDescriptors(dmaBufferDescriptorCount); +// int bytes = (mode.hFront + mode.hSync + mode.hBack + mode.hRes) * bytesPerSample(); +// for (int i = 0; i < dmaBufferDescriptorCount; i++) +// { +// dmaBufferDescriptors[i].setBuffer(DMABufferDescriptor::allocateBuffer(bytes, true), bytes); //front porch + hsync + back porch + pixels +// if (i) +// dmaBufferDescriptors[i - 1].next(dmaBufferDescriptors[i]); +// } +// dmaBufferDescriptors[dmaBufferDescriptorCount - 1].next(dmaBufferDescriptors[0]); +// } ///complete ringbuffer from frame void VGA::allocateLineBuffers(void **frameBuffer) @@ -139,7 +144,7 @@ void VGA::allocateLineBuffers(void **frameBuffer) { for (int i = 0; i < inactiveSamples; i++) { - if (i >= mode.hFront && i < mode.hFront + mode.hSync) + if (i >= (mode.hFront - CenterH) && i < (mode.hFront - CenterH + mode.hSync)) { ((unsigned char *)vSyncInactiveBuffer)[i ^ 2] = hsyncBit | vsyncBit; ((unsigned char *)inactiveBuffer)[i ^ 2] = hsyncBit | vsyncBitI; @@ -160,7 +165,7 @@ void VGA::allocateLineBuffers(void **frameBuffer) { for (int i = 0; i < inactiveSamples; i++) { - if (i >= mode.hFront && i < mode.hFront + mode.hSync) + if (i >= (mode.hFront - CenterH) && i < (mode.hFront - CenterH + mode.hSync)) { ((unsigned short *)vSyncInactiveBuffer)[i ^ 1] = hsyncBit | vsyncBit; ((unsigned short *)inactiveBuffer)[i ^ 1] = hsyncBit | vsyncBitI; @@ -182,7 +187,7 @@ void VGA::allocateLineBuffers(void **frameBuffer) for (int i = 0; i < dmaBufferDescriptorCount; i++) dmaBufferDescriptors[i].next(dmaBufferDescriptors[(i + 1) % dmaBufferDescriptorCount]); int d = 0; - for (int i = 0; i < mode.vFront; i++) + for (int i = 0; i < (mode.vFront - CenterV); i++) { dmaBufferDescriptors[d++].setBuffer(inactiveBuffer, inactiveSamples * bytesPerSample()); dmaBufferDescriptors[d++].setBuffer(blankActiveBuffer, mode.hRes * bytesPerSample()); @@ -192,7 +197,7 @@ void VGA::allocateLineBuffers(void **frameBuffer) dmaBufferDescriptors[d++].setBuffer(vSyncInactiveBuffer, inactiveSamples * bytesPerSample()); dmaBufferDescriptors[d++].setBuffer(vSyncActiveBuffer, mode.hRes * bytesPerSample()); } - for (int i = 0; i < mode.vBack; i++) + for (int i = 0; i < (mode.vBack + CenterV); i++) { dmaBufferDescriptors[d++].setBuffer(inactiveBuffer, inactiveSamples * bytesPerSample()); dmaBufferDescriptors[d++].setBuffer(blankActiveBuffer, mode.hRes * bytesPerSample()); @@ -205,47 +210,47 @@ void VGA::allocateLineBuffers(void **frameBuffer) // printf("buffer descriptors count: %d\n",d); } -void VGA::vSync() -{ - vSyncPassed = true; -} +// void VGA::vSync() +// { +// vSyncPassed = true; +// } -void VGA::interrupt() -{ - unsigned long *signal = (unsigned long *)dmaBufferDescriptors[dmaBufferDescriptorActive].buffer(); - unsigned long *pixels = &((unsigned long *)dmaBufferDescriptors[dmaBufferDescriptorActive].buffer())[(mode.hSync + mode.hBack) / 2]; - unsigned long base, baseh; - if (currentLine >= mode.vFront && currentLine < mode.vFront + mode.vSync) - { - baseh = (vsyncBit | hsyncBit) | ((vsyncBit | hsyncBit) << 16); - base = (vsyncBit | hsyncBitI) | ((vsyncBit | hsyncBitI) << 16); - } - else - { - baseh = (vsyncBitI | hsyncBit) | ((vsyncBitI | hsyncBit) << 16); - base = (vsyncBitI | hsyncBitI) | ((vsyncBitI | hsyncBitI) << 16); - } - for (int i = 0; i < mode.hSync / 2; i++) - signal[i] = baseh; - for (int i = mode.hSync / 2; i < (mode.hSync + mode.hBack) / 2; i++) - signal[i] = base; - - int y = (currentLine - mode.vFront - mode.vSync - mode.vBack) / mode.vDiv; - if (y >= 0 && y < mode.vRes) - interruptPixelLine(y, pixels, base); - else - for (int i = 0; i < mode.hRes / 2; i++) - { - pixels[i] = base | (base << 16); - } - for (int i = 0; i < mode.hFront / 2; i++) - signal[i + (mode.hSync + mode.hBack + mode.hRes) / 2] = base; - currentLine = (currentLine + 1) % totalLines; - dmaBufferDescriptorActive = (dmaBufferDescriptorActive + 1) % dmaBufferDescriptorCount; - if (currentLine == 0) - vSync(); -} +// void VGA::interrupt() +// { + // unsigned long *signal = (unsigned long *)dmaBufferDescriptors[dmaBufferDescriptorActive].buffer(); + // unsigned long *pixels = &((unsigned long *)dmaBufferDescriptors[dmaBufferDescriptorActive].buffer())[(mode.hSync + mode.hBack) / 2]; + // unsigned long base, baseh; + // if (currentLine >= mode.vFront && currentLine < mode.vFront + mode.vSync) + // { + // baseh = (vsyncBit | hsyncBit) | ((vsyncBit | hsyncBit) << 16); + // base = (vsyncBit | hsyncBitI) | ((vsyncBit | hsyncBitI) << 16); + // } + // else + // { + // baseh = (vsyncBitI | hsyncBit) | ((vsyncBitI | hsyncBit) << 16); + // base = (vsyncBitI | hsyncBitI) | ((vsyncBitI | hsyncBitI) << 16); + // } + // for (int i = 0; i < mode.hSync / 2; i++) + // signal[i] = baseh; + // for (int i = mode.hSync / 2; i < (mode.hSync + mode.hBack) / 2; i++) + // signal[i] = base; -void VGA::interruptPixelLine(int y, unsigned long *pixels, unsigned long syncBits) -{ -} + // int y = (currentLine - mode.vFront - mode.vSync - mode.vBack) / mode.vDiv; + // if (y >= 0 && y < mode.vRes) + // interruptPixelLine(y, pixels, base); + // else + // for (int i = 0; i < mode.hRes / 2; i++) + // { + // pixels[i] = base | (base << 16); + // } + // for (int i = 0; i < mode.hFront / 2; i++) + // signal[i + (mode.hSync + mode.hBack + mode.hRes) / 2] = base; + // currentLine = (currentLine + 1) % totalLines; + // dmaBufferDescriptorActive = (dmaBufferDescriptorActive + 1) % dmaBufferDescriptorCount; + // if (currentLine == 0) + // vSync(); +// } + +// void VGA::interruptPixelLine(int y, unsigned long *pixels, unsigned long syncBits) +// { +// } diff --git a/src/ESP32Lib/VGA/VGA.h b/src/ESP32Lib/VGA/VGA.h index f2b5a0ea..888bf300 100644 --- a/src/ESP32Lib/VGA/VGA.h +++ b/src/ESP32Lib/VGA/VGA.h @@ -20,7 +20,7 @@ class VGA : public I2S { public: VGA(const int i2sIndex = 0); - void setLineBufferCount(int lineBufferCount); + // void setLineBufferCount(int lineBufferCount); bool init(const Mode &mode, const int *pinMap, const int bitCount, const int clockPin = -1); virtual bool init(const Mode &mode, const PinConfig &pinConfig) = 0; @@ -45,6 +45,9 @@ class VGA : public I2S Mode mode; + int CenterH = 0; + int CenterV = 0; + virtual int bytesPerSample() const = 0; protected: @@ -52,7 +55,7 @@ class VGA : public I2S virtual void initSyncBits() = 0; virtual long syncBits(bool h, bool v) = 0; - int lineBufferCount; + // int lineBufferCount; int vsyncPin; int hsyncPin; int currentLine; @@ -69,13 +72,13 @@ class VGA : public I2S void *inactiveBuffer; void *blankActiveBuffer; - void allocateLineBuffers(const int lines); + // void allocateLineBuffers(const int lines); virtual void allocateLineBuffers(); virtual void allocateLineBuffers(void **frameBuffer); virtual void propagateResolution(const int xres, const int yres) = 0; protected: - virtual void interrupt(); - virtual void vSync(); - virtual void interruptPixelLine(int y, unsigned long *pixels, unsigned long syncBits); + // virtual void interrupt(); + // virtual void vSync(); + // virtual void interruptPixelLine(int y, unsigned long *pixels, unsigned long syncBits); }; diff --git a/src/ESPectrum.cpp b/src/ESPectrum.cpp index b193fecd..d4290402 100644 --- a/src/ESPectrum.cpp +++ b/src/ESPectrum.cpp @@ -518,7 +518,19 @@ void ESPectrum::setup() // Load snapshot if present in Config::ram_file if (Config::ram_file != NO_RAM_FILE) { - + + FileUtils::SNA_Path = Config::SNA_Path; + FileUtils::fileTypes[DISK_SNAFILE].begin_row = Config::SNA_begin_row; + FileUtils::fileTypes[DISK_SNAFILE].focus = Config::SNA_focus; + + FileUtils::TAP_Path = Config::TAP_Path; + FileUtils::fileTypes[DISK_TAPFILE].begin_row = Config::TAP_begin_row; + FileUtils::fileTypes[DISK_TAPFILE].focus = Config::TAP_focus; + + FileUtils::DSK_Path = Config::DSK_Path; + FileUtils::fileTypes[DISK_DSKFILE].begin_row = Config::DSK_begin_row; + FileUtils::fileTypes[DISK_DSKFILE].focus = Config::DSK_focus; + LoadSnapshot(Config::ram_file,""); Config::last_ram_file = Config::ram_file; @@ -704,9 +716,10 @@ void IRAM_ATTR ESPectrum::processKeyboard() { KeytoESP = NextKey.vk; Kdown = NextKey.down; - + if ((Kdown) && (((KeytoESP >= fabgl::VK_F1) && (KeytoESP <= fabgl::VK_F12)) || (KeytoESP == fabgl::VK_PAUSE) || (KeytoESP == fabgl::VK_GRAVEACCENT ))) { - OSD::do_OSD(KeytoESP); + OSD::do_OSD(KeytoESP,NextKey.SHIFT); + Kbd->emptyVirtualKeyQueue(); return; } @@ -959,49 +972,69 @@ void IRAM_ATTR ESPectrum::processKeyboard() { zxDelay = 15; if (!bitRead(ZXKeyb::ZXcols[3],0)) { - OSD::do_OSD(fabgl::VK_F1); + OSD::do_OSD(fabgl::VK_F1,0); } else if (!bitRead(ZXKeyb::ZXcols[3],1)) { - OSD::do_OSD(fabgl::VK_F2); + OSD::do_OSD(fabgl::VK_F2,0); } else if (!bitRead(ZXKeyb::ZXcols[3],2)) { - OSD::do_OSD(fabgl::VK_F3); + OSD::do_OSD(fabgl::VK_F3,0); } else if (!bitRead(ZXKeyb::ZXcols[3],3)) { - OSD::do_OSD(fabgl::VK_F4); + OSD::do_OSD(fabgl::VK_F4,0); } else if (!bitRead(ZXKeyb::ZXcols[3],4)) { - OSD::do_OSD(fabgl::VK_F5); + OSD::do_OSD(fabgl::VK_F5,0); } else if (!bitRead(ZXKeyb::ZXcols[4],4)) { - OSD::do_OSD(fabgl::VK_F6); + OSD::do_OSD(fabgl::VK_F6,0); } else if (!bitRead(ZXKeyb::ZXcols[4],3)) { - OSD::do_OSD(fabgl::VK_F7); + OSD::do_OSD(fabgl::VK_F7,0); } else if (!bitRead(ZXKeyb::ZXcols[4],2)) { - OSD::do_OSD(fabgl::VK_F8); + OSD::do_OSD(fabgl::VK_F8,0); } else if (!bitRead(ZXKeyb::ZXcols[4],1)) { - OSD::do_OSD(fabgl::VK_F9); + OSD::do_OSD(fabgl::VK_F9,0); } else if (!bitRead(ZXKeyb::ZXcols[4],0)) { - OSD::do_OSD(fabgl::VK_F10); + OSD::do_OSD(fabgl::VK_F10,0); } else if (!bitRead(ZXKeyb::ZXcols[2],0)) { - OSD::do_OSD(fabgl::VK_F11); + OSD::do_OSD(fabgl::VK_F11,0); } else if (!bitRead(ZXKeyb::ZXcols[2],1)) { - OSD::do_OSD(fabgl::VK_F12); + OSD::do_OSD(fabgl::VK_F12,0); } else if (!bitRead(ZXKeyb::ZXcols[5],0)) { // P -> Pause - OSD::do_OSD(fabgl::VK_PAUSE); + OSD::do_OSD(fabgl::VK_PAUSE,0); } else if (!bitRead(ZXKeyb::ZXcols[5],2)) { // I -> Info - OSD::do_OSD(fabgl::VK_GRAVEACCENT); + OSD::do_OSD(fabgl::VK_GRAVEACCENT,0); } else if (!bitRead(ZXKeyb::ZXcols[1],1)) { // S -> Screen capture CaptureToBmp(); + } else + if (!bitRead(ZXKeyb::ZXcols[0],1)) { // Z -> CenterH + if (Config::CenterH > -16) Config::CenterH--; + Config::save("CenterH"); + OSD::osdCenteredMsg("Horiz. center: " + to_string(Config::CenterH), LEVEL_INFO, 375); + } else + if (!bitRead(ZXKeyb::ZXcols[0],2)) { // X -> CenterH + if (Config::CenterH < 16) Config::CenterH++; + Config::save("CenterH"); + OSD::osdCenteredMsg("Horiz. center: " + to_string(Config::CenterH), LEVEL_INFO, 375); + } else + if (!bitRead(ZXKeyb::ZXcols[0],3)) { // C -> CenterV + if (Config::CenterV > -16) Config::CenterV--; + Config::save("CenterV"); + OSD::osdCenteredMsg("Vert. center: " + to_string(Config::CenterV), LEVEL_INFO, 375); + } else + if (!bitRead(ZXKeyb::ZXcols[0],4)) { // V -> CenterV + if (Config::CenterV < 16) Config::CenterV++; + Config::save("CenterV"); + OSD::osdCenteredMsg("Vert. center: " + to_string(Config::CenterV), LEVEL_INFO, 375); } else zxDelay = 0; @@ -1230,7 +1263,7 @@ for(;;) { // } // Draw stats, if activated, every 32 frames - if (((CPU::framecnt & 31) == 0) && (VIDEO::OSD)) OSD::drawStats(linea1,linea2); + if (((VIDEO::framecnt & 31) == 0) && (VIDEO::OSD)) OSD::drawStats(linea1,linea2); // Flashing flag change if (!(VIDEO::flash_ctr++ & 0x0f)) VIDEO::flashing ^= 0x80; @@ -1269,17 +1302,17 @@ for(;;) { if (Tape::tapeStatus==TAPE_LOADING) { - snprintf(linea1, sizeof(linea1), " %-12s %04d/%04d ", Tape::tapeFileName.substr(6 + TapeNameScroller, 12).c_str(), Tape::tapeCurBlock + 1, Tape::tapeNumBlocks); + snprintf(linea1, sizeof(linea1), " %-12s %04d/%04d ", Tape::tapeFileName.substr(0 + TapeNameScroller, 12).c_str(), Tape::tapeCurBlock + 1, Tape::tapeNumBlocks); float percent = (float)((Tape::tapebufByteCount + Tape::tapePlayOffset) * 100) / (float)Tape::tapeFileSize; snprintf(linea2, sizeof(linea2), " %05.2f%% %07d%s%07d ", percent, Tape::tapebufByteCount + Tape::tapePlayOffset, "/" , Tape::tapeFileSize); - if ((++TapeNameScroller + 18) > Tape::tapeFileName.length()) TapeNameScroller = 0; + if ((++TapeNameScroller + 12) > Tape::tapeFileName.length()) TapeNameScroller = 0; } else { snprintf(linea1, sizeof(linea1), "CPU: %05d / IDL: %05d ", (int)(elapsed), (int)(idle)); - snprintf(linea2, sizeof(linea2), "FPS:%6.2f / FND:%6.2f ", CPU::framecnt / (totalseconds / 1000000), CPU::framecnt / (totalsecondsnodelay / 1000000)); + snprintf(linea2, sizeof(linea2), "FPS:%6.2f / FND:%6.2f ", VIDEO::framecnt / (totalseconds / 1000000), VIDEO::framecnt / (totalsecondsnodelay / 1000000)); } @@ -1288,7 +1321,7 @@ for(;;) { totalseconds = 0; totalsecondsnodelay = 0; - CPU::framecnt = 0; + VIDEO::framecnt = 0; // ESPmedian = 0; diff --git a/src/FileUtils.cpp b/src/FileUtils.cpp index 630025a7..2683c202 100644 --- a/src/FileUtils.cpp +++ b/src/FileUtils.cpp @@ -61,13 +61,14 @@ string FileUtils::MountPoint = MOUNT_POINT_SD; // Start with SD bool FileUtils::SDReady = false; sdmmc_card_t *FileUtils::card; -string FileUtils::SNA_Path = DISK_SNA_DIR; // Current path on the SD (for future folder support) -string FileUtils::TAP_Path = DISK_TAP_DIR; // Current path on the SD (for future folder support) -string FileUtils::DSK_Path = DISK_DSK_DIR; // Current path on the SD (for future folder support) - -int FileUtils::curSNAFile = 1; // Current SNA file index on browser -int FileUtils::curTAPFile = 1; // Current TAP file index on browser -int FileUtils::curDSKFile = 1; // Current TAP file index on browser +string FileUtils::SNA_Path = "/"; // DISK_SNA_DIR; // Current path on the SD (for future folder support) +string FileUtils::TAP_Path = "/"; // DISK_TAP_DIR; // Current path on the SD (for future folder support) +string FileUtils::DSK_Path = "/"; // DISK_DSK_DIR; // Current path on the SD (for future folder support) +DISK_FTYPE FileUtils::fileTypes[3] = { + {".sna,.SNA,.z80,.Z80",".s",1,1}, + {".tap,.TAP",".t",1,1}, + {".trd,.TRD,.scl,.SCL",".d",1,1} +}; void FileUtils::initFileSystem() { @@ -257,110 +258,153 @@ void FileUtils::unmountSDCard() { // } -int FileUtils::DirToFile(string fpath, string fileExts) { +void FileUtils::DirToFile(string fpath, uint8_t ftype) { char fileName[8]; std::vector filenames; filenames.reserve(MAX_FNAMES_PER_CHUNK); - DIR* dir = opendir(fpath.c_str()); - if (dir == NULL) { - printf("Error opening %s",fpath.c_str()); - return 0; + // Populate filexts with valid filename extensions + std::vector filexts; + size_t pos = 0; + string ss = fileTypes[ftype].fileExts; + while ((pos = ss.find(",")) != std::string::npos) { + // printf("%s , ",ss.substr(0,pos).c_str()); + filexts.push_back(ss.substr(0, pos)); + ss.erase(0, pos + 1); } + // printf("%s , ",ss.substr(0).c_str()); + filexts.push_back(ss.substr(0)); + // printf("\n"); - struct dirent* de = readdir(dir); - if (!de) { - printf("No entries found!\n"); - closedir(dir); - return 0; + string fdir = fpath.substr(0,fpath.length() - 1); + DIR* dir = opendir(fdir.c_str()); + if (dir == NULL) { + printf("Error opening %s\n",fpath.c_str()); + return; } // Remove previous dir file - remove((fpath + "/.d").c_str()); + remove((fpath + fileTypes[ftype].indexFilename).c_str()); // Read filenames from medium into vector, sort it, and dump into MAX_FNAMES_PER_CHUNK filenames long files int cnt = 0; int chunk_cnt = 0; + struct dirent* de; - while (true) { + unsigned long h = 0, high; // Directory Hash + + while ((de = readdir(dir)) != nullptr) { string fname = de->d_name; - if (fname.compare(0,1,".") != 0) { - if ((fileExts.find(fname.substr(fname.size()-4)) != string::npos)) { - // printf("%s\n",fname.c_str()); - filenames.push_back(fname.c_str()); - cnt++; - if (cnt == MAX_FNAMES_PER_CHUNK) { - // Dump current chunk - sort(filenames.begin(),filenames.end()); // Sort vector - sprintf(fileName, "/.d%d", chunk_cnt); - FILE *f = fopen((fpath + fileName).c_str(), "w"); - if (f==NULL) { - printf("Error opening filelist chunk\n"); - return 0; + // if (fname[0] == 'A') printf("Fname: %s\n",fname.c_str()); + if (de->d_type == DT_REG || de->d_type == DT_DIR) { + if (fname.compare(0,1,".") != 0) { + // if (fname[0] == 'A') printf("Fname2: %s\n",fname.c_str()); + // if ((de->d_type == DT_DIR) || (std::find(filexts.begin(),filexts.end(),fname.substr(fname.size()-4)) != filexts.end())) { + if ((de->d_type == DT_DIR) || ((fname.size() > 3) && (std::find(filexts.begin(),filexts.end(),fname.substr(fname.size()-4)) != filexts.end()))) { + // if (fname[0] == 'A') printf("Fname3: %s\n",fname.c_str()); + + if (de->d_type == DT_DIR) + filenames.push_back((char(32) + fname).c_str()); + else + filenames.push_back(fname.c_str()); + + // Calc hash + for (int i = 0; i < fname.length(); i++) { + h = (h << 4) + fname[i]; + if (high = h & 0xF0000000) + h ^= high >> 24; + h &= ~high; } - for (int n=0; n < MAX_FNAMES_PER_CHUNK; n++) fputs((filenames[n] + std::string(31 - filenames[n].size(), ' ') + "\n").c_str(),f); - fclose(f); - filenames.clear(); - cnt = 0; - chunk_cnt++; - } - } + cnt++; + if (cnt == MAX_FNAMES_PER_CHUNK) { + // Dump current chunk + sort(filenames.begin(),filenames.end()); // Sort vector + sprintf(fileName, "%d", chunk_cnt); + FILE *f = fopen((fpath + fileTypes[ftype].indexFilename + fileName).c_str(), "w"); + if (f==NULL) { + printf("Error opening filelist chunk\n"); + return; + } + for (int n=0; n < MAX_FNAMES_PER_CHUNK; n++) fputs((filenames[n] + std::string(63 - filenames[n].size(), ' ') + "\n").c_str(),f); + fclose(f); + filenames.clear(); + cnt = 0; + chunk_cnt++; + } + } + } } - de = readdir(dir); - if (!de) break; } + // Add previous directory entry if not on root dir + // printf("%s - %s\n",fpath.c_str(),(MountPoint + "/").c_str()); + if (fpath != (MountPoint + "/")) { + filenames.push_back(" .."); + cnt++; + } + + closedir(dir); + + filexts.clear(); // Clear vector + std::vector().swap(filexts); // free memory + if (cnt > 0) { // Dump last chunk sort(filenames.begin(),filenames.end()); // Sort vector - sprintf(fileName, "/.d%d", chunk_cnt); - FILE *f = fopen((fpath + fileName).c_str(), "w"); + sprintf(fileName, "%d", chunk_cnt); + FILE *f = fopen((fpath + fileTypes[ftype].indexFilename + fileName).c_str(), "w"); if (f == NULL) { printf("Error opening last filelist chunk\n"); - return 0; + return; } - for (int n=0; n < cnt;n++) fputs((filenames[n] + std::string(31 - filenames[n].size(), ' ') + "\n").c_str(),f); + for (int n=0; n < cnt;n++) fputs((filenames[n] + std::string(63 - filenames[n].size(), ' ') + "\n").c_str(),f); fclose(f); } - if (chunk_cnt == 0) { - // Rename unique chunk - rename((fpath + "/.d0").c_str(),(fpath + "/.d").c_str()); - } - - closedir(dir); - filenames.clear(); // Clear vector std::vector().swap(filenames); // free memory - // printf("Sort done.\n"); + if (chunk_cnt == 0) { + if (cnt == 0) return; + rename((fpath + fileTypes[ftype].indexFilename + "0").c_str(),(fpath + fileTypes[ftype].indexFilename).c_str()); // Rename unique chunk + } else { + Mergefiles(fpath, ftype, chunk_cnt); + } - return chunk_cnt; + // Add directory hash to last line of file + // printf("Hashcode: %lu\n",h); + FILE *fout; + fout = fopen((fpath + fileTypes[ftype].indexFilename).c_str(), "a"); + fputs(to_string(h).c_str(),fout); + fclose(fout); + fout = NULL; } -void FileUtils::Mergefiles(string fpath, int chunk_cnt) { +void FileUtils::Mergefiles(string fpath, uint8_t ftype, int chunk_cnt) { char fileName[8]; // Merge sort FILE *file1,*file2,*fout; - char fname1[64]; - char fname2[64]; + // char fname1[64]; + // char fname2[64]; + char fname1[128]; + char fname2[128]; - file1 = fopen((fpath + "/.d0").c_str(), "r"); - file2 = fopen((fpath + "/.d1").c_str(), "r"); + file1 = fopen((fpath + fileTypes[ftype].indexFilename + "0").c_str(), "r"); + file2 = fopen((fpath + fileTypes[ftype].indexFilename + "1").c_str(), "r"); string bufout=""; int bufcnt = 0; int n = 1; while (file2 != NULL) { - sprintf(fileName, "/.t%d", n); + sprintf(fileName, ".tmp%d", n); // printf("Creating %s\n",fileName); fout = fopen((fpath + fileName).c_str(), "w"); @@ -388,7 +432,7 @@ void FileUtils::Mergefiles(string fpath, int chunk_cnt) { bufcnt++; - if (bufcnt == 128) { + if (bufcnt == 64) { fwrite(bufout.c_str(),sizeof(char),bufout.length(),fout); bufout = ""; bufcnt = 0; @@ -405,30 +449,34 @@ void FileUtils::Mergefiles(string fpath, int chunk_cnt) { fclose (fout); // Next cycle: open t for read - sprintf(fileName, "/.t%d", n); + sprintf(fileName, ".tmp%d", n); file1 = fopen((fpath + fileName).c_str(), "r"); n++; - sprintf(fileName, "/.d%d", n); - file2 = fopen((fpath + fileName).c_str(), "r"); + sprintf(fileName, "%d", n); + file2 = fopen((fpath + fileTypes[ftype].indexFilename + fileName).c_str(), "r"); } fclose(file1); // Rename final chunk - sprintf(fileName, "/.t%d", n - 1); - rename((fpath + fileName).c_str(),(fpath + "/.d").c_str()); + sprintf(fileName, ".tmp%d", n - 1); + rename((fpath + fileName).c_str(),(fpath + fileTypes[ftype].indexFilename).c_str()); // Remove temp files for (int n = 0; n <= chunk_cnt; n++) { - sprintf(fileName, "/.d%d", n); - remove((fpath + fileName).c_str()); - sprintf(fileName, "/.t%d", n); + sprintf(fileName, "%d", n); + remove((fpath + fileTypes[ftype].indexFilename + fileName).c_str()); + sprintf(fileName, ".tmp%d", n); remove((fpath + fileName).c_str()); } + file1 = NULL; + file2 = NULL; + fout = NULL; + } bool FileUtils::hasSNAextension(string filename) diff --git a/src/Font.cpp b/src/Font.cpp index 845a158d..dd8c88fd 100644 --- a/src/Font.cpp +++ b/src/Font.cpp @@ -437,6 +437,467 @@ const unsigned char Font6x8Pixels[] = { 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, 255, 255, 255, 0, + 0, 255, 0, 0, 0, 255, + 0, 255, 0, 0, 0, 0, + 0, 255, 0, 0, 0, 0, + 0, 255, 0, 0, 0, 0, + 0, 255, 0, 0, 0, 255, + 0, 0, 255, 255, 255, 0, + 0, 0, 0, 255, 0, 0, + + // ü + 0, 0, 255, 0, 255, 0, + 0, 0, 0, 0, 0, 0, + 0, 255, 0, 0, 0, 255, + 0, 255, 0, 0, 0, 255, + 0, 255, 0, 0, 0, 255, + 0, 255, 0, 0, 255, 255, + 0, 0, 255, 255, 0, 255, + 0, 0, 0, 0, 0, 0, + + // é + 0, 0, 0, 0, 255, 0, + 0, 0, 0, 255, 0, 0, + 0, 0, 255, 255, 255, 0, + 0, 255, 0, 0, 0, 255, + 0, 255, 255, 255, 255, 255, + 0, 255, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, + + // â + 0, 0, 255, 255, 255, 0, + 0, 255, 0, 0, 0, 255, + 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 255, + 0, 0, 255, 255, 255, 255, + 0, 255, 0, 0, 0, 255, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, + + // ä + 0, 0, 255, 0, 255, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 255, + 0, 0, 255, 255, 255, 255, + 0, 255, 0, 0, 0, 255, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, + + // à + 0, 0, 255, 0, 0, 0, + 0, 0, 0, 255, 0, 0, + 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 255, + 0, 0, 255, 255, 255, 255, + 0, 255, 0, 0, 0, 255, + 0, 0, 255, 255, 255, 255, + 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, 255, 255, 255, 0, + 0, 255, 0, 0, 0, 255, + 0, 255, 0, 0, 0, 0, + 0, 255, 0, 0, 0, 255, + 0, 0, 255, 255, 255, 0, + 0, 0, 0, 255, 0, 0, + + // ê + 0, 0, 255, 255, 255, 0, + 0, 255, 0, 0, 0, 255, + 0, 0, 255, 255, 255, 0, + 0, 255, 0, 0, 0, 255, + 0, 255, 255, 255, 255, 255, + 0, 255, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, + + // ë + 0, 0, 255, 0, 255, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, + 0, 255, 0, 0, 0, 255, + 0, 255, 255, 255, 255, 255, + 0, 255, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, + + // è + 0, 0, 255, 0, 0, 0, + 0, 0, 0, 255, 0, 0, + 0, 0, 255, 255, 255, 0, + 0, 255, 0, 0, 0, 255, + 0, 255, 255, 255, 255, 255, + 0, 255, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, + + // ï + 0, 0, 255, 0, 255, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 0, 0, + 0, 0, 0, 255, 0, 0, + 0, 0, 0, 255, 0, 0, + 0, 0, 0, 255, 0, 0, + 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, + + // î + 0, 0, 255, 255, 255, 0, + 0, 255, 0, 0, 0, 255, + 0, 0, 255, 255, 0, 0, + 0, 0, 0, 255, 0, 0, + 0, 0, 0, 255, 0, 0, + 0, 0, 0, 255, 0, 0, + 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, + + // ì + 0, 0, 255, 0, 0, 0, + 0, 0, 0, 255, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 0, 0, + 0, 0, 0, 255, 0, 0, + 0, 0, 0, 255, 0, 0, + 0, 0, 255, 255, 255, 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, + 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, 255, 255, 255, 0, + 0, 255, 0, 0, 0, 255, + 0, 0, 255, 255, 255, 0, + 0, 255, 0, 0, 0, 255, + 0, 255, 0, 0, 0, 255, + 0, 255, 0, 0, 0, 255, + 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, + + // ö + 0, 0, 255, 0, 255, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 0, + 0, 255, 0, 0, 0, 255, + 0, 255, 0, 0, 0, 255, + 0, 255, 0, 0, 0, 255, + 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, + + // ò + 0, 0, 255, 0, 0, 0, + 0, 0, 0, 255, 0, 0, + 0, 0, 255, 255, 255, 0, + 0, 255, 0, 0, 0, 255, + 0, 255, 0, 0, 0, 255, + 0, 255, 0, 0, 0, 255, + 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, + + // û + 0, 0, 0, 255, 0, 0, + 0, 0, 255, 0, 255, 0, + 0, 255, 0, 0, 0, 255, + 0, 255, 0, 0, 0, 255, + 0, 255, 0, 0, 0, 255, + 0, 255, 0, 0, 255, 255, + 0, 0, 255, 255, 0, 255, + 0, 0, 0, 0, 0, 0, + + // ù + 0, 0, 255, 0, 0, 0, + 0, 0, 0, 255, 0, 0, + 0, 255, 0, 0, 0, 255, + 0, 255, 0, 0, 0, 255, + 0, 255, 0, 0, 0, 255, + 0, 255, 0, 0, 255, 255, + 0, 0, 255, 255, 0, 255, + 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, 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, 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, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + + // á + 0, 0, 0, 0, 255, 0, + 0, 0, 0, 255, 0, 0, + 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 255, + 0, 0, 255, 255, 255, 255, + 0, 255, 0, 0, 0, 255, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, + + // í + 0, 0, 0, 0, 255, 0, + 0, 0, 0, 255, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 0, 0, + 0, 0, 0, 255, 0, 0, + 0, 0, 0, 255, 0, 0, + 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, + + // ó + 0, 0, 0, 0, 255, 0, + 0, 0, 0, 255, 0, 0, + 0, 0, 255, 255, 255, 0, + 0, 255, 0, 0, 0, 255, + 0, 255, 0, 0, 0, 255, + 0, 255, 0, 0, 0, 255, + 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, + + // ú + 0, 0, 0, 0, 255, 0, + 0, 0, 0, 255, 0, 0, + 0, 255, 0, 0, 0, 255, + 0, 255, 0, 0, 0, 255, + 0, 255, 0, 0, 0, 255, + 0, 255, 0, 0, 255, 255, + 0, 0, 255, 255, 0, 255, + 0, 0, 0, 0, 0, 0, + + // ñ + 0,0, 255, 255, 0, 255, + 0,0, 0, 0, 255, 0, + 0,255, 0, 255, 255, 0, + 0,255, 255, 0, 0, 255, + 0,255, 0, 0, 0, 255, + 0,255, 0, 0, 0, 255, + 0,255, 0, 0, 0, 255, + 0,0, 0, 0, 0, 0, + + // Ñ + 0, 0, 255, 255, 0, 255, + 0, 255, 0, 0, 255, 0, + 0, 255, 0, 0, 0, 255, + 0, 255, 255, 0, 0, 255, + 0, 255, 0, 255, 0, 255, + 0, 255, 0, 0, 255, 255, + 0, 255, 0, 0, 0, 255, + 0, 0, 0, 0, 0, 0, + + // ª + 0, 0 ,255, 255, 255, 0, + 0, 0, 0, 0, 0, 255, + 0, 0, 255, 255, 255, 255, + 0, 255, 0, 0, 0, 255, + 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + + // º + 0, 0, 255, 255, 255, 0, + 0, 255, 0, 0, 0, 255, + 0, 255, 0, 0, 0, 255, + 0, 255, 0, 0, 0, 255, + 0, 0, 255, 255, 255, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + + // ¿ + 0, 0, 0, 255, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 0, 0, + 0, 0, 255, 0, 0, 0, + 0, 255, 0, 0, 0, 0, + 0, 255, 0, 0, 0, 255, + 0, 0, 255, 255, 255, 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, + 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, 255, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 0, 0, + 0, 0, 0, 255, 0, 0, + 0, 0, 0, 255, 0, 0, + 0, 0, 0, 255, 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, + }; #endif // FONTFACE == 1 // iso8859_1 diff --git a/src/OSDMain.cpp b/src/OSDMain.cpp index 7d09e9d2..91a83794 100644 --- a/src/OSDMain.cpp +++ b/src/OSDMain.cpp @@ -251,1055 +251,1089 @@ static bool persistLoad(uint8_t slotnumber) static int zxDelay = 0; // OSD Main Loop -void OSD::do_OSD(fabgl::VirtualKey KeytoESP) { +void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t SHIFT) { static uint8_t last_sna_row = 0; fabgl::VirtualKeyItem Nextkey; - if (KeytoESP == fabgl::VK_PAUSE) { - click(); - osdCenteredMsg(OSD_PAUSE[Config::lang], LEVEL_INFO, 1000); - - // while (1) { - // ESPectrum::readKbdJoy(); - // while (ESPectrum::PS2Controller.keyboard()->virtualKeyAvailable()) { - // if (ESPectrum::readKbd(&Nextkey)) - // if (Nextkey.down) - // if (Nextkey.vk == fabgl::VK_PAUSE) { - // click(); - // return; - // } else - // osdCenteredMsg(OSD_PAUSE[Config::lang], LEVEL_INFO, 500); - // } - // vTaskDelay(5 / portTICK_PERIOD_MS); - // } + if (SHIFT) { + + if (KeytoESP == fabgl::VK_F5) { + if (Config::CenterH > -16) Config::CenterH--; + Config::save("CenterH"); + osdCenteredMsg("Horiz. center: " + to_string(Config::CenterH), LEVEL_INFO, 375); + } else + if (KeytoESP == fabgl::VK_F6) { + if (Config::CenterH < 16) Config::CenterH++; + Config::save("CenterH"); + osdCenteredMsg("Horiz. center: " + to_string(Config::CenterH), LEVEL_INFO, 375); + } else + if (KeytoESP == fabgl::VK_F7) { + if (Config::CenterV > -16) Config::CenterV--; + Config::save("CenterV"); + osdCenteredMsg("Vert. center: " + to_string(Config::CenterV), LEVEL_INFO, 375); + } else + if (KeytoESP == fabgl::VK_F8) { + if (Config::CenterV < 16) Config::CenterV++; + Config::save("CenterV"); + + osdCenteredMsg("Vert. center: " + to_string(Config::CenterV), LEVEL_INFO, 375); + } + + } else { + + if (KeytoESP == fabgl::VK_PAUSE) { + click(); + osdCenteredMsg(OSD_PAUSE[Config::lang], LEVEL_INFO, 1000); + + // while (1) { + // ESPectrum::readKbdJoy(); + // while (ESPectrum::PS2Controller.keyboard()->virtualKeyAvailable()) { + // if (ESPectrum::readKbd(&Nextkey)) + // if (Nextkey.down) + // if (Nextkey.vk == fabgl::VK_PAUSE) { + // click(); + // return; + // } else + // osdCenteredMsg(OSD_PAUSE[Config::lang], LEVEL_INFO, 500); + // } + // vTaskDelay(5 / portTICK_PERIOD_MS); + // } - zxDelay = REPDEL; + zxDelay = REPDEL; - while (1) { + while (1) { - if (ZXKeyb::Exists) { + if (ZXKeyb::Exists) { - ZXKeyb::process(); + ZXKeyb::process(); - if ((!bitRead(ZXKeyb::ZXcols[6], 0)) || (!bitRead(ZXKeyb::ZXcols[7], 0)) || (!bitRead(ZXKeyb::ZXcols[5], 0))) { // ENTER, BREAK, P - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAUSE, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAUSE, false, false); - zxDelay = REPDEL; + if ((!bitRead(ZXKeyb::ZXcols[6], 0)) || (!bitRead(ZXKeyb::ZXcols[7], 0)) || (!bitRead(ZXKeyb::ZXcols[5], 0))) { // ENTER, BREAK, P + if (zxDelay == 0) { + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAUSE, true, false); + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAUSE, false, false); + zxDelay = REPDEL; + } } + } - - } - ESPectrum::readKbdJoy(); + ESPectrum::readKbdJoy(); - if (ESPectrum::PS2Controller.keyboard()->virtualKeyAvailable()) { - if (ESPectrum::readKbd(&Nextkey)) { - if(!Nextkey.down) continue; - if (Nextkey.vk == fabgl::VK_PAUSE) { - click(); - break; - } else - osdCenteredMsg(OSD_PAUSE[Config::lang], LEVEL_INFO, 500); + if (ESPectrum::PS2Controller.keyboard()->virtualKeyAvailable()) { + if (ESPectrum::readKbd(&Nextkey)) { + if(!Nextkey.down) continue; + if (Nextkey.vk == fabgl::VK_PAUSE) { + click(); + break; + } else + osdCenteredMsg(OSD_PAUSE[Config::lang], LEVEL_INFO, 500); + } } - } - vTaskDelay(5 / portTICK_PERIOD_MS); + vTaskDelay(5 / portTICK_PERIOD_MS); - if (zxDelay > 0) zxDelay--; + if (zxDelay > 0) zxDelay--; - } + } - } - else if (KeytoESP == fabgl::VK_GRAVEACCENT) { // Show mem info + } + else if (KeytoESP == fabgl::VK_GRAVEACCENT) { // Show mem info - OSD::HWInfo(); + OSD::HWInfo(); - } - else if (KeytoESP == fabgl::VK_F2) { - menu_level = 0; - menu_curopt = 1; - string mFile = menuFile(FileUtils::MountPoint + FileUtils::SNA_Path, MENU_SNA_TITLE[Config::lang],".sna.SNA.z80.Z80", FileUtils::curSNAFile); - if (mFile != "") { - mFile.erase(0, 1); - string fname = FileUtils::MountPoint + FileUtils::SNA_Path + "/" + mFile; - LoadSnapshot(fname,""); - Config::ram_file = fname; - #ifdef SNAPSHOT_LOAD_LAST - Config::save("ram"); - #endif - Config::last_ram_file = fname; } - } - else if (KeytoESP == fabgl::VK_F3) { - menu_level = 0; - menu_curopt = 1; - // Persist Load - uint8_t opt2 = menuRun(MENU_PERSIST_LOAD[Config::lang]); - if (opt2 > 0 && opt2<11) { - persistLoad(opt2); + else if (KeytoESP == fabgl::VK_F2) { + menu_level = 0; + menu_saverect = false; + string mFile = menuFile(FileUtils::SNA_Path, MENU_SNA_TITLE[Config::lang],DISK_SNAFILE,51,22); + if (mFile != "") { + mFile.erase(0, 1); + string fname = FileUtils::MountPoint + "/" + FileUtils::SNA_Path + "/" + mFile; + LoadSnapshot(fname,""); + Config::ram_file = fname; + #ifdef SNAPSHOT_LOAD_LAST + Config::save("ram"); + #endif + Config::last_ram_file = fname; + } } - } - else if (KeytoESP == fabgl::VK_F4) { - menu_level = 0; - menu_curopt = 1; - // Persist Save - uint8_t opt2 = menuRun(MENU_PERSIST_SAVE[Config::lang]); - if (opt2 > 0 && opt2<11) { - persistSave(opt2); + else if (KeytoESP == fabgl::VK_F3) { + menu_level = 0; + menu_curopt = 1; + // Persist Load + uint8_t opt2 = menuRun(MENU_PERSIST_LOAD[Config::lang]); + if (opt2 > 0 && opt2<11) { + persistLoad(opt2); + } } - } - else if (KeytoESP == fabgl::VK_F5) { - menu_level = 0; - menu_curopt = 1; - string mFile = menuFile(FileUtils::MountPoint + FileUtils::TAP_Path, MENU_TAP_TITLE[Config::lang],".tap.TAP",FileUtils::curTAPFile); - if (mFile != "") { + else if (KeytoESP == fabgl::VK_F4) { + menu_level = 0; + menu_curopt = 1; + // Persist Save + uint8_t opt2 = menuRun(MENU_PERSIST_SAVE[Config::lang]); + if (opt2 > 0 && opt2<11) { + persistSave(opt2); + } + } + else if (KeytoESP == fabgl::VK_F5) { + menu_level = 0; + menu_saverect = false; + string mFile = menuFile(FileUtils::TAP_Path, MENU_TAP_TITLE[Config::lang],DISK_TAPFILE,51,22); + if (mFile != "") { - string keySel = mFile.substr(0,1); - mFile.erase(0, 1); + string keySel = mFile.substr(0,1); + mFile.erase(0, 1); - if ((keySel == "R") && (Config::flashload)) { + if ((keySel == "R") && (Config::flashload)) { - OSD::osdCenteredMsg(OSD_TAPE_FLASHLOAD, LEVEL_INFO, 0); + OSD::osdCenteredMsg(OSD_TAPE_FLASHLOAD, LEVEL_INFO, 0); - if (Z80Ops::is48) { - FileZ80::loader48(); - // changeSnapshot(FileUtils::MountPoint + "/load48.z80"); - } else { - FileZ80::loader128(); - // changeSnapshot(FileUtils::MountPoint + "/load128.z80"); - } + if (Z80Ops::is48) + FileZ80::loader48(); + else + FileZ80::loader128(); - // Put something random on FRAMES SYS VAR as recommended by Mark Woodmass - // https://skoolkid.github.io/rom/asm/5C78.html - MemESP::writebyte(0x5C78,rand() % 256); - MemESP::writebyte(0x5C79,rand() % 256); + // Put something random on FRAMES SYS VAR as recommended by Mark Woodmass + // https://skoolkid.github.io/rom/asm/5C78.html + MemESP::writebyte(0x5C78,rand() % 256); + MemESP::writebyte(0x5C79,rand() % 256); - if (Config::ram_file != NO_RAM_FILE) { - Config::ram_file = NO_RAM_FILE; - #ifdef SNAPSHOT_LOAD_LAST - Config::save("ram"); - #endif - } - Config::last_ram_file = NO_RAM_FILE; + if (Config::ram_file != NO_RAM_FILE) { + Config::ram_file = NO_RAM_FILE; + #ifdef SNAPSHOT_LOAD_LAST + Config::save("ram"); + #endif + } + Config::last_ram_file = NO_RAM_FILE; - } + } - Tape::TAP_Stop(); + Tape::TAP_Stop(); - // Read and analyze tape file - Tape::Open(FileUtils::MountPoint + FileUtils::TAP_Path + "/" + mFile); + // Read and analyze tape file + // Tape::Open(FileUtils::MountPoint + "/" + FileUtils::TAP_Path + "/" + mFile); + Tape::Open(mFile); - ESPectrum::TapeNameScroller = 0; + ESPectrum::TapeNameScroller = 0; - // Tape::tapeFileName=FileUtils::MountPoint + FileUtils::TAP_Path "/" + mFile; + } } + else if (KeytoESP == fabgl::VK_F6) { + // Start / Stop .tap reproduction + Tape::TAP_Play(); + click(); + } + else if (KeytoESP == fabgl::VK_F7) { + + // Tape Browser + if (Tape::tapeFileName=="none") { + OSD::osdCenteredMsg(OSD_TAPE_SELECT_ERR[Config::lang], LEVEL_WARN); + } else { + menu_level = 0; + menu_curopt = 1; + // int tBlock = menuTape(Tape::tapeFileName.substr(6,28)); + int tBlock = menuTape(Tape::tapeFileName.substr(0,22)); + if (tBlock >= 0) { + Tape::tapeCurBlock = tBlock; + Tape::TAP_Stop(); + } + } - } - else if (KeytoESP == fabgl::VK_F6) { - // Start / Stop .tap reproduction - Tape::TAP_Play(); - click(); - } - else if (KeytoESP == fabgl::VK_F7) { - - // Tape Browser - if (Tape::tapeFileName=="none") { - OSD::osdCenteredMsg(OSD_TAPE_SELECT_ERR[Config::lang], LEVEL_WARN); - } else { - menu_level = 0; - menu_curopt = 1; - int tBlock = menuTape(Tape::tapeFileName.substr(6,28)); - if (tBlock >= 0) { - Tape::tapeCurBlock = tBlock; - Tape::TAP_Stop(); + } + else if (KeytoESP == fabgl::VK_F8) { + // Show / hide OnScreen Stats + if (VIDEO::OSD) { + if (Config::aspect_16_9) + VIDEO::DrawOSD169 = Z80Ops::isPentagon ? VIDEO::MainScreen_Pentagon : VIDEO::MainScreen; + else + VIDEO::DrawOSD43 = Z80Ops::isPentagon ? VIDEO::BottomBorder_Pentagon : VIDEO::BottomBorder; + VIDEO::OSD = false; + } else { + if (Config::aspect_16_9) + VIDEO::DrawOSD169 = Z80Ops::isPentagon ? VIDEO::MainScreen_OSD_Pentagon : VIDEO::MainScreen_OSD; + else + VIDEO::DrawOSD43 = Z80Ops::isPentagon ? VIDEO::BottomBorder_OSD_Pentagon : VIDEO::BottomBorder_OSD; + VIDEO::OSD = true; + ESPectrum::TapeNameScroller = 0; + } + click(); + } + else if (KeytoESP == fabgl::VK_F9) { // Volume down + if (ESPectrum::aud_volume>-16) { + click(); + ESPectrum::aud_volume--; + pwm_audio_set_volume(ESPectrum::aud_volume); } + // osdCenteredMsg("Volume: " + to_string(ESPectrum::aud_volume + 16), LEVEL_INFO, 125); } - - } - else if (KeytoESP == fabgl::VK_F8) { - // Show / hide OnScreen Stats - if (VIDEO::OSD) { - if (Config::aspect_16_9) - VIDEO::DrawOSD169 = Z80Ops::isPentagon ? VIDEO::MainScreen_Pentagon : VIDEO::MainScreen; - else - VIDEO::DrawOSD43 = Z80Ops::isPentagon ? VIDEO::BottomBorder_Pentagon : VIDEO::BottomBorder; - VIDEO::OSD = false; - } else { - if (Config::aspect_16_9) - VIDEO::DrawOSD169 = Z80Ops::isPentagon ? VIDEO::MainScreen_OSD_Pentagon : VIDEO::MainScreen_OSD; - else - VIDEO::DrawOSD43 = Z80Ops::isPentagon ? VIDEO::BottomBorder_OSD_Pentagon : VIDEO::BottomBorder_OSD; - VIDEO::OSD = true; - ESPectrum::TapeNameScroller = 0; + else if (KeytoESP == fabgl::VK_F10) { // Volume up + if (ESPectrum::aud_volume<0) { + click(); + ESPectrum::aud_volume++; + pwm_audio_set_volume(ESPectrum::aud_volume); + } + // osdCenteredMsg("Volume: " + to_string(ESPectrum::aud_volume + 16), LEVEL_INFO, 125); } - click(); - } - else if (KeytoESP == fabgl::VK_F9) { // Volume down - if (ESPectrum::aud_volume>-16) { - click(); - ESPectrum::aud_volume--; - pwm_audio_set_volume(ESPectrum::aud_volume); - } - } - else if (KeytoESP == fabgl::VK_F10) { // Volume up - if (ESPectrum::aud_volume<0) { - click(); - ESPectrum::aud_volume++; - pwm_audio_set_volume(ESPectrum::aud_volume); + // else if (KeytoESP == fabgl::VK_F9) { + // ESPectrum::ESPoffset -= 5; + // } + // else if (KeytoESP == fabgl::VK_F10) { + // ESPectrum::ESPoffset += 5; + // } + else if (KeytoESP == fabgl::VK_F11) { // Hard reset + if (Config::ram_file != NO_RAM_FILE) { + Config::ram_file = NO_RAM_FILE; + #ifdef SNAPSHOT_LOAD_LAST + Config::save("ram"); + #endif + } + Config::last_ram_file = NO_RAM_FILE; + ESPectrum::reset(); } - } - // else if (KeytoESP == fabgl::VK_F9) { - // ESPectrum::ESPoffset -= 5; - // } - // else if (KeytoESP == fabgl::VK_F10) { - // ESPectrum::ESPoffset += 5; - // } - else if (KeytoESP == fabgl::VK_F11) { // Hard reset - if (Config::ram_file != NO_RAM_FILE) { + else if (KeytoESP == fabgl::VK_F12) { // ESP32 reset + // ESP host reset + #ifndef SNAPSHOT_LOAD_LAST Config::ram_file = NO_RAM_FILE; - #ifdef SNAPSHOT_LOAD_LAST Config::save("ram"); #endif + esp_hard_reset(); } - Config::last_ram_file = NO_RAM_FILE; - ESPectrum::reset(); - } - else if (KeytoESP == fabgl::VK_F12) { // ESP32 reset - // ESP host reset - #ifndef SNAPSHOT_LOAD_LAST - Config::ram_file = NO_RAM_FILE; - Config::save("ram"); - #endif - esp_hard_reset(); - } - else if (KeytoESP == fabgl::VK_F1) { + else if (KeytoESP == fabgl::VK_F1) { - menu_curopt = 1; - - while(1) { - - // Main menu - menu_level = 0; - uint8_t opt = menuRun("ESPectrum " + Config::getArch() + "\n" + MENU_MAIN[Config::lang]); - - if (opt == 1) { - // *********************************************************************************** - // SNAPSHOTS MENU - // *********************************************************************************** - menu_saverect = true; menu_curopt = 1; + while(1) { - menu_level = 1; - // Snapshot menu - uint8_t sna_mnu = menuRun(MENU_SNA[Config::lang]); - if (sna_mnu > 0) { - menu_level = 2; - menu_saverect = true; - if (sna_mnu == 1) { - menu_curopt = 1; - string mFile = menuFile(FileUtils::MountPoint + FileUtils::SNA_Path, MENU_SNA_TITLE[Config::lang],".sna.SNA.z80.Z80",FileUtils::curSNAFile); - if (mFile != "") { - mFile.erase(0, 1); - string fname = FileUtils::MountPoint + FileUtils::SNA_Path + "/" + mFile; - LoadSnapshot(fname,""); - Config::ram_file = fname; - #ifdef SNAPSHOT_LOAD_LAST - Config::save("ram"); - #endif - Config::last_ram_file = fname; - return; + + // Main menu + menu_saverect = false; + menu_level = 0; + uint8_t opt = menuRun("ESPectrum " + Config::getArch() + "\n" + MENU_MAIN[Config::lang]); + + if (opt == 1) { + // *********************************************************************************** + // SNAPSHOTS MENU + // *********************************************************************************** + menu_curopt = 1; + menu_saverect = true; + while(1) { + menu_level = 1; + // Snapshot menu + uint8_t sna_mnu = menuRun(MENU_SNA[Config::lang]); + if (sna_mnu > 0) { + menu_level = 2; + menu_saverect = true; + if (sna_mnu == 1) { + string mFile = menuFile(FileUtils::SNA_Path, MENU_SNA_TITLE[Config::lang],DISK_SNAFILE,30,16); + if (mFile != "") { + mFile.erase(0, 1); + string fname = FileUtils::MountPoint + "/" + FileUtils::SNA_Path + "/" + mFile; + LoadSnapshot(fname,""); + Config::ram_file = fname; + #ifdef SNAPSHOT_LOAD_LAST + Config::save("ram"); + #endif + Config::last_ram_file = fname; + return; + } } - } - else if (sna_mnu == 2) { - // Persist Load - menu_curopt = 1; - while (1) { - uint8_t opt2 = menuRun(MENU_PERSIST_LOAD[Config::lang]); - if (opt2 > 0 && opt2<11) { - if (persistLoad(opt2)) return; - menu_saverect = false; - menu_curopt = opt2; - } else break; + else if (sna_mnu == 2) { + // Persist Load + menu_curopt = 1; + menu_saverect = true; + while (1) { + uint8_t opt2 = menuRun(MENU_PERSIST_LOAD[Config::lang]); + if (opt2 > 0 && opt2<11) { + if (persistLoad(opt2)) return; + menu_saverect = false; + menu_curopt = opt2; + } else break; + } } - } - else if (sna_mnu == 3) { - // Persist Save - menu_curopt = 1; - while (1) { - uint8_t opt2 = menuRun(MENU_PERSIST_SAVE[Config::lang]); - if (opt2 > 0 && opt2<11) { - if (persistSave(opt2)) return; - menu_saverect = false; - menu_curopt = opt2; - } else break; + else if (sna_mnu == 3) { + // Persist Save + menu_curopt = 1; + menu_saverect = true; + while (1) { + uint8_t opt2 = menuRun(MENU_PERSIST_SAVE[Config::lang]); + if (opt2 > 0 && opt2<11) { + if (persistSave(opt2)) return; + menu_saverect = false; + menu_curopt = opt2; + } else break; + } } + menu_curopt = sna_mnu; + } else { + menu_curopt = 1; + break; } - menu_curopt = sna_mnu; - } else { - menu_curopt = 1; - break; } - } - } - else if (opt == 2) { - // *********************************************************************************** - // TAPE MENU - // *********************************************************************************** - menu_saverect = true; - menu_curopt = 1; - while(1) { - menu_level = 1; - // Tape menu - uint8_t tap_num = menuRun(MENU_TAPE[Config::lang]); - if (tap_num > 0) { - menu_level = 2; - menu_saverect = true; - if (tap_num == 1) { - menu_curopt = 1; - // Select TAP File - string mFile = menuFile(FileUtils::MountPoint + FileUtils::TAP_Path, MENU_TAP_TITLE[Config::lang],".tap.TAP",FileUtils::curTAPFile); - if (mFile != "") { - - string keySel = mFile.substr(0,1); - mFile.erase(0, 1); - - if ((keySel == "R") && (Config::flashload)) { - - OSD::osdCenteredMsg(OSD_TAPE_FLASHLOAD, LEVEL_INFO, 0); - - if (Z80Ops::is48) { - FileZ80::loader48(); - // changeSnapshot(FileUtils::MountPoint + "/load48.z80"); - } else { - FileZ80::loader128(); - // changeSnapshot(FileUtils::MountPoint + "/load128.z80"); - } - - // Put something random on FRAMES SYS VAR as recommended by Mark Woodmass - // https://skoolkid.github.io/rom/asm/5C78.html - MemESP::writebyte(0x5C78,rand() % 256); - MemESP::writebyte(0x5C79,rand() % 256); + } + else if (opt == 2) { + // *********************************************************************************** + // TAPE MENU + // *********************************************************************************** + menu_saverect = true; + menu_curopt = 1; + while(1) { + menu_level = 1; + // Tape menu + uint8_t tap_num = menuRun(MENU_TAPE[Config::lang]); + if (tap_num > 0) { + menu_level = 2; + menu_saverect = true; + if (tap_num == 1) { + // menu_curopt = 1; + // Select TAP File + string mFile = menuFile(FileUtils::TAP_Path, MENU_TAP_TITLE[Config::lang],DISK_TAPFILE,30,16); + if (mFile != "") { + + string keySel = mFile.substr(0,1); + mFile.erase(0, 1); + + if ((keySel == "R") && (Config::flashload)) { + + OSD::osdCenteredMsg(OSD_TAPE_FLASHLOAD, LEVEL_INFO, 0); + + if (Z80Ops::is48) { + FileZ80::loader48(); + // changeSnapshot(FileUtils::MountPoint + "/load48.z80"); + } else { + FileZ80::loader128(); + // changeSnapshot(FileUtils::MountPoint + "/load128.z80"); + } - if (Config::ram_file != NO_RAM_FILE) { - Config::ram_file = NO_RAM_FILE; - #ifdef SNAPSHOT_LOAD_LAST - Config::save("ram"); - #endif - } - Config::last_ram_file = NO_RAM_FILE; + // Put something random on FRAMES SYS VAR as recommended by Mark Woodmass + // https://skoolkid.github.io/rom/asm/5C78.html + MemESP::writebyte(0x5C78,rand() % 256); + MemESP::writebyte(0x5C79,rand() % 256); - } + if (Config::ram_file != NO_RAM_FILE) { + Config::ram_file = NO_RAM_FILE; + #ifdef SNAPSHOT_LOAD_LAST + Config::save("ram"); + #endif + } + Config::last_ram_file = NO_RAM_FILE; - Tape::TAP_Stop(); + } - // Read and analyze tape file - Tape::Open(FileUtils::MountPoint + FileUtils::TAP_Path + "/" + mFile); + Tape::TAP_Stop(); - ESPectrum::TapeNameScroller = 0; + // Read and analyze tape file + // Tape::Open(FileUtils::MountPoint + "/" + FileUtils::TAP_Path + "/" + mFile); + Tape::Open(mFile); + + ESPectrum::TapeNameScroller = 0; - return; + return; + } } - } - else if (tap_num == 2) { - // Start / Stop .tap reproduction - Tape::TAP_Play(); - return; - } - else if (tap_num == 3) { + else if (tap_num == 2) { + // Start / Stop .tap reproduction + Tape::TAP_Play(); + return; + } + else if (tap_num == 3) { - // Tape Browser - if (Tape::tapeFileName=="none") { - OSD::osdCenteredMsg(OSD_TAPE_SELECT_ERR[Config::lang], LEVEL_WARN); - menu_curopt = 2; - menu_saverect = false; - } else { - menu_level = 0; - menu_saverect = false; - menu_curopt = 1; - int tBlock = menuTape(Tape::tapeFileName.substr(6,28)); - if (tBlock >= 0) { - Tape::tapeCurBlock = tBlock; - Tape::TAP_Stop(); + // Tape Browser + if (Tape::tapeFileName=="none") { + OSD::osdCenteredMsg(OSD_TAPE_SELECT_ERR[Config::lang], LEVEL_WARN); + menu_curopt = 2; + menu_saverect = false; + } else { + menu_level = 0; + menu_saverect = false; + menu_curopt = 1; + // int tBlock = menuTape(Tape::tapeFileName.substr(6,28)); + int tBlock = menuTape(Tape::tapeFileName.substr(0,22)); + if (tBlock >= 0) { + Tape::tapeCurBlock = tBlock; + Tape::TAP_Stop(); + } + return; } - return; - } + } + } else { + menu_curopt = 2; + break; } - } else { - menu_curopt = 2; - break; } } - } - else if (opt == 3) { - // *********************************************************************************** - // BETADISK MENU - // *********************************************************************************** - menu_saverect = true; - menu_curopt = 1; - while(1) { - menu_level = 1; - // Betadisk menu - uint8_t dsk_num = menuRun(MENU_BETADISK[Config::lang]); - if (dsk_num > 0) { - menu_saverect = true; - menu_curopt = 1; - while (1) { - menu_level = 2; + else if (opt == 3) { + // *********************************************************************************** + // BETADISK MENU + // *********************************************************************************** + menu_saverect = true; + menu_curopt = 1; + while(1) { + // Betadisk menu + menu_level = 1; + uint8_t dsk_num = menuRun(MENU_BETADISK[Config::lang]); + if (dsk_num > 0) { string drvmenu = MENU_BETADRIVE[Config::lang]; drvmenu.replace(drvmenu.find("#",0),1,(string)" " + char(64 + dsk_num)); - uint8_t opt2 = menuRun(drvmenu); - if (opt2 > 0) { - menu_saverect = true; - menu_curopt = 1; - if (opt2 == 1) { - string mFile = menuFile(FileUtils::MountPoint + FileUtils::DSK_Path, MENU_DSK_TITLE[Config::lang],".trd.TRD.scl.SCL",FileUtils::curDSKFile); - if (mFile != "") { - mFile.erase(0, 1); - string fname = FileUtils::MountPoint + FileUtils::DSK_Path + "/" + mFile; + menu_saverect = true; + menu_curopt = 1; + while (1) { + menu_level = 2; + uint8_t opt2 = menuRun(drvmenu); + if (opt2 > 0) { + if (opt2 == 1) { + menu_saverect = true; + string mFile = menuFile(FileUtils::DSK_Path, MENU_DSK_TITLE[Config::lang],DISK_DSKFILE,30,16); + if (mFile != "") { + mFile.erase(0, 1); + string fname = FileUtils::MountPoint + "/" + FileUtils::DSK_Path + "/" + mFile; + ESPectrum::Betadisk.EjectDisk(dsk_num - 1); + ESPectrum::Betadisk.InsertDisk(dsk_num - 1,fname); + return; + } + } else + if (opt2 == 2) { ESPectrum::Betadisk.EjectDisk(dsk_num - 1); - ESPectrum::Betadisk.InsertDisk(dsk_num - 1,fname); return; } - } else - if (opt2 == 2) { - ESPectrum::Betadisk.EjectDisk(dsk_num - 1); - return; + } else { + menu_curopt = dsk_num; + break; } - } else { - menu_curopt = dsk_num; - break; } + } else { + menu_curopt = 3; + break; } - } else { - menu_curopt = 3; - break; } } - } - else if (opt == 4) { - // *********************************************************************************** - // RESET MENU - // *********************************************************************************** - menu_saverect = true; - menu_curopt = 1; - while(1) { - menu_level = 1; - // Reset - uint8_t opt2 = menuRun(MENU_RESET[Config::lang]); - if (opt2 == 1) { - // Soft - if (Config::last_ram_file != NO_RAM_FILE) { - LoadSnapshot(Config::last_ram_file,""); - Config::ram_file = Config::last_ram_file; - #ifdef SNAPSHOT_LOAD_LAST - Config::save("ram"); - #endif - } else ESPectrum::reset(); - return; - } - else if (opt2 == 2) { - // Hard - if (Config::ram_file != NO_RAM_FILE) { + else if (opt == 4) { + // *********************************************************************************** + // RESET MENU + // *********************************************************************************** + menu_saverect = true; + menu_curopt = 1; + while(1) { + menu_level = 1; + // Reset + uint8_t opt2 = menuRun(MENU_RESET[Config::lang]); + if (opt2 == 1) { + // Soft + if (Config::last_ram_file != NO_RAM_FILE) { + LoadSnapshot(Config::last_ram_file,""); + Config::ram_file = Config::last_ram_file; + #ifdef SNAPSHOT_LOAD_LAST + Config::save("ram"); + #endif + } else ESPectrum::reset(); + return; + } + else if (opt2 == 2) { + // Hard + if (Config::ram_file != NO_RAM_FILE) { + Config::ram_file = NO_RAM_FILE; + #ifdef SNAPSHOT_LOAD_LAST + Config::save("ram"); + #endif + } + Config::last_ram_file = NO_RAM_FILE; + ESPectrum::reset(); + return; + } + else if (opt2 == 3) { + // ESP host reset + #ifndef SNAPSHOT_LOAD_LAST Config::ram_file = NO_RAM_FILE; - #ifdef SNAPSHOT_LOAD_LAST Config::save("ram"); #endif + esp_hard_reset(); + } else { + menu_curopt = 4; + break; } - Config::last_ram_file = NO_RAM_FILE; - ESPectrum::reset(); - return; - } - else if (opt2 == 3) { - // ESP host reset - #ifndef SNAPSHOT_LOAD_LAST - Config::ram_file = NO_RAM_FILE; - Config::save("ram"); - #endif - esp_hard_reset(); - } else { - menu_curopt = 4; - break; } } - } - else if (opt == 5) { - // *********************************************************************************** - // OPTIONS MENU - // *********************************************************************************** - menu_saverect = true; - menu_curopt = 1; - while(1) { - menu_level = 1; - // Options menu - uint8_t options_num = menuRun(MENU_OPTIONS[Config::lang]); - if (options_num == 1) { - menu_saverect = true; - menu_curopt = 1; - while (1) { - menu_level = 2; - // Storage source - // string stor_menu = MENU_STORAGE[Config::lang]; - string stor_menu = Config::lang ? MENU_STORAGE_ES : MENU_STORAGE_EN; - uint8_t opt2 = menuRun(stor_menu); - if (opt2) { - if (opt2 == 1) { - menu_level = 3; - menu_curopt = 1; - menu_saverect = true; - while (1) { - string flash_menu = Config::lang ? MENU_FLASHLOAD_ES : MENU_FLASHLOAD_EN; - bool prev_flashload = Config::flashload; - if (prev_flashload) { - flash_menu.replace(flash_menu.find("[Y",0),2,"[*"); - flash_menu.replace(flash_menu.find("[N",0),2,"[ "); - } else { - flash_menu.replace(flash_menu.find("[Y",0),2,"[ "); - flash_menu.replace(flash_menu.find("[N",0),2,"[*"); - } - uint8_t opt2 = menuRun(flash_menu); - if (opt2) { - if (opt2 == 1) - Config::flashload = true; - else - Config::flashload = false; - - if (Config::flashload != prev_flashload) { - Config::save("flashload"); + else if (opt == 5) { + // *********************************************************************************** + // OPTIONS MENU + // *********************************************************************************** + menu_saverect = true; + menu_curopt = 1; + while(1) { + menu_level = 1; + // Options menu + uint8_t options_num = menuRun(MENU_OPTIONS[Config::lang]); + if (options_num == 1) { + menu_saverect = true; + menu_curopt = 1; + while (1) { + menu_level = 2; + // Storage source + // string stor_menu = MENU_STORAGE[Config::lang]; + string stor_menu = Config::lang ? MENU_STORAGE_ES : MENU_STORAGE_EN; + uint8_t opt2 = menuRun(stor_menu); + if (opt2) { + if (opt2 == 1) { + menu_level = 3; + menu_curopt = 1; + menu_saverect = true; + while (1) { + string flash_menu = Config::lang ? MENU_FLASHLOAD_ES : MENU_FLASHLOAD_EN; + bool prev_flashload = Config::flashload; + if (prev_flashload) { + flash_menu.replace(flash_menu.find("[Y",0),2,"[*"); + flash_menu.replace(flash_menu.find("[N",0),2,"[ "); + } else { + flash_menu.replace(flash_menu.find("[Y",0),2,"[ "); + flash_menu.replace(flash_menu.find("[N",0),2,"[*"); + } + uint8_t opt2 = menuRun(flash_menu); + if (opt2) { + if (opt2 == 1) + Config::flashload = true; + else + Config::flashload = false; + + if (Config::flashload != prev_flashload) { + Config::save("flashload"); + } + menu_curopt = opt2; + menu_saverect = false; + } else { + menu_curopt = 1; + menu_level = 2; + break; } - menu_curopt = opt2; - menu_saverect = false; - } else { - menu_curopt = 1; - menu_level = 2; - break; } } + // else + // if (opt2 == 2) { + + // OSD::osdCenteredMsg("Refreshing snap dir", LEVEL_INFO, 0); + // FileUtils::DirToFile(FileUtils::MountPoint + "/" + FileUtils::SNA_Path, DISK_SNAFILE); // Prepare sna filelist + + // OSD::osdCenteredMsg("Refreshing tape dir", LEVEL_INFO, 0); + // FileUtils::DirToFile(FileUtils::MountPoint + "/" + FileUtils::TAP_Path, DISK_TAPFILE); // Prepare tap filelist + + // OSD::osdCenteredMsg("Refreshing disk dir", LEVEL_INFO, 0); + // FileUtils::DirToFile(FileUtils::MountPoint + "/" + FileUtils::DSK_Path, DISK_DSKFILE); // Prepare dsk filelist + + // return; + // } + menu_curopt = opt2; + menu_saverect = false; + } else { + menu_curopt = 1; + break; } - else - if (opt2 == 2) { - OSD::osdCenteredMsg("Refreshing snap dir", LEVEL_INFO, 0); - int chunks = FileUtils::DirToFile(FileUtils::MountPoint + FileUtils::SNA_Path, ".sna.SNA.z80.Z80"); // Prepare sna filelist - if (chunks) FileUtils::Mergefiles(FileUtils::MountPoint + FileUtils::SNA_Path,chunks); // Merge files - OSD::osdCenteredMsg("Refreshing tape dir", LEVEL_INFO, 0); - chunks = FileUtils::DirToFile(FileUtils::MountPoint + FileUtils::TAP_Path, ".tap.TAP"); // Prepare tap filelist - if (chunks) FileUtils::Mergefiles(FileUtils::MountPoint + FileUtils::TAP_Path,chunks); // Merge files - OSD::osdCenteredMsg("Refreshing disk dir", LEVEL_INFO, 0); - chunks = FileUtils::DirToFile(FileUtils::MountPoint + FileUtils::DSK_Path, ".trd.TRD.scl.SCL"); // Prepare dsk filelist - if (chunks) FileUtils::Mergefiles(FileUtils::MountPoint + FileUtils::DSK_Path,chunks); // Merge files - return; - } - menu_curopt = opt2; - menu_saverect = false; - } else { - menu_curopt = 1; - break; } } - } - else if (options_num == 2) { - menu_level = 2; - menu_curopt = 1; - menu_saverect = true; - // Change ROM - string arch_menu = (string)MENU_ARCH[Config::lang]; - uint8_t arch_num = menuRun(arch_menu); - if (arch_num) { - // string arch = (arch_num==1 ? "48K" : "128K"); - string arch = Config::archnames[arch_num - 1]; - if (arch != Config::getArch()) { - Config::requestMachine(arch, "SINCLAIR"); - Config::ram_file = "none"; - Config::save(); - if(Config::videomode) esp_hard_reset(); - } - ESPectrum::reset(); - return; - } - menu_curopt = 2; - } - else if (options_num == 3) { - menu_level = 2; - menu_curopt = 1; - menu_saverect = true; - while (1) { - // aspect ratio - string asp_menu = MENU_ASPECT[Config::lang]; - bool prev_asp = Config::aspect_16_9; - if (prev_asp) { - asp_menu.replace(asp_menu.find("[4",0),2,"[ "); - asp_menu.replace(asp_menu.find("[1",0),2,"[*"); - } else { - asp_menu.replace(asp_menu.find("[4",0),2,"[*"); - asp_menu.replace(asp_menu.find("[1",0),2,"[ "); + else if (options_num == 2) { + menu_level = 2; + menu_curopt = 1; + menu_saverect = true; + // Change ROM + string arch_menu = (string)MENU_ARCH[Config::lang]; + uint8_t arch_num = menuRun(arch_menu); + if (arch_num) { + // string arch = (arch_num==1 ? "48K" : "128K"); + string arch = Config::archnames[arch_num - 1]; + if (arch != Config::getArch()) { + Config::requestMachine(arch, "SINCLAIR"); + Config::ram_file = "none"; + Config::save("ram"); + Config::save("arch"); + if(Config::videomode) esp_hard_reset(); + } + ESPectrum::reset(); + return; } - uint8_t opt2 = menuRun(asp_menu); - if (opt2) { - if (opt2 == 1) - Config::aspect_16_9 = false; - else - Config::aspect_16_9 = true; - - if (Config::aspect_16_9 != prev_asp) { - #ifndef SNAPSHOT_LOAD_LAST - Config::ram_file = "none"; - #endif - Config::save(); - esp_hard_reset(); + menu_curopt = 2; + } + else if (options_num == 3) { + menu_level = 2; + menu_curopt = 1; + menu_saverect = true; + while (1) { + // aspect ratio + string asp_menu = MENU_ASPECT[Config::lang]; + bool prev_asp = Config::aspect_16_9; + if (prev_asp) { + asp_menu.replace(asp_menu.find("[4",0),2,"[ "); + asp_menu.replace(asp_menu.find("[1",0),2,"[*"); + } else { + asp_menu.replace(asp_menu.find("[4",0),2,"[*"); + asp_menu.replace(asp_menu.find("[1",0),2,"[ "); } + uint8_t opt2 = menuRun(asp_menu); + if (opt2) { + if (opt2 == 1) + Config::aspect_16_9 = false; + else + Config::aspect_16_9 = true; + + if (Config::aspect_16_9 != prev_asp) { + #ifndef SNAPSHOT_LOAD_LAST + Config::ram_file = "none"; + #endif + Config::save("asp169"); + Config::save("ram"); + esp_hard_reset(); + } - menu_curopt = opt2; - menu_saverect = false; + menu_curopt = opt2; + menu_saverect = false; - } else { - menu_curopt = 3; - break; - } - } - } - else if (options_num == 4) { - menu_level = 2; - menu_curopt = 1; - menu_saverect = true; - while (1) { - // joystick - string Mnustr = MENU_JOY[Config::lang]; - std::size_t pos = Mnustr.find("[",0); - int nfind = 0; - while (pos != string::npos) { - if (nfind == Config::joystick) { - Mnustr.replace(pos,2,"[*"); + } else { + menu_curopt = 3; break; } - pos = Mnustr.find("[",pos + 1); - nfind++; } - uint8_t opt2 = menuRun(Mnustr); - if (opt2 > 0 && opt2 < 3) { - if (Config::joystick != (opt2 - 1)) { - Config::joystick = opt2 - 1; - Config::save("joystick"); + } + else if (options_num == 4) { + menu_level = 2; + menu_curopt = 1; + menu_saverect = true; + while (1) { + // joystick + string Mnustr = MENU_JOY[Config::lang]; + std::size_t pos = Mnustr.find("[",0); + int nfind = 0; + while (pos != string::npos) { + if (nfind == Config::joystick) { + Mnustr.replace(pos,2,"[*"); + break; + } + pos = Mnustr.find("[",pos + 1); + nfind++; } - menu_curopt = opt2; - menu_saverect = false; - } else { - if (opt2) { - // Menu cursor keys as joy - menu_level = 3; - menu_curopt = 1; - menu_saverect = true; - while (1) { - string csasjoy_menu = MENU_CURSORJOY[Config::lang]; - if (Config::CursorAsJoy) { - csasjoy_menu.replace(csasjoy_menu.find("[Y",0),2,"[*"); - csasjoy_menu.replace(csasjoy_menu.find("[N",0),2,"[ "); - } else { - csasjoy_menu.replace(csasjoy_menu.find("[Y",0),2,"[ "); - csasjoy_menu.replace(csasjoy_menu.find("[N",0),2,"[*"); - } - uint8_t opt2 = menuRun(csasjoy_menu); - if (opt2) { - if (opt2 == 1) - Config::CursorAsJoy = true; - else - Config::CursorAsJoy = false; - - ESPectrum::PS2Controller.keyboard()->setLEDs(false,false,Config::CursorAsJoy); - if(ESPectrum::ps2kbd2) - ESPectrum::PS2Controller.keybjoystick()->setLEDs(false, false, Config::CursorAsJoy); - Config::save("CursorAsJoy"); - - menu_curopt = opt2; - menu_saverect = false; - } else { - menu_curopt = 3; - menu_level = 2; - break; - } + uint8_t opt2 = menuRun(Mnustr); + if (opt2 > 0 && opt2 < 3) { + if (Config::joystick != (opt2 - 1)) { + Config::joystick = opt2 - 1; + Config::save("joystick"); } - + menu_curopt = opt2; + menu_saverect = false; } else { - menu_curopt = 4; - break; + if (opt2) { + // Menu cursor keys as joy + menu_level = 3; + menu_curopt = 1; + menu_saverect = true; + while (1) { + string csasjoy_menu = MENU_CURSORJOY[Config::lang]; + if (Config::CursorAsJoy) { + csasjoy_menu.replace(csasjoy_menu.find("[Y",0),2,"[*"); + csasjoy_menu.replace(csasjoy_menu.find("[N",0),2,"[ "); + } else { + csasjoy_menu.replace(csasjoy_menu.find("[Y",0),2,"[ "); + csasjoy_menu.replace(csasjoy_menu.find("[N",0),2,"[*"); + } + uint8_t opt2 = menuRun(csasjoy_menu); + if (opt2) { + if (opt2 == 1) + Config::CursorAsJoy = true; + else + Config::CursorAsJoy = false; + + ESPectrum::PS2Controller.keyboard()->setLEDs(false,false,Config::CursorAsJoy); + if(ESPectrum::ps2kbd2) + ESPectrum::PS2Controller.keybjoystick()->setLEDs(false, false, Config::CursorAsJoy); + Config::save("CursorAsJoy"); + + menu_curopt = opt2; + menu_saverect = false; + } else { + menu_curopt = 3; + menu_level = 2; + break; + } + } + + } else { + menu_curopt = 4; + break; + } } } } - } - else if (options_num == 5) { - menu_level = 2; - menu_curopt = 1; - menu_saverect = true; - while (1) { - // language - uint8_t opt2; - string Mnustr = MENU_INTERFACE_LANG[Config::lang]; - std::size_t pos = Mnustr.find("[",0); - int nfind = 0; - while (pos != string::npos) { - if (nfind == Config::lang) { - Mnustr.replace(pos,2,"[*"); - break; + else if (options_num == 5) { + menu_level = 2; + menu_curopt = 1; + menu_saverect = true; + while (1) { + // language + uint8_t opt2; + string Mnustr = MENU_INTERFACE_LANG[Config::lang]; + std::size_t pos = Mnustr.find("[",0); + int nfind = 0; + while (pos != string::npos) { + if (nfind == Config::lang) { + Mnustr.replace(pos,2,"[*"); + break; + } + pos = Mnustr.find("[",pos + 1); + nfind++; } - pos = Mnustr.find("[",pos + 1); - nfind++; - } - opt2 = menuRun(Mnustr); - if (opt2) { - if (Config::lang != (opt2 - 1)) { - Config::lang = opt2 - 1; - Config::save("language"); - return; + opt2 = menuRun(Mnustr); + if (opt2) { + if (Config::lang != (opt2 - 1)) { + Config::lang = opt2 - 1; + Config::save("language"); + return; + } + menu_curopt = opt2; + menu_saverect = false; + } else { + menu_curopt = 5; + break; } - menu_curopt = opt2; - menu_saverect = false; - } else { - menu_curopt = 5; - break; } } - } - else if (options_num == 6) { - menu_level = 2; - menu_curopt = 1; - menu_saverect = true; - while (1) { - // Other - uint8_t options_num = menuRun(MENU_OTHER[Config::lang]); - if (options_num > 0) { - if (options_num == 1) { - menu_level = 3; - menu_curopt = 1; - menu_saverect = true; - while (1) { - string ay_menu = MENU_AY48[Config::lang]; - bool prev_ay48 = Config::AY48; - if (prev_ay48) { - ay_menu.replace(ay_menu.find("[Y",0),2,"[*"); - ay_menu.replace(ay_menu.find("[N",0),2,"[ "); - } else { - ay_menu.replace(ay_menu.find("[Y",0),2,"[ "); - ay_menu.replace(ay_menu.find("[N",0),2,"[*"); - } - uint8_t opt2 = menuRun(ay_menu); - if (opt2) { - if (opt2 == 1) - Config::AY48 = true; - else - Config::AY48 = false; - - if (Config::AY48 != prev_ay48) { - Config::save("AY48"); + else if (options_num == 6) { + menu_level = 2; + menu_curopt = 1; + menu_saverect = true; + while (1) { + // Other + uint8_t options_num = menuRun(MENU_OTHER[Config::lang]); + if (options_num > 0) { + if (options_num == 1) { + menu_level = 3; + menu_curopt = 1; + menu_saverect = true; + while (1) { + string ay_menu = MENU_AY48[Config::lang]; + bool prev_ay48 = Config::AY48; + if (prev_ay48) { + ay_menu.replace(ay_menu.find("[Y",0),2,"[*"); + ay_menu.replace(ay_menu.find("[N",0),2,"[ "); + } else { + ay_menu.replace(ay_menu.find("[Y",0),2,"[ "); + ay_menu.replace(ay_menu.find("[N",0),2,"[*"); + } + uint8_t opt2 = menuRun(ay_menu); + if (opt2) { + if (opt2 == 1) + Config::AY48 = true; + else + Config::AY48 = false; + + if (Config::AY48 != prev_ay48) { + Config::save("AY48"); + } + menu_curopt = opt2; + menu_saverect = false; + } else { + menu_curopt = 1; + menu_level = 2; + break; } - menu_curopt = opt2; - menu_saverect = false; - } else { - menu_curopt = 1; - menu_level = 2; - break; } } - } - else if (options_num == 2) { - menu_level = 3; - menu_curopt = 1; - menu_saverect = true; - while (1) { - string alu_menu = MENU_ALUTIMING[Config::lang]; - uint8_t prev_AluTiming = Config::AluTiming; - if (prev_AluTiming == 0) { - alu_menu.replace(alu_menu.find("[E",0),2,"[*"); - alu_menu.replace(alu_menu.find("[L",0),2,"[ "); - } else { - alu_menu.replace(alu_menu.find("[E",0),2,"[ "); - alu_menu.replace(alu_menu.find("[L",0),2,"[*"); - } - uint8_t opt2 = menuRun(alu_menu); - if (opt2) { - if (opt2 == 1) - Config::AluTiming = 0; - else - Config::AluTiming = 1; - - if (Config::AluTiming != prev_AluTiming) { - CPU::latetiming = Config::AluTiming; - Config::save("AluTiming"); + else if (options_num == 2) { + menu_level = 3; + menu_curopt = 1; + menu_saverect = true; + while (1) { + string alu_menu = MENU_ALUTIMING[Config::lang]; + uint8_t prev_AluTiming = Config::AluTiming; + if (prev_AluTiming == 0) { + alu_menu.replace(alu_menu.find("[E",0),2,"[*"); + alu_menu.replace(alu_menu.find("[L",0),2,"[ "); + } else { + alu_menu.replace(alu_menu.find("[E",0),2,"[ "); + alu_menu.replace(alu_menu.find("[L",0),2,"[*"); + } + uint8_t opt2 = menuRun(alu_menu); + if (opt2) { + if (opt2 == 1) + Config::AluTiming = 0; + else + Config::AluTiming = 1; + + if (Config::AluTiming != prev_AluTiming) { + CPU::latetiming = Config::AluTiming; + Config::save("AluTiming"); + } + menu_curopt = opt2; + menu_saverect = false; + } else { + menu_curopt = 2; + menu_level = 2; + break; } - menu_curopt = opt2; - menu_saverect = false; - } else { - menu_curopt = 2; - menu_level = 2; - break; } } - } - else if (options_num == 3) { - menu_level = 3; - menu_curopt = 1; - menu_saverect = true; - while (1) { - string iss_menu = MENU_ISSUE2[Config::lang]; - bool prev_iss = Config::Issue2; - if (prev_iss) { - iss_menu.replace(iss_menu.find("[Y",0),2,"[*"); - iss_menu.replace(iss_menu.find("[N",0),2,"[ "); - } else { - iss_menu.replace(iss_menu.find("[Y",0),2,"[ "); - iss_menu.replace(iss_menu.find("[N",0),2,"[*"); - } - uint8_t opt2 = menuRun(iss_menu); - if (opt2) { - if (opt2 == 1) - Config::Issue2 = true; - else - Config::Issue2 = false; - - if (Config::Issue2 != prev_iss) { - Config::save("Issue2"); + else if (options_num == 3) { + menu_level = 3; + menu_curopt = 1; + menu_saverect = true; + while (1) { + string iss_menu = MENU_ISSUE2[Config::lang]; + bool prev_iss = Config::Issue2; + if (prev_iss) { + iss_menu.replace(iss_menu.find("[Y",0),2,"[*"); + iss_menu.replace(iss_menu.find("[N",0),2,"[ "); + } else { + iss_menu.replace(iss_menu.find("[Y",0),2,"[ "); + iss_menu.replace(iss_menu.find("[N",0),2,"[*"); + } + uint8_t opt2 = menuRun(iss_menu); + if (opt2) { + if (opt2 == 1) + Config::Issue2 = true; + else + Config::Issue2 = false; + + if (Config::Issue2 != prev_iss) { + Config::save("Issue2"); + } + menu_curopt = opt2; + menu_saverect = false; + } else { + menu_curopt = 3; + menu_level = 2; + break; } - menu_curopt = opt2; - menu_saverect = false; - } else { - menu_curopt = 3; - menu_level = 2; - break; } } - } - else if (options_num == 4) { - menu_level = 3; - menu_curopt = 1; - menu_saverect = true; - while (1) { - string ps2_menu = MENU_KBD2NDPS2[Config::lang]; - uint8_t prev_ps2 = Config::ps2_dev2; - if (prev_ps2) { - ps2_menu.replace(ps2_menu.find("[N",0),2,"[ "); - ps2_menu.replace(ps2_menu.find("[K",0),2,"[*"); - } else { - ps2_menu.replace(ps2_menu.find("[N",0),2,"[*"); - ps2_menu.replace(ps2_menu.find("[K",0),2,"[ "); - } - uint8_t opt2 = menuRun(ps2_menu); - if (opt2) { - if (opt2 == 1) - Config::ps2_dev2 = 0; - else - Config::ps2_dev2 = 1; - - if (Config::ps2_dev2 != prev_ps2) { - Config::save("PS2Dev2"); + else if (options_num == 4) { + menu_level = 3; + menu_curopt = 1; + menu_saverect = true; + while (1) { + string ps2_menu = MENU_KBD2NDPS2[Config::lang]; + uint8_t prev_ps2 = Config::ps2_dev2; + if (prev_ps2) { + ps2_menu.replace(ps2_menu.find("[N",0),2,"[ "); + ps2_menu.replace(ps2_menu.find("[K",0),2,"[*"); + } else { + ps2_menu.replace(ps2_menu.find("[N",0),2,"[*"); + ps2_menu.replace(ps2_menu.find("[K",0),2,"[ "); + } + uint8_t opt2 = menuRun(ps2_menu); + if (opt2) { + if (opt2 == 1) + Config::ps2_dev2 = 0; + else + Config::ps2_dev2 = 1; + + if (Config::ps2_dev2 != prev_ps2) { + Config::save("PS2Dev2"); + } + menu_curopt = opt2; + menu_saverect = false; + } else { + menu_curopt = 4; + menu_level = 2; + break; } - menu_curopt = opt2; - menu_saverect = false; - } else { - menu_curopt = 4; - menu_level = 2; - break; } } + } else { + menu_curopt = 6; + break; } + } + } else if (options_num == 7) { + // Open firmware file + FILE *firmware = fopen("/sd/firmware.bin", "rb"); + if (firmware == NULL) { + osdCenteredMsg("No firmware file found.", LEVEL_WARN, 2000); + return; } else { - menu_curopt = 6; - break; + esp_err_t res = updateFirmware(firmware); + fclose(firmware); + string errMsg = OSD_FIRMW_ERR[Config::lang]; + errMsg += " Code = " + to_string(res); + osdCenteredMsg(errMsg, LEVEL_ERROR, 3000); } - } - } else if (options_num == 7) { - // Open firmware file - FILE *firmware = fopen("/sd/firmware.bin", "rb"); - if (firmware == NULL) { - osdCenteredMsg("No firmware file found.", LEVEL_WARN, 2000); return; } else { - esp_err_t res = updateFirmware(firmware); - fclose(firmware); - string errMsg = OSD_FIRMW_ERR[Config::lang]; - errMsg += " Code = " + to_string(res); - osdCenteredMsg(errMsg, LEVEL_ERROR, 3000); + menu_curopt = 5; + break; } - return; - } else { - menu_curopt = 5; - break; } } - } - else if (opt == 6) { - // Help - drawOSD(true); - osdAt(2, 0); - VIDEO::vga.setTextColor(OSD::zxColor(7, 0), OSD::zxColor(1, 0)); - if (ZXKeyb::Exists) - VIDEO::vga.print(Config::lang ? OSD_HELP_ES_ZX : OSD_HELP_EN_ZX); - else - VIDEO::vga.print(Config::lang ? OSD_HELP_ES : OSD_HELP_EN); + else if (opt == 6) { + // Help + drawOSD(true); + osdAt(2, 0); + VIDEO::vga.setTextColor(OSD::zxColor(7, 0), OSD::zxColor(1, 0)); + if (ZXKeyb::Exists) + VIDEO::vga.print(Config::lang ? OSD_HELP_ES_ZX : OSD_HELP_EN_ZX); + else + VIDEO::vga.print(Config::lang ? OSD_HELP_ES : OSD_HELP_EN); - zxDelay = REPDEL; + zxDelay = REPDEL; - while (1) { + while (1) { - if (ZXKeyb::Exists) { + if (ZXKeyb::Exists) { - ZXKeyb::process(); + ZXKeyb::process(); - if ((!bitRead(ZXKeyb::ZXcols[6], 0)) || (!bitRead(ZXKeyb::ZXcols[7], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 1))) { // ENTER, BREAK, 0, 9 - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, false, false); - zxDelay = REPDEL; + if ((!bitRead(ZXKeyb::ZXcols[6], 0)) || (!bitRead(ZXKeyb::ZXcols[7], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 1))) { // ENTER, BREAK, 0, 9 + if (zxDelay == 0) { + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, true, false); + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, false, false); + zxDelay = REPDEL; + } } + } - - } - ESPectrum::readKbdJoy(); + ESPectrum::readKbdJoy(); - if (ESPectrum::PS2Controller.keyboard()->virtualKeyAvailable()) { - if (ESPectrum::readKbd(&Nextkey)) { - if(!Nextkey.down) continue; - if ((Nextkey.vk == fabgl::VK_F1) || (Nextkey.vk == fabgl::VK_ESCAPE) || (Nextkey.vk == fabgl::VK_RETURN)) break; + if (ESPectrum::PS2Controller.keyboard()->virtualKeyAvailable()) { + if (ESPectrum::readKbd(&Nextkey)) { + if(!Nextkey.down) continue; + if ((Nextkey.vk == fabgl::VK_F1) || (Nextkey.vk == fabgl::VK_ESCAPE) || (Nextkey.vk == fabgl::VK_RETURN)) break; + } } - } - - vTaskDelay(5 / portTICK_PERIOD_MS); - - if (zxDelay > 0) zxDelay--; - - } - click(); + vTaskDelay(5 / portTICK_PERIOD_MS); - return; + if (zxDelay > 0) zxDelay--; - } - else if (opt == 7) { + } - // About - drawOSD(false); - - VIDEO::vga.fillRect(Config::aspect_16_9 ? 60 : 40,Config::aspect_16_9 ? 12 : 32,240,50,OSD::zxColor(0, 0)); - - // Decode Logo in EBF8 format - uint8_t *logo = (uint8_t *)ESPectrum_logo; - int pos_x = Config::aspect_16_9 ? 86 : 66; - int pos_y = Config::aspect_16_9 ? 23 : 43; - int logo_w = (logo[5] << 8) + logo[4]; // Get Width - int logo_h = (logo[7] << 8) + logo[6]; // Get Height - logo+=8; // Skip header - for (int i=0; i < logo_h; i++) - for(int n=0; n= 'A') ? (fore - 'A' + 10) : (fore - '0'); - int backint = (back >= 'A') ? (back - 'A' + 10) : (back - '0'); - VIDEO::vga.setTextColor(zxColor(foreint & 0x7, foreint >> 3), zxColor(backint & 0x7, backint >> 3)); + // About + drawOSD(false); + + VIDEO::vga.fillRect(Config::aspect_16_9 ? 60 : 40,Config::aspect_16_9 ? 12 : 32,240,50,OSD::zxColor(0, 0)); + + // Decode Logo in EBF8 format + uint8_t *logo = (uint8_t *)ESPectrum_logo; + int pos_x = Config::aspect_16_9 ? 86 : 66; + int pos_y = Config::aspect_16_9 ? 23 : 43; + int logo_w = (logo[5] << 8) + logo[4]; // Get Width + int logo_h = (logo[7] << 8) + logo[6]; // Get Height + logo+=8; // Skip header + for (int i=0; i < logo_h; i++) + for(int n=0; n= 'A') ? (fore - 'A' + 10) : (fore - '0'); + int backint = (back >= 'A') ? (back - 'A' + 10) : (back - '0'); + VIDEO::vga.setTextColor(zxColor(foreint & 0x7, foreint >> 3), zxColor(backint & 0x7, backint >> 3)); + msgChar++; + continue; + } else { + VIDEO::vga.drawChar(pos_x + (osdCol * 6), pos_y + (osdRow * 8), nextChar); + } msgChar++; - continue; } else { - VIDEO::vga.drawChar(pos_x + (osdCol * 6), pos_y + (osdRow * 8), nextChar); + VIDEO::vga.fillRect(pos_x + (osdCol * 6), pos_y + (osdRow * 8), 6,8, zxColor(1, 0) ); + } + osdCol++; + if (osdCol == 38) { + if (osdRow == 12) { + osdCol--; + msgDelay = 192; + } else { + VIDEO::vga.fillRect(pos_x + (osdCol * 6), pos_y + (osdRow * 8), 6,8, zxColor(1, 0) ); + osdCol = 0; + msgChar++; + osdRow++; + } } - msgChar++; } else { - VIDEO::vga.fillRect(pos_x + (osdCol * 6), pos_y + (osdRow * 8), 6,8, zxColor(1, 0) ); - } - osdCol++; - if (osdCol == 38) { - if (osdRow == 12) { - osdCol--; - msgDelay = 192; - } else { - VIDEO::vga.fillRect(pos_x + (osdCol * 6), pos_y + (osdRow * 8), 6,8, zxColor(1, 0) ); + msgDelay--; + if (msgDelay==0) { + VIDEO::vga.fillRect(Config::aspect_16_9 ? 60 : 40,Config::aspect_16_9 ? 64 : 84,240,114,OSD::zxColor(1, 0)); osdCol = 0; - msgChar++; - osdRow++; + osdRow = 0; + msgChar = 0; + msgIndex++; + if (msgIndex==6) msgIndex = 0; } } - } else { - msgDelay--; - if (msgDelay==0) { - VIDEO::vga.fillRect(Config::aspect_16_9 ? 60 : 40,Config::aspect_16_9 ? 64 : 84,240,114,OSD::zxColor(1, 0)); - osdCol = 0; - osdRow = 0; - msgChar = 0; - msgIndex++; - if (msgIndex==6) msgIndex = 0; + + if (--cursorBlink == 0) { + uint16_t cursorSwap = cursorCol; + cursorCol = cursorCol2; + cursorCol2 = cursorSwap; + cursorBlink = 16; } - } - if (--cursorBlink == 0) { - uint16_t cursorSwap = cursorCol; - cursorCol = cursorCol2; - cursorCol2 = cursorSwap; - cursorBlink = 16; - } + VIDEO::vga.fillRect(pos_x + ((osdCol + 1) * 6), pos_y + (osdRow * 8), 6,8, cursorCol ); + + if (ZXKeyb::Exists) { - VIDEO::vga.fillRect(pos_x + ((osdCol + 1) * 6), pos_y + (osdRow * 8), 6,8, cursorCol ); - - if (ZXKeyb::Exists) { + ZXKeyb::process(); - ZXKeyb::process(); - - if ((!bitRead(ZXKeyb::ZXcols[6], 0)) || (!bitRead(ZXKeyb::ZXcols[7], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 1))) { // ENTER, BREAK, 0, 9 - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, false, false); - zxDelay = REPABOUT; + if ((!bitRead(ZXKeyb::ZXcols[6], 0)) || (!bitRead(ZXKeyb::ZXcols[7], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 1))) { // ENTER, BREAK, 0, 9 + if (zxDelay == 0) { + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, true, false); + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, false, false); + zxDelay = REPABOUT; + } } - } - } + } - ESPectrum::readKbdJoy(); + ESPectrum::readKbdJoy(); - if (ESPectrum::PS2Controller.keyboard()->virtualKeyAvailable()) { - if (ESPectrum::readKbd(&Nextkey)) { - if(!Nextkey.down) continue; - if ((Nextkey.vk == fabgl::VK_F1) || (Nextkey.vk == fabgl::VK_ESCAPE) || (Nextkey.vk == fabgl::VK_RETURN)) break; + if (ESPectrum::PS2Controller.keyboard()->virtualKeyAvailable()) { + if (ESPectrum::readKbd(&Nextkey)) { + if(!Nextkey.down) continue; + if ((Nextkey.vk == fabgl::VK_F1) || (Nextkey.vk == fabgl::VK_ESCAPE) || (Nextkey.vk == fabgl::VK_RETURN)) break; + } } - } - vTaskDelay(20 / portTICK_PERIOD_MS); - - if (zxDelay > 0) zxDelay--; + vTaskDelay(20 / portTICK_PERIOD_MS); + + if (zxDelay > 0) zxDelay--; - } + } - click(); + click(); - return; + return; + } + else break; + } } - else break; - } + } + } // Shows a red panel with error text @@ -1337,7 +1371,9 @@ void OSD::errorHalt(string errormsg) { void OSD::osdCenteredMsg(string msg, uint8_t warn_level) { osdCenteredMsg(msg,warn_level,1000); } + void OSD::osdCenteredMsg(string msg, uint8_t warn_level, uint16_t millispause) { + const unsigned short w = (msg.length() + 2) * OSD_FONT_W; const unsigned short h = OSD_FONT_H * 3; const unsigned short x = scrAlignCenterX(w); @@ -1365,6 +1401,7 @@ void OSD::osdCenteredMsg(string msg, uint8_t warn_level, uint16_t millispause) { } if (millispause > 0) { + // Save backbuffer data j = SaveRectpos; for (int m = y; m < y + h; m++) { @@ -1375,6 +1412,7 @@ void OSD::osdCenteredMsg(string msg, uint8_t warn_level, uint16_t millispause) { } } // printf("Saverectpos: %d\n",SaveRectpos); + } VIDEO::vga.fillRect(x, y, w, h, paper); diff --git a/src/OSDMenu.cpp b/src/OSDMenu.cpp index 7bc7b6ca..321679f6 100644 --- a/src/OSDMenu.cpp +++ b/src/OSDMenu.cpp @@ -35,10 +35,12 @@ visit https://zxespectrum.speccy.org/contacto #include #include - #include #include "errno.h" +#include "esp_vfs.h" +#include "esp_vfs_fat.h" + using namespace std; #include "FileUtils.h" @@ -66,19 +68,20 @@ using namespace std; extern Font Font6x8; static uint8_t cols; // Maximum columns +static uint8_t mf_rows; // File menu maximum rows static unsigned short real_rows; // Real row count static uint8_t virtual_rows; // Virtual maximum rows on screen -static uint8_t w; // Width in pixels -static uint8_t h; // Height in pixels -static uint8_t x; // X vertical position -static uint8_t y; // Y horizontal position -static uint8_t prev_y[5]; // Y prev. position +static uint16_t w; // Width in pixels +static uint16_t h; // Height in pixels +static uint16_t x; // X vertical position +static uint16_t y; // Y horizontal position +static uint16_t prev_y[5]; // Y prev. position static unsigned short menu_prevopt = 1; static string menu; // Menu string -static unsigned short begin_row; // First real displayed row -static uint8_t focus; // Focused virtual row -static uint8_t last_focus; // To check for changes -static unsigned short last_begin_row; // To check for changes +static unsigned short begin_row = 1; // First real displayed row +static uint8_t focus = 1; // Focused virtual row +static uint8_t last_focus = 0; // To check for changes +static unsigned short last_begin_row = 0; // To check for changes const uint8_t /*DRAM_ATTR*/ click48[12]={0,0x16,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x16,0}; @@ -115,65 +118,6 @@ uint16_t OSD::zxColor(uint8_t color, uint8_t bright) { return spectrum_colors[color]; } -// // Set menu and force recalc -// void OSD::newMenu(string new_menu) { -// menu = new_menu; -// menuRecalc(); - -// // menuDraw(); - -// // focus = menu_curopt; -// // for (uint8_t r = 1; r < virtual_rows; r++) { -// // menuPrintRow(r, r == focus ? IS_FOCUSED : IS_NORMAL); -// // } - -// // menuScrollBar(); - -// } - -// void OSD::menuRecalc() { - -// // Position -// if (menu_level == 0) { -// x = (Config::aspect_16_9 ? 24 : 8); -// y = 8; -// } else { -// x = (Config::aspect_16_9 ? 24 : 8) + (60 * menu_level); -// if (menu_saverect) { -// y += (8 + (8 * menu_prevopt)); -// prev_y[menu_level] = y; -// } else { -// y = prev_y[menu_level]; -// } -// } - -// // Rows -// real_rows = rowCount(menu); -// virtual_rows = (real_rows > MENU_MAX_ROWS ? MENU_MAX_ROWS : real_rows); -// begin_row = last_begin_row = last_focus = focus = 1; - -// // Columns -// cols = 0; -// uint8_t col_count = 0; -// for (unsigned short i = 0; i < menu.length(); i++) { -// if ((menu.at(i) == ASCII_TAB) || (menu.at(i) == ASCII_NL)) { -// if (col_count > cols) { -// cols = col_count; -// } -// while (menu.at(i) != ASCII_NL) i++; -// col_count = 0; -// } -// col_count++; -// } -// cols += 8; -// cols = (cols > 28 ? 28 : cols); - -// // Size -// w = (cols * OSD_FONT_W) + 2; -// h = (virtual_rows * OSD_FONT_H) + 2; - -// } - // Get real row number for a virtual one unsigned short OSD::menuRealRowFor(uint8_t virtual_row_num) { return begin_row + virtual_row_num - 1; } @@ -244,125 +188,30 @@ void OSD::menuPrintRow(uint8_t virtual_row_num, uint8_t line_type) { } -// // Draw the complete menu -// void OSD::menuDraw() { - -// // Set font -// VIDEO::vga.setFont(Font6x8); - -// if (menu_level!=0) { -// if (menu_saverect) { -// // Save backbuffer data -// VIDEO::SaveRect[SaveRectpos] = x; -// VIDEO::SaveRect[SaveRectpos + 1] = y; -// VIDEO::SaveRect[SaveRectpos + 2] = w; -// VIDEO::SaveRect[SaveRectpos + 3] = h; -// SaveRectpos += 4; -// for (int m = y; m < y + h; m++) { -// uint32_t *backbuffer32 = (uint32_t *)(VIDEO::vga.backBuffer[m]); -// for (int n = x >> 2; n < ((x + w) >> 2) + 1; n++) { -// VIDEO::SaveRect[SaveRectpos] = backbuffer32[n]; -// SaveRectpos++; -// } -// } -// //printf("Saverectpos after save: %d\n",SaveRectpos); -// } -// } else SaveRectpos = 0; - -// // Menu border -// VIDEO::vga.rect(x, y, w, h, OSD::zxColor(0, 0)); - -// // Title -// menuPrintRow(0, IS_TITLE); - -// // Rainbow -// unsigned short rb_y = y + 8; -// unsigned short rb_paint_x = x + w - 30; -// uint8_t rb_colors[] = {2, 6, 4, 5}; -// for (uint8_t c = 0; c < 4; c++) { -// for (uint8_t i = 0; i < 5; i++) { -// VIDEO::vga.line(rb_paint_x + i, rb_y, rb_paint_x + 8 + i, rb_y - 8, OSD::zxColor(rb_colors[c], 1)); -// } -// rb_paint_x += 5; -// } - -// // focus = menu_curopt; -// // for (uint8_t r = 1; r < virtual_rows; r++) { -// // menuPrintRow(r, r == focus ? IS_FOCUSED : IS_NORMAL); -// // } - -// // menuScrollBar(); - -// } - -// // Draw the complete menu -// void OSD::filemenuDraw() { - -// // Set font -// VIDEO::vga.setFont(Font6x8); - -// if (menu_level!=0) { -// if (menu_saverect) { -// // Save backbuffer data -// VIDEO::SaveRect[SaveRectpos] = x; -// VIDEO::SaveRect[SaveRectpos + 1] = y; -// VIDEO::SaveRect[SaveRectpos + 2] = w; -// VIDEO::SaveRect[SaveRectpos + 3] = h; -// SaveRectpos += 4; -// for (int m = y; m < y + h; m++) { -// uint32_t *backbuffer32 = (uint32_t *)(VIDEO::vga.backBuffer[m]); -// for (int n = x >> 2; n < ((x + w) >> 2) + 1; n++) { -// VIDEO::SaveRect[SaveRectpos] = backbuffer32[n]; -// SaveRectpos++; -// } -// } -// //printf("Saverectpos after save: %d\n",SaveRectpos); -// } -// } else SaveRectpos = 0; - -// // Menu border -// VIDEO::vga.rect(x, y, w, h, OSD::zxColor(0, 0)); - -// // Rainbow -// unsigned short rb_y = y + 8; -// unsigned short rb_paint_x = x + w - 30; -// uint8_t rb_colors[] = {2, 6, 4, 5}; -// for (uint8_t c = 0; c < 4; c++) { -// for (uint8_t i = 0; i < 5; i++) { -// VIDEO::vga.line(rb_paint_x + i, rb_y, rb_paint_x + 8 + i, rb_y - 8, OSD::zxColor(rb_colors[c], 1)); -// } -// rb_paint_x += 5; -// } - -// // Title -// PrintRow(0, IS_TITLE); - -// } - // Draw the complete menu void OSD::WindowDraw() { // Set font VIDEO::vga.setFont(Font6x8); - if (menu_level!=0) { - if (menu_saverect) { - // Save backbuffer data - VIDEO::SaveRect[SaveRectpos] = x; - VIDEO::SaveRect[SaveRectpos + 1] = y; - VIDEO::SaveRect[SaveRectpos + 2] = w; - VIDEO::SaveRect[SaveRectpos + 3] = h; - SaveRectpos += 4; - for (int m = y; m < y + h; m++) { - uint32_t *backbuffer32 = (uint32_t *)(VIDEO::vga.backBuffer[m]); - for (int n = x >> 2; n < ((x + w) >> 2) + 1; n++) { - VIDEO::SaveRect[SaveRectpos] = backbuffer32[n]; - SaveRectpos++; - } + if (menu_level == 0) SaveRectpos = 0; + + if (menu_saverect) { + // Save backbuffer data + VIDEO::SaveRect[SaveRectpos] = x; + VIDEO::SaveRect[SaveRectpos + 1] = y; + VIDEO::SaveRect[SaveRectpos + 2] = w; + VIDEO::SaveRect[SaveRectpos + 3] = h; + SaveRectpos += 4; + for (int m = y; m < y + h; m++) { + uint32_t *backbuffer32 = (uint32_t *)(VIDEO::vga.backBuffer[m]); + for (int n = x >> 2; n < ((x + w) >> 2) + 1; n++) { + VIDEO::SaveRect[SaveRectpos] = backbuffer32[n]; + SaveRectpos++; } - //printf("Saverectpos after save: %d\n",SaveRectpos); } - } else SaveRectpos = 0; + // printf("SaveRectPos: %04X\n",SaveRectpos << 2); + } // Menu border VIDEO::vga.rect(x, y, w, h, OSD::zxColor(0, 0)); @@ -383,21 +232,6 @@ void OSD::WindowDraw() { } -// string OSD::getArchMenu() { -// string menu = (string)MENU_ARCH[Config::lang]; -// return menu; -// } - -// string OSD::getRomsetMenu(string arch) { -// string menu; -// if (arch == "48K") { -// menu = (string)MENU_ROMSET48[Config::lang]; // + FileUtils::getFileEntriesFromDir((string)DISK_ROM_DIR + "/" + arch); -// } else { -// menu = (string)MENU_ROMSET128[Config::lang]; -// } -// return menu; -// } - #define REPDEL 140 // As in real ZX Spectrum (700 ms.) #define REPPER 20 // As in real ZX Spectrum (100 ms.) static int zxDelay = 0; @@ -742,6 +576,60 @@ void OSD::menuScrollBar() { } } +// Draw file menu scroll bar +void OSD::filemenuScrollBar(uint8_t ftype) { + + // if (real_rows > virtual_rows) { + // Top handle + menuAt(1, -1); + if (FileUtils::fileTypes[ftype].begin_row > 1) { + VIDEO::vga.setTextColor(OSD::zxColor(7, 0), OSD::zxColor(0, 0)); + VIDEO::vga.print("+"); + } else { + VIDEO::vga.setTextColor(OSD::zxColor(7, 0), OSD::zxColor(0, 0)); + VIDEO::vga.print("-"); + } + + // Complete bar + unsigned short holder_x = x + (OSD_FONT_W * (cols - 1)) + 1; + unsigned short holder_y = y + (OSD_FONT_H * 2); + unsigned short holder_h = OSD_FONT_H * (virtual_rows - 3); + unsigned short holder_w = OSD_FONT_W; + VIDEO::vga.fillRect(holder_x, holder_y, holder_w, holder_h + 1, OSD::zxColor(7, 0)); + holder_y++; + + // Scroll bar + unsigned long shown_pct = round(((float)virtual_rows / (float)real_rows) * 100.0); + unsigned long begin_pct = round(((float)(FileUtils::fileTypes[ftype].begin_row - 1) / (float)real_rows) * 100.0); + unsigned long bar_h = round(((float)holder_h / 100.0) * (float)shown_pct); + unsigned long bar_y = round(((float)holder_h / 100.0) * (float)begin_pct); + + while ((bar_y + bar_h) >= holder_h) { + bar_h--; + } + + if (bar_h == 0) bar_h = 1; + + VIDEO::vga.fillRect(holder_x + 1, holder_y + bar_y, holder_w - 2, bar_h, OSD::zxColor(0, 0)); + + // Bottom handle + menuAt(-1, -1); + if ((FileUtils::fileTypes[ftype].begin_row + virtual_rows - 1) < real_rows) { + VIDEO::vga.setTextColor(OSD::zxColor(7, 0), OSD::zxColor(0, 0)); + VIDEO::vga.print("+"); + } else { + VIDEO::vga.setTextColor(OSD::zxColor(7, 0), OSD::zxColor(0, 0)); + VIDEO::vga.print("-"); + } + // } +} + +// trim from start (in place) +static inline void ltrim(std::string &s) { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { + return !std::isspace(ch); + })); +} // trim from end (in place) static inline void rtrim(std::string &s) { s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { @@ -749,305 +637,485 @@ static inline void rtrim(std::string &s) { }).base(), s.end()); } -FILE *dirfile; +// trim from both ends (in place) +static inline void trim(std::string &s) { + rtrim(s); + ltrim(s); +} -// Run a new file menu -string OSD::menuFile(string filedir, string title, string extensions, int currentFile) { +// trim from start (copying) +static inline std::string ltrim_copy(std::string s) { + ltrim(s); + return s; +} - fabgl::VirtualKeyItem Menukey; +// trim from end (copying) +static inline std::string rtrim_copy(std::string s) { + rtrim(s); + return s; +} - // Get dir file size and use it for calc dialog rows - struct stat stat_buf; - int rc; - - rc = stat((filedir + "/.d").c_str(), &stat_buf); - if (rc < 0) { - // deallocAluBytes(); - OSD::osdCenteredMsg("Please wait: sorting directory", LEVEL_INFO, 0); - int chunks = FileUtils::DirToFile(filedir, extensions); // Prepare sna filelist - if (chunks) FileUtils::Mergefiles(filedir,chunks); // Merge files - // OSD::osdCenteredMsg(" Done: directory index ready ", LEVEL_INFO); - // precalcAluBytes(); - rc = stat((filedir + "/.d").c_str(), &stat_buf); - currentFile = 1; - } - - // Open dir file for read - dirfile = fopen((filedir + "/.d").c_str(), "r"); - if (dirfile == NULL) { - printf("Error opening dir file\n"); - return ""; - } +// trim from both ends (copying) +static inline std::string trim_copy(std::string s) { + trim(s); + return s; +} - real_rows = (stat_buf.st_size / 32) + 1; // Add 1 for title - virtual_rows = (real_rows > 19 ? 19 : real_rows); - - // printf("Real rows: %d; st_size: %d\n",real_rows,stat_buf.st_size); +FILE *dirfile; - // // Get first bunch of rows - // menu = title + "\n"; - // for (int i = 1; i < virtual_rows; i++) { - // char buf[256]; - // fgets(buf, sizeof(buf), dirfile); - // if (feof(dirfile)) break; - // menu += buf; - // } +// Run a new file menu +string OSD::menuFile(string &fdir, string title, uint8_t ftype, uint8_t mfcols, uint8_t mfrows) { + struct stat stat_buf; + bool reIndex; + // Position if (menu_level == 0) { - x = (Config::aspect_16_9 ? 24 : 8); - y = 8; + x = (Config::aspect_16_9 ? 24 : 4); + y = (Config::aspect_16_9 ? 4 : 4); } else { - // x = (Config::aspect_16_9 ? 24 : 8) + ((((cols >> 1) - 3) * 6) * menu_level); x = (Config::aspect_16_9 ? 24 : 8) + (60 * menu_level); y = 8 + (16 * menu_level); } - // Columns - cols = 31; // 28 for filename + 2 pre and post space + 1 for scrollbar + // Columns and Rows + cols = mfcols; + mf_rows = mfrows + (Config::aspect_16_9 ? 0 : 2); + + // printf("Focus: %d, Begin_row: %d, mf_rows: %d\n",(int) FileUtils::fileTypes[ftype].focus,(int) FileUtils::fileTypes[ftype].begin_row,(int) mf_rows); + if (FileUtils::fileTypes[ftype].focus > mf_rows - 1) { + FileUtils::fileTypes[ftype].begin_row += FileUtils::fileTypes[ftype].focus - (mf_rows - 1); + FileUtils::fileTypes[ftype].focus = mf_rows - 1; + } // Size w = (cols * OSD_FONT_W) + 2; - h = (virtual_rows * OSD_FONT_H) + 2; - + h = (mf_rows * OSD_FONT_H) + 2; menu = title + "\n"; + WindowDraw(); // Draw menu outline - begin_row = focus = 1; - last_begin_row = last_focus = 0; + // Draw blank rows + for (uint8_t row = 1; row < mf_rows; row++) { + VIDEO::vga.setTextColor(OSD::zxColor(0, 1), OSD::zxColor(7, 1)); + menuAt(row, 0); + VIDEO::vga.print(std::string(cols, ' ').c_str()); + } - filemenuRedraw(title); // Draw menu content + while(1) { - zxDelay = REPDEL; - lastzxKey = 0; + reIndex = false; + string filedir = FileUtils::MountPoint + fdir; - while (1) { + // Open dir file for read + dirfile = fopen((filedir + FileUtils::fileTypes[ftype].indexFilename).c_str(), "r"); + if (dirfile == NULL) { - if (ZXKeyb::Exists) { + // printf("No dir file found: reindexing\n"); + reIndex = true; - ZXKeyb::process(); + } else { - if (!bitRead(ZXKeyb::ZXcols[4], 3)) { // 6 DOWN - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_UP, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_UP, false, false); - if (lastzxKey == 1) - zxDelay = REPPER; - else - zxDelay = REPDEL; - lastzxKey = 1; - } - } else - if (!bitRead(ZXKeyb::ZXcols[4], 4)) { // 7 UP (Yes, like the drink's name, I know... :D) - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_DOWN, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_DOWN, false, false); - if (lastzxKey == 2) - zxDelay = REPPER; - else - zxDelay = REPDEL; - lastzxKey = 2; - } - } else - if (!bitRead(ZXKeyb::ZXcols[6], 0)) { // ENTER - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, false, false); - if (lastzxKey == 3) - zxDelay = REPPER; - else - zxDelay = REPDEL; - lastzxKey = 3; - } - } else - if (!bitRead(ZXKeyb::ZXcols[4], 0)) { // 0 - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_SPACE, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_SPACE, false, false); - if (lastzxKey == 4) - zxDelay = REPPER; - else - zxDelay = REPDEL; - lastzxKey = 4; - } - } else - if ((!bitRead(ZXKeyb::ZXcols[7], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 1))) { // BREAK - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_ESCAPE, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_ESCAPE, false, false); - if (lastzxKey == 5) - zxDelay = REPPER; - else - zxDelay = REPDEL; - lastzxKey = 5; - } - } else - if (!bitRead(ZXKeyb::ZXcols[3], 4)) { // LEFT - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEUP, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEUP, false, false); - if (lastzxKey == 6) - zxDelay = REPPER; - else - zxDelay = REPDEL; - lastzxKey = 6; - } - } else - if (!bitRead(ZXKeyb::ZXcols[4], 2)) { // RIGHT - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEDOWN, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEDOWN, false, false); - if (lastzxKey == 7) - zxDelay = REPPER; - else - zxDelay = REPDEL; - lastzxKey = 7; - } - } else - if (!bitRead(ZXKeyb::ZXcols[1], 1)) { // S (Capture screen) - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PRINTSCREEN, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PRINTSCREEN, false, false); - if (lastzxKey == 8) - zxDelay = REPPER; - else - zxDelay = REPDEL; - lastzxKey = 8; + // Read dir hash from file + stat((filedir + FileUtils::fileTypes[ftype].indexFilename).c_str(), &stat_buf); + fseek(dirfile, (stat_buf.st_size >> 5) << 5,SEEK_SET); + char fhash[32]; + fgets(fhash, sizeof(fhash), dirfile); + // printf("File Hash: %s\n",fhash); + rewind(dirfile); + + // Calc dir hash + DIR *dir; + struct dirent* de; + + std::vector filexts; + size_t pos = 0; + string ss = FileUtils::fileTypes[ftype].fileExts; + while ((pos = ss.find(",")) != std::string::npos) { + filexts.push_back(ss.substr(0, pos)); + ss.erase(0, pos + 1); + } + filexts.push_back(ss.substr(0)); + + unsigned long hash = 0, high; // Name checksum variables + + string fdir = filedir.substr(0,filedir.length() - 1); + if ((dir = opendir(fdir.c_str())) != nullptr) { + + while ((de = readdir(dir)) != nullptr) { + string fname = de->d_name; + if (de->d_type == DT_REG || de->d_type == DT_DIR) { + if (fname.compare(0,1,".") != 0) { + // printf("Fname: %s Fname size: %d\n",fname.c_str(),fname.size()); + if ((de->d_type == DT_DIR) || ((fname.size() > 3) && (std::find(filexts.begin(),filexts.end(),fname.substr(fname.size()-4)) != filexts.end()))) { + // Calculate name checksum + for (int i = 0; i < fname.length(); i++) { + hash = (hash << 4) + fname[i]; + if (high = hash & 0xF0000000) hash ^= high >> 24; + hash &= ~high; + } + } + } + } } - } else - { - zxDelay = 0; - lastzxKey = 0; + + // printf("Hashcode : %lu\n",hash); + + closedir(dir); + + } else { + + printf("Error opening %s\n",filedir.c_str()); + return ""; + + } + + filexts.clear(); // Clear vector + std::vector().swap(filexts); // free memory + + // If calc hash and file hash are different refresh dir index + if (stoul(fhash) != hash) { + fclose(dirfile); + reIndex = true; } } - ESPectrum::readKbdJoy(); + // Force reindex (for testing) + // fclose(dirfile); + // reIndex = true; - // Process external keyboard - if (ESPectrum::PS2Controller.keyboard()->virtualKeyAvailable()) { - if (ESPectrum::readKbd(&Menukey)) { - if (!Menukey.down) continue; + // There was no index or hashes are different: reIndex + if (reIndex) { - // // Search first ocurrence of letter if we're not on that letter yet - // if (((Menukey.vk >= fabgl::VK_a) && (Menukey.vk <= fabgl::VK_Z)) || ((Menukey.vk >= fabgl::VK_0) && (Menukey.vk <= fabgl::VK_9))) { - // uint8_t dif; - // if (Menukey.vk<=fabgl::VK_9) dif = 46; - // else if (Menukey.vk<=fabgl::VK_z) dif = 75; - // else if (Menukey.vk<=fabgl::VK_Z) dif = 17; - // uint8_t letra = rowGet(menu,focus).at(0); - // if (letra != Menukey.vk + dif) { - // // TO DO: Position on first ocurrence of letra - // filemenuRedraw(title); - // } - /*} else*/ if (Menukey.vk == fabgl::VK_UP) { - if (focus == 1 and begin_row > 1) { - // filemenuScroll(DOWN); - if (begin_row > 1) { - last_begin_row = begin_row; - begin_row--; - filemenuRedraw(title); - } - } else if (focus > 1) { - last_focus = focus; - focus--; - PrintRow(focus, IS_FOCUSED); - if (focus + 1 < virtual_rows) { - PrintRow(focus + 1, IS_NORMAL); - } + // OSD::osdCenteredMsg("Please wait: sorting directory", LEVEL_INFO, 0); // TO DO: Move this into DirtoFile function + FileUtils::DirToFile(filedir, ftype); // Prepare filelist + + stat((filedir + FileUtils::fileTypes[ftype].indexFilename).c_str(), &stat_buf); + + dirfile = fopen((filedir + FileUtils::fileTypes[ftype].indexFilename).c_str(), "r"); + if (dirfile == NULL) { + printf("Error opening index file\n"); + return ""; + } + + // Reset position + FileUtils::fileTypes[ftype].begin_row = FileUtils::fileTypes[ftype].focus = 1; + + } + + real_rows = (stat_buf.st_size / 64) + 1; // Add 1 for title + virtual_rows = (real_rows > mf_rows ? mf_rows : real_rows); + + // printf("Real rows: %d; st_size: %d\n",real_rows,stat_buf.st_size); + + last_begin_row = last_focus = 0; + + // printf("Focus: %d, Begin_row: %d, real_rows: %d, mf_rows: %d\n",(int) FileUtils::fileTypes[ftype].focus,(int) FileUtils::fileTypes[ftype].begin_row,(int) real_rows, (int) mf_rows); + if ((real_rows > mf_rows -1) && ((FileUtils::fileTypes[ftype].begin_row + mf_rows - 1) > real_rows)) { + FileUtils::fileTypes[ftype].focus += (FileUtils::fileTypes[ftype].begin_row + mf_rows - 1) - real_rows; + FileUtils::fileTypes[ftype].begin_row = real_rows - (mf_rows - 1); + } + + filemenuRedraw(title, ftype); // Draw content + + fabgl::VirtualKeyItem Menukey; + zxDelay = REPDEL; + lastzxKey = 0; + + while (1) { + + if (ZXKeyb::Exists) { + + ZXKeyb::process(); + + if (!bitRead(ZXKeyb::ZXcols[4], 3)) { // 6 DOWN + if (zxDelay == 0) { + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_UP, true, false); + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_UP, false, false); + if (lastzxKey == 1) + zxDelay = REPPER; + else + zxDelay = REPDEL; + lastzxKey = 1; } - click(); - } else if (Menukey.vk == fabgl::VK_DOWN) { - if (focus == virtual_rows - 1) { - if ((begin_row + virtual_rows - 1) < real_rows) { - last_begin_row = begin_row; - begin_row++; - filemenuRedraw(title); - } - } else if (focus < virtual_rows - 1) { - last_focus = focus; - focus++; - PrintRow(focus, IS_FOCUSED); - if (focus - 1 > 0) { - PrintRow(focus - 1, IS_NORMAL); - } + } else + if (!bitRead(ZXKeyb::ZXcols[4], 4)) { // 7 UP (Yes, like the drink's name, I know... :D) + if (zxDelay == 0) { + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_DOWN, true, false); + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_DOWN, false, false); + if (lastzxKey == 2) + zxDelay = REPPER; + else + zxDelay = REPDEL; + lastzxKey = 2; } - click(); - } else if ((Menukey.vk == fabgl::VK_PAGEUP) || (Menukey.vk == fabgl::VK_LEFT)) { - if (begin_row > virtual_rows) { - focus = 1; - begin_row -= virtual_rows - 1; - } else { - focus = 1; - begin_row = 1; + } else + if (!bitRead(ZXKeyb::ZXcols[6], 0)) { // ENTER + if (zxDelay == 0) { + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, true, false); + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, false, false); + if (lastzxKey == 3) + zxDelay = REPPER; + else + zxDelay = REPDEL; + lastzxKey = 3; } - filemenuRedraw(title); - click(); - } else if ((Menukey.vk == fabgl::VK_PAGEDOWN) || (Menukey.vk == fabgl::VK_RIGHT)) { - if (real_rows - begin_row - virtual_rows > virtual_rows) { - focus = 1; - begin_row += virtual_rows - 1; - } else { - focus = virtual_rows - 1; - begin_row = real_rows - virtual_rows + 1; + } else + if (!bitRead(ZXKeyb::ZXcols[4], 0)) { // 0 + if (zxDelay == 0) { + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_SPACE, true, false); + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_SPACE, false, false); + if (lastzxKey == 4) + zxDelay = REPPER; + else + zxDelay = REPDEL; + lastzxKey = 4; } - filemenuRedraw(title); - click(); - } else if (Menukey.vk == fabgl::VK_HOME) { - focus = 1; - begin_row = 1; - filemenuRedraw(title); - click(); - } else if (Menukey.vk == fabgl::VK_END) { - focus = virtual_rows - 1; - begin_row = real_rows - virtual_rows + 1; - filemenuRedraw(title); - click(); - } else if (Menukey.vk == fabgl::VK_RETURN) { - fclose(dirfile); - dirfile = NULL; - filedir = rowGet(menu,focus); - rtrim(filedir); - click(); - return "R" + filedir; - } else if (Menukey.vk == fabgl::VK_SPACE) { - fclose(dirfile); - dirfile = NULL; - filedir = rowGet(menu,focus); - rtrim(filedir); - click(); - return "S" + filedir; - } else if (Menukey.vk == fabgl::VK_ESCAPE) { + } else + if ((!bitRead(ZXKeyb::ZXcols[7], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 1))) { // BREAK + if (zxDelay == 0) { + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_ESCAPE, true, false); + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_ESCAPE, false, false); + if (lastzxKey == 5) + zxDelay = REPPER; + else + zxDelay = REPDEL; + lastzxKey = 5; + } + } else + if (!bitRead(ZXKeyb::ZXcols[3], 4)) { // LEFT + if (zxDelay == 0) { + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEUP, true, false); + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEUP, false, false); + if (lastzxKey == 6) + zxDelay = REPPER; + else + zxDelay = REPDEL; + lastzxKey = 6; + } + } else + if (!bitRead(ZXKeyb::ZXcols[4], 2)) { // RIGHT + if (zxDelay == 0) { + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEDOWN, true, false); + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEDOWN, false, false); + if (lastzxKey == 7) + zxDelay = REPPER; + else + zxDelay = REPDEL; + lastzxKey = 7; + } + } else + if (!bitRead(ZXKeyb::ZXcols[1], 1)) { // S (Capture screen) + if (zxDelay == 0) { + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PRINTSCREEN, true, false); + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PRINTSCREEN, false, false); + if (lastzxKey == 8) + zxDelay = REPPER; + else + zxDelay = REPDEL; + lastzxKey = 8; + } + } else + { + zxDelay = 0; + lastzxKey = 0; + } + + } + + ESPectrum::readKbdJoy(); + + // Process external keyboard + if (ESPectrum::PS2Controller.keyboard()->virtualKeyAvailable()) { + if (ESPectrum::readKbd(&Menukey)) { + if (!Menukey.down) continue; + + // // Search first ocurrence of letter if we're not on that letter yet + // if (((Menukey.vk >= fabgl::VK_a) && (Menukey.vk <= fabgl::VK_Z)) || ((Menukey.vk >= fabgl::VK_0) && (Menukey.vk <= fabgl::VK_9))) { + // uint8_t dif; + // if (Menukey.vk<=fabgl::VK_9) dif = 46; + // else if (Menukey.vk<=fabgl::VK_z) dif = 75; + // else if (Menukey.vk<=fabgl::VK_Z) dif = 17; + // uint8_t letra = rowGet(menu,focus).at(0); + // if (letra != Menukey.vk + dif) { + // // TO DO: Position on first ocurrence of letra + // filemenuRedraw(title); + // } + /*} else*/ if (Menukey.vk == fabgl::VK_UP) { + if (FileUtils::fileTypes[ftype].focus == 1 and FileUtils::fileTypes[ftype].begin_row > 1) { + // filemenuScroll(DOWN); + if (FileUtils::fileTypes[ftype].begin_row > 1) { + last_begin_row = FileUtils::fileTypes[ftype].begin_row; + FileUtils::fileTypes[ftype].begin_row--; + filemenuRedraw(title, ftype); + } + } else if (FileUtils::fileTypes[ftype].focus > 1) { + last_focus = FileUtils::fileTypes[ftype].focus; + FileUtils::fileTypes[ftype].focus--; + filemenuPrintRow(FileUtils::fileTypes[ftype].focus, IS_FOCUSED); + if (FileUtils::fileTypes[ftype].focus + 1 < virtual_rows) { + filemenuPrintRow(FileUtils::fileTypes[ftype].focus + 1, IS_NORMAL); + } + } + click(); + } else if (Menukey.vk == fabgl::VK_DOWN) { + if (FileUtils::fileTypes[ftype].focus == virtual_rows - 1) { + if ((FileUtils::fileTypes[ftype].begin_row + virtual_rows - 1) < real_rows) { + last_begin_row = FileUtils::fileTypes[ftype].begin_row; + FileUtils::fileTypes[ftype].begin_row++; + filemenuRedraw(title, ftype); + } + } else if (FileUtils::fileTypes[ftype].focus < virtual_rows - 1) { + last_focus = FileUtils::fileTypes[ftype].focus; + FileUtils::fileTypes[ftype].focus++; + filemenuPrintRow(FileUtils::fileTypes[ftype].focus, IS_FOCUSED); + if (FileUtils::fileTypes[ftype].focus - 1 > 0) { + filemenuPrintRow(FileUtils::fileTypes[ftype].focus - 1, IS_NORMAL); + } + } + click(); + } else if ((Menukey.vk == fabgl::VK_PAGEUP) || (Menukey.vk == fabgl::VK_LEFT)) { + if (FileUtils::fileTypes[ftype].begin_row > virtual_rows) { + FileUtils::fileTypes[ftype].focus = 1; + FileUtils::fileTypes[ftype].begin_row -= virtual_rows - 1; + } else { + FileUtils::fileTypes[ftype].focus = 1; + FileUtils::fileTypes[ftype].begin_row = 1; + } + filemenuRedraw(title, ftype); + click(); + } else if ((Menukey.vk == fabgl::VK_PAGEDOWN) || (Menukey.vk == fabgl::VK_RIGHT)) { + if (real_rows - FileUtils::fileTypes[ftype].begin_row - virtual_rows > virtual_rows) { + FileUtils::fileTypes[ftype].focus = 1; + FileUtils::fileTypes[ftype].begin_row += virtual_rows - 1; + } else { + FileUtils::fileTypes[ftype].focus = virtual_rows - 1; + FileUtils::fileTypes[ftype].begin_row = real_rows - virtual_rows + 1; + } + filemenuRedraw(title, ftype); + click(); + } else if (Menukey.vk == fabgl::VK_HOME) { + FileUtils::fileTypes[ftype].focus = 1; + FileUtils::fileTypes[ftype].begin_row = 1; + filemenuRedraw(title, ftype); + click(); + } else if (Menukey.vk == fabgl::VK_END) { + FileUtils::fileTypes[ftype].focus = virtual_rows - 1; + FileUtils::fileTypes[ftype].begin_row = real_rows - virtual_rows + 1; + filemenuRedraw(title, ftype); + click(); + } else if (Menukey.vk == fabgl::VK_RETURN) { + + fclose(dirfile); + dirfile = NULL; + + // // Restore backbuffer data + // int j = SaveRectpos - (((w >> 2) + 1) * h); + // SaveRectpos = j - 4; + // for (int m = y; m < y + h; m++) { + // uint32_t *backbuffer32 = (uint32_t *)(VIDEO::vga.backBuffer[m]); + // for (int n = x >> 2; n < ((x + w) >> 2) + 1; n++) { + // backbuffer32[n] = VIDEO::SaveRect[j]; + // j++; + // } + // } + + filedir = rowGet(menu,FileUtils::fileTypes[ftype].focus); + // printf("%s\n",filedir.c_str()); + if (filedir[0] == ASCII_SPC) { + if (filedir[1] == ASCII_SPC) { + fdir.pop_back(); + fdir = fdir.substr(0,fdir.find_last_of("/") + 1); + } else { + filedir.erase(0,1); + trim(filedir); + fdir = fdir + filedir + "/"; + } + FileUtils::fileTypes[ftype].begin_row = FileUtils::fileTypes[ftype].focus = 1; + // printf("Fdir: %s\n",fdir.c_str()); + break; + } else { + + if (menu_saverect) { + // Restore backbuffer data + int j = SaveRectpos - (((w >> 2) + 1) * h); + SaveRectpos = j - 4; + for (int m = y; m < y + h; m++) { + uint32_t *backbuffer32 = (uint32_t *)(VIDEO::vga.backBuffer[m]); + for (int n = x >> 2; n < ((x + w) >> 2) + 1; n++) { + backbuffer32[n] = VIDEO::SaveRect[j]; + j++; + } + } + menu_saverect = false; + } + + rtrim(filedir); + click(); + return "R" + filedir; + } + } else if (Menukey.vk == fabgl::VK_SPACE) { + fclose(dirfile); + dirfile = NULL; - if (menu_level!=0) { // Restore backbuffer data - int j = SaveRectpos - (((w >> 2) + 1) * h); - //printf("SaveRectpos: %d; J b4 restore: %d\n",SaveRectpos, j); - SaveRectpos = j - 4; - for (int m = y; m < y + h; m++) { - uint32_t *backbuffer32 = (uint32_t *)(VIDEO::vga.backBuffer[m]); - for (int n = x >> 2; n < ((x + w) >> 2) + 1; n++) { - backbuffer32[n] = VIDEO::SaveRect[j]; - j++; + if (menu_saverect) { + int j = SaveRectpos - (((w >> 2) + 1) * h); + SaveRectpos = j - 4; + for (int m = y; m < y + h; m++) { + uint32_t *backbuffer32 = (uint32_t *)(VIDEO::vga.backBuffer[m]); + for (int n = x >> 2; n < ((x + w) >> 2) + 1; n++) { + backbuffer32[n] = VIDEO::SaveRect[j]; + j++; + } } + menu_saverect = false; } - //printf("SaveRectpos: %d; J b4 restore: %d\n",SaveRectpos, j); - menu_saverect = false; - } - fclose(dirfile); - dirfile = NULL; - click(); - return ""; + filedir = rowGet(menu,FileUtils::fileTypes[ftype].focus); + rtrim(filedir); + click(); + return "S" + filedir; + } else if (Menukey.vk == fabgl::VK_ESCAPE) { + + // Restore backbuffer data + if (menu_saverect) { + int j = SaveRectpos - (((w >> 2) + 1) * h); + SaveRectpos = j - 4; + for (int m = y; m < y + h; m++) { + uint32_t *backbuffer32 = (uint32_t *)(VIDEO::vga.backBuffer[m]); + for (int n = x >> 2; n < ((x + w) >> 2) + 1; n++) { + backbuffer32[n] = VIDEO::SaveRect[j]; + j++; + } + } + menu_saverect = false; + } + + fclose(dirfile); + dirfile = NULL; + click(); + return ""; + } } + + } else { + + // TO DO: COUNT TIME TO SIGNAL START OF FOCUSED LINE SCROLL + } - } - vTaskDelay(5 / portTICK_PERIOD_MS); + vTaskDelay(5 / portTICK_PERIOD_MS); - if (zxDelay > 0) zxDelay--; + // TO DO: SCROLL FOCUSED LINE IF SIGNALED + + if (zxDelay > 0) zxDelay--; + + } } + } // Print a virtual row @@ -1100,13 +1168,62 @@ void OSD::PrintRow(uint8_t virtual_row_num, uint8_t line_type) { } +// Print a virtual row +void OSD::filemenuPrintRow(uint8_t virtual_row_num, uint8_t line_type) { + + uint8_t margin; + + string line = rowGet(menu, virtual_row_num); + + switch (line_type) { + case IS_TITLE: + VIDEO::vga.setTextColor(OSD::zxColor(7, 1), OSD::zxColor(0, 0)); + margin = 2; + break; + case IS_FOCUSED: + VIDEO::vga.setTextColor(OSD::zxColor(0, 1), OSD::zxColor(5, 1)); + margin = (real_rows > virtual_rows ? 3 : 2); + break; + default: + VIDEO::vga.setTextColor(OSD::zxColor(0, 1), OSD::zxColor(7, 1)); + margin = (real_rows > virtual_rows ? 3 : 2); + } + + menuAt(virtual_row_num, 0); + + VIDEO::vga.print(" "); + + if (line[0] == ASCII_SPC) { + // Directory + ltrim(line); + + // for(int h = 0; h < line.length(); h++) + // printf("%d, ",(int)line[h]); + // printf("\n"); + + if (line.length() < cols - margin) + line = line + std::string(cols - margin - line.length(), ' '); + line = line.substr(0,cols - margin - 6) + " "; + } else { + // Filename + if (line.length() < cols - margin) + line = line + std::string(cols - margin - line.length(), ' '); + line = line.substr(0, cols - margin); + } + + VIDEO::vga.print(line.c_str()); + + VIDEO::vga.print(" "); + +} + // Redraw inside rows -void OSD::filemenuRedraw(string title) { +void OSD::filemenuRedraw(string title, uint8_t ftype) { - if ((focus != last_focus) || (begin_row != last_begin_row)) { + if ((FileUtils::fileTypes[ftype].focus != last_focus) || (FileUtils::fileTypes[ftype].begin_row != last_begin_row)) { // Read bunch of rows - fseek(dirfile, (begin_row - 1) * 32,SEEK_SET); + fseek(dirfile, (FileUtils::fileTypes[ftype].begin_row - 1) * 64,SEEK_SET); menu = title + "\n"; for (int i = 1; i < virtual_rows; i++) { char buf[256]; @@ -1115,18 +1232,27 @@ void OSD::filemenuRedraw(string title) { menu += buf; } - for (uint8_t row = 1; row < virtual_rows; row++) { - if (row == focus) { - PrintRow(row, IS_FOCUSED); + uint8_t row = 1; + for (; row < virtual_rows; row++) { + if (row == FileUtils::fileTypes[ftype].focus) { + filemenuPrintRow(row, IS_FOCUSED); } else { - PrintRow(row, IS_NORMAL); + filemenuPrintRow(row, IS_NORMAL); + } + } + + if (real_rows > virtual_rows) { + filemenuScrollBar(ftype); + } else { + for (; row < mf_rows; row++) { + VIDEO::vga.setTextColor(OSD::zxColor(0, 1), OSD::zxColor(7, 1)); + menuAt(row, 0); + VIDEO::vga.print(std::string(cols, ' ').c_str()); } } - menuScrollBar(); - - last_focus = focus; - last_begin_row = begin_row; + last_focus = FileUtils::fileTypes[ftype].focus; + last_begin_row = FileUtils::fileTypes[ftype].begin_row; } } diff --git a/src/Snapshot.cpp b/src/Snapshot.cpp index 2a66354b..9e4f3b1b 100644 --- a/src/Snapshot.cpp +++ b/src/Snapshot.cpp @@ -157,6 +157,19 @@ bool FileSNA::load(string sna_fn, string force_arch) { // Condition this to 50hz mode if(Config::videomode) { + + Config::SNA_Path = FileUtils::SNA_Path; + Config::SNA_begin_row = FileUtils::fileTypes[DISK_SNAFILE].begin_row; + Config::SNA_focus = FileUtils::fileTypes[DISK_SNAFILE].focus; + + Config::TAP_Path = FileUtils::TAP_Path; + Config::TAP_begin_row = FileUtils::fileTypes[DISK_TAPFILE].begin_row; + Config::TAP_focus = FileUtils::fileTypes[DISK_TAPFILE].focus; + + Config::DSK_Path = FileUtils::DSK_Path; + Config::DSK_begin_row = FileUtils::fileTypes[DISK_DSKFILE].begin_row; + Config::DSK_focus = FileUtils::fileTypes[DISK_DSKFILE].focus; + Config::ram_file = sna_fn; Config::save(); OSD::esp_hard_reset(); @@ -174,6 +187,19 @@ bool FileSNA::load(string sna_fn, string force_arch) { // Condition this to 50hz mode if(Config::videomode) { + + Config::SNA_Path = FileUtils::SNA_Path; + Config::SNA_begin_row = FileUtils::fileTypes[DISK_SNAFILE].begin_row; + Config::SNA_focus = FileUtils::fileTypes[DISK_SNAFILE].focus; + + Config::TAP_Path = FileUtils::TAP_Path; + Config::TAP_begin_row = FileUtils::fileTypes[DISK_TAPFILE].begin_row; + Config::TAP_focus = FileUtils::fileTypes[DISK_TAPFILE].focus; + + Config::DSK_Path = FileUtils::DSK_Path; + Config::DSK_begin_row = FileUtils::fileTypes[DISK_DSKFILE].begin_row; + Config::DSK_focus = FileUtils::fileTypes[DISK_DSKFILE].focus; + Config::ram_file = sna_fn; Config::save(); OSD::esp_hard_reset(); @@ -196,6 +222,19 @@ bool FileSNA::load(string sna_fn, string force_arch) { // Condition this to 50hz mode if(Config::videomode) { + + Config::SNA_Path = FileUtils::SNA_Path; + Config::SNA_begin_row = FileUtils::fileTypes[DISK_SNAFILE].begin_row; + Config::SNA_focus = FileUtils::fileTypes[DISK_SNAFILE].focus; + + Config::TAP_Path = FileUtils::TAP_Path; + Config::TAP_begin_row = FileUtils::fileTypes[DISK_TAPFILE].begin_row; + Config::TAP_focus = FileUtils::fileTypes[DISK_TAPFILE].focus; + + Config::DSK_Path = FileUtils::DSK_Path; + Config::DSK_begin_row = FileUtils::fileTypes[DISK_DSKFILE].begin_row; + Config::DSK_focus = FileUtils::fileTypes[DISK_DSKFILE].focus; + Config::ram_file = sna_fn; Config::save(); OSD::esp_hard_reset(); @@ -613,11 +652,27 @@ bool FileZ80::load(string z80_fn) { return false; } + // printf("fileTypes -> Path: %s, begin_row: %d, focus: %d\n",FileUtils::SNA_Path.c_str(),FileUtils::fileTypes[DISK_SNAFILE].begin_row,FileUtils::fileTypes[DISK_SNAFILE].focus); + // printf("Config -> Path: %s, begin_row: %d, focus: %d\n",Config::Path.c_str(),(int)Config::begin_row,(int)Config::focus); + // Manage arch change if (Config::getArch() != z80_arch) { Config::requestMachine(z80_arch, "SINCLAIR"); // Condition this to 50hz mode if(Config::videomode) { + + Config::SNA_Path = FileUtils::SNA_Path; + Config::SNA_begin_row = FileUtils::fileTypes[DISK_SNAFILE].begin_row; + Config::SNA_focus = FileUtils::fileTypes[DISK_SNAFILE].focus; + + Config::TAP_Path = FileUtils::TAP_Path; + Config::TAP_begin_row = FileUtils::fileTypes[DISK_TAPFILE].begin_row; + Config::TAP_focus = FileUtils::fileTypes[DISK_TAPFILE].focus; + + Config::DSK_Path = FileUtils::DSK_Path; + Config::DSK_begin_row = FileUtils::fileTypes[DISK_DSKFILE].begin_row; + Config::DSK_focus = FileUtils::fileTypes[DISK_DSKFILE].focus; + Config::ram_file = z80_fn; Config::save(); OSD::esp_hard_reset(); diff --git a/src/Tape.cpp b/src/Tape.cpp index 8553d325..4b36fcc5 100644 --- a/src/Tape.cpp +++ b/src/Tape.cpp @@ -89,7 +89,9 @@ void Tape::Open(string name) { tape = NULL; } - tape = fopen(name.c_str(), "rb"); + string fname = FileUtils::MountPoint + "/" + FileUtils::TAP_Path + "/" + name; + + tape = fopen(fname.c_str(), "rb"); if (tape == NULL) { OSD::osdCenteredMsg(OSD_TAPE_LOAD_ERR, LEVEL_ERROR); return; @@ -592,11 +594,16 @@ void Tape::Save() { bool Tape::FlashLoad() { if (tape == NULL) { - tape = fopen(Tape::tapeFileName.c_str(), "rb"); + + string fname = FileUtils::MountPoint + "/" + FileUtils::TAP_Path + "/" + tapeFileName; + + tape = fopen(fname.c_str(), "rb"); if (tape == NULL) { return false; } + CalcTapBlockPos(tapeCurBlock); + } // printf("--< BLOCK: %d >--------------------------------\n",(int)tapeCurBlock); diff --git a/src/Video.cpp b/src/Video.cpp index 7d74fc71..d91085af 100644 --- a/src/Video.cpp +++ b/src/Video.cpp @@ -52,10 +52,13 @@ uint8_t VIDEO::flash_ctr= 0; bool VIDEO::OSD = false; uint8_t VIDEO::tStatesPerLine; int VIDEO::tStatesScreen; +// unsigned int VIDEO::tstateDraw; // Drawing start point (in Tstates) +// unsigned int VIDEO::linedraw_cnt; uint8_t* VIDEO::grmem; uint32_t* VIDEO::SaveRect; int VIDEO::VsyncFinetune[2]; // uint8_t VIDEO::dispUpdCycle; +uint32_t VIDEO::framecnt = 0; void IRAM_ATTR VGA6Bit::interrupt(void *arg) { @@ -209,6 +212,12 @@ void VIDEO::vgataskinit(void *unused) { OSD::scrW = vgaMode.hRes; OSD::scrH = vgaMode.vRes / vgaMode.vDiv; vga.VGA6Bit_useinterrupt=true; + // CRT Centering + if (Config::videomode == 2) { + vga.CenterH = Config::CenterH; + vga.CenterV = Config::CenterV; + } + // Init mode vga.init(vgaMode, redPins, grePins, bluPins, HSYNC_PIN, VSYNC_PIN); for (;;){} @@ -351,7 +360,7 @@ uint8_t VIDEO::getFloatBusData128() { void VIDEO::NoVideo(unsigned int statestoadd, bool contended) { CPU::tstates += statestoadd; } -void VIDEO::TopBorder_Blank(unsigned int statestoadd, bool contended) { +void IRAM_ATTR VIDEO::TopBorder_Blank(unsigned int statestoadd, bool contended) { CPU::tstates += statestoadd; @@ -390,7 +399,7 @@ void IRAM_ATTR VIDEO::TopBorder_Blank_Pentagon(unsigned int statestoadd, bool co } -void VIDEO::TopBorder(unsigned int statestoadd, bool contended) { +void IRAM_ATTR VIDEO::TopBorder(unsigned int statestoadd, bool contended) { CPU::tstates += statestoadd; @@ -423,7 +432,7 @@ void IRAM_ATTR VIDEO::TopBorder_Pentagon(unsigned int statestoadd, bool contende } -void VIDEO::MainScreen_Blank(unsigned int statestoadd, bool contended) { +void IRAM_ATTR VIDEO::MainScreen_Blank(unsigned int statestoadd, bool contended) { CPU::tstates += statestoadd; @@ -466,7 +475,7 @@ void IRAM_ATTR VIDEO::MainScreen_Blank_Pentagon(unsigned int statestoadd, bool c } -void VIDEO::MainScreenLB(unsigned int statestoadd, bool contended) { +void IRAM_ATTR VIDEO::MainScreenLB(unsigned int statestoadd, bool contended) { if (contended) statestoadd += wait_st[CPU::tstates - tstateDraw]; @@ -733,7 +742,7 @@ void VIDEO::MainScreen_OSD_Pentagon(unsigned int statestoadd, bool contended) { } -void VIDEO::MainScreenRB(unsigned int statestoadd, bool contended) { +void IRAM_ATTR VIDEO::MainScreenRB(unsigned int statestoadd, bool contended) { if (contended) statestoadd += wait_st[CPU::tstates - tstateDraw]; @@ -773,7 +782,7 @@ void IRAM_ATTR VIDEO::MainScreenRB_Pentagon(unsigned int statestoadd, bool conte } -void VIDEO::BottomBorder_Blank(unsigned int statestoadd, bool contended) { +void IRAM_ATTR VIDEO::BottomBorder_Blank(unsigned int statestoadd, bool contended) { CPU::tstates += statestoadd; @@ -812,7 +821,7 @@ void IRAM_ATTR VIDEO::BottomBorder_Blank_Pentagon(unsigned int statestoadd, bool } -void VIDEO::BottomBorder(unsigned int statestoadd, bool contended) { +void IRAM_ATTR VIDEO::BottomBorder(unsigned int statestoadd, bool contended) { CPU::tstates += statestoadd; @@ -848,7 +857,7 @@ void IRAM_ATTR VIDEO::BottomBorder_Pentagon(unsigned int statestoadd, bool conte } -void VIDEO::BottomBorder_OSD(unsigned int statestoadd, bool contended) { +void IRAM_ATTR VIDEO::BottomBorder_OSD(unsigned int statestoadd, bool contended) { CPU::tstates += statestoadd; @@ -904,22 +913,32 @@ void IRAM_ATTR VIDEO::Blank(unsigned int statestoadd, bool contended) { CPU::tstates += statestoadd; - if (CPU::tstates < tStatesPerLine) { - linedraw_cnt = 0; - tstateDraw = tStatesScreen; - Draw = Z80Ops::isPentagon ? &TopBorder_Blank_Pentagon : &TopBorder_Blank; - } + // if (CPU::tstates < tStatesPerLine) { + // linedraw_cnt = 0; + // tstateDraw = tStatesScreen; + // Draw = Z80Ops::isPentagon ? &TopBorder_Blank_Pentagon : &TopBorder_Blank; + // } + +} + +void IRAM_ATTR VIDEO::EndFrame() { + + linedraw_cnt = 0; + tstateDraw = tStatesScreen; + Draw = Z80Ops::isPentagon ? &TopBorder_Blank_Pentagon : &TopBorder_Blank; + framecnt++; } // /////////////////////////////////////////////////////////////////////////////// // // Flush -> Flush screen after HALT // /////////////////////////////////////////////////////////////////////////////// -void VIDEO::Flush() { +void IRAM_ATTR VIDEO::Flush() { - while (CPU::tstates < CPU::statesInFrame) { + // while (CPU::tstates < CPU::statesInFrame) { + while (Draw != &Blank) Draw(tStatesPerLine,false); - } + // } } diff --git a/src/ZXKeyb.cpp b/src/ZXKeyb.cpp index fffa2e39..5306b9f6 100644 --- a/src/ZXKeyb.cpp +++ b/src/ZXKeyb.cpp @@ -35,9 +35,8 @@ visit https://zxespectrum.speccy.org/contacto #include "ZXKeyb.h" #include "ESPectrum.h" -#include "Ports.h" -uint8_t ZXKeyb::ZXcols[8]; +uint8_t ZXKeyb::ZXcols[8] = { 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf }; bool ZXKeyb::Exists; void ZXKeyb::setup() @@ -58,109 +57,72 @@ void ZXKeyb::setup() ZXKeyb::putRows(0xFF); ZXKeyb::Exists = gpio_get_level((gpio_num_t)KM_COL_1) && gpio_get_level((gpio_num_t)KM_COL_2) && gpio_get_level((gpio_num_t)KM_COL_4); - // set all keys as not pressed - if (ZXKeyb::Exists) for (uint8_t i = 0; i < 8; i++) ZXcols[i] = 0xbf; - } -// row order depends on actual row association with address lines, see -// https://www.1000bit.it/support/manuali/sinclair/zxspectrum/sm/matrix.gif -static uint8_t roworder[8] = { - 0xDF, // % 11011111 - 0xFB, // % 11111011 - 0xFD, // % 11111101 - 0xFE, // % 11111110 - 0xF7, // % 11110111 - 0xEF, // % 11101111 - 0xBF, // % 10111111 - 0x7F // % 01111111 -}; - -void ZXKeyb::process() -{ - // traverse row indices - for (uint8_t rowidx = 0; rowidx < 8; rowidx++) - { - // first of all, take row pattern from table - uint8_t row_pattern = roworder[rowidx]; - // put row pattern to 8-tap membrane connector - // (using shift register) - putRows(row_pattern); - - // read column pattern from 5-tap membrane connector - // uint8_t cols = getCols(); - // // write column to port array at given row index - // Ports::port[rowidx] = cols; - - // read column pattern from 5-tap membrane connector - ZXcols[rowidx] = getCols(); +void ZXKeyb::process() { + + // Put row pattern to 8-tap membrane connector (using shift register) + // and read column pattern from 5-tap membrane connector. + // row order depends on actual row association with address lines, see + // https://www.1000bit.it/support/manuali/sinclair/zxspectrum/sm/matrix.gif + putRows(0b11011111); ZXcols[0] = getCols(); + putRows(0b11111011); ZXcols[1] = getCols(); + putRows(0b11111101); ZXcols[2] = getCols(); + putRows(0b11111110); ZXcols[3] = getCols(); + putRows(0b11110111); ZXcols[4] = getCols(); + putRows(0b11101111); ZXcols[5] = getCols(); + putRows(0b10111111); ZXcols[6] = getCols(); + putRows(0b01111111); ZXcols[7] = getCols(); - } } -// This function puts a row pattern -// into the membrane keyboard, for selecting a given row. +// This function puts a row pattern into the membrane keyboard, for selecting a given row. // Selection logic is active low, a 0 bit will select the row. -// A shift register is used for this task, -// so we'll need 3 output pins instead of 8. -void ZXKeyb::putRows(uint8_t row_pattern) -{ - // NOTICE: many usleeps have been commented out. - // If keyboard readings are erratic, - // maybe they should be recovered. +// A shift register is used for this task, so we'll need 3 output pins instead of 8. +void ZXKeyb::putRows(uint8_t row_pattern) { + + // NOTICE: many delays have been commented out. + // If keyboard readings are erratic, maybe they should be recovered. + + gpio_set_level((gpio_num_t)SR_LOAD, 0); // disable load pin, keep previous output + // delayMicroseconds(1); - // disable load pin, keep previous output - gpio_set_level((gpio_num_t)SR_LOAD, 0); - // usleep(1); + for (uint8_t i = 0; i < 8; i++) { // traverse bits in byte - // traverse bits in byte - for (uint8_t i = 0; i < 8; i++) { - // clock falling edge - gpio_set_level((gpio_num_t)SR_CLK, 0); - // usleep(1); + gpio_set_level((gpio_num_t)SR_CLK, 0); // clock falling edge + // delayMicroseconds(1); - // put row bit to shift register serial input - gpio_set_level((gpio_num_t)SR_DATA, row_pattern & 0x80); + gpio_set_level((gpio_num_t)SR_DATA, row_pattern & 0x80); // put row bit to shift register serial input - // just to be safe, wait just before rising edge - delayMicroseconds(1); - // usleep(1); + delayMicroseconds(1); // just to be safe, wait just before rising edge - // rising edge occurs here - gpio_set_level((gpio_num_t)SR_CLK, 1); - // usleep(1); + gpio_set_level((gpio_num_t)SR_CLK, 1); // rising edge occurs here + // delayMicroseconds(1); + + row_pattern <<= 1; // shift row bit pattern - // shift row bit pattern - row_pattern <<= 1; } - // enable load pin, update output - gpio_set_level((gpio_num_t)SR_LOAD, 1); + gpio_set_level((gpio_num_t)SR_LOAD, 1); // enable load pin, update output // this sleep is MANDATORY, do NOT remove it // or else (first column bits read will be wrong) delayMicroseconds(1); - // usleep(1); + } -// This function reads all 5 columns from the -// corresponding GPIO pins and concatenates them +// This function reads all 5 columns from the corresponding GPIO pins and concatenates them // into the lowest 5 bits of a byte. -uint8_t ZXKeyb::getCols() -{ - uint8_t cols = 0; - cols |= gpio_get_level((gpio_num_t)KM_COL_4); - cols <<= 1; - cols |= gpio_get_level((gpio_num_t)KM_COL_3); - cols <<= 1; - cols |= gpio_get_level((gpio_num_t)KM_COL_2); - cols <<= 1; - cols |= gpio_get_level((gpio_num_t)KM_COL_1); - cols <<= 1; - cols |= gpio_get_level((gpio_num_t)KM_COL_0); +uint8_t ZXKeyb::getCols() { + + uint8_t cols = gpio_get_level((gpio_num_t)KM_COL_4) << 1; + cols |= gpio_get_level((gpio_num_t)KM_COL_3); cols <<= 1; + cols |= gpio_get_level((gpio_num_t)KM_COL_2); cols <<= 1; + cols |= gpio_get_level((gpio_num_t)KM_COL_1); cols <<= 1; + cols |= gpio_get_level((gpio_num_t)KM_COL_0); - // Keep bits 5,7 up - cols |= 0xa0; + cols |= 0xa0; // Keep bits 5,7 up return cols; + } diff --git a/src/wd1793.cpp b/src/wd1793.cpp index cc5b0451..752e14f5 100644 --- a/src/wd1793.cpp +++ b/src/wd1793.cpp @@ -105,6 +105,8 @@ void WD1793::ExecuteCommand(unsigned char wdCmd) { } + // if ((StatusReg & STATUS_BUSY) != 0) return; + // set drive ready status bit StatusReg = ~((unsigned char)DriveReady) << 7; if(!DriveReady) { From d3561c619f7bc23e9400e44194c6c05116bd7e4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Iborra?= Date: Wed, 22 Nov 2023 14:30:16 +0100 Subject: [PATCH 04/30] Road to 1.0 --- include/AySound.h | 2 +- include/CPU.h | 21 +- include/ESPectrum.h | 31 +- include/MemESP.h | 10 +- include/OSDMain.h | 23 +- include/Ports.h | 17 +- include/Tape.h | 2 +- include/Video.h | 47 +- include/Z80_JLS/z80.h | 34 +- include/Z80_JLS/z80operations.h | 33 +- include/ZXKeyb.h | 1 - include/messages.h | 15 +- src/AySound.cpp | 6 +- src/CPU.cpp | 25 +- src/ESP32Lib/I2S/I2S.cpp | 2 +- src/ESP32Lib/I2S/I2S.h | 2 +- src/ESP32Lib/VGA/VGA6Bit.h | 2 +- src/ESPectrum.cpp | 357 ++++++------ src/FileUtils.cpp | 6 +- src/OSDMain.cpp | 160 ++---- src/OSDMenu.cpp | 925 ++++++++++++++++---------------- src/Ports.cpp | 28 +- src/Snapshot.cpp | 16 +- src/Tape.cpp | 7 +- src/Video.cpp | 328 +++++++---- src/Z80_JLS.cpp | 66 ++- src/wd1793.cpp | 2 +- 27 files changed, 1073 insertions(+), 1095 deletions(-) diff --git a/include/AySound.h b/include/AySound.h index f187e891..8c6613d5 100644 --- a/include/AySound.h +++ b/include/AySound.h @@ -156,7 +156,7 @@ class AySound static int set_stereo(ayemu_stereo_t stereo, int *custom_eq); static int set_sound_format(int freq, int chans, int bits); static void prepare_generation(); - static void IRAM_ATTR gen_sound(int bufsize, int bufpos); + static void gen_sound(int bufsize, int bufpos); static void(*updateReg[16])(); diff --git a/include/CPU.h b/include/CPU.h index 0c39acfd..c521100e 100644 --- a/include/CPU.h +++ b/include/CPU.h @@ -59,7 +59,7 @@ class CPU public: // call this for executing a frame's worth of instructions - static void IRAM_ATTR loop(); + static void loop(); // call this for resetting the CPU static void reset(); @@ -85,23 +85,4 @@ class CPU }; -static const unsigned char DRAM_ATTR wait_st[243] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 6, 5, 4, 3, 2, 1, 0, 0, 6, 5, 4, 3, 2, 1, 0, 0, - 6, 5, 4, 3, 2, 1, 0, 0, 6, 5, 4, 3, 2, 1, 0, 0, - 6, 5, 4, 3, 2, 1, 0, 0, 6, 5, 4, 3, 2, 1, 0, 0, - 6, 5, 4, 3, 2, 1, 0, 0, 6, 5, 4, 3, 2, 1, 0, 0, - 6, 5, 4, 3, 2, 1, 0, 0, 6, 5, 4, 3, 2, 1, 0, 0, - 6, 5, 4, 3, 2, 1, 0, 0, 6, 5, 4, 3, 2, 1, 0, 0, - 6, 5, 4, 3, 2, 1, 0, 0, 6, 5, 4, 3, 2, 1, 0, 0, - 6, 5, 4, 3, 2, 1, 0, 0, 6, 5, 4, 3, 2, 1, 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 -}; // sequence of wait states - #endif // CPU_h diff --git a/include/ESPectrum.h b/include/ESPectrum.h index fc4ca598..871d2091 100644 --- a/include/ESPectrum.h +++ b/include/ESPectrum.h @@ -62,23 +62,21 @@ class ESPectrum public: static void setup(); - // static void IRAM_ATTR loop(void* unused); - static void IRAM_ATTR loop(); + // static void loop(void* unused); + static void loop(); static void reset(); // static void loadRom(string arch, string romset); // Kbd - static void IRAM_ATTR processKeyboard(); + static void processKeyboard(); static void bootKeyboard(); - static bool IRAM_ATTR readKbd(fabgl::VirtualKeyItem *Nextkey); - static void IRAM_ATTR readKbdJoy(); + static bool readKbd(fabgl::VirtualKeyItem *Nextkey); + static void readKbdJoy(); static fabgl::PS2Controller PS2Controller; - static uint8_t PS2cols[8]; // Audio static uint8_t audioBuffer[ESP_AUDIO_SAMPLES_PENTAGON]; static uint8_t overSamplebuf[ESP_AUDIO_OVERSAMPLES_PENTAGON]; - // static uint8_t SamplebufAY[ESP_AUDIO_SAMPLES_48]; static signed char aud_volume; static uint32_t audbufcnt; static uint32_t audbufcntAY; @@ -86,16 +84,15 @@ class ESPectrum static uint32_t faudbufcntAY; static int lastaudioBit; static int faudioBit; - static void audioFrameStart(); - static void IRAM_ATTR BeeperGetSample(int Audiobit); - static void IRAM_ATTR AYGetSample(); - static void audioFrameEnd(); + // static void audioFrameStart(); + static void BeeperGetSample(int Audiobit); + static void AYGetSample(); + // static void audioFrameEnd(); static int overSamplesPerFrame; static int samplesPerFrame; static bool AY_emu; static int Audio_freq; static int sync_cnt; - static uint8_t *audbuffertosend; static int TapeNameScroller; @@ -118,7 +115,7 @@ class ESPectrum private: - static void IRAM_ATTR audioTask(void* unused); + static void audioTask(void* unused); }; @@ -127,15 +124,15 @@ class ESPectrum #define bitClear(value, bit) ((value) &= ~(1UL << (bit))) #define bitWrite(value, bit, bitvalue) ((bitvalue) ? bitSet(value, bit) : bitClear(value, bit)) -// int64_t IRAM_ATTR micros(); +// int64_t micros(); -unsigned long IRAM_ATTR millis(); +unsigned long millis(); -inline void IRAM_ATTR delay(uint32_t ms) +inline void delay(uint32_t ms) { vTaskDelay(ms / portTICK_PERIOD_MS); } -void IRAM_ATTR delayMicroseconds(int64_t us); +void delayMicroseconds(int64_t us); #endif \ No newline at end of file diff --git a/include/MemESP.h b/include/MemESP.h index 08b84a3e..0e467b7e 100644 --- a/include/MemESP.h +++ b/include/MemESP.h @@ -76,13 +76,9 @@ class MemESP static void writeword(uint16_t addr, uint16_t data); }; -static WORD_ALIGNED_ATTR DRAM_ATTR uint8_t staticMemPage0[0x4000] = { 0 }; -static WORD_ALIGNED_ATTR DRAM_ATTR uint8_t staticMemPage1[0x4000] = { 0 }; -static WORD_ALIGNED_ATTR DRAM_ATTR uint8_t staticMemPage2[0x4000] = { 0 }; - -// static uint8_t staticMemPage0[0x4000] = { 0 }; -// static uint8_t staticMemPage1[0x4000] = { 0 }; -// static uint8_t staticMemPage2[0x4000] = { 0 }; +static uint8_t staticMemPage0[0x4000] = { 0 }; +static uint8_t staticMemPage1[0x4000] = { 0 }; +static uint8_t staticMemPage2[0x4000] = { 0 }; /////////////////////////////////////////////////////////////////////////////// // diff --git a/include/OSDMain.h b/include/OSDMain.h index 17c30bfb..baa4c220 100644 --- a/include/OSDMain.h +++ b/include/OSDMain.h @@ -69,38 +69,38 @@ class OSD static unsigned short osdInsideX(); static unsigned short osdInsideY(); - // // OSD + // OSD static void osdHome(); static void osdAt(uint8_t row, uint8_t col); static void drawOSD(bool bottom_info); - static void drawStats(char *line1, char *line2); - static void do_OSD(fabgl::VirtualKey KeytoESP, uint8_t SHIFT); + static void drawStats(); + static void do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL); static void HWInfo(); - // // Error + // Error static void errorPanel(string errormsg); static void errorHalt(string errormsg); static void osdCenteredMsg(string msg, uint8_t warn_level); static void osdCenteredMsg(string msg, uint8_t warn_level, uint16_t millispause); - // // Menu + // Menu static unsigned short menuRealRowFor(uint8_t virtual_row_num); static bool menuIsSub(uint8_t virtual_row_num); static void menuPrintRow(uint8_t virtual_row_num, uint8_t line_type); static void menuRedraw(); static void WindowDraw(); static unsigned short menuRun(string new_menu); - static string menuFile(string &fdir, string title, uint8_t ftype, uint8_t mfcols, uint8_t mfrows); + static string fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols, uint8_t mfrows); static int menuTape(string title); static void menuScroll(bool up); - static void filemenuRedraw(string title, uint8_t ftype); - static void filemenuPrintRow(uint8_t virtual_row_num, uint8_t line_type); - static void filemenuScrollBar(uint8_t ftype); + static void fd_Redraw(string title, string fdir, uint8_t ftype); + static void fd_PrintRow(uint8_t virtual_row_num, uint8_t line_type); static void tapemenuRedraw(string title); static void PrintRow(uint8_t virtual_row_num, uint8_t line_type); static void menuAt(short int row, short int col); - static void menuScrollBar(); + static void menuScrollBar(unsigned short br); static void click(); + static void ZXKbdRead(); static uint8_t menu_level; static bool menu_saverect; static unsigned short menu_curopt; @@ -114,6 +114,9 @@ class OSD static esp_err_t updateFirmware(FILE *firmware); + static char stats_lin1[25]; // "CPU: 00000 / IDL: 00000 "; + static char stats_lin2[25]; // "FPS:000.00 / FND:000.00 "; + }; #endif // ESPECTRUM_OSD_H diff --git a/include/Ports.h b/include/Ports.h index d3ea5fa4..f408d2da 100644 --- a/include/Ports.h +++ b/include/Ports.h @@ -39,16 +39,19 @@ visit https://zxespectrum.speccy.org/contacto #include #include "ESPectrum.h" -class Ports -{ +class Ports { + public: + + static uint8_t input(uint16_t address); + static void output(uint16_t address, uint8_t data); static uint8_t port[128]; - static uint8_t port254; - static uint8_t IRAM_ATTR input(uint16_t address); - static void IRAM_ATTR output(uint16_t address, uint8_t data); - // static void ContendedIODelay(uint16_t portNumber); -}; +private: + static uint8_t port254; + static uint8_t speaker_values[8]; + +}; #endif // Ports_h diff --git a/include/Tape.h b/include/Tape.h index d891256c..b139dd96 100644 --- a/include/Tape.h +++ b/include/Tape.h @@ -119,7 +119,7 @@ class Tape static void Open(string name); static void TAP_Play(); static void TAP_Stop(); - static void IRAM_ATTR TAP_Read(); + static void TAP_Read(); static bool FlashLoad(); static void Save(); static uint32_t CalcTapBlockPos(int block); diff --git a/include/Video.h b/include/Video.h index 1b59fb55..6b5e4397 100644 --- a/include/Video.h +++ b/include/Video.h @@ -71,35 +71,38 @@ class VIDEO // Video draw functions /////////////////////////////////////////////////////////////////////////////////////////////////////// // Common + #ifdef NO_VIDEO static void NoVideo(unsigned int statestoadd, bool contended); - static void IRAM_ATTR EndFrame(); - static void IRAM_ATTR Blank(unsigned int statestoadd, bool contended); - static void IRAM_ATTR Flush(); // For flushing video buffer as fast as possible after HALT + #endif + // static void NoDraw(unsigned int statestoadd, bool contended); + static void EndFrame(); + static void Blank(unsigned int statestoadd, bool contended); + // static void Flush(); // For flushing video buffer as fast as possible after HALT // 48 / 128 - static void IRAM_ATTR TopBorder_Blank(unsigned int statestoadd, bool contended); - static void IRAM_ATTR TopBorder(unsigned int statestoadd, bool contended); - static void IRAM_ATTR MainScreen_Blank(unsigned int statestoadd, bool contended); - static void IRAM_ATTR MainScreenLB(unsigned int statestoadd, bool contended); + static void TopBorder_Blank(unsigned int statestoadd, bool contended); + static void TopBorder(unsigned int statestoadd, bool contended); + static void MainScreen_Blank(unsigned int statestoadd, bool contended); + static void MainScreenLB(unsigned int statestoadd, bool contended); static void MainScreen(unsigned int statestoadd, bool contended); static void MainScreen_OSD(unsigned int statestoadd, bool contended); - static void IRAM_ATTR MainScreenRB(unsigned int statestoadd, bool contended); - static void IRAM_ATTR BottomBorder_Blank(unsigned int statestoadd, bool contended); - static void IRAM_ATTR BottomBorder(unsigned int statestoadd, bool contended); - static void IRAM_ATTR BottomBorder_OSD(unsigned int statestoadd, bool contended); + static void MainScreenRB(unsigned int statestoadd, bool contended); + static void BottomBorder_Blank(unsigned int statestoadd, bool contended); + static void BottomBorder(unsigned int statestoadd, bool contended); + static void BottomBorder_OSD(unsigned int statestoadd, bool contended); // Pentagon - static void IRAM_ATTR TopBorder_Blank_Pentagon(unsigned int statestoadd, bool contended); - static void IRAM_ATTR TopBorder_Pentagon(unsigned int statestoadd, bool contended); - static void IRAM_ATTR MainScreen_Blank_Pentagon(unsigned int statestoadd, bool contended); - static void IRAM_ATTR MainScreenLB_Pentagon(unsigned int statestoadd, bool contended); + static void TopBorder_Blank_Pentagon(unsigned int statestoadd, bool contended); + static void TopBorder_Pentagon(unsigned int statestoadd, bool contended); + static void MainScreen_Blank_Pentagon(unsigned int statestoadd, bool contended); + static void MainScreenLB_Pentagon(unsigned int statestoadd, bool contended); static void MainScreen_Pentagon(unsigned int statestoadd, bool contended); static void MainScreen_Pentagon_delay(unsigned int statestoadd, bool contended); static void MainScreen_OSD_Pentagon(unsigned int statestoadd, bool contended); - static void IRAM_ATTR MainScreenRB_Pentagon(unsigned int statestoadd, bool contended); - static void IRAM_ATTR BottomBorder_Blank_Pentagon(unsigned int statestoadd, bool contended); - static void IRAM_ATTR BottomBorder_Pentagon(unsigned int statestoadd, bool contended); - static void IRAM_ATTR BottomBorder_OSD_Pentagon(unsigned int statestoadd, bool contended); + static void MainScreenRB_Pentagon(unsigned int statestoadd, bool contended); + static void BottomBorder_Blank_Pentagon(unsigned int statestoadd, bool contended); + static void BottomBorder_Pentagon(unsigned int statestoadd, bool contended); + static void BottomBorder_OSD_Pentagon(unsigned int statestoadd, bool contended); static uint8_t (*getFloatBusData)(); static uint8_t getFloatBusData48(); @@ -147,8 +150,8 @@ class VIDEO static unsigned int is169; -static WORD_ALIGNED_ATTR DRAM_ATTR uint16_t offBmp[SPEC_H]; -static WORD_ALIGNED_ATTR DRAM_ATTR uint16_t offAtt[SPEC_H]; +static uint16_t offBmp[SPEC_H]; +static uint16_t offAtt[SPEC_H]; // Colors for 6 bit mode // // BB GGRR @@ -178,7 +181,7 @@ static uint16_t spectrum_colors[NUM_SPECTRUM_COLORS] = { BRI_BLACK, BRI_BLUE, BRI_RED, BRI_MAGENTA, BRI_GREEN, BRI_CYAN, BRI_YELLOW, BRI_WHITE, }; -static WORD_ALIGNED_ATTR DRAM_ATTR uint32_t* AluBytes[16]; +static uint32_t* AluBytes[16]; // static unsigned char DrawStatus; diff --git a/include/Z80_JLS/z80.h b/include/Z80_JLS/z80.h index 2de58925..49d11fde 100644 --- a/include/Z80_JLS/z80.h +++ b/include/Z80_JLS/z80.h @@ -20,8 +20,6 @@ #include -#pragma GCC optimize ("O3") - #define Z80CPP_IS_LITTLE_ENDIAN 1 /* Union allowing a register pair to be accessed as bytes or as a word */ @@ -218,7 +216,7 @@ class Z80 { #ifdef WITH_BREAKPOINT_SUPPORT static bool breakpointEnabled {false}; #endif - static void IRAM_ATTR copyToRegister(uint8_t value); + static void copyToRegister(uint8_t value); public: // Constructor de la clase @@ -390,13 +388,13 @@ class Z80 { static void reset(void); // Execute one instruction - static void IRAM_ATTR execute(); - static void IRAM_ATTR exec_nocheck(); + static void execute(); + static void exec_nocheck(); // Check INT - static void IRAM_ATTR checkINT(void); + static void checkINT(void); - static void IRAM_ATTR incRegR(uint8_t inc); + static void incRegR(uint8_t inc); static void Xor(uint8_t oper8); @@ -491,28 +489,28 @@ class Z80 { static inline void push(uint16_t word); // LDI - static void IRAM_ATTR ldi(void); + static void ldi(void); // LDD - static void IRAM_ATTR ldd(void); + static void ldd(void); // CPI - static void IRAM_ATTR cpi(void); + static void cpi(void); // CPD - static void IRAM_ATTR cpd(void); + static void cpd(void); // INI - static void IRAM_ATTR ini(void); + static void ini(void); // IND - static void IRAM_ATTR ind(void); + static void ind(void); // OUTI - static void IRAM_ATTR outi(void); + static void outi(void); // OUTD - static void IRAM_ATTR outd(void); + static void outd(void); static void SetAbortedINxR_OTxRFlags(); @@ -750,7 +748,7 @@ class Z80 { static void decodeOpcodebd(void); static void decodeOpcodebe(void); - static void IRAM_ATTR decodeOpcodebf(void); // Used for LOAD TRAP + static void decodeOpcodebf(void); // Used for LOAD TRAP static void decodeOpcodec0(void); static void decodeOpcodec1(void); @@ -1092,8 +1090,8 @@ class Z80 { static void dcCBFE(void); static void dcCBFF(void); - static void IRAM_ATTR check_trdos(); - static void IRAM_ATTR check_trdos_unpage(); + static void check_trdos(); + static void check_trdos_unpage(); }; #endif // Z80CPP_H diff --git a/include/Z80_JLS/z80operations.h b/include/Z80_JLS/z80operations.h index 7f48d6df..646f0b6e 100644 --- a/include/Z80_JLS/z80operations.h +++ b/include/Z80_JLS/z80operations.h @@ -20,40 +20,19 @@ class Z80Ops { public: - /* Read opcode from RAM */ - // static uint8_t IRAM_ATTR fetchOpcode(uint16_t address); - /* Read/Write byte from/to RAM */ - static uint8_t IRAM_ATTR peek8(uint16_t address); - static void IRAM_ATTR poke8(uint16_t address, uint8_t value); + static uint8_t peek8(uint16_t address); + static void poke8(uint16_t address, uint8_t value); /* Read/Write word from/to RAM */ - static uint16_t IRAM_ATTR peek16(uint16_t adddress); - static void IRAM_ATTR poke16(uint16_t address, RegisterPair word); - - // /* In/Out byte from/to IO Bus */ - // static uint8_t IRAM_ATTR inPort(uint16_t port); - // static void IRAM_ATTR outPort(uint16_t port, uint8_t value); + static uint16_t peek16(uint16_t adddress); + static void poke16(uint16_t address, RegisterPair word); /* Put an address on bus lasting 'tstates' cycles */ - static void IRAM_ATTR addressOnBus(uint16_t address, int32_t wstates); - - /* Clocks needed for processing INT and NMI */ - // static void IRAM_ATTR interruptHandlingTime(int32_t wstates); + static void addressOnBus(uint16_t address, int32_t wstates); /* Callback to know when the INT signal is active */ - static bool IRAM_ATTR isActiveINT(void); - - // /* Signal HALT in tstates */ - // static void IRAM_ATTR signalHalt(); - - // static unsigned char (*delayContention)(unsigned int currentTstates); - // static unsigned char IRAM_ATTR delayContention48(unsigned int currentTstates); - // static unsigned char IRAM_ATTR delayContention128(unsigned int currentTstates); - - // static unsigned char (*delayContention)(); - // static unsigned char IRAM_ATTR delayContention48(); - // static unsigned char IRAM_ATTR delayContention128(); + static bool isActiveINT(void); static bool is48; static bool is128; diff --git a/include/ZXKeyb.h b/include/ZXKeyb.h index 56eace0d..474c98c6 100644 --- a/include/ZXKeyb.h +++ b/include/ZXKeyb.h @@ -47,7 +47,6 @@ class ZXKeyb { static uint8_t ZXcols[8]; static bool Exists; - // static uint8_t PrevFkeyOSD; private: diff --git a/include/messages.h b/include/messages.h index fe959c81..5b872721 100644 --- a/include/messages.h +++ b/include/messages.h @@ -41,7 +41,8 @@ visit https://zxespectrum.speccy.org/contacto #define MSG_LOADING_Z80 "Loading Z80 file" #define MSG_SAVE_CONFIG "Saving config file" #define MSG_VGA_INIT "Initializing VGA" -#define EMU_VERSION " v1.0pr " +// #define EMU_VERSION " v1.0 " +#define EMU_VERSION " Dev 221123 " // Error #define ERROR_TITLE " !!! ERROR - CLIVE MEDITATION !!! " @@ -62,13 +63,13 @@ visit https://zxespectrum.speccy.org/contacto static const char *OSD_PAUSE[2] = { OSD_PAUSE_EN,OSD_PAUSE_ES }; #define OSD_PSNA_NOT_AVAIL "No Persist Snapshot Available" -#define OSD_PSNA_LOADING "Loading Persist Snapshot..." -#define OSD_PSNA_SAVING "Saving Persist Snapshot..." +#define OSD_PSNA_LOADING "Loading Persist Snapshot" +#define OSD_PSNA_SAVING "Saving Persist Snapshot" #define OSD_PSNA_SAVE_WARN "Disk error. Trying slow mode, be patient" #define OSD_PSNA_SAVE_ERR "ERROR Saving Persist Snapshot" -#define OSD_PSNA_LOADED " Persist Snapshot Loaded " +#define OSD_PSNA_LOADED "Persist Snapshot Loaded" #define OSD_PSNA_LOAD_ERR "ERROR Loading Persist Snapshot" -#define OSD_PSNA_SAVED " Persist Snapshot Saved " +#define OSD_PSNA_SAVED "Persist Snapshot Saved" #define OSD_TAPE_FLASHLOAD "Flash loading TAP file" #define OSD_TAPE_LOAD_ERR "ERROR Loading TAP file" #define OSD_TAPE_SAVE_ERR "ERROR Saving TAP file" @@ -559,7 +560,7 @@ static const char *AboutMsg[2][6] = { " [Q] Hard reset\n"\ " [W] Reset ESP32\n"\ " [P] Pause\n"\ - " [C] BMP screenshot (SD folder /c)\n" + " [S] BMP screenshot (SD folder /c)\n" #define OSD_HELP_ES_ZX \ " Presione CAPS SHIFT + SYMBOL SHIFT y:\n"\ @@ -579,7 +580,7 @@ static const char *AboutMsg[2][6] = { " [Q] Reset completo\n"\ " [W] Resetear ESP32\n"\ " [P] Pausa\n"\ - " [C] Captura BMP (Carpeta SD /c)\n" + " [S] Captura BMP (Carpeta SD /c)\n" const uint8_t ESPectrum_logo[] = { 0x45, 0x42, 0x46, 0x38, 0xBB, 0x00, 0x1B, 0x00, 0xC0, 0xC0, 0xC0, 0xFF, diff --git a/src/AySound.cpp b/src/AySound.cpp index f5b49b38..7ff83edb 100644 --- a/src/AySound.cpp +++ b/src/AySound.cpp @@ -40,7 +40,7 @@ visit https://zxespectrum.speccy.org/contacto #include "hardconfig.h" #include "ESPectrum.h" -#pragma GCC optimize ("O3") +// #pragma GCC optimize("O3") /* emulator settings */ int AySound::table[32]; /**< table of volumes for chip */ @@ -114,7 +114,7 @@ static uint8_t Envelope [16][128]; // 5841, 8250, 11654, 16462, 23253, 32845, 46395, 65535 // }; -const uint8_t WORD_ALIGNED_ATTR DRAM_ATTR Rampa_AY_table[16] = {0,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31}; +DRAM_ATTR static const uint8_t Rampa_AY_table[16] = {0,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31}; // Borrowed from SoftSpectrum48 source code: // Values according to: http://forum.tslabs.info/viewtopic.php?f=6&t=539 @@ -304,7 +304,7 @@ void AySound::prepare_generation() // Generate sound. // Fill sound buffer with current register data // -void IRAM_ATTR AySound::gen_sound(int sound_bufsize, int bufpos) +IRAM_ATTR void AySound::gen_sound(int sound_bufsize, int bufpos) { int tmpvol; diff --git a/src/CPU.cpp b/src/CPU.cpp index 35425d43..2c7ff05b 100644 --- a/src/CPU.cpp +++ b/src/CPU.cpp @@ -42,7 +42,7 @@ visit https://zxespectrum.speccy.org/contacto #include "Video.h" #include "Z80_JLS/z80.h" -#pragma GCC optimize ("O3") +// #pragma GCC optimize("O3") uint32_t CPU::tstates = 0; uint64_t CPU::global_tstates = 0; @@ -102,8 +102,7 @@ void CPU::reset() { /////////////////////////////////////////////////////////////////////////////// -void IRAM_ATTR CPU::loop() -{ +IRAM_ATTR void CPU::loop() { while (tstates < IntEnd) Z80::execute(); @@ -125,7 +124,7 @@ void IRAM_ATTR CPU::loop() /////////////////////////////////////////////////////////////////////////////// -void CPU::FlushOnHalt() { +IRAM_ATTR void CPU::FlushOnHalt() { tstates &= 0x00FFFFFF; @@ -141,7 +140,11 @@ void CPU::FlushOnHalt() { } else { uint32_t pre_tstates = tstates; - VIDEO::Flush(); // Draw the rest of the frame + + // Flush the rest of frame + while (VIDEO::Draw != &VIDEO::Blank) + VIDEO::Draw(VIDEO::tStatesPerLine, false); + tstates = pre_tstates; pre_tstates += latetiming; @@ -161,21 +164,21 @@ void CPU::FlushOnHalt() { /////////////////////////////////////////////////////////////////////////////// // Read byte from RAM -uint8_t IRAM_ATTR Z80Ops::peek8(uint16_t address) { +IRAM_ATTR uint8_t Z80Ops::peek8(uint16_t address) { uint8_t page = address >> 14; VIDEO::Draw(3,MemESP::ramContended[page]); return MemESP::ramCurrent[page][address & 0x3fff]; } // Write byte to RAM -void IRAM_ATTR Z80Ops::poke8(uint16_t address, uint8_t value) { +IRAM_ATTR void Z80Ops::poke8(uint16_t address, uint8_t value) { uint8_t page = address >> 14; VIDEO::Draw(3, MemESP::ramContended[page]); if (page != 0) MemESP::ramCurrent[page][address & 0x3fff] = value; } // Read word from RAM -uint16_t IRAM_ATTR Z80Ops::peek16(uint16_t address) { +IRAM_ATTR uint16_t Z80Ops::peek16(uint16_t address) { uint8_t page = address >> 14; @@ -201,7 +204,7 @@ uint16_t IRAM_ATTR Z80Ops::peek16(uint16_t address) { } // Write word to RAM -void IRAM_ATTR Z80Ops::poke16(uint16_t address, RegisterPair word) { +IRAM_ATTR void Z80Ops::poke16(uint16_t address, RegisterPair word) { uint8_t page = address >> 14; @@ -229,7 +232,7 @@ void IRAM_ATTR Z80Ops::poke16(uint16_t address, RegisterPair word) { } /* Put an address on bus lasting 'tstates' cycles */ -void IRAM_ATTR Z80Ops::addressOnBus(uint16_t address, int32_t wstates) { +IRAM_ATTR void Z80Ops::addressOnBus(uint16_t address, int32_t wstates) { if (MemESP::ramContended[address >> 14]) { for (int idx = 0; idx < wstates; idx++) VIDEO::Draw(1, true); @@ -238,7 +241,7 @@ void IRAM_ATTR Z80Ops::addressOnBus(uint16_t address, int32_t wstates) { } /* Callback to know when the INT signal is active */ -bool IRAM_ATTR Z80Ops::isActiveINT(void) { +IRAM_ATTR bool Z80Ops::isActiveINT(void) { int tmp = CPU::tstates + CPU::latetiming; if (tmp >= CPU::statesInFrame) tmp -= CPU::statesInFrame; return ((tmp >= CPU::IntStart) && (tmp < CPU::IntEnd)); diff --git a/src/ESP32Lib/I2S/I2S.cpp b/src/ESP32Lib/I2S/I2S.cpp index daca3619..acc92079 100644 --- a/src/ESP32Lib/I2S/I2S.cpp +++ b/src/ESP32Lib/I2S/I2S.cpp @@ -30,7 +30,7 @@ I2S::I2S(const int i2sIndex) stopSignal = false; } -void IRAM_ATTR I2S::interruptStatic(void *arg) +IRAM_ATTR void I2S::interruptStatic(void *arg) { volatile i2s_dev_t &i2s = *i2sDevices[((I2S *)arg)->i2sIndex]; //i2s object not safely accesed in DRAM or IRAM diff --git a/src/ESP32Lib/I2S/I2S.h b/src/ESP32Lib/I2S/I2S.h index 2948de6c..f3821501 100644 --- a/src/ESP32Lib/I2S/I2S.h +++ b/src/ESP32Lib/I2S/I2S.h @@ -63,5 +63,5 @@ class I2S void setClock(long sampleRate, int bitCount, bool useAPLL = true); private: - static void IRAM_ATTR interruptStatic(void *arg); + static void interruptStatic(void *arg); }; diff --git a/src/ESP32Lib/VGA/VGA6Bit.h b/src/ESP32Lib/VGA/VGA6Bit.h index 1fd175bd..6f6f0a95 100644 --- a/src/ESP32Lib/VGA/VGA6Bit.h +++ b/src/ESP32Lib/VGA/VGA6Bit.h @@ -136,6 +136,6 @@ class VGA6Bit : public VGA, public GraphicsR2G2B2S2Swapped return VGA6Bit_useinterrupt; }; - static void IRAM_ATTR interrupt(void *arg); + static void interrupt(void *arg); }; diff --git a/src/ESPectrum.cpp b/src/ESPectrum.cpp index d4290402..d43f1b4e 100644 --- a/src/ESPectrum.cpp +++ b/src/ESPectrum.cpp @@ -66,8 +66,7 @@ visit https://zxespectrum.speccy.org/contacto using namespace std; -// works, but not needed for now -#pragma GCC optimize ("O3") +// #pragma GCC optimize("O3") //======================================================================================= // KEYBOARD @@ -115,22 +114,22 @@ WD1793 ESPectrum::Betadisk; #define NOP() {for(int i=0;i<1000;i++){}} #endif -// int64_t IRAM_ATTR micros() +// IRAM_ATTR int64_t micros() // { // return esp_timer_get_time(); // } -unsigned long IRAM_ATTR millis() +IRAM_ATTR unsigned long millis() { return (unsigned long) (esp_timer_get_time() / 1000ULL); } -// inline void IRAM_ATTR delay(uint32_t ms) +// inline void delay(uint32_t ms) // { // vTaskDelay(ms / portTICK_PERIOD_MS); // } -void IRAM_ATTR delayMicroseconds(int64_t us) +IRAM_ATTR void delayMicroseconds(int64_t us) { int64_t m = esp_timer_get_time(); if(us){ @@ -150,6 +149,9 @@ void IRAM_ATTR delayMicroseconds(int64_t us) // TIMING //======================================================================================= +static double totalseconds = 0; +static double totalsecondsnodelay = 0; + int64_t ESPectrum::target; //======================================================================================= @@ -645,7 +647,7 @@ void ESPectrum::reset() //======================================================================================= // KEYBOARD / KEMPSTON //======================================================================================= -bool IRAM_ATTR ESPectrum::readKbd(fabgl::VirtualKeyItem *Nextkey) { +IRAM_ATTR bool ESPectrum::readKbd(fabgl::VirtualKeyItem *Nextkey) { bool r = PS2Controller.keyboard()->getNextVirtualKey(Nextkey); // Global keys @@ -672,7 +674,7 @@ bool IRAM_ATTR ESPectrum::readKbd(fabgl::VirtualKeyItem *Nextkey) { // // Read second ps/2 port and inject on first queue // -void IRAM_ATTR ESPectrum::readKbdJoy() { +IRAM_ATTR void ESPectrum::readKbdJoy() { if (ps2kbd2) { @@ -688,12 +690,10 @@ void IRAM_ATTR ESPectrum::readKbdJoy() { } -uint8_t ESPectrum::PS2cols[8] = { 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf }; - -static int zxDelay = 0; - -void IRAM_ATTR ESPectrum::processKeyboard() { +IRAM_ATTR void ESPectrum::processKeyboard() { + static uint8_t PS2cols[8] = { 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf }; + static int zxDelay = 0; auto Kbd = PS2Controller.keyboard(); fabgl::VirtualKeyItem NextKey; fabgl::VirtualKey KeytoESP; @@ -717,10 +717,22 @@ void IRAM_ATTR ESPectrum::processKeyboard() { KeytoESP = NextKey.vk; Kdown = NextKey.down; - if ((Kdown) && (((KeytoESP >= fabgl::VK_F1) && (KeytoESP <= fabgl::VK_F12)) || (KeytoESP == fabgl::VK_PAUSE) || (KeytoESP == fabgl::VK_GRAVEACCENT ))) { - OSD::do_OSD(KeytoESP,NextKey.SHIFT); + if ((Kdown) && ((KeytoESP >= fabgl::VK_F1 && KeytoESP <= fabgl::VK_F12) || KeytoESP == fabgl::VK_PAUSE)) { + + OSD::do_OSD(KeytoESP,NextKey.CTRL); + Kbd->emptyVirtualKeyQueue(); + + // Set all zx keys as not pressed + for (uint8_t i = 0; i < 8; i++) ZXKeyb::ZXcols[i] = 0xbf; + zxDelay = 15; + + totalseconds = 0; + totalsecondsnodelay = 0; + VIDEO::framecnt = 0; + return; + } if (Config::CursorAsJoy) { @@ -1011,7 +1023,7 @@ void IRAM_ATTR ESPectrum::processKeyboard() { OSD::do_OSD(fabgl::VK_PAUSE,0); } else if (!bitRead(ZXKeyb::ZXcols[5],2)) { // I -> Info - OSD::do_OSD(fabgl::VK_GRAVEACCENT,0); + OSD::do_OSD(fabgl::VK_F1,true); } else if (!bitRead(ZXKeyb::ZXcols[1],1)) { // S -> Screen capture CaptureToBmp(); @@ -1068,7 +1080,7 @@ void IRAM_ATTR ESPectrum::processKeyboard() { //======================================================================================= // AUDIO //======================================================================================= -void IRAM_ATTR ESPectrum::audioTask(void *unused) { +IRAM_ATTR void ESPectrum::audioTask(void *unused) { size_t written; @@ -1093,13 +1105,15 @@ void IRAM_ATTR ESPectrum::audioTask(void *unused) { xQueueReceive(audioTaskQueue, ¶m, portMAX_DELAY); - pwm_audio_write(ESPectrum::audbuffertosend, samplesPerFrame, &written, 5 / portTICK_PERIOD_MS); + pwm_audio_write(ESPectrum::audioBuffer, samplesPerFrame, &written, 5 / portTICK_PERIOD_MS); xQueueReceive(audioTaskQueue, ¶m, portMAX_DELAY); // Finish fill of oversampled audio buffers - if (faudbufcnt < overSamplesPerFrame) - for (int i=faudbufcnt; i < overSamplesPerFrame;i++) overSamplebuf[i] = faudioBit; + if (faudbufcnt) { + if (faudbufcnt < overSamplesPerFrame) + for (int i=faudbufcnt; i < overSamplesPerFrame;i++) overSamplebuf[i] = faudioBit; + } // Downsample beeper (median) and mix AY channels to output buffer int beeper; @@ -1109,20 +1123,26 @@ void IRAM_ATTR ESPectrum::audioTask(void *unused) { if (faudbufcntAY < ESP_AUDIO_SAMPLES_128) AySound::gen_sound(ESP_AUDIO_SAMPLES_128 - faudbufcntAY , faudbufcntAY); - int n = 0; - for (int i=0;i 255 ? 255 : beeper; // Clamp - + if (faudbufcnt) { + int n = 0; + for (int i=0;i 255 ? 255 : beeper; // Clamp + } + } else { + for (int i = 0; i < ESP_AUDIO_SAMPLES_128; i++) { + beeper = faudioBit + AySound::SamplebufAY[i]; + audioBuffer[i] = beeper > 255 ? 255 : beeper; // Clamp + } } } else { @@ -1132,246 +1152,197 @@ void IRAM_ATTR ESPectrum::audioTask(void *unused) { AySound::gen_sound(samplesPerFrame - faudbufcntAY , faudbufcntAY); } - int n = 0; - for (int i=0;i < overSamplesPerFrame; i += 7) { - // Downsample (median) - beeper = overSamplebuf[i]; - beeper += overSamplebuf[i+1]; - beeper += overSamplebuf[i+2]; - beeper += overSamplebuf[i+3]; - beeper += overSamplebuf[i+4]; - beeper += overSamplebuf[i+5]; - beeper += overSamplebuf[i+6]; - - beeper = AY_emu ? (beeper / 7) + AySound::SamplebufAY[n] : beeper / 7; - // if (bmax < SamplebufAY[n]) bmax = SamplebufAY[n]; - audioBuffer[n++] = beeper > 255 ? 255 : beeper; // Clamp + if (faudbufcnt) { + int n = 0; + for (int i=0;i < overSamplesPerFrame; i += 7) { + // Downsample (median) + beeper = overSamplebuf[i]; + beeper += overSamplebuf[i+1]; + beeper += overSamplebuf[i+2]; + beeper += overSamplebuf[i+3]; + beeper += overSamplebuf[i+4]; + beeper += overSamplebuf[i+5]; + beeper += overSamplebuf[i+6]; + + beeper = AY_emu ? (beeper / 7) + AySound::SamplebufAY[n] : beeper / 7; + // if (bmax < SamplebufAY[n]) bmax = SamplebufAY[n]; + audioBuffer[n++] = beeper > 255 ? 255 : beeper; // Clamp + } + } else { + for (int i = 0; i < samplesPerFrame; i++) { + beeper = AY_emu ? faudioBit + AySound::SamplebufAY[i] : faudioBit; + audioBuffer[i] = beeper > 255 ? 255 : beeper; // Clamp + } } } } } -void ESPectrum::audioFrameStart() { - - xQueueSend(audioTaskQueue, ¶m, portMAX_DELAY); - - audbufcnt = 0; - audbufcntAY = 0; - -} - -void IRAM_ATTR ESPectrum::BeeperGetSample(int Audiobit) { +IRAM_ATTR void ESPectrum::BeeperGetSample(int Audiobit) { // Beeper audiobuffer generation (oversample) uint32_t audbufpos = Z80Ops::is128 ? CPU::tstates / 19 : CPU::tstates >> 4; - if (audbufpos != audbufcnt) { - for (int i=audbufcnt;i audbufcntAY) { + AySound::gen_sound(audbufpos - audbufcntAY, audbufcntAY); audbufcntAY = audbufpos; } } -void ESPectrum::audioFrameEnd() { - - faudbufcnt = audbufcnt; - faudioBit = lastaudioBit; - - faudbufcntAY = audbufcntAY; - - xQueueSend(audioTaskQueue, ¶m, portMAX_DELAY); - -} - //======================================================================================= // MAIN LOOP //======================================================================================= int ESPectrum::sync_cnt = 0; -uint8_t *ESPectrum::audbuffertosend = ESPectrum::audioBuffer; - volatile bool ESPectrum::vsync = false; -// void IRAM_ATTR ESPectrum::loop(void *unused) { -void IRAM_ATTR ESPectrum::loop() { +IRAM_ATTR void ESPectrum::loop() { -static char linea1[25]; // "CPU: 00000 / IDL: 00000 "; -static char linea2[25]; // "FPS:000.00 / FND:000.00 "; -static double totalseconds = 0; -static double totalsecondsnodelay = 0; int64_t ts_start, elapsed; int64_t idle; // int ESPmedian = 0; -// //////////////////////////////////////////////////////// -// Testing code: Start with stats on -// //////////////////////////////////////////////////////// -// if (Config::aspect_16_9) -// VIDEO::DrawOSD169 = VIDEO::MainScreen_OSD; -// else -// VIDEO::DrawOSD43 = VIDEO::BottomBorder_OSD; -// VIDEO::OSD = true; - -// //////////////////////////////////////////////////////// -// Testing code: Dump audio buffer to file -// //////////////////////////////////////////////////////// -// FILE *f = fopen("/sd/c/audioout.raw", "wb"); -// if (f==NULL) -// { -// printf("Error opening file for write.\n"); -// } -// uint32_t fpart = 0; - -// //////////////////////////////////////////////////////// -// Testing code: Start with key pressed -// //////////////////////////////////////////////////////// -// bitWrite(Ports::port[5], 4, 0); - for(;;) { ts_start = esp_timer_get_time(); - audioFrameStart(); + // Send audioBuffer to pwmaudio + xQueueSend(audioTaskQueue, ¶m, portMAX_DELAY); + audbufcnt = 0; + audbufcntAY = 0; CPU::loop(); - audioFrameEnd(); + // Process audio buffer + faudbufcnt = audbufcnt; + faudioBit = lastaudioBit; + faudbufcntAY = audbufcntAY; + xQueueSend(audioTaskQueue, ¶m, portMAX_DELAY); processKeyboard(); - // //////////////////////////////////////////////////////// - // Testing code: Dump audio buffer to file - // //////////////////////////////////////////////////////// - // if (fpart!=1001) fpart++; - // if (fpart<1000) { - // uint8_t* buffer = audioBuffer; - // fwrite(&buffer[0],samplesPerFrame,1,f); - // } else { - // if (fpart==1000) { - // fclose(f); - // printf("Audio dumped!\n"); - // } - // } - - // Draw stats, if activated, every 32 frames - if (((VIDEO::framecnt & 31) == 0) && (VIDEO::OSD)) OSD::drawStats(linea1,linea2); + // Update stats every 50 frames + if (VIDEO::framecnt == 1 && VIDEO::OSD) OSD::drawStats(); // Flashing flag change if (!(VIDEO::flash_ctr++ & 0x0f)) VIDEO::flashing ^= 0x80; // OSD calcs - totalsecondsnodelay += esp_timer_get_time() - ts_start; - if (totalseconds >= 1000000) { + if (VIDEO::framecnt) { + + totalsecondsnodelay += esp_timer_get_time() - ts_start; + + if (totalseconds >= 1000000) { - if (elapsed < 100000) { - - // printf("Tstates: %u, RegPC: %u\n",CPU::tstates,Z80::getRegPC()); + if (elapsed < 100000) { + + // printf("Tstates: %u, RegPC: %u\n",CPU::tstates,Z80::getRegPC()); - #ifdef LOG_DEBUG_TIMING - printf("===========================================================================\n"); - printf("[CPU] elapsed: %u; idle: %d\n", elapsed, idle); - printf("[Audio] Volume: %d\n", aud_volume); - printf("[Framecnt] %u; [Seconds] %f; [FPS] %f; [FPS (no delay)] %f\n", CPU::framecnt, totalseconds / 1000000, CPU::framecnt / (totalseconds / 1000000), CPU::framecnt / (totalsecondsnodelay / 1000000)); - // printf("[ESPoffset] %d\n", ESPoffset); - showMemInfo(); - #endif + #ifdef LOG_DEBUG_TIMING + printf("===========================================================================\n"); + printf("[CPU] elapsed: %u; idle: %d\n", elapsed, idle); + printf("[Audio] Volume: %d\n", aud_volume); + printf("[Framecnt] %u; [Seconds] %f; [FPS] %f; [FPS (no delay)] %f\n", CPU::framecnt, totalseconds / 1000000, CPU::framecnt / (totalseconds / 1000000), CPU::framecnt / (totalsecondsnodelay / 1000000)); + // printf("[ESPoffset] %d\n", ESPoffset); + showMemInfo(); + #endif - #ifdef TESTING_CODE + #ifdef TESTING_CODE - // printf("[Framecnt] %u; [Seconds] %f; [FPS] %f; [FPS (no delay)] %f\n", CPU::framecnt, totalseconds / 1000000, CPU::framecnt / (totalseconds / 1000000), CPU::framecnt / (totalsecondsnodelay / 1000000)); + // printf("[Framecnt] %u; [Seconds] %f; [FPS] %f; [FPS (no delay)] %f\n", CPU::framecnt, totalseconds / 1000000, CPU::framecnt / (totalseconds / 1000000), CPU::framecnt / (totalsecondsnodelay / 1000000)); - // showMemInfo(); - - snprintf(linea1, sizeof(linea1), "CPU: %05d / IDL: %05d ", (int)(elapsed), (int)(idle)); - // snprintf(linea1, sizeof(linea1), "CPU: %05d / TGT: %05d ", (int)elapsed, (int)target); - // snprintf(linea1, sizeof(linea1), "CPU: %05d / BMX: %05d ", (int)(elapsed), bmax); - // snprintf(linea1, sizeof(linea1), "CPU: %05d / OFF: %05d ", (int)(elapsed), (int)(ESPmedian/50)); + // showMemInfo(); + + snprintf(linea1, sizeof(linea1), "CPU: %05d / IDL: %05d ", (int)(elapsed), (int)(idle)); + // snprintf(linea1, sizeof(linea1), "CPU: %05d / TGT: %05d ", (int)elapsed, (int)target); + // snprintf(linea1, sizeof(linea1), "CPU: %05d / BMX: %05d ", (int)(elapsed), bmax); + // snprintf(linea1, sizeof(linea1), "CPU: %05d / OFF: %05d ", (int)(elapsed), (int)(ESPmedian/50)); - snprintf(linea2, sizeof(linea2), "FPS:%6.2f / FND:%6.2f ", CPU::framecnt / (totalseconds / 1000000), CPU::framecnt / (totalsecondsnodelay / 1000000)); + snprintf(linea2, sizeof(linea2), "FPS:%6.2f / FND:%6.2f ", CPU::framecnt / (totalseconds / 1000000), CPU::framecnt / (totalsecondsnodelay / 1000000)); - #else + #else - if (Tape::tapeStatus==TAPE_LOADING) { + if (Tape::tapeStatus==TAPE_LOADING) { - snprintf(linea1, sizeof(linea1), " %-12s %04d/%04d ", Tape::tapeFileName.substr(0 + TapeNameScroller, 12).c_str(), Tape::tapeCurBlock + 1, Tape::tapeNumBlocks); + snprintf(OSD::stats_lin1, sizeof(OSD::stats_lin1), " %-12s %04d/%04d ", Tape::tapeFileName.substr(0 + TapeNameScroller, 12).c_str(), Tape::tapeCurBlock + 1, Tape::tapeNumBlocks); - float percent = (float)((Tape::tapebufByteCount + Tape::tapePlayOffset) * 100) / (float)Tape::tapeFileSize; - snprintf(linea2, sizeof(linea2), " %05.2f%% %07d%s%07d ", percent, Tape::tapebufByteCount + Tape::tapePlayOffset, "/" , Tape::tapeFileSize); + float percent = (float)((Tape::tapebufByteCount + Tape::tapePlayOffset) * 100) / (float)Tape::tapeFileSize; + snprintf(OSD::stats_lin2, sizeof(OSD::stats_lin2), " %05.2f%% %07d%s%07d ", percent, Tape::tapebufByteCount + Tape::tapePlayOffset, "/" , Tape::tapeFileSize); - if ((++TapeNameScroller + 12) > Tape::tapeFileName.length()) TapeNameScroller = 0; + if ((++TapeNameScroller + 12) > Tape::tapeFileName.length()) TapeNameScroller = 0; - } else { + } else { - snprintf(linea1, sizeof(linea1), "CPU: %05d / IDL: %05d ", (int)(elapsed), (int)(idle)); - snprintf(linea2, sizeof(linea2), "FPS:%6.2f / FND:%6.2f ", VIDEO::framecnt / (totalseconds / 1000000), VIDEO::framecnt / (totalsecondsnodelay / 1000000)); + snprintf(OSD::stats_lin1, sizeof(OSD::stats_lin1), "CPU: %05d / IDL: %05d ", (int)(elapsed), (int)(idle)); + snprintf(OSD::stats_lin2, sizeof(OSD::stats_lin2), "FPS:%6.2f / FND:%6.2f ", VIDEO::framecnt / (totalseconds / 1000000), VIDEO::framecnt / (totalsecondsnodelay / 1000000)); + } + + #endif } - #endif + totalseconds = 0; + totalsecondsnodelay = 0; + VIDEO::framecnt = 0; + + // ESPmedian = 0; + } + + elapsed = esp_timer_get_time() - ts_start; + idle = target - elapsed - ESPoffset; - totalseconds = 0; - totalsecondsnodelay = 0; - VIDEO::framecnt = 0; + #ifdef VIDEO_FRAME_TIMING - // ESPmedian = 0; + if(Config::videomode) { - } - - elapsed = esp_timer_get_time() - ts_start; - idle = target - elapsed - ESPoffset; + if (sync_cnt++ == 0) { + if (idle > 0) { + delayMicroseconds(idle); + } + } else { - #ifdef VIDEO_FRAME_TIMING + // Audio sync (once every 250 frames ~ 2,5 seconds) + if (sync_cnt++ == 250) { + ESPoffset = 128 - pwm_audio_rbstats(); + sync_cnt = 0; + } - if(Config::videomode) { + // Wait for vertical sync + for (;;) { + if (vsync) break; + } + + } + + } else { - if (sync_cnt++ == 0) { if (idle > 0) { delayMicroseconds(idle); } - } else { - // Audio sync (once every 250 frames ~ 2,5 seconds) - if (sync_cnt++ == 250) { + // Audio sync + if (sync_cnt++ & 0x0f) { ESPoffset = 128 - pwm_audio_rbstats(); sync_cnt = 0; } - // Wait for vertical sync - for (;;) { - if (vsync) break; - } - - } - - } else { - - if (idle > 0) { - delayMicroseconds(idle); - } + // ESPmedian += ESPoffset; - // Audio sync - if (sync_cnt++ & 0x0f) { - ESPoffset = 128 - pwm_audio_rbstats(); - sync_cnt = 0; } + + #endif - // ESPmedian += ESPoffset; + totalseconds += esp_timer_get_time() - ts_start; } - - #endif - - totalseconds += esp_timer_get_time() - ts_start; } diff --git a/src/FileUtils.cpp b/src/FileUtils.cpp index 2683c202..f584a53f 100644 --- a/src/FileUtils.cpp +++ b/src/FileUtils.cpp @@ -65,9 +65,9 @@ string FileUtils::SNA_Path = "/"; // DISK_SNA_DIR; // Current path on the SD (fo string FileUtils::TAP_Path = "/"; // DISK_TAP_DIR; // Current path on the SD (for future folder support) string FileUtils::DSK_Path = "/"; // DISK_DSK_DIR; // Current path on the SD (for future folder support) DISK_FTYPE FileUtils::fileTypes[3] = { - {".sna,.SNA,.z80,.Z80",".s",1,1}, - {".tap,.TAP",".t",1,1}, - {".trd,.TRD,.scl,.SCL",".d",1,1} + {".sna,.SNA,.z80,.Z80",".s",2,2}, + {".tap,.TAP",".t",2,2}, + {".trd,.TRD,.scl,.SCL",".d",2,2} }; void FileUtils::initFileSystem() { diff --git a/src/OSDMain.cpp b/src/OSDMain.cpp index 91a83794..8eb3ec98 100644 --- a/src/OSDMain.cpp +++ b/src/OSDMain.cpp @@ -86,6 +86,9 @@ unsigned int OSD::SaveRectpos = 0; unsigned short OSD::scrW = 320; unsigned short OSD::scrH = 240; +char OSD::stats_lin1[25]; // "CPU: 00000 / IDL: 00000 "; +char OSD::stats_lin2[25]; // "FPS:000.00 / FND:000.00 "; + // // X origin to center an element with pixel_width unsigned short OSD::scrAlignCenterX(unsigned short pixel_width) { return (scrW / 2) - (pixel_width / 2); } @@ -146,7 +149,7 @@ void OSD::drawOSD(bool bottom_info) { osdHome(); } -void OSD::drawStats(char *line1, char *line2) { +void OSD::drawStats() { unsigned short x,y; @@ -161,9 +164,9 @@ void OSD::drawStats(char *line1, char *line2) { VIDEO::vga.setTextColor(OSD::zxColor(7, 0), OSD::zxColor(1, 0)); VIDEO::vga.setFont(Font6x8); VIDEO::vga.setCursor(x,y); - VIDEO::vga.print(line1); + VIDEO::vga.print(stats_lin1); VIDEO::vga.setCursor(x,y+8); - VIDEO::vga.print(line2); + VIDEO::vga.print(stats_lin2); } @@ -202,8 +205,6 @@ static bool persistSave(uint8_t slotnumber) static bool persistLoad(uint8_t slotnumber) { - OSD::osdCenteredMsg(OSD_PSNA_LOADING, LEVEL_INFO, 0); - char persistfname[sizeof(DISK_PSNA_FILE) + 6]; char persistfinfo[sizeof(DISK_PSNA_FILE) + 6]; @@ -219,9 +220,11 @@ static bool persistLoad(uint8_t slotnumber) string finfo = FileUtils::MountPoint + DISK_PSNA_DIR + "/" + persistfinfo; FILE *f = fopen(finfo.c_str(), "r"); if (f == NULL) { - printf("Error opening %s\n",persistfinfo); + OSD::osdCenteredMsg(OSD_PSNA_LOAD_ERR, LEVEL_WARN); + // printf("Error opening %s\n",persistfinfo); return false; } + char buf[256]; fgets(buf, sizeof(buf),f); string persist_arch = buf; @@ -239,7 +242,6 @@ static bool persistLoad(uint8_t slotnumber) Config::save("ram"); #endif Config::last_ram_file = Config::ram_file; - // OSD::osdCenteredMsg(OSD_PSNA_LOADED, LEVEL_INFO); } } @@ -247,16 +249,13 @@ static bool persistLoad(uint8_t slotnumber) } -#define REPDEL 140 // As in real ZX Spectrum (700 ms.) -static int zxDelay = 0; - // OSD Main Loop -void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t SHIFT) { +void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL) { static uint8_t last_sna_row = 0; fabgl::VirtualKeyItem Nextkey; - if (SHIFT) { + if (CTRL) { if (KeytoESP == fabgl::VK_F5) { if (Config::CenterH > -16) Config::CenterH--; @@ -278,52 +277,29 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t SHIFT) { Config::save("CenterV"); osdCenteredMsg("Vert. center: " + to_string(Config::CenterV), LEVEL_INFO, 375); + } else + if (KeytoESP == fabgl::VK_F1) { // Show mem info + OSD::HWInfo(); } } else { if (KeytoESP == fabgl::VK_PAUSE) { + click(); - osdCenteredMsg(OSD_PAUSE[Config::lang], LEVEL_INFO, 1000); - // while (1) { - // ESPectrum::readKbdJoy(); - // while (ESPectrum::PS2Controller.keyboard()->virtualKeyAvailable()) { - // if (ESPectrum::readKbd(&Nextkey)) - // if (Nextkey.down) - // if (Nextkey.vk == fabgl::VK_PAUSE) { - // click(); - // return; - // } else - // osdCenteredMsg(OSD_PAUSE[Config::lang], LEVEL_INFO, 500); - // } - // vTaskDelay(5 / portTICK_PERIOD_MS); - // } - - zxDelay = REPDEL; + osdCenteredMsg(OSD_PAUSE[Config::lang], LEVEL_INFO, 1000); while (1) { - if (ZXKeyb::Exists) { - - ZXKeyb::process(); - - if ((!bitRead(ZXKeyb::ZXcols[6], 0)) || (!bitRead(ZXKeyb::ZXcols[7], 0)) || (!bitRead(ZXKeyb::ZXcols[5], 0))) { // ENTER, BREAK, P - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAUSE, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAUSE, false, false); - zxDelay = REPDEL; - } - } - - } + ZXKbdRead(); ESPectrum::readKbdJoy(); if (ESPectrum::PS2Controller.keyboard()->virtualKeyAvailable()) { if (ESPectrum::readKbd(&Nextkey)) { if(!Nextkey.down) continue; - if (Nextkey.vk == fabgl::VK_PAUSE) { + if (Nextkey.vk == fabgl::VK_RETURN || Nextkey.vk == fabgl::VK_ESCAPE || Nextkey.vk == fabgl::VK_PAUSE) { click(); break; } else @@ -333,20 +309,13 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t SHIFT) { vTaskDelay(5 / portTICK_PERIOD_MS); - if (zxDelay > 0) zxDelay--; - } - } - else if (KeytoESP == fabgl::VK_GRAVEACCENT) { // Show mem info - - OSD::HWInfo(); - } else if (KeytoESP == fabgl::VK_F2) { menu_level = 0; menu_saverect = false; - string mFile = menuFile(FileUtils::SNA_Path, MENU_SNA_TITLE[Config::lang],DISK_SNAFILE,51,22); + string mFile = fileDialog(FileUtils::SNA_Path, MENU_SNA_TITLE[Config::lang],DISK_SNAFILE,51,22); if (mFile != "") { mFile.erase(0, 1); string fname = FileUtils::MountPoint + "/" + FileUtils::SNA_Path + "/" + mFile; @@ -379,7 +348,7 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t SHIFT) { else if (KeytoESP == fabgl::VK_F5) { menu_level = 0; menu_saverect = false; - string mFile = menuFile(FileUtils::TAP_Path, MENU_TAP_TITLE[Config::lang],DISK_TAPFILE,51,22); + string mFile = fileDialog(FileUtils::TAP_Path, MENU_TAP_TITLE[Config::lang],DISK_TAPFILE,51,22); if (mFile != "") { string keySel = mFile.substr(0,1); @@ -457,24 +426,25 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t SHIFT) { VIDEO::DrawOSD43 = Z80Ops::isPentagon ? VIDEO::BottomBorder_OSD_Pentagon : VIDEO::BottomBorder_OSD; VIDEO::OSD = true; ESPectrum::TapeNameScroller = 0; + OSD::drawStats(); } click(); } else if (KeytoESP == fabgl::VK_F9) { // Volume down if (ESPectrum::aud_volume>-16) { - click(); - ESPectrum::aud_volume--; - pwm_audio_set_volume(ESPectrum::aud_volume); + ESPectrum::aud_volume--; + pwm_audio_set_volume(ESPectrum::aud_volume); + click(); } - // osdCenteredMsg("Volume: " + to_string(ESPectrum::aud_volume + 16), LEVEL_INFO, 125); + // osdCenteredMsg("Volume: " + to_string(ESPectrum::aud_volume + 16), LEVEL_INFO, 125); } else if (KeytoESP == fabgl::VK_F10) { // Volume up if (ESPectrum::aud_volume<0) { - click(); - ESPectrum::aud_volume++; - pwm_audio_set_volume(ESPectrum::aud_volume); + ESPectrum::aud_volume++; + pwm_audio_set_volume(ESPectrum::aud_volume); + click(); } - // osdCenteredMsg("Volume: " + to_string(ESPectrum::aud_volume + 16), LEVEL_INFO, 125); + // osdCenteredMsg("Volume: " + to_string(ESPectrum::aud_volume + 16), LEVEL_INFO, 125); } // else if (KeytoESP == fabgl::VK_F9) { // ESPectrum::ESPoffset -= 5; @@ -525,7 +495,7 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t SHIFT) { menu_level = 2; menu_saverect = true; if (sna_mnu == 1) { - string mFile = menuFile(FileUtils::SNA_Path, MENU_SNA_TITLE[Config::lang],DISK_SNAFILE,30,16); + string mFile = fileDialog(FileUtils::SNA_Path, MENU_SNA_TITLE[Config::lang],DISK_SNAFILE,30,16); if (mFile != "") { mFile.erase(0, 1); string fname = FileUtils::MountPoint + "/" + FileUtils::SNA_Path + "/" + mFile; @@ -587,7 +557,7 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t SHIFT) { if (tap_num == 1) { // menu_curopt = 1; // Select TAP File - string mFile = menuFile(FileUtils::TAP_Path, MENU_TAP_TITLE[Config::lang],DISK_TAPFILE,30,16); + string mFile = fileDialog(FileUtils::TAP_Path, MENU_TAP_TITLE[Config::lang],DISK_TAPFILE,30,16); if (mFile != "") { string keySel = mFile.substr(0,1); @@ -685,7 +655,7 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t SHIFT) { if (opt2 > 0) { if (opt2 == 1) { menu_saverect = true; - string mFile = menuFile(FileUtils::DSK_Path, MENU_DSK_TITLE[Config::lang],DISK_DSKFILE,30,16); + string mFile = fileDialog(FileUtils::DSK_Path, MENU_DSK_TITLE[Config::lang],DISK_DSKFILE,30,16); if (mFile != "") { mFile.erase(0, 1); string fname = FileUtils::MountPoint + "/" + FileUtils::DSK_Path + "/" + mFile; @@ -1169,23 +1139,9 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t SHIFT) { else VIDEO::vga.print(Config::lang ? OSD_HELP_ES : OSD_HELP_EN); - zxDelay = REPDEL; - while (1) { - if (ZXKeyb::Exists) { - - ZXKeyb::process(); - - if ((!bitRead(ZXKeyb::ZXcols[6], 0)) || (!bitRead(ZXKeyb::ZXcols[7], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 1))) { // ENTER, BREAK, 0, 9 - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, false, false); - zxDelay = REPDEL; - } - } - - } + ZXKbdRead(); ESPectrum::readKbdJoy(); @@ -1198,8 +1154,6 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t SHIFT) { vTaskDelay(5 / portTICK_PERIOD_MS); - if (zxDelay > 0) zxDelay--; - } click(); @@ -1230,9 +1184,6 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t SHIFT) { VIDEO::vga.setTextColor(OSD::zxColor(7, 0), OSD::zxColor(1, 0)); // VIDEO::vga.print(Config::lang ? OSD_ABOUT1_ES : OSD_ABOUT1_EN); - #define REPABOUT 35 - zxDelay = REPABOUT; - pos_x = Config::aspect_16_9 ? 66 : 46; pos_y = Config::aspect_16_9 ? 68 : 88; int osdRow = 0; int osdCol = 0; @@ -1294,19 +1245,7 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t SHIFT) { VIDEO::vga.fillRect(pos_x + ((osdCol + 1) * 6), pos_y + (osdRow * 8), 6,8, cursorCol ); - if (ZXKeyb::Exists) { - - ZXKeyb::process(); - - if ((!bitRead(ZXKeyb::ZXcols[6], 0)) || (!bitRead(ZXKeyb::ZXcols[7], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 1))) { // ENTER, BREAK, 0, 9 - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, false, false); - zxDelay = REPABOUT; - } - } - - } + ZXKbdRead(); ESPectrum::readKbdJoy(); @@ -1318,8 +1257,6 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t SHIFT) { } vTaskDelay(20 / portTICK_PERIOD_MS); - - if (zxDelay > 0) zxDelay--; } @@ -1374,14 +1311,17 @@ void OSD::osdCenteredMsg(string msg, uint8_t warn_level) { void OSD::osdCenteredMsg(string msg, uint8_t warn_level, uint16_t millispause) { - const unsigned short w = (msg.length() + 2) * OSD_FONT_W; const unsigned short h = OSD_FONT_H * 3; - const unsigned short x = scrAlignCenterX(w); const unsigned short y = scrAlignCenterY(h); unsigned short paper; unsigned short ink; unsigned int j; + if (msg.length() > (scrW / 6) - 4) msg = msg.substr(0,(scrW / 6) - 4); + + const unsigned short w = (msg.length() + 2) * OSD_FONT_W; + const unsigned short x = scrAlignCenterX(w); + switch (warn_level) { case LEVEL_OK: ink = OSD::zxColor(7, 1); @@ -1442,7 +1382,6 @@ void OSD::osdCenteredMsg(string msg, uint8_t warn_level, uint16_t millispause) { unsigned short OSD::rowCount(string menu) { unsigned short count = 0; for (unsigned short i = 0; i < menu.length(); i++) { -// for (unsigned short i = 1; i < menu.length(); i++) { if (menu.at(i) == ASCII_NL) { count++; } @@ -1575,31 +1514,16 @@ void OSD::HWInfo() { VIDEO::vga.print(textout.c_str()); // Wait for key - - zxDelay = REPDEL; - while (1) { - if (ZXKeyb::Exists) { - - ZXKeyb::process(); - - if ((!bitRead(ZXKeyb::ZXcols[6], 0)) || (!bitRead(ZXKeyb::ZXcols[7], 0)) || (!bitRead(ZXKeyb::ZXcols[5], 2))) { // ENTER, BREAK or I - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, false, false); - zxDelay = REPDEL; - } - } - - } + ZXKbdRead(); ESPectrum::readKbdJoy(); if (ESPectrum::PS2Controller.keyboard()->virtualKeyAvailable()) { ESPectrum::PS2Controller.keyboard()->getNextVirtualKey(&Nextkey); if(!Nextkey.down) continue; - if (Nextkey.vk == fabgl::VK_RETURN || Nextkey.vk == fabgl::VK_ESCAPE || Nextkey.vk == fabgl::VK_GRAVEACCENT) { + if (Nextkey.vk == fabgl::VK_RETURN || Nextkey.vk == fabgl::VK_ESCAPE || Nextkey.vk == fabgl::VK_F1) { click(); break; } @@ -1607,8 +1531,6 @@ void OSD::HWInfo() { vTaskDelay(5 / portTICK_PERIOD_MS); - if (zxDelay > 0) zxDelay--; - } } diff --git a/src/OSDMenu.cpp b/src/OSDMenu.cpp index 321679f6..7a243602 100644 --- a/src/OSDMenu.cpp +++ b/src/OSDMenu.cpp @@ -61,6 +61,7 @@ using namespace std; #define IS_TITLE 0 #define IS_FOCUSED 1 #define IS_NORMAL 2 +#define IS_INFO 3 // Scroll #define UP true #define DOWN false @@ -83,19 +84,19 @@ static uint8_t focus = 1; // Focused virtual row static uint8_t last_focus = 0; // To check for changes static unsigned short last_begin_row = 0; // To check for changes -const uint8_t /*DRAM_ATTR*/ click48[12]={0,0x16,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x16,0}; +DRAM_ATTR static const uint8_t click48[12]={ 0,0x16,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x16,0 }; -const uint8_t /*DRAM_ATTR*/ click128[116]= { 0x00,0x16,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61, - 0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61, - 0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61, - 0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61, - 0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61, - 0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61, - 0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61, - 0x61,0x61,0x16,0x00 - }; +DRAM_ATTR static const uint8_t click128[116]= { 0x00,0x16,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61, + 0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61, + 0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61, + 0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61, + 0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61, + 0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61, + 0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61, + 0x61,0x61,0x16,0x00 + }; -void IRAM_ATTR OSD::click() { +IRAM_ATTR void OSD::click() { size_t written; @@ -232,10 +233,10 @@ void OSD::WindowDraw() { } -#define REPDEL 140 // As in real ZX Spectrum (700 ms.) -#define REPPER 20 // As in real ZX Spectrum (100 ms.) -static int zxDelay = 0; -static int lastzxKey = 0; +// #define REPDEL 140 // As in real ZX Spectrum (700 ms.) +// #define REPPER 20 // As in real ZX Spectrum (100 ms.) +// static int zxDelay = 0; +// static int lastzxKey = 0; // Run a new menu unsigned short OSD::menuRun(string new_menu) { @@ -293,96 +294,98 @@ unsigned short OSD::menuRun(string new_menu) { menuRedraw(); // Draw menu content - zxDelay = REPDEL; - lastzxKey = 0; + // zxDelay = REPDEL; + // lastzxKey = 0; while (1) { if (ZXKeyb::Exists) { - ZXKeyb::process(); - - if (!bitRead(ZXKeyb::ZXcols[4], 3)) { // 6 DOWN - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_UP, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_UP, false, false); - if (lastzxKey == 1) - zxDelay = REPPER; - else - zxDelay = REPDEL; - lastzxKey = 1; - } - } else - if (!bitRead(ZXKeyb::ZXcols[4], 4)) { // 7 UP (Yes, like the drink's name, I know... :D) - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_DOWN, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_DOWN, false, false); - if (lastzxKey == 2) - zxDelay = REPPER; - else - zxDelay = REPDEL; - lastzxKey = 2; - } - } else - if ((!bitRead(ZXKeyb::ZXcols[6], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 0))) { // ENTER - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, false, false); - if (lastzxKey == 3) - zxDelay = REPPER; - else - zxDelay = REPDEL; - lastzxKey = 3; - } - } else - if ((!bitRead(ZXKeyb::ZXcols[7], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 1))) { // BREAK - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_ESCAPE, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_ESCAPE, false, false); - if (lastzxKey == 4) - zxDelay = REPPER; - else - zxDelay = REPDEL; - lastzxKey = 4; - } - } else - if (!bitRead(ZXKeyb::ZXcols[3], 4)) { // LEFT - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEUP, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEUP, false, false); - if (lastzxKey == 5) - zxDelay = REPPER; - else - zxDelay = REPDEL; - lastzxKey = 5; - } - } else - if (!bitRead(ZXKeyb::ZXcols[4], 2)) { // RIGHT - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEDOWN, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEDOWN, false, false); - if (lastzxKey == 6) - zxDelay = REPPER; - else - zxDelay = REPDEL; - lastzxKey = 6; - } - } else - if (!bitRead(ZXKeyb::ZXcols[1], 1)) { // S (Capture screen) - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PRINTSCREEN, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PRINTSCREEN, false, false); - if (lastzxKey == 7) - zxDelay = REPPER; - else - zxDelay = REPDEL; - lastzxKey = 7; - } - } else - { - zxDelay = 0; - lastzxKey = 0; - } + ZXKbdRead(); + + // ZXKeyb::process(); + + // if (!bitRead(ZXKeyb::ZXcols[4], 3)) { // 6 DOWN + // if (zxDelay == 0) { + // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_UP, true, false); + // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_UP, false, false); + // if (lastzxKey == 1) + // zxDelay = REPPER; + // else + // zxDelay = REPDEL; + // lastzxKey = 1; + // } + // } else + // if (!bitRead(ZXKeyb::ZXcols[4], 4)) { // 7 UP (Yes, like the drink's name, I know... :D) + // if (zxDelay == 0) { + // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_DOWN, true, false); + // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_DOWN, false, false); + // if (lastzxKey == 2) + // zxDelay = REPPER; + // else + // zxDelay = REPDEL; + // lastzxKey = 2; + // } + // } else + // if ((!bitRead(ZXKeyb::ZXcols[6], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 0))) { // ENTER + // if (zxDelay == 0) { + // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, true, false); + // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, false, false); + // if (lastzxKey == 3) + // zxDelay = REPPER; + // else + // zxDelay = REPDEL; + // lastzxKey = 3; + // } + // } else + // if ((!bitRead(ZXKeyb::ZXcols[7], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 1))) { // BREAK + // if (zxDelay == 0) { + // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_ESCAPE, true, false); + // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_ESCAPE, false, false); + // if (lastzxKey == 4) + // zxDelay = REPPER; + // else + // zxDelay = REPDEL; + // lastzxKey = 4; + // } + // } else + // if (!bitRead(ZXKeyb::ZXcols[3], 4)) { // LEFT + // if (zxDelay == 0) { + // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEUP, true, false); + // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEUP, false, false); + // if (lastzxKey == 5) + // zxDelay = REPPER; + // else + // zxDelay = REPDEL; + // lastzxKey = 5; + // } + // } else + // if (!bitRead(ZXKeyb::ZXcols[4], 2)) { // RIGHT + // if (zxDelay == 0) { + // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEDOWN, true, false); + // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEDOWN, false, false); + // if (lastzxKey == 6) + // zxDelay = REPPER; + // else + // zxDelay = REPDEL; + // lastzxKey = 6; + // } + // } else + // if (!bitRead(ZXKeyb::ZXcols[1], 1)) { // S (Capture screen) + // if (zxDelay == 0) { + // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PRINTSCREEN, true, false); + // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PRINTSCREEN, false, false); + // if (lastzxKey == 7) + // zxDelay = REPPER; + // else + // zxDelay = REPDEL; + // lastzxKey = 7; + // } + // } else + // { + // zxDelay = 0; + // lastzxKey = 0; + // } } @@ -460,7 +463,7 @@ unsigned short OSD::menuRun(string new_menu) { begin_row = real_rows - virtual_rows + 1; menuRedraw(); click(); - } else if (Menukey.vk == fabgl::VK_RETURN) { + } else if (Menukey.vk == fabgl::VK_RETURN || Menukey.vk == fabgl::VK_SPACE) { click(); menu_prevopt = menuRealRowFor(focus); return menu_prevopt; @@ -487,9 +490,10 @@ unsigned short OSD::menuRun(string new_menu) { } } } + vTaskDelay(5 / portTICK_PERIOD_MS); - if (zxDelay > 0) zxDelay--; + // if (zxDelay > 0) zxDelay--; } @@ -520,7 +524,7 @@ void OSD::menuRedraw() { menuPrintRow(row, IS_NORMAL); } } - menuScrollBar(); + menuScrollBar(begin_row); last_focus = focus; last_begin_row = begin_row; @@ -529,12 +533,12 @@ void OSD::menuRedraw() { } // Draw menu scroll bar -void OSD::menuScrollBar() { +void OSD::menuScrollBar(unsigned short br) { if (real_rows > virtual_rows) { // Top handle menuAt(1, -1); - if (begin_row > 1) { + if (br > 1) { VIDEO::vga.setTextColor(OSD::zxColor(7, 0), OSD::zxColor(0, 0)); VIDEO::vga.print("+"); } else { @@ -552,7 +556,7 @@ void OSD::menuScrollBar() { // Scroll bar unsigned long shown_pct = round(((float)virtual_rows / (float)real_rows) * 100.0); - unsigned long begin_pct = round(((float)(begin_row - 1) / (float)real_rows) * 100.0); + unsigned long begin_pct = round(((float)(br - 1) / (float)real_rows) * 100.0); unsigned long bar_h = round(((float)holder_h / 100.0) * (float)shown_pct); unsigned long bar_y = round(((float)holder_h / 100.0) * (float)begin_pct); @@ -566,7 +570,7 @@ void OSD::menuScrollBar() { // Bottom handle menuAt(-1, -1); - if ((begin_row + virtual_rows - 1) < real_rows) { + if ((br + virtual_rows - 1) < real_rows) { VIDEO::vga.setTextColor(OSD::zxColor(7, 0), OSD::zxColor(0, 0)); VIDEO::vga.print("+"); } else { @@ -576,54 +580,6 @@ void OSD::menuScrollBar() { } } -// Draw file menu scroll bar -void OSD::filemenuScrollBar(uint8_t ftype) { - - // if (real_rows > virtual_rows) { - // Top handle - menuAt(1, -1); - if (FileUtils::fileTypes[ftype].begin_row > 1) { - VIDEO::vga.setTextColor(OSD::zxColor(7, 0), OSD::zxColor(0, 0)); - VIDEO::vga.print("+"); - } else { - VIDEO::vga.setTextColor(OSD::zxColor(7, 0), OSD::zxColor(0, 0)); - VIDEO::vga.print("-"); - } - - // Complete bar - unsigned short holder_x = x + (OSD_FONT_W * (cols - 1)) + 1; - unsigned short holder_y = y + (OSD_FONT_H * 2); - unsigned short holder_h = OSD_FONT_H * (virtual_rows - 3); - unsigned short holder_w = OSD_FONT_W; - VIDEO::vga.fillRect(holder_x, holder_y, holder_w, holder_h + 1, OSD::zxColor(7, 0)); - holder_y++; - - // Scroll bar - unsigned long shown_pct = round(((float)virtual_rows / (float)real_rows) * 100.0); - unsigned long begin_pct = round(((float)(FileUtils::fileTypes[ftype].begin_row - 1) / (float)real_rows) * 100.0); - unsigned long bar_h = round(((float)holder_h / 100.0) * (float)shown_pct); - unsigned long bar_y = round(((float)holder_h / 100.0) * (float)begin_pct); - - while ((bar_y + bar_h) >= holder_h) { - bar_h--; - } - - if (bar_h == 0) bar_h = 1; - - VIDEO::vga.fillRect(holder_x + 1, holder_y + bar_y, holder_w - 2, bar_h, OSD::zxColor(0, 0)); - - // Bottom handle - menuAt(-1, -1); - if ((FileUtils::fileTypes[ftype].begin_row + virtual_rows - 1) < real_rows) { - VIDEO::vga.setTextColor(OSD::zxColor(7, 0), OSD::zxColor(0, 0)); - VIDEO::vga.print("+"); - } else { - VIDEO::vga.setTextColor(OSD::zxColor(7, 0), OSD::zxColor(0, 0)); - VIDEO::vga.print("-"); - } - // } -} - // trim from start (in place) static inline void ltrim(std::string &s) { s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { @@ -643,28 +599,28 @@ static inline void trim(std::string &s) { ltrim(s); } -// trim from start (copying) -static inline std::string ltrim_copy(std::string s) { - ltrim(s); - return s; -} +// // trim from start (copying) +// static inline std::string ltrim_copy(std::string s) { +// ltrim(s); +// return s; +// } -// trim from end (copying) -static inline std::string rtrim_copy(std::string s) { - rtrim(s); - return s; -} +// // trim from end (copying) +// static inline std::string rtrim_copy(std::string s) { +// rtrim(s); +// return s; +// } -// trim from both ends (copying) -static inline std::string trim_copy(std::string s) { - trim(s); - return s; -} +// // trim from both ends (copying) +// static inline std::string trim_copy(std::string s) { +// trim(s); +// return s; +// } FILE *dirfile; // Run a new file menu -string OSD::menuFile(string &fdir, string title, uint8_t ftype, uint8_t mfcols, uint8_t mfrows) { +string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols, uint8_t mfrows) { struct stat stat_buf; bool reIndex; @@ -691,12 +647,14 @@ string OSD::menuFile(string &fdir, string title, uint8_t ftype, uint8_t mfcols, // Size w = (cols * OSD_FONT_W) + 2; h = (mf_rows * OSD_FONT_H) + 2; - menu = title + "\n"; - + + // menu = title + "\n" + fdir + "\n"; + menu = title + "\n" + ( fdir.length() == 1 ? fdir : fdir.substr(0,fdir.length()-1)) + "\n"; WindowDraw(); // Draw menu outline + fd_PrintRow(1, IS_INFO); // Path // Draw blank rows - for (uint8_t row = 1; row < mf_rows; row++) { + for (uint8_t row = 2; row < mf_rows; row++) { VIDEO::vga.setTextColor(OSD::zxColor(0, 1), OSD::zxColor(7, 1)); menuAt(row, 0); VIDEO::vga.print(std::string(cols, ' ').c_str()); @@ -800,127 +758,32 @@ string OSD::menuFile(string &fdir, string title, uint8_t ftype, uint8_t mfcols, } // Reset position - FileUtils::fileTypes[ftype].begin_row = FileUtils::fileTypes[ftype].focus = 1; + FileUtils::fileTypes[ftype].begin_row = FileUtils::fileTypes[ftype].focus = 2; } - real_rows = (stat_buf.st_size / 64) + 1; // Add 1 for title + real_rows = (stat_buf.st_size / 64) + 2; // Add 2 for title and status bar virtual_rows = (real_rows > mf_rows ? mf_rows : real_rows); - // printf("Real rows: %d; st_size: %d\n",real_rows,stat_buf.st_size); + // printf("Real rows: %d; st_size: %d; Virtual rows: %d\n",real_rows,stat_buf.st_size,virtual_rows); last_begin_row = last_focus = 0; // printf("Focus: %d, Begin_row: %d, real_rows: %d, mf_rows: %d\n",(int) FileUtils::fileTypes[ftype].focus,(int) FileUtils::fileTypes[ftype].begin_row,(int) real_rows, (int) mf_rows); - if ((real_rows > mf_rows -1) && ((FileUtils::fileTypes[ftype].begin_row + mf_rows - 1) > real_rows)) { - FileUtils::fileTypes[ftype].focus += (FileUtils::fileTypes[ftype].begin_row + mf_rows - 1) - real_rows; - FileUtils::fileTypes[ftype].begin_row = real_rows - (mf_rows - 1); + if ((real_rows > mf_rows) && ((FileUtils::fileTypes[ftype].begin_row + mf_rows - 2) > real_rows)) { + FileUtils::fileTypes[ftype].focus += (FileUtils::fileTypes[ftype].begin_row + mf_rows - 2) - real_rows; + FileUtils::fileTypes[ftype].begin_row = real_rows - (mf_rows - 2); + // printf("Focus: %d, BeginRow: %d\n",FileUtils::fileTypes[ftype].focus,FileUtils::fileTypes[ftype].begin_row); } - - filemenuRedraw(title, ftype); // Draw content - fabgl::VirtualKeyItem Menukey; - zxDelay = REPDEL; - lastzxKey = 0; + fd_Redraw(title, fdir, ftype); // Draw content + fabgl::VirtualKeyItem Menukey; while (1) { if (ZXKeyb::Exists) { - ZXKeyb::process(); - - if (!bitRead(ZXKeyb::ZXcols[4], 3)) { // 6 DOWN - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_UP, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_UP, false, false); - if (lastzxKey == 1) - zxDelay = REPPER; - else - zxDelay = REPDEL; - lastzxKey = 1; - } - } else - if (!bitRead(ZXKeyb::ZXcols[4], 4)) { // 7 UP (Yes, like the drink's name, I know... :D) - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_DOWN, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_DOWN, false, false); - if (lastzxKey == 2) - zxDelay = REPPER; - else - zxDelay = REPDEL; - lastzxKey = 2; - } - } else - if (!bitRead(ZXKeyb::ZXcols[6], 0)) { // ENTER - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, false, false); - if (lastzxKey == 3) - zxDelay = REPPER; - else - zxDelay = REPDEL; - lastzxKey = 3; - } - } else - if (!bitRead(ZXKeyb::ZXcols[4], 0)) { // 0 - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_SPACE, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_SPACE, false, false); - if (lastzxKey == 4) - zxDelay = REPPER; - else - zxDelay = REPDEL; - lastzxKey = 4; - } - } else - if ((!bitRead(ZXKeyb::ZXcols[7], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 1))) { // BREAK - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_ESCAPE, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_ESCAPE, false, false); - if (lastzxKey == 5) - zxDelay = REPPER; - else - zxDelay = REPDEL; - lastzxKey = 5; - } - } else - if (!bitRead(ZXKeyb::ZXcols[3], 4)) { // LEFT - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEUP, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEUP, false, false); - if (lastzxKey == 6) - zxDelay = REPPER; - else - zxDelay = REPDEL; - lastzxKey = 6; - } - } else - if (!bitRead(ZXKeyb::ZXcols[4], 2)) { // RIGHT - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEDOWN, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEDOWN, false, false); - if (lastzxKey == 7) - zxDelay = REPPER; - else - zxDelay = REPDEL; - lastzxKey = 7; - } - } else - if (!bitRead(ZXKeyb::ZXcols[1], 1)) { // S (Capture screen) - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PRINTSCREEN, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PRINTSCREEN, false, false); - if (lastzxKey == 8) - zxDelay = REPPER; - else - zxDelay = REPDEL; - lastzxKey = 8; - } - } else - { - zxDelay = 0; - lastzxKey = 0; - } + OSD::ZXKbdRead(); } @@ -931,96 +794,130 @@ string OSD::menuFile(string &fdir, string title, uint8_t ftype, uint8_t mfcols, if (ESPectrum::readKbd(&Menukey)) { if (!Menukey.down) continue; - // // Search first ocurrence of letter if we're not on that letter yet - // if (((Menukey.vk >= fabgl::VK_a) && (Menukey.vk <= fabgl::VK_Z)) || ((Menukey.vk >= fabgl::VK_0) && (Menukey.vk <= fabgl::VK_9))) { - // uint8_t dif; - // if (Menukey.vk<=fabgl::VK_9) dif = 46; - // else if (Menukey.vk<=fabgl::VK_z) dif = 75; - // else if (Menukey.vk<=fabgl::VK_Z) dif = 17; - // uint8_t letra = rowGet(menu,focus).at(0); - // if (letra != Menukey.vk + dif) { - // // TO DO: Position on first ocurrence of letra - // filemenuRedraw(title); - // } - /*} else*/ if (Menukey.vk == fabgl::VK_UP) { - if (FileUtils::fileTypes[ftype].focus == 1 and FileUtils::fileTypes[ftype].begin_row > 1) { - // filemenuScroll(DOWN); - if (FileUtils::fileTypes[ftype].begin_row > 1) { - last_begin_row = FileUtils::fileTypes[ftype].begin_row; - FileUtils::fileTypes[ftype].begin_row--; - filemenuRedraw(title, ftype); + // Search first ocurrence of letter if we're not on that letter yet + if (((Menukey.vk >= fabgl::VK_a) && (Menukey.vk <= fabgl::VK_Z)) || ((Menukey.vk >= fabgl::VK_0) && (Menukey.vk <= fabgl::VK_9))) { + int fsearch; + if (Menukey.vk<=fabgl::VK_9) + fsearch = Menukey.vk + 46; + else if (Menukey.vk<=fabgl::VK_z) + fsearch = Menukey.vk + 75; + else if (Menukey.vk<=fabgl::VK_Z) + fsearch = Menukey.vk + 17; + uint8_t letra = rowGet(menu,FileUtils::fileTypes[ftype].focus).at(0); + // printf("%d %d\n",(int)letra,fsearch); + if (letra != fsearch) { + // Seek first ocurrence of letter/number + long prevpos = ftell(dirfile); + char buf[128]; + int cnt = 0; + fseek(dirfile,0,SEEK_SET); + while(!feof(dirfile)) { + fgets(buf, sizeof(buf), dirfile); + // printf("%c %d\n",buf[0],int(buf[0])); + if (buf[0] == char(fsearch)) break; + cnt++; } - } else if (FileUtils::fileTypes[ftype].focus > 1) { + // printf("Cnt: %d Letra: %d\n",cnt,int(letra)); + if (!feof(dirfile)) { + last_begin_row = FileUtils::fileTypes[ftype].begin_row; + last_focus = FileUtils::fileTypes[ftype].focus; + if (real_rows > virtual_rows) { + int m = cnt + virtual_rows - real_rows; + if (m > 0) { + FileUtils::fileTypes[ftype].focus = m + 2; + FileUtils::fileTypes[ftype].begin_row = cnt - m + 2; + } else { + FileUtils::fileTypes[ftype].focus = 2; + FileUtils::fileTypes[ftype].begin_row = cnt + 2; + } + } else { + FileUtils::fileTypes[ftype].focus = cnt + 2; + FileUtils::fileTypes[ftype].begin_row = 2; + } + // printf("Real rows: %d; Virtual rows: %d\n",real_rows,virtual_rows); + // printf("Focus: %d, Begin_row: %d\n",(int) FileUtils::fileTypes[ftype].focus,(int) FileUtils::fileTypes[ftype].begin_row); + fd_Redraw(title,fdir,ftype); + } else + fseek(dirfile,prevpos,SEEK_SET); + } + } else if (Menukey.vk == fabgl::VK_UP) { + if (FileUtils::fileTypes[ftype].focus == 2 && FileUtils::fileTypes[ftype].begin_row > 2) { + last_begin_row = FileUtils::fileTypes[ftype].begin_row; + FileUtils::fileTypes[ftype].begin_row--; + fd_Redraw(title, fdir, ftype); + } else if (FileUtils::fileTypes[ftype].focus > 2) { last_focus = FileUtils::fileTypes[ftype].focus; - FileUtils::fileTypes[ftype].focus--; - filemenuPrintRow(FileUtils::fileTypes[ftype].focus, IS_FOCUSED); - if (FileUtils::fileTypes[ftype].focus + 1 < virtual_rows) { - filemenuPrintRow(FileUtils::fileTypes[ftype].focus + 1, IS_NORMAL); - } + fd_PrintRow(FileUtils::fileTypes[ftype].focus--, IS_NORMAL); + fd_PrintRow(FileUtils::fileTypes[ftype].focus, IS_FOCUSED); + // printf("Focus: %d, Lastfocus: %d\n",FileUtils::fileTypes[ftype].focus,(int) last_focus); } click(); } else if (Menukey.vk == fabgl::VK_DOWN) { - if (FileUtils::fileTypes[ftype].focus == virtual_rows - 1) { - if ((FileUtils::fileTypes[ftype].begin_row + virtual_rows - 1) < real_rows) { - last_begin_row = FileUtils::fileTypes[ftype].begin_row; - FileUtils::fileTypes[ftype].begin_row++; - filemenuRedraw(title, ftype); - } + if (FileUtils::fileTypes[ftype].focus == virtual_rows - 1 && FileUtils::fileTypes[ftype].begin_row + virtual_rows - 2 < real_rows) { + last_begin_row = FileUtils::fileTypes[ftype].begin_row; + FileUtils::fileTypes[ftype].begin_row++; + fd_Redraw(title, fdir, ftype); } else if (FileUtils::fileTypes[ftype].focus < virtual_rows - 1) { last_focus = FileUtils::fileTypes[ftype].focus; - FileUtils::fileTypes[ftype].focus++; - filemenuPrintRow(FileUtils::fileTypes[ftype].focus, IS_FOCUSED); - if (FileUtils::fileTypes[ftype].focus - 1 > 0) { - filemenuPrintRow(FileUtils::fileTypes[ftype].focus - 1, IS_NORMAL); - } + fd_PrintRow(FileUtils::fileTypes[ftype].focus++, IS_NORMAL); + fd_PrintRow(FileUtils::fileTypes[ftype].focus, IS_FOCUSED); + // printf("Focus: %d, Lastfocus: %d\n",FileUtils::fileTypes[ftype].focus,(int) last_focus); } click(); } else if ((Menukey.vk == fabgl::VK_PAGEUP) || (Menukey.vk == fabgl::VK_LEFT)) { if (FileUtils::fileTypes[ftype].begin_row > virtual_rows) { - FileUtils::fileTypes[ftype].focus = 1; - FileUtils::fileTypes[ftype].begin_row -= virtual_rows - 1; + FileUtils::fileTypes[ftype].focus = 2; + FileUtils::fileTypes[ftype].begin_row -= virtual_rows - 2; } else { - FileUtils::fileTypes[ftype].focus = 1; - FileUtils::fileTypes[ftype].begin_row = 1; + FileUtils::fileTypes[ftype].focus = 2; + FileUtils::fileTypes[ftype].begin_row = 2; } - filemenuRedraw(title, ftype); + fd_Redraw(title, fdir, ftype); click(); } else if ((Menukey.vk == fabgl::VK_PAGEDOWN) || (Menukey.vk == fabgl::VK_RIGHT)) { if (real_rows - FileUtils::fileTypes[ftype].begin_row - virtual_rows > virtual_rows) { - FileUtils::fileTypes[ftype].focus = 1; - FileUtils::fileTypes[ftype].begin_row += virtual_rows - 1; + FileUtils::fileTypes[ftype].focus = 2; + FileUtils::fileTypes[ftype].begin_row += virtual_rows - 2; } else { FileUtils::fileTypes[ftype].focus = virtual_rows - 1; - FileUtils::fileTypes[ftype].begin_row = real_rows - virtual_rows + 1; + FileUtils::fileTypes[ftype].begin_row = real_rows - virtual_rows + 2; } - filemenuRedraw(title, ftype); + fd_Redraw(title, fdir, ftype); click(); } else if (Menukey.vk == fabgl::VK_HOME) { - FileUtils::fileTypes[ftype].focus = 1; - FileUtils::fileTypes[ftype].begin_row = 1; - filemenuRedraw(title, ftype); + last_focus = FileUtils::fileTypes[ftype].focus; + last_begin_row = FileUtils::fileTypes[ftype].begin_row; + FileUtils::fileTypes[ftype].focus = 2; + FileUtils::fileTypes[ftype].begin_row = 2; + fd_Redraw(title, fdir, ftype); click(); } else if (Menukey.vk == fabgl::VK_END) { + last_focus = FileUtils::fileTypes[ftype].focus; + last_begin_row = FileUtils::fileTypes[ftype].begin_row; FileUtils::fileTypes[ftype].focus = virtual_rows - 1; - FileUtils::fileTypes[ftype].begin_row = real_rows - virtual_rows + 1; - filemenuRedraw(title, ftype); + FileUtils::fileTypes[ftype].begin_row = real_rows - virtual_rows + 2; + // printf("Focus: %d, Lastfocus: %d\n",FileUtils::fileTypes[ftype].focus,(int) last_focus); + fd_Redraw(title, fdir, ftype); click(); - } else if (Menukey.vk == fabgl::VK_RETURN) { + } else if (Menukey.vk == fabgl::VK_BACKSPACE) { + if (fdir != "/") { + + fclose(dirfile); + dirfile = NULL; + + fdir.pop_back(); + fdir = fdir.substr(0,fdir.find_last_of("/") + 1); + + FileUtils::fileTypes[ftype].begin_row = FileUtils::fileTypes[ftype].focus = 2; + // printf("Fdir: %s\n",fdir.c_str()); + break; + + } + } else if (Menukey.vk == fabgl::VK_RETURN || Menukey.vk == fabgl::VK_SPACE) { fclose(dirfile); dirfile = NULL; - // // Restore backbuffer data - // int j = SaveRectpos - (((w >> 2) + 1) * h); - // SaveRectpos = j - 4; - // for (int m = y; m < y + h; m++) { - // uint32_t *backbuffer32 = (uint32_t *)(VIDEO::vga.backBuffer[m]); - // for (int n = x >> 2; n < ((x + w) >> 2) + 1; n++) { - // backbuffer32[n] = VIDEO::SaveRect[j]; - // j++; - // } - // } - filedir = rowGet(menu,FileUtils::fileTypes[ftype].focus); // printf("%s\n",filedir.c_str()); if (filedir[0] == ASCII_SPC) { @@ -1032,7 +929,7 @@ string OSD::menuFile(string &fdir, string title, uint8_t ftype, uint8_t mfcols, trim(filedir); fdir = fdir + filedir + "/"; } - FileUtils::fileTypes[ftype].begin_row = FileUtils::fileTypes[ftype].focus = 1; + FileUtils::fileTypes[ftype].begin_row = FileUtils::fileTypes[ftype].focus = 2; // printf("Fdir: %s\n",fdir.c_str()); break; } else { @@ -1053,30 +950,9 @@ string OSD::menuFile(string &fdir, string title, uint8_t ftype, uint8_t mfcols, rtrim(filedir); click(); - return "R" + filedir; + return (Menukey.vk == fabgl::VK_RETURN ? "R" : "S") + filedir; } - } else if (Menukey.vk == fabgl::VK_SPACE) { - fclose(dirfile); - dirfile = NULL; - // Restore backbuffer data - if (menu_saverect) { - int j = SaveRectpos - (((w >> 2) + 1) * h); - SaveRectpos = j - 4; - for (int m = y; m < y + h; m++) { - uint32_t *backbuffer32 = (uint32_t *)(VIDEO::vga.backBuffer[m]); - for (int n = x >> 2; n < ((x + w) >> 2) + 1; n++) { - backbuffer32[n] = VIDEO::SaveRect[j]; - j++; - } - } - menu_saverect = false; - } - - filedir = rowGet(menu,FileUtils::fileTypes[ftype].focus); - rtrim(filedir); - click(); - return "S" + filedir; } else if (Menukey.vk == fabgl::VK_ESCAPE) { // Restore backbuffer data @@ -1100,17 +976,15 @@ string OSD::menuFile(string &fdir, string title, uint8_t ftype, uint8_t mfcols, } } - } else { + } /*else { // TO DO: COUNT TIME TO SIGNAL START OF FOCUSED LINE SCROLL - } - - vTaskDelay(5 / portTICK_PERIOD_MS); + }*/ // TO DO: SCROLL FOCUSED LINE IF SIGNALED - if (zxDelay > 0) zxDelay--; + vTaskDelay(5 / portTICK_PERIOD_MS); } @@ -1118,6 +992,96 @@ string OSD::menuFile(string &fdir, string title, uint8_t ftype, uint8_t mfcols, } + +void OSD::ZXKbdRead() { + + #define REPDEL 140 // As in real ZX Spectrum (700 ms.) if this function is called every 5 ms. + #define REPPER 20 // As in real ZX Spectrum (100 ms.) if this function is called every 5 ms. + + static int zxDel = REPDEL; + static int lastzxK = fabgl::VK_NONE; + + ZXKeyb::process(); + + fabgl::VirtualKey injectKey = fabgl::VK_NONE; + + if (bitRead(ZXKeyb::ZXcols[7], 1)) { // Not Symbol Shift pressed ? + + if (!bitRead(ZXKeyb::ZXcols[4], 3)) injectKey = fabgl::VK_UP; // 7 -> UP + else if (!bitRead(ZXKeyb::ZXcols[4], 4)) injectKey = fabgl::VK_DOWN; // 6 -> DOWN + else if (!bitRead(ZXKeyb::ZXcols[6], 0)) injectKey = fabgl::VK_RETURN; // ENTER + else if ((!bitRead(ZXKeyb::ZXcols[0], 0)) && (!bitRead(ZXKeyb::ZXcols[4], 0))) injectKey = fabgl::VK_BACKSPACE; // CS + 0 -> BACKSPACE + else if (!bitRead(ZXKeyb::ZXcols[4], 0)) injectKey = fabgl::VK_SPACE; // 0 -> SPACE + else if ((!bitRead(ZXKeyb::ZXcols[7], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 1))) injectKey = fabgl::VK_ESCAPE; // BREAK -> ESCAPE + else if (!bitRead(ZXKeyb::ZXcols[3], 4)) injectKey = fabgl::VK_PAGEUP; // 5 -> PGUP + else if (!bitRead(ZXKeyb::ZXcols[4], 2)) injectKey = fabgl::VK_PAGEDOWN; // 8 -> PGDOWN + else if (!bitRead(ZXKeyb::ZXcols[1], 1)) injectKey = fabgl::VK_PRINTSCREEN; // S -> PRINTSCREEN + else if (!bitRead(ZXKeyb::ZXcols[5], 0)) injectKey = fabgl::VK_PAUSE; // P -> PAUSE + + } else { + + if (!bitRead(ZXKeyb::ZXcols[0], 1)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_Z : fabgl::VK_z; + else if (!bitRead(ZXKeyb::ZXcols[0], 2)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_X : fabgl::VK_x; + else if (!bitRead(ZXKeyb::ZXcols[0], 3)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_C : fabgl::VK_c; + else if (!bitRead(ZXKeyb::ZXcols[0], 4)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_V : fabgl::VK_v; + + else if (!bitRead(ZXKeyb::ZXcols[1], 0)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_A : fabgl::VK_a; + else if (!bitRead(ZXKeyb::ZXcols[1], 1)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_S : fabgl::VK_s; + else if (!bitRead(ZXKeyb::ZXcols[1], 2)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_D : fabgl::VK_d; + else if (!bitRead(ZXKeyb::ZXcols[1], 3)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_F : fabgl::VK_f; + else if (!bitRead(ZXKeyb::ZXcols[1], 4)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_G : fabgl::VK_g; + + else if (!bitRead(ZXKeyb::ZXcols[2], 0)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_Q : fabgl::VK_q; + else if (!bitRead(ZXKeyb::ZXcols[2], 1)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_W : fabgl::VK_w; + else if (!bitRead(ZXKeyb::ZXcols[2], 2)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_E : fabgl::VK_e; + else if (!bitRead(ZXKeyb::ZXcols[2], 3)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_R : fabgl::VK_r; + else if (!bitRead(ZXKeyb::ZXcols[2], 4)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_T : fabgl::VK_t; + + else if (!bitRead(ZXKeyb::ZXcols[5], 0)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_P : fabgl::VK_p; + else if (!bitRead(ZXKeyb::ZXcols[5], 1)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_O : fabgl::VK_o; + else if (!bitRead(ZXKeyb::ZXcols[5], 2)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_I : fabgl::VK_i; + else if (!bitRead(ZXKeyb::ZXcols[5], 3)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_U : fabgl::VK_u; + else if (!bitRead(ZXKeyb::ZXcols[5], 4)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_Y : fabgl::VK_y; + + else if (!bitRead(ZXKeyb::ZXcols[6], 1)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_L : fabgl::VK_l; + else if (!bitRead(ZXKeyb::ZXcols[6], 2)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_K : fabgl::VK_k; + else if (!bitRead(ZXKeyb::ZXcols[6], 3)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_J : fabgl::VK_j; + else if (!bitRead(ZXKeyb::ZXcols[6], 4)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_H : fabgl::VK_h; + + else if (!bitRead(ZXKeyb::ZXcols[7], 2)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_M : fabgl::VK_m; + else if (!bitRead(ZXKeyb::ZXcols[7], 3)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_N : fabgl::VK_n; + else if (!bitRead(ZXKeyb::ZXcols[7], 4)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_B : fabgl::VK_b; + + else if (!bitRead(ZXKeyb::ZXcols[3], 0)) injectKey = fabgl::VK_1; + else if (!bitRead(ZXKeyb::ZXcols[3], 1)) injectKey = fabgl::VK_2; + else if (!bitRead(ZXKeyb::ZXcols[3], 2)) injectKey = fabgl::VK_3; + else if (!bitRead(ZXKeyb::ZXcols[3], 3)) injectKey = fabgl::VK_4; + else if (!bitRead(ZXKeyb::ZXcols[3], 4)) injectKey = fabgl::VK_5; + + else if (!bitRead(ZXKeyb::ZXcols[4], 0)) injectKey = fabgl::VK_0; + else if (!bitRead(ZXKeyb::ZXcols[4], 1)) injectKey = fabgl::VK_9; + else if (!bitRead(ZXKeyb::ZXcols[4], 2)) injectKey = fabgl::VK_8; + else if (!bitRead(ZXKeyb::ZXcols[4], 3)) injectKey = fabgl::VK_7; + else if (!bitRead(ZXKeyb::ZXcols[4], 4)) injectKey = fabgl::VK_6; + + } + + if (injectKey != fabgl::VK_NONE) { + if (zxDel == 0) { + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(injectKey, true, false); + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(injectKey, false, false); + zxDel = lastzxK == injectKey ? REPPER : REPDEL; + lastzxK = injectKey; + } + } else { + zxDel = 0; + lastzxK = fabgl::VK_NONE; + } + + if (zxDel > 0) zxDel--; + +} + // Print a virtual row void OSD::PrintRow(uint8_t virtual_row_num, uint8_t line_type) { @@ -1169,7 +1133,7 @@ void OSD::PrintRow(uint8_t virtual_row_num, uint8_t line_type) { } // Print a virtual row -void OSD::filemenuPrintRow(uint8_t virtual_row_num, uint8_t line_type) { +void OSD::fd_PrintRow(uint8_t virtual_row_num, uint8_t line_type) { uint8_t margin; @@ -1180,6 +1144,10 @@ void OSD::filemenuPrintRow(uint8_t virtual_row_num, uint8_t line_type) { VIDEO::vga.setTextColor(OSD::zxColor(7, 1), OSD::zxColor(0, 0)); margin = 2; break; + case IS_INFO: + VIDEO::vga.setTextColor(OSD::zxColor(7, 1), OSD::zxColor(5, 0)); + margin = (real_rows > virtual_rows ? 3 : 2); + break; case IS_FOCUSED: VIDEO::vga.setTextColor(OSD::zxColor(0, 1), OSD::zxColor(5, 1)); margin = (real_rows > virtual_rows ? 3 : 2); @@ -1196,19 +1164,22 @@ void OSD::filemenuPrintRow(uint8_t virtual_row_num, uint8_t line_type) { if (line[0] == ASCII_SPC) { // Directory ltrim(line); - - // for(int h = 0; h < line.length(); h++) - // printf("%d, ",(int)line[h]); - // printf("\n"); - if (line.length() < cols - margin) line = line + std::string(cols - margin - line.length(), ' '); line = line.substr(0,cols - margin - 6) + " "; } else { - // Filename - if (line.length() < cols - margin) + if (line.length() < cols - margin) { line = line + std::string(cols - margin - line.length(), ' '); - line = line.substr(0, cols - margin); + line = line.substr(0, cols - margin); + } else { + if (line_type == IS_INFO) { + // printf("%s %d\n",line.c_str(),line.length() - (cols - margin)); + line = ".." + line.substr(line.length() - (cols - margin) + 2); + // printf("%s\n",line.c_str()); + } else + line = line.substr(0, cols - margin); + } + } VIDEO::vga.print(line.c_str()); @@ -1218,31 +1189,35 @@ void OSD::filemenuPrintRow(uint8_t virtual_row_num, uint8_t line_type) { } // Redraw inside rows -void OSD::filemenuRedraw(string title, uint8_t ftype) { +void OSD::fd_Redraw(string title, string fdir, uint8_t ftype) { if ((FileUtils::fileTypes[ftype].focus != last_focus) || (FileUtils::fileTypes[ftype].begin_row != last_begin_row)) { + // printf("fd_Redraw\n"); + // Read bunch of rows - fseek(dirfile, (FileUtils::fileTypes[ftype].begin_row - 1) * 64,SEEK_SET); - menu = title + "\n"; - for (int i = 1; i < virtual_rows; i++) { - char buf[256]; + fseek(dirfile, (FileUtils::fileTypes[ftype].begin_row - 2) * 64,SEEK_SET); + menu = title + "\n" + ( fdir.length() == 1 ? fdir : fdir.substr(0,fdir.length()-1)) + "\n"; + for (int i = 2; i < virtual_rows; i++) { + char buf[128]; fgets(buf, sizeof(buf), dirfile); if (feof(dirfile)) break; menu += buf; } - uint8_t row = 1; + fd_PrintRow(1, IS_INFO); // Print status bar + + uint8_t row = 2; for (; row < virtual_rows; row++) { if (row == FileUtils::fileTypes[ftype].focus) { - filemenuPrintRow(row, IS_FOCUSED); + fd_PrintRow(row, IS_FOCUSED); } else { - filemenuPrintRow(row, IS_NORMAL); + fd_PrintRow(row, IS_NORMAL); } } if (real_rows > virtual_rows) { - filemenuScrollBar(ftype); + menuScrollBar(FileUtils::fileTypes[ftype].begin_row); } else { for (; row < mf_rows; row++) { VIDEO::vga.setTextColor(OSD::zxColor(0, 1), OSD::zxColor(7, 1)); @@ -1358,7 +1333,7 @@ void OSD::tapemenuRedraw(string title) { } } - menuScrollBar(); + menuScrollBar(begin_row); last_focus = focus; last_begin_row = begin_row; @@ -1422,96 +1397,98 @@ int OSD::menuTape(string title) { tapemenuRedraw(title); - zxDelay = REPDEL; - lastzxKey = 0; + // zxDelay = REPDEL; + // lastzxKey = 0; while (1) { if (ZXKeyb::Exists) { - ZXKeyb::process(); - - if (!bitRead(ZXKeyb::ZXcols[4], 3)) { // 6 DOWN - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_UP, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_UP, false, false); - if (lastzxKey == 1) - zxDelay = REPPER; - else - zxDelay = REPDEL; - lastzxKey = 1; - } - } else - if (!bitRead(ZXKeyb::ZXcols[4], 4)) { // 7 UP (Yes, like the drink's name, I know... :D) - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_DOWN, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_DOWN, false, false); - if (lastzxKey == 2) - zxDelay = REPPER; - else - zxDelay = REPDEL; - lastzxKey = 2; - } - } else - if ((!bitRead(ZXKeyb::ZXcols[6], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 0))) { // ENTER - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, false, false); - if (lastzxKey == 3) - zxDelay = REPPER; - else - zxDelay = REPDEL; - lastzxKey = 3; - } - } else - if ((!bitRead(ZXKeyb::ZXcols[7], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 1))) { // BREAK - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_ESCAPE, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_ESCAPE, false, false); - if (lastzxKey == 4) - zxDelay = REPPER; - else - zxDelay = REPDEL; - lastzxKey = 4; - } - } else - if (!bitRead(ZXKeyb::ZXcols[3], 4)) { // LEFT - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEUP, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEUP, false, false); - if (lastzxKey == 5) - zxDelay = REPPER; - else - zxDelay = REPDEL; - lastzxKey = 5; - } - } else - if (!bitRead(ZXKeyb::ZXcols[4], 2)) { // RIGHT - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEDOWN, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEDOWN, false, false); - if (lastzxKey == 6) - zxDelay = REPPER; - else - zxDelay = REPDEL; - lastzxKey = 6; - } - } else - if (!bitRead(ZXKeyb::ZXcols[1], 1)) { // S (Capture screen) - if (zxDelay == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PRINTSCREEN, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PRINTSCREEN, false, false); - if (lastzxKey == 7) - zxDelay = REPPER; - else - zxDelay = REPDEL; - lastzxKey = 7; - } - } else - { - zxDelay = 0; - lastzxKey = 0; - } + ZXKbdRead(); + + // ZXKeyb::process(); + + // if (!bitRead(ZXKeyb::ZXcols[4], 3)) { // 6 DOWN + // if (zxDelay == 0) { + // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_UP, true, false); + // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_UP, false, false); + // if (lastzxKey == 1) + // zxDelay = REPPER; + // else + // zxDelay = REPDEL; + // lastzxKey = 1; + // } + // } else + // if (!bitRead(ZXKeyb::ZXcols[4], 4)) { // 7 UP (Yes, like the drink's name, I know... :D) + // if (zxDelay == 0) { + // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_DOWN, true, false); + // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_DOWN, false, false); + // if (lastzxKey == 2) + // zxDelay = REPPER; + // else + // zxDelay = REPDEL; + // lastzxKey = 2; + // } + // } else + // if ((!bitRead(ZXKeyb::ZXcols[6], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 0))) { // ENTER + // if (zxDelay == 0) { + // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, true, false); + // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, false, false); + // if (lastzxKey == 3) + // zxDelay = REPPER; + // else + // zxDelay = REPDEL; + // lastzxKey = 3; + // } + // } else + // if ((!bitRead(ZXKeyb::ZXcols[7], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 1))) { // BREAK + // if (zxDelay == 0) { + // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_ESCAPE, true, false); + // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_ESCAPE, false, false); + // if (lastzxKey == 4) + // zxDelay = REPPER; + // else + // zxDelay = REPDEL; + // lastzxKey = 4; + // } + // } else + // if (!bitRead(ZXKeyb::ZXcols[3], 4)) { // LEFT + // if (zxDelay == 0) { + // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEUP, true, false); + // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEUP, false, false); + // if (lastzxKey == 5) + // zxDelay = REPPER; + // else + // zxDelay = REPDEL; + // lastzxKey = 5; + // } + // } else + // if (!bitRead(ZXKeyb::ZXcols[4], 2)) { // RIGHT + // if (zxDelay == 0) { + // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEDOWN, true, false); + // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEDOWN, false, false); + // if (lastzxKey == 6) + // zxDelay = REPPER; + // else + // zxDelay = REPDEL; + // lastzxKey = 6; + // } + // } else + // if (!bitRead(ZXKeyb::ZXcols[1], 1)) { // S (Capture screen) + // if (zxDelay == 0) { + // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PRINTSCREEN, true, false); + // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PRINTSCREEN, false, false); + // if (lastzxKey == 7) + // zxDelay = REPPER; + // else + // zxDelay = REPDEL; + // lastzxKey = 7; + // } + // } else + // { + // zxDelay = 0; + // lastzxKey = 0; + // } } @@ -1600,7 +1577,7 @@ int OSD::menuTape(string title) { begin_row = real_rows - virtual_rows + 1; tapemenuRedraw(title); click(); - } else if (Menukey.vk == fabgl::VK_RETURN) { + } else if (Menukey.vk == fabgl::VK_RETURN || Menukey.vk == fabgl::VK_SPACE) { click(); Tape::CalcTapBlockPos(begin_row + focus - 2); // printf("Ret value: %d\n", begin_row + focus - 2); @@ -1633,7 +1610,7 @@ int OSD::menuTape(string title) { vTaskDelay(5 / portTICK_PERIOD_MS); - if (zxDelay > 0) zxDelay--; + // if (zxDelay > 0) zxDelay--; } } diff --git a/src/Ports.cpp b/src/Ports.cpp index 25d287f5..66a33b1b 100644 --- a/src/Ports.cpp +++ b/src/Ports.cpp @@ -44,7 +44,7 @@ visit https://zxespectrum.speccy.org/contacto #include "CPU.h" #include "wd1793.h" -#pragma GCC optimize ("O3") +// #pragma GCC optimize("O3") // Values calculated for BEEPER, EAR, MIC bit mask (values 0-7) // Taken from FPGA values suggested by Rampa @@ -57,24 +57,24 @@ visit https://zxespectrum.speccy.org/contacto // 6: ula <= 8'hF8; // 7: ula <= 8'hFF; // and adjusted for BEEPER_MAX_VOLUME = 97 -uint8_t speaker_values[8]={ 0, 19, 34, 53, 97, 101, 130, 134 }; - -uint8_t Ports::port[128]; +uint8_t Ports::speaker_values[8]={ 0, 19, 34, 53, 97, 101, 130, 134 }; +DRAM_ATTR uint8_t Ports::port[128]; uint8_t Ports::port254 = 0; -uint8_t IRAM_ATTR Ports::input(uint16_t address) { +IRAM_ATTR uint8_t Ports::input(uint16_t address) { uint8_t data; + uint8_t rambank = address >> 14; // ** I/O Contention (Early) ************************* - VIDEO::Draw(1, MemESP::ramContended[address >> 14]); + VIDEO::Draw(1, MemESP::ramContended[rambank]); // ** I/O Contention (Early) ************************* // ** I/O Contention (Late) ************************** if ((address & 0x0001) == 0) { VIDEO::Draw(3, true); } else { - if (MemESP::ramContended[address >> 14]) { + if (MemESP::ramContended[rambank]) { VIDEO::Draw(1, true); VIDEO::Draw(1, true); VIDEO::Draw(1, true); @@ -171,7 +171,7 @@ uint8_t IRAM_ATTR Ports::input(uint16_t address) { if (MemESP::videoLatch != bitRead(data, 3)) { MemESP::videoLatch = bitRead(data, 3); // This, if not using the ptime128 draw version, fixs ptime and ptime128 - if (((address & 0x0001) != 0) && (MemESP::ramContended[address >> 14])) { + if (((address & 0x0001) != 0) && (MemESP::ramContended[rambank])) { VIDEO::Draw(2, false); CPU::tstates -= 2; } @@ -192,12 +192,13 @@ uint8_t IRAM_ATTR Ports::input(uint16_t address) { } -void IRAM_ATTR Ports::output(uint16_t address, uint8_t data) { +IRAM_ATTR void Ports::output(uint16_t address, uint8_t data) { int Audiobit; + uint8_t rambank = address >> 14; // ** I/O Contention (Early) ************************* - VIDEO::Draw(1, MemESP::ramContended[address >> 14]); + VIDEO::Draw(1, MemESP::ramContended[rambank]); // ** I/O Contention (Early) ************************* // ULA ======================================================================= @@ -265,15 +266,12 @@ void IRAM_ATTR Ports::output(uint16_t address, uint8_t data) { // ** I/O Contention (Late) ************************** if ((address & 0x0001) == 0) { VIDEO::Draw(3, true); - // printf("Case 1\n"); } else { - if (MemESP::ramContended[address >> 14]) { + if (MemESP::ramContended[rambank]) { VIDEO::Draw(1, true); VIDEO::Draw(1, true); VIDEO::Draw(1, true); - // printf("Case 2\n"); } else { - // printf("Case 3\n"); VIDEO::Draw(3, false); } } @@ -300,7 +298,7 @@ void IRAM_ATTR Ports::output(uint16_t address, uint8_t data) { // Seems not needed in Pentagon // This, if not using the ptime128 draw version, fixs ptime and ptime128 if (!Z80Ops::isPentagon) { - if (((address & 0x0001) != 0) && (MemESP::ramContended[address >> 14])) { + if (((address & 0x0001) != 0) && (MemESP::ramContended[rambank])) { VIDEO::Draw(2, false); CPU::tstates -= 2; } diff --git a/src/Snapshot.cpp b/src/Snapshot.cpp index 9e4f3b1b..d8a7b0dd 100644 --- a/src/Snapshot.cpp +++ b/src/Snapshot.cpp @@ -68,25 +68,17 @@ bool LoadSnapshot(string filename, string force_arch) { bool res = false; - // // Stop keyboard input - // ESPectrum::PS2Controller.keyboard()->suspendPort(); - // if (!ZXKeyb::Exists) ESPectrum::PS2Controller.keybjoystick()->suspendPort(); - bool OSDprev = VIDEO::OSD; if (FileUtils::hasSNAextension(filename)) { - // printf("FileSNA::load %s\n",filename.c_str()); - - OSD::osdCenteredMsg(MSG_LOADING_SNA + (string) ": " + filename, LEVEL_INFO, 0); + OSD::osdCenteredMsg(MSG_LOADING_SNA + (string) ": " + filename.substr(filename.find_last_of("/") + 1), LEVEL_INFO, 0); res = FileSNA::load(filename, force_arch); } else if (FileUtils::hasZ80extension(filename)) { - // printf("FileZ80::load %s\n",filename.c_str()); - - OSD::osdCenteredMsg(MSG_LOADING_Z80 + (string)": " + filename, LEVEL_INFO, 0); + OSD::osdCenteredMsg(MSG_LOADING_Z80 + (string) ": " + filename.substr(filename.find_last_of("/") + 1), LEVEL_INFO, 0); res = FileZ80::load(filename); @@ -101,10 +93,6 @@ bool LoadSnapshot(string filename, string force_arch) { ESPectrum::TapeNameScroller = 0; } - // // Resume keyboard input - // ESPectrum::PS2Controller.keyboard()->resumePort(); - // if (!ZXKeyb::Exists) ESPectrum::PS2Controller.keybjoystick()->resumePort(); - return res; } diff --git a/src/Tape.cpp b/src/Tape.cpp index 4b36fcc5..1dc758d4 100644 --- a/src/Tape.cpp +++ b/src/Tape.cpp @@ -467,7 +467,7 @@ void Tape::TAP_Stop() // tape = NULL; } -void IRAM_ATTR Tape::TAP_Read() +IRAM_ATTR void Tape::TAP_Read() { uint64_t tapeCurrent = (CPU::global_tstates + CPU::tstates) - tapeStart; @@ -546,11 +546,6 @@ void Tape::Save() { uint8_t dato; int longitud; - // if (tapeSaveName == "none") { - // OSD::osdCenteredMsg(OSD_TAPE_SAVE_ERR, LEVEL_ERROR); - // return; - // } - fichero = fopen(tapeSaveName.c_str(), "ab"); if (fichero == NULL) { diff --git a/src/Video.cpp b/src/Video.cpp index d91085af..7cb2a329 100644 --- a/src/Video.cpp +++ b/src/Video.cpp @@ -43,6 +43,8 @@ visit https://zxespectrum.speccy.org/contacto #include "Z80_JLS/z80.h" #include "Z80_JLS/z80operations.h" +#pragma GCC optimize("O3") + VGA6Bit VIDEO::vga; uint8_t VIDEO::borderColor = 0; uint32_t VIDEO::brd; @@ -60,7 +62,26 @@ int VIDEO::VsyncFinetune[2]; // uint8_t VIDEO::dispUpdCycle; uint32_t VIDEO::framecnt = 0; -void IRAM_ATTR VGA6Bit::interrupt(void *arg) { +DRAM_ATTR static const uint8_t wait_st[243] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 5, 4, 3, 2, 1, 0, 0, 6, 5, 4, 3, 2, 1, 0, 0, + 6, 5, 4, 3, 2, 1, 0, 0, 6, 5, 4, 3, 2, 1, 0, 0, + 6, 5, 4, 3, 2, 1, 0, 0, 6, 5, 4, 3, 2, 1, 0, 0, + 6, 5, 4, 3, 2, 1, 0, 0, 6, 5, 4, 3, 2, 1, 0, 0, + 6, 5, 4, 3, 2, 1, 0, 0, 6, 5, 4, 3, 2, 1, 0, 0, + 6, 5, 4, 3, 2, 1, 0, 0, 6, 5, 4, 3, 2, 1, 0, 0, + 6, 5, 4, 3, 2, 1, 0, 0, 6, 5, 4, 3, 2, 1, 0, 0, + 6, 5, 4, 3, 2, 1, 0, 0, 6, 5, 4, 3, 2, 1, 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 +}; // sequence of wait states + +IRAM_ATTR void VGA6Bit::interrupt(void *arg) { static int64_t prevmicros = 0; static int64_t elapsedmicros = 0; @@ -358,9 +379,17 @@ uint8_t VIDEO::getFloatBusData128() { // VIDEO DRAW FUNCTIONS /////////////////////////////////////////////////////////////////////////////// +#ifdef NO_VIDEO void VIDEO::NoVideo(unsigned int statestoadd, bool contended) { CPU::tstates += statestoadd; } +#endif + +// IRAM_ATTR void VIDEO::NoDraw(unsigned int statestoadd, bool contended) { +// if (contended) statestoadd += wait_st[CPU::tstates - tstateDraw]; +// CPU::tstates += statestoadd; +// video_rest += statestoadd; +// } -void IRAM_ATTR VIDEO::TopBorder_Blank(unsigned int statestoadd, bool contended) { +IRAM_ATTR void VIDEO::TopBorder_Blank(unsigned int statestoadd, bool contended) { CPU::tstates += statestoadd; @@ -376,7 +405,7 @@ void IRAM_ATTR VIDEO::TopBorder_Blank(unsigned int statestoadd, bool contended) } -void IRAM_ATTR VIDEO::TopBorder_Blank_Pentagon(unsigned int statestoadd, bool contended) { +IRAM_ATTR void VIDEO::TopBorder_Blank_Pentagon(unsigned int statestoadd, bool contended) { CPU::tstates += statestoadd; @@ -399,40 +428,78 @@ void IRAM_ATTR VIDEO::TopBorder_Blank_Pentagon(unsigned int statestoadd, bool co } -void IRAM_ATTR VIDEO::TopBorder(unsigned int statestoadd, bool contended) { +IRAM_ATTR void VIDEO::TopBorder(unsigned int statestoadd, bool contended) { CPU::tstates += statestoadd; statestoadd += video_rest; + video_rest = statestoadd & 0x03; // Mod 4 - for (int i=0; i < (statestoadd >> 2); i++) { - *lineptr32++ = brd; - *lineptr32++ = brd; - if (++coldraw_cnt == 40) { - Draw = ++linedraw_cnt == (is169 ? 4 : 24) ? &MainScreen_Blank : &TopBorder_Blank; - return; + + int loopCount = statestoadd >> 2; + + if (coldraw_cnt + loopCount > 39) { + + for (;;) { + + *lineptr32++ = brd; + *lineptr32++ = brd; + + if (++coldraw_cnt > 39) { + Draw = ++linedraw_cnt == (is169 ? 4 : 24) ? &MainScreen_Blank : &TopBorder_Blank; + return; + } + } + + } else { + + coldraw_cnt += loopCount; + + for (;loopCount > 0 ; loopCount--) { + + *lineptr32++ = brd; + *lineptr32++ = brd; + + } + } } -void IRAM_ATTR VIDEO::TopBorder_Pentagon(unsigned int statestoadd, bool contended) { +IRAM_ATTR void VIDEO::TopBorder_Pentagon(unsigned int statestoadd, bool contended) { CPU::tstates += statestoadd; statestoadd += video_rest; + video_rest = 0; - for (int i=0; i < statestoadd; i++) { - lineptr16[coldraw_cnt ^ 1] = (uint16_t) brd; - if (++coldraw_cnt == col_end) { - Draw = ++linedraw_cnt == lin_end ? &MainScreen_Blank_Pentagon : &TopBorder_Blank_Pentagon; - return; + + if (coldraw_cnt + statestoadd >= col_end) { + + for (;;) { + + lineptr16[coldraw_cnt ^ 1] = (uint16_t) brd; + + if (++coldraw_cnt == col_end) { + Draw = ++linedraw_cnt == lin_end ? &MainScreen_Blank_Pentagon : &TopBorder_Blank_Pentagon; + return; + } + } + + } else { + + for (; statestoadd > 0; statestoadd--) { + lineptr16[coldraw_cnt ^ 1] = (uint16_t) brd; + coldraw_cnt++; + } + } } -void IRAM_ATTR VIDEO::MainScreen_Blank(unsigned int statestoadd, bool contended) { +IRAM_ATTR void VIDEO::MainScreen_Blank(unsigned int statestoadd, bool contended) { CPU::tstates += statestoadd; @@ -449,7 +516,7 @@ void IRAM_ATTR VIDEO::MainScreen_Blank(unsigned int statestoadd, bool contended) } -void IRAM_ATTR VIDEO::MainScreen_Blank_Pentagon(unsigned int statestoadd, bool contended) { +IRAM_ATTR void VIDEO::MainScreen_Blank_Pentagon(unsigned int statestoadd, bool contended) { CPU::tstates += statestoadd; @@ -475,7 +542,7 @@ void IRAM_ATTR VIDEO::MainScreen_Blank_Pentagon(unsigned int statestoadd, bool c } -void IRAM_ATTR VIDEO::MainScreenLB(unsigned int statestoadd, bool contended) { +IRAM_ATTR void VIDEO::MainScreenLB(unsigned int statestoadd, bool contended) { if (contended) statestoadd += wait_st[CPU::tstates - tstateDraw]; @@ -510,7 +577,7 @@ void IRAM_ATTR VIDEO::MainScreenLB(unsigned int statestoadd, bool contended) { } -void IRAM_ATTR VIDEO::MainScreenLB_Pentagon(unsigned int statestoadd, bool contended) { +IRAM_ATTR void VIDEO::MainScreenLB_Pentagon(unsigned int statestoadd, bool contended) { CPU::tstates += statestoadd; statestoadd += video_rest; @@ -531,7 +598,8 @@ void IRAM_ATTR VIDEO::MainScreenLB_Pentagon(unsigned int statestoadd, bool conte } -void VIDEO::MainScreen(unsigned int statestoadd, bool contended) { +// IRAM_ATTR void VIDEO::MainScreen(unsigned int statestoadd, bool contended) { +void VIDEO::MainScreen(unsigned int statestoadd, bool contended) { uint8_t att, bmp; @@ -541,28 +609,51 @@ void VIDEO::MainScreen(unsigned int statestoadd, bool contended) { statestoadd += video_rest; - video_rest = statestoadd & 0x03; // Mod 4 + video_rest = statestoadd & 0x03; - for (int i=0; i < (statestoadd >> 2); i++) { + int loopCount = statestoadd >> 2; - att = grmem[attOffset++]; // get attribute byte - bmp = (att & flashing) ? ~grmem[bmpOffset++] : grmem[bmpOffset++]; + if (coldraw_cnt + loopCount > 35) { - *lineptr32++ = AluBytes[bmp >> 4][att]; - *lineptr32++ = AluBytes[bmp & 0xF][att]; + for (;;) { + + att = grmem[attOffset++]; + bmp = (att & flashing) ? ~grmem[bmpOffset++] : grmem[bmpOffset++]; + + *lineptr32++ = AluBytes[bmp >> 4][att]; + *lineptr32++ = AluBytes[bmp & 0xF][att]; + + loopCount--; + + if (++coldraw_cnt > 35) { + Draw = MainScreenRB; + video_rest += loopCount << 2; + MainScreenRB(0,false); + return; + } + + } + + } else { + + coldraw_cnt += loopCount; + + for (;loopCount > 0 ; loopCount--) { + + att = grmem[attOffset++]; // get attribute byte + bmp = (att & flashing) ? ~grmem[bmpOffset++] : grmem[bmpOffset++]; + + *lineptr32++ = AluBytes[bmp >> 4][att]; + *lineptr32++ = AluBytes[bmp & 0xF][att]; - if (++coldraw_cnt > 35) { - Draw = MainScreenRB; - video_rest += ((statestoadd >> 2) - (i + 1)) << 2; - MainScreenRB(0,false); - return; } } } -void VIDEO::MainScreen_Pentagon(unsigned int statestoadd, bool contended) { +// IRAM_ATTR void VIDEO::MainScreen_Pentagon(unsigned int statestoadd, bool contended) { +void VIDEO::MainScreen_Pentagon(unsigned int statestoadd, bool contended) { uint8_t att, bmp; @@ -570,22 +661,44 @@ void VIDEO::MainScreen_Pentagon(unsigned int statestoadd, bool contended) { statestoadd += video_rest; - video_rest = statestoadd & 0x03; // Mod 4 - - for (int i=0; i < (statestoadd >> 2); i++) { + video_rest = statestoadd & 0x03; + + int loopCount = statestoadd >> 2; + + if (coldraw_cnt + loopCount > 35) { - att = grmem[attOffset++]; // get attribute byte - bmp = (att & flashing) ? ~grmem[bmpOffset++] : grmem[bmpOffset++]; + for (;;) { + + att = grmem[attOffset++]; + bmp = (att & flashing) ? ~grmem[bmpOffset++] : grmem[bmpOffset++]; - *lineptr32++ = AluBytes[bmp >> 4][att]; - *lineptr32++ = AluBytes[bmp & 0xF][att]; + *lineptr32++ = AluBytes[bmp >> 4][att]; + *lineptr32++ = AluBytes[bmp & 0xF][att]; + + loopCount--; + + if (++coldraw_cnt > 35) { + coldraw_cnt = 0; + Draw = MainScreen_Pentagon_delay; + video_rest += loopCount << 2; + MainScreen_Pentagon_delay(0,false); + return; + } + + } + + } else { + + coldraw_cnt += loopCount; + + for (;loopCount > 0 ; loopCount--) { + + att = grmem[attOffset++]; + bmp = (att & flashing) ? ~grmem[bmpOffset++] : grmem[bmpOffset++]; + + *lineptr32++ = AluBytes[bmp >> 4][att]; + *lineptr32++ = AluBytes[bmp & 0xF][att]; - if (++coldraw_cnt > 35) { - coldraw_cnt = 0; - Draw = MainScreen_Pentagon_delay; - video_rest += ((statestoadd >> 2) - (i + 1)) << 2; - MainScreen_Pentagon_delay(0,false); - return; } } @@ -593,12 +706,15 @@ void VIDEO::MainScreen_Pentagon(unsigned int statestoadd, bool contended) { } // Needed to compensate +2 tstates added before in MainScreenLB_Pentagon -void VIDEO::MainScreen_Pentagon_delay(unsigned int statestoadd, bool contended) { +// IRAM_ATTR void VIDEO::MainScreen_Pentagon_delay(unsigned int statestoadd, bool contended) { +void VIDEO::MainScreen_Pentagon_delay(unsigned int statestoadd, bool contended) { CPU::tstates += statestoadd; statestoadd += video_rest; + video_rest = 0; + for (int i=0; i < statestoadd; i++) { if (++coldraw_cnt > 1) { coldraw_cnt = col_end; @@ -664,7 +780,8 @@ void VIDEO::MainScreen_Pentagon_delay(unsigned int statestoadd, bool contended) // } -void VIDEO::MainScreen_OSD(unsigned int statestoadd, bool contended) { +// IRAM_ATTR void VIDEO::MainScreen_OSD(unsigned int statestoadd, bool contended) { +void VIDEO::MainScreen_OSD(unsigned int statestoadd, bool contended) { uint8_t att, bmp; @@ -702,7 +819,8 @@ void VIDEO::MainScreen_OSD(unsigned int statestoadd, bool contended) { } -void VIDEO::MainScreen_OSD_Pentagon(unsigned int statestoadd, bool contended) { +// IRAM_ATTR void VIDEO::MainScreen_OSD_Pentagon(unsigned int statestoadd, bool contended) { +void VIDEO::MainScreen_OSD_Pentagon(unsigned int statestoadd, bool contended) { uint8_t att, bmp; @@ -742,29 +860,34 @@ void VIDEO::MainScreen_OSD_Pentagon(unsigned int statestoadd, bool contended) { } -void IRAM_ATTR VIDEO::MainScreenRB(unsigned int statestoadd, bool contended) { +IRAM_ATTR void VIDEO::MainScreenRB(unsigned int statestoadd, bool contended) { if (contended) statestoadd += wait_st[CPU::tstates - tstateDraw]; CPU::tstates += statestoadd; + statestoadd += video_rest; - video_rest = statestoadd & 0x03; // Mod 4 - for (int i=0; i < (statestoadd >> 2); i++) { + video_rest = statestoadd & 0x03; + + for (int i = 0; i < statestoadd >> 2; i++) { + *lineptr32++ = brd; *lineptr32++ = brd; if (++coldraw_cnt == 40) { + tstateDraw += tStatesPerLine; Draw = ++linedraw_cnt == (is169 ? 196 : 216) ? &BottomBorder_Blank : &MainScreen_Blank; return; + } } } -void IRAM_ATTR VIDEO::MainScreenRB_Pentagon(unsigned int statestoadd, bool contended) { +IRAM_ATTR void VIDEO::MainScreenRB_Pentagon(unsigned int statestoadd, bool contended) { CPU::tstates += statestoadd; @@ -782,7 +905,7 @@ void IRAM_ATTR VIDEO::MainScreenRB_Pentagon(unsigned int statestoadd, bool conte } -void IRAM_ATTR VIDEO::BottomBorder_Blank(unsigned int statestoadd, bool contended) { +IRAM_ATTR void VIDEO::BottomBorder_Blank(unsigned int statestoadd, bool contended) { CPU::tstates += statestoadd; @@ -798,7 +921,7 @@ void IRAM_ATTR VIDEO::BottomBorder_Blank(unsigned int statestoadd, bool contende } -void IRAM_ATTR VIDEO::BottomBorder_Blank_Pentagon(unsigned int statestoadd, bool contended) { +IRAM_ATTR void VIDEO::BottomBorder_Blank_Pentagon(unsigned int statestoadd, bool contended) { CPU::tstates += statestoadd; @@ -821,26 +944,56 @@ void IRAM_ATTR VIDEO::BottomBorder_Blank_Pentagon(unsigned int statestoadd, bool } -void IRAM_ATTR VIDEO::BottomBorder(unsigned int statestoadd, bool contended) { +IRAM_ATTR void VIDEO::BottomBorder(unsigned int statestoadd, bool contended) { CPU::tstates += statestoadd; statestoadd += video_rest; video_rest = statestoadd & 0x03; // Mod 4 - for (int i=0; i < (statestoadd >> 2); i++) { + + // for (int i=0; i < (statestoadd >> 2); i++) { + + // *lineptr32++ = brd; + // *lineptr32++ = brd; + // if (++coldraw_cnt == 40) { + // Draw = ++linedraw_cnt == (is169 ? 200 : 240) ? &Blank : &BottomBorder_Blank ; + // return; + // } + // } + + int loopCount = statestoadd >> 2; + + if (coldraw_cnt + loopCount > 39) { + + for (;;) { + + *lineptr32++ = brd; + *lineptr32++ = brd; + + if (++coldraw_cnt > 39) { + Draw = ++linedraw_cnt == (is169 ? 200 : 240) ? &Blank : &BottomBorder_Blank ; + return; + } - *lineptr32++ = brd; - *lineptr32++ = brd; - if (++coldraw_cnt == 40) { - Draw = ++linedraw_cnt == (is169 ? 200 : 240) ? &Blank : &BottomBorder_Blank ; - return; } + + } else { + + coldraw_cnt += loopCount; + + for (;loopCount > 0 ; loopCount--) { + + *lineptr32++ = brd; + *lineptr32++ = brd; + + } + } } -void IRAM_ATTR VIDEO::BottomBorder_Pentagon(unsigned int statestoadd, bool contended) { +IRAM_ATTR void VIDEO::BottomBorder_Pentagon(unsigned int statestoadd, bool contended) { CPU::tstates += statestoadd; @@ -857,26 +1010,27 @@ void IRAM_ATTR VIDEO::BottomBorder_Pentagon(unsigned int statestoadd, bool conte } -void IRAM_ATTR VIDEO::BottomBorder_OSD(unsigned int statestoadd, bool contended) { +IRAM_ATTR void VIDEO::BottomBorder_OSD(unsigned int statestoadd, bool contended) { CPU::tstates += statestoadd; statestoadd += video_rest; video_rest = statestoadd & 0x03; // Mod 4 - for (int i=0; i < (statestoadd >> 2); i++) { + + for (int i=0; i < statestoadd >> 2; i++) { - if ((linedraw_cnt<220) || (linedraw_cnt>235)) { + if (linedraw_cnt < 220 || linedraw_cnt>235) { *lineptr32++ = brd; *lineptr32++ = brd; } else { - if ((coldraw_cnt<21) || (coldraw_cnt>38)) { + if (coldraw_cnt < 21 || coldraw_cnt > 38) { *lineptr32++ = brd; *lineptr32++ = brd; - } else lineptr32+=2; + } else lineptr32 += 2; } - if (++coldraw_cnt == 40) { + if (++coldraw_cnt > 39) { Draw = ++linedraw_cnt == 240 ? &Blank : &BottomBorder_Blank ; return; } @@ -884,24 +1038,22 @@ void IRAM_ATTR VIDEO::BottomBorder_OSD(unsigned int statestoadd, bool contended) } -void IRAM_ATTR VIDEO::BottomBorder_OSD_Pentagon(unsigned int statestoadd, bool contended) { +IRAM_ATTR void VIDEO::BottomBorder_OSD_Pentagon(unsigned int statestoadd, bool contended) { CPU::tstates += statestoadd; statestoadd += video_rest; video_rest = 0; + for(;statestoadd > 0; statestoadd--) { - if ((linedraw_cnt < 220) || (linedraw_cnt > 235)) { + if (linedraw_cnt < 220 || linedraw_cnt > 235) + lineptr16[coldraw_cnt ^ 1] = (uint16_t) brd; + else if (coldraw_cnt < 84 || coldraw_cnt > 155) lineptr16[coldraw_cnt ^ 1] = (uint16_t) brd; - } else { - if ((coldraw_cnt < 84) || (coldraw_cnt > 155)) { - lineptr16[coldraw_cnt ^ 1] = (uint16_t) brd; - } - } - if (++coldraw_cnt == 160) { + if (++coldraw_cnt > 159) { Draw = ++linedraw_cnt == 240 ? &Blank : &BottomBorder_Blank_Pentagon; return; } @@ -909,19 +1061,13 @@ void IRAM_ATTR VIDEO::BottomBorder_OSD_Pentagon(unsigned int statestoadd, bool c } -void IRAM_ATTR VIDEO::Blank(unsigned int statestoadd, bool contended) { +IRAM_ATTR void VIDEO::Blank(unsigned int statestoadd, bool contended) { CPU::tstates += statestoadd; - // if (CPU::tstates < tStatesPerLine) { - // linedraw_cnt = 0; - // tstateDraw = tStatesScreen; - // Draw = Z80Ops::isPentagon ? &TopBorder_Blank_Pentagon : &TopBorder_Blank; - // } - } -void IRAM_ATTR VIDEO::EndFrame() { +IRAM_ATTR void VIDEO::EndFrame() { linedraw_cnt = 0; tstateDraw = tStatesScreen; @@ -933,12 +1079,12 @@ void IRAM_ATTR VIDEO::EndFrame() { // /////////////////////////////////////////////////////////////////////////////// // // Flush -> Flush screen after HALT // /////////////////////////////////////////////////////////////////////////////// -void IRAM_ATTR VIDEO::Flush() { +// IRAM_ATTR void VIDEO::Flush() { - // while (CPU::tstates < CPU::statesInFrame) { - while (Draw != &Blank) - Draw(tStatesPerLine,false); - // } +// // while (CPU::tstates < CPU::statesInFrame) { +// while (Draw != &Blank) +// Draw(tStatesPerLine,false); +// // } -} +// } diff --git a/src/Z80_JLS.cpp b/src/Z80_JLS.cpp index a3184cb9..2dbb9484 100644 --- a/src/Z80_JLS.cpp +++ b/src/Z80_JLS.cpp @@ -25,11 +25,12 @@ #include "CPU.h" #include "Tape.h" #include "Config.h" +#include "FileUtils.h" -#pragma GCC optimize ("O3") +// #pragma GCC optimize("O3") uint8_t page; -#define FETCH_OPCODE(result,address) page = address >> 14; VIDEO::Draw(4,MemESP::ramContended[page]); result = MemESP::ramCurrent[page][address & 0x3fff]; +// #define FETCH_OPCODE(result,address) page = address >> 14; VIDEO::Draw(4,MemESP::ramContended[page]); result = MemESP::ramCurrent[page][address & 0x3fff]; #define PEEK8(result,address) page = address >> 14; VIDEO::Draw(3,MemESP::ramContended[page]); result = MemESP::ramCurrent[page][address & 0x3fff]; /////////////////////////////////////////////////////////////////////////////// @@ -651,7 +652,7 @@ void Z80::push(uint16_t word) { } // LDI -void IRAM_ATTR Z80::ldi(void) { +IRAM_ATTR void Z80::ldi(void) { // uint8_t work8 = Z80Ops::peek8(REG_HL); PEEK8(uint8_t work8,REG_HL); @@ -676,7 +677,7 @@ void IRAM_ATTR Z80::ldi(void) { } // LDD -void IRAM_ATTR Z80::ldd(void) { +IRAM_ATTR void Z80::ldd(void) { // uint8_t work8 = Z80Ops::peek8(REG_HL); PEEK8(uint8_t work8,REG_HL); @@ -700,7 +701,7 @@ void IRAM_ATTR Z80::ldd(void) { } // CPI -void IRAM_ATTR Z80::cpi(void) { +IRAM_ATTR void Z80::cpi(void) { // uint8_t memHL = Z80Ops::peek8(REG_HL); PEEK8(uint8_t memHL,REG_HL); @@ -726,7 +727,7 @@ void IRAM_ATTR Z80::cpi(void) { } // CPD -void IRAM_ATTR Z80::cpd(void) { +IRAM_ATTR void Z80::cpd(void) { // uint8_t memHL = Z80Ops::peek8(REG_HL); PEEK8(uint8_t memHL,REG_HL); @@ -752,7 +753,7 @@ void IRAM_ATTR Z80::cpd(void) { } // INI -void IRAM_ATTR Z80::ini(void) { +IRAM_ATTR void Z80::ini(void) { REG_WZ = REG_BC; Z80Ops::addressOnBus(getPairIR().word, 1); uint8_t work8 = Ports::input(REG_WZ++); @@ -783,7 +784,7 @@ void IRAM_ATTR Z80::ini(void) { } // IND -void IRAM_ATTR Z80::ind(void) { +IRAM_ATTR void Z80::ind(void) { REG_WZ = REG_BC; Z80Ops::addressOnBus(getPairIR().word, 1); uint8_t work8 = Ports::input(REG_WZ--); @@ -814,7 +815,7 @@ void IRAM_ATTR Z80::ind(void) { } // OUTI -void IRAM_ATTR Z80::outi(void) { +IRAM_ATTR void Z80::outi(void) { Z80Ops::addressOnBus(getPairIR().word, 1); @@ -848,7 +849,7 @@ void IRAM_ATTR Z80::outi(void) { } // OUTD -void IRAM_ATTR Z80::outd(void) { +IRAM_ATTR void Z80::outd(void) { Z80Ops::addressOnBus(getPairIR().word, 1); @@ -908,7 +909,7 @@ void Z80::bitTest(uint8_t mask, uint8_t reg) { flagQ = true; } -void IRAM_ATTR Z80::check_trdos() { +IRAM_ATTR void Z80::check_trdos() { if (!ESPectrum::trdos) { @@ -927,7 +928,7 @@ void IRAM_ATTR Z80::check_trdos() { } -void IRAM_ATTR Z80::check_trdos_unpage() { +IRAM_ATTR void Z80::check_trdos_unpage() { if (ESPectrum::trdos) { @@ -1021,7 +1022,9 @@ void Z80::nmi(void) { // 2.- Si estaba en un HALT esperando una INT, lo saca de la espera // Z80Ops::fetchOpcode(REG_PC); // Z80Ops::interruptHandlingTime(1); - FETCH_OPCODE(uint8_t discardOpCode, REG_PC); + uint8_t pg = REG_PC >> 14; + VIDEO::Draw(4,MemESP::ramContended[pg]); + // FETCH_OPCODE(uint8_t discardOpCode, REG_PC); VIDEO::Draw(1, false); // if (halted) { @@ -1035,7 +1038,7 @@ void Z80::nmi(void) { REG_PC = REG_WZ = 0x0066; } -void IRAM_ATTR Z80::checkINT(void) { +IRAM_ATTR void Z80::checkINT(void) { // Comprueba si está activada la señal INT if (ffIFF1 && !pendingEI && Z80Ops::isActiveINT()) { @@ -1045,13 +1048,13 @@ void IRAM_ATTR Z80::checkINT(void) { } -void IRAM_ATTR Z80::incRegR(uint8_t inc) { +IRAM_ATTR void Z80::incRegR(uint8_t inc) { regR += inc; } -void IRAM_ATTR Z80::execute() { +IRAM_ATTR void Z80::execute() { uint8_t pg = REG_PC >> 14; VIDEO::Draw(4,MemESP::ramContended[pg]); @@ -1108,7 +1111,7 @@ void IRAM_ATTR Z80::execute() { } -void IRAM_ATTR Z80::exec_nocheck() { +IRAM_ATTR void Z80::exec_nocheck() { uint8_t pg = REG_PC >> 14; VIDEO::Draw(4,MemESP::ramContended[pg]); @@ -2318,7 +2321,7 @@ void Z80::decodeOpcodebe() } -void IRAM_ATTR Z80::decodeOpcodebf() +IRAM_ATTR void Z80::decodeOpcodebf() { /* CP A */ cp(regA); @@ -2450,7 +2453,12 @@ void Z80::decodeOpcodeca() void Z80::decodeOpcodecb() { /* Subconjunto de instrucciones */ - FETCH_OPCODE(opCode, REG_PC); + + uint8_t pg = REG_PC >> 14; + VIDEO::Draw(4,MemESP::ramContended[pg]); + opCode = MemESP::ramCurrent[pg][REG_PC & 0x3fff]; + // FETCH_OPCODE(opCode, REG_PC); + REG_PC++; regR++; @@ -2647,7 +2655,11 @@ void Z80::decodeOpcodedc() void Z80::decodeOpcodedd() { /* Subconjunto de instrucciones */ // opCode = Z80Ops::fetchOpcode(REG_PC++); - FETCH_OPCODE(opCode,REG_PC); + uint8_t pg = REG_PC >> 14; + VIDEO::Draw(4,MemESP::ramContended[pg]); + opCode = MemESP::ramCurrent[pg][REG_PC & 0x3fff]; + // FETCH_OPCODE(opCode,REG_PC); + REG_PC++; regR++; decodeDDFD(regIX); @@ -2805,7 +2817,10 @@ void Z80::decodeOpcodeec() /* CALL PE,nn */ void Z80::decodeOpcodeed() /*Subconjunto de instrucciones*/ { // opCode = Z80Ops::fetchOpcode(REG_PC++); - FETCH_OPCODE(opCode,REG_PC); + uint8_t pg = REG_PC >> 14; + VIDEO::Draw(4,MemESP::ramContended[pg]); + opCode = MemESP::ramCurrent[pg][REG_PC & 0x3fff]; + // FETCH_OPCODE(opCode,REG_PC); REG_PC++; regR++; decodeED(); @@ -2952,7 +2967,10 @@ void Z80::decodeOpcodefc() /* CALL M,nn */ void Z80::decodeOpcodefd() /* Subconjunto de instrucciones */ { // opCode = Z80Ops::fetchOpcode(REG_PC++); - FETCH_OPCODE(opCode,REG_PC); + uint8_t pg = REG_PC >> 14; + VIDEO::Draw(4,MemESP::ramContended[pg]); + opCode = MemESP::ramCurrent[pg][REG_PC & 0x3fff]; + // FETCH_OPCODE(opCode,REG_PC); REG_PC++; regR++; decodeDDFD(regIY); @@ -4599,7 +4617,7 @@ void Z80::decodeDDFD(RegisterPair& regIXY) { for (int i=0; i < 10; i++) name += MemESP::ramCurrent[header_data++ >> 14][header_data & 0x3fff]; rtrim(name); - Tape::tapeSaveName = "/sd/t/" + name + ".tap"; + Tape::tapeSaveName = FileUtils::MountPoint + "/" + FileUtils::TAP_Path + "/" + name + ".tap"; // printf("Removing previuous tap file %s.\n",Tape::tapeSaveName.c_str()); /*int result = */remove(Tape::tapeSaveName.c_str()); @@ -6145,7 +6163,7 @@ void Z80::decodeED(void) { } } -void IRAM_ATTR Z80::copyToRegister(uint8_t value) +IRAM_ATTR void Z80::copyToRegister(uint8_t value) { switch (opCode & 0x07) { diff --git a/src/wd1793.cpp b/src/wd1793.cpp index 752e14f5..605e9566 100644 --- a/src/wd1793.cpp +++ b/src/wd1793.cpp @@ -44,7 +44,7 @@ using namespace std; #include "wd1793.h" -#pragma GCC optimize ("O3") +// #pragma GCC optimize("O3") void WD1793::Init() { From ba8283e64ee1e490ad9ff42293314b841bd50836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Iborra?= Date: Tue, 28 Nov 2023 06:12:47 +0100 Subject: [PATCH 05/30] Video optimizations and pwm adjustments --- components/pwm_audio/include/pwm_audio.h | 2 + components/pwm_audio/pwm_audio.c | 316 ++++++++++--------- include/ESPectrum.h | 4 +- include/Ports.h | 1 + include/Video.h | 4 +- src/Config.cpp | 9 - src/ESPectrum.cpp | 46 ++- src/OSDMain.cpp | 5 +- src/Ports.cpp | 384 +++++++++++++++++++---- src/Snapshot.cpp | 37 +-- src/Video.cpp | 109 ++++--- src/Z80_JLS.cpp | 93 ++++-- 12 files changed, 659 insertions(+), 351 deletions(-) diff --git a/components/pwm_audio/include/pwm_audio.h b/components/pwm_audio/include/pwm_audio.h index 8adb675b..68d8a620 100644 --- a/components/pwm_audio/include/pwm_audio.h +++ b/components/pwm_audio/include/pwm_audio.h @@ -199,6 +199,8 @@ esp_err_t pwm_audio_get_status(pwm_audio_status_t *status); uint32_t pwm_audio_rbstats(void); +uint32_t pwm_audio_pwmcount(void); + #ifdef __cplusplus } #endif diff --git a/components/pwm_audio/pwm_audio.c b/components/pwm_audio/pwm_audio.c index 24126c1a..6b2f002c 100644 --- a/components/pwm_audio/pwm_audio.c +++ b/components/pwm_audio/pwm_audio.c @@ -237,6 +237,9 @@ static inline void ledc_set_right_duty_fast(uint32_t duty_val) *g_ledc_right_conf1_val |= 0x80000000; } + +volatile uint32_t pwmcount = 0; + /* * Timer group ISR handler */ @@ -255,77 +258,82 @@ static void IRAM_ATTR timer_group_isr(void *para) if (handle == NULL) { return ; } + + // pwmcount++; + /* Clear the interrupt */ timer_group_clr_intr_status_in_isr(handle->config.tg_num, handle->config.timer_num); /* After the alarm has been triggered we need enable it again, so it is triggered the next time */ timer_group_enable_alarm_in_isr(handle->config.tg_num, handle->config.timer_num); #endif + static uint8_t wave_h, wave_l; static uint16_t value; ringbuf_handle_t *rb = handle->ringbuf; -#if (1==ISR_DEBUG) - GPIO.out_w1ts = ISR_DEBUG_IO_MASK; -#endif + +// #if (1==ISR_DEBUG) +// GPIO.out_w1ts = ISR_DEBUG_IO_MASK; +// #endif /** * It is believed that the channel configured with GPIO needs to output sound */ - if (handle->channel_mask & CHANNEL_LEFT_MASK) { - if (handle->config.duty_resolution > 8) { - if (rb_get_count(rb) > 1) { - rb_read_byte(rb, &wave_l); - rb_read_byte(rb, &wave_h); - value = ((wave_h << 8) | wave_l); - ledc_set_left_duty_fast(value);/**< set the PWM duty */ - } - } else { + // if (handle->channel_mask & CHANNEL_LEFT_MASK) { + // if (handle->config.duty_resolution > 8) { + // if (rb_get_count(rb) > 1) { + // rb_read_byte(rb, &wave_l); + // rb_read_byte(rb, &wave_h); + // value = ((wave_h << 8) | wave_l); + // ledc_set_left_duty_fast(value);/**< set the PWM duty */ + // } + // } else { if (ESP_OK == rb_read_byte(rb, &wave_h)) { ledc_set_left_duty_fast(wave_h);/**< set the PWM duty */ } - } - } + // } + // } /** * If two gpios are configured, but the audio data has only one channel, copy the data to the right channel * Instead, the right channel data is discarded */ - if (handle->channel_mask & CHANNEL_RIGHT_MASK) { - if (handle->channel_set_num == 1) { - if (handle->config.duty_resolution > 8) { - ledc_set_right_duty_fast(value);/**< set the PWM duty */ - } else { - ledc_set_right_duty_fast(wave_h);/**< set the PWM duty */ - } - } else { - if (handle->config.duty_resolution > 8) { - if (rb_get_count(rb) > 1) { - rb_read_byte(rb, &wave_l); - rb_read_byte(rb, &wave_h); - value = ((wave_h << 8) | wave_l); - ledc_set_right_duty_fast(value);/**< set the PWM duty */ - } - } else { - if (ESP_OK == rb_read_byte(rb, &wave_h)) { - ledc_set_right_duty_fast(wave_h);/**< set the PWM duty */ - } - } - } - } else { - if (handle->channel_set_num == 2) { - /** - * Discard the right channel data only if the right channel is configured but the audio data is stereo - * Read buffer but do nothing - */ - if (handle->config.duty_resolution > 8) { - if (rb_get_count(rb) > 1) { - rb_read_byte(rb, &wave_h); - rb_read_byte(rb, &wave_h); - } - } else { - rb_read_byte(rb, &wave_h); - } - } - } + // if (handle->channel_mask & CHANNEL_RIGHT_MASK) { + // if (handle->channel_set_num == 1) { + // if (handle->config.duty_resolution > 8) { + // ledc_set_right_duty_fast(value);/**< set the PWM duty */ + // } else { + // ledc_set_right_duty_fast(wave_h);/**< set the PWM duty */ + // } + // } else { + // if (handle->config.duty_resolution > 8) { + // if (rb_get_count(rb) > 1) { + // rb_read_byte(rb, &wave_l); + // rb_read_byte(rb, &wave_h); + // value = ((wave_h << 8) | wave_l); + // ledc_set_right_duty_fast(value);/**< set the PWM duty */ + // } + // } else { + // if (ESP_OK == rb_read_byte(rb, &wave_h)) { + // ledc_set_right_duty_fast(wave_h);/**< set the PWM duty */ + // } + // } + // } + // } else { + // if (handle->channel_set_num == 2) { + // /** + // * Discard the right channel data only if the right channel is configured but the audio data is stereo + // * Read buffer but do nothing + // */ + // if (handle->config.duty_resolution > 8) { + // if (rb_get_count(rb) > 1) { + // rb_read_byte(rb, &wave_h); + // rb_read_byte(rb, &wave_h); + // } + // } else { + // rb_read_byte(rb, &wave_h); + // } + // } + // } /** * Send semaphore when buffer free is more than BUFFER_MIN_SIZE @@ -341,14 +349,16 @@ static void IRAM_ATTR timer_group_isr(void *para) portYIELD_FROM_ISR(); } } -#if (1==ISR_DEBUG) - GPIO.out_w1tc = ISR_DEBUG_IO_MASK; -#endif -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) - return true; -#else - return; -#endif + +// #if (1==ISR_DEBUG) +// GPIO.out_w1tc = ISR_DEBUG_IO_MASK; +// #endif +// #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) +// return true; +// #else +// return; +// #endif + } esp_err_t pwm_audio_get_status(pwm_audio_status_t *status) @@ -627,13 +637,17 @@ esp_err_t IRAM_ATTR pwm_audio_write(uint8_t *inbuf, size_t inbuf_len, size_t *by TickType_t start_ticks = xTaskGetTickCount(); while (inbuf_len) { + uint32_t free = rb_get_free(rb); + if (ESP_OK == rb_wait_semaphore(rb, ticks_to_wait)) { - uint32_t bytes_can_write = inbuf_len; - if (inbuf_len > free) { - bytes_can_write = free; - } + // uint32_t bytes_can_write = inbuf_len; + // if (inbuf_len > free) { + // bytes_can_write = free; + // } + + uint32_t bytes_can_write = inbuf_len > free ? free : inbuf_len; // bytes_can_write &= 0xfffffffc;/**< Aligned data, bytes_can_write should be an integral multiple of 4 */ @@ -643,99 +657,98 @@ esp_err_t IRAM_ATTR pwm_audio_write(uint8_t *inbuf, size_t inbuf_len, size_t *by // } /**< Get the difference between PWM resolution and audio samplewidth */ - int8_t shift = handle->bits_per_sample - handle->config.duty_resolution; + // int8_t shift = handle->bits_per_sample - handle->config.duty_resolution; uint32_t len = bytes_can_write; - switch (handle->bits_per_sample) { - case 8: { - if (shift < 0) { - /**< When the PWM resolution is greater than 8 bits, the value needs to be expanded */ - uint16_t value; - uint8_t temp; - shift = -shift; - len >>= 1; - bytes_can_write >>= 1; - - for (size_t i = 0; i < len; i++) { - temp = (inbuf[i] * handle->volume / VOLUME_0DB) + 0x7f; /**< offset */ - value = temp << shift; - rb_write_byte(rb, value); - rb_write_byte(rb, value >> 8); - } - } else { + // switch (handle->bits_per_sample) { + // case 8: { + // if (shift < 0) { + // /**< When the PWM resolution is greater than 8 bits, the value needs to be expanded */ + // uint16_t value; + // uint8_t temp; + // shift = -shift; + // len >>= 1; + // bytes_can_write >>= 1; + + // for (size_t i = 0; i < len; i++) { + // temp = (inbuf[i] * handle->volume / VOLUME_0DB) + 0x7f; /**< offset */ + // value = temp << shift; + // rb_write_byte(rb, value); + // rb_write_byte(rb, value >> 8); + // } + // } else { uint8_t value; - for (size_t i = 0; i < len; i++) { value = (inbuf[i] * handle->volume / VOLUME_0DB) /*+ 0x7f*/; /**< offset */ rb_write_byte(rb, value); } - } - } - break; - - case 16: { - len >>= 1; - uint16_t *buf_16b = (uint16_t *)inbuf; - static uint16_t value_16b; - int16_t temp; - - if (handle->config.duty_resolution > 8) { - for (size_t i = 0; i < len; i++) { - temp = buf_16b[i]; - temp = temp * handle->volume / VOLUME_0DB; - value_16b = temp + 0x7fff; /**< offset */ - value_16b >>= shift; - rb_write_byte(rb, value_16b); - rb_write_byte(rb, value_16b >> 8); - } - } else { - /** - * When the PWM resolution is 8 bit, only one byte is transmitted - */ - for (size_t i = 0; i < len; i++) { - temp = buf_16b[i]; - temp = temp * handle->volume / VOLUME_0DB; - value_16b = temp + 0x7fff; /**< offset */ - value_16b >>= shift; - rb_write_byte(rb, value_16b); - } - } - } - break; - - case 32: { - len >>= 2; - uint32_t *buf_32b = (uint32_t *)inbuf; - uint32_t value; - int32_t temp; - - if (handle->config.duty_resolution > 8) { - for (size_t i = 0; i < len; i++) { - temp = buf_32b[i]; - temp = temp * handle->volume / VOLUME_0DB; - value = temp + 0x7fffffff; /**< offset */ - value >>= shift; - rb_write_byte(rb, value); - rb_write_byte(rb, value >> 8); - } - } else { - /** - * When the PWM resolution is 8 bit, only one byte is transmitted - */ - for (size_t i = 0; i < len; i++) { - temp = buf_32b[i]; - temp = temp * handle->volume / VOLUME_0DB; - value = temp + 0x7fffffff; /**< offset */ - value >>= shift; - rb_write_byte(rb, value); - } - } - } - break; + // } + // } + // break; + + // case 16: { + // len >>= 1; + // uint16_t *buf_16b = (uint16_t *)inbuf; + // static uint16_t value_16b; + // int16_t temp; + + // if (handle->config.duty_resolution > 8) { + // for (size_t i = 0; i < len; i++) { + // temp = buf_16b[i]; + // temp = temp * handle->volume / VOLUME_0DB; + // value_16b = temp + 0x7fff; /**< offset */ + // value_16b >>= shift; + // rb_write_byte(rb, value_16b); + // rb_write_byte(rb, value_16b >> 8); + // } + // } else { + // /** + // * When the PWM resolution is 8 bit, only one byte is transmitted + // */ + // for (size_t i = 0; i < len; i++) { + // temp = buf_16b[i]; + // temp = temp * handle->volume / VOLUME_0DB; + // value_16b = temp + 0x7fff; /**< offset */ + // value_16b >>= shift; + // rb_write_byte(rb, value_16b); + // } + // } + // } + // break; + + // case 32: { + // len >>= 2; + // uint32_t *buf_32b = (uint32_t *)inbuf; + // uint32_t value; + // int32_t temp; + + // if (handle->config.duty_resolution > 8) { + // for (size_t i = 0; i < len; i++) { + // temp = buf_32b[i]; + // temp = temp * handle->volume / VOLUME_0DB; + // value = temp + 0x7fffffff; /**< offset */ + // value >>= shift; + // rb_write_byte(rb, value); + // rb_write_byte(rb, value >> 8); + // } + // } else { + // /** + // * When the PWM resolution is 8 bit, only one byte is transmitted + // */ + // for (size_t i = 0; i < len; i++) { + // temp = buf_32b[i]; + // temp = temp * handle->volume / VOLUME_0DB; + // value = temp + 0x7fffffff; /**< offset */ + // value >>= shift; + // rb_write_byte(rb, value); + // } + // } + // } + // break; - default: - break; - } + // default: + // break; + // } inbuf += bytes_can_write; inbuf_len -= bytes_can_write; @@ -842,3 +855,10 @@ uint32_t pwm_audio_rbstats(void) return rb_get_count(handle->ringbuf); } + +uint32_t pwm_audio_pwmcount(void) +{ + + return pwmcount; + +} diff --git a/include/ESPectrum.h b/include/ESPectrum.h index 871d2091..2459545a 100644 --- a/include/ESPectrum.h +++ b/include/ESPectrum.h @@ -85,7 +85,7 @@ class ESPectrum static int lastaudioBit; static int faudioBit; // static void audioFrameStart(); - static void BeeperGetSample(int Audiobit); + static void BeeperGetSample(); static void AYGetSample(); // static void audioFrameEnd(); static int overSamplesPerFrame; @@ -96,8 +96,6 @@ class ESPectrum static int TapeNameScroller; - // static bool Audio_restart; - static int64_t target; static int ESPoffset; // Testing diff --git a/include/Ports.h b/include/Ports.h index f408d2da..fcb2d6ae 100644 --- a/include/Ports.h +++ b/include/Ports.h @@ -49,6 +49,7 @@ class Ports { private: + static void ioContentionLate(bool contend); static uint8_t port254; static uint8_t speaker_values[8]; diff --git a/include/Video.h b/include/Video.h index 6b5e4397..e170cff9 100644 --- a/include/Video.h +++ b/include/Video.h @@ -181,7 +181,7 @@ static uint16_t spectrum_colors[NUM_SPECTRUM_COLORS] = { BRI_BLACK, BRI_BLUE, BRI_RED, BRI_MAGENTA, BRI_GREEN, BRI_CYAN, BRI_YELLOW, BRI_WHITE, }; -static uint32_t* AluBytes[16]; +// static uint32_t* AluBytes[16]; // static unsigned char DrawStatus; @@ -199,6 +199,6 @@ static unsigned int video_rest; static unsigned int bmpOffset; // offset for bitmap in graphic memory static unsigned int attOffset; // offset for attrib in graphic memory -void precalcAluBytes(); +// void precalcAluBytes(); #endif // VIDEO_h diff --git a/src/Config.cpp b/src/Config.cpp index f78a6db3..7449c22e 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -112,8 +112,6 @@ static inline void trim(std::string &s) { // Read config from FS void Config::load() { - // pwm_audio_stop(); - // Initialize NVS esp_err_t err = nvs_flash_init(); if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { @@ -152,7 +150,6 @@ void Config::load() { // No nvs data found. Save it nvs_close(handle); Config::save(); - // pwm_audio_start(); return; } @@ -340,8 +337,6 @@ void Config::load() { nvs_close(handle); } - // pwm_audio_start(); - } void Config::save() { @@ -351,8 +346,6 @@ void Config::save() { // Dump actual config to FS void Config::save(string value) { - // pwm_audio_stop(); - // Initialize NVS esp_err_t err = nvs_flash_init(); if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { @@ -467,8 +460,6 @@ void Config::save(string value) { // printf("Config saved OK\n"); - // pwm_audio_start(); - } void Config::requestMachine(string newArch, string newRomSet) diff --git a/src/ESPectrum.cpp b/src/ESPectrum.cpp index d43f1b4e..57eff450 100644 --- a/src/ESPectrum.cpp +++ b/src/ESPectrum.cpp @@ -487,7 +487,7 @@ void ESPectrum::setup() // Create Audio task audioTaskQueue = xQueueCreate(1, sizeof(uint8_t *)); // Latest parameter = Core. In ESPIF, main task runs on core 0 by default. In Arduino, loop() runs on core 1. - xTaskCreatePinnedToCore(&ESPectrum::audioTask, "audioTask", 1024, NULL, configMAX_PRIORITIES - 1, &audioTaskHandle, 1); + xTaskCreatePinnedToCore(&ESPectrum::audioTask, "audioTask", /* 1024 */ 1536, NULL, configMAX_PRIORITIES - 1, &audioTaskHandle, 1); // AY Sound AySound::init(); @@ -605,6 +605,7 @@ void ESPectrum::reset() lastaudioBit=0; // Set samples per frame and AY_emu flag depending on arch + int prevOverSamples = overSamplesPerFrame; if (arch == "48K") { overSamplesPerFrame=ESP_AUDIO_OVERSAMPLES_48; samplesPerFrame=ESP_AUDIO_SAMPLES_48; @@ -624,16 +625,20 @@ void ESPectrum::reset() ESPoffset = 0; - pwm_audio_stop(); - - delay(100); // Maybe this fix random sound lost ? - - pwm_audio_set_param(Audio_freq,LEDC_TIMER_8_BIT,1); - - pwm_audio_start(); - - pwm_audio_set_volume(aud_volume); + // Readjust output pwmaudio frequency if needed + if (overSamplesPerFrame != prevOverSamples) { + + // printf("Resetting pwmaudio to freq: %d\n",Audio_freq); + pwm_audio_set_sample_rate(Audio_freq); + // pwm_audio_stop(); + // delay(100); // Maybe this fix random sound lost ? + // pwm_audio_set_param(Audio_freq,LEDC_TIMER_8_BIT,1); + // pwm_audio_start(); + // pwm_audio_set_volume(aud_volume); + + } + // Reset AY emulation AySound::init(); AySound::set_sound_format(Audio_freq,1,8); @@ -653,9 +658,7 @@ IRAM_ATTR bool ESPectrum::readKbd(fabgl::VirtualKeyItem *Nextkey) { // Global keys if (Nextkey->down) { if (Nextkey->vk == fabgl::VK_PRINTSCREEN) { // Capture framebuffer to BMP file in SD Card (thx @dcrespo3d!) - // pwm_audio_stop(); CaptureToBmp(); - // pwm_audio_start(); r = false; } else if (Nextkey->vk == fabgl::VK_SCROLLLOCK) { // Change CursorAsJoy setting @@ -1077,6 +1080,8 @@ IRAM_ATTR void ESPectrum::processKeyboard() { // static int bmax = 0; +// static int sndalive = 0; + //======================================================================================= // AUDIO //======================================================================================= @@ -1084,6 +1089,8 @@ IRAM_ATTR void ESPectrum::audioTask(void *unused) { size_t written; + // esp_err_t err; + // PWM Audio Init pwm_audio_config_t pac; pac.duty_resolution = LEDC_TIMER_8_BIT; @@ -1094,7 +1101,7 @@ IRAM_ATTR void ESPectrum::audioTask(void *unused) { pac.ledc_timer_sel = LEDC_TIMER_0; pac.tg_num = TIMER_GROUP_0; pac.timer_num = TIMER_0; - pac.ringbuf_len = /* 1024 * 8;*/ /*2560;*/ 2880; + pac.ringbuf_len = /* 1024 * 8;*/ /*2560;*/ /* 2880; */ 1024 * 4; pwm_audio_init(&pac); pwm_audio_set_param(Audio_freq,LEDC_TIMER_8_BIT,1); @@ -1105,7 +1112,11 @@ IRAM_ATTR void ESPectrum::audioTask(void *unused) { xQueueReceive(audioTaskQueue, ¶m, portMAX_DELAY); - pwm_audio_write(ESPectrum::audioBuffer, samplesPerFrame, &written, 5 / portTICK_PERIOD_MS); + /* err = */ pwm_audio_write(ESPectrum::audioBuffer, samplesPerFrame, &written, 5 / portTICK_PERIOD_MS); + + // if (err == ESP_FAIL) printf("Audio fail!\n"); + + // sndalive++; xQueueReceive(audioTaskQueue, ¶m, portMAX_DELAY); @@ -1180,7 +1191,7 @@ IRAM_ATTR void ESPectrum::audioTask(void *unused) { } } -IRAM_ATTR void ESPectrum::BeeperGetSample(int Audiobit) { +IRAM_ATTR void ESPectrum::BeeperGetSample() { // Beeper audiobuffer generation (oversample) uint32_t audbufpos = Z80Ops::is128 ? CPU::tstates / 19 : CPU::tstates >> 4; for (;audbufcnt < audbufpos; audbufcnt++) overSamplebuf[audbufcnt] = lastaudioBit; @@ -1281,6 +1292,11 @@ for(;;) { } else { snprintf(OSD::stats_lin1, sizeof(OSD::stats_lin1), "CPU: %05d / IDL: %05d ", (int)(elapsed), (int)(idle)); + + // Audio tests + // snprintf(OSD::stats_lin1, sizeof(OSD::stats_lin1), "CPU: %05d / SND: %05d ", (int)(elapsed), (int)pwm_audio_rbstats()); + // snprintf(OSD::stats_lin1, sizeof(OSD::stats_lin1), "CPU: %05d / SND: %05d ", (int)(elapsed), (int)pwm_audio_pwmcount()); + snprintf(OSD::stats_lin2, sizeof(OSD::stats_lin2), "FPS:%6.2f / FND:%6.2f ", VIDEO::framecnt / (totalseconds / 1000000), VIDEO::framecnt / (totalsecondsnodelay / 1000000)); } diff --git a/src/OSDMain.cpp b/src/OSDMain.cpp index 8eb3ec98..e5037f0a 100644 --- a/src/OSDMain.cpp +++ b/src/OSDMain.cpp @@ -1477,8 +1477,7 @@ void OSD::HWInfo() { multi_heap_info_t info; heap_caps_get_info(&info, MALLOC_CAP_SPIRAM); - uint32_t psramsize = info.total_free_bytes + info.total_allocated_bytes; - // textout = " PSRAM size : " + to_string(info.total_free_bytes + info.total_allocated_bytes) + "\n"; + uint32_t psramsize = (info.total_free_bytes + info.total_allocated_bytes) >> 10; textout = " PSRAM size : " + ( psramsize == 0 ? "N/A or disabled" : to_string(psramsize) + " MB") + "\n"; VIDEO::vga.print(textout.c_str()); @@ -1498,7 +1497,7 @@ void OSD::HWInfo() { textout = " Largest free block : " + to_string(info.largest_free_block) + "\n"; VIDEO::vga.print(textout.c_str()); - textout = " Free (MALLOC_CAP_32BIT) : " + to_string(heap_caps_get_free_size(MALLOC_CAP_32BIT)) + "\n"; + textout = " Free (MALLOC_CAP_32BIT) : " + to_string(heap_caps_get_free_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_32BIT)) + "\n"; VIDEO::vga.print(textout.c_str()); UBaseType_t wm; diff --git a/src/Ports.cpp b/src/Ports.cpp index 66a33b1b..8355ef6e 100644 --- a/src/Ports.cpp +++ b/src/Ports.cpp @@ -61,30 +61,270 @@ uint8_t Ports::speaker_values[8]={ 0, 19, 34, 53, 97, 101, 130, 134 }; DRAM_ATTR uint8_t Ports::port[128]; uint8_t Ports::port254 = 0; +// IRAM_ATTR uint8_t Ports::input(uint16_t address) { + +// uint8_t data; +// uint8_t rambank = address >> 14; + +// // ** I/O Contention (Early) ************************* +// VIDEO::Draw(1, MemESP::ramContended[rambank]); +// // ** I/O Contention (Early) ************************* + +// // ** I/O Contention (Late) ************************** +// if ((address & 0x0001) == 0) { +// VIDEO::Draw(3, true); +// } else { +// if (MemESP::ramContended[rambank]) { +// VIDEO::Draw(1, true); +// VIDEO::Draw(1, true); +// VIDEO::Draw(1, true); +// } else VIDEO::Draw(3, false); +// } +// // ** I/O Contention (Late) ************************** + +// // ULA PORT +// if ((address & 0x0001) == 0) { + +// // The default port value is 0xBF. +// data = 0xbf; + +// uint8_t portHigh = ~(address >> 8) & 0xff; +// for (int row = 0, mask = 0x01; row < 8; row++, mask <<= 1) { +// if ((portHigh & mask) != 0) { +// data &= port[row]; +// } +// } + +// if (Tape::tapeStatus==TAPE_LOADING) { +// Tape::TAP_Read(); +// bitWrite(data,6,Tape::tapeEarBit); +// } else { +// // Issue 2 behaviour only on Spectrum 48K +// if ((Z80Ops::is48) && (Config::Issue2)) { +// if (port254 & 0x18) data |= 0x40; +// } else { +// if (port254 & 0x10) data |= 0x40; +// } +// } + +// } else { + +// // The default port value is 0xFF. +// data = 0xff; + +// // Check if TRDOS Rom is mapped. +// if (ESPectrum::trdos) { + +// int lowByte = address & 0xFF; + +// // Process Beta Disk instruction. +// if (lowByte & 0x80) { +// data = ESPectrum::Betadisk.ReadSystemReg(); +// // printf("WD1793 Read Control Register: %d\n",(int)data); +// return data; +// } + +// switch (lowByte) { +// case 0x1F: +// data = ESPectrum::Betadisk.ReadStatusReg(); +// // printf("WD1793 Read Status Register: %d\n",(int)data); +// return data; +// case 0x3F: +// data = ESPectrum::Betadisk.ReadTrackReg(); +// // printf("WD1793 Read Track Register: %d\n",(int)data); +// return data; +// case 0x5F: +// data = ESPectrum::Betadisk.ReadSectorReg(); +// // printf("WD1793 Read Sector Register: %d\n",(int)data); +// return data; +// case 0x7F: +// data = ESPectrum::Betadisk.ReadDataReg(); +// // printf("WD1793 Read Data Register: %d\n",(int)data); +// return data; +// } + +// } + +// // Kempston Joystick +// if ((Config::joystick) && ((address & 0x00E0) == 0 || (address & 0xFF) == 0xDF)) return port[0x1f]; + +// // Sound (AY-3-8912) +// if (ESPectrum::AY_emu) { +// if ((address & 0xC002) == 0xC000) +// return AySound::getRegisterData(); +// } + +// if (!Z80Ops::isPentagon) { + +// data = VIDEO::getFloatBusData(); + +// if ((!Z80Ops::is48) && ((address & 0x8002) == 0)) { + +// // // Solo en el modelo 128K, pero no en los +2/+2A/+3, si se lee el puerto +// // // 0x7ffd, el valor leído es reescrito en el puerto 0x7ffd. +// // // http://www.speccy.org/foro/viewtopic.php?f=8&t=2374 +// if (!MemESP::pagingLock) { +// MemESP::pagingLock = bitRead(data, 5); +// MemESP::bankLatch = data & 0x7; +// MemESP::ramCurrent[3] = (unsigned char *)MemESP::ram[MemESP::bankLatch]; +// MemESP::ramContended[3] = MemESP::bankLatch & 0x01 ? true: false; +// if (MemESP::videoLatch != bitRead(data, 3)) { +// MemESP::videoLatch = bitRead(data, 3); +// // This, if not using the ptime128 draw version, fixs ptime and ptime128 +// if (((address & 0x0001) != 0) && (MemESP::ramContended[rambank])) { +// VIDEO::Draw(2, false); +// CPU::tstates -= 2; +// } +// VIDEO::grmem = MemESP::videoLatch ? MemESP::ram7 : MemESP::ram5; +// } +// MemESP::romLatch = bitRead(data, 4); +// bitWrite(MemESP::romInUse, 0, MemESP::romLatch); +// MemESP::ramCurrent[0] = (unsigned char *)MemESP::rom[MemESP::romInUse]; +// } + +// } + +// } + +// } + +// return data; + +// } + +// IRAM_ATTR void Ports::output(uint16_t address, uint8_t data) { + +// int Audiobit; +// uint8_t rambank = address >> 14; + +// // ** I/O Contention (Early) ************************* +// VIDEO::Draw(1, MemESP::ramContended[rambank]); +// // ** I/O Contention (Early) ************************* + +// // ULA ======================================================================= +// if ((address & 0x0001) == 0) { + +// port254 = data; + +// // Border color +// if (VIDEO::borderColor != data & 0x07) { +// VIDEO::borderColor = data & 0x07; +// if (!Z80Ops::isPentagon) VIDEO::Draw(0,true); // Seems not needed in Pentagon +// VIDEO::brd = VIDEO::border32[VIDEO::borderColor]; +// } + +// // Beeper Audio +// Audiobit = speaker_values[((data >> 2) & 0x04 ) | (Tape::tapeEarBit << 1) | ((data >> 3) & 0x01)]; +// if (Audiobit != ESPectrum::lastaudioBit) { +// ESPectrum::BeeperGetSample(Audiobit); +// ESPectrum::lastaudioBit = Audiobit; +// } + +// } + +// // AY ======================================================================== +// if ((ESPectrum::AY_emu) && ((address & 0x8002) == 0x8000)) { +// if ((address & 0x4000) != 0) +// AySound::selectRegister(data); +// else { +// ESPectrum::AYGetSample(); +// AySound::setRegisterData(data); +// } +// } + +// // Check if TRDOS Rom is mapped. +// if (ESPectrum::trdos) { + +// int lowByte = address & 0xFF; + +// // Process Beta Disk instruction. +// if (lowByte & 0x80) { +// // printf("WD1793 Write Control Register: %d\n",data); +// ESPectrum::Betadisk.WriteSystemReg(data); +// } else +// switch (lowByte) { +// case 0x1F: +// // printf("WD1793 Write Command Register: %d\n",data); +// ESPectrum::Betadisk.WriteCommandReg(data); +// break; +// case 0x3F: +// // printf("WD1793 Write Track Register: %d\n",data); +// ESPectrum::Betadisk.WriteTrackReg(data); +// break; +// case 0x5F: +// // printf("WD1793 Write Sector Register: %d\n",data); +// ESPectrum::Betadisk.WriteSectorReg(data); +// break; +// case 0x7F: +// // printf("WD1793 Write Data Register: %d\n",data); +// ESPectrum::Betadisk.WriteDataReg(data); +// break; +// } + +// } + +// // ** I/O Contention (Late) ************************** +// if ((address & 0x0001) == 0) { +// VIDEO::Draw(3, true); +// } else { +// if (MemESP::ramContended[rambank]) { +// VIDEO::Draw(1, true); +// VIDEO::Draw(1, true); +// VIDEO::Draw(1, true); +// } else { +// VIDEO::Draw(3, false); +// } +// } +// // ** I/O Contention (Late) ************************** + +// // 128 / PENTAGON ================================================================== +// if ((!Z80Ops::is48) && ((address & 0x8002) == 0)) { + +// if (!MemESP::pagingLock) { + +// MemESP::pagingLock = bitRead(data, 5); + +// MemESP::bankLatch = data & 0x7; +// MemESP::ramCurrent[3] = (unsigned char *)MemESP::ram[MemESP::bankLatch]; +// MemESP::ramContended[3] = Z80Ops::isPentagon ? false : (MemESP::bankLatch & 0x01 ? true: false); + +// MemESP::romLatch = bitRead(data, 4); +// bitWrite(MemESP::romInUse, 0, MemESP::romLatch); +// MemESP::ramCurrent[0] = (unsigned char *)MemESP::rom[MemESP::romInUse]; + +// if (MemESP::videoLatch != bitRead(data, 3)) { +// MemESP::videoLatch = bitRead(data, 3); + +// // Seems not needed in Pentagon +// // This, if not using the ptime128 draw version, fixs ptime and ptime128 +// if (!Z80Ops::isPentagon) { +// if (((address & 0x0001) != 0) && (MemESP::ramContended[rambank])) { +// VIDEO::Draw(2, false); +// CPU::tstates -= 2; +// } +// } + +// VIDEO::grmem = MemESP::videoLatch ? MemESP::ram7 : MemESP::ram5; +// } + +// } + +// } + +// } + IRAM_ATTR uint8_t Ports::input(uint16_t address) { uint8_t data; uint8_t rambank = address >> 14; - // ** I/O Contention (Early) ************************* - VIDEO::Draw(1, MemESP::ramContended[rambank]); - // ** I/O Contention (Early) ************************* + VIDEO::Draw(1, MemESP::ramContended[rambank]); // I/O Contention (Early) - // ** I/O Contention (Late) ************************** - if ((address & 0x0001) == 0) { - VIDEO::Draw(3, true); - } else { - if (MemESP::ramContended[rambank]) { - VIDEO::Draw(1, true); - VIDEO::Draw(1, true); - VIDEO::Draw(1, true); - } else VIDEO::Draw(3, false); - } - // ** I/O Contention (Late) ************************** - // ULA PORT if ((address & 0x0001) == 0) { + VIDEO::Draw(3, true); // I/O Contention (Late) + // The default port value is 0xBF. data = 0xbf; @@ -109,22 +349,28 @@ IRAM_ATTR uint8_t Ports::input(uint16_t address) { } else { + ioContentionLate(MemESP::ramContended[rambank]); + // The default port value is 0xFF. data = 0xff; // Check if TRDOS Rom is mapped. if (ESPectrum::trdos) { - int lowByte = address & 0xFF; + // int lowByte = address & 0xFF; - // Process Beta Disk instruction. - if (lowByte & 0x80) { + // // Process Beta Disk instruction. + // if (lowByte & 0x80) { + // data = ESPectrum::Betadisk.ReadSystemReg(); + // // printf("WD1793 Read Control Register: %d\n",(int)data); + // return data; + // } + + switch (address & 0xFF) { + case 0xFF: data = ESPectrum::Betadisk.ReadSystemReg(); // printf("WD1793 Read Control Register: %d\n",(int)data); return data; - } - - switch (lowByte) { case 0x1F: data = ESPectrum::Betadisk.ReadStatusReg(); // printf("WD1793 Read Status Register: %d\n",(int)data); @@ -197,9 +443,7 @@ IRAM_ATTR void Ports::output(uint16_t address, uint8_t data) { int Audiobit; uint8_t rambank = address >> 14; - // ** I/O Contention (Early) ************************* - VIDEO::Draw(1, MemESP::ramContended[rambank]); - // ** I/O Contention (Early) ************************* + VIDEO::Draw(1, MemESP::ramContended[rambank]); // I/O Contention (Early) // ULA ======================================================================= if ((address & 0x0001) == 0) { @@ -216,33 +460,56 @@ IRAM_ATTR void Ports::output(uint16_t address, uint8_t data) { // Beeper Audio Audiobit = speaker_values[((data >> 2) & 0x04 ) | (Tape::tapeEarBit << 1) | ((data >> 3) & 0x01)]; if (Audiobit != ESPectrum::lastaudioBit) { - ESPectrum::BeeperGetSample(Audiobit); + ESPectrum::BeeperGetSample(); ESPectrum::lastaudioBit = Audiobit; } - } + // AY ======================================================================== + if ((ESPectrum::AY_emu) && ((address & 0x8002) == 0x8000)) { - // AY ======================================================================== - if ((ESPectrum::AY_emu) && ((address & 0x8002) == 0x8000)) { - if ((address & 0x4000) != 0) - AySound::selectRegister(data); - else { - ESPectrum::AYGetSample(); - AySound::setRegisterData(data); - } - } + if ((address & 0x4000) != 0) + AySound::selectRegister(data); + else { + ESPectrum::AYGetSample(); + AySound::setRegisterData(data); + } - // Check if TRDOS Rom is mapped. - if (ESPectrum::trdos) { + VIDEO::Draw(3, true); // I/O Contention (Late) + + return; - int lowByte = address & 0xFF; + } + + VIDEO::Draw(3, true); // I/O Contention (Late) + + } else { + + // AY ======================================================================== + if ((ESPectrum::AY_emu) && ((address & 0x8002) == 0x8000)) { + + if ((address & 0x4000) != 0) + AySound::selectRegister(data); + else { + ESPectrum::AYGetSample(); + AySound::setRegisterData(data); + } + + ioContentionLate(MemESP::ramContended[rambank]); + + return; + + } + + // Check if TRDOS Rom is mapped. + if (ESPectrum::trdos) { + + // int lowByte = address & 0xFF; - // Process Beta Disk instruction. - if (lowByte & 0x80) { - // printf("WD1793 Write Control Register: %d\n",data); - ESPectrum::Betadisk.WriteSystemReg(data); - } else - switch (lowByte) { + switch (address & 0xFF) { + case 0xFF: + // printf("WD1793 Write Control Register: %d\n",data); + ESPectrum::Betadisk.WriteSystemReg(data); + break; case 0x1F: // printf("WD1793 Write Command Register: %d\n",data); ESPectrum::Betadisk.WriteCommandReg(data); @@ -261,21 +528,12 @@ IRAM_ATTR void Ports::output(uint16_t address, uint8_t data) { break; } - } - - // ** I/O Contention (Late) ************************** - if ((address & 0x0001) == 0) { - VIDEO::Draw(3, true); - } else { - if (MemESP::ramContended[rambank]) { - VIDEO::Draw(1, true); - VIDEO::Draw(1, true); - VIDEO::Draw(1, true); - } else { - VIDEO::Draw(3, false); } + + ioContentionLate(MemESP::ramContended[rambank]); + + } - // ** I/O Contention (Late) ************************** // 128 / PENTAGON ================================================================== if ((!Z80Ops::is48) && ((address & 0x8002) == 0)) { @@ -312,3 +570,15 @@ IRAM_ATTR void Ports::output(uint16_t address, uint8_t data) { } } + +IRAM_ATTR void Ports::ioContentionLate(bool contend) { + + if (contend) { + VIDEO::Draw(1, true); + VIDEO::Draw(1, true); + VIDEO::Draw(1, true); + } else { + VIDEO::Draw(3, false); + } + +} \ No newline at end of file diff --git a/src/Snapshot.cpp b/src/Snapshot.cpp index d8a7b0dd..a440105c 100644 --- a/src/Snapshot.cpp +++ b/src/Snapshot.cpp @@ -337,7 +337,6 @@ bool FileSNA::load(string sna_fn, string force_arch) { // /////////////////////////////////////////////////////////////////////////////// bool FileSNA::isPersistAvailable(string filename) { - // pwm_audio_stop(); FILE *f = fopen(filename.c_str(), "rb"); if (f == NULL) @@ -345,8 +344,6 @@ bool FileSNA::isPersistAvailable(string filename) { else fclose(f); - // pwm_audio_start(); - return true; } @@ -434,25 +431,10 @@ bool FileSNA::save(string sna_file, bool blockMode) { FILE *file; - // // Stop keyboard input - // ESPectrum::PS2Controller.keyboard()->suspendPort(); - // if (!ZXKeyb::Exists) ESPectrum::PS2Controller.keybjoystick()->suspendPort(); - - // Stop audio - // pwm_audio_stop(); - file = fopen(sna_file.c_str(), "wb"); if (file==NULL) { printf("FileSNA: Error opening %s for writing",sna_file.c_str()); - - // Resume audio - // pwm_audio_start(); - - // Resume keyboard input - // ESPectrum::PS2Controller.keyboard()->resumePort(); - // if (!ZXKeyb::Exists) ESPectrum::PS2Controller.keybjoystick()->resumePort(); - return false; } @@ -500,12 +482,6 @@ bool FileSNA::save(string sna_file, bool blockMode) { uint8_t page = pages[ipage]; if (!writeMemPage(page, file, blockMode)) { fclose(file); - - // pwm_audio_start(); // Resume audio - - // ESPectrum::PS2Controller.keyboard()->resumePort(); // Resume keyboard input - // if (!ZXKeyb::Exists) ESPectrum::PS2Controller.keybjoystick()->resumePort(); - return false; } @@ -535,12 +511,6 @@ bool FileSNA::save(string sna_file, bool blockMode) { if (page != MemESP::bankLatch && page != 2 && page != 5) { if (!writeMemPage(page, file, blockMode)) { fclose(file); - - // pwm_audio_start(); // Resume audio - - // ESPectrum::PS2Controller.keyboard()->resumePort(); // Resume keyboard input - // if (!ZXKeyb::Exists) ESPectrum::PS2Controller.keybjoystick()->resumePort(); - return false; } @@ -550,13 +520,8 @@ bool FileSNA::save(string sna_file, bool blockMode) { fclose(file); - // pwm_audio_start(); // Resume audio - - // // Resume keyboard input - // ESPectrum::PS2Controller.keyboard()->resumePort(); - // if (!ZXKeyb::Exists) ESPectrum::PS2Controller.keybjoystick()->resumePort(); - return true; + } static uint16_t mkword(uint8_t lobyte, uint8_t hibyte) { diff --git a/src/Video.cpp b/src/Video.cpp index 7cb2a329..50a8690b 100644 --- a/src/Video.cpp +++ b/src/Video.cpp @@ -36,6 +36,7 @@ visit https://zxespectrum.speccy.org/contacto #include "Video.h" #include "CPU.h" #include "MemESP.h" +#include "ZXKeyb.h" #include "Config.h" #include "OSDMain.h" #include "hardconfig.h" @@ -146,60 +147,60 @@ void precalcColors() { } -// // ALUBYTES -// static const uint32_t AluBytes[16][256]={ -// {0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff}, -// {0xc0c0c0c0,0xc0c0e0c0,0xc0c0c2c0,0xc0c0e2c0,0xc0c0c8c0,0xc0c0e8c0,0xc0c0cac0,0xc0c0eac0,0xe0e0c0e0,0xe0e0e0e0,0xe0e0c2e0,0xe0e0e2e0,0xe0e0c8e0,0xe0e0e8e0,0xe0e0cae0,0xe0e0eae0,0xc2c2c0c2,0xc2c2e0c2,0xc2c2c2c2,0xc2c2e2c2,0xc2c2c8c2,0xc2c2e8c2,0xc2c2cac2,0xc2c2eac2,0xe2e2c0e2,0xe2e2e0e2,0xe2e2c2e2,0xe2e2e2e2,0xe2e2c8e2,0xe2e2e8e2,0xe2e2cae2,0xe2e2eae2,0xc8c8c0c8,0xc8c8e0c8,0xc8c8c2c8,0xc8c8e2c8,0xc8c8c8c8,0xc8c8e8c8,0xc8c8cac8,0xc8c8eac8,0xe8e8c0e8,0xe8e8e0e8,0xe8e8c2e8,0xe8e8e2e8,0xe8e8c8e8,0xe8e8e8e8,0xe8e8cae8,0xe8e8eae8,0xcacac0ca,0xcacae0ca,0xcacac2ca,0xcacae2ca,0xcacac8ca,0xcacae8ca,0xcacacaca,0xcacaeaca,0xeaeac0ea,0xeaeae0ea,0xeaeac2ea,0xeaeae2ea,0xeaeac8ea,0xeaeae8ea,0xeaeacaea,0xeaeaeaea,0xc0c0c0c0,0xc0c0f0c0,0xc0c0c3c0,0xc0c0f3c0,0xc0c0ccc0,0xc0c0fcc0,0xc0c0cfc0,0xc0c0ffc0,0xf0f0c0f0,0xf0f0f0f0,0xf0f0c3f0,0xf0f0f3f0,0xf0f0ccf0,0xf0f0fcf0,0xf0f0cff0,0xf0f0fff0,0xc3c3c0c3,0xc3c3f0c3,0xc3c3c3c3,0xc3c3f3c3,0xc3c3ccc3,0xc3c3fcc3,0xc3c3cfc3,0xc3c3ffc3,0xf3f3c0f3,0xf3f3f0f3,0xf3f3c3f3,0xf3f3f3f3,0xf3f3ccf3,0xf3f3fcf3,0xf3f3cff3,0xf3f3fff3,0xccccc0cc,0xccccf0cc,0xccccc3cc,0xccccf3cc,0xcccccccc,0xccccfccc,0xcccccfcc,0xccccffcc,0xfcfcc0fc,0xfcfcf0fc,0xfcfcc3fc,0xfcfcf3fc,0xfcfcccfc,0xfcfcfcfc,0xfcfccffc,0xfcfcfffc,0xcfcfc0cf,0xcfcff0cf,0xcfcfc3cf,0xcfcff3cf,0xcfcfcccf,0xcfcffccf,0xcfcfcfcf,0xcfcfffcf,0xffffc0ff,0xfffff0ff,0xffffc3ff,0xfffff3ff,0xffffccff,0xfffffcff,0xffffcfff,0xffffffff,0xc0c0c0c0,0xc0c0e0c0,0xc0c0c2c0,0xc0c0e2c0,0xc0c0c8c0,0xc0c0e8c0,0xc0c0cac0,0xc0c0eac0,0xe0e0c0e0,0xe0e0e0e0,0xe0e0c2e0,0xe0e0e2e0,0xe0e0c8e0,0xe0e0e8e0,0xe0e0cae0,0xe0e0eae0,0xc2c2c0c2,0xc2c2e0c2,0xc2c2c2c2,0xc2c2e2c2,0xc2c2c8c2,0xc2c2e8c2,0xc2c2cac2,0xc2c2eac2,0xe2e2c0e2,0xe2e2e0e2,0xe2e2c2e2,0xe2e2e2e2,0xe2e2c8e2,0xe2e2e8e2,0xe2e2cae2,0xe2e2eae2,0xc8c8c0c8,0xc8c8e0c8,0xc8c8c2c8,0xc8c8e2c8,0xc8c8c8c8,0xc8c8e8c8,0xc8c8cac8,0xc8c8eac8,0xe8e8c0e8,0xe8e8e0e8,0xe8e8c2e8,0xe8e8e2e8,0xe8e8c8e8,0xe8e8e8e8,0xe8e8cae8,0xe8e8eae8,0xcacac0ca,0xcacae0ca,0xcacac2ca,0xcacae2ca,0xcacac8ca,0xcacae8ca,0xcacacaca,0xcacaeaca,0xeaeac0ea,0xeaeae0ea,0xeaeac2ea,0xeaeae2ea,0xeaeac8ea,0xeaeae8ea,0xeaeacaea,0xeaeaeaea,0xc0c0c0c0,0xc0c0f0c0,0xc0c0c3c0,0xc0c0f3c0,0xc0c0ccc0,0xc0c0fcc0,0xc0c0cfc0,0xc0c0ffc0,0xf0f0c0f0,0xf0f0f0f0,0xf0f0c3f0,0xf0f0f3f0,0xf0f0ccf0,0xf0f0fcf0,0xf0f0cff0,0xf0f0fff0,0xc3c3c0c3,0xc3c3f0c3,0xc3c3c3c3,0xc3c3f3c3,0xc3c3ccc3,0xc3c3fcc3,0xc3c3cfc3,0xc3c3ffc3,0xf3f3c0f3,0xf3f3f0f3,0xf3f3c3f3,0xf3f3f3f3,0xf3f3ccf3,0xf3f3fcf3,0xf3f3cff3,0xf3f3fff3,0xccccc0cc,0xccccf0cc,0xccccc3cc,0xccccf3cc,0xcccccccc,0xccccfccc,0xcccccfcc,0xccccffcc,0xfcfcc0fc,0xfcfcf0fc,0xfcfcc3fc,0xfcfcf3fc,0xfcfcccfc,0xfcfcfcfc,0xfcfccffc,0xfcfcfffc,0xcfcfc0cf,0xcfcff0cf,0xcfcfc3cf,0xcfcff3cf,0xcfcfcccf,0xcfcffccf,0xcfcfcfcf,0xcfcfffcf,0xffffc0ff,0xfffff0ff,0xffffc3ff,0xfffff3ff,0xffffccff,0xfffffcff,0xffffcfff,0xffffffff}, -// {0xc0c0c0c0,0xc0c0c0e0,0xc0c0c0c2,0xc0c0c0e2,0xc0c0c0c8,0xc0c0c0e8,0xc0c0c0ca,0xc0c0c0ea,0xe0e0e0c0,0xe0e0e0e0,0xe0e0e0c2,0xe0e0e0e2,0xe0e0e0c8,0xe0e0e0e8,0xe0e0e0ca,0xe0e0e0ea,0xc2c2c2c0,0xc2c2c2e0,0xc2c2c2c2,0xc2c2c2e2,0xc2c2c2c8,0xc2c2c2e8,0xc2c2c2ca,0xc2c2c2ea,0xe2e2e2c0,0xe2e2e2e0,0xe2e2e2c2,0xe2e2e2e2,0xe2e2e2c8,0xe2e2e2e8,0xe2e2e2ca,0xe2e2e2ea,0xc8c8c8c0,0xc8c8c8e0,0xc8c8c8c2,0xc8c8c8e2,0xc8c8c8c8,0xc8c8c8e8,0xc8c8c8ca,0xc8c8c8ea,0xe8e8e8c0,0xe8e8e8e0,0xe8e8e8c2,0xe8e8e8e2,0xe8e8e8c8,0xe8e8e8e8,0xe8e8e8ca,0xe8e8e8ea,0xcacacac0,0xcacacae0,0xcacacac2,0xcacacae2,0xcacacac8,0xcacacae8,0xcacacaca,0xcacacaea,0xeaeaeac0,0xeaeaeae0,0xeaeaeac2,0xeaeaeae2,0xeaeaeac8,0xeaeaeae8,0xeaeaeaca,0xeaeaeaea,0xc0c0c0c0,0xc0c0c0f0,0xc0c0c0c3,0xc0c0c0f3,0xc0c0c0cc,0xc0c0c0fc,0xc0c0c0cf,0xc0c0c0ff,0xf0f0f0c0,0xf0f0f0f0,0xf0f0f0c3,0xf0f0f0f3,0xf0f0f0cc,0xf0f0f0fc,0xf0f0f0cf,0xf0f0f0ff,0xc3c3c3c0,0xc3c3c3f0,0xc3c3c3c3,0xc3c3c3f3,0xc3c3c3cc,0xc3c3c3fc,0xc3c3c3cf,0xc3c3c3ff,0xf3f3f3c0,0xf3f3f3f0,0xf3f3f3c3,0xf3f3f3f3,0xf3f3f3cc,0xf3f3f3fc,0xf3f3f3cf,0xf3f3f3ff,0xccccccc0,0xccccccf0,0xccccccc3,0xccccccf3,0xcccccccc,0xccccccfc,0xcccccccf,0xccccccff,0xfcfcfcc0,0xfcfcfcf0,0xfcfcfcc3,0xfcfcfcf3,0xfcfcfccc,0xfcfcfcfc,0xfcfcfccf,0xfcfcfcff,0xcfcfcfc0,0xcfcfcff0,0xcfcfcfc3,0xcfcfcff3,0xcfcfcfcc,0xcfcfcffc,0xcfcfcfcf,0xcfcfcfff,0xffffffc0,0xfffffff0,0xffffffc3,0xfffffff3,0xffffffcc,0xfffffffc,0xffffffcf,0xffffffff,0xc0c0c0c0,0xc0c0c0e0,0xc0c0c0c2,0xc0c0c0e2,0xc0c0c0c8,0xc0c0c0e8,0xc0c0c0ca,0xc0c0c0ea,0xe0e0e0c0,0xe0e0e0e0,0xe0e0e0c2,0xe0e0e0e2,0xe0e0e0c8,0xe0e0e0e8,0xe0e0e0ca,0xe0e0e0ea,0xc2c2c2c0,0xc2c2c2e0,0xc2c2c2c2,0xc2c2c2e2,0xc2c2c2c8,0xc2c2c2e8,0xc2c2c2ca,0xc2c2c2ea,0xe2e2e2c0,0xe2e2e2e0,0xe2e2e2c2,0xe2e2e2e2,0xe2e2e2c8,0xe2e2e2e8,0xe2e2e2ca,0xe2e2e2ea,0xc8c8c8c0,0xc8c8c8e0,0xc8c8c8c2,0xc8c8c8e2,0xc8c8c8c8,0xc8c8c8e8,0xc8c8c8ca,0xc8c8c8ea,0xe8e8e8c0,0xe8e8e8e0,0xe8e8e8c2,0xe8e8e8e2,0xe8e8e8c8,0xe8e8e8e8,0xe8e8e8ca,0xe8e8e8ea,0xcacacac0,0xcacacae0,0xcacacac2,0xcacacae2,0xcacacac8,0xcacacae8,0xcacacaca,0xcacacaea,0xeaeaeac0,0xeaeaeae0,0xeaeaeac2,0xeaeaeae2,0xeaeaeac8,0xeaeaeae8,0xeaeaeaca,0xeaeaeaea,0xc0c0c0c0,0xc0c0c0f0,0xc0c0c0c3,0xc0c0c0f3,0xc0c0c0cc,0xc0c0c0fc,0xc0c0c0cf,0xc0c0c0ff,0xf0f0f0c0,0xf0f0f0f0,0xf0f0f0c3,0xf0f0f0f3,0xf0f0f0cc,0xf0f0f0fc,0xf0f0f0cf,0xf0f0f0ff,0xc3c3c3c0,0xc3c3c3f0,0xc3c3c3c3,0xc3c3c3f3,0xc3c3c3cc,0xc3c3c3fc,0xc3c3c3cf,0xc3c3c3ff,0xf3f3f3c0,0xf3f3f3f0,0xf3f3f3c3,0xf3f3f3f3,0xf3f3f3cc,0xf3f3f3fc,0xf3f3f3cf,0xf3f3f3ff,0xccccccc0,0xccccccf0,0xccccccc3,0xccccccf3,0xcccccccc,0xccccccfc,0xcccccccf,0xccccccff,0xfcfcfcc0,0xfcfcfcf0,0xfcfcfcc3,0xfcfcfcf3,0xfcfcfccc,0xfcfcfcfc,0xfcfcfccf,0xfcfcfcff,0xcfcfcfc0,0xcfcfcff0,0xcfcfcfc3,0xcfcfcff3,0xcfcfcfcc,0xcfcfcffc,0xcfcfcfcf,0xcfcfcfff,0xffffffc0,0xfffffff0,0xffffffc3,0xfffffff3,0xffffffcc,0xfffffffc,0xffffffcf,0xffffffff}, -// {0xc0c0c0c0,0xc0c0e0e0,0xc0c0c2c2,0xc0c0e2e2,0xc0c0c8c8,0xc0c0e8e8,0xc0c0caca,0xc0c0eaea,0xe0e0c0c0,0xe0e0e0e0,0xe0e0c2c2,0xe0e0e2e2,0xe0e0c8c8,0xe0e0e8e8,0xe0e0caca,0xe0e0eaea,0xc2c2c0c0,0xc2c2e0e0,0xc2c2c2c2,0xc2c2e2e2,0xc2c2c8c8,0xc2c2e8e8,0xc2c2caca,0xc2c2eaea,0xe2e2c0c0,0xe2e2e0e0,0xe2e2c2c2,0xe2e2e2e2,0xe2e2c8c8,0xe2e2e8e8,0xe2e2caca,0xe2e2eaea,0xc8c8c0c0,0xc8c8e0e0,0xc8c8c2c2,0xc8c8e2e2,0xc8c8c8c8,0xc8c8e8e8,0xc8c8caca,0xc8c8eaea,0xe8e8c0c0,0xe8e8e0e0,0xe8e8c2c2,0xe8e8e2e2,0xe8e8c8c8,0xe8e8e8e8,0xe8e8caca,0xe8e8eaea,0xcacac0c0,0xcacae0e0,0xcacac2c2,0xcacae2e2,0xcacac8c8,0xcacae8e8,0xcacacaca,0xcacaeaea,0xeaeac0c0,0xeaeae0e0,0xeaeac2c2,0xeaeae2e2,0xeaeac8c8,0xeaeae8e8,0xeaeacaca,0xeaeaeaea,0xc0c0c0c0,0xc0c0f0f0,0xc0c0c3c3,0xc0c0f3f3,0xc0c0cccc,0xc0c0fcfc,0xc0c0cfcf,0xc0c0ffff,0xf0f0c0c0,0xf0f0f0f0,0xf0f0c3c3,0xf0f0f3f3,0xf0f0cccc,0xf0f0fcfc,0xf0f0cfcf,0xf0f0ffff,0xc3c3c0c0,0xc3c3f0f0,0xc3c3c3c3,0xc3c3f3f3,0xc3c3cccc,0xc3c3fcfc,0xc3c3cfcf,0xc3c3ffff,0xf3f3c0c0,0xf3f3f0f0,0xf3f3c3c3,0xf3f3f3f3,0xf3f3cccc,0xf3f3fcfc,0xf3f3cfcf,0xf3f3ffff,0xccccc0c0,0xccccf0f0,0xccccc3c3,0xccccf3f3,0xcccccccc,0xccccfcfc,0xcccccfcf,0xccccffff,0xfcfcc0c0,0xfcfcf0f0,0xfcfcc3c3,0xfcfcf3f3,0xfcfccccc,0xfcfcfcfc,0xfcfccfcf,0xfcfcffff,0xcfcfc0c0,0xcfcff0f0,0xcfcfc3c3,0xcfcff3f3,0xcfcfcccc,0xcfcffcfc,0xcfcfcfcf,0xcfcfffff,0xffffc0c0,0xfffff0f0,0xffffc3c3,0xfffff3f3,0xffffcccc,0xfffffcfc,0xffffcfcf,0xffffffff,0xc0c0c0c0,0xc0c0e0e0,0xc0c0c2c2,0xc0c0e2e2,0xc0c0c8c8,0xc0c0e8e8,0xc0c0caca,0xc0c0eaea,0xe0e0c0c0,0xe0e0e0e0,0xe0e0c2c2,0xe0e0e2e2,0xe0e0c8c8,0xe0e0e8e8,0xe0e0caca,0xe0e0eaea,0xc2c2c0c0,0xc2c2e0e0,0xc2c2c2c2,0xc2c2e2e2,0xc2c2c8c8,0xc2c2e8e8,0xc2c2caca,0xc2c2eaea,0xe2e2c0c0,0xe2e2e0e0,0xe2e2c2c2,0xe2e2e2e2,0xe2e2c8c8,0xe2e2e8e8,0xe2e2caca,0xe2e2eaea,0xc8c8c0c0,0xc8c8e0e0,0xc8c8c2c2,0xc8c8e2e2,0xc8c8c8c8,0xc8c8e8e8,0xc8c8caca,0xc8c8eaea,0xe8e8c0c0,0xe8e8e0e0,0xe8e8c2c2,0xe8e8e2e2,0xe8e8c8c8,0xe8e8e8e8,0xe8e8caca,0xe8e8eaea,0xcacac0c0,0xcacae0e0,0xcacac2c2,0xcacae2e2,0xcacac8c8,0xcacae8e8,0xcacacaca,0xcacaeaea,0xeaeac0c0,0xeaeae0e0,0xeaeac2c2,0xeaeae2e2,0xeaeac8c8,0xeaeae8e8,0xeaeacaca,0xeaeaeaea,0xc0c0c0c0,0xc0c0f0f0,0xc0c0c3c3,0xc0c0f3f3,0xc0c0cccc,0xc0c0fcfc,0xc0c0cfcf,0xc0c0ffff,0xf0f0c0c0,0xf0f0f0f0,0xf0f0c3c3,0xf0f0f3f3,0xf0f0cccc,0xf0f0fcfc,0xf0f0cfcf,0xf0f0ffff,0xc3c3c0c0,0xc3c3f0f0,0xc3c3c3c3,0xc3c3f3f3,0xc3c3cccc,0xc3c3fcfc,0xc3c3cfcf,0xc3c3ffff,0xf3f3c0c0,0xf3f3f0f0,0xf3f3c3c3,0xf3f3f3f3,0xf3f3cccc,0xf3f3fcfc,0xf3f3cfcf,0xf3f3ffff,0xccccc0c0,0xccccf0f0,0xccccc3c3,0xccccf3f3,0xcccccccc,0xccccfcfc,0xcccccfcf,0xccccffff,0xfcfcc0c0,0xfcfcf0f0,0xfcfcc3c3,0xfcfcf3f3,0xfcfccccc,0xfcfcfcfc,0xfcfccfcf,0xfcfcffff,0xcfcfc0c0,0xcfcff0f0,0xcfcfc3c3,0xcfcff3f3,0xcfcfcccc,0xcfcffcfc,0xcfcfcfcf,0xcfcfffff,0xffffc0c0,0xfffff0f0,0xffffc3c3,0xfffff3f3,0xffffcccc,0xfffffcfc,0xffffcfcf,0xffffffff}, -// {0xc0c0c0c0,0xe0c0c0c0,0xc2c0c0c0,0xe2c0c0c0,0xc8c0c0c0,0xe8c0c0c0,0xcac0c0c0,0xeac0c0c0,0xc0e0e0e0,0xe0e0e0e0,0xc2e0e0e0,0xe2e0e0e0,0xc8e0e0e0,0xe8e0e0e0,0xcae0e0e0,0xeae0e0e0,0xc0c2c2c2,0xe0c2c2c2,0xc2c2c2c2,0xe2c2c2c2,0xc8c2c2c2,0xe8c2c2c2,0xcac2c2c2,0xeac2c2c2,0xc0e2e2e2,0xe0e2e2e2,0xc2e2e2e2,0xe2e2e2e2,0xc8e2e2e2,0xe8e2e2e2,0xcae2e2e2,0xeae2e2e2,0xc0c8c8c8,0xe0c8c8c8,0xc2c8c8c8,0xe2c8c8c8,0xc8c8c8c8,0xe8c8c8c8,0xcac8c8c8,0xeac8c8c8,0xc0e8e8e8,0xe0e8e8e8,0xc2e8e8e8,0xe2e8e8e8,0xc8e8e8e8,0xe8e8e8e8,0xcae8e8e8,0xeae8e8e8,0xc0cacaca,0xe0cacaca,0xc2cacaca,0xe2cacaca,0xc8cacaca,0xe8cacaca,0xcacacaca,0xeacacaca,0xc0eaeaea,0xe0eaeaea,0xc2eaeaea,0xe2eaeaea,0xc8eaeaea,0xe8eaeaea,0xcaeaeaea,0xeaeaeaea,0xc0c0c0c0,0xf0c0c0c0,0xc3c0c0c0,0xf3c0c0c0,0xccc0c0c0,0xfcc0c0c0,0xcfc0c0c0,0xffc0c0c0,0xc0f0f0f0,0xf0f0f0f0,0xc3f0f0f0,0xf3f0f0f0,0xccf0f0f0,0xfcf0f0f0,0xcff0f0f0,0xfff0f0f0,0xc0c3c3c3,0xf0c3c3c3,0xc3c3c3c3,0xf3c3c3c3,0xccc3c3c3,0xfcc3c3c3,0xcfc3c3c3,0xffc3c3c3,0xc0f3f3f3,0xf0f3f3f3,0xc3f3f3f3,0xf3f3f3f3,0xccf3f3f3,0xfcf3f3f3,0xcff3f3f3,0xfff3f3f3,0xc0cccccc,0xf0cccccc,0xc3cccccc,0xf3cccccc,0xcccccccc,0xfccccccc,0xcfcccccc,0xffcccccc,0xc0fcfcfc,0xf0fcfcfc,0xc3fcfcfc,0xf3fcfcfc,0xccfcfcfc,0xfcfcfcfc,0xcffcfcfc,0xfffcfcfc,0xc0cfcfcf,0xf0cfcfcf,0xc3cfcfcf,0xf3cfcfcf,0xcccfcfcf,0xfccfcfcf,0xcfcfcfcf,0xffcfcfcf,0xc0ffffff,0xf0ffffff,0xc3ffffff,0xf3ffffff,0xccffffff,0xfcffffff,0xcfffffff,0xffffffff,0xc0c0c0c0,0xe0c0c0c0,0xc2c0c0c0,0xe2c0c0c0,0xc8c0c0c0,0xe8c0c0c0,0xcac0c0c0,0xeac0c0c0,0xc0e0e0e0,0xe0e0e0e0,0xc2e0e0e0,0xe2e0e0e0,0xc8e0e0e0,0xe8e0e0e0,0xcae0e0e0,0xeae0e0e0,0xc0c2c2c2,0xe0c2c2c2,0xc2c2c2c2,0xe2c2c2c2,0xc8c2c2c2,0xe8c2c2c2,0xcac2c2c2,0xeac2c2c2,0xc0e2e2e2,0xe0e2e2e2,0xc2e2e2e2,0xe2e2e2e2,0xc8e2e2e2,0xe8e2e2e2,0xcae2e2e2,0xeae2e2e2,0xc0c8c8c8,0xe0c8c8c8,0xc2c8c8c8,0xe2c8c8c8,0xc8c8c8c8,0xe8c8c8c8,0xcac8c8c8,0xeac8c8c8,0xc0e8e8e8,0xe0e8e8e8,0xc2e8e8e8,0xe2e8e8e8,0xc8e8e8e8,0xe8e8e8e8,0xcae8e8e8,0xeae8e8e8,0xc0cacaca,0xe0cacaca,0xc2cacaca,0xe2cacaca,0xc8cacaca,0xe8cacaca,0xcacacaca,0xeacacaca,0xc0eaeaea,0xe0eaeaea,0xc2eaeaea,0xe2eaeaea,0xc8eaeaea,0xe8eaeaea,0xcaeaeaea,0xeaeaeaea,0xc0c0c0c0,0xf0c0c0c0,0xc3c0c0c0,0xf3c0c0c0,0xccc0c0c0,0xfcc0c0c0,0xcfc0c0c0,0xffc0c0c0,0xc0f0f0f0,0xf0f0f0f0,0xc3f0f0f0,0xf3f0f0f0,0xccf0f0f0,0xfcf0f0f0,0xcff0f0f0,0xfff0f0f0,0xc0c3c3c3,0xf0c3c3c3,0xc3c3c3c3,0xf3c3c3c3,0xccc3c3c3,0xfcc3c3c3,0xcfc3c3c3,0xffc3c3c3,0xc0f3f3f3,0xf0f3f3f3,0xc3f3f3f3,0xf3f3f3f3,0xccf3f3f3,0xfcf3f3f3,0xcff3f3f3,0xfff3f3f3,0xc0cccccc,0xf0cccccc,0xc3cccccc,0xf3cccccc,0xcccccccc,0xfccccccc,0xcfcccccc,0xffcccccc,0xc0fcfcfc,0xf0fcfcfc,0xc3fcfcfc,0xf3fcfcfc,0xccfcfcfc,0xfcfcfcfc,0xcffcfcfc,0xfffcfcfc,0xc0cfcfcf,0xf0cfcfcf,0xc3cfcfcf,0xf3cfcfcf,0xcccfcfcf,0xfccfcfcf,0xcfcfcfcf,0xffcfcfcf,0xc0ffffff,0xf0ffffff,0xc3ffffff,0xf3ffffff,0xccffffff,0xfcffffff,0xcfffffff,0xffffffff}, -// {0xc0c0c0c0,0xe0c0e0c0,0xc2c0c2c0,0xe2c0e2c0,0xc8c0c8c0,0xe8c0e8c0,0xcac0cac0,0xeac0eac0,0xc0e0c0e0,0xe0e0e0e0,0xc2e0c2e0,0xe2e0e2e0,0xc8e0c8e0,0xe8e0e8e0,0xcae0cae0,0xeae0eae0,0xc0c2c0c2,0xe0c2e0c2,0xc2c2c2c2,0xe2c2e2c2,0xc8c2c8c2,0xe8c2e8c2,0xcac2cac2,0xeac2eac2,0xc0e2c0e2,0xe0e2e0e2,0xc2e2c2e2,0xe2e2e2e2,0xc8e2c8e2,0xe8e2e8e2,0xcae2cae2,0xeae2eae2,0xc0c8c0c8,0xe0c8e0c8,0xc2c8c2c8,0xe2c8e2c8,0xc8c8c8c8,0xe8c8e8c8,0xcac8cac8,0xeac8eac8,0xc0e8c0e8,0xe0e8e0e8,0xc2e8c2e8,0xe2e8e2e8,0xc8e8c8e8,0xe8e8e8e8,0xcae8cae8,0xeae8eae8,0xc0cac0ca,0xe0cae0ca,0xc2cac2ca,0xe2cae2ca,0xc8cac8ca,0xe8cae8ca,0xcacacaca,0xeacaeaca,0xc0eac0ea,0xe0eae0ea,0xc2eac2ea,0xe2eae2ea,0xc8eac8ea,0xe8eae8ea,0xcaeacaea,0xeaeaeaea,0xc0c0c0c0,0xf0c0f0c0,0xc3c0c3c0,0xf3c0f3c0,0xccc0ccc0,0xfcc0fcc0,0xcfc0cfc0,0xffc0ffc0,0xc0f0c0f0,0xf0f0f0f0,0xc3f0c3f0,0xf3f0f3f0,0xccf0ccf0,0xfcf0fcf0,0xcff0cff0,0xfff0fff0,0xc0c3c0c3,0xf0c3f0c3,0xc3c3c3c3,0xf3c3f3c3,0xccc3ccc3,0xfcc3fcc3,0xcfc3cfc3,0xffc3ffc3,0xc0f3c0f3,0xf0f3f0f3,0xc3f3c3f3,0xf3f3f3f3,0xccf3ccf3,0xfcf3fcf3,0xcff3cff3,0xfff3fff3,0xc0ccc0cc,0xf0ccf0cc,0xc3ccc3cc,0xf3ccf3cc,0xcccccccc,0xfcccfccc,0xcfcccfcc,0xffccffcc,0xc0fcc0fc,0xf0fcf0fc,0xc3fcc3fc,0xf3fcf3fc,0xccfcccfc,0xfcfcfcfc,0xcffccffc,0xfffcfffc,0xc0cfc0cf,0xf0cff0cf,0xc3cfc3cf,0xf3cff3cf,0xcccfcccf,0xfccffccf,0xcfcfcfcf,0xffcfffcf,0xc0ffc0ff,0xf0fff0ff,0xc3ffc3ff,0xf3fff3ff,0xccffccff,0xfcfffcff,0xcfffcfff,0xffffffff,0xc0c0c0c0,0xe0c0e0c0,0xc2c0c2c0,0xe2c0e2c0,0xc8c0c8c0,0xe8c0e8c0,0xcac0cac0,0xeac0eac0,0xc0e0c0e0,0xe0e0e0e0,0xc2e0c2e0,0xe2e0e2e0,0xc8e0c8e0,0xe8e0e8e0,0xcae0cae0,0xeae0eae0,0xc0c2c0c2,0xe0c2e0c2,0xc2c2c2c2,0xe2c2e2c2,0xc8c2c8c2,0xe8c2e8c2,0xcac2cac2,0xeac2eac2,0xc0e2c0e2,0xe0e2e0e2,0xc2e2c2e2,0xe2e2e2e2,0xc8e2c8e2,0xe8e2e8e2,0xcae2cae2,0xeae2eae2,0xc0c8c0c8,0xe0c8e0c8,0xc2c8c2c8,0xe2c8e2c8,0xc8c8c8c8,0xe8c8e8c8,0xcac8cac8,0xeac8eac8,0xc0e8c0e8,0xe0e8e0e8,0xc2e8c2e8,0xe2e8e2e8,0xc8e8c8e8,0xe8e8e8e8,0xcae8cae8,0xeae8eae8,0xc0cac0ca,0xe0cae0ca,0xc2cac2ca,0xe2cae2ca,0xc8cac8ca,0xe8cae8ca,0xcacacaca,0xeacaeaca,0xc0eac0ea,0xe0eae0ea,0xc2eac2ea,0xe2eae2ea,0xc8eac8ea,0xe8eae8ea,0xcaeacaea,0xeaeaeaea,0xc0c0c0c0,0xf0c0f0c0,0xc3c0c3c0,0xf3c0f3c0,0xccc0ccc0,0xfcc0fcc0,0xcfc0cfc0,0xffc0ffc0,0xc0f0c0f0,0xf0f0f0f0,0xc3f0c3f0,0xf3f0f3f0,0xccf0ccf0,0xfcf0fcf0,0xcff0cff0,0xfff0fff0,0xc0c3c0c3,0xf0c3f0c3,0xc3c3c3c3,0xf3c3f3c3,0xccc3ccc3,0xfcc3fcc3,0xcfc3cfc3,0xffc3ffc3,0xc0f3c0f3,0xf0f3f0f3,0xc3f3c3f3,0xf3f3f3f3,0xccf3ccf3,0xfcf3fcf3,0xcff3cff3,0xfff3fff3,0xc0ccc0cc,0xf0ccf0cc,0xc3ccc3cc,0xf3ccf3cc,0xcccccccc,0xfcccfccc,0xcfcccfcc,0xffccffcc,0xc0fcc0fc,0xf0fcf0fc,0xc3fcc3fc,0xf3fcf3fc,0xccfcccfc,0xfcfcfcfc,0xcffccffc,0xfffcfffc,0xc0cfc0cf,0xf0cff0cf,0xc3cfc3cf,0xf3cff3cf,0xcccfcccf,0xfccffccf,0xcfcfcfcf,0xffcfffcf,0xc0ffc0ff,0xf0fff0ff,0xc3ffc3ff,0xf3fff3ff,0xccffccff,0xfcfffcff,0xcfffcfff,0xffffffff}, -// {0xc0c0c0c0,0xe0c0c0e0,0xc2c0c0c2,0xe2c0c0e2,0xc8c0c0c8,0xe8c0c0e8,0xcac0c0ca,0xeac0c0ea,0xc0e0e0c0,0xe0e0e0e0,0xc2e0e0c2,0xe2e0e0e2,0xc8e0e0c8,0xe8e0e0e8,0xcae0e0ca,0xeae0e0ea,0xc0c2c2c0,0xe0c2c2e0,0xc2c2c2c2,0xe2c2c2e2,0xc8c2c2c8,0xe8c2c2e8,0xcac2c2ca,0xeac2c2ea,0xc0e2e2c0,0xe0e2e2e0,0xc2e2e2c2,0xe2e2e2e2,0xc8e2e2c8,0xe8e2e2e8,0xcae2e2ca,0xeae2e2ea,0xc0c8c8c0,0xe0c8c8e0,0xc2c8c8c2,0xe2c8c8e2,0xc8c8c8c8,0xe8c8c8e8,0xcac8c8ca,0xeac8c8ea,0xc0e8e8c0,0xe0e8e8e0,0xc2e8e8c2,0xe2e8e8e2,0xc8e8e8c8,0xe8e8e8e8,0xcae8e8ca,0xeae8e8ea,0xc0cacac0,0xe0cacae0,0xc2cacac2,0xe2cacae2,0xc8cacac8,0xe8cacae8,0xcacacaca,0xeacacaea,0xc0eaeac0,0xe0eaeae0,0xc2eaeac2,0xe2eaeae2,0xc8eaeac8,0xe8eaeae8,0xcaeaeaca,0xeaeaeaea,0xc0c0c0c0,0xf0c0c0f0,0xc3c0c0c3,0xf3c0c0f3,0xccc0c0cc,0xfcc0c0fc,0xcfc0c0cf,0xffc0c0ff,0xc0f0f0c0,0xf0f0f0f0,0xc3f0f0c3,0xf3f0f0f3,0xccf0f0cc,0xfcf0f0fc,0xcff0f0cf,0xfff0f0ff,0xc0c3c3c0,0xf0c3c3f0,0xc3c3c3c3,0xf3c3c3f3,0xccc3c3cc,0xfcc3c3fc,0xcfc3c3cf,0xffc3c3ff,0xc0f3f3c0,0xf0f3f3f0,0xc3f3f3c3,0xf3f3f3f3,0xccf3f3cc,0xfcf3f3fc,0xcff3f3cf,0xfff3f3ff,0xc0ccccc0,0xf0ccccf0,0xc3ccccc3,0xf3ccccf3,0xcccccccc,0xfcccccfc,0xcfcccccf,0xffccccff,0xc0fcfcc0,0xf0fcfcf0,0xc3fcfcc3,0xf3fcfcf3,0xccfcfccc,0xfcfcfcfc,0xcffcfccf,0xfffcfcff,0xc0cfcfc0,0xf0cfcff0,0xc3cfcfc3,0xf3cfcff3,0xcccfcfcc,0xfccfcffc,0xcfcfcfcf,0xffcfcfff,0xc0ffffc0,0xf0fffff0,0xc3ffffc3,0xf3fffff3,0xccffffcc,0xfcfffffc,0xcfffffcf,0xffffffff,0xc0c0c0c0,0xe0c0c0e0,0xc2c0c0c2,0xe2c0c0e2,0xc8c0c0c8,0xe8c0c0e8,0xcac0c0ca,0xeac0c0ea,0xc0e0e0c0,0xe0e0e0e0,0xc2e0e0c2,0xe2e0e0e2,0xc8e0e0c8,0xe8e0e0e8,0xcae0e0ca,0xeae0e0ea,0xc0c2c2c0,0xe0c2c2e0,0xc2c2c2c2,0xe2c2c2e2,0xc8c2c2c8,0xe8c2c2e8,0xcac2c2ca,0xeac2c2ea,0xc0e2e2c0,0xe0e2e2e0,0xc2e2e2c2,0xe2e2e2e2,0xc8e2e2c8,0xe8e2e2e8,0xcae2e2ca,0xeae2e2ea,0xc0c8c8c0,0xe0c8c8e0,0xc2c8c8c2,0xe2c8c8e2,0xc8c8c8c8,0xe8c8c8e8,0xcac8c8ca,0xeac8c8ea,0xc0e8e8c0,0xe0e8e8e0,0xc2e8e8c2,0xe2e8e8e2,0xc8e8e8c8,0xe8e8e8e8,0xcae8e8ca,0xeae8e8ea,0xc0cacac0,0xe0cacae0,0xc2cacac2,0xe2cacae2,0xc8cacac8,0xe8cacae8,0xcacacaca,0xeacacaea,0xc0eaeac0,0xe0eaeae0,0xc2eaeac2,0xe2eaeae2,0xc8eaeac8,0xe8eaeae8,0xcaeaeaca,0xeaeaeaea,0xc0c0c0c0,0xf0c0c0f0,0xc3c0c0c3,0xf3c0c0f3,0xccc0c0cc,0xfcc0c0fc,0xcfc0c0cf,0xffc0c0ff,0xc0f0f0c0,0xf0f0f0f0,0xc3f0f0c3,0xf3f0f0f3,0xccf0f0cc,0xfcf0f0fc,0xcff0f0cf,0xfff0f0ff,0xc0c3c3c0,0xf0c3c3f0,0xc3c3c3c3,0xf3c3c3f3,0xccc3c3cc,0xfcc3c3fc,0xcfc3c3cf,0xffc3c3ff,0xc0f3f3c0,0xf0f3f3f0,0xc3f3f3c3,0xf3f3f3f3,0xccf3f3cc,0xfcf3f3fc,0xcff3f3cf,0xfff3f3ff,0xc0ccccc0,0xf0ccccf0,0xc3ccccc3,0xf3ccccf3,0xcccccccc,0xfcccccfc,0xcfcccccf,0xffccccff,0xc0fcfcc0,0xf0fcfcf0,0xc3fcfcc3,0xf3fcfcf3,0xccfcfccc,0xfcfcfcfc,0xcffcfccf,0xfffcfcff,0xc0cfcfc0,0xf0cfcff0,0xc3cfcfc3,0xf3cfcff3,0xcccfcfcc,0xfccfcffc,0xcfcfcfcf,0xffcfcfff,0xc0ffffc0,0xf0fffff0,0xc3ffffc3,0xf3fffff3,0xccffffcc,0xfcfffffc,0xcfffffcf,0xffffffff}, -// {0xc0c0c0c0,0xe0c0e0e0,0xc2c0c2c2,0xe2c0e2e2,0xc8c0c8c8,0xe8c0e8e8,0xcac0caca,0xeac0eaea,0xc0e0c0c0,0xe0e0e0e0,0xc2e0c2c2,0xe2e0e2e2,0xc8e0c8c8,0xe8e0e8e8,0xcae0caca,0xeae0eaea,0xc0c2c0c0,0xe0c2e0e0,0xc2c2c2c2,0xe2c2e2e2,0xc8c2c8c8,0xe8c2e8e8,0xcac2caca,0xeac2eaea,0xc0e2c0c0,0xe0e2e0e0,0xc2e2c2c2,0xe2e2e2e2,0xc8e2c8c8,0xe8e2e8e8,0xcae2caca,0xeae2eaea,0xc0c8c0c0,0xe0c8e0e0,0xc2c8c2c2,0xe2c8e2e2,0xc8c8c8c8,0xe8c8e8e8,0xcac8caca,0xeac8eaea,0xc0e8c0c0,0xe0e8e0e0,0xc2e8c2c2,0xe2e8e2e2,0xc8e8c8c8,0xe8e8e8e8,0xcae8caca,0xeae8eaea,0xc0cac0c0,0xe0cae0e0,0xc2cac2c2,0xe2cae2e2,0xc8cac8c8,0xe8cae8e8,0xcacacaca,0xeacaeaea,0xc0eac0c0,0xe0eae0e0,0xc2eac2c2,0xe2eae2e2,0xc8eac8c8,0xe8eae8e8,0xcaeacaca,0xeaeaeaea,0xc0c0c0c0,0xf0c0f0f0,0xc3c0c3c3,0xf3c0f3f3,0xccc0cccc,0xfcc0fcfc,0xcfc0cfcf,0xffc0ffff,0xc0f0c0c0,0xf0f0f0f0,0xc3f0c3c3,0xf3f0f3f3,0xccf0cccc,0xfcf0fcfc,0xcff0cfcf,0xfff0ffff,0xc0c3c0c0,0xf0c3f0f0,0xc3c3c3c3,0xf3c3f3f3,0xccc3cccc,0xfcc3fcfc,0xcfc3cfcf,0xffc3ffff,0xc0f3c0c0,0xf0f3f0f0,0xc3f3c3c3,0xf3f3f3f3,0xccf3cccc,0xfcf3fcfc,0xcff3cfcf,0xfff3ffff,0xc0ccc0c0,0xf0ccf0f0,0xc3ccc3c3,0xf3ccf3f3,0xcccccccc,0xfcccfcfc,0xcfcccfcf,0xffccffff,0xc0fcc0c0,0xf0fcf0f0,0xc3fcc3c3,0xf3fcf3f3,0xccfccccc,0xfcfcfcfc,0xcffccfcf,0xfffcffff,0xc0cfc0c0,0xf0cff0f0,0xc3cfc3c3,0xf3cff3f3,0xcccfcccc,0xfccffcfc,0xcfcfcfcf,0xffcfffff,0xc0ffc0c0,0xf0fff0f0,0xc3ffc3c3,0xf3fff3f3,0xccffcccc,0xfcfffcfc,0xcfffcfcf,0xffffffff,0xc0c0c0c0,0xe0c0e0e0,0xc2c0c2c2,0xe2c0e2e2,0xc8c0c8c8,0xe8c0e8e8,0xcac0caca,0xeac0eaea,0xc0e0c0c0,0xe0e0e0e0,0xc2e0c2c2,0xe2e0e2e2,0xc8e0c8c8,0xe8e0e8e8,0xcae0caca,0xeae0eaea,0xc0c2c0c0,0xe0c2e0e0,0xc2c2c2c2,0xe2c2e2e2,0xc8c2c8c8,0xe8c2e8e8,0xcac2caca,0xeac2eaea,0xc0e2c0c0,0xe0e2e0e0,0xc2e2c2c2,0xe2e2e2e2,0xc8e2c8c8,0xe8e2e8e8,0xcae2caca,0xeae2eaea,0xc0c8c0c0,0xe0c8e0e0,0xc2c8c2c2,0xe2c8e2e2,0xc8c8c8c8,0xe8c8e8e8,0xcac8caca,0xeac8eaea,0xc0e8c0c0,0xe0e8e0e0,0xc2e8c2c2,0xe2e8e2e2,0xc8e8c8c8,0xe8e8e8e8,0xcae8caca,0xeae8eaea,0xc0cac0c0,0xe0cae0e0,0xc2cac2c2,0xe2cae2e2,0xc8cac8c8,0xe8cae8e8,0xcacacaca,0xeacaeaea,0xc0eac0c0,0xe0eae0e0,0xc2eac2c2,0xe2eae2e2,0xc8eac8c8,0xe8eae8e8,0xcaeacaca,0xeaeaeaea,0xc0c0c0c0,0xf0c0f0f0,0xc3c0c3c3,0xf3c0f3f3,0xccc0cccc,0xfcc0fcfc,0xcfc0cfcf,0xffc0ffff,0xc0f0c0c0,0xf0f0f0f0,0xc3f0c3c3,0xf3f0f3f3,0xccf0cccc,0xfcf0fcfc,0xcff0cfcf,0xfff0ffff,0xc0c3c0c0,0xf0c3f0f0,0xc3c3c3c3,0xf3c3f3f3,0xccc3cccc,0xfcc3fcfc,0xcfc3cfcf,0xffc3ffff,0xc0f3c0c0,0xf0f3f0f0,0xc3f3c3c3,0xf3f3f3f3,0xccf3cccc,0xfcf3fcfc,0xcff3cfcf,0xfff3ffff,0xc0ccc0c0,0xf0ccf0f0,0xc3ccc3c3,0xf3ccf3f3,0xcccccccc,0xfcccfcfc,0xcfcccfcf,0xffccffff,0xc0fcc0c0,0xf0fcf0f0,0xc3fcc3c3,0xf3fcf3f3,0xccfccccc,0xfcfcfcfc,0xcffccfcf,0xfffcffff,0xc0cfc0c0,0xf0cff0f0,0xc3cfc3c3,0xf3cff3f3,0xcccfcccc,0xfccffcfc,0xcfcfcfcf,0xffcfffff,0xc0ffc0c0,0xf0fff0f0,0xc3ffc3c3,0xf3fff3f3,0xccffcccc,0xfcfffcfc,0xcfffcfcf,0xffffffff}, -// {0xc0c0c0c0,0xc0e0c0c0,0xc0c2c0c0,0xc0e2c0c0,0xc0c8c0c0,0xc0e8c0c0,0xc0cac0c0,0xc0eac0c0,0xe0c0e0e0,0xe0e0e0e0,0xe0c2e0e0,0xe0e2e0e0,0xe0c8e0e0,0xe0e8e0e0,0xe0cae0e0,0xe0eae0e0,0xc2c0c2c2,0xc2e0c2c2,0xc2c2c2c2,0xc2e2c2c2,0xc2c8c2c2,0xc2e8c2c2,0xc2cac2c2,0xc2eac2c2,0xe2c0e2e2,0xe2e0e2e2,0xe2c2e2e2,0xe2e2e2e2,0xe2c8e2e2,0xe2e8e2e2,0xe2cae2e2,0xe2eae2e2,0xc8c0c8c8,0xc8e0c8c8,0xc8c2c8c8,0xc8e2c8c8,0xc8c8c8c8,0xc8e8c8c8,0xc8cac8c8,0xc8eac8c8,0xe8c0e8e8,0xe8e0e8e8,0xe8c2e8e8,0xe8e2e8e8,0xe8c8e8e8,0xe8e8e8e8,0xe8cae8e8,0xe8eae8e8,0xcac0caca,0xcae0caca,0xcac2caca,0xcae2caca,0xcac8caca,0xcae8caca,0xcacacaca,0xcaeacaca,0xeac0eaea,0xeae0eaea,0xeac2eaea,0xeae2eaea,0xeac8eaea,0xeae8eaea,0xeacaeaea,0xeaeaeaea,0xc0c0c0c0,0xc0f0c0c0,0xc0c3c0c0,0xc0f3c0c0,0xc0ccc0c0,0xc0fcc0c0,0xc0cfc0c0,0xc0ffc0c0,0xf0c0f0f0,0xf0f0f0f0,0xf0c3f0f0,0xf0f3f0f0,0xf0ccf0f0,0xf0fcf0f0,0xf0cff0f0,0xf0fff0f0,0xc3c0c3c3,0xc3f0c3c3,0xc3c3c3c3,0xc3f3c3c3,0xc3ccc3c3,0xc3fcc3c3,0xc3cfc3c3,0xc3ffc3c3,0xf3c0f3f3,0xf3f0f3f3,0xf3c3f3f3,0xf3f3f3f3,0xf3ccf3f3,0xf3fcf3f3,0xf3cff3f3,0xf3fff3f3,0xccc0cccc,0xccf0cccc,0xccc3cccc,0xccf3cccc,0xcccccccc,0xccfccccc,0xcccfcccc,0xccffcccc,0xfcc0fcfc,0xfcf0fcfc,0xfcc3fcfc,0xfcf3fcfc,0xfcccfcfc,0xfcfcfcfc,0xfccffcfc,0xfcfffcfc,0xcfc0cfcf,0xcff0cfcf,0xcfc3cfcf,0xcff3cfcf,0xcfcccfcf,0xcffccfcf,0xcfcfcfcf,0xcfffcfcf,0xffc0ffff,0xfff0ffff,0xffc3ffff,0xfff3ffff,0xffccffff,0xfffcffff,0xffcfffff,0xffffffff,0xc0c0c0c0,0xc0e0c0c0,0xc0c2c0c0,0xc0e2c0c0,0xc0c8c0c0,0xc0e8c0c0,0xc0cac0c0,0xc0eac0c0,0xe0c0e0e0,0xe0e0e0e0,0xe0c2e0e0,0xe0e2e0e0,0xe0c8e0e0,0xe0e8e0e0,0xe0cae0e0,0xe0eae0e0,0xc2c0c2c2,0xc2e0c2c2,0xc2c2c2c2,0xc2e2c2c2,0xc2c8c2c2,0xc2e8c2c2,0xc2cac2c2,0xc2eac2c2,0xe2c0e2e2,0xe2e0e2e2,0xe2c2e2e2,0xe2e2e2e2,0xe2c8e2e2,0xe2e8e2e2,0xe2cae2e2,0xe2eae2e2,0xc8c0c8c8,0xc8e0c8c8,0xc8c2c8c8,0xc8e2c8c8,0xc8c8c8c8,0xc8e8c8c8,0xc8cac8c8,0xc8eac8c8,0xe8c0e8e8,0xe8e0e8e8,0xe8c2e8e8,0xe8e2e8e8,0xe8c8e8e8,0xe8e8e8e8,0xe8cae8e8,0xe8eae8e8,0xcac0caca,0xcae0caca,0xcac2caca,0xcae2caca,0xcac8caca,0xcae8caca,0xcacacaca,0xcaeacaca,0xeac0eaea,0xeae0eaea,0xeac2eaea,0xeae2eaea,0xeac8eaea,0xeae8eaea,0xeacaeaea,0xeaeaeaea,0xc0c0c0c0,0xc0f0c0c0,0xc0c3c0c0,0xc0f3c0c0,0xc0ccc0c0,0xc0fcc0c0,0xc0cfc0c0,0xc0ffc0c0,0xf0c0f0f0,0xf0f0f0f0,0xf0c3f0f0,0xf0f3f0f0,0xf0ccf0f0,0xf0fcf0f0,0xf0cff0f0,0xf0fff0f0,0xc3c0c3c3,0xc3f0c3c3,0xc3c3c3c3,0xc3f3c3c3,0xc3ccc3c3,0xc3fcc3c3,0xc3cfc3c3,0xc3ffc3c3,0xf3c0f3f3,0xf3f0f3f3,0xf3c3f3f3,0xf3f3f3f3,0xf3ccf3f3,0xf3fcf3f3,0xf3cff3f3,0xf3fff3f3,0xccc0cccc,0xccf0cccc,0xccc3cccc,0xccf3cccc,0xcccccccc,0xccfccccc,0xcccfcccc,0xccffcccc,0xfcc0fcfc,0xfcf0fcfc,0xfcc3fcfc,0xfcf3fcfc,0xfcccfcfc,0xfcfcfcfc,0xfccffcfc,0xfcfffcfc,0xcfc0cfcf,0xcff0cfcf,0xcfc3cfcf,0xcff3cfcf,0xcfcccfcf,0xcffccfcf,0xcfcfcfcf,0xcfffcfcf,0xffc0ffff,0xfff0ffff,0xffc3ffff,0xfff3ffff,0xffccffff,0xfffcffff,0xffcfffff,0xffffffff}, -// {0xc0c0c0c0,0xc0e0e0c0,0xc0c2c2c0,0xc0e2e2c0,0xc0c8c8c0,0xc0e8e8c0,0xc0cacac0,0xc0eaeac0,0xe0c0c0e0,0xe0e0e0e0,0xe0c2c2e0,0xe0e2e2e0,0xe0c8c8e0,0xe0e8e8e0,0xe0cacae0,0xe0eaeae0,0xc2c0c0c2,0xc2e0e0c2,0xc2c2c2c2,0xc2e2e2c2,0xc2c8c8c2,0xc2e8e8c2,0xc2cacac2,0xc2eaeac2,0xe2c0c0e2,0xe2e0e0e2,0xe2c2c2e2,0xe2e2e2e2,0xe2c8c8e2,0xe2e8e8e2,0xe2cacae2,0xe2eaeae2,0xc8c0c0c8,0xc8e0e0c8,0xc8c2c2c8,0xc8e2e2c8,0xc8c8c8c8,0xc8e8e8c8,0xc8cacac8,0xc8eaeac8,0xe8c0c0e8,0xe8e0e0e8,0xe8c2c2e8,0xe8e2e2e8,0xe8c8c8e8,0xe8e8e8e8,0xe8cacae8,0xe8eaeae8,0xcac0c0ca,0xcae0e0ca,0xcac2c2ca,0xcae2e2ca,0xcac8c8ca,0xcae8e8ca,0xcacacaca,0xcaeaeaca,0xeac0c0ea,0xeae0e0ea,0xeac2c2ea,0xeae2e2ea,0xeac8c8ea,0xeae8e8ea,0xeacacaea,0xeaeaeaea,0xc0c0c0c0,0xc0f0f0c0,0xc0c3c3c0,0xc0f3f3c0,0xc0ccccc0,0xc0fcfcc0,0xc0cfcfc0,0xc0ffffc0,0xf0c0c0f0,0xf0f0f0f0,0xf0c3c3f0,0xf0f3f3f0,0xf0ccccf0,0xf0fcfcf0,0xf0cfcff0,0xf0fffff0,0xc3c0c0c3,0xc3f0f0c3,0xc3c3c3c3,0xc3f3f3c3,0xc3ccccc3,0xc3fcfcc3,0xc3cfcfc3,0xc3ffffc3,0xf3c0c0f3,0xf3f0f0f3,0xf3c3c3f3,0xf3f3f3f3,0xf3ccccf3,0xf3fcfcf3,0xf3cfcff3,0xf3fffff3,0xccc0c0cc,0xccf0f0cc,0xccc3c3cc,0xccf3f3cc,0xcccccccc,0xccfcfccc,0xcccfcfcc,0xccffffcc,0xfcc0c0fc,0xfcf0f0fc,0xfcc3c3fc,0xfcf3f3fc,0xfcccccfc,0xfcfcfcfc,0xfccfcffc,0xfcfffffc,0xcfc0c0cf,0xcff0f0cf,0xcfc3c3cf,0xcff3f3cf,0xcfcccccf,0xcffcfccf,0xcfcfcfcf,0xcfffffcf,0xffc0c0ff,0xfff0f0ff,0xffc3c3ff,0xfff3f3ff,0xffccccff,0xfffcfcff,0xffcfcfff,0xffffffff,0xc0c0c0c0,0xc0e0e0c0,0xc0c2c2c0,0xc0e2e2c0,0xc0c8c8c0,0xc0e8e8c0,0xc0cacac0,0xc0eaeac0,0xe0c0c0e0,0xe0e0e0e0,0xe0c2c2e0,0xe0e2e2e0,0xe0c8c8e0,0xe0e8e8e0,0xe0cacae0,0xe0eaeae0,0xc2c0c0c2,0xc2e0e0c2,0xc2c2c2c2,0xc2e2e2c2,0xc2c8c8c2,0xc2e8e8c2,0xc2cacac2,0xc2eaeac2,0xe2c0c0e2,0xe2e0e0e2,0xe2c2c2e2,0xe2e2e2e2,0xe2c8c8e2,0xe2e8e8e2,0xe2cacae2,0xe2eaeae2,0xc8c0c0c8,0xc8e0e0c8,0xc8c2c2c8,0xc8e2e2c8,0xc8c8c8c8,0xc8e8e8c8,0xc8cacac8,0xc8eaeac8,0xe8c0c0e8,0xe8e0e0e8,0xe8c2c2e8,0xe8e2e2e8,0xe8c8c8e8,0xe8e8e8e8,0xe8cacae8,0xe8eaeae8,0xcac0c0ca,0xcae0e0ca,0xcac2c2ca,0xcae2e2ca,0xcac8c8ca,0xcae8e8ca,0xcacacaca,0xcaeaeaca,0xeac0c0ea,0xeae0e0ea,0xeac2c2ea,0xeae2e2ea,0xeac8c8ea,0xeae8e8ea,0xeacacaea,0xeaeaeaea,0xc0c0c0c0,0xc0f0f0c0,0xc0c3c3c0,0xc0f3f3c0,0xc0ccccc0,0xc0fcfcc0,0xc0cfcfc0,0xc0ffffc0,0xf0c0c0f0,0xf0f0f0f0,0xf0c3c3f0,0xf0f3f3f0,0xf0ccccf0,0xf0fcfcf0,0xf0cfcff0,0xf0fffff0,0xc3c0c0c3,0xc3f0f0c3,0xc3c3c3c3,0xc3f3f3c3,0xc3ccccc3,0xc3fcfcc3,0xc3cfcfc3,0xc3ffffc3,0xf3c0c0f3,0xf3f0f0f3,0xf3c3c3f3,0xf3f3f3f3,0xf3ccccf3,0xf3fcfcf3,0xf3cfcff3,0xf3fffff3,0xccc0c0cc,0xccf0f0cc,0xccc3c3cc,0xccf3f3cc,0xcccccccc,0xccfcfccc,0xcccfcfcc,0xccffffcc,0xfcc0c0fc,0xfcf0f0fc,0xfcc3c3fc,0xfcf3f3fc,0xfcccccfc,0xfcfcfcfc,0xfccfcffc,0xfcfffffc,0xcfc0c0cf,0xcff0f0cf,0xcfc3c3cf,0xcff3f3cf,0xcfcccccf,0xcffcfccf,0xcfcfcfcf,0xcfffffcf,0xffc0c0ff,0xfff0f0ff,0xffc3c3ff,0xfff3f3ff,0xffccccff,0xfffcfcff,0xffcfcfff,0xffffffff}, -// {0xc0c0c0c0,0xc0e0c0e0,0xc0c2c0c2,0xc0e2c0e2,0xc0c8c0c8,0xc0e8c0e8,0xc0cac0ca,0xc0eac0ea,0xe0c0e0c0,0xe0e0e0e0,0xe0c2e0c2,0xe0e2e0e2,0xe0c8e0c8,0xe0e8e0e8,0xe0cae0ca,0xe0eae0ea,0xc2c0c2c0,0xc2e0c2e0,0xc2c2c2c2,0xc2e2c2e2,0xc2c8c2c8,0xc2e8c2e8,0xc2cac2ca,0xc2eac2ea,0xe2c0e2c0,0xe2e0e2e0,0xe2c2e2c2,0xe2e2e2e2,0xe2c8e2c8,0xe2e8e2e8,0xe2cae2ca,0xe2eae2ea,0xc8c0c8c0,0xc8e0c8e0,0xc8c2c8c2,0xc8e2c8e2,0xc8c8c8c8,0xc8e8c8e8,0xc8cac8ca,0xc8eac8ea,0xe8c0e8c0,0xe8e0e8e0,0xe8c2e8c2,0xe8e2e8e2,0xe8c8e8c8,0xe8e8e8e8,0xe8cae8ca,0xe8eae8ea,0xcac0cac0,0xcae0cae0,0xcac2cac2,0xcae2cae2,0xcac8cac8,0xcae8cae8,0xcacacaca,0xcaeacaea,0xeac0eac0,0xeae0eae0,0xeac2eac2,0xeae2eae2,0xeac8eac8,0xeae8eae8,0xeacaeaca,0xeaeaeaea,0xc0c0c0c0,0xc0f0c0f0,0xc0c3c0c3,0xc0f3c0f3,0xc0ccc0cc,0xc0fcc0fc,0xc0cfc0cf,0xc0ffc0ff,0xf0c0f0c0,0xf0f0f0f0,0xf0c3f0c3,0xf0f3f0f3,0xf0ccf0cc,0xf0fcf0fc,0xf0cff0cf,0xf0fff0ff,0xc3c0c3c0,0xc3f0c3f0,0xc3c3c3c3,0xc3f3c3f3,0xc3ccc3cc,0xc3fcc3fc,0xc3cfc3cf,0xc3ffc3ff,0xf3c0f3c0,0xf3f0f3f0,0xf3c3f3c3,0xf3f3f3f3,0xf3ccf3cc,0xf3fcf3fc,0xf3cff3cf,0xf3fff3ff,0xccc0ccc0,0xccf0ccf0,0xccc3ccc3,0xccf3ccf3,0xcccccccc,0xccfcccfc,0xcccfcccf,0xccffccff,0xfcc0fcc0,0xfcf0fcf0,0xfcc3fcc3,0xfcf3fcf3,0xfcccfccc,0xfcfcfcfc,0xfccffccf,0xfcfffcff,0xcfc0cfc0,0xcff0cff0,0xcfc3cfc3,0xcff3cff3,0xcfcccfcc,0xcffccffc,0xcfcfcfcf,0xcfffcfff,0xffc0ffc0,0xfff0fff0,0xffc3ffc3,0xfff3fff3,0xffccffcc,0xfffcfffc,0xffcfffcf,0xffffffff,0xc0c0c0c0,0xc0e0c0e0,0xc0c2c0c2,0xc0e2c0e2,0xc0c8c0c8,0xc0e8c0e8,0xc0cac0ca,0xc0eac0ea,0xe0c0e0c0,0xe0e0e0e0,0xe0c2e0c2,0xe0e2e0e2,0xe0c8e0c8,0xe0e8e0e8,0xe0cae0ca,0xe0eae0ea,0xc2c0c2c0,0xc2e0c2e0,0xc2c2c2c2,0xc2e2c2e2,0xc2c8c2c8,0xc2e8c2e8,0xc2cac2ca,0xc2eac2ea,0xe2c0e2c0,0xe2e0e2e0,0xe2c2e2c2,0xe2e2e2e2,0xe2c8e2c8,0xe2e8e2e8,0xe2cae2ca,0xe2eae2ea,0xc8c0c8c0,0xc8e0c8e0,0xc8c2c8c2,0xc8e2c8e2,0xc8c8c8c8,0xc8e8c8e8,0xc8cac8ca,0xc8eac8ea,0xe8c0e8c0,0xe8e0e8e0,0xe8c2e8c2,0xe8e2e8e2,0xe8c8e8c8,0xe8e8e8e8,0xe8cae8ca,0xe8eae8ea,0xcac0cac0,0xcae0cae0,0xcac2cac2,0xcae2cae2,0xcac8cac8,0xcae8cae8,0xcacacaca,0xcaeacaea,0xeac0eac0,0xeae0eae0,0xeac2eac2,0xeae2eae2,0xeac8eac8,0xeae8eae8,0xeacaeaca,0xeaeaeaea,0xc0c0c0c0,0xc0f0c0f0,0xc0c3c0c3,0xc0f3c0f3,0xc0ccc0cc,0xc0fcc0fc,0xc0cfc0cf,0xc0ffc0ff,0xf0c0f0c0,0xf0f0f0f0,0xf0c3f0c3,0xf0f3f0f3,0xf0ccf0cc,0xf0fcf0fc,0xf0cff0cf,0xf0fff0ff,0xc3c0c3c0,0xc3f0c3f0,0xc3c3c3c3,0xc3f3c3f3,0xc3ccc3cc,0xc3fcc3fc,0xc3cfc3cf,0xc3ffc3ff,0xf3c0f3c0,0xf3f0f3f0,0xf3c3f3c3,0xf3f3f3f3,0xf3ccf3cc,0xf3fcf3fc,0xf3cff3cf,0xf3fff3ff,0xccc0ccc0,0xccf0ccf0,0xccc3ccc3,0xccf3ccf3,0xcccccccc,0xccfcccfc,0xcccfcccf,0xccffccff,0xfcc0fcc0,0xfcf0fcf0,0xfcc3fcc3,0xfcf3fcf3,0xfcccfccc,0xfcfcfcfc,0xfccffccf,0xfcfffcff,0xcfc0cfc0,0xcff0cff0,0xcfc3cfc3,0xcff3cff3,0xcfcccfcc,0xcffccffc,0xcfcfcfcf,0xcfffcfff,0xffc0ffc0,0xfff0fff0,0xffc3ffc3,0xfff3fff3,0xffccffcc,0xfffcfffc,0xffcfffcf,0xffffffff}, -// {0xc0c0c0c0,0xc0e0e0e0,0xc0c2c2c2,0xc0e2e2e2,0xc0c8c8c8,0xc0e8e8e8,0xc0cacaca,0xc0eaeaea,0xe0c0c0c0,0xe0e0e0e0,0xe0c2c2c2,0xe0e2e2e2,0xe0c8c8c8,0xe0e8e8e8,0xe0cacaca,0xe0eaeaea,0xc2c0c0c0,0xc2e0e0e0,0xc2c2c2c2,0xc2e2e2e2,0xc2c8c8c8,0xc2e8e8e8,0xc2cacaca,0xc2eaeaea,0xe2c0c0c0,0xe2e0e0e0,0xe2c2c2c2,0xe2e2e2e2,0xe2c8c8c8,0xe2e8e8e8,0xe2cacaca,0xe2eaeaea,0xc8c0c0c0,0xc8e0e0e0,0xc8c2c2c2,0xc8e2e2e2,0xc8c8c8c8,0xc8e8e8e8,0xc8cacaca,0xc8eaeaea,0xe8c0c0c0,0xe8e0e0e0,0xe8c2c2c2,0xe8e2e2e2,0xe8c8c8c8,0xe8e8e8e8,0xe8cacaca,0xe8eaeaea,0xcac0c0c0,0xcae0e0e0,0xcac2c2c2,0xcae2e2e2,0xcac8c8c8,0xcae8e8e8,0xcacacaca,0xcaeaeaea,0xeac0c0c0,0xeae0e0e0,0xeac2c2c2,0xeae2e2e2,0xeac8c8c8,0xeae8e8e8,0xeacacaca,0xeaeaeaea,0xc0c0c0c0,0xc0f0f0f0,0xc0c3c3c3,0xc0f3f3f3,0xc0cccccc,0xc0fcfcfc,0xc0cfcfcf,0xc0ffffff,0xf0c0c0c0,0xf0f0f0f0,0xf0c3c3c3,0xf0f3f3f3,0xf0cccccc,0xf0fcfcfc,0xf0cfcfcf,0xf0ffffff,0xc3c0c0c0,0xc3f0f0f0,0xc3c3c3c3,0xc3f3f3f3,0xc3cccccc,0xc3fcfcfc,0xc3cfcfcf,0xc3ffffff,0xf3c0c0c0,0xf3f0f0f0,0xf3c3c3c3,0xf3f3f3f3,0xf3cccccc,0xf3fcfcfc,0xf3cfcfcf,0xf3ffffff,0xccc0c0c0,0xccf0f0f0,0xccc3c3c3,0xccf3f3f3,0xcccccccc,0xccfcfcfc,0xcccfcfcf,0xccffffff,0xfcc0c0c0,0xfcf0f0f0,0xfcc3c3c3,0xfcf3f3f3,0xfccccccc,0xfcfcfcfc,0xfccfcfcf,0xfcffffff,0xcfc0c0c0,0xcff0f0f0,0xcfc3c3c3,0xcff3f3f3,0xcfcccccc,0xcffcfcfc,0xcfcfcfcf,0xcfffffff,0xffc0c0c0,0xfff0f0f0,0xffc3c3c3,0xfff3f3f3,0xffcccccc,0xfffcfcfc,0xffcfcfcf,0xffffffff,0xc0c0c0c0,0xc0e0e0e0,0xc0c2c2c2,0xc0e2e2e2,0xc0c8c8c8,0xc0e8e8e8,0xc0cacaca,0xc0eaeaea,0xe0c0c0c0,0xe0e0e0e0,0xe0c2c2c2,0xe0e2e2e2,0xe0c8c8c8,0xe0e8e8e8,0xe0cacaca,0xe0eaeaea,0xc2c0c0c0,0xc2e0e0e0,0xc2c2c2c2,0xc2e2e2e2,0xc2c8c8c8,0xc2e8e8e8,0xc2cacaca,0xc2eaeaea,0xe2c0c0c0,0xe2e0e0e0,0xe2c2c2c2,0xe2e2e2e2,0xe2c8c8c8,0xe2e8e8e8,0xe2cacaca,0xe2eaeaea,0xc8c0c0c0,0xc8e0e0e0,0xc8c2c2c2,0xc8e2e2e2,0xc8c8c8c8,0xc8e8e8e8,0xc8cacaca,0xc8eaeaea,0xe8c0c0c0,0xe8e0e0e0,0xe8c2c2c2,0xe8e2e2e2,0xe8c8c8c8,0xe8e8e8e8,0xe8cacaca,0xe8eaeaea,0xcac0c0c0,0xcae0e0e0,0xcac2c2c2,0xcae2e2e2,0xcac8c8c8,0xcae8e8e8,0xcacacaca,0xcaeaeaea,0xeac0c0c0,0xeae0e0e0,0xeac2c2c2,0xeae2e2e2,0xeac8c8c8,0xeae8e8e8,0xeacacaca,0xeaeaeaea,0xc0c0c0c0,0xc0f0f0f0,0xc0c3c3c3,0xc0f3f3f3,0xc0cccccc,0xc0fcfcfc,0xc0cfcfcf,0xc0ffffff,0xf0c0c0c0,0xf0f0f0f0,0xf0c3c3c3,0xf0f3f3f3,0xf0cccccc,0xf0fcfcfc,0xf0cfcfcf,0xf0ffffff,0xc3c0c0c0,0xc3f0f0f0,0xc3c3c3c3,0xc3f3f3f3,0xc3cccccc,0xc3fcfcfc,0xc3cfcfcf,0xc3ffffff,0xf3c0c0c0,0xf3f0f0f0,0xf3c3c3c3,0xf3f3f3f3,0xf3cccccc,0xf3fcfcfc,0xf3cfcfcf,0xf3ffffff,0xccc0c0c0,0xccf0f0f0,0xccc3c3c3,0xccf3f3f3,0xcccccccc,0xccfcfcfc,0xcccfcfcf,0xccffffff,0xfcc0c0c0,0xfcf0f0f0,0xfcc3c3c3,0xfcf3f3f3,0xfccccccc,0xfcfcfcfc,0xfccfcfcf,0xfcffffff,0xcfc0c0c0,0xcff0f0f0,0xcfc3c3c3,0xcff3f3f3,0xcfcccccc,0xcffcfcfc,0xcfcfcfcf,0xcfffffff,0xffc0c0c0,0xfff0f0f0,0xffc3c3c3,0xfff3f3f3,0xffcccccc,0xfffcfcfc,0xffcfcfcf,0xffffffff}, -// {0xc0c0c0c0,0xe0e0c0c0,0xc2c2c0c0,0xe2e2c0c0,0xc8c8c0c0,0xe8e8c0c0,0xcacac0c0,0xeaeac0c0,0xc0c0e0e0,0xe0e0e0e0,0xc2c2e0e0,0xe2e2e0e0,0xc8c8e0e0,0xe8e8e0e0,0xcacae0e0,0xeaeae0e0,0xc0c0c2c2,0xe0e0c2c2,0xc2c2c2c2,0xe2e2c2c2,0xc8c8c2c2,0xe8e8c2c2,0xcacac2c2,0xeaeac2c2,0xc0c0e2e2,0xe0e0e2e2,0xc2c2e2e2,0xe2e2e2e2,0xc8c8e2e2,0xe8e8e2e2,0xcacae2e2,0xeaeae2e2,0xc0c0c8c8,0xe0e0c8c8,0xc2c2c8c8,0xe2e2c8c8,0xc8c8c8c8,0xe8e8c8c8,0xcacac8c8,0xeaeac8c8,0xc0c0e8e8,0xe0e0e8e8,0xc2c2e8e8,0xe2e2e8e8,0xc8c8e8e8,0xe8e8e8e8,0xcacae8e8,0xeaeae8e8,0xc0c0caca,0xe0e0caca,0xc2c2caca,0xe2e2caca,0xc8c8caca,0xe8e8caca,0xcacacaca,0xeaeacaca,0xc0c0eaea,0xe0e0eaea,0xc2c2eaea,0xe2e2eaea,0xc8c8eaea,0xe8e8eaea,0xcacaeaea,0xeaeaeaea,0xc0c0c0c0,0xf0f0c0c0,0xc3c3c0c0,0xf3f3c0c0,0xccccc0c0,0xfcfcc0c0,0xcfcfc0c0,0xffffc0c0,0xc0c0f0f0,0xf0f0f0f0,0xc3c3f0f0,0xf3f3f0f0,0xccccf0f0,0xfcfcf0f0,0xcfcff0f0,0xfffff0f0,0xc0c0c3c3,0xf0f0c3c3,0xc3c3c3c3,0xf3f3c3c3,0xccccc3c3,0xfcfcc3c3,0xcfcfc3c3,0xffffc3c3,0xc0c0f3f3,0xf0f0f3f3,0xc3c3f3f3,0xf3f3f3f3,0xccccf3f3,0xfcfcf3f3,0xcfcff3f3,0xfffff3f3,0xc0c0cccc,0xf0f0cccc,0xc3c3cccc,0xf3f3cccc,0xcccccccc,0xfcfccccc,0xcfcfcccc,0xffffcccc,0xc0c0fcfc,0xf0f0fcfc,0xc3c3fcfc,0xf3f3fcfc,0xccccfcfc,0xfcfcfcfc,0xcfcffcfc,0xfffffcfc,0xc0c0cfcf,0xf0f0cfcf,0xc3c3cfcf,0xf3f3cfcf,0xcccccfcf,0xfcfccfcf,0xcfcfcfcf,0xffffcfcf,0xc0c0ffff,0xf0f0ffff,0xc3c3ffff,0xf3f3ffff,0xccccffff,0xfcfcffff,0xcfcfffff,0xffffffff,0xc0c0c0c0,0xe0e0c0c0,0xc2c2c0c0,0xe2e2c0c0,0xc8c8c0c0,0xe8e8c0c0,0xcacac0c0,0xeaeac0c0,0xc0c0e0e0,0xe0e0e0e0,0xc2c2e0e0,0xe2e2e0e0,0xc8c8e0e0,0xe8e8e0e0,0xcacae0e0,0xeaeae0e0,0xc0c0c2c2,0xe0e0c2c2,0xc2c2c2c2,0xe2e2c2c2,0xc8c8c2c2,0xe8e8c2c2,0xcacac2c2,0xeaeac2c2,0xc0c0e2e2,0xe0e0e2e2,0xc2c2e2e2,0xe2e2e2e2,0xc8c8e2e2,0xe8e8e2e2,0xcacae2e2,0xeaeae2e2,0xc0c0c8c8,0xe0e0c8c8,0xc2c2c8c8,0xe2e2c8c8,0xc8c8c8c8,0xe8e8c8c8,0xcacac8c8,0xeaeac8c8,0xc0c0e8e8,0xe0e0e8e8,0xc2c2e8e8,0xe2e2e8e8,0xc8c8e8e8,0xe8e8e8e8,0xcacae8e8,0xeaeae8e8,0xc0c0caca,0xe0e0caca,0xc2c2caca,0xe2e2caca,0xc8c8caca,0xe8e8caca,0xcacacaca,0xeaeacaca,0xc0c0eaea,0xe0e0eaea,0xc2c2eaea,0xe2e2eaea,0xc8c8eaea,0xe8e8eaea,0xcacaeaea,0xeaeaeaea,0xc0c0c0c0,0xf0f0c0c0,0xc3c3c0c0,0xf3f3c0c0,0xccccc0c0,0xfcfcc0c0,0xcfcfc0c0,0xffffc0c0,0xc0c0f0f0,0xf0f0f0f0,0xc3c3f0f0,0xf3f3f0f0,0xccccf0f0,0xfcfcf0f0,0xcfcff0f0,0xfffff0f0,0xc0c0c3c3,0xf0f0c3c3,0xc3c3c3c3,0xf3f3c3c3,0xccccc3c3,0xfcfcc3c3,0xcfcfc3c3,0xffffc3c3,0xc0c0f3f3,0xf0f0f3f3,0xc3c3f3f3,0xf3f3f3f3,0xccccf3f3,0xfcfcf3f3,0xcfcff3f3,0xfffff3f3,0xc0c0cccc,0xf0f0cccc,0xc3c3cccc,0xf3f3cccc,0xcccccccc,0xfcfccccc,0xcfcfcccc,0xffffcccc,0xc0c0fcfc,0xf0f0fcfc,0xc3c3fcfc,0xf3f3fcfc,0xccccfcfc,0xfcfcfcfc,0xcfcffcfc,0xfffffcfc,0xc0c0cfcf,0xf0f0cfcf,0xc3c3cfcf,0xf3f3cfcf,0xcccccfcf,0xfcfccfcf,0xcfcfcfcf,0xffffcfcf,0xc0c0ffff,0xf0f0ffff,0xc3c3ffff,0xf3f3ffff,0xccccffff,0xfcfcffff,0xcfcfffff,0xffffffff}, -// {0xc0c0c0c0,0xe0e0e0c0,0xc2c2c2c0,0xe2e2e2c0,0xc8c8c8c0,0xe8e8e8c0,0xcacacac0,0xeaeaeac0,0xc0c0c0e0,0xe0e0e0e0,0xc2c2c2e0,0xe2e2e2e0,0xc8c8c8e0,0xe8e8e8e0,0xcacacae0,0xeaeaeae0,0xc0c0c0c2,0xe0e0e0c2,0xc2c2c2c2,0xe2e2e2c2,0xc8c8c8c2,0xe8e8e8c2,0xcacacac2,0xeaeaeac2,0xc0c0c0e2,0xe0e0e0e2,0xc2c2c2e2,0xe2e2e2e2,0xc8c8c8e2,0xe8e8e8e2,0xcacacae2,0xeaeaeae2,0xc0c0c0c8,0xe0e0e0c8,0xc2c2c2c8,0xe2e2e2c8,0xc8c8c8c8,0xe8e8e8c8,0xcacacac8,0xeaeaeac8,0xc0c0c0e8,0xe0e0e0e8,0xc2c2c2e8,0xe2e2e2e8,0xc8c8c8e8,0xe8e8e8e8,0xcacacae8,0xeaeaeae8,0xc0c0c0ca,0xe0e0e0ca,0xc2c2c2ca,0xe2e2e2ca,0xc8c8c8ca,0xe8e8e8ca,0xcacacaca,0xeaeaeaca,0xc0c0c0ea,0xe0e0e0ea,0xc2c2c2ea,0xe2e2e2ea,0xc8c8c8ea,0xe8e8e8ea,0xcacacaea,0xeaeaeaea,0xc0c0c0c0,0xf0f0f0c0,0xc3c3c3c0,0xf3f3f3c0,0xccccccc0,0xfcfcfcc0,0xcfcfcfc0,0xffffffc0,0xc0c0c0f0,0xf0f0f0f0,0xc3c3c3f0,0xf3f3f3f0,0xccccccf0,0xfcfcfcf0,0xcfcfcff0,0xfffffff0,0xc0c0c0c3,0xf0f0f0c3,0xc3c3c3c3,0xf3f3f3c3,0xccccccc3,0xfcfcfcc3,0xcfcfcfc3,0xffffffc3,0xc0c0c0f3,0xf0f0f0f3,0xc3c3c3f3,0xf3f3f3f3,0xccccccf3,0xfcfcfcf3,0xcfcfcff3,0xfffffff3,0xc0c0c0cc,0xf0f0f0cc,0xc3c3c3cc,0xf3f3f3cc,0xcccccccc,0xfcfcfccc,0xcfcfcfcc,0xffffffcc,0xc0c0c0fc,0xf0f0f0fc,0xc3c3c3fc,0xf3f3f3fc,0xccccccfc,0xfcfcfcfc,0xcfcfcffc,0xfffffffc,0xc0c0c0cf,0xf0f0f0cf,0xc3c3c3cf,0xf3f3f3cf,0xcccccccf,0xfcfcfccf,0xcfcfcfcf,0xffffffcf,0xc0c0c0ff,0xf0f0f0ff,0xc3c3c3ff,0xf3f3f3ff,0xccccccff,0xfcfcfcff,0xcfcfcfff,0xffffffff,0xc0c0c0c0,0xe0e0e0c0,0xc2c2c2c0,0xe2e2e2c0,0xc8c8c8c0,0xe8e8e8c0,0xcacacac0,0xeaeaeac0,0xc0c0c0e0,0xe0e0e0e0,0xc2c2c2e0,0xe2e2e2e0,0xc8c8c8e0,0xe8e8e8e0,0xcacacae0,0xeaeaeae0,0xc0c0c0c2,0xe0e0e0c2,0xc2c2c2c2,0xe2e2e2c2,0xc8c8c8c2,0xe8e8e8c2,0xcacacac2,0xeaeaeac2,0xc0c0c0e2,0xe0e0e0e2,0xc2c2c2e2,0xe2e2e2e2,0xc8c8c8e2,0xe8e8e8e2,0xcacacae2,0xeaeaeae2,0xc0c0c0c8,0xe0e0e0c8,0xc2c2c2c8,0xe2e2e2c8,0xc8c8c8c8,0xe8e8e8c8,0xcacacac8,0xeaeaeac8,0xc0c0c0e8,0xe0e0e0e8,0xc2c2c2e8,0xe2e2e2e8,0xc8c8c8e8,0xe8e8e8e8,0xcacacae8,0xeaeaeae8,0xc0c0c0ca,0xe0e0e0ca,0xc2c2c2ca,0xe2e2e2ca,0xc8c8c8ca,0xe8e8e8ca,0xcacacaca,0xeaeaeaca,0xc0c0c0ea,0xe0e0e0ea,0xc2c2c2ea,0xe2e2e2ea,0xc8c8c8ea,0xe8e8e8ea,0xcacacaea,0xeaeaeaea,0xc0c0c0c0,0xf0f0f0c0,0xc3c3c3c0,0xf3f3f3c0,0xccccccc0,0xfcfcfcc0,0xcfcfcfc0,0xffffffc0,0xc0c0c0f0,0xf0f0f0f0,0xc3c3c3f0,0xf3f3f3f0,0xccccccf0,0xfcfcfcf0,0xcfcfcff0,0xfffffff0,0xc0c0c0c3,0xf0f0f0c3,0xc3c3c3c3,0xf3f3f3c3,0xccccccc3,0xfcfcfcc3,0xcfcfcfc3,0xffffffc3,0xc0c0c0f3,0xf0f0f0f3,0xc3c3c3f3,0xf3f3f3f3,0xccccccf3,0xfcfcfcf3,0xcfcfcff3,0xfffffff3,0xc0c0c0cc,0xf0f0f0cc,0xc3c3c3cc,0xf3f3f3cc,0xcccccccc,0xfcfcfccc,0xcfcfcfcc,0xffffffcc,0xc0c0c0fc,0xf0f0f0fc,0xc3c3c3fc,0xf3f3f3fc,0xccccccfc,0xfcfcfcfc,0xcfcfcffc,0xfffffffc,0xc0c0c0cf,0xf0f0f0cf,0xc3c3c3cf,0xf3f3f3cf,0xcccccccf,0xfcfcfccf,0xcfcfcfcf,0xffffffcf,0xc0c0c0ff,0xf0f0f0ff,0xc3c3c3ff,0xf3f3f3ff,0xccccccff,0xfcfcfcff,0xcfcfcfff,0xffffffff}, -// {0xc0c0c0c0,0xe0e0c0e0,0xc2c2c0c2,0xe2e2c0e2,0xc8c8c0c8,0xe8e8c0e8,0xcacac0ca,0xeaeac0ea,0xc0c0e0c0,0xe0e0e0e0,0xc2c2e0c2,0xe2e2e0e2,0xc8c8e0c8,0xe8e8e0e8,0xcacae0ca,0xeaeae0ea,0xc0c0c2c0,0xe0e0c2e0,0xc2c2c2c2,0xe2e2c2e2,0xc8c8c2c8,0xe8e8c2e8,0xcacac2ca,0xeaeac2ea,0xc0c0e2c0,0xe0e0e2e0,0xc2c2e2c2,0xe2e2e2e2,0xc8c8e2c8,0xe8e8e2e8,0xcacae2ca,0xeaeae2ea,0xc0c0c8c0,0xe0e0c8e0,0xc2c2c8c2,0xe2e2c8e2,0xc8c8c8c8,0xe8e8c8e8,0xcacac8ca,0xeaeac8ea,0xc0c0e8c0,0xe0e0e8e0,0xc2c2e8c2,0xe2e2e8e2,0xc8c8e8c8,0xe8e8e8e8,0xcacae8ca,0xeaeae8ea,0xc0c0cac0,0xe0e0cae0,0xc2c2cac2,0xe2e2cae2,0xc8c8cac8,0xe8e8cae8,0xcacacaca,0xeaeacaea,0xc0c0eac0,0xe0e0eae0,0xc2c2eac2,0xe2e2eae2,0xc8c8eac8,0xe8e8eae8,0xcacaeaca,0xeaeaeaea,0xc0c0c0c0,0xf0f0c0f0,0xc3c3c0c3,0xf3f3c0f3,0xccccc0cc,0xfcfcc0fc,0xcfcfc0cf,0xffffc0ff,0xc0c0f0c0,0xf0f0f0f0,0xc3c3f0c3,0xf3f3f0f3,0xccccf0cc,0xfcfcf0fc,0xcfcff0cf,0xfffff0ff,0xc0c0c3c0,0xf0f0c3f0,0xc3c3c3c3,0xf3f3c3f3,0xccccc3cc,0xfcfcc3fc,0xcfcfc3cf,0xffffc3ff,0xc0c0f3c0,0xf0f0f3f0,0xc3c3f3c3,0xf3f3f3f3,0xccccf3cc,0xfcfcf3fc,0xcfcff3cf,0xfffff3ff,0xc0c0ccc0,0xf0f0ccf0,0xc3c3ccc3,0xf3f3ccf3,0xcccccccc,0xfcfcccfc,0xcfcfcccf,0xffffccff,0xc0c0fcc0,0xf0f0fcf0,0xc3c3fcc3,0xf3f3fcf3,0xccccfccc,0xfcfcfcfc,0xcfcffccf,0xfffffcff,0xc0c0cfc0,0xf0f0cff0,0xc3c3cfc3,0xf3f3cff3,0xcccccfcc,0xfcfccffc,0xcfcfcfcf,0xffffcfff,0xc0c0ffc0,0xf0f0fff0,0xc3c3ffc3,0xf3f3fff3,0xccccffcc,0xfcfcfffc,0xcfcfffcf,0xffffffff,0xc0c0c0c0,0xe0e0c0e0,0xc2c2c0c2,0xe2e2c0e2,0xc8c8c0c8,0xe8e8c0e8,0xcacac0ca,0xeaeac0ea,0xc0c0e0c0,0xe0e0e0e0,0xc2c2e0c2,0xe2e2e0e2,0xc8c8e0c8,0xe8e8e0e8,0xcacae0ca,0xeaeae0ea,0xc0c0c2c0,0xe0e0c2e0,0xc2c2c2c2,0xe2e2c2e2,0xc8c8c2c8,0xe8e8c2e8,0xcacac2ca,0xeaeac2ea,0xc0c0e2c0,0xe0e0e2e0,0xc2c2e2c2,0xe2e2e2e2,0xc8c8e2c8,0xe8e8e2e8,0xcacae2ca,0xeaeae2ea,0xc0c0c8c0,0xe0e0c8e0,0xc2c2c8c2,0xe2e2c8e2,0xc8c8c8c8,0xe8e8c8e8,0xcacac8ca,0xeaeac8ea,0xc0c0e8c0,0xe0e0e8e0,0xc2c2e8c2,0xe2e2e8e2,0xc8c8e8c8,0xe8e8e8e8,0xcacae8ca,0xeaeae8ea,0xc0c0cac0,0xe0e0cae0,0xc2c2cac2,0xe2e2cae2,0xc8c8cac8,0xe8e8cae8,0xcacacaca,0xeaeacaea,0xc0c0eac0,0xe0e0eae0,0xc2c2eac2,0xe2e2eae2,0xc8c8eac8,0xe8e8eae8,0xcacaeaca,0xeaeaeaea,0xc0c0c0c0,0xf0f0c0f0,0xc3c3c0c3,0xf3f3c0f3,0xccccc0cc,0xfcfcc0fc,0xcfcfc0cf,0xffffc0ff,0xc0c0f0c0,0xf0f0f0f0,0xc3c3f0c3,0xf3f3f0f3,0xccccf0cc,0xfcfcf0fc,0xcfcff0cf,0xfffff0ff,0xc0c0c3c0,0xf0f0c3f0,0xc3c3c3c3,0xf3f3c3f3,0xccccc3cc,0xfcfcc3fc,0xcfcfc3cf,0xffffc3ff,0xc0c0f3c0,0xf0f0f3f0,0xc3c3f3c3,0xf3f3f3f3,0xccccf3cc,0xfcfcf3fc,0xcfcff3cf,0xfffff3ff,0xc0c0ccc0,0xf0f0ccf0,0xc3c3ccc3,0xf3f3ccf3,0xcccccccc,0xfcfcccfc,0xcfcfcccf,0xffffccff,0xc0c0fcc0,0xf0f0fcf0,0xc3c3fcc3,0xf3f3fcf3,0xccccfccc,0xfcfcfcfc,0xcfcffccf,0xfffffcff,0xc0c0cfc0,0xf0f0cff0,0xc3c3cfc3,0xf3f3cff3,0xcccccfcc,0xfcfccffc,0xcfcfcfcf,0xffffcfff,0xc0c0ffc0,0xf0f0fff0,0xc3c3ffc3,0xf3f3fff3,0xccccffcc,0xfcfcfffc,0xcfcfffcf,0xffffffff}, -// {0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff} -// }; - -void precalcAluBytes() { - - uint16_t specfast_colors[128]; // Array for faster color calc in Draw - - unsigned int pal[2],b0,b1,b2,b3; - - // Calc array for faster color calcs in Draw - for (int i = 0; i < (NUM_SPECTRUM_COLORS >> 1); i++) { - // Normal - specfast_colors[i] = spectrum_colors[i]; - specfast_colors[i << 3] = spectrum_colors[i]; - // Bright - specfast_colors[i | 0x40] = spectrum_colors[i + (NUM_SPECTRUM_COLORS >> 1)]; - specfast_colors[(i << 3) | 0x40] = spectrum_colors[i + (NUM_SPECTRUM_COLORS >> 1)]; - } +// ALUBYTES +static const uint32_t AluBytes[16][256]={ +{0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff}, +{0xc0c0c0c0,0xc0c0e0c0,0xc0c0c2c0,0xc0c0e2c0,0xc0c0c8c0,0xc0c0e8c0,0xc0c0cac0,0xc0c0eac0,0xe0e0c0e0,0xe0e0e0e0,0xe0e0c2e0,0xe0e0e2e0,0xe0e0c8e0,0xe0e0e8e0,0xe0e0cae0,0xe0e0eae0,0xc2c2c0c2,0xc2c2e0c2,0xc2c2c2c2,0xc2c2e2c2,0xc2c2c8c2,0xc2c2e8c2,0xc2c2cac2,0xc2c2eac2,0xe2e2c0e2,0xe2e2e0e2,0xe2e2c2e2,0xe2e2e2e2,0xe2e2c8e2,0xe2e2e8e2,0xe2e2cae2,0xe2e2eae2,0xc8c8c0c8,0xc8c8e0c8,0xc8c8c2c8,0xc8c8e2c8,0xc8c8c8c8,0xc8c8e8c8,0xc8c8cac8,0xc8c8eac8,0xe8e8c0e8,0xe8e8e0e8,0xe8e8c2e8,0xe8e8e2e8,0xe8e8c8e8,0xe8e8e8e8,0xe8e8cae8,0xe8e8eae8,0xcacac0ca,0xcacae0ca,0xcacac2ca,0xcacae2ca,0xcacac8ca,0xcacae8ca,0xcacacaca,0xcacaeaca,0xeaeac0ea,0xeaeae0ea,0xeaeac2ea,0xeaeae2ea,0xeaeac8ea,0xeaeae8ea,0xeaeacaea,0xeaeaeaea,0xc0c0c0c0,0xc0c0f0c0,0xc0c0c3c0,0xc0c0f3c0,0xc0c0ccc0,0xc0c0fcc0,0xc0c0cfc0,0xc0c0ffc0,0xf0f0c0f0,0xf0f0f0f0,0xf0f0c3f0,0xf0f0f3f0,0xf0f0ccf0,0xf0f0fcf0,0xf0f0cff0,0xf0f0fff0,0xc3c3c0c3,0xc3c3f0c3,0xc3c3c3c3,0xc3c3f3c3,0xc3c3ccc3,0xc3c3fcc3,0xc3c3cfc3,0xc3c3ffc3,0xf3f3c0f3,0xf3f3f0f3,0xf3f3c3f3,0xf3f3f3f3,0xf3f3ccf3,0xf3f3fcf3,0xf3f3cff3,0xf3f3fff3,0xccccc0cc,0xccccf0cc,0xccccc3cc,0xccccf3cc,0xcccccccc,0xccccfccc,0xcccccfcc,0xccccffcc,0xfcfcc0fc,0xfcfcf0fc,0xfcfcc3fc,0xfcfcf3fc,0xfcfcccfc,0xfcfcfcfc,0xfcfccffc,0xfcfcfffc,0xcfcfc0cf,0xcfcff0cf,0xcfcfc3cf,0xcfcff3cf,0xcfcfcccf,0xcfcffccf,0xcfcfcfcf,0xcfcfffcf,0xffffc0ff,0xfffff0ff,0xffffc3ff,0xfffff3ff,0xffffccff,0xfffffcff,0xffffcfff,0xffffffff,0xc0c0c0c0,0xc0c0e0c0,0xc0c0c2c0,0xc0c0e2c0,0xc0c0c8c0,0xc0c0e8c0,0xc0c0cac0,0xc0c0eac0,0xe0e0c0e0,0xe0e0e0e0,0xe0e0c2e0,0xe0e0e2e0,0xe0e0c8e0,0xe0e0e8e0,0xe0e0cae0,0xe0e0eae0,0xc2c2c0c2,0xc2c2e0c2,0xc2c2c2c2,0xc2c2e2c2,0xc2c2c8c2,0xc2c2e8c2,0xc2c2cac2,0xc2c2eac2,0xe2e2c0e2,0xe2e2e0e2,0xe2e2c2e2,0xe2e2e2e2,0xe2e2c8e2,0xe2e2e8e2,0xe2e2cae2,0xe2e2eae2,0xc8c8c0c8,0xc8c8e0c8,0xc8c8c2c8,0xc8c8e2c8,0xc8c8c8c8,0xc8c8e8c8,0xc8c8cac8,0xc8c8eac8,0xe8e8c0e8,0xe8e8e0e8,0xe8e8c2e8,0xe8e8e2e8,0xe8e8c8e8,0xe8e8e8e8,0xe8e8cae8,0xe8e8eae8,0xcacac0ca,0xcacae0ca,0xcacac2ca,0xcacae2ca,0xcacac8ca,0xcacae8ca,0xcacacaca,0xcacaeaca,0xeaeac0ea,0xeaeae0ea,0xeaeac2ea,0xeaeae2ea,0xeaeac8ea,0xeaeae8ea,0xeaeacaea,0xeaeaeaea,0xc0c0c0c0,0xc0c0f0c0,0xc0c0c3c0,0xc0c0f3c0,0xc0c0ccc0,0xc0c0fcc0,0xc0c0cfc0,0xc0c0ffc0,0xf0f0c0f0,0xf0f0f0f0,0xf0f0c3f0,0xf0f0f3f0,0xf0f0ccf0,0xf0f0fcf0,0xf0f0cff0,0xf0f0fff0,0xc3c3c0c3,0xc3c3f0c3,0xc3c3c3c3,0xc3c3f3c3,0xc3c3ccc3,0xc3c3fcc3,0xc3c3cfc3,0xc3c3ffc3,0xf3f3c0f3,0xf3f3f0f3,0xf3f3c3f3,0xf3f3f3f3,0xf3f3ccf3,0xf3f3fcf3,0xf3f3cff3,0xf3f3fff3,0xccccc0cc,0xccccf0cc,0xccccc3cc,0xccccf3cc,0xcccccccc,0xccccfccc,0xcccccfcc,0xccccffcc,0xfcfcc0fc,0xfcfcf0fc,0xfcfcc3fc,0xfcfcf3fc,0xfcfcccfc,0xfcfcfcfc,0xfcfccffc,0xfcfcfffc,0xcfcfc0cf,0xcfcff0cf,0xcfcfc3cf,0xcfcff3cf,0xcfcfcccf,0xcfcffccf,0xcfcfcfcf,0xcfcfffcf,0xffffc0ff,0xfffff0ff,0xffffc3ff,0xfffff3ff,0xffffccff,0xfffffcff,0xffffcfff,0xffffffff}, +{0xc0c0c0c0,0xc0c0c0e0,0xc0c0c0c2,0xc0c0c0e2,0xc0c0c0c8,0xc0c0c0e8,0xc0c0c0ca,0xc0c0c0ea,0xe0e0e0c0,0xe0e0e0e0,0xe0e0e0c2,0xe0e0e0e2,0xe0e0e0c8,0xe0e0e0e8,0xe0e0e0ca,0xe0e0e0ea,0xc2c2c2c0,0xc2c2c2e0,0xc2c2c2c2,0xc2c2c2e2,0xc2c2c2c8,0xc2c2c2e8,0xc2c2c2ca,0xc2c2c2ea,0xe2e2e2c0,0xe2e2e2e0,0xe2e2e2c2,0xe2e2e2e2,0xe2e2e2c8,0xe2e2e2e8,0xe2e2e2ca,0xe2e2e2ea,0xc8c8c8c0,0xc8c8c8e0,0xc8c8c8c2,0xc8c8c8e2,0xc8c8c8c8,0xc8c8c8e8,0xc8c8c8ca,0xc8c8c8ea,0xe8e8e8c0,0xe8e8e8e0,0xe8e8e8c2,0xe8e8e8e2,0xe8e8e8c8,0xe8e8e8e8,0xe8e8e8ca,0xe8e8e8ea,0xcacacac0,0xcacacae0,0xcacacac2,0xcacacae2,0xcacacac8,0xcacacae8,0xcacacaca,0xcacacaea,0xeaeaeac0,0xeaeaeae0,0xeaeaeac2,0xeaeaeae2,0xeaeaeac8,0xeaeaeae8,0xeaeaeaca,0xeaeaeaea,0xc0c0c0c0,0xc0c0c0f0,0xc0c0c0c3,0xc0c0c0f3,0xc0c0c0cc,0xc0c0c0fc,0xc0c0c0cf,0xc0c0c0ff,0xf0f0f0c0,0xf0f0f0f0,0xf0f0f0c3,0xf0f0f0f3,0xf0f0f0cc,0xf0f0f0fc,0xf0f0f0cf,0xf0f0f0ff,0xc3c3c3c0,0xc3c3c3f0,0xc3c3c3c3,0xc3c3c3f3,0xc3c3c3cc,0xc3c3c3fc,0xc3c3c3cf,0xc3c3c3ff,0xf3f3f3c0,0xf3f3f3f0,0xf3f3f3c3,0xf3f3f3f3,0xf3f3f3cc,0xf3f3f3fc,0xf3f3f3cf,0xf3f3f3ff,0xccccccc0,0xccccccf0,0xccccccc3,0xccccccf3,0xcccccccc,0xccccccfc,0xcccccccf,0xccccccff,0xfcfcfcc0,0xfcfcfcf0,0xfcfcfcc3,0xfcfcfcf3,0xfcfcfccc,0xfcfcfcfc,0xfcfcfccf,0xfcfcfcff,0xcfcfcfc0,0xcfcfcff0,0xcfcfcfc3,0xcfcfcff3,0xcfcfcfcc,0xcfcfcffc,0xcfcfcfcf,0xcfcfcfff,0xffffffc0,0xfffffff0,0xffffffc3,0xfffffff3,0xffffffcc,0xfffffffc,0xffffffcf,0xffffffff,0xc0c0c0c0,0xc0c0c0e0,0xc0c0c0c2,0xc0c0c0e2,0xc0c0c0c8,0xc0c0c0e8,0xc0c0c0ca,0xc0c0c0ea,0xe0e0e0c0,0xe0e0e0e0,0xe0e0e0c2,0xe0e0e0e2,0xe0e0e0c8,0xe0e0e0e8,0xe0e0e0ca,0xe0e0e0ea,0xc2c2c2c0,0xc2c2c2e0,0xc2c2c2c2,0xc2c2c2e2,0xc2c2c2c8,0xc2c2c2e8,0xc2c2c2ca,0xc2c2c2ea,0xe2e2e2c0,0xe2e2e2e0,0xe2e2e2c2,0xe2e2e2e2,0xe2e2e2c8,0xe2e2e2e8,0xe2e2e2ca,0xe2e2e2ea,0xc8c8c8c0,0xc8c8c8e0,0xc8c8c8c2,0xc8c8c8e2,0xc8c8c8c8,0xc8c8c8e8,0xc8c8c8ca,0xc8c8c8ea,0xe8e8e8c0,0xe8e8e8e0,0xe8e8e8c2,0xe8e8e8e2,0xe8e8e8c8,0xe8e8e8e8,0xe8e8e8ca,0xe8e8e8ea,0xcacacac0,0xcacacae0,0xcacacac2,0xcacacae2,0xcacacac8,0xcacacae8,0xcacacaca,0xcacacaea,0xeaeaeac0,0xeaeaeae0,0xeaeaeac2,0xeaeaeae2,0xeaeaeac8,0xeaeaeae8,0xeaeaeaca,0xeaeaeaea,0xc0c0c0c0,0xc0c0c0f0,0xc0c0c0c3,0xc0c0c0f3,0xc0c0c0cc,0xc0c0c0fc,0xc0c0c0cf,0xc0c0c0ff,0xf0f0f0c0,0xf0f0f0f0,0xf0f0f0c3,0xf0f0f0f3,0xf0f0f0cc,0xf0f0f0fc,0xf0f0f0cf,0xf0f0f0ff,0xc3c3c3c0,0xc3c3c3f0,0xc3c3c3c3,0xc3c3c3f3,0xc3c3c3cc,0xc3c3c3fc,0xc3c3c3cf,0xc3c3c3ff,0xf3f3f3c0,0xf3f3f3f0,0xf3f3f3c3,0xf3f3f3f3,0xf3f3f3cc,0xf3f3f3fc,0xf3f3f3cf,0xf3f3f3ff,0xccccccc0,0xccccccf0,0xccccccc3,0xccccccf3,0xcccccccc,0xccccccfc,0xcccccccf,0xccccccff,0xfcfcfcc0,0xfcfcfcf0,0xfcfcfcc3,0xfcfcfcf3,0xfcfcfccc,0xfcfcfcfc,0xfcfcfccf,0xfcfcfcff,0xcfcfcfc0,0xcfcfcff0,0xcfcfcfc3,0xcfcfcff3,0xcfcfcfcc,0xcfcfcffc,0xcfcfcfcf,0xcfcfcfff,0xffffffc0,0xfffffff0,0xffffffc3,0xfffffff3,0xffffffcc,0xfffffffc,0xffffffcf,0xffffffff}, +{0xc0c0c0c0,0xc0c0e0e0,0xc0c0c2c2,0xc0c0e2e2,0xc0c0c8c8,0xc0c0e8e8,0xc0c0caca,0xc0c0eaea,0xe0e0c0c0,0xe0e0e0e0,0xe0e0c2c2,0xe0e0e2e2,0xe0e0c8c8,0xe0e0e8e8,0xe0e0caca,0xe0e0eaea,0xc2c2c0c0,0xc2c2e0e0,0xc2c2c2c2,0xc2c2e2e2,0xc2c2c8c8,0xc2c2e8e8,0xc2c2caca,0xc2c2eaea,0xe2e2c0c0,0xe2e2e0e0,0xe2e2c2c2,0xe2e2e2e2,0xe2e2c8c8,0xe2e2e8e8,0xe2e2caca,0xe2e2eaea,0xc8c8c0c0,0xc8c8e0e0,0xc8c8c2c2,0xc8c8e2e2,0xc8c8c8c8,0xc8c8e8e8,0xc8c8caca,0xc8c8eaea,0xe8e8c0c0,0xe8e8e0e0,0xe8e8c2c2,0xe8e8e2e2,0xe8e8c8c8,0xe8e8e8e8,0xe8e8caca,0xe8e8eaea,0xcacac0c0,0xcacae0e0,0xcacac2c2,0xcacae2e2,0xcacac8c8,0xcacae8e8,0xcacacaca,0xcacaeaea,0xeaeac0c0,0xeaeae0e0,0xeaeac2c2,0xeaeae2e2,0xeaeac8c8,0xeaeae8e8,0xeaeacaca,0xeaeaeaea,0xc0c0c0c0,0xc0c0f0f0,0xc0c0c3c3,0xc0c0f3f3,0xc0c0cccc,0xc0c0fcfc,0xc0c0cfcf,0xc0c0ffff,0xf0f0c0c0,0xf0f0f0f0,0xf0f0c3c3,0xf0f0f3f3,0xf0f0cccc,0xf0f0fcfc,0xf0f0cfcf,0xf0f0ffff,0xc3c3c0c0,0xc3c3f0f0,0xc3c3c3c3,0xc3c3f3f3,0xc3c3cccc,0xc3c3fcfc,0xc3c3cfcf,0xc3c3ffff,0xf3f3c0c0,0xf3f3f0f0,0xf3f3c3c3,0xf3f3f3f3,0xf3f3cccc,0xf3f3fcfc,0xf3f3cfcf,0xf3f3ffff,0xccccc0c0,0xccccf0f0,0xccccc3c3,0xccccf3f3,0xcccccccc,0xccccfcfc,0xcccccfcf,0xccccffff,0xfcfcc0c0,0xfcfcf0f0,0xfcfcc3c3,0xfcfcf3f3,0xfcfccccc,0xfcfcfcfc,0xfcfccfcf,0xfcfcffff,0xcfcfc0c0,0xcfcff0f0,0xcfcfc3c3,0xcfcff3f3,0xcfcfcccc,0xcfcffcfc,0xcfcfcfcf,0xcfcfffff,0xffffc0c0,0xfffff0f0,0xffffc3c3,0xfffff3f3,0xffffcccc,0xfffffcfc,0xffffcfcf,0xffffffff,0xc0c0c0c0,0xc0c0e0e0,0xc0c0c2c2,0xc0c0e2e2,0xc0c0c8c8,0xc0c0e8e8,0xc0c0caca,0xc0c0eaea,0xe0e0c0c0,0xe0e0e0e0,0xe0e0c2c2,0xe0e0e2e2,0xe0e0c8c8,0xe0e0e8e8,0xe0e0caca,0xe0e0eaea,0xc2c2c0c0,0xc2c2e0e0,0xc2c2c2c2,0xc2c2e2e2,0xc2c2c8c8,0xc2c2e8e8,0xc2c2caca,0xc2c2eaea,0xe2e2c0c0,0xe2e2e0e0,0xe2e2c2c2,0xe2e2e2e2,0xe2e2c8c8,0xe2e2e8e8,0xe2e2caca,0xe2e2eaea,0xc8c8c0c0,0xc8c8e0e0,0xc8c8c2c2,0xc8c8e2e2,0xc8c8c8c8,0xc8c8e8e8,0xc8c8caca,0xc8c8eaea,0xe8e8c0c0,0xe8e8e0e0,0xe8e8c2c2,0xe8e8e2e2,0xe8e8c8c8,0xe8e8e8e8,0xe8e8caca,0xe8e8eaea,0xcacac0c0,0xcacae0e0,0xcacac2c2,0xcacae2e2,0xcacac8c8,0xcacae8e8,0xcacacaca,0xcacaeaea,0xeaeac0c0,0xeaeae0e0,0xeaeac2c2,0xeaeae2e2,0xeaeac8c8,0xeaeae8e8,0xeaeacaca,0xeaeaeaea,0xc0c0c0c0,0xc0c0f0f0,0xc0c0c3c3,0xc0c0f3f3,0xc0c0cccc,0xc0c0fcfc,0xc0c0cfcf,0xc0c0ffff,0xf0f0c0c0,0xf0f0f0f0,0xf0f0c3c3,0xf0f0f3f3,0xf0f0cccc,0xf0f0fcfc,0xf0f0cfcf,0xf0f0ffff,0xc3c3c0c0,0xc3c3f0f0,0xc3c3c3c3,0xc3c3f3f3,0xc3c3cccc,0xc3c3fcfc,0xc3c3cfcf,0xc3c3ffff,0xf3f3c0c0,0xf3f3f0f0,0xf3f3c3c3,0xf3f3f3f3,0xf3f3cccc,0xf3f3fcfc,0xf3f3cfcf,0xf3f3ffff,0xccccc0c0,0xccccf0f0,0xccccc3c3,0xccccf3f3,0xcccccccc,0xccccfcfc,0xcccccfcf,0xccccffff,0xfcfcc0c0,0xfcfcf0f0,0xfcfcc3c3,0xfcfcf3f3,0xfcfccccc,0xfcfcfcfc,0xfcfccfcf,0xfcfcffff,0xcfcfc0c0,0xcfcff0f0,0xcfcfc3c3,0xcfcff3f3,0xcfcfcccc,0xcfcffcfc,0xcfcfcfcf,0xcfcfffff,0xffffc0c0,0xfffff0f0,0xffffc3c3,0xfffff3f3,0xffffcccc,0xfffffcfc,0xffffcfcf,0xffffffff}, +{0xc0c0c0c0,0xe0c0c0c0,0xc2c0c0c0,0xe2c0c0c0,0xc8c0c0c0,0xe8c0c0c0,0xcac0c0c0,0xeac0c0c0,0xc0e0e0e0,0xe0e0e0e0,0xc2e0e0e0,0xe2e0e0e0,0xc8e0e0e0,0xe8e0e0e0,0xcae0e0e0,0xeae0e0e0,0xc0c2c2c2,0xe0c2c2c2,0xc2c2c2c2,0xe2c2c2c2,0xc8c2c2c2,0xe8c2c2c2,0xcac2c2c2,0xeac2c2c2,0xc0e2e2e2,0xe0e2e2e2,0xc2e2e2e2,0xe2e2e2e2,0xc8e2e2e2,0xe8e2e2e2,0xcae2e2e2,0xeae2e2e2,0xc0c8c8c8,0xe0c8c8c8,0xc2c8c8c8,0xe2c8c8c8,0xc8c8c8c8,0xe8c8c8c8,0xcac8c8c8,0xeac8c8c8,0xc0e8e8e8,0xe0e8e8e8,0xc2e8e8e8,0xe2e8e8e8,0xc8e8e8e8,0xe8e8e8e8,0xcae8e8e8,0xeae8e8e8,0xc0cacaca,0xe0cacaca,0xc2cacaca,0xe2cacaca,0xc8cacaca,0xe8cacaca,0xcacacaca,0xeacacaca,0xc0eaeaea,0xe0eaeaea,0xc2eaeaea,0xe2eaeaea,0xc8eaeaea,0xe8eaeaea,0xcaeaeaea,0xeaeaeaea,0xc0c0c0c0,0xf0c0c0c0,0xc3c0c0c0,0xf3c0c0c0,0xccc0c0c0,0xfcc0c0c0,0xcfc0c0c0,0xffc0c0c0,0xc0f0f0f0,0xf0f0f0f0,0xc3f0f0f0,0xf3f0f0f0,0xccf0f0f0,0xfcf0f0f0,0xcff0f0f0,0xfff0f0f0,0xc0c3c3c3,0xf0c3c3c3,0xc3c3c3c3,0xf3c3c3c3,0xccc3c3c3,0xfcc3c3c3,0xcfc3c3c3,0xffc3c3c3,0xc0f3f3f3,0xf0f3f3f3,0xc3f3f3f3,0xf3f3f3f3,0xccf3f3f3,0xfcf3f3f3,0xcff3f3f3,0xfff3f3f3,0xc0cccccc,0xf0cccccc,0xc3cccccc,0xf3cccccc,0xcccccccc,0xfccccccc,0xcfcccccc,0xffcccccc,0xc0fcfcfc,0xf0fcfcfc,0xc3fcfcfc,0xf3fcfcfc,0xccfcfcfc,0xfcfcfcfc,0xcffcfcfc,0xfffcfcfc,0xc0cfcfcf,0xf0cfcfcf,0xc3cfcfcf,0xf3cfcfcf,0xcccfcfcf,0xfccfcfcf,0xcfcfcfcf,0xffcfcfcf,0xc0ffffff,0xf0ffffff,0xc3ffffff,0xf3ffffff,0xccffffff,0xfcffffff,0xcfffffff,0xffffffff,0xc0c0c0c0,0xe0c0c0c0,0xc2c0c0c0,0xe2c0c0c0,0xc8c0c0c0,0xe8c0c0c0,0xcac0c0c0,0xeac0c0c0,0xc0e0e0e0,0xe0e0e0e0,0xc2e0e0e0,0xe2e0e0e0,0xc8e0e0e0,0xe8e0e0e0,0xcae0e0e0,0xeae0e0e0,0xc0c2c2c2,0xe0c2c2c2,0xc2c2c2c2,0xe2c2c2c2,0xc8c2c2c2,0xe8c2c2c2,0xcac2c2c2,0xeac2c2c2,0xc0e2e2e2,0xe0e2e2e2,0xc2e2e2e2,0xe2e2e2e2,0xc8e2e2e2,0xe8e2e2e2,0xcae2e2e2,0xeae2e2e2,0xc0c8c8c8,0xe0c8c8c8,0xc2c8c8c8,0xe2c8c8c8,0xc8c8c8c8,0xe8c8c8c8,0xcac8c8c8,0xeac8c8c8,0xc0e8e8e8,0xe0e8e8e8,0xc2e8e8e8,0xe2e8e8e8,0xc8e8e8e8,0xe8e8e8e8,0xcae8e8e8,0xeae8e8e8,0xc0cacaca,0xe0cacaca,0xc2cacaca,0xe2cacaca,0xc8cacaca,0xe8cacaca,0xcacacaca,0xeacacaca,0xc0eaeaea,0xe0eaeaea,0xc2eaeaea,0xe2eaeaea,0xc8eaeaea,0xe8eaeaea,0xcaeaeaea,0xeaeaeaea,0xc0c0c0c0,0xf0c0c0c0,0xc3c0c0c0,0xf3c0c0c0,0xccc0c0c0,0xfcc0c0c0,0xcfc0c0c0,0xffc0c0c0,0xc0f0f0f0,0xf0f0f0f0,0xc3f0f0f0,0xf3f0f0f0,0xccf0f0f0,0xfcf0f0f0,0xcff0f0f0,0xfff0f0f0,0xc0c3c3c3,0xf0c3c3c3,0xc3c3c3c3,0xf3c3c3c3,0xccc3c3c3,0xfcc3c3c3,0xcfc3c3c3,0xffc3c3c3,0xc0f3f3f3,0xf0f3f3f3,0xc3f3f3f3,0xf3f3f3f3,0xccf3f3f3,0xfcf3f3f3,0xcff3f3f3,0xfff3f3f3,0xc0cccccc,0xf0cccccc,0xc3cccccc,0xf3cccccc,0xcccccccc,0xfccccccc,0xcfcccccc,0xffcccccc,0xc0fcfcfc,0xf0fcfcfc,0xc3fcfcfc,0xf3fcfcfc,0xccfcfcfc,0xfcfcfcfc,0xcffcfcfc,0xfffcfcfc,0xc0cfcfcf,0xf0cfcfcf,0xc3cfcfcf,0xf3cfcfcf,0xcccfcfcf,0xfccfcfcf,0xcfcfcfcf,0xffcfcfcf,0xc0ffffff,0xf0ffffff,0xc3ffffff,0xf3ffffff,0xccffffff,0xfcffffff,0xcfffffff,0xffffffff}, +{0xc0c0c0c0,0xe0c0e0c0,0xc2c0c2c0,0xe2c0e2c0,0xc8c0c8c0,0xe8c0e8c0,0xcac0cac0,0xeac0eac0,0xc0e0c0e0,0xe0e0e0e0,0xc2e0c2e0,0xe2e0e2e0,0xc8e0c8e0,0xe8e0e8e0,0xcae0cae0,0xeae0eae0,0xc0c2c0c2,0xe0c2e0c2,0xc2c2c2c2,0xe2c2e2c2,0xc8c2c8c2,0xe8c2e8c2,0xcac2cac2,0xeac2eac2,0xc0e2c0e2,0xe0e2e0e2,0xc2e2c2e2,0xe2e2e2e2,0xc8e2c8e2,0xe8e2e8e2,0xcae2cae2,0xeae2eae2,0xc0c8c0c8,0xe0c8e0c8,0xc2c8c2c8,0xe2c8e2c8,0xc8c8c8c8,0xe8c8e8c8,0xcac8cac8,0xeac8eac8,0xc0e8c0e8,0xe0e8e0e8,0xc2e8c2e8,0xe2e8e2e8,0xc8e8c8e8,0xe8e8e8e8,0xcae8cae8,0xeae8eae8,0xc0cac0ca,0xe0cae0ca,0xc2cac2ca,0xe2cae2ca,0xc8cac8ca,0xe8cae8ca,0xcacacaca,0xeacaeaca,0xc0eac0ea,0xe0eae0ea,0xc2eac2ea,0xe2eae2ea,0xc8eac8ea,0xe8eae8ea,0xcaeacaea,0xeaeaeaea,0xc0c0c0c0,0xf0c0f0c0,0xc3c0c3c0,0xf3c0f3c0,0xccc0ccc0,0xfcc0fcc0,0xcfc0cfc0,0xffc0ffc0,0xc0f0c0f0,0xf0f0f0f0,0xc3f0c3f0,0xf3f0f3f0,0xccf0ccf0,0xfcf0fcf0,0xcff0cff0,0xfff0fff0,0xc0c3c0c3,0xf0c3f0c3,0xc3c3c3c3,0xf3c3f3c3,0xccc3ccc3,0xfcc3fcc3,0xcfc3cfc3,0xffc3ffc3,0xc0f3c0f3,0xf0f3f0f3,0xc3f3c3f3,0xf3f3f3f3,0xccf3ccf3,0xfcf3fcf3,0xcff3cff3,0xfff3fff3,0xc0ccc0cc,0xf0ccf0cc,0xc3ccc3cc,0xf3ccf3cc,0xcccccccc,0xfcccfccc,0xcfcccfcc,0xffccffcc,0xc0fcc0fc,0xf0fcf0fc,0xc3fcc3fc,0xf3fcf3fc,0xccfcccfc,0xfcfcfcfc,0xcffccffc,0xfffcfffc,0xc0cfc0cf,0xf0cff0cf,0xc3cfc3cf,0xf3cff3cf,0xcccfcccf,0xfccffccf,0xcfcfcfcf,0xffcfffcf,0xc0ffc0ff,0xf0fff0ff,0xc3ffc3ff,0xf3fff3ff,0xccffccff,0xfcfffcff,0xcfffcfff,0xffffffff,0xc0c0c0c0,0xe0c0e0c0,0xc2c0c2c0,0xe2c0e2c0,0xc8c0c8c0,0xe8c0e8c0,0xcac0cac0,0xeac0eac0,0xc0e0c0e0,0xe0e0e0e0,0xc2e0c2e0,0xe2e0e2e0,0xc8e0c8e0,0xe8e0e8e0,0xcae0cae0,0xeae0eae0,0xc0c2c0c2,0xe0c2e0c2,0xc2c2c2c2,0xe2c2e2c2,0xc8c2c8c2,0xe8c2e8c2,0xcac2cac2,0xeac2eac2,0xc0e2c0e2,0xe0e2e0e2,0xc2e2c2e2,0xe2e2e2e2,0xc8e2c8e2,0xe8e2e8e2,0xcae2cae2,0xeae2eae2,0xc0c8c0c8,0xe0c8e0c8,0xc2c8c2c8,0xe2c8e2c8,0xc8c8c8c8,0xe8c8e8c8,0xcac8cac8,0xeac8eac8,0xc0e8c0e8,0xe0e8e0e8,0xc2e8c2e8,0xe2e8e2e8,0xc8e8c8e8,0xe8e8e8e8,0xcae8cae8,0xeae8eae8,0xc0cac0ca,0xe0cae0ca,0xc2cac2ca,0xe2cae2ca,0xc8cac8ca,0xe8cae8ca,0xcacacaca,0xeacaeaca,0xc0eac0ea,0xe0eae0ea,0xc2eac2ea,0xe2eae2ea,0xc8eac8ea,0xe8eae8ea,0xcaeacaea,0xeaeaeaea,0xc0c0c0c0,0xf0c0f0c0,0xc3c0c3c0,0xf3c0f3c0,0xccc0ccc0,0xfcc0fcc0,0xcfc0cfc0,0xffc0ffc0,0xc0f0c0f0,0xf0f0f0f0,0xc3f0c3f0,0xf3f0f3f0,0xccf0ccf0,0xfcf0fcf0,0xcff0cff0,0xfff0fff0,0xc0c3c0c3,0xf0c3f0c3,0xc3c3c3c3,0xf3c3f3c3,0xccc3ccc3,0xfcc3fcc3,0xcfc3cfc3,0xffc3ffc3,0xc0f3c0f3,0xf0f3f0f3,0xc3f3c3f3,0xf3f3f3f3,0xccf3ccf3,0xfcf3fcf3,0xcff3cff3,0xfff3fff3,0xc0ccc0cc,0xf0ccf0cc,0xc3ccc3cc,0xf3ccf3cc,0xcccccccc,0xfcccfccc,0xcfcccfcc,0xffccffcc,0xc0fcc0fc,0xf0fcf0fc,0xc3fcc3fc,0xf3fcf3fc,0xccfcccfc,0xfcfcfcfc,0xcffccffc,0xfffcfffc,0xc0cfc0cf,0xf0cff0cf,0xc3cfc3cf,0xf3cff3cf,0xcccfcccf,0xfccffccf,0xcfcfcfcf,0xffcfffcf,0xc0ffc0ff,0xf0fff0ff,0xc3ffc3ff,0xf3fff3ff,0xccffccff,0xfcfffcff,0xcfffcfff,0xffffffff}, +{0xc0c0c0c0,0xe0c0c0e0,0xc2c0c0c2,0xe2c0c0e2,0xc8c0c0c8,0xe8c0c0e8,0xcac0c0ca,0xeac0c0ea,0xc0e0e0c0,0xe0e0e0e0,0xc2e0e0c2,0xe2e0e0e2,0xc8e0e0c8,0xe8e0e0e8,0xcae0e0ca,0xeae0e0ea,0xc0c2c2c0,0xe0c2c2e0,0xc2c2c2c2,0xe2c2c2e2,0xc8c2c2c8,0xe8c2c2e8,0xcac2c2ca,0xeac2c2ea,0xc0e2e2c0,0xe0e2e2e0,0xc2e2e2c2,0xe2e2e2e2,0xc8e2e2c8,0xe8e2e2e8,0xcae2e2ca,0xeae2e2ea,0xc0c8c8c0,0xe0c8c8e0,0xc2c8c8c2,0xe2c8c8e2,0xc8c8c8c8,0xe8c8c8e8,0xcac8c8ca,0xeac8c8ea,0xc0e8e8c0,0xe0e8e8e0,0xc2e8e8c2,0xe2e8e8e2,0xc8e8e8c8,0xe8e8e8e8,0xcae8e8ca,0xeae8e8ea,0xc0cacac0,0xe0cacae0,0xc2cacac2,0xe2cacae2,0xc8cacac8,0xe8cacae8,0xcacacaca,0xeacacaea,0xc0eaeac0,0xe0eaeae0,0xc2eaeac2,0xe2eaeae2,0xc8eaeac8,0xe8eaeae8,0xcaeaeaca,0xeaeaeaea,0xc0c0c0c0,0xf0c0c0f0,0xc3c0c0c3,0xf3c0c0f3,0xccc0c0cc,0xfcc0c0fc,0xcfc0c0cf,0xffc0c0ff,0xc0f0f0c0,0xf0f0f0f0,0xc3f0f0c3,0xf3f0f0f3,0xccf0f0cc,0xfcf0f0fc,0xcff0f0cf,0xfff0f0ff,0xc0c3c3c0,0xf0c3c3f0,0xc3c3c3c3,0xf3c3c3f3,0xccc3c3cc,0xfcc3c3fc,0xcfc3c3cf,0xffc3c3ff,0xc0f3f3c0,0xf0f3f3f0,0xc3f3f3c3,0xf3f3f3f3,0xccf3f3cc,0xfcf3f3fc,0xcff3f3cf,0xfff3f3ff,0xc0ccccc0,0xf0ccccf0,0xc3ccccc3,0xf3ccccf3,0xcccccccc,0xfcccccfc,0xcfcccccf,0xffccccff,0xc0fcfcc0,0xf0fcfcf0,0xc3fcfcc3,0xf3fcfcf3,0xccfcfccc,0xfcfcfcfc,0xcffcfccf,0xfffcfcff,0xc0cfcfc0,0xf0cfcff0,0xc3cfcfc3,0xf3cfcff3,0xcccfcfcc,0xfccfcffc,0xcfcfcfcf,0xffcfcfff,0xc0ffffc0,0xf0fffff0,0xc3ffffc3,0xf3fffff3,0xccffffcc,0xfcfffffc,0xcfffffcf,0xffffffff,0xc0c0c0c0,0xe0c0c0e0,0xc2c0c0c2,0xe2c0c0e2,0xc8c0c0c8,0xe8c0c0e8,0xcac0c0ca,0xeac0c0ea,0xc0e0e0c0,0xe0e0e0e0,0xc2e0e0c2,0xe2e0e0e2,0xc8e0e0c8,0xe8e0e0e8,0xcae0e0ca,0xeae0e0ea,0xc0c2c2c0,0xe0c2c2e0,0xc2c2c2c2,0xe2c2c2e2,0xc8c2c2c8,0xe8c2c2e8,0xcac2c2ca,0xeac2c2ea,0xc0e2e2c0,0xe0e2e2e0,0xc2e2e2c2,0xe2e2e2e2,0xc8e2e2c8,0xe8e2e2e8,0xcae2e2ca,0xeae2e2ea,0xc0c8c8c0,0xe0c8c8e0,0xc2c8c8c2,0xe2c8c8e2,0xc8c8c8c8,0xe8c8c8e8,0xcac8c8ca,0xeac8c8ea,0xc0e8e8c0,0xe0e8e8e0,0xc2e8e8c2,0xe2e8e8e2,0xc8e8e8c8,0xe8e8e8e8,0xcae8e8ca,0xeae8e8ea,0xc0cacac0,0xe0cacae0,0xc2cacac2,0xe2cacae2,0xc8cacac8,0xe8cacae8,0xcacacaca,0xeacacaea,0xc0eaeac0,0xe0eaeae0,0xc2eaeac2,0xe2eaeae2,0xc8eaeac8,0xe8eaeae8,0xcaeaeaca,0xeaeaeaea,0xc0c0c0c0,0xf0c0c0f0,0xc3c0c0c3,0xf3c0c0f3,0xccc0c0cc,0xfcc0c0fc,0xcfc0c0cf,0xffc0c0ff,0xc0f0f0c0,0xf0f0f0f0,0xc3f0f0c3,0xf3f0f0f3,0xccf0f0cc,0xfcf0f0fc,0xcff0f0cf,0xfff0f0ff,0xc0c3c3c0,0xf0c3c3f0,0xc3c3c3c3,0xf3c3c3f3,0xccc3c3cc,0xfcc3c3fc,0xcfc3c3cf,0xffc3c3ff,0xc0f3f3c0,0xf0f3f3f0,0xc3f3f3c3,0xf3f3f3f3,0xccf3f3cc,0xfcf3f3fc,0xcff3f3cf,0xfff3f3ff,0xc0ccccc0,0xf0ccccf0,0xc3ccccc3,0xf3ccccf3,0xcccccccc,0xfcccccfc,0xcfcccccf,0xffccccff,0xc0fcfcc0,0xf0fcfcf0,0xc3fcfcc3,0xf3fcfcf3,0xccfcfccc,0xfcfcfcfc,0xcffcfccf,0xfffcfcff,0xc0cfcfc0,0xf0cfcff0,0xc3cfcfc3,0xf3cfcff3,0xcccfcfcc,0xfccfcffc,0xcfcfcfcf,0xffcfcfff,0xc0ffffc0,0xf0fffff0,0xc3ffffc3,0xf3fffff3,0xccffffcc,0xfcfffffc,0xcfffffcf,0xffffffff}, +{0xc0c0c0c0,0xe0c0e0e0,0xc2c0c2c2,0xe2c0e2e2,0xc8c0c8c8,0xe8c0e8e8,0xcac0caca,0xeac0eaea,0xc0e0c0c0,0xe0e0e0e0,0xc2e0c2c2,0xe2e0e2e2,0xc8e0c8c8,0xe8e0e8e8,0xcae0caca,0xeae0eaea,0xc0c2c0c0,0xe0c2e0e0,0xc2c2c2c2,0xe2c2e2e2,0xc8c2c8c8,0xe8c2e8e8,0xcac2caca,0xeac2eaea,0xc0e2c0c0,0xe0e2e0e0,0xc2e2c2c2,0xe2e2e2e2,0xc8e2c8c8,0xe8e2e8e8,0xcae2caca,0xeae2eaea,0xc0c8c0c0,0xe0c8e0e0,0xc2c8c2c2,0xe2c8e2e2,0xc8c8c8c8,0xe8c8e8e8,0xcac8caca,0xeac8eaea,0xc0e8c0c0,0xe0e8e0e0,0xc2e8c2c2,0xe2e8e2e2,0xc8e8c8c8,0xe8e8e8e8,0xcae8caca,0xeae8eaea,0xc0cac0c0,0xe0cae0e0,0xc2cac2c2,0xe2cae2e2,0xc8cac8c8,0xe8cae8e8,0xcacacaca,0xeacaeaea,0xc0eac0c0,0xe0eae0e0,0xc2eac2c2,0xe2eae2e2,0xc8eac8c8,0xe8eae8e8,0xcaeacaca,0xeaeaeaea,0xc0c0c0c0,0xf0c0f0f0,0xc3c0c3c3,0xf3c0f3f3,0xccc0cccc,0xfcc0fcfc,0xcfc0cfcf,0xffc0ffff,0xc0f0c0c0,0xf0f0f0f0,0xc3f0c3c3,0xf3f0f3f3,0xccf0cccc,0xfcf0fcfc,0xcff0cfcf,0xfff0ffff,0xc0c3c0c0,0xf0c3f0f0,0xc3c3c3c3,0xf3c3f3f3,0xccc3cccc,0xfcc3fcfc,0xcfc3cfcf,0xffc3ffff,0xc0f3c0c0,0xf0f3f0f0,0xc3f3c3c3,0xf3f3f3f3,0xccf3cccc,0xfcf3fcfc,0xcff3cfcf,0xfff3ffff,0xc0ccc0c0,0xf0ccf0f0,0xc3ccc3c3,0xf3ccf3f3,0xcccccccc,0xfcccfcfc,0xcfcccfcf,0xffccffff,0xc0fcc0c0,0xf0fcf0f0,0xc3fcc3c3,0xf3fcf3f3,0xccfccccc,0xfcfcfcfc,0xcffccfcf,0xfffcffff,0xc0cfc0c0,0xf0cff0f0,0xc3cfc3c3,0xf3cff3f3,0xcccfcccc,0xfccffcfc,0xcfcfcfcf,0xffcfffff,0xc0ffc0c0,0xf0fff0f0,0xc3ffc3c3,0xf3fff3f3,0xccffcccc,0xfcfffcfc,0xcfffcfcf,0xffffffff,0xc0c0c0c0,0xe0c0e0e0,0xc2c0c2c2,0xe2c0e2e2,0xc8c0c8c8,0xe8c0e8e8,0xcac0caca,0xeac0eaea,0xc0e0c0c0,0xe0e0e0e0,0xc2e0c2c2,0xe2e0e2e2,0xc8e0c8c8,0xe8e0e8e8,0xcae0caca,0xeae0eaea,0xc0c2c0c0,0xe0c2e0e0,0xc2c2c2c2,0xe2c2e2e2,0xc8c2c8c8,0xe8c2e8e8,0xcac2caca,0xeac2eaea,0xc0e2c0c0,0xe0e2e0e0,0xc2e2c2c2,0xe2e2e2e2,0xc8e2c8c8,0xe8e2e8e8,0xcae2caca,0xeae2eaea,0xc0c8c0c0,0xe0c8e0e0,0xc2c8c2c2,0xe2c8e2e2,0xc8c8c8c8,0xe8c8e8e8,0xcac8caca,0xeac8eaea,0xc0e8c0c0,0xe0e8e0e0,0xc2e8c2c2,0xe2e8e2e2,0xc8e8c8c8,0xe8e8e8e8,0xcae8caca,0xeae8eaea,0xc0cac0c0,0xe0cae0e0,0xc2cac2c2,0xe2cae2e2,0xc8cac8c8,0xe8cae8e8,0xcacacaca,0xeacaeaea,0xc0eac0c0,0xe0eae0e0,0xc2eac2c2,0xe2eae2e2,0xc8eac8c8,0xe8eae8e8,0xcaeacaca,0xeaeaeaea,0xc0c0c0c0,0xf0c0f0f0,0xc3c0c3c3,0xf3c0f3f3,0xccc0cccc,0xfcc0fcfc,0xcfc0cfcf,0xffc0ffff,0xc0f0c0c0,0xf0f0f0f0,0xc3f0c3c3,0xf3f0f3f3,0xccf0cccc,0xfcf0fcfc,0xcff0cfcf,0xfff0ffff,0xc0c3c0c0,0xf0c3f0f0,0xc3c3c3c3,0xf3c3f3f3,0xccc3cccc,0xfcc3fcfc,0xcfc3cfcf,0xffc3ffff,0xc0f3c0c0,0xf0f3f0f0,0xc3f3c3c3,0xf3f3f3f3,0xccf3cccc,0xfcf3fcfc,0xcff3cfcf,0xfff3ffff,0xc0ccc0c0,0xf0ccf0f0,0xc3ccc3c3,0xf3ccf3f3,0xcccccccc,0xfcccfcfc,0xcfcccfcf,0xffccffff,0xc0fcc0c0,0xf0fcf0f0,0xc3fcc3c3,0xf3fcf3f3,0xccfccccc,0xfcfcfcfc,0xcffccfcf,0xfffcffff,0xc0cfc0c0,0xf0cff0f0,0xc3cfc3c3,0xf3cff3f3,0xcccfcccc,0xfccffcfc,0xcfcfcfcf,0xffcfffff,0xc0ffc0c0,0xf0fff0f0,0xc3ffc3c3,0xf3fff3f3,0xccffcccc,0xfcfffcfc,0xcfffcfcf,0xffffffff}, +{0xc0c0c0c0,0xc0e0c0c0,0xc0c2c0c0,0xc0e2c0c0,0xc0c8c0c0,0xc0e8c0c0,0xc0cac0c0,0xc0eac0c0,0xe0c0e0e0,0xe0e0e0e0,0xe0c2e0e0,0xe0e2e0e0,0xe0c8e0e0,0xe0e8e0e0,0xe0cae0e0,0xe0eae0e0,0xc2c0c2c2,0xc2e0c2c2,0xc2c2c2c2,0xc2e2c2c2,0xc2c8c2c2,0xc2e8c2c2,0xc2cac2c2,0xc2eac2c2,0xe2c0e2e2,0xe2e0e2e2,0xe2c2e2e2,0xe2e2e2e2,0xe2c8e2e2,0xe2e8e2e2,0xe2cae2e2,0xe2eae2e2,0xc8c0c8c8,0xc8e0c8c8,0xc8c2c8c8,0xc8e2c8c8,0xc8c8c8c8,0xc8e8c8c8,0xc8cac8c8,0xc8eac8c8,0xe8c0e8e8,0xe8e0e8e8,0xe8c2e8e8,0xe8e2e8e8,0xe8c8e8e8,0xe8e8e8e8,0xe8cae8e8,0xe8eae8e8,0xcac0caca,0xcae0caca,0xcac2caca,0xcae2caca,0xcac8caca,0xcae8caca,0xcacacaca,0xcaeacaca,0xeac0eaea,0xeae0eaea,0xeac2eaea,0xeae2eaea,0xeac8eaea,0xeae8eaea,0xeacaeaea,0xeaeaeaea,0xc0c0c0c0,0xc0f0c0c0,0xc0c3c0c0,0xc0f3c0c0,0xc0ccc0c0,0xc0fcc0c0,0xc0cfc0c0,0xc0ffc0c0,0xf0c0f0f0,0xf0f0f0f0,0xf0c3f0f0,0xf0f3f0f0,0xf0ccf0f0,0xf0fcf0f0,0xf0cff0f0,0xf0fff0f0,0xc3c0c3c3,0xc3f0c3c3,0xc3c3c3c3,0xc3f3c3c3,0xc3ccc3c3,0xc3fcc3c3,0xc3cfc3c3,0xc3ffc3c3,0xf3c0f3f3,0xf3f0f3f3,0xf3c3f3f3,0xf3f3f3f3,0xf3ccf3f3,0xf3fcf3f3,0xf3cff3f3,0xf3fff3f3,0xccc0cccc,0xccf0cccc,0xccc3cccc,0xccf3cccc,0xcccccccc,0xccfccccc,0xcccfcccc,0xccffcccc,0xfcc0fcfc,0xfcf0fcfc,0xfcc3fcfc,0xfcf3fcfc,0xfcccfcfc,0xfcfcfcfc,0xfccffcfc,0xfcfffcfc,0xcfc0cfcf,0xcff0cfcf,0xcfc3cfcf,0xcff3cfcf,0xcfcccfcf,0xcffccfcf,0xcfcfcfcf,0xcfffcfcf,0xffc0ffff,0xfff0ffff,0xffc3ffff,0xfff3ffff,0xffccffff,0xfffcffff,0xffcfffff,0xffffffff,0xc0c0c0c0,0xc0e0c0c0,0xc0c2c0c0,0xc0e2c0c0,0xc0c8c0c0,0xc0e8c0c0,0xc0cac0c0,0xc0eac0c0,0xe0c0e0e0,0xe0e0e0e0,0xe0c2e0e0,0xe0e2e0e0,0xe0c8e0e0,0xe0e8e0e0,0xe0cae0e0,0xe0eae0e0,0xc2c0c2c2,0xc2e0c2c2,0xc2c2c2c2,0xc2e2c2c2,0xc2c8c2c2,0xc2e8c2c2,0xc2cac2c2,0xc2eac2c2,0xe2c0e2e2,0xe2e0e2e2,0xe2c2e2e2,0xe2e2e2e2,0xe2c8e2e2,0xe2e8e2e2,0xe2cae2e2,0xe2eae2e2,0xc8c0c8c8,0xc8e0c8c8,0xc8c2c8c8,0xc8e2c8c8,0xc8c8c8c8,0xc8e8c8c8,0xc8cac8c8,0xc8eac8c8,0xe8c0e8e8,0xe8e0e8e8,0xe8c2e8e8,0xe8e2e8e8,0xe8c8e8e8,0xe8e8e8e8,0xe8cae8e8,0xe8eae8e8,0xcac0caca,0xcae0caca,0xcac2caca,0xcae2caca,0xcac8caca,0xcae8caca,0xcacacaca,0xcaeacaca,0xeac0eaea,0xeae0eaea,0xeac2eaea,0xeae2eaea,0xeac8eaea,0xeae8eaea,0xeacaeaea,0xeaeaeaea,0xc0c0c0c0,0xc0f0c0c0,0xc0c3c0c0,0xc0f3c0c0,0xc0ccc0c0,0xc0fcc0c0,0xc0cfc0c0,0xc0ffc0c0,0xf0c0f0f0,0xf0f0f0f0,0xf0c3f0f0,0xf0f3f0f0,0xf0ccf0f0,0xf0fcf0f0,0xf0cff0f0,0xf0fff0f0,0xc3c0c3c3,0xc3f0c3c3,0xc3c3c3c3,0xc3f3c3c3,0xc3ccc3c3,0xc3fcc3c3,0xc3cfc3c3,0xc3ffc3c3,0xf3c0f3f3,0xf3f0f3f3,0xf3c3f3f3,0xf3f3f3f3,0xf3ccf3f3,0xf3fcf3f3,0xf3cff3f3,0xf3fff3f3,0xccc0cccc,0xccf0cccc,0xccc3cccc,0xccf3cccc,0xcccccccc,0xccfccccc,0xcccfcccc,0xccffcccc,0xfcc0fcfc,0xfcf0fcfc,0xfcc3fcfc,0xfcf3fcfc,0xfcccfcfc,0xfcfcfcfc,0xfccffcfc,0xfcfffcfc,0xcfc0cfcf,0xcff0cfcf,0xcfc3cfcf,0xcff3cfcf,0xcfcccfcf,0xcffccfcf,0xcfcfcfcf,0xcfffcfcf,0xffc0ffff,0xfff0ffff,0xffc3ffff,0xfff3ffff,0xffccffff,0xfffcffff,0xffcfffff,0xffffffff}, +{0xc0c0c0c0,0xc0e0e0c0,0xc0c2c2c0,0xc0e2e2c0,0xc0c8c8c0,0xc0e8e8c0,0xc0cacac0,0xc0eaeac0,0xe0c0c0e0,0xe0e0e0e0,0xe0c2c2e0,0xe0e2e2e0,0xe0c8c8e0,0xe0e8e8e0,0xe0cacae0,0xe0eaeae0,0xc2c0c0c2,0xc2e0e0c2,0xc2c2c2c2,0xc2e2e2c2,0xc2c8c8c2,0xc2e8e8c2,0xc2cacac2,0xc2eaeac2,0xe2c0c0e2,0xe2e0e0e2,0xe2c2c2e2,0xe2e2e2e2,0xe2c8c8e2,0xe2e8e8e2,0xe2cacae2,0xe2eaeae2,0xc8c0c0c8,0xc8e0e0c8,0xc8c2c2c8,0xc8e2e2c8,0xc8c8c8c8,0xc8e8e8c8,0xc8cacac8,0xc8eaeac8,0xe8c0c0e8,0xe8e0e0e8,0xe8c2c2e8,0xe8e2e2e8,0xe8c8c8e8,0xe8e8e8e8,0xe8cacae8,0xe8eaeae8,0xcac0c0ca,0xcae0e0ca,0xcac2c2ca,0xcae2e2ca,0xcac8c8ca,0xcae8e8ca,0xcacacaca,0xcaeaeaca,0xeac0c0ea,0xeae0e0ea,0xeac2c2ea,0xeae2e2ea,0xeac8c8ea,0xeae8e8ea,0xeacacaea,0xeaeaeaea,0xc0c0c0c0,0xc0f0f0c0,0xc0c3c3c0,0xc0f3f3c0,0xc0ccccc0,0xc0fcfcc0,0xc0cfcfc0,0xc0ffffc0,0xf0c0c0f0,0xf0f0f0f0,0xf0c3c3f0,0xf0f3f3f0,0xf0ccccf0,0xf0fcfcf0,0xf0cfcff0,0xf0fffff0,0xc3c0c0c3,0xc3f0f0c3,0xc3c3c3c3,0xc3f3f3c3,0xc3ccccc3,0xc3fcfcc3,0xc3cfcfc3,0xc3ffffc3,0xf3c0c0f3,0xf3f0f0f3,0xf3c3c3f3,0xf3f3f3f3,0xf3ccccf3,0xf3fcfcf3,0xf3cfcff3,0xf3fffff3,0xccc0c0cc,0xccf0f0cc,0xccc3c3cc,0xccf3f3cc,0xcccccccc,0xccfcfccc,0xcccfcfcc,0xccffffcc,0xfcc0c0fc,0xfcf0f0fc,0xfcc3c3fc,0xfcf3f3fc,0xfcccccfc,0xfcfcfcfc,0xfccfcffc,0xfcfffffc,0xcfc0c0cf,0xcff0f0cf,0xcfc3c3cf,0xcff3f3cf,0xcfcccccf,0xcffcfccf,0xcfcfcfcf,0xcfffffcf,0xffc0c0ff,0xfff0f0ff,0xffc3c3ff,0xfff3f3ff,0xffccccff,0xfffcfcff,0xffcfcfff,0xffffffff,0xc0c0c0c0,0xc0e0e0c0,0xc0c2c2c0,0xc0e2e2c0,0xc0c8c8c0,0xc0e8e8c0,0xc0cacac0,0xc0eaeac0,0xe0c0c0e0,0xe0e0e0e0,0xe0c2c2e0,0xe0e2e2e0,0xe0c8c8e0,0xe0e8e8e0,0xe0cacae0,0xe0eaeae0,0xc2c0c0c2,0xc2e0e0c2,0xc2c2c2c2,0xc2e2e2c2,0xc2c8c8c2,0xc2e8e8c2,0xc2cacac2,0xc2eaeac2,0xe2c0c0e2,0xe2e0e0e2,0xe2c2c2e2,0xe2e2e2e2,0xe2c8c8e2,0xe2e8e8e2,0xe2cacae2,0xe2eaeae2,0xc8c0c0c8,0xc8e0e0c8,0xc8c2c2c8,0xc8e2e2c8,0xc8c8c8c8,0xc8e8e8c8,0xc8cacac8,0xc8eaeac8,0xe8c0c0e8,0xe8e0e0e8,0xe8c2c2e8,0xe8e2e2e8,0xe8c8c8e8,0xe8e8e8e8,0xe8cacae8,0xe8eaeae8,0xcac0c0ca,0xcae0e0ca,0xcac2c2ca,0xcae2e2ca,0xcac8c8ca,0xcae8e8ca,0xcacacaca,0xcaeaeaca,0xeac0c0ea,0xeae0e0ea,0xeac2c2ea,0xeae2e2ea,0xeac8c8ea,0xeae8e8ea,0xeacacaea,0xeaeaeaea,0xc0c0c0c0,0xc0f0f0c0,0xc0c3c3c0,0xc0f3f3c0,0xc0ccccc0,0xc0fcfcc0,0xc0cfcfc0,0xc0ffffc0,0xf0c0c0f0,0xf0f0f0f0,0xf0c3c3f0,0xf0f3f3f0,0xf0ccccf0,0xf0fcfcf0,0xf0cfcff0,0xf0fffff0,0xc3c0c0c3,0xc3f0f0c3,0xc3c3c3c3,0xc3f3f3c3,0xc3ccccc3,0xc3fcfcc3,0xc3cfcfc3,0xc3ffffc3,0xf3c0c0f3,0xf3f0f0f3,0xf3c3c3f3,0xf3f3f3f3,0xf3ccccf3,0xf3fcfcf3,0xf3cfcff3,0xf3fffff3,0xccc0c0cc,0xccf0f0cc,0xccc3c3cc,0xccf3f3cc,0xcccccccc,0xccfcfccc,0xcccfcfcc,0xccffffcc,0xfcc0c0fc,0xfcf0f0fc,0xfcc3c3fc,0xfcf3f3fc,0xfcccccfc,0xfcfcfcfc,0xfccfcffc,0xfcfffffc,0xcfc0c0cf,0xcff0f0cf,0xcfc3c3cf,0xcff3f3cf,0xcfcccccf,0xcffcfccf,0xcfcfcfcf,0xcfffffcf,0xffc0c0ff,0xfff0f0ff,0xffc3c3ff,0xfff3f3ff,0xffccccff,0xfffcfcff,0xffcfcfff,0xffffffff}, +{0xc0c0c0c0,0xc0e0c0e0,0xc0c2c0c2,0xc0e2c0e2,0xc0c8c0c8,0xc0e8c0e8,0xc0cac0ca,0xc0eac0ea,0xe0c0e0c0,0xe0e0e0e0,0xe0c2e0c2,0xe0e2e0e2,0xe0c8e0c8,0xe0e8e0e8,0xe0cae0ca,0xe0eae0ea,0xc2c0c2c0,0xc2e0c2e0,0xc2c2c2c2,0xc2e2c2e2,0xc2c8c2c8,0xc2e8c2e8,0xc2cac2ca,0xc2eac2ea,0xe2c0e2c0,0xe2e0e2e0,0xe2c2e2c2,0xe2e2e2e2,0xe2c8e2c8,0xe2e8e2e8,0xe2cae2ca,0xe2eae2ea,0xc8c0c8c0,0xc8e0c8e0,0xc8c2c8c2,0xc8e2c8e2,0xc8c8c8c8,0xc8e8c8e8,0xc8cac8ca,0xc8eac8ea,0xe8c0e8c0,0xe8e0e8e0,0xe8c2e8c2,0xe8e2e8e2,0xe8c8e8c8,0xe8e8e8e8,0xe8cae8ca,0xe8eae8ea,0xcac0cac0,0xcae0cae0,0xcac2cac2,0xcae2cae2,0xcac8cac8,0xcae8cae8,0xcacacaca,0xcaeacaea,0xeac0eac0,0xeae0eae0,0xeac2eac2,0xeae2eae2,0xeac8eac8,0xeae8eae8,0xeacaeaca,0xeaeaeaea,0xc0c0c0c0,0xc0f0c0f0,0xc0c3c0c3,0xc0f3c0f3,0xc0ccc0cc,0xc0fcc0fc,0xc0cfc0cf,0xc0ffc0ff,0xf0c0f0c0,0xf0f0f0f0,0xf0c3f0c3,0xf0f3f0f3,0xf0ccf0cc,0xf0fcf0fc,0xf0cff0cf,0xf0fff0ff,0xc3c0c3c0,0xc3f0c3f0,0xc3c3c3c3,0xc3f3c3f3,0xc3ccc3cc,0xc3fcc3fc,0xc3cfc3cf,0xc3ffc3ff,0xf3c0f3c0,0xf3f0f3f0,0xf3c3f3c3,0xf3f3f3f3,0xf3ccf3cc,0xf3fcf3fc,0xf3cff3cf,0xf3fff3ff,0xccc0ccc0,0xccf0ccf0,0xccc3ccc3,0xccf3ccf3,0xcccccccc,0xccfcccfc,0xcccfcccf,0xccffccff,0xfcc0fcc0,0xfcf0fcf0,0xfcc3fcc3,0xfcf3fcf3,0xfcccfccc,0xfcfcfcfc,0xfccffccf,0xfcfffcff,0xcfc0cfc0,0xcff0cff0,0xcfc3cfc3,0xcff3cff3,0xcfcccfcc,0xcffccffc,0xcfcfcfcf,0xcfffcfff,0xffc0ffc0,0xfff0fff0,0xffc3ffc3,0xfff3fff3,0xffccffcc,0xfffcfffc,0xffcfffcf,0xffffffff,0xc0c0c0c0,0xc0e0c0e0,0xc0c2c0c2,0xc0e2c0e2,0xc0c8c0c8,0xc0e8c0e8,0xc0cac0ca,0xc0eac0ea,0xe0c0e0c0,0xe0e0e0e0,0xe0c2e0c2,0xe0e2e0e2,0xe0c8e0c8,0xe0e8e0e8,0xe0cae0ca,0xe0eae0ea,0xc2c0c2c0,0xc2e0c2e0,0xc2c2c2c2,0xc2e2c2e2,0xc2c8c2c8,0xc2e8c2e8,0xc2cac2ca,0xc2eac2ea,0xe2c0e2c0,0xe2e0e2e0,0xe2c2e2c2,0xe2e2e2e2,0xe2c8e2c8,0xe2e8e2e8,0xe2cae2ca,0xe2eae2ea,0xc8c0c8c0,0xc8e0c8e0,0xc8c2c8c2,0xc8e2c8e2,0xc8c8c8c8,0xc8e8c8e8,0xc8cac8ca,0xc8eac8ea,0xe8c0e8c0,0xe8e0e8e0,0xe8c2e8c2,0xe8e2e8e2,0xe8c8e8c8,0xe8e8e8e8,0xe8cae8ca,0xe8eae8ea,0xcac0cac0,0xcae0cae0,0xcac2cac2,0xcae2cae2,0xcac8cac8,0xcae8cae8,0xcacacaca,0xcaeacaea,0xeac0eac0,0xeae0eae0,0xeac2eac2,0xeae2eae2,0xeac8eac8,0xeae8eae8,0xeacaeaca,0xeaeaeaea,0xc0c0c0c0,0xc0f0c0f0,0xc0c3c0c3,0xc0f3c0f3,0xc0ccc0cc,0xc0fcc0fc,0xc0cfc0cf,0xc0ffc0ff,0xf0c0f0c0,0xf0f0f0f0,0xf0c3f0c3,0xf0f3f0f3,0xf0ccf0cc,0xf0fcf0fc,0xf0cff0cf,0xf0fff0ff,0xc3c0c3c0,0xc3f0c3f0,0xc3c3c3c3,0xc3f3c3f3,0xc3ccc3cc,0xc3fcc3fc,0xc3cfc3cf,0xc3ffc3ff,0xf3c0f3c0,0xf3f0f3f0,0xf3c3f3c3,0xf3f3f3f3,0xf3ccf3cc,0xf3fcf3fc,0xf3cff3cf,0xf3fff3ff,0xccc0ccc0,0xccf0ccf0,0xccc3ccc3,0xccf3ccf3,0xcccccccc,0xccfcccfc,0xcccfcccf,0xccffccff,0xfcc0fcc0,0xfcf0fcf0,0xfcc3fcc3,0xfcf3fcf3,0xfcccfccc,0xfcfcfcfc,0xfccffccf,0xfcfffcff,0xcfc0cfc0,0xcff0cff0,0xcfc3cfc3,0xcff3cff3,0xcfcccfcc,0xcffccffc,0xcfcfcfcf,0xcfffcfff,0xffc0ffc0,0xfff0fff0,0xffc3ffc3,0xfff3fff3,0xffccffcc,0xfffcfffc,0xffcfffcf,0xffffffff}, +{0xc0c0c0c0,0xc0e0e0e0,0xc0c2c2c2,0xc0e2e2e2,0xc0c8c8c8,0xc0e8e8e8,0xc0cacaca,0xc0eaeaea,0xe0c0c0c0,0xe0e0e0e0,0xe0c2c2c2,0xe0e2e2e2,0xe0c8c8c8,0xe0e8e8e8,0xe0cacaca,0xe0eaeaea,0xc2c0c0c0,0xc2e0e0e0,0xc2c2c2c2,0xc2e2e2e2,0xc2c8c8c8,0xc2e8e8e8,0xc2cacaca,0xc2eaeaea,0xe2c0c0c0,0xe2e0e0e0,0xe2c2c2c2,0xe2e2e2e2,0xe2c8c8c8,0xe2e8e8e8,0xe2cacaca,0xe2eaeaea,0xc8c0c0c0,0xc8e0e0e0,0xc8c2c2c2,0xc8e2e2e2,0xc8c8c8c8,0xc8e8e8e8,0xc8cacaca,0xc8eaeaea,0xe8c0c0c0,0xe8e0e0e0,0xe8c2c2c2,0xe8e2e2e2,0xe8c8c8c8,0xe8e8e8e8,0xe8cacaca,0xe8eaeaea,0xcac0c0c0,0xcae0e0e0,0xcac2c2c2,0xcae2e2e2,0xcac8c8c8,0xcae8e8e8,0xcacacaca,0xcaeaeaea,0xeac0c0c0,0xeae0e0e0,0xeac2c2c2,0xeae2e2e2,0xeac8c8c8,0xeae8e8e8,0xeacacaca,0xeaeaeaea,0xc0c0c0c0,0xc0f0f0f0,0xc0c3c3c3,0xc0f3f3f3,0xc0cccccc,0xc0fcfcfc,0xc0cfcfcf,0xc0ffffff,0xf0c0c0c0,0xf0f0f0f0,0xf0c3c3c3,0xf0f3f3f3,0xf0cccccc,0xf0fcfcfc,0xf0cfcfcf,0xf0ffffff,0xc3c0c0c0,0xc3f0f0f0,0xc3c3c3c3,0xc3f3f3f3,0xc3cccccc,0xc3fcfcfc,0xc3cfcfcf,0xc3ffffff,0xf3c0c0c0,0xf3f0f0f0,0xf3c3c3c3,0xf3f3f3f3,0xf3cccccc,0xf3fcfcfc,0xf3cfcfcf,0xf3ffffff,0xccc0c0c0,0xccf0f0f0,0xccc3c3c3,0xccf3f3f3,0xcccccccc,0xccfcfcfc,0xcccfcfcf,0xccffffff,0xfcc0c0c0,0xfcf0f0f0,0xfcc3c3c3,0xfcf3f3f3,0xfccccccc,0xfcfcfcfc,0xfccfcfcf,0xfcffffff,0xcfc0c0c0,0xcff0f0f0,0xcfc3c3c3,0xcff3f3f3,0xcfcccccc,0xcffcfcfc,0xcfcfcfcf,0xcfffffff,0xffc0c0c0,0xfff0f0f0,0xffc3c3c3,0xfff3f3f3,0xffcccccc,0xfffcfcfc,0xffcfcfcf,0xffffffff,0xc0c0c0c0,0xc0e0e0e0,0xc0c2c2c2,0xc0e2e2e2,0xc0c8c8c8,0xc0e8e8e8,0xc0cacaca,0xc0eaeaea,0xe0c0c0c0,0xe0e0e0e0,0xe0c2c2c2,0xe0e2e2e2,0xe0c8c8c8,0xe0e8e8e8,0xe0cacaca,0xe0eaeaea,0xc2c0c0c0,0xc2e0e0e0,0xc2c2c2c2,0xc2e2e2e2,0xc2c8c8c8,0xc2e8e8e8,0xc2cacaca,0xc2eaeaea,0xe2c0c0c0,0xe2e0e0e0,0xe2c2c2c2,0xe2e2e2e2,0xe2c8c8c8,0xe2e8e8e8,0xe2cacaca,0xe2eaeaea,0xc8c0c0c0,0xc8e0e0e0,0xc8c2c2c2,0xc8e2e2e2,0xc8c8c8c8,0xc8e8e8e8,0xc8cacaca,0xc8eaeaea,0xe8c0c0c0,0xe8e0e0e0,0xe8c2c2c2,0xe8e2e2e2,0xe8c8c8c8,0xe8e8e8e8,0xe8cacaca,0xe8eaeaea,0xcac0c0c0,0xcae0e0e0,0xcac2c2c2,0xcae2e2e2,0xcac8c8c8,0xcae8e8e8,0xcacacaca,0xcaeaeaea,0xeac0c0c0,0xeae0e0e0,0xeac2c2c2,0xeae2e2e2,0xeac8c8c8,0xeae8e8e8,0xeacacaca,0xeaeaeaea,0xc0c0c0c0,0xc0f0f0f0,0xc0c3c3c3,0xc0f3f3f3,0xc0cccccc,0xc0fcfcfc,0xc0cfcfcf,0xc0ffffff,0xf0c0c0c0,0xf0f0f0f0,0xf0c3c3c3,0xf0f3f3f3,0xf0cccccc,0xf0fcfcfc,0xf0cfcfcf,0xf0ffffff,0xc3c0c0c0,0xc3f0f0f0,0xc3c3c3c3,0xc3f3f3f3,0xc3cccccc,0xc3fcfcfc,0xc3cfcfcf,0xc3ffffff,0xf3c0c0c0,0xf3f0f0f0,0xf3c3c3c3,0xf3f3f3f3,0xf3cccccc,0xf3fcfcfc,0xf3cfcfcf,0xf3ffffff,0xccc0c0c0,0xccf0f0f0,0xccc3c3c3,0xccf3f3f3,0xcccccccc,0xccfcfcfc,0xcccfcfcf,0xccffffff,0xfcc0c0c0,0xfcf0f0f0,0xfcc3c3c3,0xfcf3f3f3,0xfccccccc,0xfcfcfcfc,0xfccfcfcf,0xfcffffff,0xcfc0c0c0,0xcff0f0f0,0xcfc3c3c3,0xcff3f3f3,0xcfcccccc,0xcffcfcfc,0xcfcfcfcf,0xcfffffff,0xffc0c0c0,0xfff0f0f0,0xffc3c3c3,0xfff3f3f3,0xffcccccc,0xfffcfcfc,0xffcfcfcf,0xffffffff}, +{0xc0c0c0c0,0xe0e0c0c0,0xc2c2c0c0,0xe2e2c0c0,0xc8c8c0c0,0xe8e8c0c0,0xcacac0c0,0xeaeac0c0,0xc0c0e0e0,0xe0e0e0e0,0xc2c2e0e0,0xe2e2e0e0,0xc8c8e0e0,0xe8e8e0e0,0xcacae0e0,0xeaeae0e0,0xc0c0c2c2,0xe0e0c2c2,0xc2c2c2c2,0xe2e2c2c2,0xc8c8c2c2,0xe8e8c2c2,0xcacac2c2,0xeaeac2c2,0xc0c0e2e2,0xe0e0e2e2,0xc2c2e2e2,0xe2e2e2e2,0xc8c8e2e2,0xe8e8e2e2,0xcacae2e2,0xeaeae2e2,0xc0c0c8c8,0xe0e0c8c8,0xc2c2c8c8,0xe2e2c8c8,0xc8c8c8c8,0xe8e8c8c8,0xcacac8c8,0xeaeac8c8,0xc0c0e8e8,0xe0e0e8e8,0xc2c2e8e8,0xe2e2e8e8,0xc8c8e8e8,0xe8e8e8e8,0xcacae8e8,0xeaeae8e8,0xc0c0caca,0xe0e0caca,0xc2c2caca,0xe2e2caca,0xc8c8caca,0xe8e8caca,0xcacacaca,0xeaeacaca,0xc0c0eaea,0xe0e0eaea,0xc2c2eaea,0xe2e2eaea,0xc8c8eaea,0xe8e8eaea,0xcacaeaea,0xeaeaeaea,0xc0c0c0c0,0xf0f0c0c0,0xc3c3c0c0,0xf3f3c0c0,0xccccc0c0,0xfcfcc0c0,0xcfcfc0c0,0xffffc0c0,0xc0c0f0f0,0xf0f0f0f0,0xc3c3f0f0,0xf3f3f0f0,0xccccf0f0,0xfcfcf0f0,0xcfcff0f0,0xfffff0f0,0xc0c0c3c3,0xf0f0c3c3,0xc3c3c3c3,0xf3f3c3c3,0xccccc3c3,0xfcfcc3c3,0xcfcfc3c3,0xffffc3c3,0xc0c0f3f3,0xf0f0f3f3,0xc3c3f3f3,0xf3f3f3f3,0xccccf3f3,0xfcfcf3f3,0xcfcff3f3,0xfffff3f3,0xc0c0cccc,0xf0f0cccc,0xc3c3cccc,0xf3f3cccc,0xcccccccc,0xfcfccccc,0xcfcfcccc,0xffffcccc,0xc0c0fcfc,0xf0f0fcfc,0xc3c3fcfc,0xf3f3fcfc,0xccccfcfc,0xfcfcfcfc,0xcfcffcfc,0xfffffcfc,0xc0c0cfcf,0xf0f0cfcf,0xc3c3cfcf,0xf3f3cfcf,0xcccccfcf,0xfcfccfcf,0xcfcfcfcf,0xffffcfcf,0xc0c0ffff,0xf0f0ffff,0xc3c3ffff,0xf3f3ffff,0xccccffff,0xfcfcffff,0xcfcfffff,0xffffffff,0xc0c0c0c0,0xe0e0c0c0,0xc2c2c0c0,0xe2e2c0c0,0xc8c8c0c0,0xe8e8c0c0,0xcacac0c0,0xeaeac0c0,0xc0c0e0e0,0xe0e0e0e0,0xc2c2e0e0,0xe2e2e0e0,0xc8c8e0e0,0xe8e8e0e0,0xcacae0e0,0xeaeae0e0,0xc0c0c2c2,0xe0e0c2c2,0xc2c2c2c2,0xe2e2c2c2,0xc8c8c2c2,0xe8e8c2c2,0xcacac2c2,0xeaeac2c2,0xc0c0e2e2,0xe0e0e2e2,0xc2c2e2e2,0xe2e2e2e2,0xc8c8e2e2,0xe8e8e2e2,0xcacae2e2,0xeaeae2e2,0xc0c0c8c8,0xe0e0c8c8,0xc2c2c8c8,0xe2e2c8c8,0xc8c8c8c8,0xe8e8c8c8,0xcacac8c8,0xeaeac8c8,0xc0c0e8e8,0xe0e0e8e8,0xc2c2e8e8,0xe2e2e8e8,0xc8c8e8e8,0xe8e8e8e8,0xcacae8e8,0xeaeae8e8,0xc0c0caca,0xe0e0caca,0xc2c2caca,0xe2e2caca,0xc8c8caca,0xe8e8caca,0xcacacaca,0xeaeacaca,0xc0c0eaea,0xe0e0eaea,0xc2c2eaea,0xe2e2eaea,0xc8c8eaea,0xe8e8eaea,0xcacaeaea,0xeaeaeaea,0xc0c0c0c0,0xf0f0c0c0,0xc3c3c0c0,0xf3f3c0c0,0xccccc0c0,0xfcfcc0c0,0xcfcfc0c0,0xffffc0c0,0xc0c0f0f0,0xf0f0f0f0,0xc3c3f0f0,0xf3f3f0f0,0xccccf0f0,0xfcfcf0f0,0xcfcff0f0,0xfffff0f0,0xc0c0c3c3,0xf0f0c3c3,0xc3c3c3c3,0xf3f3c3c3,0xccccc3c3,0xfcfcc3c3,0xcfcfc3c3,0xffffc3c3,0xc0c0f3f3,0xf0f0f3f3,0xc3c3f3f3,0xf3f3f3f3,0xccccf3f3,0xfcfcf3f3,0xcfcff3f3,0xfffff3f3,0xc0c0cccc,0xf0f0cccc,0xc3c3cccc,0xf3f3cccc,0xcccccccc,0xfcfccccc,0xcfcfcccc,0xffffcccc,0xc0c0fcfc,0xf0f0fcfc,0xc3c3fcfc,0xf3f3fcfc,0xccccfcfc,0xfcfcfcfc,0xcfcffcfc,0xfffffcfc,0xc0c0cfcf,0xf0f0cfcf,0xc3c3cfcf,0xf3f3cfcf,0xcccccfcf,0xfcfccfcf,0xcfcfcfcf,0xffffcfcf,0xc0c0ffff,0xf0f0ffff,0xc3c3ffff,0xf3f3ffff,0xccccffff,0xfcfcffff,0xcfcfffff,0xffffffff}, +{0xc0c0c0c0,0xe0e0e0c0,0xc2c2c2c0,0xe2e2e2c0,0xc8c8c8c0,0xe8e8e8c0,0xcacacac0,0xeaeaeac0,0xc0c0c0e0,0xe0e0e0e0,0xc2c2c2e0,0xe2e2e2e0,0xc8c8c8e0,0xe8e8e8e0,0xcacacae0,0xeaeaeae0,0xc0c0c0c2,0xe0e0e0c2,0xc2c2c2c2,0xe2e2e2c2,0xc8c8c8c2,0xe8e8e8c2,0xcacacac2,0xeaeaeac2,0xc0c0c0e2,0xe0e0e0e2,0xc2c2c2e2,0xe2e2e2e2,0xc8c8c8e2,0xe8e8e8e2,0xcacacae2,0xeaeaeae2,0xc0c0c0c8,0xe0e0e0c8,0xc2c2c2c8,0xe2e2e2c8,0xc8c8c8c8,0xe8e8e8c8,0xcacacac8,0xeaeaeac8,0xc0c0c0e8,0xe0e0e0e8,0xc2c2c2e8,0xe2e2e2e8,0xc8c8c8e8,0xe8e8e8e8,0xcacacae8,0xeaeaeae8,0xc0c0c0ca,0xe0e0e0ca,0xc2c2c2ca,0xe2e2e2ca,0xc8c8c8ca,0xe8e8e8ca,0xcacacaca,0xeaeaeaca,0xc0c0c0ea,0xe0e0e0ea,0xc2c2c2ea,0xe2e2e2ea,0xc8c8c8ea,0xe8e8e8ea,0xcacacaea,0xeaeaeaea,0xc0c0c0c0,0xf0f0f0c0,0xc3c3c3c0,0xf3f3f3c0,0xccccccc0,0xfcfcfcc0,0xcfcfcfc0,0xffffffc0,0xc0c0c0f0,0xf0f0f0f0,0xc3c3c3f0,0xf3f3f3f0,0xccccccf0,0xfcfcfcf0,0xcfcfcff0,0xfffffff0,0xc0c0c0c3,0xf0f0f0c3,0xc3c3c3c3,0xf3f3f3c3,0xccccccc3,0xfcfcfcc3,0xcfcfcfc3,0xffffffc3,0xc0c0c0f3,0xf0f0f0f3,0xc3c3c3f3,0xf3f3f3f3,0xccccccf3,0xfcfcfcf3,0xcfcfcff3,0xfffffff3,0xc0c0c0cc,0xf0f0f0cc,0xc3c3c3cc,0xf3f3f3cc,0xcccccccc,0xfcfcfccc,0xcfcfcfcc,0xffffffcc,0xc0c0c0fc,0xf0f0f0fc,0xc3c3c3fc,0xf3f3f3fc,0xccccccfc,0xfcfcfcfc,0xcfcfcffc,0xfffffffc,0xc0c0c0cf,0xf0f0f0cf,0xc3c3c3cf,0xf3f3f3cf,0xcccccccf,0xfcfcfccf,0xcfcfcfcf,0xffffffcf,0xc0c0c0ff,0xf0f0f0ff,0xc3c3c3ff,0xf3f3f3ff,0xccccccff,0xfcfcfcff,0xcfcfcfff,0xffffffff,0xc0c0c0c0,0xe0e0e0c0,0xc2c2c2c0,0xe2e2e2c0,0xc8c8c8c0,0xe8e8e8c0,0xcacacac0,0xeaeaeac0,0xc0c0c0e0,0xe0e0e0e0,0xc2c2c2e0,0xe2e2e2e0,0xc8c8c8e0,0xe8e8e8e0,0xcacacae0,0xeaeaeae0,0xc0c0c0c2,0xe0e0e0c2,0xc2c2c2c2,0xe2e2e2c2,0xc8c8c8c2,0xe8e8e8c2,0xcacacac2,0xeaeaeac2,0xc0c0c0e2,0xe0e0e0e2,0xc2c2c2e2,0xe2e2e2e2,0xc8c8c8e2,0xe8e8e8e2,0xcacacae2,0xeaeaeae2,0xc0c0c0c8,0xe0e0e0c8,0xc2c2c2c8,0xe2e2e2c8,0xc8c8c8c8,0xe8e8e8c8,0xcacacac8,0xeaeaeac8,0xc0c0c0e8,0xe0e0e0e8,0xc2c2c2e8,0xe2e2e2e8,0xc8c8c8e8,0xe8e8e8e8,0xcacacae8,0xeaeaeae8,0xc0c0c0ca,0xe0e0e0ca,0xc2c2c2ca,0xe2e2e2ca,0xc8c8c8ca,0xe8e8e8ca,0xcacacaca,0xeaeaeaca,0xc0c0c0ea,0xe0e0e0ea,0xc2c2c2ea,0xe2e2e2ea,0xc8c8c8ea,0xe8e8e8ea,0xcacacaea,0xeaeaeaea,0xc0c0c0c0,0xf0f0f0c0,0xc3c3c3c0,0xf3f3f3c0,0xccccccc0,0xfcfcfcc0,0xcfcfcfc0,0xffffffc0,0xc0c0c0f0,0xf0f0f0f0,0xc3c3c3f0,0xf3f3f3f0,0xccccccf0,0xfcfcfcf0,0xcfcfcff0,0xfffffff0,0xc0c0c0c3,0xf0f0f0c3,0xc3c3c3c3,0xf3f3f3c3,0xccccccc3,0xfcfcfcc3,0xcfcfcfc3,0xffffffc3,0xc0c0c0f3,0xf0f0f0f3,0xc3c3c3f3,0xf3f3f3f3,0xccccccf3,0xfcfcfcf3,0xcfcfcff3,0xfffffff3,0xc0c0c0cc,0xf0f0f0cc,0xc3c3c3cc,0xf3f3f3cc,0xcccccccc,0xfcfcfccc,0xcfcfcfcc,0xffffffcc,0xc0c0c0fc,0xf0f0f0fc,0xc3c3c3fc,0xf3f3f3fc,0xccccccfc,0xfcfcfcfc,0xcfcfcffc,0xfffffffc,0xc0c0c0cf,0xf0f0f0cf,0xc3c3c3cf,0xf3f3f3cf,0xcccccccf,0xfcfcfccf,0xcfcfcfcf,0xffffffcf,0xc0c0c0ff,0xf0f0f0ff,0xc3c3c3ff,0xf3f3f3ff,0xccccccff,0xfcfcfcff,0xcfcfcfff,0xffffffff}, +{0xc0c0c0c0,0xe0e0c0e0,0xc2c2c0c2,0xe2e2c0e2,0xc8c8c0c8,0xe8e8c0e8,0xcacac0ca,0xeaeac0ea,0xc0c0e0c0,0xe0e0e0e0,0xc2c2e0c2,0xe2e2e0e2,0xc8c8e0c8,0xe8e8e0e8,0xcacae0ca,0xeaeae0ea,0xc0c0c2c0,0xe0e0c2e0,0xc2c2c2c2,0xe2e2c2e2,0xc8c8c2c8,0xe8e8c2e8,0xcacac2ca,0xeaeac2ea,0xc0c0e2c0,0xe0e0e2e0,0xc2c2e2c2,0xe2e2e2e2,0xc8c8e2c8,0xe8e8e2e8,0xcacae2ca,0xeaeae2ea,0xc0c0c8c0,0xe0e0c8e0,0xc2c2c8c2,0xe2e2c8e2,0xc8c8c8c8,0xe8e8c8e8,0xcacac8ca,0xeaeac8ea,0xc0c0e8c0,0xe0e0e8e0,0xc2c2e8c2,0xe2e2e8e2,0xc8c8e8c8,0xe8e8e8e8,0xcacae8ca,0xeaeae8ea,0xc0c0cac0,0xe0e0cae0,0xc2c2cac2,0xe2e2cae2,0xc8c8cac8,0xe8e8cae8,0xcacacaca,0xeaeacaea,0xc0c0eac0,0xe0e0eae0,0xc2c2eac2,0xe2e2eae2,0xc8c8eac8,0xe8e8eae8,0xcacaeaca,0xeaeaeaea,0xc0c0c0c0,0xf0f0c0f0,0xc3c3c0c3,0xf3f3c0f3,0xccccc0cc,0xfcfcc0fc,0xcfcfc0cf,0xffffc0ff,0xc0c0f0c0,0xf0f0f0f0,0xc3c3f0c3,0xf3f3f0f3,0xccccf0cc,0xfcfcf0fc,0xcfcff0cf,0xfffff0ff,0xc0c0c3c0,0xf0f0c3f0,0xc3c3c3c3,0xf3f3c3f3,0xccccc3cc,0xfcfcc3fc,0xcfcfc3cf,0xffffc3ff,0xc0c0f3c0,0xf0f0f3f0,0xc3c3f3c3,0xf3f3f3f3,0xccccf3cc,0xfcfcf3fc,0xcfcff3cf,0xfffff3ff,0xc0c0ccc0,0xf0f0ccf0,0xc3c3ccc3,0xf3f3ccf3,0xcccccccc,0xfcfcccfc,0xcfcfcccf,0xffffccff,0xc0c0fcc0,0xf0f0fcf0,0xc3c3fcc3,0xf3f3fcf3,0xccccfccc,0xfcfcfcfc,0xcfcffccf,0xfffffcff,0xc0c0cfc0,0xf0f0cff0,0xc3c3cfc3,0xf3f3cff3,0xcccccfcc,0xfcfccffc,0xcfcfcfcf,0xffffcfff,0xc0c0ffc0,0xf0f0fff0,0xc3c3ffc3,0xf3f3fff3,0xccccffcc,0xfcfcfffc,0xcfcfffcf,0xffffffff,0xc0c0c0c0,0xe0e0c0e0,0xc2c2c0c2,0xe2e2c0e2,0xc8c8c0c8,0xe8e8c0e8,0xcacac0ca,0xeaeac0ea,0xc0c0e0c0,0xe0e0e0e0,0xc2c2e0c2,0xe2e2e0e2,0xc8c8e0c8,0xe8e8e0e8,0xcacae0ca,0xeaeae0ea,0xc0c0c2c0,0xe0e0c2e0,0xc2c2c2c2,0xe2e2c2e2,0xc8c8c2c8,0xe8e8c2e8,0xcacac2ca,0xeaeac2ea,0xc0c0e2c0,0xe0e0e2e0,0xc2c2e2c2,0xe2e2e2e2,0xc8c8e2c8,0xe8e8e2e8,0xcacae2ca,0xeaeae2ea,0xc0c0c8c0,0xe0e0c8e0,0xc2c2c8c2,0xe2e2c8e2,0xc8c8c8c8,0xe8e8c8e8,0xcacac8ca,0xeaeac8ea,0xc0c0e8c0,0xe0e0e8e0,0xc2c2e8c2,0xe2e2e8e2,0xc8c8e8c8,0xe8e8e8e8,0xcacae8ca,0xeaeae8ea,0xc0c0cac0,0xe0e0cae0,0xc2c2cac2,0xe2e2cae2,0xc8c8cac8,0xe8e8cae8,0xcacacaca,0xeaeacaea,0xc0c0eac0,0xe0e0eae0,0xc2c2eac2,0xe2e2eae2,0xc8c8eac8,0xe8e8eae8,0xcacaeaca,0xeaeaeaea,0xc0c0c0c0,0xf0f0c0f0,0xc3c3c0c3,0xf3f3c0f3,0xccccc0cc,0xfcfcc0fc,0xcfcfc0cf,0xffffc0ff,0xc0c0f0c0,0xf0f0f0f0,0xc3c3f0c3,0xf3f3f0f3,0xccccf0cc,0xfcfcf0fc,0xcfcff0cf,0xfffff0ff,0xc0c0c3c0,0xf0f0c3f0,0xc3c3c3c3,0xf3f3c3f3,0xccccc3cc,0xfcfcc3fc,0xcfcfc3cf,0xffffc3ff,0xc0c0f3c0,0xf0f0f3f0,0xc3c3f3c3,0xf3f3f3f3,0xccccf3cc,0xfcfcf3fc,0xcfcff3cf,0xfffff3ff,0xc0c0ccc0,0xf0f0ccf0,0xc3c3ccc3,0xf3f3ccf3,0xcccccccc,0xfcfcccfc,0xcfcfcccf,0xffffccff,0xc0c0fcc0,0xf0f0fcf0,0xc3c3fcc3,0xf3f3fcf3,0xccccfccc,0xfcfcfcfc,0xcfcffccf,0xfffffcff,0xc0c0cfc0,0xf0f0cff0,0xc3c3cfc3,0xf3f3cff3,0xcccccfcc,0xfcfccffc,0xcfcfcfcf,0xffffcfff,0xc0c0ffc0,0xf0f0fff0,0xc3c3ffc3,0xf3f3fff3,0xccccffcc,0xfcfcfffc,0xcfcfffcf,0xffffffff}, +{0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff} +}; + +// void precalcAluBytes() { + +// uint16_t specfast_colors[128]; // Array for faster color calc in Draw + +// unsigned int pal[2],b0,b1,b2,b3; + +// // Calc array for faster color calcs in Draw +// for (int i = 0; i < (NUM_SPECTRUM_COLORS >> 1); i++) { +// // Normal +// specfast_colors[i] = spectrum_colors[i]; +// specfast_colors[i << 3] = spectrum_colors[i]; +// // Bright +// specfast_colors[i | 0x40] = spectrum_colors[i + (NUM_SPECTRUM_COLORS >> 1)]; +// specfast_colors[(i << 3) | 0x40] = spectrum_colors[i + (NUM_SPECTRUM_COLORS >> 1)]; +// } - // Alloc ALUbytes - for (int i = 0; i < 16; i++) { - AluBytes[i] = (uint32_t *) heap_caps_malloc(0x400,MALLOC_CAP_INTERNAL | MALLOC_CAP_32BIT); - } +// // Alloc ALUbytes +// for (int i = 0; i < 16; i++) { +// AluBytes[i] = (uint32_t *) heap_caps_malloc(0x400, MALLOC_CAP_INTERNAL | MALLOC_CAP_32BIT); +// } - for (int i = 0; i < 16; i++) { - for (int n = 0; n < 256; n++) { - pal[0] = specfast_colors[n & 0x78]; - pal[1] = specfast_colors[n & 0x47]; - b0 = pal[(i >> 3) & 0x01]; - b1 = pal[(i >> 2) & 0x01]; - b2 = pal[(i >> 1) & 0x01]; - b3 = pal[i & 0x01]; - AluBytes[i][n]=b2 | (b3<<8) | (b0<<16) | (b1<<24); - } - } +// for (int i = 0; i < 16; i++) { +// for (int n = 0; n < 256; n++) { +// pal[0] = specfast_colors[n & 0x78]; +// pal[1] = specfast_colors[n & 0x47]; +// b0 = pal[(i >> 3) & 0x01]; +// b1 = pal[(i >> 2) & 0x01]; +// b2 = pal[(i >> 1) & 0x01]; +// b3 = pal[i & 0x01]; +// AluBytes[i][n]=b2 | (b3<<8) | (b0<<16) | (b1<<24); +// } +// } -} +// } uint16_t zxColor(uint8_t color, uint8_t bright) { if (bright) color += 8; @@ -264,7 +265,7 @@ void VIDEO::Init() { precalcColors(); // precalculate colors for current VGA mode - precalcAluBytes(); // Alloc and calc AluBytes + // precalcAluBytes(); // Alloc and calc AluBytes precalcULASWAP(); // precalculate ULA SWAP values @@ -613,6 +614,8 @@ void VIDEO::MainScreen(unsigned int statestoadd, bool contended) { int loopCount = statestoadd >> 2; + if (loopCount == 0) return; + if (coldraw_cnt + loopCount > 35) { for (;;) { @@ -665,6 +668,8 @@ void VIDEO::MainScreen_Pentagon(unsigned int statestoadd, bool contended) { int loopCount = statestoadd >> 2; + if (loopCount == 0) return; + if (coldraw_cnt + loopCount > 35) { for (;;) { diff --git a/src/Z80_JLS.cpp b/src/Z80_JLS.cpp index 2dbb9484..6f1df33d 100644 --- a/src/Z80_JLS.cpp +++ b/src/Z80_JLS.cpp @@ -5681,10 +5681,15 @@ void Z80::decodeED(void) { } case 0x41: { /* OUT (C),B */ - REG_WZ = REG_BC; - Ports::output(REG_WZ, REG_B); - REG_WZ++; - break; + // REG_WZ = REG_BC; + // Ports::output(REG_WZ, REG_B); + // REG_WZ++; + + Ports::output(REG_BC, REG_B); + REG_WZ = REG_BC + 1; + + return; + // break; } case 0x42: { /* SBC HL,BC */ @@ -5757,10 +5762,16 @@ void Z80::decodeED(void) { } case 0x49: { /* OUT (C),C */ - REG_WZ = REG_BC; - Ports::output(REG_WZ, REG_C); - REG_WZ++; - break; + // REG_WZ = REG_BC; + // Ports::output(REG_WZ, REG_C); + // REG_WZ++; + + Ports::output(REG_BC, REG_C); + REG_WZ = REG_BC + 1; + + return; + // break; + } case 0x4A: { /* ADC HL,BC */ @@ -5797,9 +5808,14 @@ void Z80::decodeED(void) { } case 0x51: { /* OUT (C),D */ - REG_WZ = REG_BC; - Ports::output(REG_WZ++, REG_D); - break; + // REG_WZ = REG_BC; + // Ports::output(REG_WZ++, REG_D); + + Ports::output(REG_BC, REG_D); + REG_WZ = REG_BC + 1; + + return; + // break; } case 0x52: { /* SBC HL,DE */ @@ -5841,9 +5857,14 @@ void Z80::decodeED(void) { } case 0x59: { /* OUT (C),E */ - REG_WZ = REG_BC; - Ports::output(REG_WZ++, REG_E); - break; + // REG_WZ = REG_BC; + // Ports::output(REG_WZ++, REG_E); + + Ports::output(REG_BC, REG_E); + REG_WZ = REG_BC + 1; + + return; + // break; } case 0x5A: { /* ADC HL,DE */ @@ -5885,9 +5906,14 @@ void Z80::decodeED(void) { } case 0x61: { /* OUT (C),H */ - REG_WZ = REG_BC; - Ports::output(REG_WZ++, REG_H); - break; + // REG_WZ = REG_BC; + // Ports::output(REG_WZ++, REG_H); + + Ports::output(REG_BC, REG_H); + REG_WZ = REG_BC + 1; + + return; + // break; } case 0x62: { /* SBC HL,HL */ @@ -5930,9 +5956,14 @@ void Z80::decodeED(void) { } case 0x69: { /* OUT (C),L */ - REG_WZ = REG_BC; - Ports::output(REG_WZ++, REG_L); - break; + // REG_WZ = REG_BC; + // Ports::output(REG_WZ++, REG_L); + + Ports::output(REG_BC, REG_L); + REG_WZ = REG_BC + 1; + + return; + // break; } case 0x6A: { /* ADC HL,HL */ @@ -5975,9 +6006,14 @@ void Z80::decodeED(void) { } case 0x71: { /* OUT (C),0 */ - REG_WZ = REG_BC; - Ports::output(REG_WZ++, 0x00); - break; + // REG_WZ = REG_BC; + // Ports::output(REG_WZ++, 0x00); + + Ports::output(REG_BC, 0x00); + REG_WZ = REG_BC + 1; + + return; + // break; } case 0x72: { /* SBC HL,SP */ @@ -6002,9 +6038,14 @@ void Z80::decodeED(void) { } case 0x79: { /* OUT (C),A */ - REG_WZ = REG_BC; - Ports::output(REG_WZ++, regA); - break; + // REG_WZ = REG_BC; + // Ports::output(REG_WZ++, regA); + + Ports::output(REG_BC, regA); + REG_WZ = REG_BC + 1; + + return; + // break; } case 0x7A: { /* ADC HL,SP */ From e6856933fc4d53718b9581c2a7d7edd4241b800f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Iborra?= Date: Wed, 29 Nov 2023 18:21:56 +0100 Subject: [PATCH 06/30] Dev version number upgrade --- include/messages.h | 2 +- src/Ports.cpp | 252 --------------------------------------------- 2 files changed, 1 insertion(+), 253 deletions(-) diff --git a/include/messages.h b/include/messages.h index 5b872721..24cb6a83 100644 --- a/include/messages.h +++ b/include/messages.h @@ -42,7 +42,7 @@ visit https://zxespectrum.speccy.org/contacto #define MSG_SAVE_CONFIG "Saving config file" #define MSG_VGA_INIT "Initializing VGA" // #define EMU_VERSION " v1.0 " -#define EMU_VERSION " Dev 221123 " +#define EMU_VERSION " Dev 281123 " // Error #define ERROR_TITLE " !!! ERROR - CLIVE MEDITATION !!! " diff --git a/src/Ports.cpp b/src/Ports.cpp index 8355ef6e..aa878a08 100644 --- a/src/Ports.cpp +++ b/src/Ports.cpp @@ -61,258 +61,6 @@ uint8_t Ports::speaker_values[8]={ 0, 19, 34, 53, 97, 101, 130, 134 }; DRAM_ATTR uint8_t Ports::port[128]; uint8_t Ports::port254 = 0; -// IRAM_ATTR uint8_t Ports::input(uint16_t address) { - -// uint8_t data; -// uint8_t rambank = address >> 14; - -// // ** I/O Contention (Early) ************************* -// VIDEO::Draw(1, MemESP::ramContended[rambank]); -// // ** I/O Contention (Early) ************************* - -// // ** I/O Contention (Late) ************************** -// if ((address & 0x0001) == 0) { -// VIDEO::Draw(3, true); -// } else { -// if (MemESP::ramContended[rambank]) { -// VIDEO::Draw(1, true); -// VIDEO::Draw(1, true); -// VIDEO::Draw(1, true); -// } else VIDEO::Draw(3, false); -// } -// // ** I/O Contention (Late) ************************** - -// // ULA PORT -// if ((address & 0x0001) == 0) { - -// // The default port value is 0xBF. -// data = 0xbf; - -// uint8_t portHigh = ~(address >> 8) & 0xff; -// for (int row = 0, mask = 0x01; row < 8; row++, mask <<= 1) { -// if ((portHigh & mask) != 0) { -// data &= port[row]; -// } -// } - -// if (Tape::tapeStatus==TAPE_LOADING) { -// Tape::TAP_Read(); -// bitWrite(data,6,Tape::tapeEarBit); -// } else { -// // Issue 2 behaviour only on Spectrum 48K -// if ((Z80Ops::is48) && (Config::Issue2)) { -// if (port254 & 0x18) data |= 0x40; -// } else { -// if (port254 & 0x10) data |= 0x40; -// } -// } - -// } else { - -// // The default port value is 0xFF. -// data = 0xff; - -// // Check if TRDOS Rom is mapped. -// if (ESPectrum::trdos) { - -// int lowByte = address & 0xFF; - -// // Process Beta Disk instruction. -// if (lowByte & 0x80) { -// data = ESPectrum::Betadisk.ReadSystemReg(); -// // printf("WD1793 Read Control Register: %d\n",(int)data); -// return data; -// } - -// switch (lowByte) { -// case 0x1F: -// data = ESPectrum::Betadisk.ReadStatusReg(); -// // printf("WD1793 Read Status Register: %d\n",(int)data); -// return data; -// case 0x3F: -// data = ESPectrum::Betadisk.ReadTrackReg(); -// // printf("WD1793 Read Track Register: %d\n",(int)data); -// return data; -// case 0x5F: -// data = ESPectrum::Betadisk.ReadSectorReg(); -// // printf("WD1793 Read Sector Register: %d\n",(int)data); -// return data; -// case 0x7F: -// data = ESPectrum::Betadisk.ReadDataReg(); -// // printf("WD1793 Read Data Register: %d\n",(int)data); -// return data; -// } - -// } - -// // Kempston Joystick -// if ((Config::joystick) && ((address & 0x00E0) == 0 || (address & 0xFF) == 0xDF)) return port[0x1f]; - -// // Sound (AY-3-8912) -// if (ESPectrum::AY_emu) { -// if ((address & 0xC002) == 0xC000) -// return AySound::getRegisterData(); -// } - -// if (!Z80Ops::isPentagon) { - -// data = VIDEO::getFloatBusData(); - -// if ((!Z80Ops::is48) && ((address & 0x8002) == 0)) { - -// // // Solo en el modelo 128K, pero no en los +2/+2A/+3, si se lee el puerto -// // // 0x7ffd, el valor leído es reescrito en el puerto 0x7ffd. -// // // http://www.speccy.org/foro/viewtopic.php?f=8&t=2374 -// if (!MemESP::pagingLock) { -// MemESP::pagingLock = bitRead(data, 5); -// MemESP::bankLatch = data & 0x7; -// MemESP::ramCurrent[3] = (unsigned char *)MemESP::ram[MemESP::bankLatch]; -// MemESP::ramContended[3] = MemESP::bankLatch & 0x01 ? true: false; -// if (MemESP::videoLatch != bitRead(data, 3)) { -// MemESP::videoLatch = bitRead(data, 3); -// // This, if not using the ptime128 draw version, fixs ptime and ptime128 -// if (((address & 0x0001) != 0) && (MemESP::ramContended[rambank])) { -// VIDEO::Draw(2, false); -// CPU::tstates -= 2; -// } -// VIDEO::grmem = MemESP::videoLatch ? MemESP::ram7 : MemESP::ram5; -// } -// MemESP::romLatch = bitRead(data, 4); -// bitWrite(MemESP::romInUse, 0, MemESP::romLatch); -// MemESP::ramCurrent[0] = (unsigned char *)MemESP::rom[MemESP::romInUse]; -// } - -// } - -// } - -// } - -// return data; - -// } - -// IRAM_ATTR void Ports::output(uint16_t address, uint8_t data) { - -// int Audiobit; -// uint8_t rambank = address >> 14; - -// // ** I/O Contention (Early) ************************* -// VIDEO::Draw(1, MemESP::ramContended[rambank]); -// // ** I/O Contention (Early) ************************* - -// // ULA ======================================================================= -// if ((address & 0x0001) == 0) { - -// port254 = data; - -// // Border color -// if (VIDEO::borderColor != data & 0x07) { -// VIDEO::borderColor = data & 0x07; -// if (!Z80Ops::isPentagon) VIDEO::Draw(0,true); // Seems not needed in Pentagon -// VIDEO::brd = VIDEO::border32[VIDEO::borderColor]; -// } - -// // Beeper Audio -// Audiobit = speaker_values[((data >> 2) & 0x04 ) | (Tape::tapeEarBit << 1) | ((data >> 3) & 0x01)]; -// if (Audiobit != ESPectrum::lastaudioBit) { -// ESPectrum::BeeperGetSample(Audiobit); -// ESPectrum::lastaudioBit = Audiobit; -// } - -// } - -// // AY ======================================================================== -// if ((ESPectrum::AY_emu) && ((address & 0x8002) == 0x8000)) { -// if ((address & 0x4000) != 0) -// AySound::selectRegister(data); -// else { -// ESPectrum::AYGetSample(); -// AySound::setRegisterData(data); -// } -// } - -// // Check if TRDOS Rom is mapped. -// if (ESPectrum::trdos) { - -// int lowByte = address & 0xFF; - -// // Process Beta Disk instruction. -// if (lowByte & 0x80) { -// // printf("WD1793 Write Control Register: %d\n",data); -// ESPectrum::Betadisk.WriteSystemReg(data); -// } else -// switch (lowByte) { -// case 0x1F: -// // printf("WD1793 Write Command Register: %d\n",data); -// ESPectrum::Betadisk.WriteCommandReg(data); -// break; -// case 0x3F: -// // printf("WD1793 Write Track Register: %d\n",data); -// ESPectrum::Betadisk.WriteTrackReg(data); -// break; -// case 0x5F: -// // printf("WD1793 Write Sector Register: %d\n",data); -// ESPectrum::Betadisk.WriteSectorReg(data); -// break; -// case 0x7F: -// // printf("WD1793 Write Data Register: %d\n",data); -// ESPectrum::Betadisk.WriteDataReg(data); -// break; -// } - -// } - -// // ** I/O Contention (Late) ************************** -// if ((address & 0x0001) == 0) { -// VIDEO::Draw(3, true); -// } else { -// if (MemESP::ramContended[rambank]) { -// VIDEO::Draw(1, true); -// VIDEO::Draw(1, true); -// VIDEO::Draw(1, true); -// } else { -// VIDEO::Draw(3, false); -// } -// } -// // ** I/O Contention (Late) ************************** - -// // 128 / PENTAGON ================================================================== -// if ((!Z80Ops::is48) && ((address & 0x8002) == 0)) { - -// if (!MemESP::pagingLock) { - -// MemESP::pagingLock = bitRead(data, 5); - -// MemESP::bankLatch = data & 0x7; -// MemESP::ramCurrent[3] = (unsigned char *)MemESP::ram[MemESP::bankLatch]; -// MemESP::ramContended[3] = Z80Ops::isPentagon ? false : (MemESP::bankLatch & 0x01 ? true: false); - -// MemESP::romLatch = bitRead(data, 4); -// bitWrite(MemESP::romInUse, 0, MemESP::romLatch); -// MemESP::ramCurrent[0] = (unsigned char *)MemESP::rom[MemESP::romInUse]; - -// if (MemESP::videoLatch != bitRead(data, 3)) { -// MemESP::videoLatch = bitRead(data, 3); - -// // Seems not needed in Pentagon -// // This, if not using the ptime128 draw version, fixs ptime and ptime128 -// if (!Z80Ops::isPentagon) { -// if (((address & 0x0001) != 0) && (MemESP::ramContended[rambank])) { -// VIDEO::Draw(2, false); -// CPU::tstates -= 2; -// } -// } - -// VIDEO::grmem = MemESP::videoLatch ? MemESP::ram7 : MemESP::ram5; -// } - -// } - -// } - -// } - IRAM_ATTR uint8_t Ports::input(uint16_t address) { uint8_t data; From 0dbaaea8763765e307157d2b09cc79001f584209 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Iborra?= Date: Thu, 30 Nov 2023 10:10:21 +0100 Subject: [PATCH 07/30] Pause Kbd fix --- src/OSDMain.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/OSDMain.cpp b/src/OSDMain.cpp index e5037f0a..2ecb81cf 100644 --- a/src/OSDMain.cpp +++ b/src/OSDMain.cpp @@ -291,8 +291,8 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL) { osdCenteredMsg(OSD_PAUSE[Config::lang], LEVEL_INFO, 1000); while (1) { - - ZXKbdRead(); + + if (ZXKeyb::Exists) ZXKbdRead(); ESPectrum::readKbdJoy(); @@ -1110,6 +1110,9 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL) { } } } else if (options_num == 7) { + + // msgDialog("¿Flashear firmware.bin?","",0); + // Open firmware file FILE *firmware = fopen("/sd/firmware.bin", "rb"); if (firmware == NULL) { @@ -1141,7 +1144,7 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL) { while (1) { - ZXKbdRead(); + if (ZXKeyb::Exists) ZXKbdRead(); ESPectrum::readKbdJoy(); @@ -1245,7 +1248,7 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL) { VIDEO::vga.fillRect(pos_x + ((osdCol + 1) * 6), pos_y + (osdRow * 8), 6,8, cursorCol ); - ZXKbdRead(); + if (ZXKeyb::Exists) ZXKbdRead(); ESPectrum::readKbdJoy(); @@ -1515,7 +1518,7 @@ void OSD::HWInfo() { // Wait for key while (1) { - ZXKbdRead(); + if (ZXKeyb::Exists) ZXKbdRead(); ESPectrum::readKbdJoy(); From adc64151a41f389853ee11e546d35f5e5066b55e Mon Sep 17 00:00:00 2001 From: redcode Date: Sat, 2 Dec 2023 08:36:24 +0100 Subject: [PATCH 08/30] Add accurate emulation of WZ according to the latest discoveries --- src/Z80_JLS.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Z80_JLS.cpp b/src/Z80_JLS.cpp index a3184cb9..85a5e042 100644 --- a/src/Z80_JLS.cpp +++ b/src/Z80_JLS.cpp @@ -6071,6 +6071,7 @@ void Z80::decodeED(void) { ini(); if (REG_B != 0) { REG_PC = REG_PC - 2; + REG_WZ = REG_PC + 1; Z80Ops::addressOnBus(REG_HL - 1, 5); SetAbortedINxR_OTxRFlags(); } @@ -6081,6 +6082,7 @@ void Z80::decodeED(void) { outi(); if (REG_B != 0) { REG_PC = REG_PC - 2; + REG_WZ = REG_PC + 1; Z80Ops::addressOnBus(REG_BC, 5); SetAbortedINxR_OTxRFlags(); } @@ -6116,6 +6118,7 @@ void Z80::decodeED(void) { ind(); if (REG_B != 0) { REG_PC = REG_PC - 2; + REG_WZ = REG_PC + 1; Z80Ops::addressOnBus(REG_HL + 1, 5); SetAbortedINxR_OTxRFlags(); } @@ -6126,6 +6129,7 @@ void Z80::decodeED(void) { outd(); if (REG_B != 0) { REG_PC = REG_PC - 2; + REG_WZ = REG_PC + 1; Z80Ops::addressOnBus(REG_BC, 5); SetAbortedINxR_OTxRFlags(); } From c77ee8e80b5f7c13d8213bf1e440800e12e692ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Iborra?= Date: Sat, 2 Dec 2023 10:58:32 +0100 Subject: [PATCH 09/30] git ignore update --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 7c1cec80..574ad981 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ .pio .vscode .vscode/** +.VSCodeCounter +.VSCodeCounter/** data/s/** !data/s/sppong.sna !data/s/fantasy.sna @@ -16,3 +18,4 @@ downloaded_fs*.bin /doc/** /releases /releases/** + From a048a8e760504bf0bdc2d3e8903710e4228a7855 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Iborra?= Date: Mon, 4 Dec 2023 06:24:56 +0100 Subject: [PATCH 10/30] 16:9 video fix --- include/Video.h | 4 +- src/ESP32Lib/VGA/VGA.cpp | 4 +- src/Video.cpp | 104 +++++++++++++++++++-------------------- 3 files changed, 56 insertions(+), 56 deletions(-) diff --git a/include/Video.h b/include/Video.h index e170cff9..6b5e4397 100644 --- a/include/Video.h +++ b/include/Video.h @@ -181,7 +181,7 @@ static uint16_t spectrum_colors[NUM_SPECTRUM_COLORS] = { BRI_BLACK, BRI_BLUE, BRI_RED, BRI_MAGENTA, BRI_GREEN, BRI_CYAN, BRI_YELLOW, BRI_WHITE, }; -// static uint32_t* AluBytes[16]; +static uint32_t* AluBytes[16]; // static unsigned char DrawStatus; @@ -199,6 +199,6 @@ static unsigned int video_rest; static unsigned int bmpOffset; // offset for bitmap in graphic memory static unsigned int attOffset; // offset for attrib in graphic memory -// void precalcAluBytes(); +void precalcAluBytes(); #endif // VIDEO_h diff --git a/src/ESP32Lib/VGA/VGA.cpp b/src/ESP32Lib/VGA/VGA.cpp index 74c5f3fd..01c2ee08 100644 --- a/src/ESP32Lib/VGA/VGA.cpp +++ b/src/ESP32Lib/VGA/VGA.cpp @@ -21,8 +21,8 @@ const Mode VGA::MODE320x240(8, 48, 24, 320, 10, 2, 33, 480, 2, 12587500, 1, 1, 41,84,7,7,0,0,6,6); // 31469 / 59.94 INDUSTRY STANDARD // const Mode VGA::MODE360x200(9, 54, 27, 360, 12, 2, 35, 400, 2, 14160000, 1, 0, 121,233,5,5,0,0,6,5); // 31467 / 70.08 TAKEN FROM FABGL -const Mode VGA::MODE360x200(9, 54, 27, 360, 12, 2, 35, 400, 2, 14163704, 1, 0, 186,84,7,6,0,0,6,5); // 31475 / 70.1 - +// const Mode VGA::MODE360x200(9, 54, 27, 360, 12, 2, 35, 400, 2, 14163704, 1, 0, 186,84,7,6,0,0,6,5); // 31475 / 70.1 +const Mode VGA::MODE360x200(9, 54, 27, 360, 12, 2, 35, 400, 2, 14161078, 1, 0, 48,84,7,6,0,0,6,5); // 31469 / 70.087 TAKEN FROM SAMSUNG S22C450 MANUAL ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // VGA 50HZ MODES: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/Video.cpp b/src/Video.cpp index 50a8690b..49ce7159 100644 --- a/src/Video.cpp +++ b/src/Video.cpp @@ -147,60 +147,60 @@ void precalcColors() { } -// ALUBYTES -static const uint32_t AluBytes[16][256]={ -{0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff}, -{0xc0c0c0c0,0xc0c0e0c0,0xc0c0c2c0,0xc0c0e2c0,0xc0c0c8c0,0xc0c0e8c0,0xc0c0cac0,0xc0c0eac0,0xe0e0c0e0,0xe0e0e0e0,0xe0e0c2e0,0xe0e0e2e0,0xe0e0c8e0,0xe0e0e8e0,0xe0e0cae0,0xe0e0eae0,0xc2c2c0c2,0xc2c2e0c2,0xc2c2c2c2,0xc2c2e2c2,0xc2c2c8c2,0xc2c2e8c2,0xc2c2cac2,0xc2c2eac2,0xe2e2c0e2,0xe2e2e0e2,0xe2e2c2e2,0xe2e2e2e2,0xe2e2c8e2,0xe2e2e8e2,0xe2e2cae2,0xe2e2eae2,0xc8c8c0c8,0xc8c8e0c8,0xc8c8c2c8,0xc8c8e2c8,0xc8c8c8c8,0xc8c8e8c8,0xc8c8cac8,0xc8c8eac8,0xe8e8c0e8,0xe8e8e0e8,0xe8e8c2e8,0xe8e8e2e8,0xe8e8c8e8,0xe8e8e8e8,0xe8e8cae8,0xe8e8eae8,0xcacac0ca,0xcacae0ca,0xcacac2ca,0xcacae2ca,0xcacac8ca,0xcacae8ca,0xcacacaca,0xcacaeaca,0xeaeac0ea,0xeaeae0ea,0xeaeac2ea,0xeaeae2ea,0xeaeac8ea,0xeaeae8ea,0xeaeacaea,0xeaeaeaea,0xc0c0c0c0,0xc0c0f0c0,0xc0c0c3c0,0xc0c0f3c0,0xc0c0ccc0,0xc0c0fcc0,0xc0c0cfc0,0xc0c0ffc0,0xf0f0c0f0,0xf0f0f0f0,0xf0f0c3f0,0xf0f0f3f0,0xf0f0ccf0,0xf0f0fcf0,0xf0f0cff0,0xf0f0fff0,0xc3c3c0c3,0xc3c3f0c3,0xc3c3c3c3,0xc3c3f3c3,0xc3c3ccc3,0xc3c3fcc3,0xc3c3cfc3,0xc3c3ffc3,0xf3f3c0f3,0xf3f3f0f3,0xf3f3c3f3,0xf3f3f3f3,0xf3f3ccf3,0xf3f3fcf3,0xf3f3cff3,0xf3f3fff3,0xccccc0cc,0xccccf0cc,0xccccc3cc,0xccccf3cc,0xcccccccc,0xccccfccc,0xcccccfcc,0xccccffcc,0xfcfcc0fc,0xfcfcf0fc,0xfcfcc3fc,0xfcfcf3fc,0xfcfcccfc,0xfcfcfcfc,0xfcfccffc,0xfcfcfffc,0xcfcfc0cf,0xcfcff0cf,0xcfcfc3cf,0xcfcff3cf,0xcfcfcccf,0xcfcffccf,0xcfcfcfcf,0xcfcfffcf,0xffffc0ff,0xfffff0ff,0xffffc3ff,0xfffff3ff,0xffffccff,0xfffffcff,0xffffcfff,0xffffffff,0xc0c0c0c0,0xc0c0e0c0,0xc0c0c2c0,0xc0c0e2c0,0xc0c0c8c0,0xc0c0e8c0,0xc0c0cac0,0xc0c0eac0,0xe0e0c0e0,0xe0e0e0e0,0xe0e0c2e0,0xe0e0e2e0,0xe0e0c8e0,0xe0e0e8e0,0xe0e0cae0,0xe0e0eae0,0xc2c2c0c2,0xc2c2e0c2,0xc2c2c2c2,0xc2c2e2c2,0xc2c2c8c2,0xc2c2e8c2,0xc2c2cac2,0xc2c2eac2,0xe2e2c0e2,0xe2e2e0e2,0xe2e2c2e2,0xe2e2e2e2,0xe2e2c8e2,0xe2e2e8e2,0xe2e2cae2,0xe2e2eae2,0xc8c8c0c8,0xc8c8e0c8,0xc8c8c2c8,0xc8c8e2c8,0xc8c8c8c8,0xc8c8e8c8,0xc8c8cac8,0xc8c8eac8,0xe8e8c0e8,0xe8e8e0e8,0xe8e8c2e8,0xe8e8e2e8,0xe8e8c8e8,0xe8e8e8e8,0xe8e8cae8,0xe8e8eae8,0xcacac0ca,0xcacae0ca,0xcacac2ca,0xcacae2ca,0xcacac8ca,0xcacae8ca,0xcacacaca,0xcacaeaca,0xeaeac0ea,0xeaeae0ea,0xeaeac2ea,0xeaeae2ea,0xeaeac8ea,0xeaeae8ea,0xeaeacaea,0xeaeaeaea,0xc0c0c0c0,0xc0c0f0c0,0xc0c0c3c0,0xc0c0f3c0,0xc0c0ccc0,0xc0c0fcc0,0xc0c0cfc0,0xc0c0ffc0,0xf0f0c0f0,0xf0f0f0f0,0xf0f0c3f0,0xf0f0f3f0,0xf0f0ccf0,0xf0f0fcf0,0xf0f0cff0,0xf0f0fff0,0xc3c3c0c3,0xc3c3f0c3,0xc3c3c3c3,0xc3c3f3c3,0xc3c3ccc3,0xc3c3fcc3,0xc3c3cfc3,0xc3c3ffc3,0xf3f3c0f3,0xf3f3f0f3,0xf3f3c3f3,0xf3f3f3f3,0xf3f3ccf3,0xf3f3fcf3,0xf3f3cff3,0xf3f3fff3,0xccccc0cc,0xccccf0cc,0xccccc3cc,0xccccf3cc,0xcccccccc,0xccccfccc,0xcccccfcc,0xccccffcc,0xfcfcc0fc,0xfcfcf0fc,0xfcfcc3fc,0xfcfcf3fc,0xfcfcccfc,0xfcfcfcfc,0xfcfccffc,0xfcfcfffc,0xcfcfc0cf,0xcfcff0cf,0xcfcfc3cf,0xcfcff3cf,0xcfcfcccf,0xcfcffccf,0xcfcfcfcf,0xcfcfffcf,0xffffc0ff,0xfffff0ff,0xffffc3ff,0xfffff3ff,0xffffccff,0xfffffcff,0xffffcfff,0xffffffff}, -{0xc0c0c0c0,0xc0c0c0e0,0xc0c0c0c2,0xc0c0c0e2,0xc0c0c0c8,0xc0c0c0e8,0xc0c0c0ca,0xc0c0c0ea,0xe0e0e0c0,0xe0e0e0e0,0xe0e0e0c2,0xe0e0e0e2,0xe0e0e0c8,0xe0e0e0e8,0xe0e0e0ca,0xe0e0e0ea,0xc2c2c2c0,0xc2c2c2e0,0xc2c2c2c2,0xc2c2c2e2,0xc2c2c2c8,0xc2c2c2e8,0xc2c2c2ca,0xc2c2c2ea,0xe2e2e2c0,0xe2e2e2e0,0xe2e2e2c2,0xe2e2e2e2,0xe2e2e2c8,0xe2e2e2e8,0xe2e2e2ca,0xe2e2e2ea,0xc8c8c8c0,0xc8c8c8e0,0xc8c8c8c2,0xc8c8c8e2,0xc8c8c8c8,0xc8c8c8e8,0xc8c8c8ca,0xc8c8c8ea,0xe8e8e8c0,0xe8e8e8e0,0xe8e8e8c2,0xe8e8e8e2,0xe8e8e8c8,0xe8e8e8e8,0xe8e8e8ca,0xe8e8e8ea,0xcacacac0,0xcacacae0,0xcacacac2,0xcacacae2,0xcacacac8,0xcacacae8,0xcacacaca,0xcacacaea,0xeaeaeac0,0xeaeaeae0,0xeaeaeac2,0xeaeaeae2,0xeaeaeac8,0xeaeaeae8,0xeaeaeaca,0xeaeaeaea,0xc0c0c0c0,0xc0c0c0f0,0xc0c0c0c3,0xc0c0c0f3,0xc0c0c0cc,0xc0c0c0fc,0xc0c0c0cf,0xc0c0c0ff,0xf0f0f0c0,0xf0f0f0f0,0xf0f0f0c3,0xf0f0f0f3,0xf0f0f0cc,0xf0f0f0fc,0xf0f0f0cf,0xf0f0f0ff,0xc3c3c3c0,0xc3c3c3f0,0xc3c3c3c3,0xc3c3c3f3,0xc3c3c3cc,0xc3c3c3fc,0xc3c3c3cf,0xc3c3c3ff,0xf3f3f3c0,0xf3f3f3f0,0xf3f3f3c3,0xf3f3f3f3,0xf3f3f3cc,0xf3f3f3fc,0xf3f3f3cf,0xf3f3f3ff,0xccccccc0,0xccccccf0,0xccccccc3,0xccccccf3,0xcccccccc,0xccccccfc,0xcccccccf,0xccccccff,0xfcfcfcc0,0xfcfcfcf0,0xfcfcfcc3,0xfcfcfcf3,0xfcfcfccc,0xfcfcfcfc,0xfcfcfccf,0xfcfcfcff,0xcfcfcfc0,0xcfcfcff0,0xcfcfcfc3,0xcfcfcff3,0xcfcfcfcc,0xcfcfcffc,0xcfcfcfcf,0xcfcfcfff,0xffffffc0,0xfffffff0,0xffffffc3,0xfffffff3,0xffffffcc,0xfffffffc,0xffffffcf,0xffffffff,0xc0c0c0c0,0xc0c0c0e0,0xc0c0c0c2,0xc0c0c0e2,0xc0c0c0c8,0xc0c0c0e8,0xc0c0c0ca,0xc0c0c0ea,0xe0e0e0c0,0xe0e0e0e0,0xe0e0e0c2,0xe0e0e0e2,0xe0e0e0c8,0xe0e0e0e8,0xe0e0e0ca,0xe0e0e0ea,0xc2c2c2c0,0xc2c2c2e0,0xc2c2c2c2,0xc2c2c2e2,0xc2c2c2c8,0xc2c2c2e8,0xc2c2c2ca,0xc2c2c2ea,0xe2e2e2c0,0xe2e2e2e0,0xe2e2e2c2,0xe2e2e2e2,0xe2e2e2c8,0xe2e2e2e8,0xe2e2e2ca,0xe2e2e2ea,0xc8c8c8c0,0xc8c8c8e0,0xc8c8c8c2,0xc8c8c8e2,0xc8c8c8c8,0xc8c8c8e8,0xc8c8c8ca,0xc8c8c8ea,0xe8e8e8c0,0xe8e8e8e0,0xe8e8e8c2,0xe8e8e8e2,0xe8e8e8c8,0xe8e8e8e8,0xe8e8e8ca,0xe8e8e8ea,0xcacacac0,0xcacacae0,0xcacacac2,0xcacacae2,0xcacacac8,0xcacacae8,0xcacacaca,0xcacacaea,0xeaeaeac0,0xeaeaeae0,0xeaeaeac2,0xeaeaeae2,0xeaeaeac8,0xeaeaeae8,0xeaeaeaca,0xeaeaeaea,0xc0c0c0c0,0xc0c0c0f0,0xc0c0c0c3,0xc0c0c0f3,0xc0c0c0cc,0xc0c0c0fc,0xc0c0c0cf,0xc0c0c0ff,0xf0f0f0c0,0xf0f0f0f0,0xf0f0f0c3,0xf0f0f0f3,0xf0f0f0cc,0xf0f0f0fc,0xf0f0f0cf,0xf0f0f0ff,0xc3c3c3c0,0xc3c3c3f0,0xc3c3c3c3,0xc3c3c3f3,0xc3c3c3cc,0xc3c3c3fc,0xc3c3c3cf,0xc3c3c3ff,0xf3f3f3c0,0xf3f3f3f0,0xf3f3f3c3,0xf3f3f3f3,0xf3f3f3cc,0xf3f3f3fc,0xf3f3f3cf,0xf3f3f3ff,0xccccccc0,0xccccccf0,0xccccccc3,0xccccccf3,0xcccccccc,0xccccccfc,0xcccccccf,0xccccccff,0xfcfcfcc0,0xfcfcfcf0,0xfcfcfcc3,0xfcfcfcf3,0xfcfcfccc,0xfcfcfcfc,0xfcfcfccf,0xfcfcfcff,0xcfcfcfc0,0xcfcfcff0,0xcfcfcfc3,0xcfcfcff3,0xcfcfcfcc,0xcfcfcffc,0xcfcfcfcf,0xcfcfcfff,0xffffffc0,0xfffffff0,0xffffffc3,0xfffffff3,0xffffffcc,0xfffffffc,0xffffffcf,0xffffffff}, -{0xc0c0c0c0,0xc0c0e0e0,0xc0c0c2c2,0xc0c0e2e2,0xc0c0c8c8,0xc0c0e8e8,0xc0c0caca,0xc0c0eaea,0xe0e0c0c0,0xe0e0e0e0,0xe0e0c2c2,0xe0e0e2e2,0xe0e0c8c8,0xe0e0e8e8,0xe0e0caca,0xe0e0eaea,0xc2c2c0c0,0xc2c2e0e0,0xc2c2c2c2,0xc2c2e2e2,0xc2c2c8c8,0xc2c2e8e8,0xc2c2caca,0xc2c2eaea,0xe2e2c0c0,0xe2e2e0e0,0xe2e2c2c2,0xe2e2e2e2,0xe2e2c8c8,0xe2e2e8e8,0xe2e2caca,0xe2e2eaea,0xc8c8c0c0,0xc8c8e0e0,0xc8c8c2c2,0xc8c8e2e2,0xc8c8c8c8,0xc8c8e8e8,0xc8c8caca,0xc8c8eaea,0xe8e8c0c0,0xe8e8e0e0,0xe8e8c2c2,0xe8e8e2e2,0xe8e8c8c8,0xe8e8e8e8,0xe8e8caca,0xe8e8eaea,0xcacac0c0,0xcacae0e0,0xcacac2c2,0xcacae2e2,0xcacac8c8,0xcacae8e8,0xcacacaca,0xcacaeaea,0xeaeac0c0,0xeaeae0e0,0xeaeac2c2,0xeaeae2e2,0xeaeac8c8,0xeaeae8e8,0xeaeacaca,0xeaeaeaea,0xc0c0c0c0,0xc0c0f0f0,0xc0c0c3c3,0xc0c0f3f3,0xc0c0cccc,0xc0c0fcfc,0xc0c0cfcf,0xc0c0ffff,0xf0f0c0c0,0xf0f0f0f0,0xf0f0c3c3,0xf0f0f3f3,0xf0f0cccc,0xf0f0fcfc,0xf0f0cfcf,0xf0f0ffff,0xc3c3c0c0,0xc3c3f0f0,0xc3c3c3c3,0xc3c3f3f3,0xc3c3cccc,0xc3c3fcfc,0xc3c3cfcf,0xc3c3ffff,0xf3f3c0c0,0xf3f3f0f0,0xf3f3c3c3,0xf3f3f3f3,0xf3f3cccc,0xf3f3fcfc,0xf3f3cfcf,0xf3f3ffff,0xccccc0c0,0xccccf0f0,0xccccc3c3,0xccccf3f3,0xcccccccc,0xccccfcfc,0xcccccfcf,0xccccffff,0xfcfcc0c0,0xfcfcf0f0,0xfcfcc3c3,0xfcfcf3f3,0xfcfccccc,0xfcfcfcfc,0xfcfccfcf,0xfcfcffff,0xcfcfc0c0,0xcfcff0f0,0xcfcfc3c3,0xcfcff3f3,0xcfcfcccc,0xcfcffcfc,0xcfcfcfcf,0xcfcfffff,0xffffc0c0,0xfffff0f0,0xffffc3c3,0xfffff3f3,0xffffcccc,0xfffffcfc,0xffffcfcf,0xffffffff,0xc0c0c0c0,0xc0c0e0e0,0xc0c0c2c2,0xc0c0e2e2,0xc0c0c8c8,0xc0c0e8e8,0xc0c0caca,0xc0c0eaea,0xe0e0c0c0,0xe0e0e0e0,0xe0e0c2c2,0xe0e0e2e2,0xe0e0c8c8,0xe0e0e8e8,0xe0e0caca,0xe0e0eaea,0xc2c2c0c0,0xc2c2e0e0,0xc2c2c2c2,0xc2c2e2e2,0xc2c2c8c8,0xc2c2e8e8,0xc2c2caca,0xc2c2eaea,0xe2e2c0c0,0xe2e2e0e0,0xe2e2c2c2,0xe2e2e2e2,0xe2e2c8c8,0xe2e2e8e8,0xe2e2caca,0xe2e2eaea,0xc8c8c0c0,0xc8c8e0e0,0xc8c8c2c2,0xc8c8e2e2,0xc8c8c8c8,0xc8c8e8e8,0xc8c8caca,0xc8c8eaea,0xe8e8c0c0,0xe8e8e0e0,0xe8e8c2c2,0xe8e8e2e2,0xe8e8c8c8,0xe8e8e8e8,0xe8e8caca,0xe8e8eaea,0xcacac0c0,0xcacae0e0,0xcacac2c2,0xcacae2e2,0xcacac8c8,0xcacae8e8,0xcacacaca,0xcacaeaea,0xeaeac0c0,0xeaeae0e0,0xeaeac2c2,0xeaeae2e2,0xeaeac8c8,0xeaeae8e8,0xeaeacaca,0xeaeaeaea,0xc0c0c0c0,0xc0c0f0f0,0xc0c0c3c3,0xc0c0f3f3,0xc0c0cccc,0xc0c0fcfc,0xc0c0cfcf,0xc0c0ffff,0xf0f0c0c0,0xf0f0f0f0,0xf0f0c3c3,0xf0f0f3f3,0xf0f0cccc,0xf0f0fcfc,0xf0f0cfcf,0xf0f0ffff,0xc3c3c0c0,0xc3c3f0f0,0xc3c3c3c3,0xc3c3f3f3,0xc3c3cccc,0xc3c3fcfc,0xc3c3cfcf,0xc3c3ffff,0xf3f3c0c0,0xf3f3f0f0,0xf3f3c3c3,0xf3f3f3f3,0xf3f3cccc,0xf3f3fcfc,0xf3f3cfcf,0xf3f3ffff,0xccccc0c0,0xccccf0f0,0xccccc3c3,0xccccf3f3,0xcccccccc,0xccccfcfc,0xcccccfcf,0xccccffff,0xfcfcc0c0,0xfcfcf0f0,0xfcfcc3c3,0xfcfcf3f3,0xfcfccccc,0xfcfcfcfc,0xfcfccfcf,0xfcfcffff,0xcfcfc0c0,0xcfcff0f0,0xcfcfc3c3,0xcfcff3f3,0xcfcfcccc,0xcfcffcfc,0xcfcfcfcf,0xcfcfffff,0xffffc0c0,0xfffff0f0,0xffffc3c3,0xfffff3f3,0xffffcccc,0xfffffcfc,0xffffcfcf,0xffffffff}, -{0xc0c0c0c0,0xe0c0c0c0,0xc2c0c0c0,0xe2c0c0c0,0xc8c0c0c0,0xe8c0c0c0,0xcac0c0c0,0xeac0c0c0,0xc0e0e0e0,0xe0e0e0e0,0xc2e0e0e0,0xe2e0e0e0,0xc8e0e0e0,0xe8e0e0e0,0xcae0e0e0,0xeae0e0e0,0xc0c2c2c2,0xe0c2c2c2,0xc2c2c2c2,0xe2c2c2c2,0xc8c2c2c2,0xe8c2c2c2,0xcac2c2c2,0xeac2c2c2,0xc0e2e2e2,0xe0e2e2e2,0xc2e2e2e2,0xe2e2e2e2,0xc8e2e2e2,0xe8e2e2e2,0xcae2e2e2,0xeae2e2e2,0xc0c8c8c8,0xe0c8c8c8,0xc2c8c8c8,0xe2c8c8c8,0xc8c8c8c8,0xe8c8c8c8,0xcac8c8c8,0xeac8c8c8,0xc0e8e8e8,0xe0e8e8e8,0xc2e8e8e8,0xe2e8e8e8,0xc8e8e8e8,0xe8e8e8e8,0xcae8e8e8,0xeae8e8e8,0xc0cacaca,0xe0cacaca,0xc2cacaca,0xe2cacaca,0xc8cacaca,0xe8cacaca,0xcacacaca,0xeacacaca,0xc0eaeaea,0xe0eaeaea,0xc2eaeaea,0xe2eaeaea,0xc8eaeaea,0xe8eaeaea,0xcaeaeaea,0xeaeaeaea,0xc0c0c0c0,0xf0c0c0c0,0xc3c0c0c0,0xf3c0c0c0,0xccc0c0c0,0xfcc0c0c0,0xcfc0c0c0,0xffc0c0c0,0xc0f0f0f0,0xf0f0f0f0,0xc3f0f0f0,0xf3f0f0f0,0xccf0f0f0,0xfcf0f0f0,0xcff0f0f0,0xfff0f0f0,0xc0c3c3c3,0xf0c3c3c3,0xc3c3c3c3,0xf3c3c3c3,0xccc3c3c3,0xfcc3c3c3,0xcfc3c3c3,0xffc3c3c3,0xc0f3f3f3,0xf0f3f3f3,0xc3f3f3f3,0xf3f3f3f3,0xccf3f3f3,0xfcf3f3f3,0xcff3f3f3,0xfff3f3f3,0xc0cccccc,0xf0cccccc,0xc3cccccc,0xf3cccccc,0xcccccccc,0xfccccccc,0xcfcccccc,0xffcccccc,0xc0fcfcfc,0xf0fcfcfc,0xc3fcfcfc,0xf3fcfcfc,0xccfcfcfc,0xfcfcfcfc,0xcffcfcfc,0xfffcfcfc,0xc0cfcfcf,0xf0cfcfcf,0xc3cfcfcf,0xf3cfcfcf,0xcccfcfcf,0xfccfcfcf,0xcfcfcfcf,0xffcfcfcf,0xc0ffffff,0xf0ffffff,0xc3ffffff,0xf3ffffff,0xccffffff,0xfcffffff,0xcfffffff,0xffffffff,0xc0c0c0c0,0xe0c0c0c0,0xc2c0c0c0,0xe2c0c0c0,0xc8c0c0c0,0xe8c0c0c0,0xcac0c0c0,0xeac0c0c0,0xc0e0e0e0,0xe0e0e0e0,0xc2e0e0e0,0xe2e0e0e0,0xc8e0e0e0,0xe8e0e0e0,0xcae0e0e0,0xeae0e0e0,0xc0c2c2c2,0xe0c2c2c2,0xc2c2c2c2,0xe2c2c2c2,0xc8c2c2c2,0xe8c2c2c2,0xcac2c2c2,0xeac2c2c2,0xc0e2e2e2,0xe0e2e2e2,0xc2e2e2e2,0xe2e2e2e2,0xc8e2e2e2,0xe8e2e2e2,0xcae2e2e2,0xeae2e2e2,0xc0c8c8c8,0xe0c8c8c8,0xc2c8c8c8,0xe2c8c8c8,0xc8c8c8c8,0xe8c8c8c8,0xcac8c8c8,0xeac8c8c8,0xc0e8e8e8,0xe0e8e8e8,0xc2e8e8e8,0xe2e8e8e8,0xc8e8e8e8,0xe8e8e8e8,0xcae8e8e8,0xeae8e8e8,0xc0cacaca,0xe0cacaca,0xc2cacaca,0xe2cacaca,0xc8cacaca,0xe8cacaca,0xcacacaca,0xeacacaca,0xc0eaeaea,0xe0eaeaea,0xc2eaeaea,0xe2eaeaea,0xc8eaeaea,0xe8eaeaea,0xcaeaeaea,0xeaeaeaea,0xc0c0c0c0,0xf0c0c0c0,0xc3c0c0c0,0xf3c0c0c0,0xccc0c0c0,0xfcc0c0c0,0xcfc0c0c0,0xffc0c0c0,0xc0f0f0f0,0xf0f0f0f0,0xc3f0f0f0,0xf3f0f0f0,0xccf0f0f0,0xfcf0f0f0,0xcff0f0f0,0xfff0f0f0,0xc0c3c3c3,0xf0c3c3c3,0xc3c3c3c3,0xf3c3c3c3,0xccc3c3c3,0xfcc3c3c3,0xcfc3c3c3,0xffc3c3c3,0xc0f3f3f3,0xf0f3f3f3,0xc3f3f3f3,0xf3f3f3f3,0xccf3f3f3,0xfcf3f3f3,0xcff3f3f3,0xfff3f3f3,0xc0cccccc,0xf0cccccc,0xc3cccccc,0xf3cccccc,0xcccccccc,0xfccccccc,0xcfcccccc,0xffcccccc,0xc0fcfcfc,0xf0fcfcfc,0xc3fcfcfc,0xf3fcfcfc,0xccfcfcfc,0xfcfcfcfc,0xcffcfcfc,0xfffcfcfc,0xc0cfcfcf,0xf0cfcfcf,0xc3cfcfcf,0xf3cfcfcf,0xcccfcfcf,0xfccfcfcf,0xcfcfcfcf,0xffcfcfcf,0xc0ffffff,0xf0ffffff,0xc3ffffff,0xf3ffffff,0xccffffff,0xfcffffff,0xcfffffff,0xffffffff}, -{0xc0c0c0c0,0xe0c0e0c0,0xc2c0c2c0,0xe2c0e2c0,0xc8c0c8c0,0xe8c0e8c0,0xcac0cac0,0xeac0eac0,0xc0e0c0e0,0xe0e0e0e0,0xc2e0c2e0,0xe2e0e2e0,0xc8e0c8e0,0xe8e0e8e0,0xcae0cae0,0xeae0eae0,0xc0c2c0c2,0xe0c2e0c2,0xc2c2c2c2,0xe2c2e2c2,0xc8c2c8c2,0xe8c2e8c2,0xcac2cac2,0xeac2eac2,0xc0e2c0e2,0xe0e2e0e2,0xc2e2c2e2,0xe2e2e2e2,0xc8e2c8e2,0xe8e2e8e2,0xcae2cae2,0xeae2eae2,0xc0c8c0c8,0xe0c8e0c8,0xc2c8c2c8,0xe2c8e2c8,0xc8c8c8c8,0xe8c8e8c8,0xcac8cac8,0xeac8eac8,0xc0e8c0e8,0xe0e8e0e8,0xc2e8c2e8,0xe2e8e2e8,0xc8e8c8e8,0xe8e8e8e8,0xcae8cae8,0xeae8eae8,0xc0cac0ca,0xe0cae0ca,0xc2cac2ca,0xe2cae2ca,0xc8cac8ca,0xe8cae8ca,0xcacacaca,0xeacaeaca,0xc0eac0ea,0xe0eae0ea,0xc2eac2ea,0xe2eae2ea,0xc8eac8ea,0xe8eae8ea,0xcaeacaea,0xeaeaeaea,0xc0c0c0c0,0xf0c0f0c0,0xc3c0c3c0,0xf3c0f3c0,0xccc0ccc0,0xfcc0fcc0,0xcfc0cfc0,0xffc0ffc0,0xc0f0c0f0,0xf0f0f0f0,0xc3f0c3f0,0xf3f0f3f0,0xccf0ccf0,0xfcf0fcf0,0xcff0cff0,0xfff0fff0,0xc0c3c0c3,0xf0c3f0c3,0xc3c3c3c3,0xf3c3f3c3,0xccc3ccc3,0xfcc3fcc3,0xcfc3cfc3,0xffc3ffc3,0xc0f3c0f3,0xf0f3f0f3,0xc3f3c3f3,0xf3f3f3f3,0xccf3ccf3,0xfcf3fcf3,0xcff3cff3,0xfff3fff3,0xc0ccc0cc,0xf0ccf0cc,0xc3ccc3cc,0xf3ccf3cc,0xcccccccc,0xfcccfccc,0xcfcccfcc,0xffccffcc,0xc0fcc0fc,0xf0fcf0fc,0xc3fcc3fc,0xf3fcf3fc,0xccfcccfc,0xfcfcfcfc,0xcffccffc,0xfffcfffc,0xc0cfc0cf,0xf0cff0cf,0xc3cfc3cf,0xf3cff3cf,0xcccfcccf,0xfccffccf,0xcfcfcfcf,0xffcfffcf,0xc0ffc0ff,0xf0fff0ff,0xc3ffc3ff,0xf3fff3ff,0xccffccff,0xfcfffcff,0xcfffcfff,0xffffffff,0xc0c0c0c0,0xe0c0e0c0,0xc2c0c2c0,0xe2c0e2c0,0xc8c0c8c0,0xe8c0e8c0,0xcac0cac0,0xeac0eac0,0xc0e0c0e0,0xe0e0e0e0,0xc2e0c2e0,0xe2e0e2e0,0xc8e0c8e0,0xe8e0e8e0,0xcae0cae0,0xeae0eae0,0xc0c2c0c2,0xe0c2e0c2,0xc2c2c2c2,0xe2c2e2c2,0xc8c2c8c2,0xe8c2e8c2,0xcac2cac2,0xeac2eac2,0xc0e2c0e2,0xe0e2e0e2,0xc2e2c2e2,0xe2e2e2e2,0xc8e2c8e2,0xe8e2e8e2,0xcae2cae2,0xeae2eae2,0xc0c8c0c8,0xe0c8e0c8,0xc2c8c2c8,0xe2c8e2c8,0xc8c8c8c8,0xe8c8e8c8,0xcac8cac8,0xeac8eac8,0xc0e8c0e8,0xe0e8e0e8,0xc2e8c2e8,0xe2e8e2e8,0xc8e8c8e8,0xe8e8e8e8,0xcae8cae8,0xeae8eae8,0xc0cac0ca,0xe0cae0ca,0xc2cac2ca,0xe2cae2ca,0xc8cac8ca,0xe8cae8ca,0xcacacaca,0xeacaeaca,0xc0eac0ea,0xe0eae0ea,0xc2eac2ea,0xe2eae2ea,0xc8eac8ea,0xe8eae8ea,0xcaeacaea,0xeaeaeaea,0xc0c0c0c0,0xf0c0f0c0,0xc3c0c3c0,0xf3c0f3c0,0xccc0ccc0,0xfcc0fcc0,0xcfc0cfc0,0xffc0ffc0,0xc0f0c0f0,0xf0f0f0f0,0xc3f0c3f0,0xf3f0f3f0,0xccf0ccf0,0xfcf0fcf0,0xcff0cff0,0xfff0fff0,0xc0c3c0c3,0xf0c3f0c3,0xc3c3c3c3,0xf3c3f3c3,0xccc3ccc3,0xfcc3fcc3,0xcfc3cfc3,0xffc3ffc3,0xc0f3c0f3,0xf0f3f0f3,0xc3f3c3f3,0xf3f3f3f3,0xccf3ccf3,0xfcf3fcf3,0xcff3cff3,0xfff3fff3,0xc0ccc0cc,0xf0ccf0cc,0xc3ccc3cc,0xf3ccf3cc,0xcccccccc,0xfcccfccc,0xcfcccfcc,0xffccffcc,0xc0fcc0fc,0xf0fcf0fc,0xc3fcc3fc,0xf3fcf3fc,0xccfcccfc,0xfcfcfcfc,0xcffccffc,0xfffcfffc,0xc0cfc0cf,0xf0cff0cf,0xc3cfc3cf,0xf3cff3cf,0xcccfcccf,0xfccffccf,0xcfcfcfcf,0xffcfffcf,0xc0ffc0ff,0xf0fff0ff,0xc3ffc3ff,0xf3fff3ff,0xccffccff,0xfcfffcff,0xcfffcfff,0xffffffff}, -{0xc0c0c0c0,0xe0c0c0e0,0xc2c0c0c2,0xe2c0c0e2,0xc8c0c0c8,0xe8c0c0e8,0xcac0c0ca,0xeac0c0ea,0xc0e0e0c0,0xe0e0e0e0,0xc2e0e0c2,0xe2e0e0e2,0xc8e0e0c8,0xe8e0e0e8,0xcae0e0ca,0xeae0e0ea,0xc0c2c2c0,0xe0c2c2e0,0xc2c2c2c2,0xe2c2c2e2,0xc8c2c2c8,0xe8c2c2e8,0xcac2c2ca,0xeac2c2ea,0xc0e2e2c0,0xe0e2e2e0,0xc2e2e2c2,0xe2e2e2e2,0xc8e2e2c8,0xe8e2e2e8,0xcae2e2ca,0xeae2e2ea,0xc0c8c8c0,0xe0c8c8e0,0xc2c8c8c2,0xe2c8c8e2,0xc8c8c8c8,0xe8c8c8e8,0xcac8c8ca,0xeac8c8ea,0xc0e8e8c0,0xe0e8e8e0,0xc2e8e8c2,0xe2e8e8e2,0xc8e8e8c8,0xe8e8e8e8,0xcae8e8ca,0xeae8e8ea,0xc0cacac0,0xe0cacae0,0xc2cacac2,0xe2cacae2,0xc8cacac8,0xe8cacae8,0xcacacaca,0xeacacaea,0xc0eaeac0,0xe0eaeae0,0xc2eaeac2,0xe2eaeae2,0xc8eaeac8,0xe8eaeae8,0xcaeaeaca,0xeaeaeaea,0xc0c0c0c0,0xf0c0c0f0,0xc3c0c0c3,0xf3c0c0f3,0xccc0c0cc,0xfcc0c0fc,0xcfc0c0cf,0xffc0c0ff,0xc0f0f0c0,0xf0f0f0f0,0xc3f0f0c3,0xf3f0f0f3,0xccf0f0cc,0xfcf0f0fc,0xcff0f0cf,0xfff0f0ff,0xc0c3c3c0,0xf0c3c3f0,0xc3c3c3c3,0xf3c3c3f3,0xccc3c3cc,0xfcc3c3fc,0xcfc3c3cf,0xffc3c3ff,0xc0f3f3c0,0xf0f3f3f0,0xc3f3f3c3,0xf3f3f3f3,0xccf3f3cc,0xfcf3f3fc,0xcff3f3cf,0xfff3f3ff,0xc0ccccc0,0xf0ccccf0,0xc3ccccc3,0xf3ccccf3,0xcccccccc,0xfcccccfc,0xcfcccccf,0xffccccff,0xc0fcfcc0,0xf0fcfcf0,0xc3fcfcc3,0xf3fcfcf3,0xccfcfccc,0xfcfcfcfc,0xcffcfccf,0xfffcfcff,0xc0cfcfc0,0xf0cfcff0,0xc3cfcfc3,0xf3cfcff3,0xcccfcfcc,0xfccfcffc,0xcfcfcfcf,0xffcfcfff,0xc0ffffc0,0xf0fffff0,0xc3ffffc3,0xf3fffff3,0xccffffcc,0xfcfffffc,0xcfffffcf,0xffffffff,0xc0c0c0c0,0xe0c0c0e0,0xc2c0c0c2,0xe2c0c0e2,0xc8c0c0c8,0xe8c0c0e8,0xcac0c0ca,0xeac0c0ea,0xc0e0e0c0,0xe0e0e0e0,0xc2e0e0c2,0xe2e0e0e2,0xc8e0e0c8,0xe8e0e0e8,0xcae0e0ca,0xeae0e0ea,0xc0c2c2c0,0xe0c2c2e0,0xc2c2c2c2,0xe2c2c2e2,0xc8c2c2c8,0xe8c2c2e8,0xcac2c2ca,0xeac2c2ea,0xc0e2e2c0,0xe0e2e2e0,0xc2e2e2c2,0xe2e2e2e2,0xc8e2e2c8,0xe8e2e2e8,0xcae2e2ca,0xeae2e2ea,0xc0c8c8c0,0xe0c8c8e0,0xc2c8c8c2,0xe2c8c8e2,0xc8c8c8c8,0xe8c8c8e8,0xcac8c8ca,0xeac8c8ea,0xc0e8e8c0,0xe0e8e8e0,0xc2e8e8c2,0xe2e8e8e2,0xc8e8e8c8,0xe8e8e8e8,0xcae8e8ca,0xeae8e8ea,0xc0cacac0,0xe0cacae0,0xc2cacac2,0xe2cacae2,0xc8cacac8,0xe8cacae8,0xcacacaca,0xeacacaea,0xc0eaeac0,0xe0eaeae0,0xc2eaeac2,0xe2eaeae2,0xc8eaeac8,0xe8eaeae8,0xcaeaeaca,0xeaeaeaea,0xc0c0c0c0,0xf0c0c0f0,0xc3c0c0c3,0xf3c0c0f3,0xccc0c0cc,0xfcc0c0fc,0xcfc0c0cf,0xffc0c0ff,0xc0f0f0c0,0xf0f0f0f0,0xc3f0f0c3,0xf3f0f0f3,0xccf0f0cc,0xfcf0f0fc,0xcff0f0cf,0xfff0f0ff,0xc0c3c3c0,0xf0c3c3f0,0xc3c3c3c3,0xf3c3c3f3,0xccc3c3cc,0xfcc3c3fc,0xcfc3c3cf,0xffc3c3ff,0xc0f3f3c0,0xf0f3f3f0,0xc3f3f3c3,0xf3f3f3f3,0xccf3f3cc,0xfcf3f3fc,0xcff3f3cf,0xfff3f3ff,0xc0ccccc0,0xf0ccccf0,0xc3ccccc3,0xf3ccccf3,0xcccccccc,0xfcccccfc,0xcfcccccf,0xffccccff,0xc0fcfcc0,0xf0fcfcf0,0xc3fcfcc3,0xf3fcfcf3,0xccfcfccc,0xfcfcfcfc,0xcffcfccf,0xfffcfcff,0xc0cfcfc0,0xf0cfcff0,0xc3cfcfc3,0xf3cfcff3,0xcccfcfcc,0xfccfcffc,0xcfcfcfcf,0xffcfcfff,0xc0ffffc0,0xf0fffff0,0xc3ffffc3,0xf3fffff3,0xccffffcc,0xfcfffffc,0xcfffffcf,0xffffffff}, -{0xc0c0c0c0,0xe0c0e0e0,0xc2c0c2c2,0xe2c0e2e2,0xc8c0c8c8,0xe8c0e8e8,0xcac0caca,0xeac0eaea,0xc0e0c0c0,0xe0e0e0e0,0xc2e0c2c2,0xe2e0e2e2,0xc8e0c8c8,0xe8e0e8e8,0xcae0caca,0xeae0eaea,0xc0c2c0c0,0xe0c2e0e0,0xc2c2c2c2,0xe2c2e2e2,0xc8c2c8c8,0xe8c2e8e8,0xcac2caca,0xeac2eaea,0xc0e2c0c0,0xe0e2e0e0,0xc2e2c2c2,0xe2e2e2e2,0xc8e2c8c8,0xe8e2e8e8,0xcae2caca,0xeae2eaea,0xc0c8c0c0,0xe0c8e0e0,0xc2c8c2c2,0xe2c8e2e2,0xc8c8c8c8,0xe8c8e8e8,0xcac8caca,0xeac8eaea,0xc0e8c0c0,0xe0e8e0e0,0xc2e8c2c2,0xe2e8e2e2,0xc8e8c8c8,0xe8e8e8e8,0xcae8caca,0xeae8eaea,0xc0cac0c0,0xe0cae0e0,0xc2cac2c2,0xe2cae2e2,0xc8cac8c8,0xe8cae8e8,0xcacacaca,0xeacaeaea,0xc0eac0c0,0xe0eae0e0,0xc2eac2c2,0xe2eae2e2,0xc8eac8c8,0xe8eae8e8,0xcaeacaca,0xeaeaeaea,0xc0c0c0c0,0xf0c0f0f0,0xc3c0c3c3,0xf3c0f3f3,0xccc0cccc,0xfcc0fcfc,0xcfc0cfcf,0xffc0ffff,0xc0f0c0c0,0xf0f0f0f0,0xc3f0c3c3,0xf3f0f3f3,0xccf0cccc,0xfcf0fcfc,0xcff0cfcf,0xfff0ffff,0xc0c3c0c0,0xf0c3f0f0,0xc3c3c3c3,0xf3c3f3f3,0xccc3cccc,0xfcc3fcfc,0xcfc3cfcf,0xffc3ffff,0xc0f3c0c0,0xf0f3f0f0,0xc3f3c3c3,0xf3f3f3f3,0xccf3cccc,0xfcf3fcfc,0xcff3cfcf,0xfff3ffff,0xc0ccc0c0,0xf0ccf0f0,0xc3ccc3c3,0xf3ccf3f3,0xcccccccc,0xfcccfcfc,0xcfcccfcf,0xffccffff,0xc0fcc0c0,0xf0fcf0f0,0xc3fcc3c3,0xf3fcf3f3,0xccfccccc,0xfcfcfcfc,0xcffccfcf,0xfffcffff,0xc0cfc0c0,0xf0cff0f0,0xc3cfc3c3,0xf3cff3f3,0xcccfcccc,0xfccffcfc,0xcfcfcfcf,0xffcfffff,0xc0ffc0c0,0xf0fff0f0,0xc3ffc3c3,0xf3fff3f3,0xccffcccc,0xfcfffcfc,0xcfffcfcf,0xffffffff,0xc0c0c0c0,0xe0c0e0e0,0xc2c0c2c2,0xe2c0e2e2,0xc8c0c8c8,0xe8c0e8e8,0xcac0caca,0xeac0eaea,0xc0e0c0c0,0xe0e0e0e0,0xc2e0c2c2,0xe2e0e2e2,0xc8e0c8c8,0xe8e0e8e8,0xcae0caca,0xeae0eaea,0xc0c2c0c0,0xe0c2e0e0,0xc2c2c2c2,0xe2c2e2e2,0xc8c2c8c8,0xe8c2e8e8,0xcac2caca,0xeac2eaea,0xc0e2c0c0,0xe0e2e0e0,0xc2e2c2c2,0xe2e2e2e2,0xc8e2c8c8,0xe8e2e8e8,0xcae2caca,0xeae2eaea,0xc0c8c0c0,0xe0c8e0e0,0xc2c8c2c2,0xe2c8e2e2,0xc8c8c8c8,0xe8c8e8e8,0xcac8caca,0xeac8eaea,0xc0e8c0c0,0xe0e8e0e0,0xc2e8c2c2,0xe2e8e2e2,0xc8e8c8c8,0xe8e8e8e8,0xcae8caca,0xeae8eaea,0xc0cac0c0,0xe0cae0e0,0xc2cac2c2,0xe2cae2e2,0xc8cac8c8,0xe8cae8e8,0xcacacaca,0xeacaeaea,0xc0eac0c0,0xe0eae0e0,0xc2eac2c2,0xe2eae2e2,0xc8eac8c8,0xe8eae8e8,0xcaeacaca,0xeaeaeaea,0xc0c0c0c0,0xf0c0f0f0,0xc3c0c3c3,0xf3c0f3f3,0xccc0cccc,0xfcc0fcfc,0xcfc0cfcf,0xffc0ffff,0xc0f0c0c0,0xf0f0f0f0,0xc3f0c3c3,0xf3f0f3f3,0xccf0cccc,0xfcf0fcfc,0xcff0cfcf,0xfff0ffff,0xc0c3c0c0,0xf0c3f0f0,0xc3c3c3c3,0xf3c3f3f3,0xccc3cccc,0xfcc3fcfc,0xcfc3cfcf,0xffc3ffff,0xc0f3c0c0,0xf0f3f0f0,0xc3f3c3c3,0xf3f3f3f3,0xccf3cccc,0xfcf3fcfc,0xcff3cfcf,0xfff3ffff,0xc0ccc0c0,0xf0ccf0f0,0xc3ccc3c3,0xf3ccf3f3,0xcccccccc,0xfcccfcfc,0xcfcccfcf,0xffccffff,0xc0fcc0c0,0xf0fcf0f0,0xc3fcc3c3,0xf3fcf3f3,0xccfccccc,0xfcfcfcfc,0xcffccfcf,0xfffcffff,0xc0cfc0c0,0xf0cff0f0,0xc3cfc3c3,0xf3cff3f3,0xcccfcccc,0xfccffcfc,0xcfcfcfcf,0xffcfffff,0xc0ffc0c0,0xf0fff0f0,0xc3ffc3c3,0xf3fff3f3,0xccffcccc,0xfcfffcfc,0xcfffcfcf,0xffffffff}, -{0xc0c0c0c0,0xc0e0c0c0,0xc0c2c0c0,0xc0e2c0c0,0xc0c8c0c0,0xc0e8c0c0,0xc0cac0c0,0xc0eac0c0,0xe0c0e0e0,0xe0e0e0e0,0xe0c2e0e0,0xe0e2e0e0,0xe0c8e0e0,0xe0e8e0e0,0xe0cae0e0,0xe0eae0e0,0xc2c0c2c2,0xc2e0c2c2,0xc2c2c2c2,0xc2e2c2c2,0xc2c8c2c2,0xc2e8c2c2,0xc2cac2c2,0xc2eac2c2,0xe2c0e2e2,0xe2e0e2e2,0xe2c2e2e2,0xe2e2e2e2,0xe2c8e2e2,0xe2e8e2e2,0xe2cae2e2,0xe2eae2e2,0xc8c0c8c8,0xc8e0c8c8,0xc8c2c8c8,0xc8e2c8c8,0xc8c8c8c8,0xc8e8c8c8,0xc8cac8c8,0xc8eac8c8,0xe8c0e8e8,0xe8e0e8e8,0xe8c2e8e8,0xe8e2e8e8,0xe8c8e8e8,0xe8e8e8e8,0xe8cae8e8,0xe8eae8e8,0xcac0caca,0xcae0caca,0xcac2caca,0xcae2caca,0xcac8caca,0xcae8caca,0xcacacaca,0xcaeacaca,0xeac0eaea,0xeae0eaea,0xeac2eaea,0xeae2eaea,0xeac8eaea,0xeae8eaea,0xeacaeaea,0xeaeaeaea,0xc0c0c0c0,0xc0f0c0c0,0xc0c3c0c0,0xc0f3c0c0,0xc0ccc0c0,0xc0fcc0c0,0xc0cfc0c0,0xc0ffc0c0,0xf0c0f0f0,0xf0f0f0f0,0xf0c3f0f0,0xf0f3f0f0,0xf0ccf0f0,0xf0fcf0f0,0xf0cff0f0,0xf0fff0f0,0xc3c0c3c3,0xc3f0c3c3,0xc3c3c3c3,0xc3f3c3c3,0xc3ccc3c3,0xc3fcc3c3,0xc3cfc3c3,0xc3ffc3c3,0xf3c0f3f3,0xf3f0f3f3,0xf3c3f3f3,0xf3f3f3f3,0xf3ccf3f3,0xf3fcf3f3,0xf3cff3f3,0xf3fff3f3,0xccc0cccc,0xccf0cccc,0xccc3cccc,0xccf3cccc,0xcccccccc,0xccfccccc,0xcccfcccc,0xccffcccc,0xfcc0fcfc,0xfcf0fcfc,0xfcc3fcfc,0xfcf3fcfc,0xfcccfcfc,0xfcfcfcfc,0xfccffcfc,0xfcfffcfc,0xcfc0cfcf,0xcff0cfcf,0xcfc3cfcf,0xcff3cfcf,0xcfcccfcf,0xcffccfcf,0xcfcfcfcf,0xcfffcfcf,0xffc0ffff,0xfff0ffff,0xffc3ffff,0xfff3ffff,0xffccffff,0xfffcffff,0xffcfffff,0xffffffff,0xc0c0c0c0,0xc0e0c0c0,0xc0c2c0c0,0xc0e2c0c0,0xc0c8c0c0,0xc0e8c0c0,0xc0cac0c0,0xc0eac0c0,0xe0c0e0e0,0xe0e0e0e0,0xe0c2e0e0,0xe0e2e0e0,0xe0c8e0e0,0xe0e8e0e0,0xe0cae0e0,0xe0eae0e0,0xc2c0c2c2,0xc2e0c2c2,0xc2c2c2c2,0xc2e2c2c2,0xc2c8c2c2,0xc2e8c2c2,0xc2cac2c2,0xc2eac2c2,0xe2c0e2e2,0xe2e0e2e2,0xe2c2e2e2,0xe2e2e2e2,0xe2c8e2e2,0xe2e8e2e2,0xe2cae2e2,0xe2eae2e2,0xc8c0c8c8,0xc8e0c8c8,0xc8c2c8c8,0xc8e2c8c8,0xc8c8c8c8,0xc8e8c8c8,0xc8cac8c8,0xc8eac8c8,0xe8c0e8e8,0xe8e0e8e8,0xe8c2e8e8,0xe8e2e8e8,0xe8c8e8e8,0xe8e8e8e8,0xe8cae8e8,0xe8eae8e8,0xcac0caca,0xcae0caca,0xcac2caca,0xcae2caca,0xcac8caca,0xcae8caca,0xcacacaca,0xcaeacaca,0xeac0eaea,0xeae0eaea,0xeac2eaea,0xeae2eaea,0xeac8eaea,0xeae8eaea,0xeacaeaea,0xeaeaeaea,0xc0c0c0c0,0xc0f0c0c0,0xc0c3c0c0,0xc0f3c0c0,0xc0ccc0c0,0xc0fcc0c0,0xc0cfc0c0,0xc0ffc0c0,0xf0c0f0f0,0xf0f0f0f0,0xf0c3f0f0,0xf0f3f0f0,0xf0ccf0f0,0xf0fcf0f0,0xf0cff0f0,0xf0fff0f0,0xc3c0c3c3,0xc3f0c3c3,0xc3c3c3c3,0xc3f3c3c3,0xc3ccc3c3,0xc3fcc3c3,0xc3cfc3c3,0xc3ffc3c3,0xf3c0f3f3,0xf3f0f3f3,0xf3c3f3f3,0xf3f3f3f3,0xf3ccf3f3,0xf3fcf3f3,0xf3cff3f3,0xf3fff3f3,0xccc0cccc,0xccf0cccc,0xccc3cccc,0xccf3cccc,0xcccccccc,0xccfccccc,0xcccfcccc,0xccffcccc,0xfcc0fcfc,0xfcf0fcfc,0xfcc3fcfc,0xfcf3fcfc,0xfcccfcfc,0xfcfcfcfc,0xfccffcfc,0xfcfffcfc,0xcfc0cfcf,0xcff0cfcf,0xcfc3cfcf,0xcff3cfcf,0xcfcccfcf,0xcffccfcf,0xcfcfcfcf,0xcfffcfcf,0xffc0ffff,0xfff0ffff,0xffc3ffff,0xfff3ffff,0xffccffff,0xfffcffff,0xffcfffff,0xffffffff}, -{0xc0c0c0c0,0xc0e0e0c0,0xc0c2c2c0,0xc0e2e2c0,0xc0c8c8c0,0xc0e8e8c0,0xc0cacac0,0xc0eaeac0,0xe0c0c0e0,0xe0e0e0e0,0xe0c2c2e0,0xe0e2e2e0,0xe0c8c8e0,0xe0e8e8e0,0xe0cacae0,0xe0eaeae0,0xc2c0c0c2,0xc2e0e0c2,0xc2c2c2c2,0xc2e2e2c2,0xc2c8c8c2,0xc2e8e8c2,0xc2cacac2,0xc2eaeac2,0xe2c0c0e2,0xe2e0e0e2,0xe2c2c2e2,0xe2e2e2e2,0xe2c8c8e2,0xe2e8e8e2,0xe2cacae2,0xe2eaeae2,0xc8c0c0c8,0xc8e0e0c8,0xc8c2c2c8,0xc8e2e2c8,0xc8c8c8c8,0xc8e8e8c8,0xc8cacac8,0xc8eaeac8,0xe8c0c0e8,0xe8e0e0e8,0xe8c2c2e8,0xe8e2e2e8,0xe8c8c8e8,0xe8e8e8e8,0xe8cacae8,0xe8eaeae8,0xcac0c0ca,0xcae0e0ca,0xcac2c2ca,0xcae2e2ca,0xcac8c8ca,0xcae8e8ca,0xcacacaca,0xcaeaeaca,0xeac0c0ea,0xeae0e0ea,0xeac2c2ea,0xeae2e2ea,0xeac8c8ea,0xeae8e8ea,0xeacacaea,0xeaeaeaea,0xc0c0c0c0,0xc0f0f0c0,0xc0c3c3c0,0xc0f3f3c0,0xc0ccccc0,0xc0fcfcc0,0xc0cfcfc0,0xc0ffffc0,0xf0c0c0f0,0xf0f0f0f0,0xf0c3c3f0,0xf0f3f3f0,0xf0ccccf0,0xf0fcfcf0,0xf0cfcff0,0xf0fffff0,0xc3c0c0c3,0xc3f0f0c3,0xc3c3c3c3,0xc3f3f3c3,0xc3ccccc3,0xc3fcfcc3,0xc3cfcfc3,0xc3ffffc3,0xf3c0c0f3,0xf3f0f0f3,0xf3c3c3f3,0xf3f3f3f3,0xf3ccccf3,0xf3fcfcf3,0xf3cfcff3,0xf3fffff3,0xccc0c0cc,0xccf0f0cc,0xccc3c3cc,0xccf3f3cc,0xcccccccc,0xccfcfccc,0xcccfcfcc,0xccffffcc,0xfcc0c0fc,0xfcf0f0fc,0xfcc3c3fc,0xfcf3f3fc,0xfcccccfc,0xfcfcfcfc,0xfccfcffc,0xfcfffffc,0xcfc0c0cf,0xcff0f0cf,0xcfc3c3cf,0xcff3f3cf,0xcfcccccf,0xcffcfccf,0xcfcfcfcf,0xcfffffcf,0xffc0c0ff,0xfff0f0ff,0xffc3c3ff,0xfff3f3ff,0xffccccff,0xfffcfcff,0xffcfcfff,0xffffffff,0xc0c0c0c0,0xc0e0e0c0,0xc0c2c2c0,0xc0e2e2c0,0xc0c8c8c0,0xc0e8e8c0,0xc0cacac0,0xc0eaeac0,0xe0c0c0e0,0xe0e0e0e0,0xe0c2c2e0,0xe0e2e2e0,0xe0c8c8e0,0xe0e8e8e0,0xe0cacae0,0xe0eaeae0,0xc2c0c0c2,0xc2e0e0c2,0xc2c2c2c2,0xc2e2e2c2,0xc2c8c8c2,0xc2e8e8c2,0xc2cacac2,0xc2eaeac2,0xe2c0c0e2,0xe2e0e0e2,0xe2c2c2e2,0xe2e2e2e2,0xe2c8c8e2,0xe2e8e8e2,0xe2cacae2,0xe2eaeae2,0xc8c0c0c8,0xc8e0e0c8,0xc8c2c2c8,0xc8e2e2c8,0xc8c8c8c8,0xc8e8e8c8,0xc8cacac8,0xc8eaeac8,0xe8c0c0e8,0xe8e0e0e8,0xe8c2c2e8,0xe8e2e2e8,0xe8c8c8e8,0xe8e8e8e8,0xe8cacae8,0xe8eaeae8,0xcac0c0ca,0xcae0e0ca,0xcac2c2ca,0xcae2e2ca,0xcac8c8ca,0xcae8e8ca,0xcacacaca,0xcaeaeaca,0xeac0c0ea,0xeae0e0ea,0xeac2c2ea,0xeae2e2ea,0xeac8c8ea,0xeae8e8ea,0xeacacaea,0xeaeaeaea,0xc0c0c0c0,0xc0f0f0c0,0xc0c3c3c0,0xc0f3f3c0,0xc0ccccc0,0xc0fcfcc0,0xc0cfcfc0,0xc0ffffc0,0xf0c0c0f0,0xf0f0f0f0,0xf0c3c3f0,0xf0f3f3f0,0xf0ccccf0,0xf0fcfcf0,0xf0cfcff0,0xf0fffff0,0xc3c0c0c3,0xc3f0f0c3,0xc3c3c3c3,0xc3f3f3c3,0xc3ccccc3,0xc3fcfcc3,0xc3cfcfc3,0xc3ffffc3,0xf3c0c0f3,0xf3f0f0f3,0xf3c3c3f3,0xf3f3f3f3,0xf3ccccf3,0xf3fcfcf3,0xf3cfcff3,0xf3fffff3,0xccc0c0cc,0xccf0f0cc,0xccc3c3cc,0xccf3f3cc,0xcccccccc,0xccfcfccc,0xcccfcfcc,0xccffffcc,0xfcc0c0fc,0xfcf0f0fc,0xfcc3c3fc,0xfcf3f3fc,0xfcccccfc,0xfcfcfcfc,0xfccfcffc,0xfcfffffc,0xcfc0c0cf,0xcff0f0cf,0xcfc3c3cf,0xcff3f3cf,0xcfcccccf,0xcffcfccf,0xcfcfcfcf,0xcfffffcf,0xffc0c0ff,0xfff0f0ff,0xffc3c3ff,0xfff3f3ff,0xffccccff,0xfffcfcff,0xffcfcfff,0xffffffff}, -{0xc0c0c0c0,0xc0e0c0e0,0xc0c2c0c2,0xc0e2c0e2,0xc0c8c0c8,0xc0e8c0e8,0xc0cac0ca,0xc0eac0ea,0xe0c0e0c0,0xe0e0e0e0,0xe0c2e0c2,0xe0e2e0e2,0xe0c8e0c8,0xe0e8e0e8,0xe0cae0ca,0xe0eae0ea,0xc2c0c2c0,0xc2e0c2e0,0xc2c2c2c2,0xc2e2c2e2,0xc2c8c2c8,0xc2e8c2e8,0xc2cac2ca,0xc2eac2ea,0xe2c0e2c0,0xe2e0e2e0,0xe2c2e2c2,0xe2e2e2e2,0xe2c8e2c8,0xe2e8e2e8,0xe2cae2ca,0xe2eae2ea,0xc8c0c8c0,0xc8e0c8e0,0xc8c2c8c2,0xc8e2c8e2,0xc8c8c8c8,0xc8e8c8e8,0xc8cac8ca,0xc8eac8ea,0xe8c0e8c0,0xe8e0e8e0,0xe8c2e8c2,0xe8e2e8e2,0xe8c8e8c8,0xe8e8e8e8,0xe8cae8ca,0xe8eae8ea,0xcac0cac0,0xcae0cae0,0xcac2cac2,0xcae2cae2,0xcac8cac8,0xcae8cae8,0xcacacaca,0xcaeacaea,0xeac0eac0,0xeae0eae0,0xeac2eac2,0xeae2eae2,0xeac8eac8,0xeae8eae8,0xeacaeaca,0xeaeaeaea,0xc0c0c0c0,0xc0f0c0f0,0xc0c3c0c3,0xc0f3c0f3,0xc0ccc0cc,0xc0fcc0fc,0xc0cfc0cf,0xc0ffc0ff,0xf0c0f0c0,0xf0f0f0f0,0xf0c3f0c3,0xf0f3f0f3,0xf0ccf0cc,0xf0fcf0fc,0xf0cff0cf,0xf0fff0ff,0xc3c0c3c0,0xc3f0c3f0,0xc3c3c3c3,0xc3f3c3f3,0xc3ccc3cc,0xc3fcc3fc,0xc3cfc3cf,0xc3ffc3ff,0xf3c0f3c0,0xf3f0f3f0,0xf3c3f3c3,0xf3f3f3f3,0xf3ccf3cc,0xf3fcf3fc,0xf3cff3cf,0xf3fff3ff,0xccc0ccc0,0xccf0ccf0,0xccc3ccc3,0xccf3ccf3,0xcccccccc,0xccfcccfc,0xcccfcccf,0xccffccff,0xfcc0fcc0,0xfcf0fcf0,0xfcc3fcc3,0xfcf3fcf3,0xfcccfccc,0xfcfcfcfc,0xfccffccf,0xfcfffcff,0xcfc0cfc0,0xcff0cff0,0xcfc3cfc3,0xcff3cff3,0xcfcccfcc,0xcffccffc,0xcfcfcfcf,0xcfffcfff,0xffc0ffc0,0xfff0fff0,0xffc3ffc3,0xfff3fff3,0xffccffcc,0xfffcfffc,0xffcfffcf,0xffffffff,0xc0c0c0c0,0xc0e0c0e0,0xc0c2c0c2,0xc0e2c0e2,0xc0c8c0c8,0xc0e8c0e8,0xc0cac0ca,0xc0eac0ea,0xe0c0e0c0,0xe0e0e0e0,0xe0c2e0c2,0xe0e2e0e2,0xe0c8e0c8,0xe0e8e0e8,0xe0cae0ca,0xe0eae0ea,0xc2c0c2c0,0xc2e0c2e0,0xc2c2c2c2,0xc2e2c2e2,0xc2c8c2c8,0xc2e8c2e8,0xc2cac2ca,0xc2eac2ea,0xe2c0e2c0,0xe2e0e2e0,0xe2c2e2c2,0xe2e2e2e2,0xe2c8e2c8,0xe2e8e2e8,0xe2cae2ca,0xe2eae2ea,0xc8c0c8c0,0xc8e0c8e0,0xc8c2c8c2,0xc8e2c8e2,0xc8c8c8c8,0xc8e8c8e8,0xc8cac8ca,0xc8eac8ea,0xe8c0e8c0,0xe8e0e8e0,0xe8c2e8c2,0xe8e2e8e2,0xe8c8e8c8,0xe8e8e8e8,0xe8cae8ca,0xe8eae8ea,0xcac0cac0,0xcae0cae0,0xcac2cac2,0xcae2cae2,0xcac8cac8,0xcae8cae8,0xcacacaca,0xcaeacaea,0xeac0eac0,0xeae0eae0,0xeac2eac2,0xeae2eae2,0xeac8eac8,0xeae8eae8,0xeacaeaca,0xeaeaeaea,0xc0c0c0c0,0xc0f0c0f0,0xc0c3c0c3,0xc0f3c0f3,0xc0ccc0cc,0xc0fcc0fc,0xc0cfc0cf,0xc0ffc0ff,0xf0c0f0c0,0xf0f0f0f0,0xf0c3f0c3,0xf0f3f0f3,0xf0ccf0cc,0xf0fcf0fc,0xf0cff0cf,0xf0fff0ff,0xc3c0c3c0,0xc3f0c3f0,0xc3c3c3c3,0xc3f3c3f3,0xc3ccc3cc,0xc3fcc3fc,0xc3cfc3cf,0xc3ffc3ff,0xf3c0f3c0,0xf3f0f3f0,0xf3c3f3c3,0xf3f3f3f3,0xf3ccf3cc,0xf3fcf3fc,0xf3cff3cf,0xf3fff3ff,0xccc0ccc0,0xccf0ccf0,0xccc3ccc3,0xccf3ccf3,0xcccccccc,0xccfcccfc,0xcccfcccf,0xccffccff,0xfcc0fcc0,0xfcf0fcf0,0xfcc3fcc3,0xfcf3fcf3,0xfcccfccc,0xfcfcfcfc,0xfccffccf,0xfcfffcff,0xcfc0cfc0,0xcff0cff0,0xcfc3cfc3,0xcff3cff3,0xcfcccfcc,0xcffccffc,0xcfcfcfcf,0xcfffcfff,0xffc0ffc0,0xfff0fff0,0xffc3ffc3,0xfff3fff3,0xffccffcc,0xfffcfffc,0xffcfffcf,0xffffffff}, -{0xc0c0c0c0,0xc0e0e0e0,0xc0c2c2c2,0xc0e2e2e2,0xc0c8c8c8,0xc0e8e8e8,0xc0cacaca,0xc0eaeaea,0xe0c0c0c0,0xe0e0e0e0,0xe0c2c2c2,0xe0e2e2e2,0xe0c8c8c8,0xe0e8e8e8,0xe0cacaca,0xe0eaeaea,0xc2c0c0c0,0xc2e0e0e0,0xc2c2c2c2,0xc2e2e2e2,0xc2c8c8c8,0xc2e8e8e8,0xc2cacaca,0xc2eaeaea,0xe2c0c0c0,0xe2e0e0e0,0xe2c2c2c2,0xe2e2e2e2,0xe2c8c8c8,0xe2e8e8e8,0xe2cacaca,0xe2eaeaea,0xc8c0c0c0,0xc8e0e0e0,0xc8c2c2c2,0xc8e2e2e2,0xc8c8c8c8,0xc8e8e8e8,0xc8cacaca,0xc8eaeaea,0xe8c0c0c0,0xe8e0e0e0,0xe8c2c2c2,0xe8e2e2e2,0xe8c8c8c8,0xe8e8e8e8,0xe8cacaca,0xe8eaeaea,0xcac0c0c0,0xcae0e0e0,0xcac2c2c2,0xcae2e2e2,0xcac8c8c8,0xcae8e8e8,0xcacacaca,0xcaeaeaea,0xeac0c0c0,0xeae0e0e0,0xeac2c2c2,0xeae2e2e2,0xeac8c8c8,0xeae8e8e8,0xeacacaca,0xeaeaeaea,0xc0c0c0c0,0xc0f0f0f0,0xc0c3c3c3,0xc0f3f3f3,0xc0cccccc,0xc0fcfcfc,0xc0cfcfcf,0xc0ffffff,0xf0c0c0c0,0xf0f0f0f0,0xf0c3c3c3,0xf0f3f3f3,0xf0cccccc,0xf0fcfcfc,0xf0cfcfcf,0xf0ffffff,0xc3c0c0c0,0xc3f0f0f0,0xc3c3c3c3,0xc3f3f3f3,0xc3cccccc,0xc3fcfcfc,0xc3cfcfcf,0xc3ffffff,0xf3c0c0c0,0xf3f0f0f0,0xf3c3c3c3,0xf3f3f3f3,0xf3cccccc,0xf3fcfcfc,0xf3cfcfcf,0xf3ffffff,0xccc0c0c0,0xccf0f0f0,0xccc3c3c3,0xccf3f3f3,0xcccccccc,0xccfcfcfc,0xcccfcfcf,0xccffffff,0xfcc0c0c0,0xfcf0f0f0,0xfcc3c3c3,0xfcf3f3f3,0xfccccccc,0xfcfcfcfc,0xfccfcfcf,0xfcffffff,0xcfc0c0c0,0xcff0f0f0,0xcfc3c3c3,0xcff3f3f3,0xcfcccccc,0xcffcfcfc,0xcfcfcfcf,0xcfffffff,0xffc0c0c0,0xfff0f0f0,0xffc3c3c3,0xfff3f3f3,0xffcccccc,0xfffcfcfc,0xffcfcfcf,0xffffffff,0xc0c0c0c0,0xc0e0e0e0,0xc0c2c2c2,0xc0e2e2e2,0xc0c8c8c8,0xc0e8e8e8,0xc0cacaca,0xc0eaeaea,0xe0c0c0c0,0xe0e0e0e0,0xe0c2c2c2,0xe0e2e2e2,0xe0c8c8c8,0xe0e8e8e8,0xe0cacaca,0xe0eaeaea,0xc2c0c0c0,0xc2e0e0e0,0xc2c2c2c2,0xc2e2e2e2,0xc2c8c8c8,0xc2e8e8e8,0xc2cacaca,0xc2eaeaea,0xe2c0c0c0,0xe2e0e0e0,0xe2c2c2c2,0xe2e2e2e2,0xe2c8c8c8,0xe2e8e8e8,0xe2cacaca,0xe2eaeaea,0xc8c0c0c0,0xc8e0e0e0,0xc8c2c2c2,0xc8e2e2e2,0xc8c8c8c8,0xc8e8e8e8,0xc8cacaca,0xc8eaeaea,0xe8c0c0c0,0xe8e0e0e0,0xe8c2c2c2,0xe8e2e2e2,0xe8c8c8c8,0xe8e8e8e8,0xe8cacaca,0xe8eaeaea,0xcac0c0c0,0xcae0e0e0,0xcac2c2c2,0xcae2e2e2,0xcac8c8c8,0xcae8e8e8,0xcacacaca,0xcaeaeaea,0xeac0c0c0,0xeae0e0e0,0xeac2c2c2,0xeae2e2e2,0xeac8c8c8,0xeae8e8e8,0xeacacaca,0xeaeaeaea,0xc0c0c0c0,0xc0f0f0f0,0xc0c3c3c3,0xc0f3f3f3,0xc0cccccc,0xc0fcfcfc,0xc0cfcfcf,0xc0ffffff,0xf0c0c0c0,0xf0f0f0f0,0xf0c3c3c3,0xf0f3f3f3,0xf0cccccc,0xf0fcfcfc,0xf0cfcfcf,0xf0ffffff,0xc3c0c0c0,0xc3f0f0f0,0xc3c3c3c3,0xc3f3f3f3,0xc3cccccc,0xc3fcfcfc,0xc3cfcfcf,0xc3ffffff,0xf3c0c0c0,0xf3f0f0f0,0xf3c3c3c3,0xf3f3f3f3,0xf3cccccc,0xf3fcfcfc,0xf3cfcfcf,0xf3ffffff,0xccc0c0c0,0xccf0f0f0,0xccc3c3c3,0xccf3f3f3,0xcccccccc,0xccfcfcfc,0xcccfcfcf,0xccffffff,0xfcc0c0c0,0xfcf0f0f0,0xfcc3c3c3,0xfcf3f3f3,0xfccccccc,0xfcfcfcfc,0xfccfcfcf,0xfcffffff,0xcfc0c0c0,0xcff0f0f0,0xcfc3c3c3,0xcff3f3f3,0xcfcccccc,0xcffcfcfc,0xcfcfcfcf,0xcfffffff,0xffc0c0c0,0xfff0f0f0,0xffc3c3c3,0xfff3f3f3,0xffcccccc,0xfffcfcfc,0xffcfcfcf,0xffffffff}, -{0xc0c0c0c0,0xe0e0c0c0,0xc2c2c0c0,0xe2e2c0c0,0xc8c8c0c0,0xe8e8c0c0,0xcacac0c0,0xeaeac0c0,0xc0c0e0e0,0xe0e0e0e0,0xc2c2e0e0,0xe2e2e0e0,0xc8c8e0e0,0xe8e8e0e0,0xcacae0e0,0xeaeae0e0,0xc0c0c2c2,0xe0e0c2c2,0xc2c2c2c2,0xe2e2c2c2,0xc8c8c2c2,0xe8e8c2c2,0xcacac2c2,0xeaeac2c2,0xc0c0e2e2,0xe0e0e2e2,0xc2c2e2e2,0xe2e2e2e2,0xc8c8e2e2,0xe8e8e2e2,0xcacae2e2,0xeaeae2e2,0xc0c0c8c8,0xe0e0c8c8,0xc2c2c8c8,0xe2e2c8c8,0xc8c8c8c8,0xe8e8c8c8,0xcacac8c8,0xeaeac8c8,0xc0c0e8e8,0xe0e0e8e8,0xc2c2e8e8,0xe2e2e8e8,0xc8c8e8e8,0xe8e8e8e8,0xcacae8e8,0xeaeae8e8,0xc0c0caca,0xe0e0caca,0xc2c2caca,0xe2e2caca,0xc8c8caca,0xe8e8caca,0xcacacaca,0xeaeacaca,0xc0c0eaea,0xe0e0eaea,0xc2c2eaea,0xe2e2eaea,0xc8c8eaea,0xe8e8eaea,0xcacaeaea,0xeaeaeaea,0xc0c0c0c0,0xf0f0c0c0,0xc3c3c0c0,0xf3f3c0c0,0xccccc0c0,0xfcfcc0c0,0xcfcfc0c0,0xffffc0c0,0xc0c0f0f0,0xf0f0f0f0,0xc3c3f0f0,0xf3f3f0f0,0xccccf0f0,0xfcfcf0f0,0xcfcff0f0,0xfffff0f0,0xc0c0c3c3,0xf0f0c3c3,0xc3c3c3c3,0xf3f3c3c3,0xccccc3c3,0xfcfcc3c3,0xcfcfc3c3,0xffffc3c3,0xc0c0f3f3,0xf0f0f3f3,0xc3c3f3f3,0xf3f3f3f3,0xccccf3f3,0xfcfcf3f3,0xcfcff3f3,0xfffff3f3,0xc0c0cccc,0xf0f0cccc,0xc3c3cccc,0xf3f3cccc,0xcccccccc,0xfcfccccc,0xcfcfcccc,0xffffcccc,0xc0c0fcfc,0xf0f0fcfc,0xc3c3fcfc,0xf3f3fcfc,0xccccfcfc,0xfcfcfcfc,0xcfcffcfc,0xfffffcfc,0xc0c0cfcf,0xf0f0cfcf,0xc3c3cfcf,0xf3f3cfcf,0xcccccfcf,0xfcfccfcf,0xcfcfcfcf,0xffffcfcf,0xc0c0ffff,0xf0f0ffff,0xc3c3ffff,0xf3f3ffff,0xccccffff,0xfcfcffff,0xcfcfffff,0xffffffff,0xc0c0c0c0,0xe0e0c0c0,0xc2c2c0c0,0xe2e2c0c0,0xc8c8c0c0,0xe8e8c0c0,0xcacac0c0,0xeaeac0c0,0xc0c0e0e0,0xe0e0e0e0,0xc2c2e0e0,0xe2e2e0e0,0xc8c8e0e0,0xe8e8e0e0,0xcacae0e0,0xeaeae0e0,0xc0c0c2c2,0xe0e0c2c2,0xc2c2c2c2,0xe2e2c2c2,0xc8c8c2c2,0xe8e8c2c2,0xcacac2c2,0xeaeac2c2,0xc0c0e2e2,0xe0e0e2e2,0xc2c2e2e2,0xe2e2e2e2,0xc8c8e2e2,0xe8e8e2e2,0xcacae2e2,0xeaeae2e2,0xc0c0c8c8,0xe0e0c8c8,0xc2c2c8c8,0xe2e2c8c8,0xc8c8c8c8,0xe8e8c8c8,0xcacac8c8,0xeaeac8c8,0xc0c0e8e8,0xe0e0e8e8,0xc2c2e8e8,0xe2e2e8e8,0xc8c8e8e8,0xe8e8e8e8,0xcacae8e8,0xeaeae8e8,0xc0c0caca,0xe0e0caca,0xc2c2caca,0xe2e2caca,0xc8c8caca,0xe8e8caca,0xcacacaca,0xeaeacaca,0xc0c0eaea,0xe0e0eaea,0xc2c2eaea,0xe2e2eaea,0xc8c8eaea,0xe8e8eaea,0xcacaeaea,0xeaeaeaea,0xc0c0c0c0,0xf0f0c0c0,0xc3c3c0c0,0xf3f3c0c0,0xccccc0c0,0xfcfcc0c0,0xcfcfc0c0,0xffffc0c0,0xc0c0f0f0,0xf0f0f0f0,0xc3c3f0f0,0xf3f3f0f0,0xccccf0f0,0xfcfcf0f0,0xcfcff0f0,0xfffff0f0,0xc0c0c3c3,0xf0f0c3c3,0xc3c3c3c3,0xf3f3c3c3,0xccccc3c3,0xfcfcc3c3,0xcfcfc3c3,0xffffc3c3,0xc0c0f3f3,0xf0f0f3f3,0xc3c3f3f3,0xf3f3f3f3,0xccccf3f3,0xfcfcf3f3,0xcfcff3f3,0xfffff3f3,0xc0c0cccc,0xf0f0cccc,0xc3c3cccc,0xf3f3cccc,0xcccccccc,0xfcfccccc,0xcfcfcccc,0xffffcccc,0xc0c0fcfc,0xf0f0fcfc,0xc3c3fcfc,0xf3f3fcfc,0xccccfcfc,0xfcfcfcfc,0xcfcffcfc,0xfffffcfc,0xc0c0cfcf,0xf0f0cfcf,0xc3c3cfcf,0xf3f3cfcf,0xcccccfcf,0xfcfccfcf,0xcfcfcfcf,0xffffcfcf,0xc0c0ffff,0xf0f0ffff,0xc3c3ffff,0xf3f3ffff,0xccccffff,0xfcfcffff,0xcfcfffff,0xffffffff}, -{0xc0c0c0c0,0xe0e0e0c0,0xc2c2c2c0,0xe2e2e2c0,0xc8c8c8c0,0xe8e8e8c0,0xcacacac0,0xeaeaeac0,0xc0c0c0e0,0xe0e0e0e0,0xc2c2c2e0,0xe2e2e2e0,0xc8c8c8e0,0xe8e8e8e0,0xcacacae0,0xeaeaeae0,0xc0c0c0c2,0xe0e0e0c2,0xc2c2c2c2,0xe2e2e2c2,0xc8c8c8c2,0xe8e8e8c2,0xcacacac2,0xeaeaeac2,0xc0c0c0e2,0xe0e0e0e2,0xc2c2c2e2,0xe2e2e2e2,0xc8c8c8e2,0xe8e8e8e2,0xcacacae2,0xeaeaeae2,0xc0c0c0c8,0xe0e0e0c8,0xc2c2c2c8,0xe2e2e2c8,0xc8c8c8c8,0xe8e8e8c8,0xcacacac8,0xeaeaeac8,0xc0c0c0e8,0xe0e0e0e8,0xc2c2c2e8,0xe2e2e2e8,0xc8c8c8e8,0xe8e8e8e8,0xcacacae8,0xeaeaeae8,0xc0c0c0ca,0xe0e0e0ca,0xc2c2c2ca,0xe2e2e2ca,0xc8c8c8ca,0xe8e8e8ca,0xcacacaca,0xeaeaeaca,0xc0c0c0ea,0xe0e0e0ea,0xc2c2c2ea,0xe2e2e2ea,0xc8c8c8ea,0xe8e8e8ea,0xcacacaea,0xeaeaeaea,0xc0c0c0c0,0xf0f0f0c0,0xc3c3c3c0,0xf3f3f3c0,0xccccccc0,0xfcfcfcc0,0xcfcfcfc0,0xffffffc0,0xc0c0c0f0,0xf0f0f0f0,0xc3c3c3f0,0xf3f3f3f0,0xccccccf0,0xfcfcfcf0,0xcfcfcff0,0xfffffff0,0xc0c0c0c3,0xf0f0f0c3,0xc3c3c3c3,0xf3f3f3c3,0xccccccc3,0xfcfcfcc3,0xcfcfcfc3,0xffffffc3,0xc0c0c0f3,0xf0f0f0f3,0xc3c3c3f3,0xf3f3f3f3,0xccccccf3,0xfcfcfcf3,0xcfcfcff3,0xfffffff3,0xc0c0c0cc,0xf0f0f0cc,0xc3c3c3cc,0xf3f3f3cc,0xcccccccc,0xfcfcfccc,0xcfcfcfcc,0xffffffcc,0xc0c0c0fc,0xf0f0f0fc,0xc3c3c3fc,0xf3f3f3fc,0xccccccfc,0xfcfcfcfc,0xcfcfcffc,0xfffffffc,0xc0c0c0cf,0xf0f0f0cf,0xc3c3c3cf,0xf3f3f3cf,0xcccccccf,0xfcfcfccf,0xcfcfcfcf,0xffffffcf,0xc0c0c0ff,0xf0f0f0ff,0xc3c3c3ff,0xf3f3f3ff,0xccccccff,0xfcfcfcff,0xcfcfcfff,0xffffffff,0xc0c0c0c0,0xe0e0e0c0,0xc2c2c2c0,0xe2e2e2c0,0xc8c8c8c0,0xe8e8e8c0,0xcacacac0,0xeaeaeac0,0xc0c0c0e0,0xe0e0e0e0,0xc2c2c2e0,0xe2e2e2e0,0xc8c8c8e0,0xe8e8e8e0,0xcacacae0,0xeaeaeae0,0xc0c0c0c2,0xe0e0e0c2,0xc2c2c2c2,0xe2e2e2c2,0xc8c8c8c2,0xe8e8e8c2,0xcacacac2,0xeaeaeac2,0xc0c0c0e2,0xe0e0e0e2,0xc2c2c2e2,0xe2e2e2e2,0xc8c8c8e2,0xe8e8e8e2,0xcacacae2,0xeaeaeae2,0xc0c0c0c8,0xe0e0e0c8,0xc2c2c2c8,0xe2e2e2c8,0xc8c8c8c8,0xe8e8e8c8,0xcacacac8,0xeaeaeac8,0xc0c0c0e8,0xe0e0e0e8,0xc2c2c2e8,0xe2e2e2e8,0xc8c8c8e8,0xe8e8e8e8,0xcacacae8,0xeaeaeae8,0xc0c0c0ca,0xe0e0e0ca,0xc2c2c2ca,0xe2e2e2ca,0xc8c8c8ca,0xe8e8e8ca,0xcacacaca,0xeaeaeaca,0xc0c0c0ea,0xe0e0e0ea,0xc2c2c2ea,0xe2e2e2ea,0xc8c8c8ea,0xe8e8e8ea,0xcacacaea,0xeaeaeaea,0xc0c0c0c0,0xf0f0f0c0,0xc3c3c3c0,0xf3f3f3c0,0xccccccc0,0xfcfcfcc0,0xcfcfcfc0,0xffffffc0,0xc0c0c0f0,0xf0f0f0f0,0xc3c3c3f0,0xf3f3f3f0,0xccccccf0,0xfcfcfcf0,0xcfcfcff0,0xfffffff0,0xc0c0c0c3,0xf0f0f0c3,0xc3c3c3c3,0xf3f3f3c3,0xccccccc3,0xfcfcfcc3,0xcfcfcfc3,0xffffffc3,0xc0c0c0f3,0xf0f0f0f3,0xc3c3c3f3,0xf3f3f3f3,0xccccccf3,0xfcfcfcf3,0xcfcfcff3,0xfffffff3,0xc0c0c0cc,0xf0f0f0cc,0xc3c3c3cc,0xf3f3f3cc,0xcccccccc,0xfcfcfccc,0xcfcfcfcc,0xffffffcc,0xc0c0c0fc,0xf0f0f0fc,0xc3c3c3fc,0xf3f3f3fc,0xccccccfc,0xfcfcfcfc,0xcfcfcffc,0xfffffffc,0xc0c0c0cf,0xf0f0f0cf,0xc3c3c3cf,0xf3f3f3cf,0xcccccccf,0xfcfcfccf,0xcfcfcfcf,0xffffffcf,0xc0c0c0ff,0xf0f0f0ff,0xc3c3c3ff,0xf3f3f3ff,0xccccccff,0xfcfcfcff,0xcfcfcfff,0xffffffff}, -{0xc0c0c0c0,0xe0e0c0e0,0xc2c2c0c2,0xe2e2c0e2,0xc8c8c0c8,0xe8e8c0e8,0xcacac0ca,0xeaeac0ea,0xc0c0e0c0,0xe0e0e0e0,0xc2c2e0c2,0xe2e2e0e2,0xc8c8e0c8,0xe8e8e0e8,0xcacae0ca,0xeaeae0ea,0xc0c0c2c0,0xe0e0c2e0,0xc2c2c2c2,0xe2e2c2e2,0xc8c8c2c8,0xe8e8c2e8,0xcacac2ca,0xeaeac2ea,0xc0c0e2c0,0xe0e0e2e0,0xc2c2e2c2,0xe2e2e2e2,0xc8c8e2c8,0xe8e8e2e8,0xcacae2ca,0xeaeae2ea,0xc0c0c8c0,0xe0e0c8e0,0xc2c2c8c2,0xe2e2c8e2,0xc8c8c8c8,0xe8e8c8e8,0xcacac8ca,0xeaeac8ea,0xc0c0e8c0,0xe0e0e8e0,0xc2c2e8c2,0xe2e2e8e2,0xc8c8e8c8,0xe8e8e8e8,0xcacae8ca,0xeaeae8ea,0xc0c0cac0,0xe0e0cae0,0xc2c2cac2,0xe2e2cae2,0xc8c8cac8,0xe8e8cae8,0xcacacaca,0xeaeacaea,0xc0c0eac0,0xe0e0eae0,0xc2c2eac2,0xe2e2eae2,0xc8c8eac8,0xe8e8eae8,0xcacaeaca,0xeaeaeaea,0xc0c0c0c0,0xf0f0c0f0,0xc3c3c0c3,0xf3f3c0f3,0xccccc0cc,0xfcfcc0fc,0xcfcfc0cf,0xffffc0ff,0xc0c0f0c0,0xf0f0f0f0,0xc3c3f0c3,0xf3f3f0f3,0xccccf0cc,0xfcfcf0fc,0xcfcff0cf,0xfffff0ff,0xc0c0c3c0,0xf0f0c3f0,0xc3c3c3c3,0xf3f3c3f3,0xccccc3cc,0xfcfcc3fc,0xcfcfc3cf,0xffffc3ff,0xc0c0f3c0,0xf0f0f3f0,0xc3c3f3c3,0xf3f3f3f3,0xccccf3cc,0xfcfcf3fc,0xcfcff3cf,0xfffff3ff,0xc0c0ccc0,0xf0f0ccf0,0xc3c3ccc3,0xf3f3ccf3,0xcccccccc,0xfcfcccfc,0xcfcfcccf,0xffffccff,0xc0c0fcc0,0xf0f0fcf0,0xc3c3fcc3,0xf3f3fcf3,0xccccfccc,0xfcfcfcfc,0xcfcffccf,0xfffffcff,0xc0c0cfc0,0xf0f0cff0,0xc3c3cfc3,0xf3f3cff3,0xcccccfcc,0xfcfccffc,0xcfcfcfcf,0xffffcfff,0xc0c0ffc0,0xf0f0fff0,0xc3c3ffc3,0xf3f3fff3,0xccccffcc,0xfcfcfffc,0xcfcfffcf,0xffffffff,0xc0c0c0c0,0xe0e0c0e0,0xc2c2c0c2,0xe2e2c0e2,0xc8c8c0c8,0xe8e8c0e8,0xcacac0ca,0xeaeac0ea,0xc0c0e0c0,0xe0e0e0e0,0xc2c2e0c2,0xe2e2e0e2,0xc8c8e0c8,0xe8e8e0e8,0xcacae0ca,0xeaeae0ea,0xc0c0c2c0,0xe0e0c2e0,0xc2c2c2c2,0xe2e2c2e2,0xc8c8c2c8,0xe8e8c2e8,0xcacac2ca,0xeaeac2ea,0xc0c0e2c0,0xe0e0e2e0,0xc2c2e2c2,0xe2e2e2e2,0xc8c8e2c8,0xe8e8e2e8,0xcacae2ca,0xeaeae2ea,0xc0c0c8c0,0xe0e0c8e0,0xc2c2c8c2,0xe2e2c8e2,0xc8c8c8c8,0xe8e8c8e8,0xcacac8ca,0xeaeac8ea,0xc0c0e8c0,0xe0e0e8e0,0xc2c2e8c2,0xe2e2e8e2,0xc8c8e8c8,0xe8e8e8e8,0xcacae8ca,0xeaeae8ea,0xc0c0cac0,0xe0e0cae0,0xc2c2cac2,0xe2e2cae2,0xc8c8cac8,0xe8e8cae8,0xcacacaca,0xeaeacaea,0xc0c0eac0,0xe0e0eae0,0xc2c2eac2,0xe2e2eae2,0xc8c8eac8,0xe8e8eae8,0xcacaeaca,0xeaeaeaea,0xc0c0c0c0,0xf0f0c0f0,0xc3c3c0c3,0xf3f3c0f3,0xccccc0cc,0xfcfcc0fc,0xcfcfc0cf,0xffffc0ff,0xc0c0f0c0,0xf0f0f0f0,0xc3c3f0c3,0xf3f3f0f3,0xccccf0cc,0xfcfcf0fc,0xcfcff0cf,0xfffff0ff,0xc0c0c3c0,0xf0f0c3f0,0xc3c3c3c3,0xf3f3c3f3,0xccccc3cc,0xfcfcc3fc,0xcfcfc3cf,0xffffc3ff,0xc0c0f3c0,0xf0f0f3f0,0xc3c3f3c3,0xf3f3f3f3,0xccccf3cc,0xfcfcf3fc,0xcfcff3cf,0xfffff3ff,0xc0c0ccc0,0xf0f0ccf0,0xc3c3ccc3,0xf3f3ccf3,0xcccccccc,0xfcfcccfc,0xcfcfcccf,0xffffccff,0xc0c0fcc0,0xf0f0fcf0,0xc3c3fcc3,0xf3f3fcf3,0xccccfccc,0xfcfcfcfc,0xcfcffccf,0xfffffcff,0xc0c0cfc0,0xf0f0cff0,0xc3c3cfc3,0xf3f3cff3,0xcccccfcc,0xfcfccffc,0xcfcfcfcf,0xffffcfff,0xc0c0ffc0,0xf0f0fff0,0xc3c3ffc3,0xf3f3fff3,0xccccffcc,0xfcfcfffc,0xcfcfffcf,0xffffffff}, -{0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff} -}; - -// void precalcAluBytes() { - -// uint16_t specfast_colors[128]; // Array for faster color calc in Draw - -// unsigned int pal[2],b0,b1,b2,b3; - -// // Calc array for faster color calcs in Draw -// for (int i = 0; i < (NUM_SPECTRUM_COLORS >> 1); i++) { -// // Normal -// specfast_colors[i] = spectrum_colors[i]; -// specfast_colors[i << 3] = spectrum_colors[i]; -// // Bright -// specfast_colors[i | 0x40] = spectrum_colors[i + (NUM_SPECTRUM_COLORS >> 1)]; -// specfast_colors[(i << 3) | 0x40] = spectrum_colors[i + (NUM_SPECTRUM_COLORS >> 1)]; -// } +// // ALUBYTES +// static const uint32_t AluBytes[16][256]={ +// {0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xe0e0e0e0,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xc2c2c2c2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xe2e2e2e2,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xc8c8c8c8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xe8e8e8e8,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xcacacaca,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xeaeaeaea,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xc0c0c0c0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xf0f0f0f0,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xc3c3c3c3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xf3f3f3f3,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xcccccccc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xfcfcfcfc,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xcfcfcfcf,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff}, +// {0xc0c0c0c0,0xc0c0e0c0,0xc0c0c2c0,0xc0c0e2c0,0xc0c0c8c0,0xc0c0e8c0,0xc0c0cac0,0xc0c0eac0,0xe0e0c0e0,0xe0e0e0e0,0xe0e0c2e0,0xe0e0e2e0,0xe0e0c8e0,0xe0e0e8e0,0xe0e0cae0,0xe0e0eae0,0xc2c2c0c2,0xc2c2e0c2,0xc2c2c2c2,0xc2c2e2c2,0xc2c2c8c2,0xc2c2e8c2,0xc2c2cac2,0xc2c2eac2,0xe2e2c0e2,0xe2e2e0e2,0xe2e2c2e2,0xe2e2e2e2,0xe2e2c8e2,0xe2e2e8e2,0xe2e2cae2,0xe2e2eae2,0xc8c8c0c8,0xc8c8e0c8,0xc8c8c2c8,0xc8c8e2c8,0xc8c8c8c8,0xc8c8e8c8,0xc8c8cac8,0xc8c8eac8,0xe8e8c0e8,0xe8e8e0e8,0xe8e8c2e8,0xe8e8e2e8,0xe8e8c8e8,0xe8e8e8e8,0xe8e8cae8,0xe8e8eae8,0xcacac0ca,0xcacae0ca,0xcacac2ca,0xcacae2ca,0xcacac8ca,0xcacae8ca,0xcacacaca,0xcacaeaca,0xeaeac0ea,0xeaeae0ea,0xeaeac2ea,0xeaeae2ea,0xeaeac8ea,0xeaeae8ea,0xeaeacaea,0xeaeaeaea,0xc0c0c0c0,0xc0c0f0c0,0xc0c0c3c0,0xc0c0f3c0,0xc0c0ccc0,0xc0c0fcc0,0xc0c0cfc0,0xc0c0ffc0,0xf0f0c0f0,0xf0f0f0f0,0xf0f0c3f0,0xf0f0f3f0,0xf0f0ccf0,0xf0f0fcf0,0xf0f0cff0,0xf0f0fff0,0xc3c3c0c3,0xc3c3f0c3,0xc3c3c3c3,0xc3c3f3c3,0xc3c3ccc3,0xc3c3fcc3,0xc3c3cfc3,0xc3c3ffc3,0xf3f3c0f3,0xf3f3f0f3,0xf3f3c3f3,0xf3f3f3f3,0xf3f3ccf3,0xf3f3fcf3,0xf3f3cff3,0xf3f3fff3,0xccccc0cc,0xccccf0cc,0xccccc3cc,0xccccf3cc,0xcccccccc,0xccccfccc,0xcccccfcc,0xccccffcc,0xfcfcc0fc,0xfcfcf0fc,0xfcfcc3fc,0xfcfcf3fc,0xfcfcccfc,0xfcfcfcfc,0xfcfccffc,0xfcfcfffc,0xcfcfc0cf,0xcfcff0cf,0xcfcfc3cf,0xcfcff3cf,0xcfcfcccf,0xcfcffccf,0xcfcfcfcf,0xcfcfffcf,0xffffc0ff,0xfffff0ff,0xffffc3ff,0xfffff3ff,0xffffccff,0xfffffcff,0xffffcfff,0xffffffff,0xc0c0c0c0,0xc0c0e0c0,0xc0c0c2c0,0xc0c0e2c0,0xc0c0c8c0,0xc0c0e8c0,0xc0c0cac0,0xc0c0eac0,0xe0e0c0e0,0xe0e0e0e0,0xe0e0c2e0,0xe0e0e2e0,0xe0e0c8e0,0xe0e0e8e0,0xe0e0cae0,0xe0e0eae0,0xc2c2c0c2,0xc2c2e0c2,0xc2c2c2c2,0xc2c2e2c2,0xc2c2c8c2,0xc2c2e8c2,0xc2c2cac2,0xc2c2eac2,0xe2e2c0e2,0xe2e2e0e2,0xe2e2c2e2,0xe2e2e2e2,0xe2e2c8e2,0xe2e2e8e2,0xe2e2cae2,0xe2e2eae2,0xc8c8c0c8,0xc8c8e0c8,0xc8c8c2c8,0xc8c8e2c8,0xc8c8c8c8,0xc8c8e8c8,0xc8c8cac8,0xc8c8eac8,0xe8e8c0e8,0xe8e8e0e8,0xe8e8c2e8,0xe8e8e2e8,0xe8e8c8e8,0xe8e8e8e8,0xe8e8cae8,0xe8e8eae8,0xcacac0ca,0xcacae0ca,0xcacac2ca,0xcacae2ca,0xcacac8ca,0xcacae8ca,0xcacacaca,0xcacaeaca,0xeaeac0ea,0xeaeae0ea,0xeaeac2ea,0xeaeae2ea,0xeaeac8ea,0xeaeae8ea,0xeaeacaea,0xeaeaeaea,0xc0c0c0c0,0xc0c0f0c0,0xc0c0c3c0,0xc0c0f3c0,0xc0c0ccc0,0xc0c0fcc0,0xc0c0cfc0,0xc0c0ffc0,0xf0f0c0f0,0xf0f0f0f0,0xf0f0c3f0,0xf0f0f3f0,0xf0f0ccf0,0xf0f0fcf0,0xf0f0cff0,0xf0f0fff0,0xc3c3c0c3,0xc3c3f0c3,0xc3c3c3c3,0xc3c3f3c3,0xc3c3ccc3,0xc3c3fcc3,0xc3c3cfc3,0xc3c3ffc3,0xf3f3c0f3,0xf3f3f0f3,0xf3f3c3f3,0xf3f3f3f3,0xf3f3ccf3,0xf3f3fcf3,0xf3f3cff3,0xf3f3fff3,0xccccc0cc,0xccccf0cc,0xccccc3cc,0xccccf3cc,0xcccccccc,0xccccfccc,0xcccccfcc,0xccccffcc,0xfcfcc0fc,0xfcfcf0fc,0xfcfcc3fc,0xfcfcf3fc,0xfcfcccfc,0xfcfcfcfc,0xfcfccffc,0xfcfcfffc,0xcfcfc0cf,0xcfcff0cf,0xcfcfc3cf,0xcfcff3cf,0xcfcfcccf,0xcfcffccf,0xcfcfcfcf,0xcfcfffcf,0xffffc0ff,0xfffff0ff,0xffffc3ff,0xfffff3ff,0xffffccff,0xfffffcff,0xffffcfff,0xffffffff}, +// {0xc0c0c0c0,0xc0c0c0e0,0xc0c0c0c2,0xc0c0c0e2,0xc0c0c0c8,0xc0c0c0e8,0xc0c0c0ca,0xc0c0c0ea,0xe0e0e0c0,0xe0e0e0e0,0xe0e0e0c2,0xe0e0e0e2,0xe0e0e0c8,0xe0e0e0e8,0xe0e0e0ca,0xe0e0e0ea,0xc2c2c2c0,0xc2c2c2e0,0xc2c2c2c2,0xc2c2c2e2,0xc2c2c2c8,0xc2c2c2e8,0xc2c2c2ca,0xc2c2c2ea,0xe2e2e2c0,0xe2e2e2e0,0xe2e2e2c2,0xe2e2e2e2,0xe2e2e2c8,0xe2e2e2e8,0xe2e2e2ca,0xe2e2e2ea,0xc8c8c8c0,0xc8c8c8e0,0xc8c8c8c2,0xc8c8c8e2,0xc8c8c8c8,0xc8c8c8e8,0xc8c8c8ca,0xc8c8c8ea,0xe8e8e8c0,0xe8e8e8e0,0xe8e8e8c2,0xe8e8e8e2,0xe8e8e8c8,0xe8e8e8e8,0xe8e8e8ca,0xe8e8e8ea,0xcacacac0,0xcacacae0,0xcacacac2,0xcacacae2,0xcacacac8,0xcacacae8,0xcacacaca,0xcacacaea,0xeaeaeac0,0xeaeaeae0,0xeaeaeac2,0xeaeaeae2,0xeaeaeac8,0xeaeaeae8,0xeaeaeaca,0xeaeaeaea,0xc0c0c0c0,0xc0c0c0f0,0xc0c0c0c3,0xc0c0c0f3,0xc0c0c0cc,0xc0c0c0fc,0xc0c0c0cf,0xc0c0c0ff,0xf0f0f0c0,0xf0f0f0f0,0xf0f0f0c3,0xf0f0f0f3,0xf0f0f0cc,0xf0f0f0fc,0xf0f0f0cf,0xf0f0f0ff,0xc3c3c3c0,0xc3c3c3f0,0xc3c3c3c3,0xc3c3c3f3,0xc3c3c3cc,0xc3c3c3fc,0xc3c3c3cf,0xc3c3c3ff,0xf3f3f3c0,0xf3f3f3f0,0xf3f3f3c3,0xf3f3f3f3,0xf3f3f3cc,0xf3f3f3fc,0xf3f3f3cf,0xf3f3f3ff,0xccccccc0,0xccccccf0,0xccccccc3,0xccccccf3,0xcccccccc,0xccccccfc,0xcccccccf,0xccccccff,0xfcfcfcc0,0xfcfcfcf0,0xfcfcfcc3,0xfcfcfcf3,0xfcfcfccc,0xfcfcfcfc,0xfcfcfccf,0xfcfcfcff,0xcfcfcfc0,0xcfcfcff0,0xcfcfcfc3,0xcfcfcff3,0xcfcfcfcc,0xcfcfcffc,0xcfcfcfcf,0xcfcfcfff,0xffffffc0,0xfffffff0,0xffffffc3,0xfffffff3,0xffffffcc,0xfffffffc,0xffffffcf,0xffffffff,0xc0c0c0c0,0xc0c0c0e0,0xc0c0c0c2,0xc0c0c0e2,0xc0c0c0c8,0xc0c0c0e8,0xc0c0c0ca,0xc0c0c0ea,0xe0e0e0c0,0xe0e0e0e0,0xe0e0e0c2,0xe0e0e0e2,0xe0e0e0c8,0xe0e0e0e8,0xe0e0e0ca,0xe0e0e0ea,0xc2c2c2c0,0xc2c2c2e0,0xc2c2c2c2,0xc2c2c2e2,0xc2c2c2c8,0xc2c2c2e8,0xc2c2c2ca,0xc2c2c2ea,0xe2e2e2c0,0xe2e2e2e0,0xe2e2e2c2,0xe2e2e2e2,0xe2e2e2c8,0xe2e2e2e8,0xe2e2e2ca,0xe2e2e2ea,0xc8c8c8c0,0xc8c8c8e0,0xc8c8c8c2,0xc8c8c8e2,0xc8c8c8c8,0xc8c8c8e8,0xc8c8c8ca,0xc8c8c8ea,0xe8e8e8c0,0xe8e8e8e0,0xe8e8e8c2,0xe8e8e8e2,0xe8e8e8c8,0xe8e8e8e8,0xe8e8e8ca,0xe8e8e8ea,0xcacacac0,0xcacacae0,0xcacacac2,0xcacacae2,0xcacacac8,0xcacacae8,0xcacacaca,0xcacacaea,0xeaeaeac0,0xeaeaeae0,0xeaeaeac2,0xeaeaeae2,0xeaeaeac8,0xeaeaeae8,0xeaeaeaca,0xeaeaeaea,0xc0c0c0c0,0xc0c0c0f0,0xc0c0c0c3,0xc0c0c0f3,0xc0c0c0cc,0xc0c0c0fc,0xc0c0c0cf,0xc0c0c0ff,0xf0f0f0c0,0xf0f0f0f0,0xf0f0f0c3,0xf0f0f0f3,0xf0f0f0cc,0xf0f0f0fc,0xf0f0f0cf,0xf0f0f0ff,0xc3c3c3c0,0xc3c3c3f0,0xc3c3c3c3,0xc3c3c3f3,0xc3c3c3cc,0xc3c3c3fc,0xc3c3c3cf,0xc3c3c3ff,0xf3f3f3c0,0xf3f3f3f0,0xf3f3f3c3,0xf3f3f3f3,0xf3f3f3cc,0xf3f3f3fc,0xf3f3f3cf,0xf3f3f3ff,0xccccccc0,0xccccccf0,0xccccccc3,0xccccccf3,0xcccccccc,0xccccccfc,0xcccccccf,0xccccccff,0xfcfcfcc0,0xfcfcfcf0,0xfcfcfcc3,0xfcfcfcf3,0xfcfcfccc,0xfcfcfcfc,0xfcfcfccf,0xfcfcfcff,0xcfcfcfc0,0xcfcfcff0,0xcfcfcfc3,0xcfcfcff3,0xcfcfcfcc,0xcfcfcffc,0xcfcfcfcf,0xcfcfcfff,0xffffffc0,0xfffffff0,0xffffffc3,0xfffffff3,0xffffffcc,0xfffffffc,0xffffffcf,0xffffffff}, +// {0xc0c0c0c0,0xc0c0e0e0,0xc0c0c2c2,0xc0c0e2e2,0xc0c0c8c8,0xc0c0e8e8,0xc0c0caca,0xc0c0eaea,0xe0e0c0c0,0xe0e0e0e0,0xe0e0c2c2,0xe0e0e2e2,0xe0e0c8c8,0xe0e0e8e8,0xe0e0caca,0xe0e0eaea,0xc2c2c0c0,0xc2c2e0e0,0xc2c2c2c2,0xc2c2e2e2,0xc2c2c8c8,0xc2c2e8e8,0xc2c2caca,0xc2c2eaea,0xe2e2c0c0,0xe2e2e0e0,0xe2e2c2c2,0xe2e2e2e2,0xe2e2c8c8,0xe2e2e8e8,0xe2e2caca,0xe2e2eaea,0xc8c8c0c0,0xc8c8e0e0,0xc8c8c2c2,0xc8c8e2e2,0xc8c8c8c8,0xc8c8e8e8,0xc8c8caca,0xc8c8eaea,0xe8e8c0c0,0xe8e8e0e0,0xe8e8c2c2,0xe8e8e2e2,0xe8e8c8c8,0xe8e8e8e8,0xe8e8caca,0xe8e8eaea,0xcacac0c0,0xcacae0e0,0xcacac2c2,0xcacae2e2,0xcacac8c8,0xcacae8e8,0xcacacaca,0xcacaeaea,0xeaeac0c0,0xeaeae0e0,0xeaeac2c2,0xeaeae2e2,0xeaeac8c8,0xeaeae8e8,0xeaeacaca,0xeaeaeaea,0xc0c0c0c0,0xc0c0f0f0,0xc0c0c3c3,0xc0c0f3f3,0xc0c0cccc,0xc0c0fcfc,0xc0c0cfcf,0xc0c0ffff,0xf0f0c0c0,0xf0f0f0f0,0xf0f0c3c3,0xf0f0f3f3,0xf0f0cccc,0xf0f0fcfc,0xf0f0cfcf,0xf0f0ffff,0xc3c3c0c0,0xc3c3f0f0,0xc3c3c3c3,0xc3c3f3f3,0xc3c3cccc,0xc3c3fcfc,0xc3c3cfcf,0xc3c3ffff,0xf3f3c0c0,0xf3f3f0f0,0xf3f3c3c3,0xf3f3f3f3,0xf3f3cccc,0xf3f3fcfc,0xf3f3cfcf,0xf3f3ffff,0xccccc0c0,0xccccf0f0,0xccccc3c3,0xccccf3f3,0xcccccccc,0xccccfcfc,0xcccccfcf,0xccccffff,0xfcfcc0c0,0xfcfcf0f0,0xfcfcc3c3,0xfcfcf3f3,0xfcfccccc,0xfcfcfcfc,0xfcfccfcf,0xfcfcffff,0xcfcfc0c0,0xcfcff0f0,0xcfcfc3c3,0xcfcff3f3,0xcfcfcccc,0xcfcffcfc,0xcfcfcfcf,0xcfcfffff,0xffffc0c0,0xfffff0f0,0xffffc3c3,0xfffff3f3,0xffffcccc,0xfffffcfc,0xffffcfcf,0xffffffff,0xc0c0c0c0,0xc0c0e0e0,0xc0c0c2c2,0xc0c0e2e2,0xc0c0c8c8,0xc0c0e8e8,0xc0c0caca,0xc0c0eaea,0xe0e0c0c0,0xe0e0e0e0,0xe0e0c2c2,0xe0e0e2e2,0xe0e0c8c8,0xe0e0e8e8,0xe0e0caca,0xe0e0eaea,0xc2c2c0c0,0xc2c2e0e0,0xc2c2c2c2,0xc2c2e2e2,0xc2c2c8c8,0xc2c2e8e8,0xc2c2caca,0xc2c2eaea,0xe2e2c0c0,0xe2e2e0e0,0xe2e2c2c2,0xe2e2e2e2,0xe2e2c8c8,0xe2e2e8e8,0xe2e2caca,0xe2e2eaea,0xc8c8c0c0,0xc8c8e0e0,0xc8c8c2c2,0xc8c8e2e2,0xc8c8c8c8,0xc8c8e8e8,0xc8c8caca,0xc8c8eaea,0xe8e8c0c0,0xe8e8e0e0,0xe8e8c2c2,0xe8e8e2e2,0xe8e8c8c8,0xe8e8e8e8,0xe8e8caca,0xe8e8eaea,0xcacac0c0,0xcacae0e0,0xcacac2c2,0xcacae2e2,0xcacac8c8,0xcacae8e8,0xcacacaca,0xcacaeaea,0xeaeac0c0,0xeaeae0e0,0xeaeac2c2,0xeaeae2e2,0xeaeac8c8,0xeaeae8e8,0xeaeacaca,0xeaeaeaea,0xc0c0c0c0,0xc0c0f0f0,0xc0c0c3c3,0xc0c0f3f3,0xc0c0cccc,0xc0c0fcfc,0xc0c0cfcf,0xc0c0ffff,0xf0f0c0c0,0xf0f0f0f0,0xf0f0c3c3,0xf0f0f3f3,0xf0f0cccc,0xf0f0fcfc,0xf0f0cfcf,0xf0f0ffff,0xc3c3c0c0,0xc3c3f0f0,0xc3c3c3c3,0xc3c3f3f3,0xc3c3cccc,0xc3c3fcfc,0xc3c3cfcf,0xc3c3ffff,0xf3f3c0c0,0xf3f3f0f0,0xf3f3c3c3,0xf3f3f3f3,0xf3f3cccc,0xf3f3fcfc,0xf3f3cfcf,0xf3f3ffff,0xccccc0c0,0xccccf0f0,0xccccc3c3,0xccccf3f3,0xcccccccc,0xccccfcfc,0xcccccfcf,0xccccffff,0xfcfcc0c0,0xfcfcf0f0,0xfcfcc3c3,0xfcfcf3f3,0xfcfccccc,0xfcfcfcfc,0xfcfccfcf,0xfcfcffff,0xcfcfc0c0,0xcfcff0f0,0xcfcfc3c3,0xcfcff3f3,0xcfcfcccc,0xcfcffcfc,0xcfcfcfcf,0xcfcfffff,0xffffc0c0,0xfffff0f0,0xffffc3c3,0xfffff3f3,0xffffcccc,0xfffffcfc,0xffffcfcf,0xffffffff}, +// {0xc0c0c0c0,0xe0c0c0c0,0xc2c0c0c0,0xe2c0c0c0,0xc8c0c0c0,0xe8c0c0c0,0xcac0c0c0,0xeac0c0c0,0xc0e0e0e0,0xe0e0e0e0,0xc2e0e0e0,0xe2e0e0e0,0xc8e0e0e0,0xe8e0e0e0,0xcae0e0e0,0xeae0e0e0,0xc0c2c2c2,0xe0c2c2c2,0xc2c2c2c2,0xe2c2c2c2,0xc8c2c2c2,0xe8c2c2c2,0xcac2c2c2,0xeac2c2c2,0xc0e2e2e2,0xe0e2e2e2,0xc2e2e2e2,0xe2e2e2e2,0xc8e2e2e2,0xe8e2e2e2,0xcae2e2e2,0xeae2e2e2,0xc0c8c8c8,0xe0c8c8c8,0xc2c8c8c8,0xe2c8c8c8,0xc8c8c8c8,0xe8c8c8c8,0xcac8c8c8,0xeac8c8c8,0xc0e8e8e8,0xe0e8e8e8,0xc2e8e8e8,0xe2e8e8e8,0xc8e8e8e8,0xe8e8e8e8,0xcae8e8e8,0xeae8e8e8,0xc0cacaca,0xe0cacaca,0xc2cacaca,0xe2cacaca,0xc8cacaca,0xe8cacaca,0xcacacaca,0xeacacaca,0xc0eaeaea,0xe0eaeaea,0xc2eaeaea,0xe2eaeaea,0xc8eaeaea,0xe8eaeaea,0xcaeaeaea,0xeaeaeaea,0xc0c0c0c0,0xf0c0c0c0,0xc3c0c0c0,0xf3c0c0c0,0xccc0c0c0,0xfcc0c0c0,0xcfc0c0c0,0xffc0c0c0,0xc0f0f0f0,0xf0f0f0f0,0xc3f0f0f0,0xf3f0f0f0,0xccf0f0f0,0xfcf0f0f0,0xcff0f0f0,0xfff0f0f0,0xc0c3c3c3,0xf0c3c3c3,0xc3c3c3c3,0xf3c3c3c3,0xccc3c3c3,0xfcc3c3c3,0xcfc3c3c3,0xffc3c3c3,0xc0f3f3f3,0xf0f3f3f3,0xc3f3f3f3,0xf3f3f3f3,0xccf3f3f3,0xfcf3f3f3,0xcff3f3f3,0xfff3f3f3,0xc0cccccc,0xf0cccccc,0xc3cccccc,0xf3cccccc,0xcccccccc,0xfccccccc,0xcfcccccc,0xffcccccc,0xc0fcfcfc,0xf0fcfcfc,0xc3fcfcfc,0xf3fcfcfc,0xccfcfcfc,0xfcfcfcfc,0xcffcfcfc,0xfffcfcfc,0xc0cfcfcf,0xf0cfcfcf,0xc3cfcfcf,0xf3cfcfcf,0xcccfcfcf,0xfccfcfcf,0xcfcfcfcf,0xffcfcfcf,0xc0ffffff,0xf0ffffff,0xc3ffffff,0xf3ffffff,0xccffffff,0xfcffffff,0xcfffffff,0xffffffff,0xc0c0c0c0,0xe0c0c0c0,0xc2c0c0c0,0xe2c0c0c0,0xc8c0c0c0,0xe8c0c0c0,0xcac0c0c0,0xeac0c0c0,0xc0e0e0e0,0xe0e0e0e0,0xc2e0e0e0,0xe2e0e0e0,0xc8e0e0e0,0xe8e0e0e0,0xcae0e0e0,0xeae0e0e0,0xc0c2c2c2,0xe0c2c2c2,0xc2c2c2c2,0xe2c2c2c2,0xc8c2c2c2,0xe8c2c2c2,0xcac2c2c2,0xeac2c2c2,0xc0e2e2e2,0xe0e2e2e2,0xc2e2e2e2,0xe2e2e2e2,0xc8e2e2e2,0xe8e2e2e2,0xcae2e2e2,0xeae2e2e2,0xc0c8c8c8,0xe0c8c8c8,0xc2c8c8c8,0xe2c8c8c8,0xc8c8c8c8,0xe8c8c8c8,0xcac8c8c8,0xeac8c8c8,0xc0e8e8e8,0xe0e8e8e8,0xc2e8e8e8,0xe2e8e8e8,0xc8e8e8e8,0xe8e8e8e8,0xcae8e8e8,0xeae8e8e8,0xc0cacaca,0xe0cacaca,0xc2cacaca,0xe2cacaca,0xc8cacaca,0xe8cacaca,0xcacacaca,0xeacacaca,0xc0eaeaea,0xe0eaeaea,0xc2eaeaea,0xe2eaeaea,0xc8eaeaea,0xe8eaeaea,0xcaeaeaea,0xeaeaeaea,0xc0c0c0c0,0xf0c0c0c0,0xc3c0c0c0,0xf3c0c0c0,0xccc0c0c0,0xfcc0c0c0,0xcfc0c0c0,0xffc0c0c0,0xc0f0f0f0,0xf0f0f0f0,0xc3f0f0f0,0xf3f0f0f0,0xccf0f0f0,0xfcf0f0f0,0xcff0f0f0,0xfff0f0f0,0xc0c3c3c3,0xf0c3c3c3,0xc3c3c3c3,0xf3c3c3c3,0xccc3c3c3,0xfcc3c3c3,0xcfc3c3c3,0xffc3c3c3,0xc0f3f3f3,0xf0f3f3f3,0xc3f3f3f3,0xf3f3f3f3,0xccf3f3f3,0xfcf3f3f3,0xcff3f3f3,0xfff3f3f3,0xc0cccccc,0xf0cccccc,0xc3cccccc,0xf3cccccc,0xcccccccc,0xfccccccc,0xcfcccccc,0xffcccccc,0xc0fcfcfc,0xf0fcfcfc,0xc3fcfcfc,0xf3fcfcfc,0xccfcfcfc,0xfcfcfcfc,0xcffcfcfc,0xfffcfcfc,0xc0cfcfcf,0xf0cfcfcf,0xc3cfcfcf,0xf3cfcfcf,0xcccfcfcf,0xfccfcfcf,0xcfcfcfcf,0xffcfcfcf,0xc0ffffff,0xf0ffffff,0xc3ffffff,0xf3ffffff,0xccffffff,0xfcffffff,0xcfffffff,0xffffffff}, +// {0xc0c0c0c0,0xe0c0e0c0,0xc2c0c2c0,0xe2c0e2c0,0xc8c0c8c0,0xe8c0e8c0,0xcac0cac0,0xeac0eac0,0xc0e0c0e0,0xe0e0e0e0,0xc2e0c2e0,0xe2e0e2e0,0xc8e0c8e0,0xe8e0e8e0,0xcae0cae0,0xeae0eae0,0xc0c2c0c2,0xe0c2e0c2,0xc2c2c2c2,0xe2c2e2c2,0xc8c2c8c2,0xe8c2e8c2,0xcac2cac2,0xeac2eac2,0xc0e2c0e2,0xe0e2e0e2,0xc2e2c2e2,0xe2e2e2e2,0xc8e2c8e2,0xe8e2e8e2,0xcae2cae2,0xeae2eae2,0xc0c8c0c8,0xe0c8e0c8,0xc2c8c2c8,0xe2c8e2c8,0xc8c8c8c8,0xe8c8e8c8,0xcac8cac8,0xeac8eac8,0xc0e8c0e8,0xe0e8e0e8,0xc2e8c2e8,0xe2e8e2e8,0xc8e8c8e8,0xe8e8e8e8,0xcae8cae8,0xeae8eae8,0xc0cac0ca,0xe0cae0ca,0xc2cac2ca,0xe2cae2ca,0xc8cac8ca,0xe8cae8ca,0xcacacaca,0xeacaeaca,0xc0eac0ea,0xe0eae0ea,0xc2eac2ea,0xe2eae2ea,0xc8eac8ea,0xe8eae8ea,0xcaeacaea,0xeaeaeaea,0xc0c0c0c0,0xf0c0f0c0,0xc3c0c3c0,0xf3c0f3c0,0xccc0ccc0,0xfcc0fcc0,0xcfc0cfc0,0xffc0ffc0,0xc0f0c0f0,0xf0f0f0f0,0xc3f0c3f0,0xf3f0f3f0,0xccf0ccf0,0xfcf0fcf0,0xcff0cff0,0xfff0fff0,0xc0c3c0c3,0xf0c3f0c3,0xc3c3c3c3,0xf3c3f3c3,0xccc3ccc3,0xfcc3fcc3,0xcfc3cfc3,0xffc3ffc3,0xc0f3c0f3,0xf0f3f0f3,0xc3f3c3f3,0xf3f3f3f3,0xccf3ccf3,0xfcf3fcf3,0xcff3cff3,0xfff3fff3,0xc0ccc0cc,0xf0ccf0cc,0xc3ccc3cc,0xf3ccf3cc,0xcccccccc,0xfcccfccc,0xcfcccfcc,0xffccffcc,0xc0fcc0fc,0xf0fcf0fc,0xc3fcc3fc,0xf3fcf3fc,0xccfcccfc,0xfcfcfcfc,0xcffccffc,0xfffcfffc,0xc0cfc0cf,0xf0cff0cf,0xc3cfc3cf,0xf3cff3cf,0xcccfcccf,0xfccffccf,0xcfcfcfcf,0xffcfffcf,0xc0ffc0ff,0xf0fff0ff,0xc3ffc3ff,0xf3fff3ff,0xccffccff,0xfcfffcff,0xcfffcfff,0xffffffff,0xc0c0c0c0,0xe0c0e0c0,0xc2c0c2c0,0xe2c0e2c0,0xc8c0c8c0,0xe8c0e8c0,0xcac0cac0,0xeac0eac0,0xc0e0c0e0,0xe0e0e0e0,0xc2e0c2e0,0xe2e0e2e0,0xc8e0c8e0,0xe8e0e8e0,0xcae0cae0,0xeae0eae0,0xc0c2c0c2,0xe0c2e0c2,0xc2c2c2c2,0xe2c2e2c2,0xc8c2c8c2,0xe8c2e8c2,0xcac2cac2,0xeac2eac2,0xc0e2c0e2,0xe0e2e0e2,0xc2e2c2e2,0xe2e2e2e2,0xc8e2c8e2,0xe8e2e8e2,0xcae2cae2,0xeae2eae2,0xc0c8c0c8,0xe0c8e0c8,0xc2c8c2c8,0xe2c8e2c8,0xc8c8c8c8,0xe8c8e8c8,0xcac8cac8,0xeac8eac8,0xc0e8c0e8,0xe0e8e0e8,0xc2e8c2e8,0xe2e8e2e8,0xc8e8c8e8,0xe8e8e8e8,0xcae8cae8,0xeae8eae8,0xc0cac0ca,0xe0cae0ca,0xc2cac2ca,0xe2cae2ca,0xc8cac8ca,0xe8cae8ca,0xcacacaca,0xeacaeaca,0xc0eac0ea,0xe0eae0ea,0xc2eac2ea,0xe2eae2ea,0xc8eac8ea,0xe8eae8ea,0xcaeacaea,0xeaeaeaea,0xc0c0c0c0,0xf0c0f0c0,0xc3c0c3c0,0xf3c0f3c0,0xccc0ccc0,0xfcc0fcc0,0xcfc0cfc0,0xffc0ffc0,0xc0f0c0f0,0xf0f0f0f0,0xc3f0c3f0,0xf3f0f3f0,0xccf0ccf0,0xfcf0fcf0,0xcff0cff0,0xfff0fff0,0xc0c3c0c3,0xf0c3f0c3,0xc3c3c3c3,0xf3c3f3c3,0xccc3ccc3,0xfcc3fcc3,0xcfc3cfc3,0xffc3ffc3,0xc0f3c0f3,0xf0f3f0f3,0xc3f3c3f3,0xf3f3f3f3,0xccf3ccf3,0xfcf3fcf3,0xcff3cff3,0xfff3fff3,0xc0ccc0cc,0xf0ccf0cc,0xc3ccc3cc,0xf3ccf3cc,0xcccccccc,0xfcccfccc,0xcfcccfcc,0xffccffcc,0xc0fcc0fc,0xf0fcf0fc,0xc3fcc3fc,0xf3fcf3fc,0xccfcccfc,0xfcfcfcfc,0xcffccffc,0xfffcfffc,0xc0cfc0cf,0xf0cff0cf,0xc3cfc3cf,0xf3cff3cf,0xcccfcccf,0xfccffccf,0xcfcfcfcf,0xffcfffcf,0xc0ffc0ff,0xf0fff0ff,0xc3ffc3ff,0xf3fff3ff,0xccffccff,0xfcfffcff,0xcfffcfff,0xffffffff}, +// {0xc0c0c0c0,0xe0c0c0e0,0xc2c0c0c2,0xe2c0c0e2,0xc8c0c0c8,0xe8c0c0e8,0xcac0c0ca,0xeac0c0ea,0xc0e0e0c0,0xe0e0e0e0,0xc2e0e0c2,0xe2e0e0e2,0xc8e0e0c8,0xe8e0e0e8,0xcae0e0ca,0xeae0e0ea,0xc0c2c2c0,0xe0c2c2e0,0xc2c2c2c2,0xe2c2c2e2,0xc8c2c2c8,0xe8c2c2e8,0xcac2c2ca,0xeac2c2ea,0xc0e2e2c0,0xe0e2e2e0,0xc2e2e2c2,0xe2e2e2e2,0xc8e2e2c8,0xe8e2e2e8,0xcae2e2ca,0xeae2e2ea,0xc0c8c8c0,0xe0c8c8e0,0xc2c8c8c2,0xe2c8c8e2,0xc8c8c8c8,0xe8c8c8e8,0xcac8c8ca,0xeac8c8ea,0xc0e8e8c0,0xe0e8e8e0,0xc2e8e8c2,0xe2e8e8e2,0xc8e8e8c8,0xe8e8e8e8,0xcae8e8ca,0xeae8e8ea,0xc0cacac0,0xe0cacae0,0xc2cacac2,0xe2cacae2,0xc8cacac8,0xe8cacae8,0xcacacaca,0xeacacaea,0xc0eaeac0,0xe0eaeae0,0xc2eaeac2,0xe2eaeae2,0xc8eaeac8,0xe8eaeae8,0xcaeaeaca,0xeaeaeaea,0xc0c0c0c0,0xf0c0c0f0,0xc3c0c0c3,0xf3c0c0f3,0xccc0c0cc,0xfcc0c0fc,0xcfc0c0cf,0xffc0c0ff,0xc0f0f0c0,0xf0f0f0f0,0xc3f0f0c3,0xf3f0f0f3,0xccf0f0cc,0xfcf0f0fc,0xcff0f0cf,0xfff0f0ff,0xc0c3c3c0,0xf0c3c3f0,0xc3c3c3c3,0xf3c3c3f3,0xccc3c3cc,0xfcc3c3fc,0xcfc3c3cf,0xffc3c3ff,0xc0f3f3c0,0xf0f3f3f0,0xc3f3f3c3,0xf3f3f3f3,0xccf3f3cc,0xfcf3f3fc,0xcff3f3cf,0xfff3f3ff,0xc0ccccc0,0xf0ccccf0,0xc3ccccc3,0xf3ccccf3,0xcccccccc,0xfcccccfc,0xcfcccccf,0xffccccff,0xc0fcfcc0,0xf0fcfcf0,0xc3fcfcc3,0xf3fcfcf3,0xccfcfccc,0xfcfcfcfc,0xcffcfccf,0xfffcfcff,0xc0cfcfc0,0xf0cfcff0,0xc3cfcfc3,0xf3cfcff3,0xcccfcfcc,0xfccfcffc,0xcfcfcfcf,0xffcfcfff,0xc0ffffc0,0xf0fffff0,0xc3ffffc3,0xf3fffff3,0xccffffcc,0xfcfffffc,0xcfffffcf,0xffffffff,0xc0c0c0c0,0xe0c0c0e0,0xc2c0c0c2,0xe2c0c0e2,0xc8c0c0c8,0xe8c0c0e8,0xcac0c0ca,0xeac0c0ea,0xc0e0e0c0,0xe0e0e0e0,0xc2e0e0c2,0xe2e0e0e2,0xc8e0e0c8,0xe8e0e0e8,0xcae0e0ca,0xeae0e0ea,0xc0c2c2c0,0xe0c2c2e0,0xc2c2c2c2,0xe2c2c2e2,0xc8c2c2c8,0xe8c2c2e8,0xcac2c2ca,0xeac2c2ea,0xc0e2e2c0,0xe0e2e2e0,0xc2e2e2c2,0xe2e2e2e2,0xc8e2e2c8,0xe8e2e2e8,0xcae2e2ca,0xeae2e2ea,0xc0c8c8c0,0xe0c8c8e0,0xc2c8c8c2,0xe2c8c8e2,0xc8c8c8c8,0xe8c8c8e8,0xcac8c8ca,0xeac8c8ea,0xc0e8e8c0,0xe0e8e8e0,0xc2e8e8c2,0xe2e8e8e2,0xc8e8e8c8,0xe8e8e8e8,0xcae8e8ca,0xeae8e8ea,0xc0cacac0,0xe0cacae0,0xc2cacac2,0xe2cacae2,0xc8cacac8,0xe8cacae8,0xcacacaca,0xeacacaea,0xc0eaeac0,0xe0eaeae0,0xc2eaeac2,0xe2eaeae2,0xc8eaeac8,0xe8eaeae8,0xcaeaeaca,0xeaeaeaea,0xc0c0c0c0,0xf0c0c0f0,0xc3c0c0c3,0xf3c0c0f3,0xccc0c0cc,0xfcc0c0fc,0xcfc0c0cf,0xffc0c0ff,0xc0f0f0c0,0xf0f0f0f0,0xc3f0f0c3,0xf3f0f0f3,0xccf0f0cc,0xfcf0f0fc,0xcff0f0cf,0xfff0f0ff,0xc0c3c3c0,0xf0c3c3f0,0xc3c3c3c3,0xf3c3c3f3,0xccc3c3cc,0xfcc3c3fc,0xcfc3c3cf,0xffc3c3ff,0xc0f3f3c0,0xf0f3f3f0,0xc3f3f3c3,0xf3f3f3f3,0xccf3f3cc,0xfcf3f3fc,0xcff3f3cf,0xfff3f3ff,0xc0ccccc0,0xf0ccccf0,0xc3ccccc3,0xf3ccccf3,0xcccccccc,0xfcccccfc,0xcfcccccf,0xffccccff,0xc0fcfcc0,0xf0fcfcf0,0xc3fcfcc3,0xf3fcfcf3,0xccfcfccc,0xfcfcfcfc,0xcffcfccf,0xfffcfcff,0xc0cfcfc0,0xf0cfcff0,0xc3cfcfc3,0xf3cfcff3,0xcccfcfcc,0xfccfcffc,0xcfcfcfcf,0xffcfcfff,0xc0ffffc0,0xf0fffff0,0xc3ffffc3,0xf3fffff3,0xccffffcc,0xfcfffffc,0xcfffffcf,0xffffffff}, +// {0xc0c0c0c0,0xe0c0e0e0,0xc2c0c2c2,0xe2c0e2e2,0xc8c0c8c8,0xe8c0e8e8,0xcac0caca,0xeac0eaea,0xc0e0c0c0,0xe0e0e0e0,0xc2e0c2c2,0xe2e0e2e2,0xc8e0c8c8,0xe8e0e8e8,0xcae0caca,0xeae0eaea,0xc0c2c0c0,0xe0c2e0e0,0xc2c2c2c2,0xe2c2e2e2,0xc8c2c8c8,0xe8c2e8e8,0xcac2caca,0xeac2eaea,0xc0e2c0c0,0xe0e2e0e0,0xc2e2c2c2,0xe2e2e2e2,0xc8e2c8c8,0xe8e2e8e8,0xcae2caca,0xeae2eaea,0xc0c8c0c0,0xe0c8e0e0,0xc2c8c2c2,0xe2c8e2e2,0xc8c8c8c8,0xe8c8e8e8,0xcac8caca,0xeac8eaea,0xc0e8c0c0,0xe0e8e0e0,0xc2e8c2c2,0xe2e8e2e2,0xc8e8c8c8,0xe8e8e8e8,0xcae8caca,0xeae8eaea,0xc0cac0c0,0xe0cae0e0,0xc2cac2c2,0xe2cae2e2,0xc8cac8c8,0xe8cae8e8,0xcacacaca,0xeacaeaea,0xc0eac0c0,0xe0eae0e0,0xc2eac2c2,0xe2eae2e2,0xc8eac8c8,0xe8eae8e8,0xcaeacaca,0xeaeaeaea,0xc0c0c0c0,0xf0c0f0f0,0xc3c0c3c3,0xf3c0f3f3,0xccc0cccc,0xfcc0fcfc,0xcfc0cfcf,0xffc0ffff,0xc0f0c0c0,0xf0f0f0f0,0xc3f0c3c3,0xf3f0f3f3,0xccf0cccc,0xfcf0fcfc,0xcff0cfcf,0xfff0ffff,0xc0c3c0c0,0xf0c3f0f0,0xc3c3c3c3,0xf3c3f3f3,0xccc3cccc,0xfcc3fcfc,0xcfc3cfcf,0xffc3ffff,0xc0f3c0c0,0xf0f3f0f0,0xc3f3c3c3,0xf3f3f3f3,0xccf3cccc,0xfcf3fcfc,0xcff3cfcf,0xfff3ffff,0xc0ccc0c0,0xf0ccf0f0,0xc3ccc3c3,0xf3ccf3f3,0xcccccccc,0xfcccfcfc,0xcfcccfcf,0xffccffff,0xc0fcc0c0,0xf0fcf0f0,0xc3fcc3c3,0xf3fcf3f3,0xccfccccc,0xfcfcfcfc,0xcffccfcf,0xfffcffff,0xc0cfc0c0,0xf0cff0f0,0xc3cfc3c3,0xf3cff3f3,0xcccfcccc,0xfccffcfc,0xcfcfcfcf,0xffcfffff,0xc0ffc0c0,0xf0fff0f0,0xc3ffc3c3,0xf3fff3f3,0xccffcccc,0xfcfffcfc,0xcfffcfcf,0xffffffff,0xc0c0c0c0,0xe0c0e0e0,0xc2c0c2c2,0xe2c0e2e2,0xc8c0c8c8,0xe8c0e8e8,0xcac0caca,0xeac0eaea,0xc0e0c0c0,0xe0e0e0e0,0xc2e0c2c2,0xe2e0e2e2,0xc8e0c8c8,0xe8e0e8e8,0xcae0caca,0xeae0eaea,0xc0c2c0c0,0xe0c2e0e0,0xc2c2c2c2,0xe2c2e2e2,0xc8c2c8c8,0xe8c2e8e8,0xcac2caca,0xeac2eaea,0xc0e2c0c0,0xe0e2e0e0,0xc2e2c2c2,0xe2e2e2e2,0xc8e2c8c8,0xe8e2e8e8,0xcae2caca,0xeae2eaea,0xc0c8c0c0,0xe0c8e0e0,0xc2c8c2c2,0xe2c8e2e2,0xc8c8c8c8,0xe8c8e8e8,0xcac8caca,0xeac8eaea,0xc0e8c0c0,0xe0e8e0e0,0xc2e8c2c2,0xe2e8e2e2,0xc8e8c8c8,0xe8e8e8e8,0xcae8caca,0xeae8eaea,0xc0cac0c0,0xe0cae0e0,0xc2cac2c2,0xe2cae2e2,0xc8cac8c8,0xe8cae8e8,0xcacacaca,0xeacaeaea,0xc0eac0c0,0xe0eae0e0,0xc2eac2c2,0xe2eae2e2,0xc8eac8c8,0xe8eae8e8,0xcaeacaca,0xeaeaeaea,0xc0c0c0c0,0xf0c0f0f0,0xc3c0c3c3,0xf3c0f3f3,0xccc0cccc,0xfcc0fcfc,0xcfc0cfcf,0xffc0ffff,0xc0f0c0c0,0xf0f0f0f0,0xc3f0c3c3,0xf3f0f3f3,0xccf0cccc,0xfcf0fcfc,0xcff0cfcf,0xfff0ffff,0xc0c3c0c0,0xf0c3f0f0,0xc3c3c3c3,0xf3c3f3f3,0xccc3cccc,0xfcc3fcfc,0xcfc3cfcf,0xffc3ffff,0xc0f3c0c0,0xf0f3f0f0,0xc3f3c3c3,0xf3f3f3f3,0xccf3cccc,0xfcf3fcfc,0xcff3cfcf,0xfff3ffff,0xc0ccc0c0,0xf0ccf0f0,0xc3ccc3c3,0xf3ccf3f3,0xcccccccc,0xfcccfcfc,0xcfcccfcf,0xffccffff,0xc0fcc0c0,0xf0fcf0f0,0xc3fcc3c3,0xf3fcf3f3,0xccfccccc,0xfcfcfcfc,0xcffccfcf,0xfffcffff,0xc0cfc0c0,0xf0cff0f0,0xc3cfc3c3,0xf3cff3f3,0xcccfcccc,0xfccffcfc,0xcfcfcfcf,0xffcfffff,0xc0ffc0c0,0xf0fff0f0,0xc3ffc3c3,0xf3fff3f3,0xccffcccc,0xfcfffcfc,0xcfffcfcf,0xffffffff}, +// {0xc0c0c0c0,0xc0e0c0c0,0xc0c2c0c0,0xc0e2c0c0,0xc0c8c0c0,0xc0e8c0c0,0xc0cac0c0,0xc0eac0c0,0xe0c0e0e0,0xe0e0e0e0,0xe0c2e0e0,0xe0e2e0e0,0xe0c8e0e0,0xe0e8e0e0,0xe0cae0e0,0xe0eae0e0,0xc2c0c2c2,0xc2e0c2c2,0xc2c2c2c2,0xc2e2c2c2,0xc2c8c2c2,0xc2e8c2c2,0xc2cac2c2,0xc2eac2c2,0xe2c0e2e2,0xe2e0e2e2,0xe2c2e2e2,0xe2e2e2e2,0xe2c8e2e2,0xe2e8e2e2,0xe2cae2e2,0xe2eae2e2,0xc8c0c8c8,0xc8e0c8c8,0xc8c2c8c8,0xc8e2c8c8,0xc8c8c8c8,0xc8e8c8c8,0xc8cac8c8,0xc8eac8c8,0xe8c0e8e8,0xe8e0e8e8,0xe8c2e8e8,0xe8e2e8e8,0xe8c8e8e8,0xe8e8e8e8,0xe8cae8e8,0xe8eae8e8,0xcac0caca,0xcae0caca,0xcac2caca,0xcae2caca,0xcac8caca,0xcae8caca,0xcacacaca,0xcaeacaca,0xeac0eaea,0xeae0eaea,0xeac2eaea,0xeae2eaea,0xeac8eaea,0xeae8eaea,0xeacaeaea,0xeaeaeaea,0xc0c0c0c0,0xc0f0c0c0,0xc0c3c0c0,0xc0f3c0c0,0xc0ccc0c0,0xc0fcc0c0,0xc0cfc0c0,0xc0ffc0c0,0xf0c0f0f0,0xf0f0f0f0,0xf0c3f0f0,0xf0f3f0f0,0xf0ccf0f0,0xf0fcf0f0,0xf0cff0f0,0xf0fff0f0,0xc3c0c3c3,0xc3f0c3c3,0xc3c3c3c3,0xc3f3c3c3,0xc3ccc3c3,0xc3fcc3c3,0xc3cfc3c3,0xc3ffc3c3,0xf3c0f3f3,0xf3f0f3f3,0xf3c3f3f3,0xf3f3f3f3,0xf3ccf3f3,0xf3fcf3f3,0xf3cff3f3,0xf3fff3f3,0xccc0cccc,0xccf0cccc,0xccc3cccc,0xccf3cccc,0xcccccccc,0xccfccccc,0xcccfcccc,0xccffcccc,0xfcc0fcfc,0xfcf0fcfc,0xfcc3fcfc,0xfcf3fcfc,0xfcccfcfc,0xfcfcfcfc,0xfccffcfc,0xfcfffcfc,0xcfc0cfcf,0xcff0cfcf,0xcfc3cfcf,0xcff3cfcf,0xcfcccfcf,0xcffccfcf,0xcfcfcfcf,0xcfffcfcf,0xffc0ffff,0xfff0ffff,0xffc3ffff,0xfff3ffff,0xffccffff,0xfffcffff,0xffcfffff,0xffffffff,0xc0c0c0c0,0xc0e0c0c0,0xc0c2c0c0,0xc0e2c0c0,0xc0c8c0c0,0xc0e8c0c0,0xc0cac0c0,0xc0eac0c0,0xe0c0e0e0,0xe0e0e0e0,0xe0c2e0e0,0xe0e2e0e0,0xe0c8e0e0,0xe0e8e0e0,0xe0cae0e0,0xe0eae0e0,0xc2c0c2c2,0xc2e0c2c2,0xc2c2c2c2,0xc2e2c2c2,0xc2c8c2c2,0xc2e8c2c2,0xc2cac2c2,0xc2eac2c2,0xe2c0e2e2,0xe2e0e2e2,0xe2c2e2e2,0xe2e2e2e2,0xe2c8e2e2,0xe2e8e2e2,0xe2cae2e2,0xe2eae2e2,0xc8c0c8c8,0xc8e0c8c8,0xc8c2c8c8,0xc8e2c8c8,0xc8c8c8c8,0xc8e8c8c8,0xc8cac8c8,0xc8eac8c8,0xe8c0e8e8,0xe8e0e8e8,0xe8c2e8e8,0xe8e2e8e8,0xe8c8e8e8,0xe8e8e8e8,0xe8cae8e8,0xe8eae8e8,0xcac0caca,0xcae0caca,0xcac2caca,0xcae2caca,0xcac8caca,0xcae8caca,0xcacacaca,0xcaeacaca,0xeac0eaea,0xeae0eaea,0xeac2eaea,0xeae2eaea,0xeac8eaea,0xeae8eaea,0xeacaeaea,0xeaeaeaea,0xc0c0c0c0,0xc0f0c0c0,0xc0c3c0c0,0xc0f3c0c0,0xc0ccc0c0,0xc0fcc0c0,0xc0cfc0c0,0xc0ffc0c0,0xf0c0f0f0,0xf0f0f0f0,0xf0c3f0f0,0xf0f3f0f0,0xf0ccf0f0,0xf0fcf0f0,0xf0cff0f0,0xf0fff0f0,0xc3c0c3c3,0xc3f0c3c3,0xc3c3c3c3,0xc3f3c3c3,0xc3ccc3c3,0xc3fcc3c3,0xc3cfc3c3,0xc3ffc3c3,0xf3c0f3f3,0xf3f0f3f3,0xf3c3f3f3,0xf3f3f3f3,0xf3ccf3f3,0xf3fcf3f3,0xf3cff3f3,0xf3fff3f3,0xccc0cccc,0xccf0cccc,0xccc3cccc,0xccf3cccc,0xcccccccc,0xccfccccc,0xcccfcccc,0xccffcccc,0xfcc0fcfc,0xfcf0fcfc,0xfcc3fcfc,0xfcf3fcfc,0xfcccfcfc,0xfcfcfcfc,0xfccffcfc,0xfcfffcfc,0xcfc0cfcf,0xcff0cfcf,0xcfc3cfcf,0xcff3cfcf,0xcfcccfcf,0xcffccfcf,0xcfcfcfcf,0xcfffcfcf,0xffc0ffff,0xfff0ffff,0xffc3ffff,0xfff3ffff,0xffccffff,0xfffcffff,0xffcfffff,0xffffffff}, +// {0xc0c0c0c0,0xc0e0e0c0,0xc0c2c2c0,0xc0e2e2c0,0xc0c8c8c0,0xc0e8e8c0,0xc0cacac0,0xc0eaeac0,0xe0c0c0e0,0xe0e0e0e0,0xe0c2c2e0,0xe0e2e2e0,0xe0c8c8e0,0xe0e8e8e0,0xe0cacae0,0xe0eaeae0,0xc2c0c0c2,0xc2e0e0c2,0xc2c2c2c2,0xc2e2e2c2,0xc2c8c8c2,0xc2e8e8c2,0xc2cacac2,0xc2eaeac2,0xe2c0c0e2,0xe2e0e0e2,0xe2c2c2e2,0xe2e2e2e2,0xe2c8c8e2,0xe2e8e8e2,0xe2cacae2,0xe2eaeae2,0xc8c0c0c8,0xc8e0e0c8,0xc8c2c2c8,0xc8e2e2c8,0xc8c8c8c8,0xc8e8e8c8,0xc8cacac8,0xc8eaeac8,0xe8c0c0e8,0xe8e0e0e8,0xe8c2c2e8,0xe8e2e2e8,0xe8c8c8e8,0xe8e8e8e8,0xe8cacae8,0xe8eaeae8,0xcac0c0ca,0xcae0e0ca,0xcac2c2ca,0xcae2e2ca,0xcac8c8ca,0xcae8e8ca,0xcacacaca,0xcaeaeaca,0xeac0c0ea,0xeae0e0ea,0xeac2c2ea,0xeae2e2ea,0xeac8c8ea,0xeae8e8ea,0xeacacaea,0xeaeaeaea,0xc0c0c0c0,0xc0f0f0c0,0xc0c3c3c0,0xc0f3f3c0,0xc0ccccc0,0xc0fcfcc0,0xc0cfcfc0,0xc0ffffc0,0xf0c0c0f0,0xf0f0f0f0,0xf0c3c3f0,0xf0f3f3f0,0xf0ccccf0,0xf0fcfcf0,0xf0cfcff0,0xf0fffff0,0xc3c0c0c3,0xc3f0f0c3,0xc3c3c3c3,0xc3f3f3c3,0xc3ccccc3,0xc3fcfcc3,0xc3cfcfc3,0xc3ffffc3,0xf3c0c0f3,0xf3f0f0f3,0xf3c3c3f3,0xf3f3f3f3,0xf3ccccf3,0xf3fcfcf3,0xf3cfcff3,0xf3fffff3,0xccc0c0cc,0xccf0f0cc,0xccc3c3cc,0xccf3f3cc,0xcccccccc,0xccfcfccc,0xcccfcfcc,0xccffffcc,0xfcc0c0fc,0xfcf0f0fc,0xfcc3c3fc,0xfcf3f3fc,0xfcccccfc,0xfcfcfcfc,0xfccfcffc,0xfcfffffc,0xcfc0c0cf,0xcff0f0cf,0xcfc3c3cf,0xcff3f3cf,0xcfcccccf,0xcffcfccf,0xcfcfcfcf,0xcfffffcf,0xffc0c0ff,0xfff0f0ff,0xffc3c3ff,0xfff3f3ff,0xffccccff,0xfffcfcff,0xffcfcfff,0xffffffff,0xc0c0c0c0,0xc0e0e0c0,0xc0c2c2c0,0xc0e2e2c0,0xc0c8c8c0,0xc0e8e8c0,0xc0cacac0,0xc0eaeac0,0xe0c0c0e0,0xe0e0e0e0,0xe0c2c2e0,0xe0e2e2e0,0xe0c8c8e0,0xe0e8e8e0,0xe0cacae0,0xe0eaeae0,0xc2c0c0c2,0xc2e0e0c2,0xc2c2c2c2,0xc2e2e2c2,0xc2c8c8c2,0xc2e8e8c2,0xc2cacac2,0xc2eaeac2,0xe2c0c0e2,0xe2e0e0e2,0xe2c2c2e2,0xe2e2e2e2,0xe2c8c8e2,0xe2e8e8e2,0xe2cacae2,0xe2eaeae2,0xc8c0c0c8,0xc8e0e0c8,0xc8c2c2c8,0xc8e2e2c8,0xc8c8c8c8,0xc8e8e8c8,0xc8cacac8,0xc8eaeac8,0xe8c0c0e8,0xe8e0e0e8,0xe8c2c2e8,0xe8e2e2e8,0xe8c8c8e8,0xe8e8e8e8,0xe8cacae8,0xe8eaeae8,0xcac0c0ca,0xcae0e0ca,0xcac2c2ca,0xcae2e2ca,0xcac8c8ca,0xcae8e8ca,0xcacacaca,0xcaeaeaca,0xeac0c0ea,0xeae0e0ea,0xeac2c2ea,0xeae2e2ea,0xeac8c8ea,0xeae8e8ea,0xeacacaea,0xeaeaeaea,0xc0c0c0c0,0xc0f0f0c0,0xc0c3c3c0,0xc0f3f3c0,0xc0ccccc0,0xc0fcfcc0,0xc0cfcfc0,0xc0ffffc0,0xf0c0c0f0,0xf0f0f0f0,0xf0c3c3f0,0xf0f3f3f0,0xf0ccccf0,0xf0fcfcf0,0xf0cfcff0,0xf0fffff0,0xc3c0c0c3,0xc3f0f0c3,0xc3c3c3c3,0xc3f3f3c3,0xc3ccccc3,0xc3fcfcc3,0xc3cfcfc3,0xc3ffffc3,0xf3c0c0f3,0xf3f0f0f3,0xf3c3c3f3,0xf3f3f3f3,0xf3ccccf3,0xf3fcfcf3,0xf3cfcff3,0xf3fffff3,0xccc0c0cc,0xccf0f0cc,0xccc3c3cc,0xccf3f3cc,0xcccccccc,0xccfcfccc,0xcccfcfcc,0xccffffcc,0xfcc0c0fc,0xfcf0f0fc,0xfcc3c3fc,0xfcf3f3fc,0xfcccccfc,0xfcfcfcfc,0xfccfcffc,0xfcfffffc,0xcfc0c0cf,0xcff0f0cf,0xcfc3c3cf,0xcff3f3cf,0xcfcccccf,0xcffcfccf,0xcfcfcfcf,0xcfffffcf,0xffc0c0ff,0xfff0f0ff,0xffc3c3ff,0xfff3f3ff,0xffccccff,0xfffcfcff,0xffcfcfff,0xffffffff}, +// {0xc0c0c0c0,0xc0e0c0e0,0xc0c2c0c2,0xc0e2c0e2,0xc0c8c0c8,0xc0e8c0e8,0xc0cac0ca,0xc0eac0ea,0xe0c0e0c0,0xe0e0e0e0,0xe0c2e0c2,0xe0e2e0e2,0xe0c8e0c8,0xe0e8e0e8,0xe0cae0ca,0xe0eae0ea,0xc2c0c2c0,0xc2e0c2e0,0xc2c2c2c2,0xc2e2c2e2,0xc2c8c2c8,0xc2e8c2e8,0xc2cac2ca,0xc2eac2ea,0xe2c0e2c0,0xe2e0e2e0,0xe2c2e2c2,0xe2e2e2e2,0xe2c8e2c8,0xe2e8e2e8,0xe2cae2ca,0xe2eae2ea,0xc8c0c8c0,0xc8e0c8e0,0xc8c2c8c2,0xc8e2c8e2,0xc8c8c8c8,0xc8e8c8e8,0xc8cac8ca,0xc8eac8ea,0xe8c0e8c0,0xe8e0e8e0,0xe8c2e8c2,0xe8e2e8e2,0xe8c8e8c8,0xe8e8e8e8,0xe8cae8ca,0xe8eae8ea,0xcac0cac0,0xcae0cae0,0xcac2cac2,0xcae2cae2,0xcac8cac8,0xcae8cae8,0xcacacaca,0xcaeacaea,0xeac0eac0,0xeae0eae0,0xeac2eac2,0xeae2eae2,0xeac8eac8,0xeae8eae8,0xeacaeaca,0xeaeaeaea,0xc0c0c0c0,0xc0f0c0f0,0xc0c3c0c3,0xc0f3c0f3,0xc0ccc0cc,0xc0fcc0fc,0xc0cfc0cf,0xc0ffc0ff,0xf0c0f0c0,0xf0f0f0f0,0xf0c3f0c3,0xf0f3f0f3,0xf0ccf0cc,0xf0fcf0fc,0xf0cff0cf,0xf0fff0ff,0xc3c0c3c0,0xc3f0c3f0,0xc3c3c3c3,0xc3f3c3f3,0xc3ccc3cc,0xc3fcc3fc,0xc3cfc3cf,0xc3ffc3ff,0xf3c0f3c0,0xf3f0f3f0,0xf3c3f3c3,0xf3f3f3f3,0xf3ccf3cc,0xf3fcf3fc,0xf3cff3cf,0xf3fff3ff,0xccc0ccc0,0xccf0ccf0,0xccc3ccc3,0xccf3ccf3,0xcccccccc,0xccfcccfc,0xcccfcccf,0xccffccff,0xfcc0fcc0,0xfcf0fcf0,0xfcc3fcc3,0xfcf3fcf3,0xfcccfccc,0xfcfcfcfc,0xfccffccf,0xfcfffcff,0xcfc0cfc0,0xcff0cff0,0xcfc3cfc3,0xcff3cff3,0xcfcccfcc,0xcffccffc,0xcfcfcfcf,0xcfffcfff,0xffc0ffc0,0xfff0fff0,0xffc3ffc3,0xfff3fff3,0xffccffcc,0xfffcfffc,0xffcfffcf,0xffffffff,0xc0c0c0c0,0xc0e0c0e0,0xc0c2c0c2,0xc0e2c0e2,0xc0c8c0c8,0xc0e8c0e8,0xc0cac0ca,0xc0eac0ea,0xe0c0e0c0,0xe0e0e0e0,0xe0c2e0c2,0xe0e2e0e2,0xe0c8e0c8,0xe0e8e0e8,0xe0cae0ca,0xe0eae0ea,0xc2c0c2c0,0xc2e0c2e0,0xc2c2c2c2,0xc2e2c2e2,0xc2c8c2c8,0xc2e8c2e8,0xc2cac2ca,0xc2eac2ea,0xe2c0e2c0,0xe2e0e2e0,0xe2c2e2c2,0xe2e2e2e2,0xe2c8e2c8,0xe2e8e2e8,0xe2cae2ca,0xe2eae2ea,0xc8c0c8c0,0xc8e0c8e0,0xc8c2c8c2,0xc8e2c8e2,0xc8c8c8c8,0xc8e8c8e8,0xc8cac8ca,0xc8eac8ea,0xe8c0e8c0,0xe8e0e8e0,0xe8c2e8c2,0xe8e2e8e2,0xe8c8e8c8,0xe8e8e8e8,0xe8cae8ca,0xe8eae8ea,0xcac0cac0,0xcae0cae0,0xcac2cac2,0xcae2cae2,0xcac8cac8,0xcae8cae8,0xcacacaca,0xcaeacaea,0xeac0eac0,0xeae0eae0,0xeac2eac2,0xeae2eae2,0xeac8eac8,0xeae8eae8,0xeacaeaca,0xeaeaeaea,0xc0c0c0c0,0xc0f0c0f0,0xc0c3c0c3,0xc0f3c0f3,0xc0ccc0cc,0xc0fcc0fc,0xc0cfc0cf,0xc0ffc0ff,0xf0c0f0c0,0xf0f0f0f0,0xf0c3f0c3,0xf0f3f0f3,0xf0ccf0cc,0xf0fcf0fc,0xf0cff0cf,0xf0fff0ff,0xc3c0c3c0,0xc3f0c3f0,0xc3c3c3c3,0xc3f3c3f3,0xc3ccc3cc,0xc3fcc3fc,0xc3cfc3cf,0xc3ffc3ff,0xf3c0f3c0,0xf3f0f3f0,0xf3c3f3c3,0xf3f3f3f3,0xf3ccf3cc,0xf3fcf3fc,0xf3cff3cf,0xf3fff3ff,0xccc0ccc0,0xccf0ccf0,0xccc3ccc3,0xccf3ccf3,0xcccccccc,0xccfcccfc,0xcccfcccf,0xccffccff,0xfcc0fcc0,0xfcf0fcf0,0xfcc3fcc3,0xfcf3fcf3,0xfcccfccc,0xfcfcfcfc,0xfccffccf,0xfcfffcff,0xcfc0cfc0,0xcff0cff0,0xcfc3cfc3,0xcff3cff3,0xcfcccfcc,0xcffccffc,0xcfcfcfcf,0xcfffcfff,0xffc0ffc0,0xfff0fff0,0xffc3ffc3,0xfff3fff3,0xffccffcc,0xfffcfffc,0xffcfffcf,0xffffffff}, +// {0xc0c0c0c0,0xc0e0e0e0,0xc0c2c2c2,0xc0e2e2e2,0xc0c8c8c8,0xc0e8e8e8,0xc0cacaca,0xc0eaeaea,0xe0c0c0c0,0xe0e0e0e0,0xe0c2c2c2,0xe0e2e2e2,0xe0c8c8c8,0xe0e8e8e8,0xe0cacaca,0xe0eaeaea,0xc2c0c0c0,0xc2e0e0e0,0xc2c2c2c2,0xc2e2e2e2,0xc2c8c8c8,0xc2e8e8e8,0xc2cacaca,0xc2eaeaea,0xe2c0c0c0,0xe2e0e0e0,0xe2c2c2c2,0xe2e2e2e2,0xe2c8c8c8,0xe2e8e8e8,0xe2cacaca,0xe2eaeaea,0xc8c0c0c0,0xc8e0e0e0,0xc8c2c2c2,0xc8e2e2e2,0xc8c8c8c8,0xc8e8e8e8,0xc8cacaca,0xc8eaeaea,0xe8c0c0c0,0xe8e0e0e0,0xe8c2c2c2,0xe8e2e2e2,0xe8c8c8c8,0xe8e8e8e8,0xe8cacaca,0xe8eaeaea,0xcac0c0c0,0xcae0e0e0,0xcac2c2c2,0xcae2e2e2,0xcac8c8c8,0xcae8e8e8,0xcacacaca,0xcaeaeaea,0xeac0c0c0,0xeae0e0e0,0xeac2c2c2,0xeae2e2e2,0xeac8c8c8,0xeae8e8e8,0xeacacaca,0xeaeaeaea,0xc0c0c0c0,0xc0f0f0f0,0xc0c3c3c3,0xc0f3f3f3,0xc0cccccc,0xc0fcfcfc,0xc0cfcfcf,0xc0ffffff,0xf0c0c0c0,0xf0f0f0f0,0xf0c3c3c3,0xf0f3f3f3,0xf0cccccc,0xf0fcfcfc,0xf0cfcfcf,0xf0ffffff,0xc3c0c0c0,0xc3f0f0f0,0xc3c3c3c3,0xc3f3f3f3,0xc3cccccc,0xc3fcfcfc,0xc3cfcfcf,0xc3ffffff,0xf3c0c0c0,0xf3f0f0f0,0xf3c3c3c3,0xf3f3f3f3,0xf3cccccc,0xf3fcfcfc,0xf3cfcfcf,0xf3ffffff,0xccc0c0c0,0xccf0f0f0,0xccc3c3c3,0xccf3f3f3,0xcccccccc,0xccfcfcfc,0xcccfcfcf,0xccffffff,0xfcc0c0c0,0xfcf0f0f0,0xfcc3c3c3,0xfcf3f3f3,0xfccccccc,0xfcfcfcfc,0xfccfcfcf,0xfcffffff,0xcfc0c0c0,0xcff0f0f0,0xcfc3c3c3,0xcff3f3f3,0xcfcccccc,0xcffcfcfc,0xcfcfcfcf,0xcfffffff,0xffc0c0c0,0xfff0f0f0,0xffc3c3c3,0xfff3f3f3,0xffcccccc,0xfffcfcfc,0xffcfcfcf,0xffffffff,0xc0c0c0c0,0xc0e0e0e0,0xc0c2c2c2,0xc0e2e2e2,0xc0c8c8c8,0xc0e8e8e8,0xc0cacaca,0xc0eaeaea,0xe0c0c0c0,0xe0e0e0e0,0xe0c2c2c2,0xe0e2e2e2,0xe0c8c8c8,0xe0e8e8e8,0xe0cacaca,0xe0eaeaea,0xc2c0c0c0,0xc2e0e0e0,0xc2c2c2c2,0xc2e2e2e2,0xc2c8c8c8,0xc2e8e8e8,0xc2cacaca,0xc2eaeaea,0xe2c0c0c0,0xe2e0e0e0,0xe2c2c2c2,0xe2e2e2e2,0xe2c8c8c8,0xe2e8e8e8,0xe2cacaca,0xe2eaeaea,0xc8c0c0c0,0xc8e0e0e0,0xc8c2c2c2,0xc8e2e2e2,0xc8c8c8c8,0xc8e8e8e8,0xc8cacaca,0xc8eaeaea,0xe8c0c0c0,0xe8e0e0e0,0xe8c2c2c2,0xe8e2e2e2,0xe8c8c8c8,0xe8e8e8e8,0xe8cacaca,0xe8eaeaea,0xcac0c0c0,0xcae0e0e0,0xcac2c2c2,0xcae2e2e2,0xcac8c8c8,0xcae8e8e8,0xcacacaca,0xcaeaeaea,0xeac0c0c0,0xeae0e0e0,0xeac2c2c2,0xeae2e2e2,0xeac8c8c8,0xeae8e8e8,0xeacacaca,0xeaeaeaea,0xc0c0c0c0,0xc0f0f0f0,0xc0c3c3c3,0xc0f3f3f3,0xc0cccccc,0xc0fcfcfc,0xc0cfcfcf,0xc0ffffff,0xf0c0c0c0,0xf0f0f0f0,0xf0c3c3c3,0xf0f3f3f3,0xf0cccccc,0xf0fcfcfc,0xf0cfcfcf,0xf0ffffff,0xc3c0c0c0,0xc3f0f0f0,0xc3c3c3c3,0xc3f3f3f3,0xc3cccccc,0xc3fcfcfc,0xc3cfcfcf,0xc3ffffff,0xf3c0c0c0,0xf3f0f0f0,0xf3c3c3c3,0xf3f3f3f3,0xf3cccccc,0xf3fcfcfc,0xf3cfcfcf,0xf3ffffff,0xccc0c0c0,0xccf0f0f0,0xccc3c3c3,0xccf3f3f3,0xcccccccc,0xccfcfcfc,0xcccfcfcf,0xccffffff,0xfcc0c0c0,0xfcf0f0f0,0xfcc3c3c3,0xfcf3f3f3,0xfccccccc,0xfcfcfcfc,0xfccfcfcf,0xfcffffff,0xcfc0c0c0,0xcff0f0f0,0xcfc3c3c3,0xcff3f3f3,0xcfcccccc,0xcffcfcfc,0xcfcfcfcf,0xcfffffff,0xffc0c0c0,0xfff0f0f0,0xffc3c3c3,0xfff3f3f3,0xffcccccc,0xfffcfcfc,0xffcfcfcf,0xffffffff}, +// {0xc0c0c0c0,0xe0e0c0c0,0xc2c2c0c0,0xe2e2c0c0,0xc8c8c0c0,0xe8e8c0c0,0xcacac0c0,0xeaeac0c0,0xc0c0e0e0,0xe0e0e0e0,0xc2c2e0e0,0xe2e2e0e0,0xc8c8e0e0,0xe8e8e0e0,0xcacae0e0,0xeaeae0e0,0xc0c0c2c2,0xe0e0c2c2,0xc2c2c2c2,0xe2e2c2c2,0xc8c8c2c2,0xe8e8c2c2,0xcacac2c2,0xeaeac2c2,0xc0c0e2e2,0xe0e0e2e2,0xc2c2e2e2,0xe2e2e2e2,0xc8c8e2e2,0xe8e8e2e2,0xcacae2e2,0xeaeae2e2,0xc0c0c8c8,0xe0e0c8c8,0xc2c2c8c8,0xe2e2c8c8,0xc8c8c8c8,0xe8e8c8c8,0xcacac8c8,0xeaeac8c8,0xc0c0e8e8,0xe0e0e8e8,0xc2c2e8e8,0xe2e2e8e8,0xc8c8e8e8,0xe8e8e8e8,0xcacae8e8,0xeaeae8e8,0xc0c0caca,0xe0e0caca,0xc2c2caca,0xe2e2caca,0xc8c8caca,0xe8e8caca,0xcacacaca,0xeaeacaca,0xc0c0eaea,0xe0e0eaea,0xc2c2eaea,0xe2e2eaea,0xc8c8eaea,0xe8e8eaea,0xcacaeaea,0xeaeaeaea,0xc0c0c0c0,0xf0f0c0c0,0xc3c3c0c0,0xf3f3c0c0,0xccccc0c0,0xfcfcc0c0,0xcfcfc0c0,0xffffc0c0,0xc0c0f0f0,0xf0f0f0f0,0xc3c3f0f0,0xf3f3f0f0,0xccccf0f0,0xfcfcf0f0,0xcfcff0f0,0xfffff0f0,0xc0c0c3c3,0xf0f0c3c3,0xc3c3c3c3,0xf3f3c3c3,0xccccc3c3,0xfcfcc3c3,0xcfcfc3c3,0xffffc3c3,0xc0c0f3f3,0xf0f0f3f3,0xc3c3f3f3,0xf3f3f3f3,0xccccf3f3,0xfcfcf3f3,0xcfcff3f3,0xfffff3f3,0xc0c0cccc,0xf0f0cccc,0xc3c3cccc,0xf3f3cccc,0xcccccccc,0xfcfccccc,0xcfcfcccc,0xffffcccc,0xc0c0fcfc,0xf0f0fcfc,0xc3c3fcfc,0xf3f3fcfc,0xccccfcfc,0xfcfcfcfc,0xcfcffcfc,0xfffffcfc,0xc0c0cfcf,0xf0f0cfcf,0xc3c3cfcf,0xf3f3cfcf,0xcccccfcf,0xfcfccfcf,0xcfcfcfcf,0xffffcfcf,0xc0c0ffff,0xf0f0ffff,0xc3c3ffff,0xf3f3ffff,0xccccffff,0xfcfcffff,0xcfcfffff,0xffffffff,0xc0c0c0c0,0xe0e0c0c0,0xc2c2c0c0,0xe2e2c0c0,0xc8c8c0c0,0xe8e8c0c0,0xcacac0c0,0xeaeac0c0,0xc0c0e0e0,0xe0e0e0e0,0xc2c2e0e0,0xe2e2e0e0,0xc8c8e0e0,0xe8e8e0e0,0xcacae0e0,0xeaeae0e0,0xc0c0c2c2,0xe0e0c2c2,0xc2c2c2c2,0xe2e2c2c2,0xc8c8c2c2,0xe8e8c2c2,0xcacac2c2,0xeaeac2c2,0xc0c0e2e2,0xe0e0e2e2,0xc2c2e2e2,0xe2e2e2e2,0xc8c8e2e2,0xe8e8e2e2,0xcacae2e2,0xeaeae2e2,0xc0c0c8c8,0xe0e0c8c8,0xc2c2c8c8,0xe2e2c8c8,0xc8c8c8c8,0xe8e8c8c8,0xcacac8c8,0xeaeac8c8,0xc0c0e8e8,0xe0e0e8e8,0xc2c2e8e8,0xe2e2e8e8,0xc8c8e8e8,0xe8e8e8e8,0xcacae8e8,0xeaeae8e8,0xc0c0caca,0xe0e0caca,0xc2c2caca,0xe2e2caca,0xc8c8caca,0xe8e8caca,0xcacacaca,0xeaeacaca,0xc0c0eaea,0xe0e0eaea,0xc2c2eaea,0xe2e2eaea,0xc8c8eaea,0xe8e8eaea,0xcacaeaea,0xeaeaeaea,0xc0c0c0c0,0xf0f0c0c0,0xc3c3c0c0,0xf3f3c0c0,0xccccc0c0,0xfcfcc0c0,0xcfcfc0c0,0xffffc0c0,0xc0c0f0f0,0xf0f0f0f0,0xc3c3f0f0,0xf3f3f0f0,0xccccf0f0,0xfcfcf0f0,0xcfcff0f0,0xfffff0f0,0xc0c0c3c3,0xf0f0c3c3,0xc3c3c3c3,0xf3f3c3c3,0xccccc3c3,0xfcfcc3c3,0xcfcfc3c3,0xffffc3c3,0xc0c0f3f3,0xf0f0f3f3,0xc3c3f3f3,0xf3f3f3f3,0xccccf3f3,0xfcfcf3f3,0xcfcff3f3,0xfffff3f3,0xc0c0cccc,0xf0f0cccc,0xc3c3cccc,0xf3f3cccc,0xcccccccc,0xfcfccccc,0xcfcfcccc,0xffffcccc,0xc0c0fcfc,0xf0f0fcfc,0xc3c3fcfc,0xf3f3fcfc,0xccccfcfc,0xfcfcfcfc,0xcfcffcfc,0xfffffcfc,0xc0c0cfcf,0xf0f0cfcf,0xc3c3cfcf,0xf3f3cfcf,0xcccccfcf,0xfcfccfcf,0xcfcfcfcf,0xffffcfcf,0xc0c0ffff,0xf0f0ffff,0xc3c3ffff,0xf3f3ffff,0xccccffff,0xfcfcffff,0xcfcfffff,0xffffffff}, +// {0xc0c0c0c0,0xe0e0e0c0,0xc2c2c2c0,0xe2e2e2c0,0xc8c8c8c0,0xe8e8e8c0,0xcacacac0,0xeaeaeac0,0xc0c0c0e0,0xe0e0e0e0,0xc2c2c2e0,0xe2e2e2e0,0xc8c8c8e0,0xe8e8e8e0,0xcacacae0,0xeaeaeae0,0xc0c0c0c2,0xe0e0e0c2,0xc2c2c2c2,0xe2e2e2c2,0xc8c8c8c2,0xe8e8e8c2,0xcacacac2,0xeaeaeac2,0xc0c0c0e2,0xe0e0e0e2,0xc2c2c2e2,0xe2e2e2e2,0xc8c8c8e2,0xe8e8e8e2,0xcacacae2,0xeaeaeae2,0xc0c0c0c8,0xe0e0e0c8,0xc2c2c2c8,0xe2e2e2c8,0xc8c8c8c8,0xe8e8e8c8,0xcacacac8,0xeaeaeac8,0xc0c0c0e8,0xe0e0e0e8,0xc2c2c2e8,0xe2e2e2e8,0xc8c8c8e8,0xe8e8e8e8,0xcacacae8,0xeaeaeae8,0xc0c0c0ca,0xe0e0e0ca,0xc2c2c2ca,0xe2e2e2ca,0xc8c8c8ca,0xe8e8e8ca,0xcacacaca,0xeaeaeaca,0xc0c0c0ea,0xe0e0e0ea,0xc2c2c2ea,0xe2e2e2ea,0xc8c8c8ea,0xe8e8e8ea,0xcacacaea,0xeaeaeaea,0xc0c0c0c0,0xf0f0f0c0,0xc3c3c3c0,0xf3f3f3c0,0xccccccc0,0xfcfcfcc0,0xcfcfcfc0,0xffffffc0,0xc0c0c0f0,0xf0f0f0f0,0xc3c3c3f0,0xf3f3f3f0,0xccccccf0,0xfcfcfcf0,0xcfcfcff0,0xfffffff0,0xc0c0c0c3,0xf0f0f0c3,0xc3c3c3c3,0xf3f3f3c3,0xccccccc3,0xfcfcfcc3,0xcfcfcfc3,0xffffffc3,0xc0c0c0f3,0xf0f0f0f3,0xc3c3c3f3,0xf3f3f3f3,0xccccccf3,0xfcfcfcf3,0xcfcfcff3,0xfffffff3,0xc0c0c0cc,0xf0f0f0cc,0xc3c3c3cc,0xf3f3f3cc,0xcccccccc,0xfcfcfccc,0xcfcfcfcc,0xffffffcc,0xc0c0c0fc,0xf0f0f0fc,0xc3c3c3fc,0xf3f3f3fc,0xccccccfc,0xfcfcfcfc,0xcfcfcffc,0xfffffffc,0xc0c0c0cf,0xf0f0f0cf,0xc3c3c3cf,0xf3f3f3cf,0xcccccccf,0xfcfcfccf,0xcfcfcfcf,0xffffffcf,0xc0c0c0ff,0xf0f0f0ff,0xc3c3c3ff,0xf3f3f3ff,0xccccccff,0xfcfcfcff,0xcfcfcfff,0xffffffff,0xc0c0c0c0,0xe0e0e0c0,0xc2c2c2c0,0xe2e2e2c0,0xc8c8c8c0,0xe8e8e8c0,0xcacacac0,0xeaeaeac0,0xc0c0c0e0,0xe0e0e0e0,0xc2c2c2e0,0xe2e2e2e0,0xc8c8c8e0,0xe8e8e8e0,0xcacacae0,0xeaeaeae0,0xc0c0c0c2,0xe0e0e0c2,0xc2c2c2c2,0xe2e2e2c2,0xc8c8c8c2,0xe8e8e8c2,0xcacacac2,0xeaeaeac2,0xc0c0c0e2,0xe0e0e0e2,0xc2c2c2e2,0xe2e2e2e2,0xc8c8c8e2,0xe8e8e8e2,0xcacacae2,0xeaeaeae2,0xc0c0c0c8,0xe0e0e0c8,0xc2c2c2c8,0xe2e2e2c8,0xc8c8c8c8,0xe8e8e8c8,0xcacacac8,0xeaeaeac8,0xc0c0c0e8,0xe0e0e0e8,0xc2c2c2e8,0xe2e2e2e8,0xc8c8c8e8,0xe8e8e8e8,0xcacacae8,0xeaeaeae8,0xc0c0c0ca,0xe0e0e0ca,0xc2c2c2ca,0xe2e2e2ca,0xc8c8c8ca,0xe8e8e8ca,0xcacacaca,0xeaeaeaca,0xc0c0c0ea,0xe0e0e0ea,0xc2c2c2ea,0xe2e2e2ea,0xc8c8c8ea,0xe8e8e8ea,0xcacacaea,0xeaeaeaea,0xc0c0c0c0,0xf0f0f0c0,0xc3c3c3c0,0xf3f3f3c0,0xccccccc0,0xfcfcfcc0,0xcfcfcfc0,0xffffffc0,0xc0c0c0f0,0xf0f0f0f0,0xc3c3c3f0,0xf3f3f3f0,0xccccccf0,0xfcfcfcf0,0xcfcfcff0,0xfffffff0,0xc0c0c0c3,0xf0f0f0c3,0xc3c3c3c3,0xf3f3f3c3,0xccccccc3,0xfcfcfcc3,0xcfcfcfc3,0xffffffc3,0xc0c0c0f3,0xf0f0f0f3,0xc3c3c3f3,0xf3f3f3f3,0xccccccf3,0xfcfcfcf3,0xcfcfcff3,0xfffffff3,0xc0c0c0cc,0xf0f0f0cc,0xc3c3c3cc,0xf3f3f3cc,0xcccccccc,0xfcfcfccc,0xcfcfcfcc,0xffffffcc,0xc0c0c0fc,0xf0f0f0fc,0xc3c3c3fc,0xf3f3f3fc,0xccccccfc,0xfcfcfcfc,0xcfcfcffc,0xfffffffc,0xc0c0c0cf,0xf0f0f0cf,0xc3c3c3cf,0xf3f3f3cf,0xcccccccf,0xfcfcfccf,0xcfcfcfcf,0xffffffcf,0xc0c0c0ff,0xf0f0f0ff,0xc3c3c3ff,0xf3f3f3ff,0xccccccff,0xfcfcfcff,0xcfcfcfff,0xffffffff}, +// {0xc0c0c0c0,0xe0e0c0e0,0xc2c2c0c2,0xe2e2c0e2,0xc8c8c0c8,0xe8e8c0e8,0xcacac0ca,0xeaeac0ea,0xc0c0e0c0,0xe0e0e0e0,0xc2c2e0c2,0xe2e2e0e2,0xc8c8e0c8,0xe8e8e0e8,0xcacae0ca,0xeaeae0ea,0xc0c0c2c0,0xe0e0c2e0,0xc2c2c2c2,0xe2e2c2e2,0xc8c8c2c8,0xe8e8c2e8,0xcacac2ca,0xeaeac2ea,0xc0c0e2c0,0xe0e0e2e0,0xc2c2e2c2,0xe2e2e2e2,0xc8c8e2c8,0xe8e8e2e8,0xcacae2ca,0xeaeae2ea,0xc0c0c8c0,0xe0e0c8e0,0xc2c2c8c2,0xe2e2c8e2,0xc8c8c8c8,0xe8e8c8e8,0xcacac8ca,0xeaeac8ea,0xc0c0e8c0,0xe0e0e8e0,0xc2c2e8c2,0xe2e2e8e2,0xc8c8e8c8,0xe8e8e8e8,0xcacae8ca,0xeaeae8ea,0xc0c0cac0,0xe0e0cae0,0xc2c2cac2,0xe2e2cae2,0xc8c8cac8,0xe8e8cae8,0xcacacaca,0xeaeacaea,0xc0c0eac0,0xe0e0eae0,0xc2c2eac2,0xe2e2eae2,0xc8c8eac8,0xe8e8eae8,0xcacaeaca,0xeaeaeaea,0xc0c0c0c0,0xf0f0c0f0,0xc3c3c0c3,0xf3f3c0f3,0xccccc0cc,0xfcfcc0fc,0xcfcfc0cf,0xffffc0ff,0xc0c0f0c0,0xf0f0f0f0,0xc3c3f0c3,0xf3f3f0f3,0xccccf0cc,0xfcfcf0fc,0xcfcff0cf,0xfffff0ff,0xc0c0c3c0,0xf0f0c3f0,0xc3c3c3c3,0xf3f3c3f3,0xccccc3cc,0xfcfcc3fc,0xcfcfc3cf,0xffffc3ff,0xc0c0f3c0,0xf0f0f3f0,0xc3c3f3c3,0xf3f3f3f3,0xccccf3cc,0xfcfcf3fc,0xcfcff3cf,0xfffff3ff,0xc0c0ccc0,0xf0f0ccf0,0xc3c3ccc3,0xf3f3ccf3,0xcccccccc,0xfcfcccfc,0xcfcfcccf,0xffffccff,0xc0c0fcc0,0xf0f0fcf0,0xc3c3fcc3,0xf3f3fcf3,0xccccfccc,0xfcfcfcfc,0xcfcffccf,0xfffffcff,0xc0c0cfc0,0xf0f0cff0,0xc3c3cfc3,0xf3f3cff3,0xcccccfcc,0xfcfccffc,0xcfcfcfcf,0xffffcfff,0xc0c0ffc0,0xf0f0fff0,0xc3c3ffc3,0xf3f3fff3,0xccccffcc,0xfcfcfffc,0xcfcfffcf,0xffffffff,0xc0c0c0c0,0xe0e0c0e0,0xc2c2c0c2,0xe2e2c0e2,0xc8c8c0c8,0xe8e8c0e8,0xcacac0ca,0xeaeac0ea,0xc0c0e0c0,0xe0e0e0e0,0xc2c2e0c2,0xe2e2e0e2,0xc8c8e0c8,0xe8e8e0e8,0xcacae0ca,0xeaeae0ea,0xc0c0c2c0,0xe0e0c2e0,0xc2c2c2c2,0xe2e2c2e2,0xc8c8c2c8,0xe8e8c2e8,0xcacac2ca,0xeaeac2ea,0xc0c0e2c0,0xe0e0e2e0,0xc2c2e2c2,0xe2e2e2e2,0xc8c8e2c8,0xe8e8e2e8,0xcacae2ca,0xeaeae2ea,0xc0c0c8c0,0xe0e0c8e0,0xc2c2c8c2,0xe2e2c8e2,0xc8c8c8c8,0xe8e8c8e8,0xcacac8ca,0xeaeac8ea,0xc0c0e8c0,0xe0e0e8e0,0xc2c2e8c2,0xe2e2e8e2,0xc8c8e8c8,0xe8e8e8e8,0xcacae8ca,0xeaeae8ea,0xc0c0cac0,0xe0e0cae0,0xc2c2cac2,0xe2e2cae2,0xc8c8cac8,0xe8e8cae8,0xcacacaca,0xeaeacaea,0xc0c0eac0,0xe0e0eae0,0xc2c2eac2,0xe2e2eae2,0xc8c8eac8,0xe8e8eae8,0xcacaeaca,0xeaeaeaea,0xc0c0c0c0,0xf0f0c0f0,0xc3c3c0c3,0xf3f3c0f3,0xccccc0cc,0xfcfcc0fc,0xcfcfc0cf,0xffffc0ff,0xc0c0f0c0,0xf0f0f0f0,0xc3c3f0c3,0xf3f3f0f3,0xccccf0cc,0xfcfcf0fc,0xcfcff0cf,0xfffff0ff,0xc0c0c3c0,0xf0f0c3f0,0xc3c3c3c3,0xf3f3c3f3,0xccccc3cc,0xfcfcc3fc,0xcfcfc3cf,0xffffc3ff,0xc0c0f3c0,0xf0f0f3f0,0xc3c3f3c3,0xf3f3f3f3,0xccccf3cc,0xfcfcf3fc,0xcfcff3cf,0xfffff3ff,0xc0c0ccc0,0xf0f0ccf0,0xc3c3ccc3,0xf3f3ccf3,0xcccccccc,0xfcfcccfc,0xcfcfcccf,0xffffccff,0xc0c0fcc0,0xf0f0fcf0,0xc3c3fcc3,0xf3f3fcf3,0xccccfccc,0xfcfcfcfc,0xcfcffccf,0xfffffcff,0xc0c0cfc0,0xf0f0cff0,0xc3c3cfc3,0xf3f3cff3,0xcccccfcc,0xfcfccffc,0xcfcfcfcf,0xffffcfff,0xc0c0ffc0,0xf0f0fff0,0xc3c3ffc3,0xf3f3fff3,0xccccffcc,0xfcfcfffc,0xcfcfffcf,0xffffffff}, +// {0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xe0e0e0e0,0xc2c2c2c2,0xe2e2e2e2,0xc8c8c8c8,0xe8e8e8e8,0xcacacaca,0xeaeaeaea,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff,0xc0c0c0c0,0xf0f0f0f0,0xc3c3c3c3,0xf3f3f3f3,0xcccccccc,0xfcfcfcfc,0xcfcfcfcf,0xffffffff} +// }; + +void precalcAluBytes() { + + uint16_t specfast_colors[128]; // Array for faster color calc in Draw + + unsigned int pal[2],b0,b1,b2,b3; + + // Calc array for faster color calcs in Draw + for (int i = 0; i < (NUM_SPECTRUM_COLORS >> 1); i++) { + // Normal + specfast_colors[i] = spectrum_colors[i]; + specfast_colors[i << 3] = spectrum_colors[i]; + // Bright + specfast_colors[i | 0x40] = spectrum_colors[i + (NUM_SPECTRUM_COLORS >> 1)]; + specfast_colors[(i << 3) | 0x40] = spectrum_colors[i + (NUM_SPECTRUM_COLORS >> 1)]; + } -// // Alloc ALUbytes -// for (int i = 0; i < 16; i++) { -// AluBytes[i] = (uint32_t *) heap_caps_malloc(0x400, MALLOC_CAP_INTERNAL | MALLOC_CAP_32BIT); -// } + // Alloc ALUbytes + for (int i = 0; i < 16; i++) { + AluBytes[i] = (uint32_t *) heap_caps_malloc(0x400, MALLOC_CAP_INTERNAL | MALLOC_CAP_32BIT); + } -// for (int i = 0; i < 16; i++) { -// for (int n = 0; n < 256; n++) { -// pal[0] = specfast_colors[n & 0x78]; -// pal[1] = specfast_colors[n & 0x47]; -// b0 = pal[(i >> 3) & 0x01]; -// b1 = pal[(i >> 2) & 0x01]; -// b2 = pal[(i >> 1) & 0x01]; -// b3 = pal[i & 0x01]; -// AluBytes[i][n]=b2 | (b3<<8) | (b0<<16) | (b1<<24); -// } -// } + for (int i = 0; i < 16; i++) { + for (int n = 0; n < 256; n++) { + pal[0] = specfast_colors[n & 0x78]; + pal[1] = specfast_colors[n & 0x47]; + b0 = pal[(i >> 3) & 0x01]; + b1 = pal[(i >> 2) & 0x01]; + b2 = pal[(i >> 1) & 0x01]; + b3 = pal[i & 0x01]; + AluBytes[i][n]=b2 | (b3<<8) | (b0<<16) | (b1<<24); + } + } -// } +} uint16_t zxColor(uint8_t color, uint8_t bright) { if (bright) color += 8; @@ -265,7 +265,7 @@ void VIDEO::Init() { precalcColors(); // precalculate colors for current VGA mode - // precalcAluBytes(); // Alloc and calc AluBytes + precalcAluBytes(); // Alloc and calc AluBytes precalcULASWAP(); // precalculate ULA SWAP values From 28b2791d25404d02aa33188e82babf50ca271e3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Iborra?= Date: Mon, 4 Dec 2023 13:41:29 +0100 Subject: [PATCH 11/30] Interface improvements --- include/OSDMain.h | 8 + include/messages.h | 35 +++- src/FileUtils.cpp | 46 ++++- src/OSDMain.cpp | 77 ++++---- src/OSDMenu.cpp | 437 ++++++++++++++++++++++++--------------------- 5 files changed, 372 insertions(+), 231 deletions(-) diff --git a/include/OSDMain.h b/include/OSDMain.h index baa4c220..c0565585 100644 --- a/include/OSDMain.h +++ b/include/OSDMain.h @@ -50,6 +50,10 @@ using namespace std; #define LEVEL_WARN 2 #define LEVEL_ERROR 3 +#define DLG_CANCEL 0 +#define DLG_YES 1 +#define DLG_NO 2 + // OSD Interface class OSD { @@ -106,6 +110,10 @@ class OSD static unsigned short menu_curopt; static unsigned int SaveRectpos; + static uint8_t msgDialog(string title, string msg); + + static void progressDialog(string title, string msg, int percent, int action); + // Rows static unsigned short rowCount(string menu); static string rowGet(string menu, unsigned short row_number); diff --git a/include/messages.h b/include/messages.h index 24cb6a83..7ed175e3 100644 --- a/include/messages.h +++ b/include/messages.h @@ -42,7 +42,7 @@ visit https://zxespectrum.speccy.org/contacto #define MSG_SAVE_CONFIG "Saving config file" #define MSG_VGA_INIT "Initializing VGA" // #define EMU_VERSION " v1.0 " -#define EMU_VERSION " Dev 281123 " +#define EMU_VERSION " Dev 041223 " // Error #define ERROR_TITLE " !!! ERROR - CLIVE MEDITATION !!! " @@ -79,6 +79,34 @@ static const char *OSD_PAUSE[2] = { OSD_PAUSE_EN,OSD_PAUSE_ES }; #define OSD_TAPE_SELECT_ERR_ES "TAP no seleccionado" static const char *OSD_TAPE_SELECT_ERR[2] = { OSD_TAPE_SELECT_ERR_EN,OSD_TAPE_SELECT_ERR_ES }; +#define OSD_FILE_INDEXING_EN "Indexing folder" +#define OSD_FILE_INDEXING_ES "Indexando carpeta" +static const char *OSD_FILE_INDEXING[2] = { OSD_FILE_INDEXING_EN, OSD_FILE_INDEXING_ES }; + +#define OSD_FILE_INDEXING_EN_1 " Sorting & Hashing " +#define OSD_FILE_INDEXING_ES_1 "Ordenando y hasheando" +static const char *OSD_FILE_INDEXING_1[2] = { OSD_FILE_INDEXING_EN_1, OSD_FILE_INDEXING_ES_1 }; + +#define OSD_FILE_INDEXING_EN_2 " Merge Sorting " +#define OSD_FILE_INDEXING_ES_2 "Realizando merge sort" +static const char *OSD_FILE_INDEXING_2[2] = { OSD_FILE_INDEXING_EN_2, OSD_FILE_INDEXING_ES_2 }; + +#define OSD_FILE_INDEXING_EN_3 " Cleaning temp files " +#define OSD_FILE_INDEXING_ES_3 "Limpiando temporales " +static const char *OSD_FILE_INDEXING_3[2] = { OSD_FILE_INDEXING_EN_3, OSD_FILE_INDEXING_ES_3 }; + +#define OSD_FIRMW_UPDATE_EN "Firmware update" +#define OSD_FIRMW_UPDATE_ES "Actualizar firmware" +static const char *OSD_FIRMW_UPDATE[2] = { OSD_FIRMW_UPDATE_EN,OSD_FIRMW_UPDATE_ES}; + +#define OSD_DLG_SURE_EN "Are you sure?" +static const char DLG_SURE_ES[] = {168,'D','e','s','e','a',' ','c','o','n','t','i','n','u','a','r','?'}; +static const char *OSD_DLG_SURE[2] = { OSD_DLG_SURE_EN, DLG_SURE_ES}; + +#define OSD_FIRMW_EN "Updating firmware" +#define OSD_FIRMW_ES "Actualizando firmware" +static const char *OSD_FIRMW[2] = { OSD_FIRMW_EN,OSD_FIRMW_ES}; + #define OSD_FIRMW_BEGIN_EN "Erasing destination partition." #define OSD_FIRMW_BEGIN_ES "Borrando particion de destino." static const char *OSD_FIRMW_BEGIN[2] = { OSD_FIRMW_BEGIN_EN,OSD_FIRMW_BEGIN_ES}; @@ -91,6 +119,11 @@ static const char *OSD_FIRMW_WRITE[2] = { OSD_FIRMW_WRITE_EN,OSD_FIRMW_WRITE_ES} #define OSD_FIRMW_END_ES " Completado. Reiniciando. " static const char *OSD_FIRMW_END[2] = { OSD_FIRMW_END_EN,OSD_FIRMW_END_ES}; + +#define OSD_NOFIRMW_ERR_EN "No firmware file found." +#define OSD_NOFIRMW_ERR_ES "Firmware no encontrado." +static const char *OSD_NOFIRMW_ERR[2] = { OSD_NOFIRMW_ERR_EN,OSD_NOFIRMW_ERR_ES}; + #define OSD_FIRMW_ERR_EN "Problem updating firmware." #define OSD_FIRMW_ERR_ES "Error actualizando firmware." static const char *OSD_FIRMW_ERR[2] = { OSD_FIRMW_ERR_EN,OSD_FIRMW_ERR_ES}; diff --git a/src/FileUtils.cpp b/src/FileUtils.cpp index f584a53f..b1d33c2d 100644 --- a/src/FileUtils.cpp +++ b/src/FileUtils.cpp @@ -288,11 +288,24 @@ void FileUtils::DirToFile(string fpath, uint8_t ftype) { // Remove previous dir file remove((fpath + fileTypes[ftype].indexFilename).c_str()); + OSD::progressDialog(OSD_FILE_INDEXING[Config::lang],OSD_FILE_INDEXING_1[Config::lang],0,0); + // Read filenames from medium into vector, sort it, and dump into MAX_FNAMES_PER_CHUNK filenames long files int cnt = 0; int chunk_cnt = 0; + int item_count = 0; + int items_processed = 0; struct dirent* de; + // Count items to process + rewinddir(dir); + while ((de = readdir(dir)) != nullptr) { + if (de->d_type == DT_REG || de->d_type == DT_DIR) { + item_count++; + } + } + rewinddir(dir); + unsigned long h = 0, high; // Directory Hash while ((de = readdir(dir)) != nullptr) { @@ -326,6 +339,8 @@ void FileUtils::DirToFile(string fpath, uint8_t ftype) { FILE *f = fopen((fpath + fileTypes[ftype].indexFilename + fileName).c_str(), "w"); if (f==NULL) { printf("Error opening filelist chunk\n"); + // Close progress dialog + OSD::progressDialog("","",0,2); return; } for (int n=0; n < MAX_FNAMES_PER_CHUNK; n++) fputs((filenames[n] + std::string(63 - filenames[n].size(), ' ') + "\n").c_str(),f); @@ -333,11 +348,17 @@ void FileUtils::DirToFile(string fpath, uint8_t ftype) { filenames.clear(); cnt = 0; chunk_cnt++; + items_processed--; } } } + + items_processed++; + OSD::progressDialog("","",(float) 100 / ((float) item_count / (float) items_processed),1); + } + } // Add previous directory entry if not on root dir @@ -359,6 +380,8 @@ void FileUtils::DirToFile(string fpath, uint8_t ftype) { FILE *f = fopen((fpath + fileTypes[ftype].indexFilename + fileName).c_str(), "w"); if (f == NULL) { printf("Error opening last filelist chunk\n"); + // Close progress dialog + OSD::progressDialog("","",0,2); return; } for (int n=0; n < cnt;n++) fputs((filenames[n] + std::string(63 - filenames[n].size(), ' ') + "\n").c_str(),f); @@ -369,9 +392,14 @@ void FileUtils::DirToFile(string fpath, uint8_t ftype) { std::vector().swap(filenames); // free memory if (chunk_cnt == 0) { - if (cnt == 0) return; + if (cnt == 0) { + // Close progress dialog + OSD::progressDialog("","",0,2); + return; + } rename((fpath + fileTypes[ftype].indexFilename + "0").c_str(),(fpath + fileTypes[ftype].indexFilename).c_str()); // Rename unique chunk } else { + OSD::progressDialog(OSD_FILE_INDEXING[Config::lang],OSD_FILE_INDEXING_2[Config::lang],0,1); Mergefiles(fpath, ftype, chunk_cnt); } @@ -383,6 +411,9 @@ void FileUtils::DirToFile(string fpath, uint8_t ftype) { fclose(fout); fout = NULL; + // Close progress dialog + OSD::progressDialog("","",0,2); + } void FileUtils::Mergefiles(string fpath, uint8_t ftype, int chunk_cnt) { @@ -452,25 +483,38 @@ void FileUtils::Mergefiles(string fpath, uint8_t ftype, int chunk_cnt) { sprintf(fileName, ".tmp%d", n); file1 = fopen((fpath + fileName).c_str(), "r"); + OSD::progressDialog("","",(float) 100 / ((float) chunk_cnt / (float) n),1); + + // printf("chunkcnt: %d n: %d\n",(int) chunk_cnt, (int)n); + n++; sprintf(fileName, "%d", n); file2 = fopen((fpath + fileTypes[ftype].indexFilename + fileName).c_str(), "r"); + // printf("n Opened: %d\n",(int)n); + } + // printf("Closing file1\n"); + fclose(file1); + // printf("File1 closed\n"); + // Rename final chunk sprintf(fileName, ".tmp%d", n - 1); rename((fpath + fileName).c_str(),(fpath + fileTypes[ftype].indexFilename).c_str()); + OSD::progressDialog(OSD_FILE_INDEXING[Config::lang],OSD_FILE_INDEXING_3[Config::lang],0,1); + // Remove temp files for (int n = 0; n <= chunk_cnt; n++) { sprintf(fileName, "%d", n); remove((fpath + fileTypes[ftype].indexFilename + fileName).c_str()); sprintf(fileName, ".tmp%d", n); remove((fpath + fileName).c_str()); + OSD::progressDialog("","",(float) 100 / ((float) chunk_cnt / (float) n),1); } file1 = NULL; diff --git a/src/OSDMain.cpp b/src/OSDMain.cpp index 2ecb81cf..5a0a5e63 100644 --- a/src/OSDMain.cpp +++ b/src/OSDMain.cpp @@ -736,10 +736,11 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL) { // Options menu uint8_t options_num = menuRun(MENU_OPTIONS[Config::lang]); if (options_num == 1) { - menu_saverect = true; + menu_level = 2; menu_curopt = 1; + menu_saverect = true; while (1) { - menu_level = 2; + // menu_level = 2; // Storage source // string stor_menu = MENU_STORAGE[Config::lang]; string stor_menu = Config::lang ? MENU_STORAGE_ES : MENU_STORAGE_EN; @@ -778,20 +779,6 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL) { } } } - // else - // if (opt2 == 2) { - - // OSD::osdCenteredMsg("Refreshing snap dir", LEVEL_INFO, 0); - // FileUtils::DirToFile(FileUtils::MountPoint + "/" + FileUtils::SNA_Path, DISK_SNAFILE); // Prepare sna filelist - - // OSD::osdCenteredMsg("Refreshing tape dir", LEVEL_INFO, 0); - // FileUtils::DirToFile(FileUtils::MountPoint + "/" + FileUtils::TAP_Path, DISK_TAPFILE); // Prepare tap filelist - - // OSD::osdCenteredMsg("Refreshing disk dir", LEVEL_INFO, 0); - // FileUtils::DirToFile(FileUtils::MountPoint + "/" + FileUtils::DSK_Path, DISK_DSKFILE); // Prepare dsk filelist - - // return; - // } menu_curopt = opt2; menu_saverect = false; } else { @@ -1111,21 +1098,36 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL) { } } else if (options_num == 7) { - // msgDialog("¿Flashear firmware.bin?","",0); + menu_level = 2; + + string title = OSD_FIRMW_UPDATE[Config::lang]; + string msg = OSD_DLG_SURE[Config::lang]; + uint8_t res = msgDialog(title,msg); + + if (res == DLG_YES) { + + // Open firmware file + FILE *firmware = fopen("/sd/firmware.bin", "rb"); + if (firmware == NULL) { + osdCenteredMsg(OSD_NOFIRMW_ERR[Config::lang], LEVEL_WARN, 2000); + return; + } else { + esp_err_t res = updateFirmware(firmware); + fclose(firmware); + string errMsg = OSD_FIRMW_ERR[Config::lang]; + errMsg += " Code = " + to_string(res); + osdCenteredMsg(errMsg, LEVEL_ERROR, 3000); + } - // Open firmware file - FILE *firmware = fopen("/sd/firmware.bin", "rb"); - if (firmware == NULL) { - osdCenteredMsg("No firmware file found.", LEVEL_WARN, 2000); return; + } else { - esp_err_t res = updateFirmware(firmware); - fclose(firmware); - string errMsg = OSD_FIRMW_ERR[Config::lang]; - errMsg += " Code = " + to_string(res); - osdCenteredMsg(errMsg, LEVEL_ERROR, 3000); + + menu_curopt = 7; + menu_saverect = false; + } - return; + } else { menu_curopt = 5; break; @@ -1570,26 +1572,37 @@ if (target == NULL) { // printf("Running partition type %d subtype %d at offset 0x%x.\n", partition->type, partition->subtype, partition->address); // printf("Target partition type %d subtype %d at offset 0x%x.\n", target->type, target->subtype, target->address); -osdCenteredMsg(OSD_FIRMW_BEGIN[Config::lang], LEVEL_INFO,0); +// osdCenteredMsg(OSD_FIRMW_BEGIN[Config::lang], LEVEL_INFO,0); + +progressDialog(OSD_FIRMW[Config::lang],OSD_FIRMW_BEGIN[Config::lang],0,0); esp_ota_handle_t ota_handle; esp_err_t result = esp_ota_begin(target, OTA_SIZE_UNKNOWN, &ota_handle); if (result != ESP_OK) { + progressDialog("","",0,2); return result; } size_t bytesread; uint32_t byteswritten = 0; -osdCenteredMsg(OSD_FIRMW_WRITE[Config::lang], LEVEL_INFO,0); +// osdCenteredMsg(OSD_FIRMW_WRITE[Config::lang], LEVEL_INFO,0); +progressDialog(OSD_FIRMW[Config::lang],OSD_FIRMW_WRITE[Config::lang],0,1); + +// Get firmware size +fseek(firmware, 0, SEEK_END); +long bytesfirmware = ftell(firmware); +rewind(firmware); while (1) { bytesread = fread(ota_write_data, 1, 0x1000 , firmware); result = esp_ota_write(ota_handle,(const void *) ota_write_data, bytesread); if (result != ESP_OK) { + progressDialog("","",0,2); return result; } byteswritten += bytesread; + progressDialog("","",(float) 100 / ((float) bytesfirmware / (float) byteswritten),1); // printf("Bytes written: %d\n",byteswritten); if (feof(firmware)) break; } @@ -1598,16 +1611,20 @@ result = esp_ota_end(ota_handle); if (result != ESP_OK) { // printf("esp_ota_end failed, err=0x%x.\n", result); + progressDialog("","",0,2); return result; } result = esp_ota_set_boot_partition(target); if (result != ESP_OK) { // printf("esp_ota_set_boot_partition failed, err=0x%x.\n", result); + progressDialog("","",0,2); return result; } -osdCenteredMsg(OSD_FIRMW_END[Config::lang], LEVEL_INFO, 0); +// osdCenteredMsg(OSD_FIRMW_END[Config::lang], LEVEL_INFO, 0); +progressDialog(OSD_FIRMW[Config::lang],OSD_FIRMW_END[Config::lang],100,1); + delay(1000); // Firmware written: reboot diff --git a/src/OSDMenu.cpp b/src/OSDMenu.cpp index 7a243602..6e667294 100644 --- a/src/OSDMenu.cpp +++ b/src/OSDMenu.cpp @@ -233,18 +233,11 @@ void OSD::WindowDraw() { } -// #define REPDEL 140 // As in real ZX Spectrum (700 ms.) -// #define REPPER 20 // As in real ZX Spectrum (100 ms.) -// static int zxDelay = 0; -// static int lastzxKey = 0; - // Run a new menu unsigned short OSD::menuRun(string new_menu) { fabgl::VirtualKeyItem Menukey; - // newMenu(new_menu); - menu = new_menu; // Position @@ -294,100 +287,9 @@ unsigned short OSD::menuRun(string new_menu) { menuRedraw(); // Draw menu content - // zxDelay = REPDEL; - // lastzxKey = 0; - while (1) { - if (ZXKeyb::Exists) { - - ZXKbdRead(); - - // ZXKeyb::process(); - - // if (!bitRead(ZXKeyb::ZXcols[4], 3)) { // 6 DOWN - // if (zxDelay == 0) { - // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_UP, true, false); - // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_UP, false, false); - // if (lastzxKey == 1) - // zxDelay = REPPER; - // else - // zxDelay = REPDEL; - // lastzxKey = 1; - // } - // } else - // if (!bitRead(ZXKeyb::ZXcols[4], 4)) { // 7 UP (Yes, like the drink's name, I know... :D) - // if (zxDelay == 0) { - // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_DOWN, true, false); - // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_DOWN, false, false); - // if (lastzxKey == 2) - // zxDelay = REPPER; - // else - // zxDelay = REPDEL; - // lastzxKey = 2; - // } - // } else - // if ((!bitRead(ZXKeyb::ZXcols[6], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 0))) { // ENTER - // if (zxDelay == 0) { - // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, true, false); - // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, false, false); - // if (lastzxKey == 3) - // zxDelay = REPPER; - // else - // zxDelay = REPDEL; - // lastzxKey = 3; - // } - // } else - // if ((!bitRead(ZXKeyb::ZXcols[7], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 1))) { // BREAK - // if (zxDelay == 0) { - // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_ESCAPE, true, false); - // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_ESCAPE, false, false); - // if (lastzxKey == 4) - // zxDelay = REPPER; - // else - // zxDelay = REPDEL; - // lastzxKey = 4; - // } - // } else - // if (!bitRead(ZXKeyb::ZXcols[3], 4)) { // LEFT - // if (zxDelay == 0) { - // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEUP, true, false); - // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEUP, false, false); - // if (lastzxKey == 5) - // zxDelay = REPPER; - // else - // zxDelay = REPDEL; - // lastzxKey = 5; - // } - // } else - // if (!bitRead(ZXKeyb::ZXcols[4], 2)) { // RIGHT - // if (zxDelay == 0) { - // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEDOWN, true, false); - // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEDOWN, false, false); - // if (lastzxKey == 6) - // zxDelay = REPPER; - // else - // zxDelay = REPDEL; - // lastzxKey = 6; - // } - // } else - // if (!bitRead(ZXKeyb::ZXcols[1], 1)) { // S (Capture screen) - // if (zxDelay == 0) { - // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PRINTSCREEN, true, false); - // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PRINTSCREEN, false, false); - // if (lastzxKey == 7) - // zxDelay = REPPER; - // else - // zxDelay = REPDEL; - // lastzxKey = 7; - // } - // } else - // { - // zxDelay = 0; - // lastzxKey = 0; - // } - - } + if (ZXKeyb::Exists) ZXKbdRead(); ESPectrum::readKbdJoy(); @@ -493,8 +395,6 @@ unsigned short OSD::menuRun(string new_menu) { vTaskDelay(5 / portTICK_PERIOD_MS); - // if (zxDelay > 0) zxDelay--; - } } @@ -739,7 +639,7 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols } - // Force reindex (for testing) + // // Force reindex (for testing) // fclose(dirfile); // reIndex = true; @@ -781,11 +681,7 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols fabgl::VirtualKeyItem Menukey; while (1) { - if (ZXKeyb::Exists) { - - OSD::ZXKbdRead(); - - } + if (ZXKeyb::Exists) OSD::ZXKbdRead(); ESPectrum::readKbdJoy(); @@ -1013,8 +909,8 @@ void OSD::ZXKbdRead() { else if ((!bitRead(ZXKeyb::ZXcols[0], 0)) && (!bitRead(ZXKeyb::ZXcols[4], 0))) injectKey = fabgl::VK_BACKSPACE; // CS + 0 -> BACKSPACE else if (!bitRead(ZXKeyb::ZXcols[4], 0)) injectKey = fabgl::VK_SPACE; // 0 -> SPACE else if ((!bitRead(ZXKeyb::ZXcols[7], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 1))) injectKey = fabgl::VK_ESCAPE; // BREAK -> ESCAPE - else if (!bitRead(ZXKeyb::ZXcols[3], 4)) injectKey = fabgl::VK_PAGEUP; // 5 -> PGUP - else if (!bitRead(ZXKeyb::ZXcols[4], 2)) injectKey = fabgl::VK_PAGEDOWN; // 8 -> PGDOWN + else if (!bitRead(ZXKeyb::ZXcols[3], 4)) injectKey = fabgl::VK_LEFT; // 5 -> PGUP + else if (!bitRead(ZXKeyb::ZXcols[4], 2)) injectKey = fabgl::VK_RIGHT; // 8 -> PGDOWN else if (!bitRead(ZXKeyb::ZXcols[1], 1)) injectKey = fabgl::VK_PRINTSCREEN; // S -> PRINTSCREEN else if (!bitRead(ZXKeyb::ZXcols[5], 0)) injectKey = fabgl::VK_PAUSE; // P -> PAUSE @@ -1402,95 +1298,7 @@ int OSD::menuTape(string title) { while (1) { - if (ZXKeyb::Exists) { - - ZXKbdRead(); - - // ZXKeyb::process(); - - // if (!bitRead(ZXKeyb::ZXcols[4], 3)) { // 6 DOWN - // if (zxDelay == 0) { - // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_UP, true, false); - // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_UP, false, false); - // if (lastzxKey == 1) - // zxDelay = REPPER; - // else - // zxDelay = REPDEL; - // lastzxKey = 1; - // } - // } else - // if (!bitRead(ZXKeyb::ZXcols[4], 4)) { // 7 UP (Yes, like the drink's name, I know... :D) - // if (zxDelay == 0) { - // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_DOWN, true, false); - // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_DOWN, false, false); - // if (lastzxKey == 2) - // zxDelay = REPPER; - // else - // zxDelay = REPDEL; - // lastzxKey = 2; - // } - // } else - // if ((!bitRead(ZXKeyb::ZXcols[6], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 0))) { // ENTER - // if (zxDelay == 0) { - // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, true, false); - // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_RETURN, false, false); - // if (lastzxKey == 3) - // zxDelay = REPPER; - // else - // zxDelay = REPDEL; - // lastzxKey = 3; - // } - // } else - // if ((!bitRead(ZXKeyb::ZXcols[7], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 1))) { // BREAK - // if (zxDelay == 0) { - // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_ESCAPE, true, false); - // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_ESCAPE, false, false); - // if (lastzxKey == 4) - // zxDelay = REPPER; - // else - // zxDelay = REPDEL; - // lastzxKey = 4; - // } - // } else - // if (!bitRead(ZXKeyb::ZXcols[3], 4)) { // LEFT - // if (zxDelay == 0) { - // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEUP, true, false); - // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEUP, false, false); - // if (lastzxKey == 5) - // zxDelay = REPPER; - // else - // zxDelay = REPDEL; - // lastzxKey = 5; - // } - // } else - // if (!bitRead(ZXKeyb::ZXcols[4], 2)) { // RIGHT - // if (zxDelay == 0) { - // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEDOWN, true, false); - // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PAGEDOWN, false, false); - // if (lastzxKey == 6) - // zxDelay = REPPER; - // else - // zxDelay = REPDEL; - // lastzxKey = 6; - // } - // } else - // if (!bitRead(ZXKeyb::ZXcols[1], 1)) { // S (Capture screen) - // if (zxDelay == 0) { - // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PRINTSCREEN, true, false); - // ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_PRINTSCREEN, false, false); - // if (lastzxKey == 7) - // zxDelay = REPPER; - // else - // zxDelay = REPDEL; - // lastzxKey = 7; - // } - // } else - // { - // zxDelay = 0; - // lastzxKey = 0; - // } - - } + if (ZXKeyb::Exists) ZXKbdRead(); ESPectrum::readKbdJoy(); @@ -1610,8 +1418,239 @@ int OSD::menuTape(string title) { vTaskDelay(5 / portTICK_PERIOD_MS); - // if (zxDelay > 0) zxDelay--; + } +} + +uint8_t OSD::msgDialog(string title, string msg) { + + const unsigned short h = (OSD_FONT_H * 6) + 2; + const unsigned short y = scrAlignCenterY(h); + uint8_t res = DLG_NO; + + if (msg.length() > (scrW / 6) - 4) msg = msg.substr(0,(scrW / 6) - 4); + if (title.length() > (scrW / 6) - 4) title = title.substr(0,(scrW / 6) - 4); + + const unsigned short w = (((msg.length() > title.length() + 6 ? msg.length() : title.length() + 6) + 2) * OSD_FONT_W) + 2; + const unsigned short x = scrAlignCenterX(w); + + // Save backbuffer data + unsigned int j = SaveRectpos; + for (int m = y; m < y + h; m++) { + uint32_t *backbuffer32 = (uint32_t *)(VIDEO::vga.backBuffer[m]); + for (int n = x >> 2; n < ((x + w) >> 2) + 1; n++) { + VIDEO::SaveRect[SaveRectpos] = backbuffer32[n]; + SaveRectpos++; + } + } + + // Set font + VIDEO::vga.setFont(Font6x8); + + // Menu border + VIDEO::vga.rect(x, y, w, h, OSD::zxColor(0, 0)); + + VIDEO::vga.fillRect(x + 1, y + 1, w - 2, OSD_FONT_H, zxColor(0,0)); + VIDEO::vga.fillRect(x + 1, y + 1 + OSD_FONT_H, w - 2, h - OSD_FONT_H - 2, zxColor(7,1)); + + // Title + VIDEO::vga.setTextColor(OSD::zxColor(7, 1), OSD::zxColor(0, 0)); + VIDEO::vga.setCursor(x + OSD_FONT_W + 1, y + 1); + VIDEO::vga.print(title.c_str()); + + // Msg + VIDEO::vga.setTextColor(OSD::zxColor(0, 0), OSD::zxColor(7, 1)); + VIDEO::vga.setCursor(scrAlignCenterX(msg.length() * OSD_FONT_W), y + 1 + (OSD_FONT_H * 2)); + VIDEO::vga.print(msg.c_str()); + + // Yes + VIDEO::vga.setTextColor(OSD::zxColor(0, 0), OSD::zxColor(7, 1)); + VIDEO::vga.setCursor(scrAlignCenterX(6 * OSD_FONT_W) - (w >> 2), y + 1 + (OSD_FONT_H * 4)); + VIDEO::vga.print(Config::lang ? " Si " : " Yes "); + + // // Ruler + // VIDEO::vga.setTextColor(OSD::zxColor(0, 0), OSD::zxColor(7, 1)); + // VIDEO::vga.setCursor(x + 1, y + 1 + (OSD_FONT_H * 3)); + // VIDEO::vga.print("123456789012345678901234567"); + + // No + VIDEO::vga.setTextColor(OSD::zxColor(0, 1), OSD::zxColor(5, 1)); + VIDEO::vga.setCursor(scrAlignCenterX(6 * OSD_FONT_W) + (w >> 2), y + 1 + (OSD_FONT_H * 4)); + VIDEO::vga.print(" No "); + + // Rainbow + unsigned short rb_y = y + 8; + unsigned short rb_paint_x = x + w - 30; + uint8_t rb_colors[] = {2, 6, 4, 5}; + for (uint8_t c = 0; c < 4; c++) { + for (uint8_t i = 0; i < 5; i++) { + VIDEO::vga.line(rb_paint_x + i, rb_y, rb_paint_x + 8 + i, rb_y - 8, OSD::zxColor(rb_colors[c], 1)); + } + rb_paint_x += 5; + } + + // VIDEO::vga.fillRect(x, y, w, h, paper); + // // VIDEO::vga.rect(x - 1, y - 1, w + 2, h + 2, ink); + // VIDEO::vga.setTextColor(ink, paper); + // VIDEO::vga.setFont(Font6x8); + // VIDEO::vga.setCursor(x + OSD_FONT_W, y + OSD_FONT_H); + // VIDEO::vga.print(msg.c_str()); + + // Keyboard loop + fabgl::VirtualKeyItem Menukey; + while (1) { + + if (ZXKeyb::Exists) OSD::ZXKbdRead(); + + ESPectrum::readKbdJoy(); + + // Process external keyboard + if (ESPectrum::PS2Controller.keyboard()->virtualKeyAvailable()) { + if (ESPectrum::readKbd(&Menukey)) { + if (!Menukey.down) continue; + + if (Menukey.vk == fabgl::VK_LEFT) { + // Yes + VIDEO::vga.setTextColor(OSD::zxColor(0, 1), OSD::zxColor(5, 1)); + VIDEO::vga.setCursor(scrAlignCenterX(6 * OSD_FONT_W) - (w >> 2), y + 1 + (OSD_FONT_H * 4)); + VIDEO::vga.print(Config::lang ? " Si " : " Yes "); + // No + VIDEO::vga.setTextColor(OSD::zxColor(0, 0), OSD::zxColor(7, 1)); + VIDEO::vga.setCursor(scrAlignCenterX(6 * OSD_FONT_W) + (w >> 2), y + 1 + (OSD_FONT_H * 4)); + VIDEO::vga.print(" No "); + click(); + res = DLG_YES; + } else if (Menukey.vk == fabgl::VK_RIGHT) { + // Yes + VIDEO::vga.setTextColor(OSD::zxColor(0, 0), OSD::zxColor(7, 1)); + VIDEO::vga.setCursor(scrAlignCenterX(6 * OSD_FONT_W) - (w >> 2), y + 1 + (OSD_FONT_H * 4)); + VIDEO::vga.print(Config::lang ? " Si " : " Yes "); + // No + VIDEO::vga.setTextColor(OSD::zxColor(0, 1), OSD::zxColor(5, 1)); + VIDEO::vga.setCursor(scrAlignCenterX(6 * OSD_FONT_W) + (w >> 2), y + 1 + (OSD_FONT_H * 4)); + VIDEO::vga.print(" No "); + click(); + res = DLG_NO; + } else if (Menukey.vk == fabgl::VK_RETURN || Menukey.vk == fabgl::VK_SPACE) { + break; + } else if (Menukey.vk == fabgl::VK_ESCAPE) { + res = DLG_CANCEL; + break; + } + } + + } + + vTaskDelay(5 / portTICK_PERIOD_MS); } + + click(); + + // Restore backbuffer data + SaveRectpos = j; + for (int m = y; m < y + h; m++) { + uint32_t *backbuffer32 = (uint32_t *)(VIDEO::vga.backBuffer[m]); + for (int n = x >> 2; n < ((x + w) >> 2) + 1; n++) { + backbuffer32[n] = VIDEO::SaveRect[j]; + j++; + } + } + + return res; + } +void OSD::progressDialog(string title, string msg, int percent, int action) { + + static unsigned short h; + static unsigned short y; + static unsigned short w; + static unsigned short x; + static unsigned short progress_x; + static unsigned short progress_y; + static unsigned int j; + + if (action == 0 ) { // SHOW + + h = (OSD_FONT_H * 6) + 2; + y = scrAlignCenterY(h); + + if (msg.length() > (scrW / 6) - 4) msg = msg.substr(0,(scrW / 6) - 4); + if (title.length() > (scrW / 6) - 4) title = title.substr(0,(scrW / 6) - 4); + + w = (((msg.length() > title.length() + 6 ? msg.length(): title.length() + 6) + 2) * OSD_FONT_W) + 2; + x = scrAlignCenterX(w); + + // Save backbuffer data + j = SaveRectpos; + for (int m = y; m < y + h; m++) { + uint32_t *backbuffer32 = (uint32_t *)(VIDEO::vga.backBuffer[m]); + for (int n = x >> 2; n < ((x + w) >> 2) + 1; n++) { + VIDEO::SaveRect[SaveRectpos] = backbuffer32[n]; + SaveRectpos++; + } + } + + // Set font + VIDEO::vga.setFont(Font6x8); + + // Menu border + VIDEO::vga.rect(x, y, w, h, OSD::zxColor(0, 0)); + + VIDEO::vga.fillRect(x + 1, y + 1, w - 2, OSD_FONT_H, zxColor(0,0)); + VIDEO::vga.fillRect(x + 1, y + 1 + OSD_FONT_H, w - 2, h - OSD_FONT_H - 2, zxColor(7,1)); + + // Title + VIDEO::vga.setTextColor(OSD::zxColor(7, 1), OSD::zxColor(0, 0)); + VIDEO::vga.setCursor(x + OSD_FONT_W + 1, y + 1); + VIDEO::vga.print(title.c_str()); + + // Msg + VIDEO::vga.setTextColor(OSD::zxColor(0, 0), OSD::zxColor(7, 1)); + VIDEO::vga.setCursor(scrAlignCenterX(msg.length() * OSD_FONT_W), y + 1 + (OSD_FONT_H * 2)); + VIDEO::vga.print(msg.c_str()); + + // Rainbow + unsigned short rb_y = y + 8; + unsigned short rb_paint_x = x + w - 30; + uint8_t rb_colors[] = {2, 6, 4, 5}; + for (uint8_t c = 0; c < 4; c++) { + for (uint8_t i = 0; i < 5; i++) { + VIDEO::vga.line(rb_paint_x + i, rb_y, rb_paint_x + 8 + i, rb_y - 8, OSD::zxColor(rb_colors[c], 1)); + } + rb_paint_x += 5; + } + + // Progress bar frame + progress_x = scrAlignCenterX(102); + progress_y = y + (OSD_FONT_H * 4); + VIDEO::vga.rect(progress_x, progress_y, 102, OSD_FONT_H + 2, OSD::zxColor(0, 0)); + progress_x++; + progress_y++; + + } else if (action == 1 ) { // UPDATE + + // Msg + VIDEO::vga.setTextColor(OSD::zxColor(0, 0), OSD::zxColor(7, 1)); + VIDEO::vga.setCursor(scrAlignCenterX(msg.length() * OSD_FONT_W), y + 1 + (OSD_FONT_H * 2)); + VIDEO::vga.print(msg.c_str()); + + // Progress bar + VIDEO::vga.fillRect(progress_x, progress_y, percent, OSD_FONT_H, zxColor(5,1)); + VIDEO::vga.fillRect(progress_x + percent, progress_y, 100 - percent, OSD_FONT_H, zxColor(7,1)); + + } else if (action == 2) { // CLOSE + + // Restore backbuffer data + SaveRectpos = j; + for (int m = y; m < y + h; m++) { + uint32_t *backbuffer32 = (uint32_t *)(VIDEO::vga.backBuffer[m]); + for (int n = x >> 2; n < ((x + w) >> 2) + 1; n++) { + backbuffer32[n] = VIDEO::SaveRect[j]; + j++; + } + } + + } + +} From ad558d6db07ab72269706699e9a956ee515791ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Iborra?= Date: Tue, 5 Dec 2023 10:47:50 +0100 Subject: [PATCH 12/30] Progress bar trd scl fix --- include/messages.h | 16 ++++++++-------- src/OSDMain.cpp | 15 ++++++++++++--- src/OSDMenu.cpp | 18 +++++++++++------- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/include/messages.h b/include/messages.h index 7ed175e3..4546acca 100644 --- a/include/messages.h +++ b/include/messages.h @@ -79,20 +79,20 @@ static const char *OSD_PAUSE[2] = { OSD_PAUSE_EN,OSD_PAUSE_ES }; #define OSD_TAPE_SELECT_ERR_ES "TAP no seleccionado" static const char *OSD_TAPE_SELECT_ERR[2] = { OSD_TAPE_SELECT_ERR_EN,OSD_TAPE_SELECT_ERR_ES }; -#define OSD_FILE_INDEXING_EN "Indexing folder" -#define OSD_FILE_INDEXING_ES "Indexando carpeta" +#define OSD_FILE_INDEXING_EN "Indexing" +#define OSD_FILE_INDEXING_ES "Indexando" static const char *OSD_FILE_INDEXING[2] = { OSD_FILE_INDEXING_EN, OSD_FILE_INDEXING_ES }; -#define OSD_FILE_INDEXING_EN_1 " Sorting & Hashing " -#define OSD_FILE_INDEXING_ES_1 "Ordenando y hasheando" +#define OSD_FILE_INDEXING_EN_1 " Sorting " +#define OSD_FILE_INDEXING_ES_1 " Ordenando " static const char *OSD_FILE_INDEXING_1[2] = { OSD_FILE_INDEXING_EN_1, OSD_FILE_INDEXING_ES_1 }; -#define OSD_FILE_INDEXING_EN_2 " Merge Sorting " -#define OSD_FILE_INDEXING_ES_2 "Realizando merge sort" +#define OSD_FILE_INDEXING_EN_2 "Saving index" +#define OSD_FILE_INDEXING_ES_2 "Grabando indice" static const char *OSD_FILE_INDEXING_2[2] = { OSD_FILE_INDEXING_EN_2, OSD_FILE_INDEXING_ES_2 }; -#define OSD_FILE_INDEXING_EN_3 " Cleaning temp files " -#define OSD_FILE_INDEXING_ES_3 "Limpiando temporales " +#define OSD_FILE_INDEXING_EN_3 " Cleaning " +#define OSD_FILE_INDEXING_ES_3 " Limpiando " static const char *OSD_FILE_INDEXING_3[2] = { OSD_FILE_INDEXING_EN_3, OSD_FILE_INDEXING_ES_3 }; #define OSD_FIRMW_UPDATE_EN "Firmware update" diff --git a/src/OSDMain.cpp b/src/OSDMain.cpp index 5a0a5e63..315ef6f9 100644 --- a/src/OSDMain.cpp +++ b/src/OSDMain.cpp @@ -480,6 +480,7 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL) { menu_saverect = false; menu_level = 0; uint8_t opt = menuRun("ESPectrum " + Config::getArch() + "\n" + MENU_MAIN[Config::lang]); + // uint8_t opt = menuRun(Config::getArch() + "\n" + MENU_MAIN[Config::lang]); if (opt == 1) { // *********************************************************************************** @@ -495,7 +496,7 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL) { menu_level = 2; menu_saverect = true; if (sna_mnu == 1) { - string mFile = fileDialog(FileUtils::SNA_Path, MENU_SNA_TITLE[Config::lang],DISK_SNAFILE,30,16); + string mFile = fileDialog(FileUtils::SNA_Path, MENU_SNA_TITLE[Config::lang],DISK_SNAFILE,28,16); if (mFile != "") { mFile.erase(0, 1); string fname = FileUtils::MountPoint + "/" + FileUtils::SNA_Path + "/" + mFile; @@ -557,7 +558,7 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL) { if (tap_num == 1) { // menu_curopt = 1; // Select TAP File - string mFile = fileDialog(FileUtils::TAP_Path, MENU_TAP_TITLE[Config::lang],DISK_TAPFILE,30,16); + string mFile = fileDialog(FileUtils::TAP_Path, MENU_TAP_TITLE[Config::lang],DISK_TAPFILE,28,16); if (mFile != "") { string keySel = mFile.substr(0,1); @@ -655,7 +656,7 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL) { if (opt2 > 0) { if (opt2 == 1) { menu_saverect = true; - string mFile = fileDialog(FileUtils::DSK_Path, MENU_DSK_TITLE[Config::lang],DISK_DSKFILE,30,16); + string mFile = fileDialog(FileUtils::DSK_Path, MENU_DSK_TITLE[Config::lang],DISK_DSKFILE,26,15); if (mFile != "") { mFile.erase(0, 1); string fname = FileUtils::MountPoint + "/" + FileUtils::DSK_Path + "/" + mFile; @@ -1574,8 +1575,16 @@ if (target == NULL) { // osdCenteredMsg(OSD_FIRMW_BEGIN[Config::lang], LEVEL_INFO,0); + progressDialog(OSD_FIRMW[Config::lang],OSD_FIRMW_BEGIN[Config::lang],0,0); +// Fake erase progress bar ;D +delay(100); +for(int n=0; n <= 100; n += 10) { + progressDialog("","",n,1); + delay(100); +} + esp_ota_handle_t ota_handle; esp_err_t result = esp_ota_begin(target, OTA_SIZE_UNKNOWN, &ota_handle); if (result != ESP_OK) { diff --git a/src/OSDMenu.cpp b/src/OSDMenu.cpp index 6e667294..f23d96e2 100644 --- a/src/OSDMenu.cpp +++ b/src/OSDMenu.cpp @@ -272,6 +272,7 @@ unsigned short OSD::menuRun(string new_menu) { } col_count++; } + // printf("Cols: %d\n",cols); cols += 8; cols = (cols > 28 ? 28 : cols); @@ -639,9 +640,9 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols } - // // Force reindex (for testing) - // fclose(dirfile); - // reIndex = true; + // Force reindex (for testing) + fclose(dirfile); + reIndex = true; // There was no index or hashes are different: reIndex if (reIndex) { @@ -1590,6 +1591,8 @@ void OSD::progressDialog(string title, string msg, int percent, int action) { SaveRectpos++; } } + + // printf("SaveRectPos: %04X\n",SaveRectpos << 2); // Set font VIDEO::vga.setFont(Font6x8); @@ -1622,9 +1625,9 @@ void OSD::progressDialog(string title, string msg, int percent, int action) { } // Progress bar frame - progress_x = scrAlignCenterX(102); + progress_x = scrAlignCenterX(72); progress_y = y + (OSD_FONT_H * 4); - VIDEO::vga.rect(progress_x, progress_y, 102, OSD_FONT_H + 2, OSD::zxColor(0, 0)); + VIDEO::vga.rect(progress_x, progress_y, 72, OSD_FONT_H + 2, OSD::zxColor(0, 0)); progress_x++; progress_y++; @@ -1636,8 +1639,9 @@ void OSD::progressDialog(string title, string msg, int percent, int action) { VIDEO::vga.print(msg.c_str()); // Progress bar - VIDEO::vga.fillRect(progress_x, progress_y, percent, OSD_FONT_H, zxColor(5,1)); - VIDEO::vga.fillRect(progress_x + percent, progress_y, 100 - percent, OSD_FONT_H, zxColor(7,1)); + int barsize = 70 * ((float) percent / (float) 100); + VIDEO::vga.fillRect(progress_x, progress_y, barsize, OSD_FONT_H, zxColor(5,1)); + VIDEO::vga.fillRect(progress_x + barsize, progress_y, 70 - barsize, OSD_FONT_H, zxColor(7,1)); } else if (action == 2) { // CLOSE From 2fa546dc5665f9a392f517e8020a06a6424ba52f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Iborra?= Date: Tue, 5 Dec 2023 12:14:55 +0100 Subject: [PATCH 13/30] Progress bar trd scl fix --- src/OSDMenu.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/OSDMenu.cpp b/src/OSDMenu.cpp index f23d96e2..920d2bdc 100644 --- a/src/OSDMenu.cpp +++ b/src/OSDMenu.cpp @@ -640,9 +640,9 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols } - // Force reindex (for testing) - fclose(dirfile); - reIndex = true; + // // Force reindex (for testing) + // fclose(dirfile); + // reIndex = true; // There was no index or hashes are different: reIndex if (reIndex) { From 5952bf7f7a95e2984ee128c4db6ef3b0696ed677 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Iborra?= Date: Tue, 5 Dec 2023 12:44:22 +0100 Subject: [PATCH 14/30] Progress bar trd scl fix --- src/OSDMain.cpp | 240 +++++++++++++++++++++++++++++++++++++++++++++++- src/OSDMenu.cpp | 236 ----------------------------------------------- 2 files changed, 239 insertions(+), 237 deletions(-) diff --git a/src/OSDMain.cpp b/src/OSDMain.cpp index 315ef6f9..ee05133e 100644 --- a/src/OSDMain.cpp +++ b/src/OSDMain.cpp @@ -1639,4 +1639,242 @@ delay(1000); // Firmware written: reboot OSD::esp_hard_reset(); -} \ No newline at end of file +} + +void OSD::progressDialog(string title, string msg, int percent, int action) { + + static unsigned short h; + static unsigned short y; + static unsigned short w; + static unsigned short x; + static unsigned short progress_x; + static unsigned short progress_y; + static unsigned int j; + + if (action == 0 ) { // SHOW + + h = (OSD_FONT_H * 6) + 2; + y = scrAlignCenterY(h); + + if (msg.length() > (scrW / 6) - 4) msg = msg.substr(0,(scrW / 6) - 4); + if (title.length() > (scrW / 6) - 4) title = title.substr(0,(scrW / 6) - 4); + + w = (((msg.length() > title.length() + 6 ? msg.length(): title.length() + 6) + 2) * OSD_FONT_W) + 2; + x = scrAlignCenterX(w); + + // Save backbuffer data + j = SaveRectpos; + for (int m = y; m < y + h; m++) { + uint32_t *backbuffer32 = (uint32_t *)(VIDEO::vga.backBuffer[m]); + for (int n = x >> 2; n < ((x + w) >> 2) + 1; n++) { + VIDEO::SaveRect[SaveRectpos] = backbuffer32[n]; + SaveRectpos++; + } + } + + // printf("SaveRectPos: %04X\n",SaveRectpos << 2); + + // Set font + VIDEO::vga.setFont(Font6x8); + + // Menu border + VIDEO::vga.rect(x, y, w, h, OSD::zxColor(0, 0)); + + VIDEO::vga.fillRect(x + 1, y + 1, w - 2, OSD_FONT_H, zxColor(0,0)); + VIDEO::vga.fillRect(x + 1, y + 1 + OSD_FONT_H, w - 2, h - OSD_FONT_H - 2, zxColor(7,1)); + + // Title + VIDEO::vga.setTextColor(OSD::zxColor(7, 1), OSD::zxColor(0, 0)); + VIDEO::vga.setCursor(x + OSD_FONT_W + 1, y + 1); + VIDEO::vga.print(title.c_str()); + + // Msg + VIDEO::vga.setTextColor(OSD::zxColor(0, 0), OSD::zxColor(7, 1)); + VIDEO::vga.setCursor(scrAlignCenterX(msg.length() * OSD_FONT_W), y + 1 + (OSD_FONT_H * 2)); + VIDEO::vga.print(msg.c_str()); + + // Rainbow + unsigned short rb_y = y + 8; + unsigned short rb_paint_x = x + w - 30; + uint8_t rb_colors[] = {2, 6, 4, 5}; + for (uint8_t c = 0; c < 4; c++) { + for (uint8_t i = 0; i < 5; i++) { + VIDEO::vga.line(rb_paint_x + i, rb_y, rb_paint_x + 8 + i, rb_y - 8, OSD::zxColor(rb_colors[c], 1)); + } + rb_paint_x += 5; + } + + // Progress bar frame + progress_x = scrAlignCenterX(72); + progress_y = y + (OSD_FONT_H * 4); + VIDEO::vga.rect(progress_x, progress_y, 72, OSD_FONT_H + 2, OSD::zxColor(0, 0)); + progress_x++; + progress_y++; + + } else if (action == 1 ) { // UPDATE + + // Msg + VIDEO::vga.setTextColor(OSD::zxColor(0, 0), OSD::zxColor(7, 1)); + VIDEO::vga.setCursor(scrAlignCenterX(msg.length() * OSD_FONT_W), y + 1 + (OSD_FONT_H * 2)); + VIDEO::vga.print(msg.c_str()); + + // Progress bar + int barsize = (70 * percent) / 100; + VIDEO::vga.fillRect(progress_x, progress_y, barsize, OSD_FONT_H, zxColor(5,1)); + VIDEO::vga.fillRect(progress_x + barsize, progress_y, 70 - barsize, OSD_FONT_H, zxColor(7,1)); + + } else if (action == 2) { // CLOSE + + // Restore backbuffer data + SaveRectpos = j; + for (int m = y; m < y + h; m++) { + uint32_t *backbuffer32 = (uint32_t *)(VIDEO::vga.backBuffer[m]); + for (int n = x >> 2; n < ((x + w) >> 2) + 1; n++) { + backbuffer32[n] = VIDEO::SaveRect[j]; + j++; + } + } + + } + +} + +uint8_t OSD::msgDialog(string title, string msg) { + + const unsigned short h = (OSD_FONT_H * 6) + 2; + const unsigned short y = scrAlignCenterY(h); + uint8_t res = DLG_NO; + + if (msg.length() > (scrW / 6) - 4) msg = msg.substr(0,(scrW / 6) - 4); + if (title.length() > (scrW / 6) - 4) title = title.substr(0,(scrW / 6) - 4); + + const unsigned short w = (((msg.length() > title.length() + 6 ? msg.length() : title.length() + 6) + 2) * OSD_FONT_W) + 2; + const unsigned short x = scrAlignCenterX(w); + + // Save backbuffer data + unsigned int j = SaveRectpos; + for (int m = y; m < y + h; m++) { + uint32_t *backbuffer32 = (uint32_t *)(VIDEO::vga.backBuffer[m]); + for (int n = x >> 2; n < ((x + w) >> 2) + 1; n++) { + VIDEO::SaveRect[SaveRectpos] = backbuffer32[n]; + SaveRectpos++; + } + } + + // Set font + VIDEO::vga.setFont(Font6x8); + + // Menu border + VIDEO::vga.rect(x, y, w, h, OSD::zxColor(0, 0)); + + VIDEO::vga.fillRect(x + 1, y + 1, w - 2, OSD_FONT_H, zxColor(0,0)); + VIDEO::vga.fillRect(x + 1, y + 1 + OSD_FONT_H, w - 2, h - OSD_FONT_H - 2, zxColor(7,1)); + + // Title + VIDEO::vga.setTextColor(OSD::zxColor(7, 1), OSD::zxColor(0, 0)); + VIDEO::vga.setCursor(x + OSD_FONT_W + 1, y + 1); + VIDEO::vga.print(title.c_str()); + + // Msg + VIDEO::vga.setTextColor(OSD::zxColor(0, 0), OSD::zxColor(7, 1)); + VIDEO::vga.setCursor(scrAlignCenterX(msg.length() * OSD_FONT_W), y + 1 + (OSD_FONT_H * 2)); + VIDEO::vga.print(msg.c_str()); + + // Yes + VIDEO::vga.setTextColor(OSD::zxColor(0, 0), OSD::zxColor(7, 1)); + VIDEO::vga.setCursor(scrAlignCenterX(6 * OSD_FONT_W) - (w >> 2), y + 1 + (OSD_FONT_H * 4)); + VIDEO::vga.print(Config::lang ? " Si " : " Yes "); + + // // Ruler + // VIDEO::vga.setTextColor(OSD::zxColor(0, 0), OSD::zxColor(7, 1)); + // VIDEO::vga.setCursor(x + 1, y + 1 + (OSD_FONT_H * 3)); + // VIDEO::vga.print("123456789012345678901234567"); + + // No + VIDEO::vga.setTextColor(OSD::zxColor(0, 1), OSD::zxColor(5, 1)); + VIDEO::vga.setCursor(scrAlignCenterX(6 * OSD_FONT_W) + (w >> 2), y + 1 + (OSD_FONT_H * 4)); + VIDEO::vga.print(" No "); + + // Rainbow + unsigned short rb_y = y + 8; + unsigned short rb_paint_x = x + w - 30; + uint8_t rb_colors[] = {2, 6, 4, 5}; + for (uint8_t c = 0; c < 4; c++) { + for (uint8_t i = 0; i < 5; i++) { + VIDEO::vga.line(rb_paint_x + i, rb_y, rb_paint_x + 8 + i, rb_y - 8, OSD::zxColor(rb_colors[c], 1)); + } + rb_paint_x += 5; + } + + // VIDEO::vga.fillRect(x, y, w, h, paper); + // // VIDEO::vga.rect(x - 1, y - 1, w + 2, h + 2, ink); + // VIDEO::vga.setTextColor(ink, paper); + // VIDEO::vga.setFont(Font6x8); + // VIDEO::vga.setCursor(x + OSD_FONT_W, y + OSD_FONT_H); + // VIDEO::vga.print(msg.c_str()); + + // Keyboard loop + fabgl::VirtualKeyItem Menukey; + while (1) { + + if (ZXKeyb::Exists) OSD::ZXKbdRead(); + + ESPectrum::readKbdJoy(); + + // Process external keyboard + if (ESPectrum::PS2Controller.keyboard()->virtualKeyAvailable()) { + if (ESPectrum::readKbd(&Menukey)) { + if (!Menukey.down) continue; + + if (Menukey.vk == fabgl::VK_LEFT) { + // Yes + VIDEO::vga.setTextColor(OSD::zxColor(0, 1), OSD::zxColor(5, 1)); + VIDEO::vga.setCursor(scrAlignCenterX(6 * OSD_FONT_W) - (w >> 2), y + 1 + (OSD_FONT_H * 4)); + VIDEO::vga.print(Config::lang ? " Si " : " Yes "); + // No + VIDEO::vga.setTextColor(OSD::zxColor(0, 0), OSD::zxColor(7, 1)); + VIDEO::vga.setCursor(scrAlignCenterX(6 * OSD_FONT_W) + (w >> 2), y + 1 + (OSD_FONT_H * 4)); + VIDEO::vga.print(" No "); + click(); + res = DLG_YES; + } else if (Menukey.vk == fabgl::VK_RIGHT) { + // Yes + VIDEO::vga.setTextColor(OSD::zxColor(0, 0), OSD::zxColor(7, 1)); + VIDEO::vga.setCursor(scrAlignCenterX(6 * OSD_FONT_W) - (w >> 2), y + 1 + (OSD_FONT_H * 4)); + VIDEO::vga.print(Config::lang ? " Si " : " Yes "); + // No + VIDEO::vga.setTextColor(OSD::zxColor(0, 1), OSD::zxColor(5, 1)); + VIDEO::vga.setCursor(scrAlignCenterX(6 * OSD_FONT_W) + (w >> 2), y + 1 + (OSD_FONT_H * 4)); + VIDEO::vga.print(" No "); + click(); + res = DLG_NO; + } else if (Menukey.vk == fabgl::VK_RETURN || Menukey.vk == fabgl::VK_SPACE) { + break; + } else if (Menukey.vk == fabgl::VK_ESCAPE) { + res = DLG_CANCEL; + break; + } + } + + } + + vTaskDelay(5 / portTICK_PERIOD_MS); + + } + + click(); + + // Restore backbuffer data + SaveRectpos = j; + for (int m = y; m < y + h; m++) { + uint32_t *backbuffer32 = (uint32_t *)(VIDEO::vga.backBuffer[m]); + for (int n = x >> 2; n < ((x + w) >> 2) + 1; n++) { + backbuffer32[n] = VIDEO::SaveRect[j]; + j++; + } + } + + return res; + +} + diff --git a/src/OSDMenu.cpp b/src/OSDMenu.cpp index 920d2bdc..9c568833 100644 --- a/src/OSDMenu.cpp +++ b/src/OSDMenu.cpp @@ -1422,239 +1422,3 @@ int OSD::menuTape(string title) { } } -uint8_t OSD::msgDialog(string title, string msg) { - - const unsigned short h = (OSD_FONT_H * 6) + 2; - const unsigned short y = scrAlignCenterY(h); - uint8_t res = DLG_NO; - - if (msg.length() > (scrW / 6) - 4) msg = msg.substr(0,(scrW / 6) - 4); - if (title.length() > (scrW / 6) - 4) title = title.substr(0,(scrW / 6) - 4); - - const unsigned short w = (((msg.length() > title.length() + 6 ? msg.length() : title.length() + 6) + 2) * OSD_FONT_W) + 2; - const unsigned short x = scrAlignCenterX(w); - - // Save backbuffer data - unsigned int j = SaveRectpos; - for (int m = y; m < y + h; m++) { - uint32_t *backbuffer32 = (uint32_t *)(VIDEO::vga.backBuffer[m]); - for (int n = x >> 2; n < ((x + w) >> 2) + 1; n++) { - VIDEO::SaveRect[SaveRectpos] = backbuffer32[n]; - SaveRectpos++; - } - } - - // Set font - VIDEO::vga.setFont(Font6x8); - - // Menu border - VIDEO::vga.rect(x, y, w, h, OSD::zxColor(0, 0)); - - VIDEO::vga.fillRect(x + 1, y + 1, w - 2, OSD_FONT_H, zxColor(0,0)); - VIDEO::vga.fillRect(x + 1, y + 1 + OSD_FONT_H, w - 2, h - OSD_FONT_H - 2, zxColor(7,1)); - - // Title - VIDEO::vga.setTextColor(OSD::zxColor(7, 1), OSD::zxColor(0, 0)); - VIDEO::vga.setCursor(x + OSD_FONT_W + 1, y + 1); - VIDEO::vga.print(title.c_str()); - - // Msg - VIDEO::vga.setTextColor(OSD::zxColor(0, 0), OSD::zxColor(7, 1)); - VIDEO::vga.setCursor(scrAlignCenterX(msg.length() * OSD_FONT_W), y + 1 + (OSD_FONT_H * 2)); - VIDEO::vga.print(msg.c_str()); - - // Yes - VIDEO::vga.setTextColor(OSD::zxColor(0, 0), OSD::zxColor(7, 1)); - VIDEO::vga.setCursor(scrAlignCenterX(6 * OSD_FONT_W) - (w >> 2), y + 1 + (OSD_FONT_H * 4)); - VIDEO::vga.print(Config::lang ? " Si " : " Yes "); - - // // Ruler - // VIDEO::vga.setTextColor(OSD::zxColor(0, 0), OSD::zxColor(7, 1)); - // VIDEO::vga.setCursor(x + 1, y + 1 + (OSD_FONT_H * 3)); - // VIDEO::vga.print("123456789012345678901234567"); - - // No - VIDEO::vga.setTextColor(OSD::zxColor(0, 1), OSD::zxColor(5, 1)); - VIDEO::vga.setCursor(scrAlignCenterX(6 * OSD_FONT_W) + (w >> 2), y + 1 + (OSD_FONT_H * 4)); - VIDEO::vga.print(" No "); - - // Rainbow - unsigned short rb_y = y + 8; - unsigned short rb_paint_x = x + w - 30; - uint8_t rb_colors[] = {2, 6, 4, 5}; - for (uint8_t c = 0; c < 4; c++) { - for (uint8_t i = 0; i < 5; i++) { - VIDEO::vga.line(rb_paint_x + i, rb_y, rb_paint_x + 8 + i, rb_y - 8, OSD::zxColor(rb_colors[c], 1)); - } - rb_paint_x += 5; - } - - // VIDEO::vga.fillRect(x, y, w, h, paper); - // // VIDEO::vga.rect(x - 1, y - 1, w + 2, h + 2, ink); - // VIDEO::vga.setTextColor(ink, paper); - // VIDEO::vga.setFont(Font6x8); - // VIDEO::vga.setCursor(x + OSD_FONT_W, y + OSD_FONT_H); - // VIDEO::vga.print(msg.c_str()); - - // Keyboard loop - fabgl::VirtualKeyItem Menukey; - while (1) { - - if (ZXKeyb::Exists) OSD::ZXKbdRead(); - - ESPectrum::readKbdJoy(); - - // Process external keyboard - if (ESPectrum::PS2Controller.keyboard()->virtualKeyAvailable()) { - if (ESPectrum::readKbd(&Menukey)) { - if (!Menukey.down) continue; - - if (Menukey.vk == fabgl::VK_LEFT) { - // Yes - VIDEO::vga.setTextColor(OSD::zxColor(0, 1), OSD::zxColor(5, 1)); - VIDEO::vga.setCursor(scrAlignCenterX(6 * OSD_FONT_W) - (w >> 2), y + 1 + (OSD_FONT_H * 4)); - VIDEO::vga.print(Config::lang ? " Si " : " Yes "); - // No - VIDEO::vga.setTextColor(OSD::zxColor(0, 0), OSD::zxColor(7, 1)); - VIDEO::vga.setCursor(scrAlignCenterX(6 * OSD_FONT_W) + (w >> 2), y + 1 + (OSD_FONT_H * 4)); - VIDEO::vga.print(" No "); - click(); - res = DLG_YES; - } else if (Menukey.vk == fabgl::VK_RIGHT) { - // Yes - VIDEO::vga.setTextColor(OSD::zxColor(0, 0), OSD::zxColor(7, 1)); - VIDEO::vga.setCursor(scrAlignCenterX(6 * OSD_FONT_W) - (w >> 2), y + 1 + (OSD_FONT_H * 4)); - VIDEO::vga.print(Config::lang ? " Si " : " Yes "); - // No - VIDEO::vga.setTextColor(OSD::zxColor(0, 1), OSD::zxColor(5, 1)); - VIDEO::vga.setCursor(scrAlignCenterX(6 * OSD_FONT_W) + (w >> 2), y + 1 + (OSD_FONT_H * 4)); - VIDEO::vga.print(" No "); - click(); - res = DLG_NO; - } else if (Menukey.vk == fabgl::VK_RETURN || Menukey.vk == fabgl::VK_SPACE) { - break; - } else if (Menukey.vk == fabgl::VK_ESCAPE) { - res = DLG_CANCEL; - break; - } - } - - } - - vTaskDelay(5 / portTICK_PERIOD_MS); - - } - - click(); - - // Restore backbuffer data - SaveRectpos = j; - for (int m = y; m < y + h; m++) { - uint32_t *backbuffer32 = (uint32_t *)(VIDEO::vga.backBuffer[m]); - for (int n = x >> 2; n < ((x + w) >> 2) + 1; n++) { - backbuffer32[n] = VIDEO::SaveRect[j]; - j++; - } - } - - return res; - -} - -void OSD::progressDialog(string title, string msg, int percent, int action) { - - static unsigned short h; - static unsigned short y; - static unsigned short w; - static unsigned short x; - static unsigned short progress_x; - static unsigned short progress_y; - static unsigned int j; - - if (action == 0 ) { // SHOW - - h = (OSD_FONT_H * 6) + 2; - y = scrAlignCenterY(h); - - if (msg.length() > (scrW / 6) - 4) msg = msg.substr(0,(scrW / 6) - 4); - if (title.length() > (scrW / 6) - 4) title = title.substr(0,(scrW / 6) - 4); - - w = (((msg.length() > title.length() + 6 ? msg.length(): title.length() + 6) + 2) * OSD_FONT_W) + 2; - x = scrAlignCenterX(w); - - // Save backbuffer data - j = SaveRectpos; - for (int m = y; m < y + h; m++) { - uint32_t *backbuffer32 = (uint32_t *)(VIDEO::vga.backBuffer[m]); - for (int n = x >> 2; n < ((x + w) >> 2) + 1; n++) { - VIDEO::SaveRect[SaveRectpos] = backbuffer32[n]; - SaveRectpos++; - } - } - - // printf("SaveRectPos: %04X\n",SaveRectpos << 2); - - // Set font - VIDEO::vga.setFont(Font6x8); - - // Menu border - VIDEO::vga.rect(x, y, w, h, OSD::zxColor(0, 0)); - - VIDEO::vga.fillRect(x + 1, y + 1, w - 2, OSD_FONT_H, zxColor(0,0)); - VIDEO::vga.fillRect(x + 1, y + 1 + OSD_FONT_H, w - 2, h - OSD_FONT_H - 2, zxColor(7,1)); - - // Title - VIDEO::vga.setTextColor(OSD::zxColor(7, 1), OSD::zxColor(0, 0)); - VIDEO::vga.setCursor(x + OSD_FONT_W + 1, y + 1); - VIDEO::vga.print(title.c_str()); - - // Msg - VIDEO::vga.setTextColor(OSD::zxColor(0, 0), OSD::zxColor(7, 1)); - VIDEO::vga.setCursor(scrAlignCenterX(msg.length() * OSD_FONT_W), y + 1 + (OSD_FONT_H * 2)); - VIDEO::vga.print(msg.c_str()); - - // Rainbow - unsigned short rb_y = y + 8; - unsigned short rb_paint_x = x + w - 30; - uint8_t rb_colors[] = {2, 6, 4, 5}; - for (uint8_t c = 0; c < 4; c++) { - for (uint8_t i = 0; i < 5; i++) { - VIDEO::vga.line(rb_paint_x + i, rb_y, rb_paint_x + 8 + i, rb_y - 8, OSD::zxColor(rb_colors[c], 1)); - } - rb_paint_x += 5; - } - - // Progress bar frame - progress_x = scrAlignCenterX(72); - progress_y = y + (OSD_FONT_H * 4); - VIDEO::vga.rect(progress_x, progress_y, 72, OSD_FONT_H + 2, OSD::zxColor(0, 0)); - progress_x++; - progress_y++; - - } else if (action == 1 ) { // UPDATE - - // Msg - VIDEO::vga.setTextColor(OSD::zxColor(0, 0), OSD::zxColor(7, 1)); - VIDEO::vga.setCursor(scrAlignCenterX(msg.length() * OSD_FONT_W), y + 1 + (OSD_FONT_H * 2)); - VIDEO::vga.print(msg.c_str()); - - // Progress bar - int barsize = 70 * ((float) percent / (float) 100); - VIDEO::vga.fillRect(progress_x, progress_y, barsize, OSD_FONT_H, zxColor(5,1)); - VIDEO::vga.fillRect(progress_x + barsize, progress_y, 70 - barsize, OSD_FONT_H, zxColor(7,1)); - - } else if (action == 2) { // CLOSE - - // Restore backbuffer data - SaveRectpos = j; - for (int m = y; m < y + h; m++) { - uint32_t *backbuffer32 = (uint32_t *)(VIDEO::vga.backBuffer[m]); - for (int n = x >> 2; n < ((x + w) >> 2) + 1; n++) { - backbuffer32[n] = VIDEO::SaveRect[j]; - j++; - } - } - - } - -} From 60e908255f2fa56627c796534aef24b84ff138cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Iborra?= Date: Tue, 5 Dec 2023 15:34:31 +0100 Subject: [PATCH 15/30] Little audio adjustments --- components/pwm_audio/pwm_audio.c | 2 +- src/ESPectrum.cpp | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/components/pwm_audio/pwm_audio.c b/components/pwm_audio/pwm_audio.c index 6b2f002c..8a2b723b 100644 --- a/components/pwm_audio/pwm_audio.c +++ b/components/pwm_audio/pwm_audio.c @@ -582,7 +582,7 @@ esp_err_t pwm_audio_set_sample_rate(int rate) { esp_err_t res; PWM_AUDIO_CHECK(NULL != g_pwm_audio_handle, PWM_AUDIO_NOT_INITIALIZED, ESP_ERR_INVALID_STATE); - PWM_AUDIO_CHECK(g_pwm_audio_handle->status != PWM_AUDIO_STATUS_BUSY, PWM_AUDIO_STATUS_ERROR, ESP_ERR_INVALID_ARG); + // PWM_AUDIO_CHECK(g_pwm_audio_handle->status != PWM_AUDIO_STATUS_BUSY, PWM_AUDIO_STATUS_ERROR, ESP_ERR_INVALID_ARG); PWM_AUDIO_CHECK(rate <= SAMPLE_RATE_MAX && rate >= SAMPLE_RATE_MIN, PWM_AUDIO_FRAMERATE_ERROR, ESP_ERR_INVALID_ARG); pwm_audio_data_t *handle = g_pwm_audio_handle; diff --git a/src/ESPectrum.cpp b/src/ESPectrum.cpp index 57eff450..088dd568 100644 --- a/src/ESPectrum.cpp +++ b/src/ESPectrum.cpp @@ -605,7 +605,7 @@ void ESPectrum::reset() lastaudioBit=0; // Set samples per frame and AY_emu flag depending on arch - int prevOverSamples = overSamplesPerFrame; + int prevAudio_freq = Audio_freq; if (arch == "48K") { overSamplesPerFrame=ESP_AUDIO_OVERSAMPLES_48; samplesPerFrame=ESP_AUDIO_SAMPLES_48; @@ -626,10 +626,15 @@ void ESPectrum::reset() ESPoffset = 0; // Readjust output pwmaudio frequency if needed - if (overSamplesPerFrame != prevOverSamples) { + if (prevAudio_freq != Audio_freq) { // printf("Resetting pwmaudio to freq: %d\n",Audio_freq); - pwm_audio_set_sample_rate(Audio_freq); + + esp_err_t res; + res = pwm_audio_set_sample_rate(Audio_freq); + if (res != ESP_OK) { + printf("Can't set sample rate\n"); + } // pwm_audio_stop(); // delay(100); // Maybe this fix random sound lost ? From 5f9f261bb5030305038b597128f2f4e159b56127 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Iborra?= Date: Wed, 6 Dec 2023 02:46:53 +0100 Subject: [PATCH 16/30] Confirmation dialog on snapshot save --- include/messages.h | 12 ++++++++++-- src/OSDMain.cpp | 41 ++++++++++++++++++++++++++--------------- 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/include/messages.h b/include/messages.h index 4546acca..41ac0972 100644 --- a/include/messages.h +++ b/include/messages.h @@ -75,6 +75,14 @@ static const char *OSD_PAUSE[2] = { OSD_PAUSE_EN,OSD_PAUSE_ES }; #define OSD_TAPE_SAVE_ERR "ERROR Saving TAP file" #define OSD_BETADISK_LOAD_ERR "ERROR Loading Disk file" +#define OSD_PSNA_SAVE_EN "Save snapshot" +#define OSD_PSNA_SAVE_ES "Guardar snapshot" +static const char *OSD_PSNA_SAVE[2] = { OSD_PSNA_SAVE_EN, OSD_PSNA_SAVE_ES }; + +#define OSD_PSNA_EXISTS_EN "Overwrite slot?" +#define OSD_PSNA_EXISTS_ES "\xA8" "Sobreescribir slot?" +static const char *OSD_PSNA_EXISTS[2] = { OSD_PSNA_EXISTS_EN, OSD_PSNA_EXISTS_ES }; + #define OSD_TAPE_SELECT_ERR_EN "No TAP selected" #define OSD_TAPE_SELECT_ERR_ES "TAP no seleccionado" static const char *OSD_TAPE_SELECT_ERR[2] = { OSD_TAPE_SELECT_ERR_EN,OSD_TAPE_SELECT_ERR_ES }; @@ -100,8 +108,8 @@ static const char *OSD_FILE_INDEXING_3[2] = { OSD_FILE_INDEXING_EN_3, OSD_FILE_I static const char *OSD_FIRMW_UPDATE[2] = { OSD_FIRMW_UPDATE_EN,OSD_FIRMW_UPDATE_ES}; #define OSD_DLG_SURE_EN "Are you sure?" -static const char DLG_SURE_ES[] = {168,'D','e','s','e','a',' ','c','o','n','t','i','n','u','a','r','?'}; -static const char *OSD_DLG_SURE[2] = { OSD_DLG_SURE_EN, DLG_SURE_ES}; +#define OSD_DLG_SURE_ES "\xA8" "Desea continuar?" +static const char *OSD_DLG_SURE[2] = { OSD_DLG_SURE_EN, OSD_DLG_SURE_ES}; #define OSD_FIRMW_EN "Updating firmware" #define OSD_FIRMW_ES "Actualizando firmware" diff --git a/src/OSDMain.cpp b/src/OSDMain.cpp index ee05133e..c915f918 100644 --- a/src/OSDMain.cpp +++ b/src/OSDMain.cpp @@ -172,31 +172,38 @@ void OSD::drawStats() { static bool persistSave(uint8_t slotnumber) { + struct stat stat_buf; char persistfname[sizeof(DISK_PSNA_FILE) + 6]; char persistfinfo[sizeof(DISK_PSNA_FILE) + 6]; sprintf(persistfname,DISK_PSNA_FILE "%u.sna",slotnumber); sprintf(persistfinfo,DISK_PSNA_FILE "%u.esp",slotnumber); + string finfo = FileUtils::MountPoint + DISK_PSNA_DIR + "/" + persistfinfo; + + // Slot isn't void + if (stat(finfo.c_str(), &stat_buf) == 0) { + string title = OSD_PSNA_SAVE[Config::lang]; + string msg = OSD_PSNA_EXISTS[Config::lang]; + uint8_t res = OSD::msgDialog(title,msg); + if (res != DLG_YES) return false; + } OSD::osdCenteredMsg(OSD_PSNA_SAVING, LEVEL_INFO, 0); // Save info file - string finfo = FileUtils::MountPoint + DISK_PSNA_DIR + "/" + persistfinfo; FILE *f = fopen(finfo.c_str(), "w"); if (f == NULL) { + printf("Error opening %s\n",persistfinfo); - return false; - } - // Put architecture on info file - fputs((Config::getArch() + "\n").c_str(),f); - fclose(f); - if (!FileSNA::save(FileUtils::MountPoint + DISK_PSNA_DIR + "/" + persistfname)) { - OSD::osdCenteredMsg(OSD_PSNA_SAVE_ERR, LEVEL_WARN); - return false; - } + } else { + + fputs((Config::getArch() + "\n").c_str(),f); // Put architecture on info file + fclose(f); - // OSD::osdCenteredMsg(OSD_PSNA_SAVED, LEVEL_INFO); + if (!FileSNA::save(FileUtils::MountPoint + DISK_PSNA_DIR + "/" + persistfname)) OSD::osdCenteredMsg(OSD_PSNA_SAVE_ERR, LEVEL_WARN); + + } return true; @@ -337,12 +344,15 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL) { } } else if (KeytoESP == fabgl::VK_F4) { + // Persist Save menu_level = 0; menu_curopt = 1; - // Persist Save - uint8_t opt2 = menuRun(MENU_PERSIST_SAVE[Config::lang]); - if (opt2 > 0 && opt2<11) { - persistSave(opt2); + while (1) { + uint8_t opt2 = menuRun(MENU_PERSIST_SAVE[Config::lang]); + if (opt2 > 0 && opt2<11) { + if (persistSave(opt2)) return; + menu_curopt = opt2; + } else break; } } else if (KeytoESP == fabgl::VK_F5) { @@ -1760,6 +1770,7 @@ uint8_t OSD::msgDialog(string title, string msg) { SaveRectpos++; } } + // printf("SaveRectPos: %04X\n",SaveRectpos << 2); // Set font VIDEO::vga.setFont(Font6x8); From 6d37cf8fa6b0df306743d935d066f34582669152 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Iborra?= Date: Wed, 6 Dec 2023 02:49:55 +0100 Subject: [PATCH 17/30] message.h adjustments --- include/messages.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/messages.h b/include/messages.h index 41ac0972..5d0851fc 100644 --- a/include/messages.h +++ b/include/messages.h @@ -42,7 +42,7 @@ visit https://zxespectrum.speccy.org/contacto #define MSG_SAVE_CONFIG "Saving config file" #define MSG_VGA_INIT "Initializing VGA" // #define EMU_VERSION " v1.0 " -#define EMU_VERSION " Dev 041223 " +#define EMU_VERSION " Dev 061223 " // Error #define ERROR_TITLE " !!! ERROR - CLIVE MEDITATION !!! " @@ -80,7 +80,7 @@ static const char *OSD_PAUSE[2] = { OSD_PAUSE_EN,OSD_PAUSE_ES }; static const char *OSD_PSNA_SAVE[2] = { OSD_PSNA_SAVE_EN, OSD_PSNA_SAVE_ES }; #define OSD_PSNA_EXISTS_EN "Overwrite slot?" -#define OSD_PSNA_EXISTS_ES "\xA8" "Sobreescribir slot?" +#define OSD_PSNA_EXISTS_ES "\xA8" "Sobreescribir ranura?" static const char *OSD_PSNA_EXISTS[2] = { OSD_PSNA_EXISTS_EN, OSD_PSNA_EXISTS_ES }; #define OSD_TAPE_SELECT_ERR_EN "No TAP selected" From f8f2cb95cf4e5b5df86e40dd55f42b3582cf7e36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Iborra?= Date: Fri, 8 Dec 2023 02:06:55 +0100 Subject: [PATCH 18/30] File dialog status bar and SAVE command dialog --- include/OSDMain.h | 5 +++- include/messages.h | 12 +++++++-- src/FileUtils.cpp | 10 ++++++-- src/OSDMain.cpp | 5 ++++ src/OSDMenu.cpp | 37 ++++++++++++++++++++++++--- src/Z80_JLS.cpp | 62 ++++++++++++++++++++++++++++++---------------- 6 files changed, 101 insertions(+), 30 deletions(-) diff --git a/include/OSDMain.h b/include/OSDMain.h index c0565585..271068d2 100644 --- a/include/OSDMain.h +++ b/include/OSDMain.h @@ -110,9 +110,12 @@ class OSD static unsigned short menu_curopt; static unsigned int SaveRectpos; - static uint8_t msgDialog(string title, string msg); + static unsigned int elements; + static unsigned int ndirs; + static uint8_t msgDialog(string title, string msg); static void progressDialog(string title, string msg, int percent, int action); + string inputBox(int x, int y, string text); // Rows static unsigned short rowCount(string menu); diff --git a/include/messages.h b/include/messages.h index 5d0851fc..a9ad3923 100644 --- a/include/messages.h +++ b/include/messages.h @@ -75,6 +75,14 @@ static const char *OSD_PAUSE[2] = { OSD_PAUSE_EN,OSD_PAUSE_ES }; #define OSD_TAPE_SAVE_ERR "ERROR Saving TAP file" #define OSD_BETADISK_LOAD_ERR "ERROR Loading Disk file" +#define OSD_TAPE_SAVE_EN "SAVE command" +#define OSD_TAPE_SAVE_ES "Comando SAVE" +static const char *OSD_TAPE_SAVE[2] = { OSD_TAPE_SAVE_EN, OSD_TAPE_SAVE_ES }; + +#define OSD_TAPE_SAVE_EXIST_EN "File exists. Overwrite?" +#define OSD_TAPE_SAVE_EXIST_ES "El fichero ya existe " "\xA8" "Sobreescribir?" +static const char *OSD_TAPE_SAVE_EXIST[2] = { OSD_TAPE_SAVE_EXIST_EN, OSD_TAPE_SAVE_EXIST_ES }; + #define OSD_PSNA_SAVE_EN "Save snapshot" #define OSD_PSNA_SAVE_ES "Guardar snapshot" static const char *OSD_PSNA_SAVE[2] = { OSD_PSNA_SAVE_EN, OSD_PSNA_SAVE_ES }; @@ -144,8 +152,8 @@ static const char *MENU_SNA_TITLE[2] = { MENU_SNA_TITLE_EN,MENU_SNA_TITLE_ES }; #define MENU_TAP_TITLE_ES "Elija fichero TAP" static const char *MENU_TAP_TITLE[2] = { MENU_TAP_TITLE_EN,MENU_TAP_TITLE_ES }; -#define MENU_DSK_TITLE_EN "Select disk image" -#define MENU_DSK_TITLE_ES "Elija imagen de disco" +#define MENU_DSK_TITLE_EN "Select disk" +#define MENU_DSK_TITLE_ES "Elija disco" static const char *MENU_DSK_TITLE[2] = { MENU_DSK_TITLE_EN,MENU_DSK_TITLE_ES }; #define MENU_SNA_EN \ diff --git a/src/FileUtils.cpp b/src/FileUtils.cpp index b1d33c2d..882360f4 100644 --- a/src/FileUtils.cpp +++ b/src/FileUtils.cpp @@ -308,6 +308,9 @@ void FileUtils::DirToFile(string fpath, uint8_t ftype) { unsigned long h = 0, high; // Directory Hash + OSD::elements = 0; + OSD::ndirs = 0; + while ((de = readdir(dir)) != nullptr) { string fname = de->d_name; // if (fname[0] == 'A') printf("Fname: %s\n",fname.c_str()); @@ -318,10 +321,13 @@ void FileUtils::DirToFile(string fpath, uint8_t ftype) { if ((de->d_type == DT_DIR) || ((fname.size() > 3) && (std::find(filexts.begin(),filexts.end(),fname.substr(fname.size()-4)) != filexts.end()))) { // if (fname[0] == 'A') printf("Fname3: %s\n",fname.c_str()); - if (de->d_type == DT_DIR) + if (de->d_type == DT_DIR) { filenames.push_back((char(32) + fname).c_str()); - else + OSD::ndirs++; + } else { filenames.push_back(fname.c_str()); + OSD::elements++; + } // Calc hash for (int i = 0; i < fname.length(); i++) { diff --git a/src/OSDMain.cpp b/src/OSDMain.cpp index c915f918..ca68c85b 100644 --- a/src/OSDMain.cpp +++ b/src/OSDMain.cpp @@ -1889,3 +1889,8 @@ uint8_t OSD::msgDialog(string title, string msg) { } +string OSD::inputBox(int x, int y, string text) { + +return text; + +} \ No newline at end of file diff --git a/src/OSDMenu.cpp b/src/OSDMenu.cpp index 9c568833..0ca8b42b 100644 --- a/src/OSDMenu.cpp +++ b/src/OSDMenu.cpp @@ -519,13 +519,15 @@ static inline void trim(std::string &s) { // } FILE *dirfile; +unsigned int OSD::elements; +unsigned int OSD::ndirs; // Run a new file menu string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols, uint8_t mfrows) { struct stat stat_buf; bool reIndex; - + // Position if (menu_level == 0) { x = (Config::aspect_16_9 ? 24 : 4); @@ -537,7 +539,7 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols // Columns and Rows cols = mfcols; - mf_rows = mfrows + (Config::aspect_16_9 ? 0 : 2); + mf_rows = mfrows + (Config::aspect_16_9 ? 0 : 1); // printf("Focus: %d, Begin_row: %d, mf_rows: %d\n",(int) FileUtils::fileTypes[ftype].focus,(int) FileUtils::fileTypes[ftype].begin_row,(int) mf_rows); if (FileUtils::fileTypes[ftype].focus > mf_rows - 1) { @@ -547,7 +549,7 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols // Size w = (cols * OSD_FONT_W) + 2; - h = (mf_rows * OSD_FONT_H) + 2; + h = ((mf_rows + 1) * OSD_FONT_H) + 2; // menu = title + "\n" + fdir + "\n"; menu = title + "\n" + ( fdir.length() == 1 ? fdir : fdir.substr(0,fdir.length()-1)) + "\n"; @@ -555,12 +557,18 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols fd_PrintRow(1, IS_INFO); // Path // Draw blank rows - for (uint8_t row = 2; row < mf_rows; row++) { + uint8_t row = 2; + for (; row < mf_rows; row++) { VIDEO::vga.setTextColor(OSD::zxColor(0, 1), OSD::zxColor(7, 1)); menuAt(row, 0); VIDEO::vga.print(std::string(cols, ' ').c_str()); } + // Print status bar + menuAt(row, 0); + VIDEO::vga.setTextColor(OSD::zxColor(7, 1), OSD::zxColor(5, 0)); + VIDEO::vga.print(std::string(cols, ' ').c_str()); + while(1) { reIndex = false; @@ -601,6 +609,8 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols string fdir = filedir.substr(0,filedir.length() - 1); if ((dir = opendir(fdir.c_str())) != nullptr) { + elements = 0; + ndirs = 0; while ((de = readdir(dir)) != nullptr) { string fname = de->d_name; if (de->d_type == DT_REG || de->d_type == DT_DIR) { @@ -613,6 +623,10 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols if (high = hash & 0xF0000000) hash ^= high >> 24; hash &= ~high; } + if (de->d_type == DT_REG) + elements++; // Count elements in dir + else if (de->d_type == DT_DIR) + ndirs++; } } } @@ -688,6 +702,21 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols // Process external keyboard if (ESPectrum::PS2Controller.keyboard()->virtualKeyAvailable()) { + + // Print elements + VIDEO::vga.setTextColor(OSD::zxColor(7, 1), OSD::zxColor(5, 0)); + if (elements) { + menuAt(mfrows + 1, cols - (real_rows > virtual_rows ? 13 : 12)); + char elements_txt[13]; + int nitem = (FileUtils::fileTypes[ftype].begin_row + FileUtils::fileTypes[ftype].focus ) - (4 + ndirs) + (fdir.length() == 1); + snprintf(elements_txt, sizeof(elements_txt), "%d/%d ", nitem > 0 ? nitem : 0 , elements); + VIDEO::vga.print(std::string(12 - strlen(elements_txt), ' ').c_str()); + VIDEO::vga.print(elements_txt); + } else { + menuAt(mfrows + 1, cols - 13); + VIDEO::vga.print(" "); + } + if (ESPectrum::readKbd(&Menukey)) { if (!Menukey.down) continue; diff --git a/src/Z80_JLS.cpp b/src/Z80_JLS.cpp index 9f061f40..b60f35de 100644 --- a/src/Z80_JLS.cpp +++ b/src/Z80_JLS.cpp @@ -26,6 +26,8 @@ #include "Tape.h" #include "Config.h" #include "FileUtils.h" +#include "OSDMain.h" +#include "messages.h" // #pragma GCC optimize("O3") @@ -4520,6 +4522,8 @@ static inline void rtrim(std::string &s) { }).base(), s.end()); } +static uint8_t SaveRes; + //Subconjunto de instrucciones 0xDD / 0xFD /* * Hay que tener en cuenta el manejo de secuencias códigos DD/FD que no @@ -4619,26 +4623,38 @@ void Z80::decodeDDFD(RegisterPair& regIXY) { rtrim(name); Tape::tapeSaveName = FileUtils::MountPoint + "/" + FileUtils::TAP_Path + "/" + name + ".tap"; - // printf("Removing previuous tap file %s.\n",Tape::tapeSaveName.c_str()); - /*int result = */remove(Tape::tapeSaveName.c_str()); + struct stat stat_buf; + SaveRes = DLG_YES; + if (stat(Tape::tapeSaveName.c_str(), &stat_buf) == 0) { + string title = OSD_TAPE_SAVE[Config::lang]; + string msg = OSD_TAPE_SAVE_EXIST[Config::lang]; + SaveRes = OSD::msgDialog(title,msg); + } - // check if file has been deleted successfully - // if (result != 0) { - // // print error message - // printf("File deletion failed\n"); - // } - // else { - // printf("File deleted succesfully\n"); - // } + if (SaveRes == DLG_YES) { - // printf("Saving %s header.\n",Tape::tapeSaveName.c_str()); - - REG_DE--; - regA = 0x00; + // printf("Removing previuous tap file %s.\n",Tape::tapeSaveName.c_str()); + /*int result = */remove(Tape::tapeSaveName.c_str()); + + // check if file has been deleted successfully + // if (result != 0) { + // // print error message + // printf("File deletion failed\n"); + // } + // else { + // printf("File deleted succesfully\n"); + // } + + // printf("Saving %s header.\n",Tape::tapeSaveName.c_str()); + + REG_DE--; + regA = 0x00; - Tape::Save(); + Tape::Save(); - REG_PC = 0x555; + REG_PC = 0x555; + + } } else { @@ -4648,13 +4664,17 @@ void Z80::decodeDDFD(RegisterPair& regIXY) { // printf("Saving %s block.\n",Tape::tapeSaveName.c_str()); - REG_DE--; - regIXY.word++; - regA = 0xFF; + if (SaveRes == DLG_YES) { + + REG_DE--; + regIXY.word++; + regA = 0xFF; + + Tape::Save(); - Tape::Save(); + REG_PC = 0x555; - REG_PC = 0x555; + } } From d937180baa5700d8aefbcd63e2c9c13e90d2606c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Iborra?= Date: Fri, 8 Dec 2023 09:58:06 +0100 Subject: [PATCH 19/30] 16:9 fileMenu status bar fix --- src/OSDMenu.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OSDMenu.cpp b/src/OSDMenu.cpp index 0ca8b42b..71e5ebbf 100644 --- a/src/OSDMenu.cpp +++ b/src/OSDMenu.cpp @@ -706,14 +706,14 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols // Print elements VIDEO::vga.setTextColor(OSD::zxColor(7, 1), OSD::zxColor(5, 0)); if (elements) { - menuAt(mfrows + 1, cols - (real_rows > virtual_rows ? 13 : 12)); + menuAt(mfrows + (Config::aspect_16_9 ? 0 : 1), cols - (real_rows > virtual_rows ? 13 : 12)); char elements_txt[13]; int nitem = (FileUtils::fileTypes[ftype].begin_row + FileUtils::fileTypes[ftype].focus ) - (4 + ndirs) + (fdir.length() == 1); snprintf(elements_txt, sizeof(elements_txt), "%d/%d ", nitem > 0 ? nitem : 0 , elements); VIDEO::vga.print(std::string(12 - strlen(elements_txt), ' ').c_str()); VIDEO::vga.print(elements_txt); } else { - menuAt(mfrows + 1, cols - 13); + menuAt(mfrows + (Config::aspect_16_9 ? 0 : 1), cols - 13); VIDEO::vga.print(" "); } From 38f344113501adf4e2b5e569344f80cc741382e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Iborra?= Date: Sat, 9 Dec 2023 04:45:03 +0100 Subject: [PATCH 20/30] fileDialog focused item autoscroll --- include/ESPectrum.h | 2 + include/OSDMain.h | 3 + src/ESPectrum.cpp | 11 +++ src/OSDMenu.cpp | 212 +++++++++++++++++++++++++++++--------------- 4 files changed, 158 insertions(+), 70 deletions(-) diff --git a/include/ESPectrum.h b/include/ESPectrum.h index 2459545a..4cfb623e 100644 --- a/include/ESPectrum.h +++ b/include/ESPectrum.h @@ -111,6 +111,8 @@ class ESPectrum static bool trdos; static WD1793 Betadisk; + // static uint32_t sessid; + private: static void audioTask(void* unused); diff --git a/include/OSDMain.h b/include/OSDMain.h index 271068d2..afdfd33e 100644 --- a/include/OSDMain.h +++ b/include/OSDMain.h @@ -110,6 +110,9 @@ class OSD static unsigned short menu_curopt; static unsigned int SaveRectpos; + static int8_t fdScrollPos; + static int timeStartScroll; + static int timeScroll; static unsigned int elements; static unsigned int ndirs; diff --git a/src/ESPectrum.cpp b/src/ESPectrum.cpp index 088dd568..82494bf0 100644 --- a/src/ESPectrum.cpp +++ b/src/ESPectrum.cpp @@ -62,6 +62,7 @@ visit https://zxespectrum.speccy.org/contacto #include "esp_timer.h" #include "esp_system.h" #include "esp_spi_flash.h" +// #include "bootloader_random.h" #endif using namespace std; @@ -304,9 +305,19 @@ void ESPectrum::bootKeyboard() { //======================================================================================= // TaskHandle_t ESPectrum::loopTaskHandle; +// uint32_t ESPectrum::sessid; + void ESPectrum::setup() { + // //======================================================================================= + // // GENERATE SESSION ID + // //======================================================================================= + // bootloader_random_enable(); + // sessid = esp_random(); + // printf("SESSION ID: %08X\n",(unsigned int)sessid); + // bootloader_random_disable(); + #ifndef ESP32_SDL2_WRAPPER if (Config::slog_on) { diff --git a/src/OSDMenu.cpp b/src/OSDMenu.cpp index 71e5ebbf..e082448e 100644 --- a/src/OSDMenu.cpp +++ b/src/OSDMenu.cpp @@ -521,11 +521,15 @@ static inline void trim(std::string &s) { FILE *dirfile; unsigned int OSD::elements; unsigned int OSD::ndirs; +int8_t OSD::fdScrollPos; +int OSD::timeStartScroll; +int OSD::timeScroll; // Run a new file menu string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols, uint8_t mfrows) { - struct stat stat_buf; + // struct stat stat_buf; + long dirfilesize; bool reIndex; // Position @@ -569,11 +573,28 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols VIDEO::vga.setTextColor(OSD::zxColor(7, 1), OSD::zxColor(5, 0)); VIDEO::vga.print(std::string(cols, ' ').c_str()); + // char fsessid[32]; while(1) { reIndex = false; string filedir = FileUtils::MountPoint + fdir; + // // Get / Create sessid + // bool sessid_ok = false; + // if (stat((filedir + ".sessid").c_str(), &stat_buf) == 0) { + // dirfile = fopen((filedir + ".sessid").c_str(), "r"); + // fgets(fsessid, sizeof(fsessid), dirfile); + // printf("FSessId: %s Sessid: %u\n",fsessid,ESPectrum::sessid); + // if (stoul(fsessid) == ESPectrum::sessid) sessid_ok = true; + // } + + // if (!sessid_ok) { + // dirfile = fopen((filedir + ".sessid").c_str(), "w"); + // fputs(to_string(ESPectrum::sessid).c_str(),dirfile); + // } + + // fclose(dirfile); + // Open dir file for read dirfile = fopen((filedir + FileUtils::fileTypes[ftype].indexFilename).c_str(), "r"); if (dirfile == NULL) { @@ -583,74 +604,82 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols } else { - // Read dir hash from file - stat((filedir + FileUtils::fileTypes[ftype].indexFilename).c_str(), &stat_buf); - fseek(dirfile, (stat_buf.st_size >> 5) << 5,SEEK_SET); - char fhash[32]; - fgets(fhash, sizeof(fhash), dirfile); - // printf("File Hash: %s\n",fhash); - rewind(dirfile); - - // Calc dir hash - DIR *dir; - struct dirent* de; - - std::vector filexts; - size_t pos = 0; - string ss = FileUtils::fileTypes[ftype].fileExts; - while ((pos = ss.find(",")) != std::string::npos) { - filexts.push_back(ss.substr(0, pos)); - ss.erase(0, pos + 1); - } - filexts.push_back(ss.substr(0)); - - unsigned long hash = 0, high; // Name checksum variables - - string fdir = filedir.substr(0,filedir.length() - 1); - if ((dir = opendir(fdir.c_str())) != nullptr) { - - elements = 0; - ndirs = 0; - while ((de = readdir(dir)) != nullptr) { - string fname = de->d_name; - if (de->d_type == DT_REG || de->d_type == DT_DIR) { - if (fname.compare(0,1,".") != 0) { - // printf("Fname: %s Fname size: %d\n",fname.c_str(),fname.size()); - if ((de->d_type == DT_DIR) || ((fname.size() > 3) && (std::find(filexts.begin(),filexts.end(),fname.substr(fname.size()-4)) != filexts.end()))) { - // Calculate name checksum - for (int i = 0; i < fname.length(); i++) { - hash = (hash << 4) + fname[i]; - if (high = hash & 0xF0000000) hash ^= high >> 24; - hash &= ~high; + // stat((filedir + FileUtils::fileTypes[ftype].indexFilename).c_str(), &stat_buf); + fseek(dirfile,0,SEEK_END); + dirfilesize = ftell(dirfile); + + // if (!sessid_ok) { + + // Read dir hash from file + // fseek(dirfile, (stat_buf.st_size >> 5) << 5,SEEK_SET); + fseek(dirfile, (dirfilesize >> 5) << 5,SEEK_SET); + + char fhash[32]; + fgets(fhash, sizeof(fhash), dirfile); + // printf("File Hash: %s\n",fhash); + + // Count dir items and calc hash + DIR *dir; + struct dirent* de; + + std::vector filexts; + size_t pos = 0; + string ss = FileUtils::fileTypes[ftype].fileExts; + while ((pos = ss.find(",")) != std::string::npos) { + filexts.push_back(ss.substr(0, pos)); + ss.erase(0, pos + 1); + } + filexts.push_back(ss.substr(0)); + + unsigned long hash = 0, high; // Name checksum variables + + string fdir = filedir.substr(0,filedir.length() - 1); + if ((dir = opendir(fdir.c_str())) != nullptr) { + + elements = 0; + ndirs = 0; + while ((de = readdir(dir)) != nullptr) { + string fname = de->d_name; + if (de->d_type == DT_REG || de->d_type == DT_DIR) { + if (fname.compare(0,1,".") != 0) { + // printf("Fname: %s Fname size: %d\n",fname.c_str(),fname.size()); + if ((de->d_type == DT_DIR) || ((fname.size() > 3) && (std::find(filexts.begin(),filexts.end(),fname.substr(fname.size()-4)) != filexts.end()))) { + // Calculate name checksum + for (int i = 0; i < fname.length(); i++) { + hash = (hash << 4) + fname[i]; + if (high = hash & 0xF0000000) hash ^= high >> 24; + hash &= ~high; + } + if (de->d_type == DT_REG) + elements++; // Count elements in dir + else if (de->d_type == DT_DIR) + ndirs++; } - if (de->d_type == DT_REG) - elements++; // Count elements in dir - else if (de->d_type == DT_DIR) - ndirs++; } } } - } - // printf("Hashcode : %lu\n",hash); - - closedir(dir); + // printf("Hashcode : %lu\n",hash); + + closedir(dir); - } else { + } else { - printf("Error opening %s\n",filedir.c_str()); - return ""; + printf("Error opening %s\n",filedir.c_str()); + return ""; - } + } - filexts.clear(); // Clear vector - std::vector().swap(filexts); // free memory + filexts.clear(); // Clear vector + std::vector().swap(filexts); // free memory - // If calc hash and file hash are different refresh dir index - if (stoul(fhash) != hash) { - fclose(dirfile); - reIndex = true; - } + // If calc hash and file hash are different refresh dir index + if (stoul(fhash) != hash) { + fclose(dirfile); + reIndex = true; + } + + // } } @@ -661,10 +690,9 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols // There was no index or hashes are different: reIndex if (reIndex) { - // OSD::osdCenteredMsg("Please wait: sorting directory", LEVEL_INFO, 0); // TO DO: Move this into DirtoFile function FileUtils::DirToFile(filedir, ftype); // Prepare filelist - stat((filedir + FileUtils::fileTypes[ftype].indexFilename).c_str(), &stat_buf); + // stat((filedir + FileUtils::fileTypes[ftype].indexFilename).c_str(), &stat_buf); dirfile = fopen((filedir + FileUtils::fileTypes[ftype].indexFilename).c_str(), "r"); if (dirfile == NULL) { @@ -672,12 +700,16 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols return ""; } + fseek(dirfile,0,SEEK_END); + dirfilesize = ftell(dirfile); + // Reset position FileUtils::fileTypes[ftype].begin_row = FileUtils::fileTypes[ftype].focus = 2; } - real_rows = (stat_buf.st_size / 64) + 2; // Add 2 for title and status bar + // real_rows = (stat_buf.st_size / 64) + 2; // Add 2 for title and status bar + real_rows = (dirfilesize / 64) + 2; // Add 2 for title and status bar virtual_rows = (real_rows > mf_rows ? mf_rows : real_rows); // printf("Real rows: %d; st_size: %d; Virtual rows: %d\n",real_rows,stat_buf.st_size,virtual_rows); @@ -693,6 +725,10 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols fd_Redraw(title, fdir, ftype); // Draw content + // Focus line scroll position + fdScrollPos = 0; + timeStartScroll = 0; + timeScroll = 0; fabgl::VirtualKeyItem Menukey; while (1) { @@ -703,6 +739,10 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols // Process external keyboard if (ESPectrum::PS2Controller.keyboard()->virtualKeyAvailable()) { + timeStartScroll = 0; + timeScroll = 0; + fdScrollPos = 0; + // Print elements VIDEO::vga.setTextColor(OSD::zxColor(7, 1), OSD::zxColor(5, 0)); if (elements) { @@ -718,6 +758,7 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols } if (ESPectrum::readKbd(&Menukey)) { + if (!Menukey.down) continue; // Search first ocurrence of letter if we're not on that letter yet @@ -902,13 +943,21 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols } } - } /*else { + } else { - // TO DO: COUNT TIME TO SIGNAL START OF FOCUSED LINE SCROLL + if (timeStartScroll < 200) timeStartScroll++; - }*/ + } // TO DO: SCROLL FOCUSED LINE IF SIGNALED + if (timeStartScroll == 200) { + timeScroll++; + if (timeScroll == 50) { + fdScrollPos++; + fd_PrintRow(FileUtils::fileTypes[ftype].focus, IS_FOCUSED); + timeScroll = 0; + } + } vTaskDelay(5 / portTICK_PERIOD_MS); @@ -1065,6 +1114,10 @@ void OSD::fd_PrintRow(uint8_t virtual_row_num, uint8_t line_type) { string line = rowGet(menu, virtual_row_num); + bool isDir = (line[0] == ASCII_SPC); + + trim(line); + switch (line_type) { case IS_TITLE: VIDEO::vga.setTextColor(OSD::zxColor(7, 1), OSD::zxColor(0, 0)); @@ -1087,14 +1140,25 @@ void OSD::fd_PrintRow(uint8_t virtual_row_num, uint8_t line_type) { VIDEO::vga.print(" "); - if (line[0] == ASCII_SPC) { + if (isDir) { + // Directory - ltrim(line); - if (line.length() < cols - margin) + if (line.length() <= cols - margin - 6) line = line + std::string(cols - margin - line.length(), ' '); + else + if (line_type == IS_FOCUSED) { + line = line.substr(fdScrollPos); + if (line.length() <= cols - margin - 6) { + fdScrollPos = -1; + timeStartScroll = 0; + } + } + line = line.substr(0,cols - margin - 6) + " "; + } else { - if (line.length() < cols - margin) { + + if (line.length() <= cols - margin) { line = line + std::string(cols - margin - line.length(), ' '); line = line.substr(0, cols - margin); } else { @@ -1102,8 +1166,16 @@ void OSD::fd_PrintRow(uint8_t virtual_row_num, uint8_t line_type) { // printf("%s %d\n",line.c_str(),line.length() - (cols - margin)); line = ".." + line.substr(line.length() - (cols - margin) + 2); // printf("%s\n",line.c_str()); - } else + } else { + if (line_type == IS_FOCUSED) { + line = line.substr(fdScrollPos); + if (line.length() <= cols - margin) { + fdScrollPos = -1; + timeStartScroll = 0; + } + } line = line.substr(0, cols - margin); + } } } From 7c26ddcb2233e410c6b9234cb7b57310c1f613b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Iborra?= Date: Sat, 9 Dec 2023 05:02:36 +0100 Subject: [PATCH 21/30] OSD functions adjustments --- include/OSDMain.h | 4 +- include/Video.h | 2 + src/OSDMain.cpp | 203 ++++++++++++++++++++++++++++++++++++---------- src/OSDMenu.cpp | 191 ++++++++----------------------------------- 4 files changed, 198 insertions(+), 202 deletions(-) diff --git a/include/OSDMain.h b/include/OSDMain.h index afdfd33e..d73fcad0 100644 --- a/include/OSDMain.h +++ b/include/OSDMain.h @@ -59,7 +59,7 @@ class OSD { public: // ZX Color - static uint16_t zxColor(uint8_t color, uint8_t bright); + // static uint16_t zxColor(uint8_t color, uint8_t bright); // Screen size to be set at initialization static unsigned short scrW; @@ -89,7 +89,7 @@ class OSD // Menu static unsigned short menuRealRowFor(uint8_t virtual_row_num); - static bool menuIsSub(uint8_t virtual_row_num); + // static bool menuIsSub(uint8_t virtual_row_num); static void menuPrintRow(uint8_t virtual_row_num, uint8_t line_type); static void menuRedraw(); static void WindowDraw(); diff --git a/include/Video.h b/include/Video.h index 6b5e4397..4495f01b 100644 --- a/include/Video.h +++ b/include/Video.h @@ -181,6 +181,8 @@ static uint16_t spectrum_colors[NUM_SPECTRUM_COLORS] = { BRI_BLACK, BRI_BLUE, BRI_RED, BRI_MAGENTA, BRI_GREEN, BRI_CYAN, BRI_YELLOW, BRI_WHITE, }; +uint16_t zxColor(uint8_t color, uint8_t bright); + static uint32_t* AluBytes[16]; // static unsigned char DrawStatus; diff --git a/src/OSDMain.cpp b/src/OSDMain.cpp index ca68c85b..98604998 100644 --- a/src/OSDMain.cpp +++ b/src/OSDMain.cpp @@ -100,6 +100,36 @@ uint8_t OSD::osdMaxCols() { return (OSD_W - (OSD_MARGIN * 2)) / OSD_FONT_W; } unsigned short OSD::osdInsideX() { return scrAlignCenterX(OSD_W) + OSD_MARGIN; } unsigned short OSD::osdInsideY() { return scrAlignCenterY(OSD_H) + OSD_MARGIN; } +DRAM_ATTR static const uint8_t click48[12]={ 0,0x16,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x16,0 }; + +DRAM_ATTR static const uint8_t click128[116]= { 0x00,0x16,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61, + 0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61, + 0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61, + 0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61, + 0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61, + 0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61, + 0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61, + 0x61,0x61,0x16,0x00 + }; + +IRAM_ATTR void OSD::click() { + + size_t written; + + pwm_audio_set_volume(ESP_DEFAULT_VOLUME); + + if (Z80Ops::is48) { + pwm_audio_write((uint8_t *) click48, 12, &written, 5 / portTICK_PERIOD_MS); + } else { + pwm_audio_write((uint8_t *) click128, 116, &written, 5 / portTICK_PERIOD_MS); + } + + pwm_audio_set_volume(ESPectrum::aud_volume); + + // printf("Written: %d\n",written); + +} + void OSD::esp_hard_reset() { // RESTART ESP32 (This is the most similar way to hard resetting it) #ifndef ESP32_SDL2_WRAPPER @@ -129,10 +159,10 @@ void OSD::osdAt(uint8_t row, uint8_t col) { void OSD::drawOSD(bool bottom_info) { unsigned short x = scrAlignCenterX(OSD_W); unsigned short y = scrAlignCenterY(OSD_H); - VIDEO::vga.fillRect(x, y, OSD_W, OSD_H, OSD::zxColor(1, 0)); - VIDEO::vga.rect(x, y, OSD_W, OSD_H, OSD::zxColor(0, 0)); - VIDEO::vga.rect(x + 1, y + 1, OSD_W - 2, OSD_H - 2, OSD::zxColor(7, 0)); - VIDEO::vga.setTextColor(OSD::zxColor(0, 0), OSD::zxColor(5, 1)); + VIDEO::vga.fillRect(x, y, OSD_W, OSD_H, zxColor(1, 0)); + VIDEO::vga.rect(x, y, OSD_W, OSD_H, zxColor(0, 0)); + VIDEO::vga.rect(x + 1, y + 1, OSD_W - 2, OSD_H - 2, zxColor(7, 0)); + VIDEO::vga.setTextColor(zxColor(0, 0), zxColor(5, 1)); VIDEO::vga.setFont(Font6x8); osdHome(); VIDEO::vga.print(OSD_TITLE); @@ -161,7 +191,7 @@ void OSD::drawStats() { y = 220; } - VIDEO::vga.setTextColor(OSD::zxColor(7, 0), OSD::zxColor(1, 0)); + VIDEO::vga.setTextColor(zxColor(7, 0), zxColor(1, 0)); VIDEO::vga.setFont(Font6x8); VIDEO::vga.setCursor(x,y); VIDEO::vga.print(stats_lin1); @@ -1149,7 +1179,7 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL) { // Help drawOSD(true); osdAt(2, 0); - VIDEO::vga.setTextColor(OSD::zxColor(7, 0), OSD::zxColor(1, 0)); + VIDEO::vga.setTextColor(zxColor(7, 0), zxColor(1, 0)); if (ZXKeyb::Exists) VIDEO::vga.print(Config::lang ? OSD_HELP_ES_ZX : OSD_HELP_EN_ZX); else @@ -1182,7 +1212,7 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL) { // About drawOSD(false); - VIDEO::vga.fillRect(Config::aspect_16_9 ? 60 : 40,Config::aspect_16_9 ? 12 : 32,240,50,OSD::zxColor(0, 0)); + VIDEO::vga.fillRect(Config::aspect_16_9 ? 60 : 40,Config::aspect_16_9 ? 12 : 32,240,50,zxColor(0, 0)); // Decode Logo in EBF8 format uint8_t *logo = (uint8_t *)ESPectrum_logo; @@ -1197,7 +1227,7 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL) { // About Page 1 // osdAt(7, 0); - VIDEO::vga.setTextColor(OSD::zxColor(7, 0), OSD::zxColor(1, 0)); + VIDEO::vga.setTextColor(zxColor(7, 0), zxColor(1, 0)); // VIDEO::vga.print(Config::lang ? OSD_ABOUT1_ES : OSD_ABOUT1_EN); pos_x = Config::aspect_16_9 ? 66 : 46; @@ -1243,7 +1273,7 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL) { } else { msgDelay--; if (msgDelay==0) { - VIDEO::vga.fillRect(Config::aspect_16_9 ? 60 : 40,Config::aspect_16_9 ? 64 : 84,240,114,OSD::zxColor(1, 0)); + VIDEO::vga.fillRect(Config::aspect_16_9 ? 60 : 40,Config::aspect_16_9 ? 64 : 84,240,114,zxColor(1, 0)); osdCol = 0; osdRow = 0; msgChar = 0; @@ -1297,18 +1327,18 @@ void OSD::errorPanel(string errormsg) { if (Config::slog_on) printf((errormsg + "\n").c_str()); - VIDEO::vga.fillRect(x, y, OSD_W, OSD_H, OSD::zxColor(0, 0)); - VIDEO::vga.rect(x, y, OSD_W, OSD_H, OSD::zxColor(7, 0)); - VIDEO::vga.rect(x + 1, y + 1, OSD_W - 2, OSD_H - 2, OSD::zxColor(2, 1)); + VIDEO::vga.fillRect(x, y, OSD_W, OSD_H, zxColor(0, 0)); + VIDEO::vga.rect(x, y, OSD_W, OSD_H, zxColor(7, 0)); + VIDEO::vga.rect(x + 1, y + 1, OSD_W - 2, OSD_H - 2, zxColor(2, 1)); VIDEO::vga.setFont(Font6x8); osdHome(); - VIDEO::vga.setTextColor(OSD::zxColor(7, 1), OSD::zxColor(2, 1)); + VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(2, 1)); VIDEO::vga.print(ERROR_TITLE); osdAt(2, 0); - VIDEO::vga.setTextColor(OSD::zxColor(7, 1), OSD::zxColor(0, 0)); + VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(0, 0)); VIDEO::vga.println(errormsg.c_str()); osdAt(17, 0); - VIDEO::vga.setTextColor(OSD::zxColor(7, 1), OSD::zxColor(2, 1)); + VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(2, 1)); VIDEO::vga.print(ERROR_BOTTOM); } @@ -1340,20 +1370,20 @@ void OSD::osdCenteredMsg(string msg, uint8_t warn_level, uint16_t millispause) { switch (warn_level) { case LEVEL_OK: - ink = OSD::zxColor(7, 1); - paper = OSD::zxColor(4, 0); + ink = zxColor(7, 1); + paper = zxColor(4, 0); break; case LEVEL_ERROR: - ink = OSD::zxColor(7, 1); - paper = OSD::zxColor(2, 0); + ink = zxColor(7, 1); + paper = zxColor(2, 0); break; case LEVEL_WARN: - ink = OSD::zxColor(0, 0); - paper = OSD::zxColor(6, 0); + ink = zxColor(0, 0); + paper = zxColor(6, 0); break; default: - ink = OSD::zxColor(7, 0); - paper = OSD::zxColor(1, 0); + ink = zxColor(7, 0); + paper = zxColor(1, 0); } if (millispause > 0) { @@ -1431,7 +1461,7 @@ void OSD::HWInfo() { drawOSD(true); osdAt(2, 0); - VIDEO::vga.setTextColor(OSD::zxColor(7, 0), OSD::zxColor(1, 0)); + VIDEO::vga.setTextColor(zxColor(7, 0), zxColor(1, 0)); // Get chip information esp_chip_info_t chip_info; @@ -1688,18 +1718,18 @@ void OSD::progressDialog(string title, string msg, int percent, int action) { VIDEO::vga.setFont(Font6x8); // Menu border - VIDEO::vga.rect(x, y, w, h, OSD::zxColor(0, 0)); + VIDEO::vga.rect(x, y, w, h, zxColor(0, 0)); VIDEO::vga.fillRect(x + 1, y + 1, w - 2, OSD_FONT_H, zxColor(0,0)); VIDEO::vga.fillRect(x + 1, y + 1 + OSD_FONT_H, w - 2, h - OSD_FONT_H - 2, zxColor(7,1)); // Title - VIDEO::vga.setTextColor(OSD::zxColor(7, 1), OSD::zxColor(0, 0)); + VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(0, 0)); VIDEO::vga.setCursor(x + OSD_FONT_W + 1, y + 1); VIDEO::vga.print(title.c_str()); // Msg - VIDEO::vga.setTextColor(OSD::zxColor(0, 0), OSD::zxColor(7, 1)); + VIDEO::vga.setTextColor(zxColor(0, 0), zxColor(7, 1)); VIDEO::vga.setCursor(scrAlignCenterX(msg.length() * OSD_FONT_W), y + 1 + (OSD_FONT_H * 2)); VIDEO::vga.print(msg.c_str()); @@ -1709,7 +1739,7 @@ void OSD::progressDialog(string title, string msg, int percent, int action) { uint8_t rb_colors[] = {2, 6, 4, 5}; for (uint8_t c = 0; c < 4; c++) { for (uint8_t i = 0; i < 5; i++) { - VIDEO::vga.line(rb_paint_x + i, rb_y, rb_paint_x + 8 + i, rb_y - 8, OSD::zxColor(rb_colors[c], 1)); + VIDEO::vga.line(rb_paint_x + i, rb_y, rb_paint_x + 8 + i, rb_y - 8, zxColor(rb_colors[c], 1)); } rb_paint_x += 5; } @@ -1717,14 +1747,14 @@ void OSD::progressDialog(string title, string msg, int percent, int action) { // Progress bar frame progress_x = scrAlignCenterX(72); progress_y = y + (OSD_FONT_H * 4); - VIDEO::vga.rect(progress_x, progress_y, 72, OSD_FONT_H + 2, OSD::zxColor(0, 0)); + VIDEO::vga.rect(progress_x, progress_y, 72, OSD_FONT_H + 2, zxColor(0, 0)); progress_x++; progress_y++; } else if (action == 1 ) { // UPDATE // Msg - VIDEO::vga.setTextColor(OSD::zxColor(0, 0), OSD::zxColor(7, 1)); + VIDEO::vga.setTextColor(zxColor(0, 0), zxColor(7, 1)); VIDEO::vga.setCursor(scrAlignCenterX(msg.length() * OSD_FONT_W), y + 1 + (OSD_FONT_H * 2)); VIDEO::vga.print(msg.c_str()); @@ -1776,33 +1806,33 @@ uint8_t OSD::msgDialog(string title, string msg) { VIDEO::vga.setFont(Font6x8); // Menu border - VIDEO::vga.rect(x, y, w, h, OSD::zxColor(0, 0)); + VIDEO::vga.rect(x, y, w, h, zxColor(0, 0)); VIDEO::vga.fillRect(x + 1, y + 1, w - 2, OSD_FONT_H, zxColor(0,0)); VIDEO::vga.fillRect(x + 1, y + 1 + OSD_FONT_H, w - 2, h - OSD_FONT_H - 2, zxColor(7,1)); // Title - VIDEO::vga.setTextColor(OSD::zxColor(7, 1), OSD::zxColor(0, 0)); + VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(0, 0)); VIDEO::vga.setCursor(x + OSD_FONT_W + 1, y + 1); VIDEO::vga.print(title.c_str()); // Msg - VIDEO::vga.setTextColor(OSD::zxColor(0, 0), OSD::zxColor(7, 1)); + VIDEO::vga.setTextColor(zxColor(0, 0), zxColor(7, 1)); VIDEO::vga.setCursor(scrAlignCenterX(msg.length() * OSD_FONT_W), y + 1 + (OSD_FONT_H * 2)); VIDEO::vga.print(msg.c_str()); // Yes - VIDEO::vga.setTextColor(OSD::zxColor(0, 0), OSD::zxColor(7, 1)); + VIDEO::vga.setTextColor(zxColor(0, 0), zxColor(7, 1)); VIDEO::vga.setCursor(scrAlignCenterX(6 * OSD_FONT_W) - (w >> 2), y + 1 + (OSD_FONT_H * 4)); VIDEO::vga.print(Config::lang ? " Si " : " Yes "); // // Ruler - // VIDEO::vga.setTextColor(OSD::zxColor(0, 0), OSD::zxColor(7, 1)); + // VIDEO::vga.setTextColor(zxColor(0, 0), zxColor(7, 1)); // VIDEO::vga.setCursor(x + 1, y + 1 + (OSD_FONT_H * 3)); // VIDEO::vga.print("123456789012345678901234567"); // No - VIDEO::vga.setTextColor(OSD::zxColor(0, 1), OSD::zxColor(5, 1)); + VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(5, 1)); VIDEO::vga.setCursor(scrAlignCenterX(6 * OSD_FONT_W) + (w >> 2), y + 1 + (OSD_FONT_H * 4)); VIDEO::vga.print(" No "); @@ -1812,7 +1842,7 @@ uint8_t OSD::msgDialog(string title, string msg) { uint8_t rb_colors[] = {2, 6, 4, 5}; for (uint8_t c = 0; c < 4; c++) { for (uint8_t i = 0; i < 5; i++) { - VIDEO::vga.line(rb_paint_x + i, rb_y, rb_paint_x + 8 + i, rb_y - 8, OSD::zxColor(rb_colors[c], 1)); + VIDEO::vga.line(rb_paint_x + i, rb_y, rb_paint_x + 8 + i, rb_y - 8, zxColor(rb_colors[c], 1)); } rb_paint_x += 5; } @@ -1839,22 +1869,22 @@ uint8_t OSD::msgDialog(string title, string msg) { if (Menukey.vk == fabgl::VK_LEFT) { // Yes - VIDEO::vga.setTextColor(OSD::zxColor(0, 1), OSD::zxColor(5, 1)); + VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(5, 1)); VIDEO::vga.setCursor(scrAlignCenterX(6 * OSD_FONT_W) - (w >> 2), y + 1 + (OSD_FONT_H * 4)); VIDEO::vga.print(Config::lang ? " Si " : " Yes "); // No - VIDEO::vga.setTextColor(OSD::zxColor(0, 0), OSD::zxColor(7, 1)); + VIDEO::vga.setTextColor(zxColor(0, 0), zxColor(7, 1)); VIDEO::vga.setCursor(scrAlignCenterX(6 * OSD_FONT_W) + (w >> 2), y + 1 + (OSD_FONT_H * 4)); VIDEO::vga.print(" No "); click(); res = DLG_YES; } else if (Menukey.vk == fabgl::VK_RIGHT) { // Yes - VIDEO::vga.setTextColor(OSD::zxColor(0, 0), OSD::zxColor(7, 1)); + VIDEO::vga.setTextColor(zxColor(0, 0), zxColor(7, 1)); VIDEO::vga.setCursor(scrAlignCenterX(6 * OSD_FONT_W) - (w >> 2), y + 1 + (OSD_FONT_H * 4)); VIDEO::vga.print(Config::lang ? " Si " : " Yes "); // No - VIDEO::vga.setTextColor(OSD::zxColor(0, 1), OSD::zxColor(5, 1)); + VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(5, 1)); VIDEO::vga.setCursor(scrAlignCenterX(6 * OSD_FONT_W) + (w >> 2), y + 1 + (OSD_FONT_H * 4)); VIDEO::vga.print(" No "); click(); @@ -1893,4 +1923,93 @@ string OSD::inputBox(int x, int y, string text) { return text; -} \ No newline at end of file +} + +void OSD::ZXKbdRead() { + + #define REPDEL 140 // As in real ZX Spectrum (700 ms.) if this function is called every 5 ms. + #define REPPER 20 // As in real ZX Spectrum (100 ms.) if this function is called every 5 ms. + + static int zxDel = REPDEL; + static int lastzxK = fabgl::VK_NONE; + + ZXKeyb::process(); + + fabgl::VirtualKey injectKey = fabgl::VK_NONE; + + if (bitRead(ZXKeyb::ZXcols[7], 1)) { // Not Symbol Shift pressed ? + + if (!bitRead(ZXKeyb::ZXcols[4], 3)) injectKey = fabgl::VK_UP; // 7 -> UP + else if (!bitRead(ZXKeyb::ZXcols[4], 4)) injectKey = fabgl::VK_DOWN; // 6 -> DOWN + else if (!bitRead(ZXKeyb::ZXcols[6], 0)) injectKey = fabgl::VK_RETURN; // ENTER + else if ((!bitRead(ZXKeyb::ZXcols[0], 0)) && (!bitRead(ZXKeyb::ZXcols[4], 0))) injectKey = fabgl::VK_BACKSPACE; // CS + 0 -> BACKSPACE + else if (!bitRead(ZXKeyb::ZXcols[4], 0)) injectKey = fabgl::VK_SPACE; // 0 -> SPACE + else if ((!bitRead(ZXKeyb::ZXcols[7], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 1))) injectKey = fabgl::VK_ESCAPE; // BREAK -> ESCAPE + else if (!bitRead(ZXKeyb::ZXcols[3], 4)) injectKey = fabgl::VK_LEFT; // 5 -> PGUP + else if (!bitRead(ZXKeyb::ZXcols[4], 2)) injectKey = fabgl::VK_RIGHT; // 8 -> PGDOWN + else if (!bitRead(ZXKeyb::ZXcols[1], 1)) injectKey = fabgl::VK_PRINTSCREEN; // S -> PRINTSCREEN + else if (!bitRead(ZXKeyb::ZXcols[5], 0)) injectKey = fabgl::VK_PAUSE; // P -> PAUSE + + } else { + + if (!bitRead(ZXKeyb::ZXcols[0], 1)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_Z : fabgl::VK_z; + else if (!bitRead(ZXKeyb::ZXcols[0], 2)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_X : fabgl::VK_x; + else if (!bitRead(ZXKeyb::ZXcols[0], 3)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_C : fabgl::VK_c; + else if (!bitRead(ZXKeyb::ZXcols[0], 4)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_V : fabgl::VK_v; + + else if (!bitRead(ZXKeyb::ZXcols[1], 0)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_A : fabgl::VK_a; + else if (!bitRead(ZXKeyb::ZXcols[1], 1)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_S : fabgl::VK_s; + else if (!bitRead(ZXKeyb::ZXcols[1], 2)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_D : fabgl::VK_d; + else if (!bitRead(ZXKeyb::ZXcols[1], 3)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_F : fabgl::VK_f; + else if (!bitRead(ZXKeyb::ZXcols[1], 4)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_G : fabgl::VK_g; + + else if (!bitRead(ZXKeyb::ZXcols[2], 0)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_Q : fabgl::VK_q; + else if (!bitRead(ZXKeyb::ZXcols[2], 1)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_W : fabgl::VK_w; + else if (!bitRead(ZXKeyb::ZXcols[2], 2)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_E : fabgl::VK_e; + else if (!bitRead(ZXKeyb::ZXcols[2], 3)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_R : fabgl::VK_r; + else if (!bitRead(ZXKeyb::ZXcols[2], 4)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_T : fabgl::VK_t; + + else if (!bitRead(ZXKeyb::ZXcols[5], 0)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_P : fabgl::VK_p; + else if (!bitRead(ZXKeyb::ZXcols[5], 1)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_O : fabgl::VK_o; + else if (!bitRead(ZXKeyb::ZXcols[5], 2)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_I : fabgl::VK_i; + else if (!bitRead(ZXKeyb::ZXcols[5], 3)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_U : fabgl::VK_u; + else if (!bitRead(ZXKeyb::ZXcols[5], 4)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_Y : fabgl::VK_y; + + else if (!bitRead(ZXKeyb::ZXcols[6], 1)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_L : fabgl::VK_l; + else if (!bitRead(ZXKeyb::ZXcols[6], 2)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_K : fabgl::VK_k; + else if (!bitRead(ZXKeyb::ZXcols[6], 3)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_J : fabgl::VK_j; + else if (!bitRead(ZXKeyb::ZXcols[6], 4)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_H : fabgl::VK_h; + + else if (!bitRead(ZXKeyb::ZXcols[7], 2)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_M : fabgl::VK_m; + else if (!bitRead(ZXKeyb::ZXcols[7], 3)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_N : fabgl::VK_n; + else if (!bitRead(ZXKeyb::ZXcols[7], 4)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_B : fabgl::VK_b; + + else if (!bitRead(ZXKeyb::ZXcols[3], 0)) injectKey = fabgl::VK_1; + else if (!bitRead(ZXKeyb::ZXcols[3], 1)) injectKey = fabgl::VK_2; + else if (!bitRead(ZXKeyb::ZXcols[3], 2)) injectKey = fabgl::VK_3; + else if (!bitRead(ZXKeyb::ZXcols[3], 3)) injectKey = fabgl::VK_4; + else if (!bitRead(ZXKeyb::ZXcols[3], 4)) injectKey = fabgl::VK_5; + + else if (!bitRead(ZXKeyb::ZXcols[4], 0)) injectKey = fabgl::VK_0; + else if (!bitRead(ZXKeyb::ZXcols[4], 1)) injectKey = fabgl::VK_9; + else if (!bitRead(ZXKeyb::ZXcols[4], 2)) injectKey = fabgl::VK_8; + else if (!bitRead(ZXKeyb::ZXcols[4], 3)) injectKey = fabgl::VK_7; + else if (!bitRead(ZXKeyb::ZXcols[4], 4)) injectKey = fabgl::VK_6; + + } + + if (injectKey != fabgl::VK_NONE) { + if (zxDel == 0) { + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(injectKey, true, false); + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(injectKey, false, false); + zxDel = lastzxK == injectKey ? REPPER : REPDEL; + lastzxK = injectKey; + } + } else { + zxDel = 0; + lastzxK = fabgl::VK_NONE; + } + + if (zxDel > 0) zxDel--; + +} diff --git a/src/OSDMenu.cpp b/src/OSDMenu.cpp index e082448e..474aceb2 100644 --- a/src/OSDMenu.cpp +++ b/src/OSDMenu.cpp @@ -84,51 +84,16 @@ static uint8_t focus = 1; // Focused virtual row static uint8_t last_focus = 0; // To check for changes static unsigned short last_begin_row = 0; // To check for changes -DRAM_ATTR static const uint8_t click48[12]={ 0,0x16,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x16,0 }; - -DRAM_ATTR static const uint8_t click128[116]= { 0x00,0x16,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61, - 0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61, - 0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61, - 0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61, - 0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61, - 0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61, - 0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61, - 0x61,0x61,0x16,0x00 - }; - -IRAM_ATTR void OSD::click() { - - size_t written; - - pwm_audio_set_volume(ESP_DEFAULT_VOLUME); - - if (Z80Ops::is48) { - pwm_audio_write((uint8_t *) click48, 12, &written, 5 / portTICK_PERIOD_MS); - } else { - pwm_audio_write((uint8_t *) click128, 116, &written, 5 / portTICK_PERIOD_MS); - } - - pwm_audio_set_volume(ESPectrum::aud_volume); - - // printf("Written: %d\n",written); - -} - -uint16_t OSD::zxColor(uint8_t color, uint8_t bright) { - if (bright) color += 8; - return spectrum_colors[color]; -} - // Get real row number for a virtual one unsigned short OSD::menuRealRowFor(uint8_t virtual_row_num) { return begin_row + virtual_row_num - 1; } -// Get real row number for a virtual one -bool OSD::menuIsSub(uint8_t virtual_row_num) { - string line = rowGet(menu, menuRealRowFor(virtual_row_num)); - int n = line.find(ASCII_TAB); - if (n == line.npos) return false; - return (line.substr(n+1).find(">") != line.npos); -} +// // Get real row number for a virtual one +// bool OSD::menuIsSub(uint8_t virtual_row_num) { +// string line = rowGet(menu, menuRealRowFor(virtual_row_num)); +// int n = line.find(ASCII_TAB); +// if (n == line.npos) return false; +// return (line.substr(n+1).find(">") != line.npos); +// } // Menu relative AT void OSD::menuAt(short int row, short int col) { @@ -148,15 +113,15 @@ void OSD::menuPrintRow(uint8_t virtual_row_num, uint8_t line_type) { switch (line_type) { case IS_TITLE: - VIDEO::vga.setTextColor(OSD::zxColor(7, 1), OSD::zxColor(0, 0)); + VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(0, 0)); margin = 2; break; case IS_FOCUSED: - VIDEO::vga.setTextColor(OSD::zxColor(0, 1), OSD::zxColor(5, 1)); + VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(5, 1)); margin = (real_rows > virtual_rows ? 3 : 2); break; default: - VIDEO::vga.setTextColor(OSD::zxColor(0, 1), OSD::zxColor(7, 1)); + VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(7, 1)); margin = (real_rows > virtual_rows ? 3 : 2); } @@ -169,9 +134,9 @@ void OSD::menuPrintRow(uint8_t virtual_row_num, uint8_t line_type) { VIDEO::vga.print(" "); if (line.substr(0,9) == "ESPectrum") { - VIDEO::vga.setTextColor(ESP_ORANGE, OSD::zxColor(0, 0)); + VIDEO::vga.setTextColor(ESP_ORANGE, zxColor(0, 0)); VIDEO::vga.print("ESP"); - VIDEO::vga.setTextColor(OSD::zxColor(7, 1), OSD::zxColor(0, 0)); + VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(0, 0)); VIDEO::vga.print(("ectrum " + Config::getArch()).c_str()); for (uint8_t i = line.length(); i < (cols - margin); i++) VIDEO::vga.print(" "); @@ -215,7 +180,7 @@ void OSD::WindowDraw() { } // Menu border - VIDEO::vga.rect(x, y, w, h, OSD::zxColor(0, 0)); + VIDEO::vga.rect(x, y, w, h, zxColor(0, 0)); // Title PrintRow(0, IS_TITLE); @@ -226,7 +191,7 @@ void OSD::WindowDraw() { uint8_t rb_colors[] = {2, 6, 4, 5}; for (uint8_t c = 0; c < 4; c++) { for (uint8_t i = 0; i < 5; i++) { - VIDEO::vga.line(rb_paint_x + i, rb_y, rb_paint_x + 8 + i, rb_y - 8, OSD::zxColor(rb_colors[c], 1)); + VIDEO::vga.line(rb_paint_x + i, rb_y, rb_paint_x + 8 + i, rb_y - 8, zxColor(rb_colors[c], 1)); } rb_paint_x += 5; } @@ -440,10 +405,10 @@ void OSD::menuScrollBar(unsigned short br) { // Top handle menuAt(1, -1); if (br > 1) { - VIDEO::vga.setTextColor(OSD::zxColor(7, 0), OSD::zxColor(0, 0)); + VIDEO::vga.setTextColor(zxColor(7, 0), zxColor(0, 0)); VIDEO::vga.print("+"); } else { - VIDEO::vga.setTextColor(OSD::zxColor(7, 0), OSD::zxColor(0, 0)); + VIDEO::vga.setTextColor(zxColor(7, 0), zxColor(0, 0)); VIDEO::vga.print("-"); } @@ -452,7 +417,7 @@ void OSD::menuScrollBar(unsigned short br) { unsigned short holder_y = y + (OSD_FONT_H * 2); unsigned short holder_h = OSD_FONT_H * (virtual_rows - 3); unsigned short holder_w = OSD_FONT_W; - VIDEO::vga.fillRect(holder_x, holder_y, holder_w, holder_h + 1, OSD::zxColor(7, 0)); + VIDEO::vga.fillRect(holder_x, holder_y, holder_w, holder_h + 1, zxColor(7, 0)); holder_y++; // Scroll bar @@ -467,15 +432,15 @@ void OSD::menuScrollBar(unsigned short br) { if (bar_h == 0) bar_h = 1; - VIDEO::vga.fillRect(holder_x + 1, holder_y + bar_y, holder_w - 2, bar_h, OSD::zxColor(0, 0)); + VIDEO::vga.fillRect(holder_x + 1, holder_y + bar_y, holder_w - 2, bar_h, zxColor(0, 0)); // Bottom handle menuAt(-1, -1); if ((br + virtual_rows - 1) < real_rows) { - VIDEO::vga.setTextColor(OSD::zxColor(7, 0), OSD::zxColor(0, 0)); + VIDEO::vga.setTextColor(zxColor(7, 0), zxColor(0, 0)); VIDEO::vga.print("+"); } else { - VIDEO::vga.setTextColor(OSD::zxColor(7, 0), OSD::zxColor(0, 0)); + VIDEO::vga.setTextColor(zxColor(7, 0), zxColor(0, 0)); VIDEO::vga.print("-"); } } @@ -563,14 +528,14 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols // Draw blank rows uint8_t row = 2; for (; row < mf_rows; row++) { - VIDEO::vga.setTextColor(OSD::zxColor(0, 1), OSD::zxColor(7, 1)); + VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(7, 1)); menuAt(row, 0); VIDEO::vga.print(std::string(cols, ' ').c_str()); } // Print status bar menuAt(row, 0); - VIDEO::vga.setTextColor(OSD::zxColor(7, 1), OSD::zxColor(5, 0)); + VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(5, 0)); VIDEO::vga.print(std::string(cols, ' ').c_str()); // char fsessid[32]; @@ -744,7 +709,7 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols fdScrollPos = 0; // Print elements - VIDEO::vga.setTextColor(OSD::zxColor(7, 1), OSD::zxColor(5, 0)); + VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(5, 0)); if (elements) { menuAt(mfrows + (Config::aspect_16_9 ? 0 : 1), cols - (real_rows > virtual_rows ? 13 : 12)); char elements_txt[13]; @@ -967,96 +932,6 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols } - -void OSD::ZXKbdRead() { - - #define REPDEL 140 // As in real ZX Spectrum (700 ms.) if this function is called every 5 ms. - #define REPPER 20 // As in real ZX Spectrum (100 ms.) if this function is called every 5 ms. - - static int zxDel = REPDEL; - static int lastzxK = fabgl::VK_NONE; - - ZXKeyb::process(); - - fabgl::VirtualKey injectKey = fabgl::VK_NONE; - - if (bitRead(ZXKeyb::ZXcols[7], 1)) { // Not Symbol Shift pressed ? - - if (!bitRead(ZXKeyb::ZXcols[4], 3)) injectKey = fabgl::VK_UP; // 7 -> UP - else if (!bitRead(ZXKeyb::ZXcols[4], 4)) injectKey = fabgl::VK_DOWN; // 6 -> DOWN - else if (!bitRead(ZXKeyb::ZXcols[6], 0)) injectKey = fabgl::VK_RETURN; // ENTER - else if ((!bitRead(ZXKeyb::ZXcols[0], 0)) && (!bitRead(ZXKeyb::ZXcols[4], 0))) injectKey = fabgl::VK_BACKSPACE; // CS + 0 -> BACKSPACE - else if (!bitRead(ZXKeyb::ZXcols[4], 0)) injectKey = fabgl::VK_SPACE; // 0 -> SPACE - else if ((!bitRead(ZXKeyb::ZXcols[7], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 1))) injectKey = fabgl::VK_ESCAPE; // BREAK -> ESCAPE - else if (!bitRead(ZXKeyb::ZXcols[3], 4)) injectKey = fabgl::VK_LEFT; // 5 -> PGUP - else if (!bitRead(ZXKeyb::ZXcols[4], 2)) injectKey = fabgl::VK_RIGHT; // 8 -> PGDOWN - else if (!bitRead(ZXKeyb::ZXcols[1], 1)) injectKey = fabgl::VK_PRINTSCREEN; // S -> PRINTSCREEN - else if (!bitRead(ZXKeyb::ZXcols[5], 0)) injectKey = fabgl::VK_PAUSE; // P -> PAUSE - - } else { - - if (!bitRead(ZXKeyb::ZXcols[0], 1)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_Z : fabgl::VK_z; - else if (!bitRead(ZXKeyb::ZXcols[0], 2)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_X : fabgl::VK_x; - else if (!bitRead(ZXKeyb::ZXcols[0], 3)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_C : fabgl::VK_c; - else if (!bitRead(ZXKeyb::ZXcols[0], 4)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_V : fabgl::VK_v; - - else if (!bitRead(ZXKeyb::ZXcols[1], 0)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_A : fabgl::VK_a; - else if (!bitRead(ZXKeyb::ZXcols[1], 1)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_S : fabgl::VK_s; - else if (!bitRead(ZXKeyb::ZXcols[1], 2)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_D : fabgl::VK_d; - else if (!bitRead(ZXKeyb::ZXcols[1], 3)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_F : fabgl::VK_f; - else if (!bitRead(ZXKeyb::ZXcols[1], 4)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_G : fabgl::VK_g; - - else if (!bitRead(ZXKeyb::ZXcols[2], 0)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_Q : fabgl::VK_q; - else if (!bitRead(ZXKeyb::ZXcols[2], 1)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_W : fabgl::VK_w; - else if (!bitRead(ZXKeyb::ZXcols[2], 2)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_E : fabgl::VK_e; - else if (!bitRead(ZXKeyb::ZXcols[2], 3)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_R : fabgl::VK_r; - else if (!bitRead(ZXKeyb::ZXcols[2], 4)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_T : fabgl::VK_t; - - else if (!bitRead(ZXKeyb::ZXcols[5], 0)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_P : fabgl::VK_p; - else if (!bitRead(ZXKeyb::ZXcols[5], 1)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_O : fabgl::VK_o; - else if (!bitRead(ZXKeyb::ZXcols[5], 2)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_I : fabgl::VK_i; - else if (!bitRead(ZXKeyb::ZXcols[5], 3)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_U : fabgl::VK_u; - else if (!bitRead(ZXKeyb::ZXcols[5], 4)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_Y : fabgl::VK_y; - - else if (!bitRead(ZXKeyb::ZXcols[6], 1)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_L : fabgl::VK_l; - else if (!bitRead(ZXKeyb::ZXcols[6], 2)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_K : fabgl::VK_k; - else if (!bitRead(ZXKeyb::ZXcols[6], 3)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_J : fabgl::VK_j; - else if (!bitRead(ZXKeyb::ZXcols[6], 4)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_H : fabgl::VK_h; - - else if (!bitRead(ZXKeyb::ZXcols[7], 2)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_M : fabgl::VK_m; - else if (!bitRead(ZXKeyb::ZXcols[7], 3)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_N : fabgl::VK_n; - else if (!bitRead(ZXKeyb::ZXcols[7], 4)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_B : fabgl::VK_b; - - else if (!bitRead(ZXKeyb::ZXcols[3], 0)) injectKey = fabgl::VK_1; - else if (!bitRead(ZXKeyb::ZXcols[3], 1)) injectKey = fabgl::VK_2; - else if (!bitRead(ZXKeyb::ZXcols[3], 2)) injectKey = fabgl::VK_3; - else if (!bitRead(ZXKeyb::ZXcols[3], 3)) injectKey = fabgl::VK_4; - else if (!bitRead(ZXKeyb::ZXcols[3], 4)) injectKey = fabgl::VK_5; - - else if (!bitRead(ZXKeyb::ZXcols[4], 0)) injectKey = fabgl::VK_0; - else if (!bitRead(ZXKeyb::ZXcols[4], 1)) injectKey = fabgl::VK_9; - else if (!bitRead(ZXKeyb::ZXcols[4], 2)) injectKey = fabgl::VK_8; - else if (!bitRead(ZXKeyb::ZXcols[4], 3)) injectKey = fabgl::VK_7; - else if (!bitRead(ZXKeyb::ZXcols[4], 4)) injectKey = fabgl::VK_6; - - } - - if (injectKey != fabgl::VK_NONE) { - if (zxDel == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(injectKey, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(injectKey, false, false); - zxDel = lastzxK == injectKey ? REPPER : REPDEL; - lastzxK = injectKey; - } - } else { - zxDel = 0; - lastzxK = fabgl::VK_NONE; - } - - if (zxDel > 0) zxDel--; - -} - // Print a virtual row void OSD::PrintRow(uint8_t virtual_row_num, uint8_t line_type) { @@ -1066,15 +941,15 @@ void OSD::PrintRow(uint8_t virtual_row_num, uint8_t line_type) { switch (line_type) { case IS_TITLE: - VIDEO::vga.setTextColor(OSD::zxColor(7, 1), OSD::zxColor(0, 0)); + VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(0, 0)); margin = 2; break; case IS_FOCUSED: - VIDEO::vga.setTextColor(OSD::zxColor(0, 1), OSD::zxColor(5, 1)); + VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(5, 1)); margin = (real_rows > virtual_rows ? 3 : 2); break; default: - VIDEO::vga.setTextColor(OSD::zxColor(0, 1), OSD::zxColor(7, 1)); + VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(7, 1)); margin = (real_rows > virtual_rows ? 3 : 2); } @@ -1087,9 +962,9 @@ void OSD::PrintRow(uint8_t virtual_row_num, uint8_t line_type) { VIDEO::vga.print(" "); if ((virtual_row_num == 0) && (line.substr(0,9) == "ESPectrum")) { - VIDEO::vga.setTextColor(ESP_ORANGE, OSD::zxColor(0, 0)); + VIDEO::vga.setTextColor(ESP_ORANGE, zxColor(0, 0)); VIDEO::vga.print("ESP"); - VIDEO::vga.setTextColor(OSD::zxColor(7, 1), OSD::zxColor(0, 0)); + VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(0, 0)); VIDEO::vga.print(("ectrum " + Config::getArch()).c_str()); for (uint8_t i = line.length(); i < (cols - margin); i++) VIDEO::vga.print(" "); @@ -1120,19 +995,19 @@ void OSD::fd_PrintRow(uint8_t virtual_row_num, uint8_t line_type) { switch (line_type) { case IS_TITLE: - VIDEO::vga.setTextColor(OSD::zxColor(7, 1), OSD::zxColor(0, 0)); + VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(0, 0)); margin = 2; break; case IS_INFO: - VIDEO::vga.setTextColor(OSD::zxColor(7, 1), OSD::zxColor(5, 0)); + VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(5, 0)); margin = (real_rows > virtual_rows ? 3 : 2); break; case IS_FOCUSED: - VIDEO::vga.setTextColor(OSD::zxColor(0, 1), OSD::zxColor(5, 1)); + VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(5, 1)); margin = (real_rows > virtual_rows ? 3 : 2); break; default: - VIDEO::vga.setTextColor(OSD::zxColor(0, 1), OSD::zxColor(7, 1)); + VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(7, 1)); margin = (real_rows > virtual_rows ? 3 : 2); } @@ -1218,7 +1093,7 @@ void OSD::fd_Redraw(string title, string fdir, uint8_t ftype) { menuScrollBar(FileUtils::fileTypes[ftype].begin_row); } else { for (; row < mf_rows; row++) { - VIDEO::vga.setTextColor(OSD::zxColor(0, 1), OSD::zxColor(7, 1)); + VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(7, 1)); menuAt(row, 0); VIDEO::vga.print(std::string(cols, ' ').c_str()); } From c944f3d2e1faf4b58914324be7cb1bc658e8b124 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Iborra?= Date: Sun, 10 Dec 2023 04:17:03 +0100 Subject: [PATCH 22/30] More video optimizations, refactor OSD files --- include/OSDMain.h | 69 ++++- include/Video.h | 5 +- include/ZXKeyb.h | 1 + src/OSDFile.cpp | 629 +++++++++++++++++++++++++++++++++++++++++++++ src/OSDMain.cpp | 115 ++------- src/OSDMenu.cpp | 637 +--------------------------------------------- src/Video.cpp | 100 ++++---- src/Z80_JLS.cpp | 16 +- src/ZXKeyb.cpp | 93 ++++++- 9 files changed, 870 insertions(+), 795 deletions(-) create mode 100644 src/OSDFile.cpp diff --git a/include/OSDMain.h b/include/OSDMain.h index d73fcad0..aeaf7ac0 100644 --- a/include/OSDMain.h +++ b/include/OSDMain.h @@ -38,10 +38,18 @@ visit https://zxespectrum.speccy.org/contacto #include "fabgl.h" #include +#include using namespace std; // Defines + +// Line type +#define IS_TITLE 0 +#define IS_FOCUSED 1 +#define IS_NORMAL 2 +#define IS_INFO 3 + #define OSD_FONT_W 6 #define OSD_FONT_H 8 @@ -55,11 +63,11 @@ using namespace std; #define DLG_NO 2 // OSD Interface -class OSD -{ +class OSD { + public: // ZX Color - // static uint16_t zxColor(uint8_t color, uint8_t bright); + static uint16_t zxColor(uint8_t color, uint8_t bright); // Screen size to be set at initialization static unsigned short scrW; @@ -104,7 +112,6 @@ class OSD static void menuAt(short int row, short int col); static void menuScrollBar(unsigned short br); static void click(); - static void ZXKbdRead(); static uint8_t menu_level; static bool menu_saverect; static unsigned short menu_curopt; @@ -131,6 +138,60 @@ class OSD static char stats_lin1[25]; // "CPU: 00000 / IDL: 00000 "; static char stats_lin2[25]; // "FPS:000.00 / FND:000.00 "; + static uint8_t cols; // Maximum columns + static uint8_t mf_rows; // File menu maximum rows + static unsigned short real_rows; // Real row count + static uint8_t virtual_rows; // Virtual maximum rows on screen + static uint16_t w; // Width in pixels + static uint16_t h; // Height in pixels + static uint16_t x; // X vertical position + static uint16_t y; // Y horizontal position + static uint16_t prev_y[5]; // Y prev. position + static unsigned short menu_prevopt; + static string menu; // Menu string + static unsigned short begin_row; // First real displayed row + static uint8_t focus; // Focused virtual row + static uint8_t last_focus; // To check for changes + static unsigned short last_begin_row; // To check for changes + }; +// trim from start (in place) +static inline void ltrim(std::string &s) { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { + return !std::isspace(ch); + })); +} + +// trim from end (in place) +static inline void rtrim(std::string &s) { + s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { + return !std::isspace(ch); + }).base(), s.end()); +} + +// trim from both ends (in place) +static inline void trim(std::string &s) { + rtrim(s); + ltrim(s); +} + +// trim from start (copying) +static inline std::string ltrim_copy(std::string s) { + ltrim(s); + return s; +} + +// trim from end (copying) +static inline std::string rtrim_copy(std::string s) { + rtrim(s); + return s; +} + +// trim from both ends (copying) +static inline std::string trim_copy(std::string s) { + trim(s); + return s; +} + #endif // ESPECTRUM_OSD_H diff --git a/include/Video.h b/include/Video.h index 4495f01b..6d95eb20 100644 --- a/include/Video.h +++ b/include/Video.h @@ -77,8 +77,7 @@ class VIDEO // static void NoDraw(unsigned int statestoadd, bool contended); static void EndFrame(); static void Blank(unsigned int statestoadd, bool contended); - // static void Flush(); // For flushing video buffer as fast as possible after HALT - + // 48 / 128 static void TopBorder_Blank(unsigned int statestoadd, bool contended); static void TopBorder(unsigned int statestoadd, bool contended); @@ -181,7 +180,7 @@ static uint16_t spectrum_colors[NUM_SPECTRUM_COLORS] = { BRI_BLACK, BRI_BLUE, BRI_RED, BRI_MAGENTA, BRI_GREEN, BRI_CYAN, BRI_YELLOW, BRI_WHITE, }; -uint16_t zxColor(uint8_t color, uint8_t bright); +// uint16_t zxColor(uint8_t color, uint8_t bright); static uint32_t* AluBytes[16]; diff --git a/include/ZXKeyb.h b/include/ZXKeyb.h index 474c98c6..6b883012 100644 --- a/include/ZXKeyb.h +++ b/include/ZXKeyb.h @@ -44,6 +44,7 @@ class ZXKeyb { static void setup(); // setup pins for physical keyboard static void process(); // process physical keyboard + static void ZXKbdRead(); static uint8_t ZXcols[8]; static bool Exists; diff --git a/src/OSDFile.cpp b/src/OSDFile.cpp new file mode 100644 index 00000000..972e45fc --- /dev/null +++ b/src/OSDFile.cpp @@ -0,0 +1,629 @@ +/* + +ESPectrum, a Sinclair ZX Spectrum emulator for Espressif ESP32 SoC + +Copyright (c) 2023 Víctor Iborra [Eremus] and David Crespo [dcrespo3d] +https://github.com/EremusOne/ZX-ESPectrum-IDF + +Based on ZX-ESPectrum-Wiimote +Copyright (c) 2020, 2022 David Crespo [dcrespo3d] +https://github.com/dcrespo3d/ZX-ESPectrum-Wiimote + +Based on previous work by Ramón Martinez and Jorge Fuertes +https://github.com/rampa069/ZX-ESPectrum + +Original project by Pete Todd +https://github.com/retrogubbins/paseVGA + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +To Contact the dev team you can write to zxespectrum@gmail.com or +visit https://zxespectrum.speccy.org/contacto + +*/ + +#include +#include +#include +#include "errno.h" + +#include "esp_vfs.h" +#include "esp_vfs_fat.h" + +using namespace std; + +#include "OSDMain.h" +#include "FileUtils.h" +#include "Config.h" +#include "ESPectrum.h" +#include "CPU.h" +#include "Video.h" +#include "messages.h" +#include +#include "ZXKeyb.h" +#include "pwm_audio.h" +#include "Z80_JLS/z80.h" +#include "Tape.h" + +FILE *dirfile; +unsigned int OSD::elements; +unsigned int OSD::ndirs; +int8_t OSD::fdScrollPos; +int OSD::timeStartScroll; +int OSD::timeScroll; + +// Run a new file menu +string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols, uint8_t mfrows) { + + // struct stat stat_buf; + long dirfilesize; + bool reIndex; + + // Position + if (menu_level == 0) { + x = (Config::aspect_16_9 ? 24 : 4); + y = (Config::aspect_16_9 ? 4 : 4); + } else { + x = (Config::aspect_16_9 ? 24 : 8) + (60 * menu_level); + y = 8 + (16 * menu_level); + } + + // Columns and Rows + cols = mfcols; + mf_rows = mfrows + (Config::aspect_16_9 ? 0 : 1); + + // printf("Focus: %d, Begin_row: %d, mf_rows: %d\n",(int) FileUtils::fileTypes[ftype].focus,(int) FileUtils::fileTypes[ftype].begin_row,(int) mf_rows); + if (FileUtils::fileTypes[ftype].focus > mf_rows - 1) { + FileUtils::fileTypes[ftype].begin_row += FileUtils::fileTypes[ftype].focus - (mf_rows - 1); + FileUtils::fileTypes[ftype].focus = mf_rows - 1; + } + + // Size + w = (cols * OSD_FONT_W) + 2; + h = ((mf_rows + 1) * OSD_FONT_H) + 2; + + // menu = title + "\n" + fdir + "\n"; + menu = title + "\n" + ( fdir.length() == 1 ? fdir : fdir.substr(0,fdir.length()-1)) + "\n"; + WindowDraw(); // Draw menu outline + fd_PrintRow(1, IS_INFO); // Path + + // Draw blank rows + uint8_t row = 2; + for (; row < mf_rows; row++) { + VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(7, 1)); + menuAt(row, 0); + VIDEO::vga.print(std::string(cols, ' ').c_str()); + } + + // Print status bar + menuAt(row, 0); + VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(5, 0)); + VIDEO::vga.print(std::string(cols, ' ').c_str()); + + // char fsessid[32]; + while(1) { + + reIndex = false; + string filedir = FileUtils::MountPoint + fdir; + + // // Get / Create sessid + // bool sessid_ok = false; + // if (stat((filedir + ".sessid").c_str(), &stat_buf) == 0) { + // dirfile = fopen((filedir + ".sessid").c_str(), "r"); + // fgets(fsessid, sizeof(fsessid), dirfile); + // printf("FSessId: %s Sessid: %u\n",fsessid,ESPectrum::sessid); + // if (stoul(fsessid) == ESPectrum::sessid) sessid_ok = true; + // } + + // if (!sessid_ok) { + // dirfile = fopen((filedir + ".sessid").c_str(), "w"); + // fputs(to_string(ESPectrum::sessid).c_str(),dirfile); + // } + + // fclose(dirfile); + + // Open dir file for read + dirfile = fopen((filedir + FileUtils::fileTypes[ftype].indexFilename).c_str(), "r"); + if (dirfile == NULL) { + + // printf("No dir file found: reindexing\n"); + reIndex = true; + + } else { + + // stat((filedir + FileUtils::fileTypes[ftype].indexFilename).c_str(), &stat_buf); + fseek(dirfile,0,SEEK_END); + dirfilesize = ftell(dirfile); + + // if (!sessid_ok) { + + // Read dir hash from file + // fseek(dirfile, (stat_buf.st_size >> 5) << 5,SEEK_SET); + fseek(dirfile, (dirfilesize >> 5) << 5,SEEK_SET); + + char fhash[32]; + fgets(fhash, sizeof(fhash), dirfile); + // printf("File Hash: %s\n",fhash); + + // Count dir items and calc hash + DIR *dir; + struct dirent* de; + + std::vector filexts; + size_t pos = 0; + string ss = FileUtils::fileTypes[ftype].fileExts; + while ((pos = ss.find(",")) != std::string::npos) { + filexts.push_back(ss.substr(0, pos)); + ss.erase(0, pos + 1); + } + filexts.push_back(ss.substr(0)); + + unsigned long hash = 0, high; // Name checksum variables + + string fdir = filedir.substr(0,filedir.length() - 1); + if ((dir = opendir(fdir.c_str())) != nullptr) { + + elements = 0; + ndirs = 0; + while ((de = readdir(dir)) != nullptr) { + string fname = de->d_name; + if (de->d_type == DT_REG || de->d_type == DT_DIR) { + if (fname.compare(0,1,".") != 0) { + // printf("Fname: %s Fname size: %d\n",fname.c_str(),fname.size()); + if ((de->d_type == DT_DIR) || ((fname.size() > 3) && (std::find(filexts.begin(),filexts.end(),fname.substr(fname.size()-4)) != filexts.end()))) { + // Calculate name checksum + for (int i = 0; i < fname.length(); i++) { + hash = (hash << 4) + fname[i]; + if (high = hash & 0xF0000000) hash ^= high >> 24; + hash &= ~high; + } + if (de->d_type == DT_REG) + elements++; // Count elements in dir + else if (de->d_type == DT_DIR) + ndirs++; + } + } + } + } + + // printf("Hashcode : %lu\n",hash); + + closedir(dir); + + } else { + + printf("Error opening %s\n",filedir.c_str()); + return ""; + + } + + filexts.clear(); // Clear vector + std::vector().swap(filexts); // free memory + + // If calc hash and file hash are different refresh dir index + if (stoul(fhash) != hash) { + fclose(dirfile); + reIndex = true; + } + + // } + + } + + // // Force reindex (for testing) + // fclose(dirfile); + // reIndex = true; + + // There was no index or hashes are different: reIndex + if (reIndex) { + + FileUtils::DirToFile(filedir, ftype); // Prepare filelist + + // stat((filedir + FileUtils::fileTypes[ftype].indexFilename).c_str(), &stat_buf); + + dirfile = fopen((filedir + FileUtils::fileTypes[ftype].indexFilename).c_str(), "r"); + if (dirfile == NULL) { + printf("Error opening index file\n"); + return ""; + } + + fseek(dirfile,0,SEEK_END); + dirfilesize = ftell(dirfile); + + // Reset position + FileUtils::fileTypes[ftype].begin_row = FileUtils::fileTypes[ftype].focus = 2; + + } + + // real_rows = (stat_buf.st_size / 64) + 2; // Add 2 for title and status bar + real_rows = (dirfilesize / 64) + 2; // Add 2 for title and status bar + virtual_rows = (real_rows > mf_rows ? mf_rows : real_rows); + + // printf("Real rows: %d; st_size: %d; Virtual rows: %d\n",real_rows,stat_buf.st_size,virtual_rows); + + last_begin_row = last_focus = 0; + + // printf("Focus: %d, Begin_row: %d, real_rows: %d, mf_rows: %d\n",(int) FileUtils::fileTypes[ftype].focus,(int) FileUtils::fileTypes[ftype].begin_row,(int) real_rows, (int) mf_rows); + if ((real_rows > mf_rows) && ((FileUtils::fileTypes[ftype].begin_row + mf_rows - 2) > real_rows)) { + FileUtils::fileTypes[ftype].focus += (FileUtils::fileTypes[ftype].begin_row + mf_rows - 2) - real_rows; + FileUtils::fileTypes[ftype].begin_row = real_rows - (mf_rows - 2); + // printf("Focus: %d, BeginRow: %d\n",FileUtils::fileTypes[ftype].focus,FileUtils::fileTypes[ftype].begin_row); + } + + fd_Redraw(title, fdir, ftype); // Draw content + + // Focus line scroll position + fdScrollPos = 0; + timeStartScroll = 0; + timeScroll = 0; + fabgl::VirtualKeyItem Menukey; + while (1) { + + if (ZXKeyb::Exists) ZXKeyb::ZXKbdRead(); + + ESPectrum::readKbdJoy(); + + // Process external keyboard + if (ESPectrum::PS2Controller.keyboard()->virtualKeyAvailable()) { + + timeStartScroll = 0; + timeScroll = 0; + fdScrollPos = 0; + + // Print elements + VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(5, 0)); + if (elements) { + menuAt(mfrows + (Config::aspect_16_9 ? 0 : 1), cols - (real_rows > virtual_rows ? 13 : 12)); + char elements_txt[13]; + int nitem = (FileUtils::fileTypes[ftype].begin_row + FileUtils::fileTypes[ftype].focus ) - (4 + ndirs) + (fdir.length() == 1); + snprintf(elements_txt, sizeof(elements_txt), "%d/%d ", nitem > 0 ? nitem : 0 , elements); + VIDEO::vga.print(std::string(12 - strlen(elements_txt), ' ').c_str()); + VIDEO::vga.print(elements_txt); + } else { + menuAt(mfrows + (Config::aspect_16_9 ? 0 : 1), cols - 13); + VIDEO::vga.print(" "); + } + + if (ESPectrum::readKbd(&Menukey)) { + + if (!Menukey.down) continue; + + // Search first ocurrence of letter if we're not on that letter yet + if (((Menukey.vk >= fabgl::VK_a) && (Menukey.vk <= fabgl::VK_Z)) || ((Menukey.vk >= fabgl::VK_0) && (Menukey.vk <= fabgl::VK_9))) { + int fsearch; + if (Menukey.vk<=fabgl::VK_9) + fsearch = Menukey.vk + 46; + else if (Menukey.vk<=fabgl::VK_z) + fsearch = Menukey.vk + 75; + else if (Menukey.vk<=fabgl::VK_Z) + fsearch = Menukey.vk + 17; + uint8_t letra = rowGet(menu,FileUtils::fileTypes[ftype].focus).at(0); + // printf("%d %d\n",(int)letra,fsearch); + if (letra != fsearch) { + // Seek first ocurrence of letter/number + long prevpos = ftell(dirfile); + char buf[128]; + int cnt = 0; + fseek(dirfile,0,SEEK_SET); + while(!feof(dirfile)) { + fgets(buf, sizeof(buf), dirfile); + // printf("%c %d\n",buf[0],int(buf[0])); + if (buf[0] == char(fsearch)) break; + cnt++; + } + // printf("Cnt: %d Letra: %d\n",cnt,int(letra)); + if (!feof(dirfile)) { + last_begin_row = FileUtils::fileTypes[ftype].begin_row; + last_focus = FileUtils::fileTypes[ftype].focus; + if (real_rows > virtual_rows) { + int m = cnt + virtual_rows - real_rows; + if (m > 0) { + FileUtils::fileTypes[ftype].focus = m + 2; + FileUtils::fileTypes[ftype].begin_row = cnt - m + 2; + } else { + FileUtils::fileTypes[ftype].focus = 2; + FileUtils::fileTypes[ftype].begin_row = cnt + 2; + } + } else { + FileUtils::fileTypes[ftype].focus = cnt + 2; + FileUtils::fileTypes[ftype].begin_row = 2; + } + // printf("Real rows: %d; Virtual rows: %d\n",real_rows,virtual_rows); + // printf("Focus: %d, Begin_row: %d\n",(int) FileUtils::fileTypes[ftype].focus,(int) FileUtils::fileTypes[ftype].begin_row); + fd_Redraw(title,fdir,ftype); + } else + fseek(dirfile,prevpos,SEEK_SET); + } + } else if (Menukey.vk == fabgl::VK_UP) { + if (FileUtils::fileTypes[ftype].focus == 2 && FileUtils::fileTypes[ftype].begin_row > 2) { + last_begin_row = FileUtils::fileTypes[ftype].begin_row; + FileUtils::fileTypes[ftype].begin_row--; + fd_Redraw(title, fdir, ftype); + } else if (FileUtils::fileTypes[ftype].focus > 2) { + last_focus = FileUtils::fileTypes[ftype].focus; + fd_PrintRow(FileUtils::fileTypes[ftype].focus--, IS_NORMAL); + fd_PrintRow(FileUtils::fileTypes[ftype].focus, IS_FOCUSED); + // printf("Focus: %d, Lastfocus: %d\n",FileUtils::fileTypes[ftype].focus,(int) last_focus); + } + click(); + } else if (Menukey.vk == fabgl::VK_DOWN) { + if (FileUtils::fileTypes[ftype].focus == virtual_rows - 1 && FileUtils::fileTypes[ftype].begin_row + virtual_rows - 2 < real_rows) { + last_begin_row = FileUtils::fileTypes[ftype].begin_row; + FileUtils::fileTypes[ftype].begin_row++; + fd_Redraw(title, fdir, ftype); + } else if (FileUtils::fileTypes[ftype].focus < virtual_rows - 1) { + last_focus = FileUtils::fileTypes[ftype].focus; + fd_PrintRow(FileUtils::fileTypes[ftype].focus++, IS_NORMAL); + fd_PrintRow(FileUtils::fileTypes[ftype].focus, IS_FOCUSED); + // printf("Focus: %d, Lastfocus: %d\n",FileUtils::fileTypes[ftype].focus,(int) last_focus); + } + click(); + } else if ((Menukey.vk == fabgl::VK_PAGEUP) || (Menukey.vk == fabgl::VK_LEFT)) { + if (FileUtils::fileTypes[ftype].begin_row > virtual_rows) { + FileUtils::fileTypes[ftype].focus = 2; + FileUtils::fileTypes[ftype].begin_row -= virtual_rows - 2; + } else { + FileUtils::fileTypes[ftype].focus = 2; + FileUtils::fileTypes[ftype].begin_row = 2; + } + fd_Redraw(title, fdir, ftype); + click(); + } else if ((Menukey.vk == fabgl::VK_PAGEDOWN) || (Menukey.vk == fabgl::VK_RIGHT)) { + if (real_rows - FileUtils::fileTypes[ftype].begin_row - virtual_rows > virtual_rows) { + FileUtils::fileTypes[ftype].focus = 2; + FileUtils::fileTypes[ftype].begin_row += virtual_rows - 2; + } else { + FileUtils::fileTypes[ftype].focus = virtual_rows - 1; + FileUtils::fileTypes[ftype].begin_row = real_rows - virtual_rows + 2; + } + fd_Redraw(title, fdir, ftype); + click(); + } else if (Menukey.vk == fabgl::VK_HOME) { + last_focus = FileUtils::fileTypes[ftype].focus; + last_begin_row = FileUtils::fileTypes[ftype].begin_row; + FileUtils::fileTypes[ftype].focus = 2; + FileUtils::fileTypes[ftype].begin_row = 2; + fd_Redraw(title, fdir, ftype); + click(); + } else if (Menukey.vk == fabgl::VK_END) { + last_focus = FileUtils::fileTypes[ftype].focus; + last_begin_row = FileUtils::fileTypes[ftype].begin_row; + FileUtils::fileTypes[ftype].focus = virtual_rows - 1; + FileUtils::fileTypes[ftype].begin_row = real_rows - virtual_rows + 2; + // printf("Focus: %d, Lastfocus: %d\n",FileUtils::fileTypes[ftype].focus,(int) last_focus); + fd_Redraw(title, fdir, ftype); + click(); + } else if (Menukey.vk == fabgl::VK_BACKSPACE) { + if (fdir != "/") { + + fclose(dirfile); + dirfile = NULL; + + fdir.pop_back(); + fdir = fdir.substr(0,fdir.find_last_of("/") + 1); + + FileUtils::fileTypes[ftype].begin_row = FileUtils::fileTypes[ftype].focus = 2; + // printf("Fdir: %s\n",fdir.c_str()); + break; + + } + } else if (Menukey.vk == fabgl::VK_RETURN || Menukey.vk == fabgl::VK_SPACE) { + + fclose(dirfile); + dirfile = NULL; + + filedir = rowGet(menu,FileUtils::fileTypes[ftype].focus); + // printf("%s\n",filedir.c_str()); + if (filedir[0] == ASCII_SPC) { + if (filedir[1] == ASCII_SPC) { + fdir.pop_back(); + fdir = fdir.substr(0,fdir.find_last_of("/") + 1); + } else { + filedir.erase(0,1); + trim(filedir); + fdir = fdir + filedir + "/"; + } + FileUtils::fileTypes[ftype].begin_row = FileUtils::fileTypes[ftype].focus = 2; + // printf("Fdir: %s\n",fdir.c_str()); + break; + } else { + + if (menu_saverect) { + // Restore backbuffer data + int j = SaveRectpos - (((w >> 2) + 1) * h); + SaveRectpos = j - 4; + for (int m = y; m < y + h; m++) { + uint32_t *backbuffer32 = (uint32_t *)(VIDEO::vga.backBuffer[m]); + for (int n = x >> 2; n < ((x + w) >> 2) + 1; n++) { + backbuffer32[n] = VIDEO::SaveRect[j]; + j++; + } + } + menu_saverect = false; + } + + rtrim(filedir); + click(); + return (Menukey.vk == fabgl::VK_RETURN ? "R" : "S") + filedir; + } + + } else if (Menukey.vk == fabgl::VK_ESCAPE) { + + // Restore backbuffer data + if (menu_saverect) { + int j = SaveRectpos - (((w >> 2) + 1) * h); + SaveRectpos = j - 4; + for (int m = y; m < y + h; m++) { + uint32_t *backbuffer32 = (uint32_t *)(VIDEO::vga.backBuffer[m]); + for (int n = x >> 2; n < ((x + w) >> 2) + 1; n++) { + backbuffer32[n] = VIDEO::SaveRect[j]; + j++; + } + } + menu_saverect = false; + } + + fclose(dirfile); + dirfile = NULL; + click(); + return ""; + } + } + + } else { + + if (timeStartScroll < 200) timeStartScroll++; + + } + + // TO DO: SCROLL FOCUSED LINE IF SIGNALED + if (timeStartScroll == 200) { + timeScroll++; + if (timeScroll == 50) { + fdScrollPos++; + fd_PrintRow(FileUtils::fileTypes[ftype].focus, IS_FOCUSED); + timeScroll = 0; + } + } + + vTaskDelay(5 / portTICK_PERIOD_MS); + + } + + } + +} + +// Redraw inside rows +void OSD::fd_Redraw(string title, string fdir, uint8_t ftype) { + + if ((FileUtils::fileTypes[ftype].focus != last_focus) || (FileUtils::fileTypes[ftype].begin_row != last_begin_row)) { + + // printf("fd_Redraw\n"); + + // Read bunch of rows + fseek(dirfile, (FileUtils::fileTypes[ftype].begin_row - 2) * 64,SEEK_SET); + menu = title + "\n" + ( fdir.length() == 1 ? fdir : fdir.substr(0,fdir.length()-1)) + "\n"; + for (int i = 2; i < virtual_rows; i++) { + char buf[128]; + fgets(buf, sizeof(buf), dirfile); + if (feof(dirfile)) break; + menu += buf; + } + + fd_PrintRow(1, IS_INFO); // Print status bar + + uint8_t row = 2; + for (; row < virtual_rows; row++) { + if (row == FileUtils::fileTypes[ftype].focus) { + fd_PrintRow(row, IS_FOCUSED); + } else { + fd_PrintRow(row, IS_NORMAL); + } + } + + if (real_rows > virtual_rows) { + menuScrollBar(FileUtils::fileTypes[ftype].begin_row); + } else { + for (; row < mf_rows; row++) { + VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(7, 1)); + menuAt(row, 0); + VIDEO::vga.print(std::string(cols, ' ').c_str()); + } + } + + last_focus = FileUtils::fileTypes[ftype].focus; + last_begin_row = FileUtils::fileTypes[ftype].begin_row; + } + +} + +// Print a virtual row +void OSD::fd_PrintRow(uint8_t virtual_row_num, uint8_t line_type) { + + uint8_t margin; + + string line = rowGet(menu, virtual_row_num); + + bool isDir = (line[0] == ASCII_SPC); + + trim(line); + + switch (line_type) { + case IS_TITLE: + VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(0, 0)); + margin = 2; + break; + case IS_INFO: + VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(5, 0)); + margin = (real_rows > virtual_rows ? 3 : 2); + break; + case IS_FOCUSED: + VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(5, 1)); + margin = (real_rows > virtual_rows ? 3 : 2); + break; + default: + VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(7, 1)); + margin = (real_rows > virtual_rows ? 3 : 2); + } + + menuAt(virtual_row_num, 0); + + VIDEO::vga.print(" "); + + if (isDir) { + + // Directory + if (line.length() <= cols - margin - 6) + line = line + std::string(cols - margin - line.length(), ' '); + else + if (line_type == IS_FOCUSED) { + line = line.substr(fdScrollPos); + if (line.length() <= cols - margin - 6) { + fdScrollPos = -1; + timeStartScroll = 0; + } + } + + line = line.substr(0,cols - margin - 6) + " "; + + } else { + + if (line.length() <= cols - margin) { + line = line + std::string(cols - margin - line.length(), ' '); + line = line.substr(0, cols - margin); + } else { + if (line_type == IS_INFO) { + // printf("%s %d\n",line.c_str(),line.length() - (cols - margin)); + line = ".." + line.substr(line.length() - (cols - margin) + 2); + // printf("%s\n",line.c_str()); + } else { + if (line_type == IS_FOCUSED) { + line = line.substr(fdScrollPos); + if (line.length() <= cols - margin) { + fdScrollPos = -1; + timeStartScroll = 0; + } + } + line = line.substr(0, cols - margin); + } + } + + } + + VIDEO::vga.print(line.c_str()); + + VIDEO::vga.print(" "); + +} diff --git a/src/OSDMain.cpp b/src/OSDMain.cpp index 98604998..179adadf 100644 --- a/src/OSDMain.cpp +++ b/src/OSDMain.cpp @@ -78,6 +78,22 @@ using namespace std; extern Font Font6x8; +uint8_t OSD::cols; // Maximum columns +uint8_t OSD::mf_rows; // File menu maximum rows +unsigned short OSD::real_rows; // Real row count +uint8_t OSD::virtual_rows; // Virtual maximum rows on screen +uint16_t OSD::w; // Width in pixels +uint16_t OSD::h; // Height in pixels +uint16_t OSD::x; // X vertical position +uint16_t OSD::y; // Y horizontal position +uint16_t OSD::prev_y[5]; // Y prev. position +unsigned short OSD::menu_prevopt = 1; +string OSD::menu; // Menu string +unsigned short OSD::begin_row = 1; // First real displayed row +uint8_t OSD::focus = 1; // Focused virtual row +uint8_t OSD::last_focus = 0; // To check for changes +unsigned short OSD::last_begin_row = 0; // To check for changes + uint8_t OSD::menu_level = 0; bool OSD::menu_saverect = false; unsigned short OSD::menu_curopt = 1; @@ -329,7 +345,7 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL) { while (1) { - if (ZXKeyb::Exists) ZXKbdRead(); + if (ZXKeyb::Exists) ZXKeyb::ZXKbdRead(); ESPectrum::readKbdJoy(); @@ -1187,7 +1203,7 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL) { while (1) { - if (ZXKeyb::Exists) ZXKbdRead(); + if (ZXKeyb::Exists) ZXKeyb::ZXKbdRead(); ESPectrum::readKbdJoy(); @@ -1291,7 +1307,7 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL) { VIDEO::vga.fillRect(pos_x + ((osdCol + 1) * 6), pos_y + (osdRow * 8), 6,8, cursorCol ); - if (ZXKeyb::Exists) ZXKbdRead(); + if (ZXKeyb::Exists) ZXKeyb::ZXKbdRead(); ESPectrum::readKbdJoy(); @@ -1561,7 +1577,7 @@ void OSD::HWInfo() { // Wait for key while (1) { - if (ZXKeyb::Exists) ZXKbdRead(); + if (ZXKeyb::Exists) ZXKeyb::ZXKbdRead(); ESPectrum::readKbdJoy(); @@ -1858,7 +1874,7 @@ uint8_t OSD::msgDialog(string title, string msg) { fabgl::VirtualKeyItem Menukey; while (1) { - if (ZXKeyb::Exists) OSD::ZXKbdRead(); + if (ZXKeyb::Exists) ZXKeyb::ZXKbdRead(); ESPectrum::readKbdJoy(); @@ -1924,92 +1940,3 @@ string OSD::inputBox(int x, int y, string text) { return text; } - -void OSD::ZXKbdRead() { - - #define REPDEL 140 // As in real ZX Spectrum (700 ms.) if this function is called every 5 ms. - #define REPPER 20 // As in real ZX Spectrum (100 ms.) if this function is called every 5 ms. - - static int zxDel = REPDEL; - static int lastzxK = fabgl::VK_NONE; - - ZXKeyb::process(); - - fabgl::VirtualKey injectKey = fabgl::VK_NONE; - - if (bitRead(ZXKeyb::ZXcols[7], 1)) { // Not Symbol Shift pressed ? - - if (!bitRead(ZXKeyb::ZXcols[4], 3)) injectKey = fabgl::VK_UP; // 7 -> UP - else if (!bitRead(ZXKeyb::ZXcols[4], 4)) injectKey = fabgl::VK_DOWN; // 6 -> DOWN - else if (!bitRead(ZXKeyb::ZXcols[6], 0)) injectKey = fabgl::VK_RETURN; // ENTER - else if ((!bitRead(ZXKeyb::ZXcols[0], 0)) && (!bitRead(ZXKeyb::ZXcols[4], 0))) injectKey = fabgl::VK_BACKSPACE; // CS + 0 -> BACKSPACE - else if (!bitRead(ZXKeyb::ZXcols[4], 0)) injectKey = fabgl::VK_SPACE; // 0 -> SPACE - else if ((!bitRead(ZXKeyb::ZXcols[7], 0)) || (!bitRead(ZXKeyb::ZXcols[4], 1))) injectKey = fabgl::VK_ESCAPE; // BREAK -> ESCAPE - else if (!bitRead(ZXKeyb::ZXcols[3], 4)) injectKey = fabgl::VK_LEFT; // 5 -> PGUP - else if (!bitRead(ZXKeyb::ZXcols[4], 2)) injectKey = fabgl::VK_RIGHT; // 8 -> PGDOWN - else if (!bitRead(ZXKeyb::ZXcols[1], 1)) injectKey = fabgl::VK_PRINTSCREEN; // S -> PRINTSCREEN - else if (!bitRead(ZXKeyb::ZXcols[5], 0)) injectKey = fabgl::VK_PAUSE; // P -> PAUSE - - } else { - - if (!bitRead(ZXKeyb::ZXcols[0], 1)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_Z : fabgl::VK_z; - else if (!bitRead(ZXKeyb::ZXcols[0], 2)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_X : fabgl::VK_x; - else if (!bitRead(ZXKeyb::ZXcols[0], 3)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_C : fabgl::VK_c; - else if (!bitRead(ZXKeyb::ZXcols[0], 4)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_V : fabgl::VK_v; - - else if (!bitRead(ZXKeyb::ZXcols[1], 0)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_A : fabgl::VK_a; - else if (!bitRead(ZXKeyb::ZXcols[1], 1)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_S : fabgl::VK_s; - else if (!bitRead(ZXKeyb::ZXcols[1], 2)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_D : fabgl::VK_d; - else if (!bitRead(ZXKeyb::ZXcols[1], 3)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_F : fabgl::VK_f; - else if (!bitRead(ZXKeyb::ZXcols[1], 4)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_G : fabgl::VK_g; - - else if (!bitRead(ZXKeyb::ZXcols[2], 0)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_Q : fabgl::VK_q; - else if (!bitRead(ZXKeyb::ZXcols[2], 1)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_W : fabgl::VK_w; - else if (!bitRead(ZXKeyb::ZXcols[2], 2)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_E : fabgl::VK_e; - else if (!bitRead(ZXKeyb::ZXcols[2], 3)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_R : fabgl::VK_r; - else if (!bitRead(ZXKeyb::ZXcols[2], 4)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_T : fabgl::VK_t; - - else if (!bitRead(ZXKeyb::ZXcols[5], 0)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_P : fabgl::VK_p; - else if (!bitRead(ZXKeyb::ZXcols[5], 1)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_O : fabgl::VK_o; - else if (!bitRead(ZXKeyb::ZXcols[5], 2)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_I : fabgl::VK_i; - else if (!bitRead(ZXKeyb::ZXcols[5], 3)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_U : fabgl::VK_u; - else if (!bitRead(ZXKeyb::ZXcols[5], 4)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_Y : fabgl::VK_y; - - else if (!bitRead(ZXKeyb::ZXcols[6], 1)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_L : fabgl::VK_l; - else if (!bitRead(ZXKeyb::ZXcols[6], 2)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_K : fabgl::VK_k; - else if (!bitRead(ZXKeyb::ZXcols[6], 3)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_J : fabgl::VK_j; - else if (!bitRead(ZXKeyb::ZXcols[6], 4)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_H : fabgl::VK_h; - - else if (!bitRead(ZXKeyb::ZXcols[7], 2)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_M : fabgl::VK_m; - else if (!bitRead(ZXKeyb::ZXcols[7], 3)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_N : fabgl::VK_n; - else if (!bitRead(ZXKeyb::ZXcols[7], 4)) injectKey = !bitRead(ZXKeyb::ZXcols[0], 0) ? fabgl::VK_B : fabgl::VK_b; - - else if (!bitRead(ZXKeyb::ZXcols[3], 0)) injectKey = fabgl::VK_1; - else if (!bitRead(ZXKeyb::ZXcols[3], 1)) injectKey = fabgl::VK_2; - else if (!bitRead(ZXKeyb::ZXcols[3], 2)) injectKey = fabgl::VK_3; - else if (!bitRead(ZXKeyb::ZXcols[3], 3)) injectKey = fabgl::VK_4; - else if (!bitRead(ZXKeyb::ZXcols[3], 4)) injectKey = fabgl::VK_5; - - else if (!bitRead(ZXKeyb::ZXcols[4], 0)) injectKey = fabgl::VK_0; - else if (!bitRead(ZXKeyb::ZXcols[4], 1)) injectKey = fabgl::VK_9; - else if (!bitRead(ZXKeyb::ZXcols[4], 2)) injectKey = fabgl::VK_8; - else if (!bitRead(ZXKeyb::ZXcols[4], 3)) injectKey = fabgl::VK_7; - else if (!bitRead(ZXKeyb::ZXcols[4], 4)) injectKey = fabgl::VK_6; - - } - - if (injectKey != fabgl::VK_NONE) { - if (zxDel == 0) { - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(injectKey, true, false); - ESPectrum::PS2Controller.keyboard()->injectVirtualKey(injectKey, false, false); - zxDel = lastzxK == injectKey ? REPPER : REPDEL; - lastzxK = injectKey; - } - } else { - zxDel = 0; - lastzxK = fabgl::VK_NONE; - } - - if (zxDel > 0) zxDel--; - -} diff --git a/src/OSDMenu.cpp b/src/OSDMenu.cpp index 474aceb2..123abb30 100644 --- a/src/OSDMenu.cpp +++ b/src/OSDMenu.cpp @@ -57,32 +57,17 @@ using namespace std; #include "Tape.h" #define MENU_MAX_ROWS 18 -// Line type -#define IS_TITLE 0 -#define IS_FOCUSED 1 -#define IS_NORMAL 2 -#define IS_INFO 3 + // Scroll #define UP true #define DOWN false extern Font Font6x8; -static uint8_t cols; // Maximum columns -static uint8_t mf_rows; // File menu maximum rows -static unsigned short real_rows; // Real row count -static uint8_t virtual_rows; // Virtual maximum rows on screen -static uint16_t w; // Width in pixels -static uint16_t h; // Height in pixels -static uint16_t x; // X vertical position -static uint16_t y; // Y horizontal position -static uint16_t prev_y[5]; // Y prev. position -static unsigned short menu_prevopt = 1; -static string menu; // Menu string -static unsigned short begin_row = 1; // First real displayed row -static uint8_t focus = 1; // Focused virtual row -static uint8_t last_focus = 0; // To check for changes -static unsigned short last_begin_row = 0; // To check for changes +uint16_t OSD::zxColor(uint8_t color, uint8_t bright) { + if (bright) color += 8; + return spectrum_colors[color]; +} // Get real row number for a virtual one unsigned short OSD::menuRealRowFor(uint8_t virtual_row_num) { return begin_row + virtual_row_num - 1; } @@ -255,7 +240,7 @@ unsigned short OSD::menuRun(string new_menu) { while (1) { - if (ZXKeyb::Exists) ZXKbdRead(); + if (ZXKeyb::Exists) ZXKeyb::ZXKbdRead(); ESPectrum::readKbdJoy(); @@ -446,492 +431,6 @@ void OSD::menuScrollBar(unsigned short br) { } } -// trim from start (in place) -static inline void ltrim(std::string &s) { - s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { - return !std::isspace(ch); - })); -} -// trim from end (in place) -static inline void rtrim(std::string &s) { - s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { - return !std::isspace(ch); - }).base(), s.end()); -} - -// trim from both ends (in place) -static inline void trim(std::string &s) { - rtrim(s); - ltrim(s); -} - -// // trim from start (copying) -// static inline std::string ltrim_copy(std::string s) { -// ltrim(s); -// return s; -// } - -// // trim from end (copying) -// static inline std::string rtrim_copy(std::string s) { -// rtrim(s); -// return s; -// } - -// // trim from both ends (copying) -// static inline std::string trim_copy(std::string s) { -// trim(s); -// return s; -// } - -FILE *dirfile; -unsigned int OSD::elements; -unsigned int OSD::ndirs; -int8_t OSD::fdScrollPos; -int OSD::timeStartScroll; -int OSD::timeScroll; - -// Run a new file menu -string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols, uint8_t mfrows) { - - // struct stat stat_buf; - long dirfilesize; - bool reIndex; - - // Position - if (menu_level == 0) { - x = (Config::aspect_16_9 ? 24 : 4); - y = (Config::aspect_16_9 ? 4 : 4); - } else { - x = (Config::aspect_16_9 ? 24 : 8) + (60 * menu_level); - y = 8 + (16 * menu_level); - } - - // Columns and Rows - cols = mfcols; - mf_rows = mfrows + (Config::aspect_16_9 ? 0 : 1); - - // printf("Focus: %d, Begin_row: %d, mf_rows: %d\n",(int) FileUtils::fileTypes[ftype].focus,(int) FileUtils::fileTypes[ftype].begin_row,(int) mf_rows); - if (FileUtils::fileTypes[ftype].focus > mf_rows - 1) { - FileUtils::fileTypes[ftype].begin_row += FileUtils::fileTypes[ftype].focus - (mf_rows - 1); - FileUtils::fileTypes[ftype].focus = mf_rows - 1; - } - - // Size - w = (cols * OSD_FONT_W) + 2; - h = ((mf_rows + 1) * OSD_FONT_H) + 2; - - // menu = title + "\n" + fdir + "\n"; - menu = title + "\n" + ( fdir.length() == 1 ? fdir : fdir.substr(0,fdir.length()-1)) + "\n"; - WindowDraw(); // Draw menu outline - fd_PrintRow(1, IS_INFO); // Path - - // Draw blank rows - uint8_t row = 2; - for (; row < mf_rows; row++) { - VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(7, 1)); - menuAt(row, 0); - VIDEO::vga.print(std::string(cols, ' ').c_str()); - } - - // Print status bar - menuAt(row, 0); - VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(5, 0)); - VIDEO::vga.print(std::string(cols, ' ').c_str()); - - // char fsessid[32]; - while(1) { - - reIndex = false; - string filedir = FileUtils::MountPoint + fdir; - - // // Get / Create sessid - // bool sessid_ok = false; - // if (stat((filedir + ".sessid").c_str(), &stat_buf) == 0) { - // dirfile = fopen((filedir + ".sessid").c_str(), "r"); - // fgets(fsessid, sizeof(fsessid), dirfile); - // printf("FSessId: %s Sessid: %u\n",fsessid,ESPectrum::sessid); - // if (stoul(fsessid) == ESPectrum::sessid) sessid_ok = true; - // } - - // if (!sessid_ok) { - // dirfile = fopen((filedir + ".sessid").c_str(), "w"); - // fputs(to_string(ESPectrum::sessid).c_str(),dirfile); - // } - - // fclose(dirfile); - - // Open dir file for read - dirfile = fopen((filedir + FileUtils::fileTypes[ftype].indexFilename).c_str(), "r"); - if (dirfile == NULL) { - - // printf("No dir file found: reindexing\n"); - reIndex = true; - - } else { - - // stat((filedir + FileUtils::fileTypes[ftype].indexFilename).c_str(), &stat_buf); - fseek(dirfile,0,SEEK_END); - dirfilesize = ftell(dirfile); - - // if (!sessid_ok) { - - // Read dir hash from file - // fseek(dirfile, (stat_buf.st_size >> 5) << 5,SEEK_SET); - fseek(dirfile, (dirfilesize >> 5) << 5,SEEK_SET); - - char fhash[32]; - fgets(fhash, sizeof(fhash), dirfile); - // printf("File Hash: %s\n",fhash); - - // Count dir items and calc hash - DIR *dir; - struct dirent* de; - - std::vector filexts; - size_t pos = 0; - string ss = FileUtils::fileTypes[ftype].fileExts; - while ((pos = ss.find(",")) != std::string::npos) { - filexts.push_back(ss.substr(0, pos)); - ss.erase(0, pos + 1); - } - filexts.push_back(ss.substr(0)); - - unsigned long hash = 0, high; // Name checksum variables - - string fdir = filedir.substr(0,filedir.length() - 1); - if ((dir = opendir(fdir.c_str())) != nullptr) { - - elements = 0; - ndirs = 0; - while ((de = readdir(dir)) != nullptr) { - string fname = de->d_name; - if (de->d_type == DT_REG || de->d_type == DT_DIR) { - if (fname.compare(0,1,".") != 0) { - // printf("Fname: %s Fname size: %d\n",fname.c_str(),fname.size()); - if ((de->d_type == DT_DIR) || ((fname.size() > 3) && (std::find(filexts.begin(),filexts.end(),fname.substr(fname.size()-4)) != filexts.end()))) { - // Calculate name checksum - for (int i = 0; i < fname.length(); i++) { - hash = (hash << 4) + fname[i]; - if (high = hash & 0xF0000000) hash ^= high >> 24; - hash &= ~high; - } - if (de->d_type == DT_REG) - elements++; // Count elements in dir - else if (de->d_type == DT_DIR) - ndirs++; - } - } - } - } - - // printf("Hashcode : %lu\n",hash); - - closedir(dir); - - } else { - - printf("Error opening %s\n",filedir.c_str()); - return ""; - - } - - filexts.clear(); // Clear vector - std::vector().swap(filexts); // free memory - - // If calc hash and file hash are different refresh dir index - if (stoul(fhash) != hash) { - fclose(dirfile); - reIndex = true; - } - - // } - - } - - // // Force reindex (for testing) - // fclose(dirfile); - // reIndex = true; - - // There was no index or hashes are different: reIndex - if (reIndex) { - - FileUtils::DirToFile(filedir, ftype); // Prepare filelist - - // stat((filedir + FileUtils::fileTypes[ftype].indexFilename).c_str(), &stat_buf); - - dirfile = fopen((filedir + FileUtils::fileTypes[ftype].indexFilename).c_str(), "r"); - if (dirfile == NULL) { - printf("Error opening index file\n"); - return ""; - } - - fseek(dirfile,0,SEEK_END); - dirfilesize = ftell(dirfile); - - // Reset position - FileUtils::fileTypes[ftype].begin_row = FileUtils::fileTypes[ftype].focus = 2; - - } - - // real_rows = (stat_buf.st_size / 64) + 2; // Add 2 for title and status bar - real_rows = (dirfilesize / 64) + 2; // Add 2 for title and status bar - virtual_rows = (real_rows > mf_rows ? mf_rows : real_rows); - - // printf("Real rows: %d; st_size: %d; Virtual rows: %d\n",real_rows,stat_buf.st_size,virtual_rows); - - last_begin_row = last_focus = 0; - - // printf("Focus: %d, Begin_row: %d, real_rows: %d, mf_rows: %d\n",(int) FileUtils::fileTypes[ftype].focus,(int) FileUtils::fileTypes[ftype].begin_row,(int) real_rows, (int) mf_rows); - if ((real_rows > mf_rows) && ((FileUtils::fileTypes[ftype].begin_row + mf_rows - 2) > real_rows)) { - FileUtils::fileTypes[ftype].focus += (FileUtils::fileTypes[ftype].begin_row + mf_rows - 2) - real_rows; - FileUtils::fileTypes[ftype].begin_row = real_rows - (mf_rows - 2); - // printf("Focus: %d, BeginRow: %d\n",FileUtils::fileTypes[ftype].focus,FileUtils::fileTypes[ftype].begin_row); - } - - fd_Redraw(title, fdir, ftype); // Draw content - - // Focus line scroll position - fdScrollPos = 0; - timeStartScroll = 0; - timeScroll = 0; - fabgl::VirtualKeyItem Menukey; - while (1) { - - if (ZXKeyb::Exists) OSD::ZXKbdRead(); - - ESPectrum::readKbdJoy(); - - // Process external keyboard - if (ESPectrum::PS2Controller.keyboard()->virtualKeyAvailable()) { - - timeStartScroll = 0; - timeScroll = 0; - fdScrollPos = 0; - - // Print elements - VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(5, 0)); - if (elements) { - menuAt(mfrows + (Config::aspect_16_9 ? 0 : 1), cols - (real_rows > virtual_rows ? 13 : 12)); - char elements_txt[13]; - int nitem = (FileUtils::fileTypes[ftype].begin_row + FileUtils::fileTypes[ftype].focus ) - (4 + ndirs) + (fdir.length() == 1); - snprintf(elements_txt, sizeof(elements_txt), "%d/%d ", nitem > 0 ? nitem : 0 , elements); - VIDEO::vga.print(std::string(12 - strlen(elements_txt), ' ').c_str()); - VIDEO::vga.print(elements_txt); - } else { - menuAt(mfrows + (Config::aspect_16_9 ? 0 : 1), cols - 13); - VIDEO::vga.print(" "); - } - - if (ESPectrum::readKbd(&Menukey)) { - - if (!Menukey.down) continue; - - // Search first ocurrence of letter if we're not on that letter yet - if (((Menukey.vk >= fabgl::VK_a) && (Menukey.vk <= fabgl::VK_Z)) || ((Menukey.vk >= fabgl::VK_0) && (Menukey.vk <= fabgl::VK_9))) { - int fsearch; - if (Menukey.vk<=fabgl::VK_9) - fsearch = Menukey.vk + 46; - else if (Menukey.vk<=fabgl::VK_z) - fsearch = Menukey.vk + 75; - else if (Menukey.vk<=fabgl::VK_Z) - fsearch = Menukey.vk + 17; - uint8_t letra = rowGet(menu,FileUtils::fileTypes[ftype].focus).at(0); - // printf("%d %d\n",(int)letra,fsearch); - if (letra != fsearch) { - // Seek first ocurrence of letter/number - long prevpos = ftell(dirfile); - char buf[128]; - int cnt = 0; - fseek(dirfile,0,SEEK_SET); - while(!feof(dirfile)) { - fgets(buf, sizeof(buf), dirfile); - // printf("%c %d\n",buf[0],int(buf[0])); - if (buf[0] == char(fsearch)) break; - cnt++; - } - // printf("Cnt: %d Letra: %d\n",cnt,int(letra)); - if (!feof(dirfile)) { - last_begin_row = FileUtils::fileTypes[ftype].begin_row; - last_focus = FileUtils::fileTypes[ftype].focus; - if (real_rows > virtual_rows) { - int m = cnt + virtual_rows - real_rows; - if (m > 0) { - FileUtils::fileTypes[ftype].focus = m + 2; - FileUtils::fileTypes[ftype].begin_row = cnt - m + 2; - } else { - FileUtils::fileTypes[ftype].focus = 2; - FileUtils::fileTypes[ftype].begin_row = cnt + 2; - } - } else { - FileUtils::fileTypes[ftype].focus = cnt + 2; - FileUtils::fileTypes[ftype].begin_row = 2; - } - // printf("Real rows: %d; Virtual rows: %d\n",real_rows,virtual_rows); - // printf("Focus: %d, Begin_row: %d\n",(int) FileUtils::fileTypes[ftype].focus,(int) FileUtils::fileTypes[ftype].begin_row); - fd_Redraw(title,fdir,ftype); - } else - fseek(dirfile,prevpos,SEEK_SET); - } - } else if (Menukey.vk == fabgl::VK_UP) { - if (FileUtils::fileTypes[ftype].focus == 2 && FileUtils::fileTypes[ftype].begin_row > 2) { - last_begin_row = FileUtils::fileTypes[ftype].begin_row; - FileUtils::fileTypes[ftype].begin_row--; - fd_Redraw(title, fdir, ftype); - } else if (FileUtils::fileTypes[ftype].focus > 2) { - last_focus = FileUtils::fileTypes[ftype].focus; - fd_PrintRow(FileUtils::fileTypes[ftype].focus--, IS_NORMAL); - fd_PrintRow(FileUtils::fileTypes[ftype].focus, IS_FOCUSED); - // printf("Focus: %d, Lastfocus: %d\n",FileUtils::fileTypes[ftype].focus,(int) last_focus); - } - click(); - } else if (Menukey.vk == fabgl::VK_DOWN) { - if (FileUtils::fileTypes[ftype].focus == virtual_rows - 1 && FileUtils::fileTypes[ftype].begin_row + virtual_rows - 2 < real_rows) { - last_begin_row = FileUtils::fileTypes[ftype].begin_row; - FileUtils::fileTypes[ftype].begin_row++; - fd_Redraw(title, fdir, ftype); - } else if (FileUtils::fileTypes[ftype].focus < virtual_rows - 1) { - last_focus = FileUtils::fileTypes[ftype].focus; - fd_PrintRow(FileUtils::fileTypes[ftype].focus++, IS_NORMAL); - fd_PrintRow(FileUtils::fileTypes[ftype].focus, IS_FOCUSED); - // printf("Focus: %d, Lastfocus: %d\n",FileUtils::fileTypes[ftype].focus,(int) last_focus); - } - click(); - } else if ((Menukey.vk == fabgl::VK_PAGEUP) || (Menukey.vk == fabgl::VK_LEFT)) { - if (FileUtils::fileTypes[ftype].begin_row > virtual_rows) { - FileUtils::fileTypes[ftype].focus = 2; - FileUtils::fileTypes[ftype].begin_row -= virtual_rows - 2; - } else { - FileUtils::fileTypes[ftype].focus = 2; - FileUtils::fileTypes[ftype].begin_row = 2; - } - fd_Redraw(title, fdir, ftype); - click(); - } else if ((Menukey.vk == fabgl::VK_PAGEDOWN) || (Menukey.vk == fabgl::VK_RIGHT)) { - if (real_rows - FileUtils::fileTypes[ftype].begin_row - virtual_rows > virtual_rows) { - FileUtils::fileTypes[ftype].focus = 2; - FileUtils::fileTypes[ftype].begin_row += virtual_rows - 2; - } else { - FileUtils::fileTypes[ftype].focus = virtual_rows - 1; - FileUtils::fileTypes[ftype].begin_row = real_rows - virtual_rows + 2; - } - fd_Redraw(title, fdir, ftype); - click(); - } else if (Menukey.vk == fabgl::VK_HOME) { - last_focus = FileUtils::fileTypes[ftype].focus; - last_begin_row = FileUtils::fileTypes[ftype].begin_row; - FileUtils::fileTypes[ftype].focus = 2; - FileUtils::fileTypes[ftype].begin_row = 2; - fd_Redraw(title, fdir, ftype); - click(); - } else if (Menukey.vk == fabgl::VK_END) { - last_focus = FileUtils::fileTypes[ftype].focus; - last_begin_row = FileUtils::fileTypes[ftype].begin_row; - FileUtils::fileTypes[ftype].focus = virtual_rows - 1; - FileUtils::fileTypes[ftype].begin_row = real_rows - virtual_rows + 2; - // printf("Focus: %d, Lastfocus: %d\n",FileUtils::fileTypes[ftype].focus,(int) last_focus); - fd_Redraw(title, fdir, ftype); - click(); - } else if (Menukey.vk == fabgl::VK_BACKSPACE) { - if (fdir != "/") { - - fclose(dirfile); - dirfile = NULL; - - fdir.pop_back(); - fdir = fdir.substr(0,fdir.find_last_of("/") + 1); - - FileUtils::fileTypes[ftype].begin_row = FileUtils::fileTypes[ftype].focus = 2; - // printf("Fdir: %s\n",fdir.c_str()); - break; - - } - } else if (Menukey.vk == fabgl::VK_RETURN || Menukey.vk == fabgl::VK_SPACE) { - - fclose(dirfile); - dirfile = NULL; - - filedir = rowGet(menu,FileUtils::fileTypes[ftype].focus); - // printf("%s\n",filedir.c_str()); - if (filedir[0] == ASCII_SPC) { - if (filedir[1] == ASCII_SPC) { - fdir.pop_back(); - fdir = fdir.substr(0,fdir.find_last_of("/") + 1); - } else { - filedir.erase(0,1); - trim(filedir); - fdir = fdir + filedir + "/"; - } - FileUtils::fileTypes[ftype].begin_row = FileUtils::fileTypes[ftype].focus = 2; - // printf("Fdir: %s\n",fdir.c_str()); - break; - } else { - - if (menu_saverect) { - // Restore backbuffer data - int j = SaveRectpos - (((w >> 2) + 1) * h); - SaveRectpos = j - 4; - for (int m = y; m < y + h; m++) { - uint32_t *backbuffer32 = (uint32_t *)(VIDEO::vga.backBuffer[m]); - for (int n = x >> 2; n < ((x + w) >> 2) + 1; n++) { - backbuffer32[n] = VIDEO::SaveRect[j]; - j++; - } - } - menu_saverect = false; - } - - rtrim(filedir); - click(); - return (Menukey.vk == fabgl::VK_RETURN ? "R" : "S") + filedir; - } - - } else if (Menukey.vk == fabgl::VK_ESCAPE) { - - // Restore backbuffer data - if (menu_saverect) { - int j = SaveRectpos - (((w >> 2) + 1) * h); - SaveRectpos = j - 4; - for (int m = y; m < y + h; m++) { - uint32_t *backbuffer32 = (uint32_t *)(VIDEO::vga.backBuffer[m]); - for (int n = x >> 2; n < ((x + w) >> 2) + 1; n++) { - backbuffer32[n] = VIDEO::SaveRect[j]; - j++; - } - } - menu_saverect = false; - } - - fclose(dirfile); - dirfile = NULL; - click(); - return ""; - } - } - - } else { - - if (timeStartScroll < 200) timeStartScroll++; - - } - - // TO DO: SCROLL FOCUSED LINE IF SIGNALED - if (timeStartScroll == 200) { - timeScroll++; - if (timeScroll == 50) { - fdScrollPos++; - fd_PrintRow(FileUtils::fileTypes[ftype].focus, IS_FOCUSED); - timeScroll = 0; - } - } - - vTaskDelay(5 / portTICK_PERIOD_MS); - - } - - } - -} - // Print a virtual row void OSD::PrintRow(uint8_t virtual_row_num, uint8_t line_type) { @@ -982,128 +481,6 @@ void OSD::PrintRow(uint8_t virtual_row_num, uint8_t line_type) { } -// Print a virtual row -void OSD::fd_PrintRow(uint8_t virtual_row_num, uint8_t line_type) { - - uint8_t margin; - - string line = rowGet(menu, virtual_row_num); - - bool isDir = (line[0] == ASCII_SPC); - - trim(line); - - switch (line_type) { - case IS_TITLE: - VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(0, 0)); - margin = 2; - break; - case IS_INFO: - VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(5, 0)); - margin = (real_rows > virtual_rows ? 3 : 2); - break; - case IS_FOCUSED: - VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(5, 1)); - margin = (real_rows > virtual_rows ? 3 : 2); - break; - default: - VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(7, 1)); - margin = (real_rows > virtual_rows ? 3 : 2); - } - - menuAt(virtual_row_num, 0); - - VIDEO::vga.print(" "); - - if (isDir) { - - // Directory - if (line.length() <= cols - margin - 6) - line = line + std::string(cols - margin - line.length(), ' '); - else - if (line_type == IS_FOCUSED) { - line = line.substr(fdScrollPos); - if (line.length() <= cols - margin - 6) { - fdScrollPos = -1; - timeStartScroll = 0; - } - } - - line = line.substr(0,cols - margin - 6) + " "; - - } else { - - if (line.length() <= cols - margin) { - line = line + std::string(cols - margin - line.length(), ' '); - line = line.substr(0, cols - margin); - } else { - if (line_type == IS_INFO) { - // printf("%s %d\n",line.c_str(),line.length() - (cols - margin)); - line = ".." + line.substr(line.length() - (cols - margin) + 2); - // printf("%s\n",line.c_str()); - } else { - if (line_type == IS_FOCUSED) { - line = line.substr(fdScrollPos); - if (line.length() <= cols - margin) { - fdScrollPos = -1; - timeStartScroll = 0; - } - } - line = line.substr(0, cols - margin); - } - } - - } - - VIDEO::vga.print(line.c_str()); - - VIDEO::vga.print(" "); - -} - -// Redraw inside rows -void OSD::fd_Redraw(string title, string fdir, uint8_t ftype) { - - if ((FileUtils::fileTypes[ftype].focus != last_focus) || (FileUtils::fileTypes[ftype].begin_row != last_begin_row)) { - - // printf("fd_Redraw\n"); - - // Read bunch of rows - fseek(dirfile, (FileUtils::fileTypes[ftype].begin_row - 2) * 64,SEEK_SET); - menu = title + "\n" + ( fdir.length() == 1 ? fdir : fdir.substr(0,fdir.length()-1)) + "\n"; - for (int i = 2; i < virtual_rows; i++) { - char buf[128]; - fgets(buf, sizeof(buf), dirfile); - if (feof(dirfile)) break; - menu += buf; - } - - fd_PrintRow(1, IS_INFO); // Print status bar - - uint8_t row = 2; - for (; row < virtual_rows; row++) { - if (row == FileUtils::fileTypes[ftype].focus) { - fd_PrintRow(row, IS_FOCUSED); - } else { - fd_PrintRow(row, IS_NORMAL); - } - } - - if (real_rows > virtual_rows) { - menuScrollBar(FileUtils::fileTypes[ftype].begin_row); - } else { - for (; row < mf_rows; row++) { - VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(7, 1)); - menuAt(row, 0); - VIDEO::vga.print(std::string(cols, ' ').c_str()); - } - } - - last_focus = FileUtils::fileTypes[ftype].focus; - last_begin_row = FileUtils::fileTypes[ftype].begin_row; - } - -} string tapeBlockReadData(int Blocknum) { @@ -1275,7 +652,7 @@ int OSD::menuTape(string title) { while (1) { - if (ZXKeyb::Exists) ZXKbdRead(); + if (ZXKeyb::Exists) ZXKeyb::ZXKbdRead(); ESPectrum::readKbdJoy(); diff --git a/src/Video.cpp b/src/Video.cpp index 49ce7159..09014d1c 100644 --- a/src/Video.cpp +++ b/src/Video.cpp @@ -723,11 +723,7 @@ void VIDEO::MainScreen_Pentagon_delay(unsigned int statestoadd, bool contended) for (int i=0; i < statestoadd; i++) { if (++coldraw_cnt > 1) { coldraw_cnt = col_end; - if (is169) { - col_end = 170; - } else { - col_end = 160; - } + col_end = is169 ? 170 : 160; Draw = MainScreenRB_Pentagon; video_rest += statestoadd - (i + 1); MainScreenRB_Pentagon(0,false); @@ -957,41 +953,24 @@ IRAM_ATTR void VIDEO::BottomBorder(unsigned int statestoadd, bool contended) { video_rest = statestoadd & 0x03; // Mod 4 - // for (int i=0; i < (statestoadd >> 2); i++) { - - // *lineptr32++ = brd; - // *lineptr32++ = brd; - // if (++coldraw_cnt == 40) { - // Draw = ++linedraw_cnt == (is169 ? 200 : 240) ? &Blank : &BottomBorder_Blank ; - // return; - // } - // } + unsigned int i = coldraw_cnt; - int loopCount = statestoadd >> 2; - - if (coldraw_cnt + loopCount > 39) { + coldraw_cnt += statestoadd >> 2; + + if (coldraw_cnt > 39) { - for (;;) { - + for (;i < 40;i++) { *lineptr32++ = brd; *lineptr32++ = brd; - - if (++coldraw_cnt > 39) { - Draw = ++linedraw_cnt == (is169 ? 200 : 240) ? &Blank : &BottomBorder_Blank ; - return; - } - } - } else { + Draw = ++linedraw_cnt == (is169 ? 200 : 240) ? &Blank : &BottomBorder_Blank ; - coldraw_cnt += loopCount; - - for (;loopCount > 0 ; loopCount--) { + } else { + for (;i < coldraw_cnt; i++) { *lineptr32++ = brd; *lineptr32++ = brd; - } } @@ -1023,22 +1002,48 @@ IRAM_ATTR void VIDEO::BottomBorder_OSD(unsigned int statestoadd, bool contended) video_rest = statestoadd & 0x03; // Mod 4 - for (int i=0; i < statestoadd >> 2; i++) { - - if (linedraw_cnt < 220 || linedraw_cnt>235) { - *lineptr32++ = brd; - *lineptr32++ = brd; + if (linedraw_cnt < 220 || linedraw_cnt > 235) { + + unsigned int i = coldraw_cnt; + + coldraw_cnt += statestoadd >> 2; + + if (coldraw_cnt > 39) { + + for (;i < 40;i++) { + *lineptr32++ = brd; + *lineptr32++ = brd; + } + + Draw = ++linedraw_cnt == 240 ? &Blank : &BottomBorder_Blank ; + } else { - if (coldraw_cnt < 21 || coldraw_cnt > 38) { + + for (;i < coldraw_cnt; i++) { *lineptr32++ = brd; *lineptr32++ = brd; - } else lineptr32 += 2; + } + } - - if (++coldraw_cnt > 39) { - Draw = ++linedraw_cnt == 240 ? &Blank : &BottomBorder_Blank ; - return; + + } else { + + for (unsigned int i=0; i < statestoadd >> 2; i++) { + + if (coldraw_cnt < 21) { + *lineptr32++ = brd; + *lineptr32++ = brd; + } else if (coldraw_cnt > 38) { + *lineptr32++ = brd; + *lineptr32++ = brd; + Draw = ++linedraw_cnt == 240 ? &Blank : &BottomBorder_Blank ; + return; + } else lineptr32 += 2; + + coldraw_cnt++; + } + } } @@ -1080,16 +1085,3 @@ IRAM_ATTR void VIDEO::EndFrame() { framecnt++; } - -// /////////////////////////////////////////////////////////////////////////////// -// // Flush -> Flush screen after HALT -// /////////////////////////////////////////////////////////////////////////////// -// IRAM_ATTR void VIDEO::Flush() { - -// // while (CPU::tstates < CPU::statesInFrame) { -// while (Draw != &Blank) -// Draw(tStatesPerLine,false); -// // } - -// } - diff --git a/src/Z80_JLS.cpp b/src/Z80_JLS.cpp index b60f35de..c2088e07 100644 --- a/src/Z80_JLS.cpp +++ b/src/Z80_JLS.cpp @@ -4515,14 +4515,12 @@ void (*Z80::dcCB[256])() = { }; -// Trim from end (in place) (for SAVE trap, clean this up later) -static inline void rtrim(std::string &s) { - s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { - return !std::isspace(ch); - }).base(), s.end()); -} - -static uint8_t SaveRes; +// // Trim from end (in place) (for SAVE trap, clean this up later) +// static inline void rtrim(std::string &s) { +// s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { +// return !std::isspace(ch); +// }).base(), s.end()); +// } //Subconjunto de instrucciones 0xDD / 0xFD /* @@ -4607,6 +4605,8 @@ void Z80::decodeDDFD(RegisterPair& regIXY) { if (REG_PC == 0x04d4) { // Save trap + static uint8_t SaveRes; + if (REG_HL == 0x1F80) { // printf("Saving header!\n"); diff --git a/src/ZXKeyb.cpp b/src/ZXKeyb.cpp index 5306b9f6..e1052832 100644 --- a/src/ZXKeyb.cpp +++ b/src/ZXKeyb.cpp @@ -54,8 +54,8 @@ void ZXKeyb::setup() gpio_set_direction((gpio_num_t)KM_COL_4, (gpio_mode_t)GPIO_MODE_INPUT); // Check if membrane keyboard is present - ZXKeyb::putRows(0xFF); - ZXKeyb::Exists = gpio_get_level((gpio_num_t)KM_COL_1) && gpio_get_level((gpio_num_t)KM_COL_2) && gpio_get_level((gpio_num_t)KM_COL_4); + putRows(0xFF); + Exists = gpio_get_level((gpio_num_t)KM_COL_1) && gpio_get_level((gpio_num_t)KM_COL_2) && gpio_get_level((gpio_num_t)KM_COL_4); } @@ -126,3 +126,92 @@ uint8_t ZXKeyb::getCols() { return cols; } + +void ZXKeyb::ZXKbdRead() { + + #define REPDEL 140 // As in real ZX Spectrum (700 ms.) if this function is called every 5 ms. + #define REPPER 20 // As in real ZX Spectrum (100 ms.) if this function is called every 5 ms. + + static int zxDel = REPDEL; + static int lastzxK = fabgl::VK_NONE; + + process(); + + fabgl::VirtualKey injectKey = fabgl::VK_NONE; + + if (bitRead(ZXcols[7], 1)) { // Not Symbol Shift pressed ? + + if (!bitRead(ZXcols[4], 3)) injectKey = fabgl::VK_UP; // 7 -> UP + else if (!bitRead(ZXcols[4], 4)) injectKey = fabgl::VK_DOWN; // 6 -> DOWN + else if (!bitRead(ZXcols[6], 0)) injectKey = fabgl::VK_RETURN; // ENTER + else if ((!bitRead(ZXcols[0], 0)) && (!bitRead(ZXcols[4], 0))) injectKey = fabgl::VK_BACKSPACE; // CS + 0 -> BACKSPACE + else if (!bitRead(ZXcols[4], 0)) injectKey = fabgl::VK_SPACE; // 0 -> SPACE + else if ((!bitRead(ZXcols[7], 0)) || (!bitRead(ZXcols[4], 1))) injectKey = fabgl::VK_ESCAPE; // BREAK -> ESCAPE + else if (!bitRead(ZXcols[3], 4)) injectKey = fabgl::VK_LEFT; // 5 -> PGUP + else if (!bitRead(ZXcols[4], 2)) injectKey = fabgl::VK_RIGHT; // 8 -> PGDOWN + else if (!bitRead(ZXcols[1], 1)) injectKey = fabgl::VK_PRINTSCREEN; // S -> PRINTSCREEN + else if (!bitRead(ZXcols[5], 0)) injectKey = fabgl::VK_PAUSE; // P -> PAUSE + + } else { + + if (!bitRead(ZXcols[0], 1)) injectKey = !bitRead(ZXcols[0], 0) ? fabgl::VK_Z : fabgl::VK_z; + else if (!bitRead(ZXcols[0], 2)) injectKey = !bitRead(ZXcols[0], 0) ? fabgl::VK_X : fabgl::VK_x; + else if (!bitRead(ZXcols[0], 3)) injectKey = !bitRead(ZXcols[0], 0) ? fabgl::VK_C : fabgl::VK_c; + else if (!bitRead(ZXcols[0], 4)) injectKey = !bitRead(ZXcols[0], 0) ? fabgl::VK_V : fabgl::VK_v; + + else if (!bitRead(ZXcols[1], 0)) injectKey = !bitRead(ZXcols[0], 0) ? fabgl::VK_A : fabgl::VK_a; + else if (!bitRead(ZXcols[1], 1)) injectKey = !bitRead(ZXcols[0], 0) ? fabgl::VK_S : fabgl::VK_s; + else if (!bitRead(ZXcols[1], 2)) injectKey = !bitRead(ZXcols[0], 0) ? fabgl::VK_D : fabgl::VK_d; + else if (!bitRead(ZXcols[1], 3)) injectKey = !bitRead(ZXcols[0], 0) ? fabgl::VK_F : fabgl::VK_f; + else if (!bitRead(ZXcols[1], 4)) injectKey = !bitRead(ZXcols[0], 0) ? fabgl::VK_G : fabgl::VK_g; + + else if (!bitRead(ZXcols[2], 0)) injectKey = !bitRead(ZXcols[0], 0) ? fabgl::VK_Q : fabgl::VK_q; + else if (!bitRead(ZXcols[2], 1)) injectKey = !bitRead(ZXcols[0], 0) ? fabgl::VK_W : fabgl::VK_w; + else if (!bitRead(ZXcols[2], 2)) injectKey = !bitRead(ZXcols[0], 0) ? fabgl::VK_E : fabgl::VK_e; + else if (!bitRead(ZXcols[2], 3)) injectKey = !bitRead(ZXcols[0], 0) ? fabgl::VK_R : fabgl::VK_r; + else if (!bitRead(ZXcols[2], 4)) injectKey = !bitRead(ZXcols[0], 0) ? fabgl::VK_T : fabgl::VK_t; + + else if (!bitRead(ZXcols[5], 0)) injectKey = !bitRead(ZXcols[0], 0) ? fabgl::VK_P : fabgl::VK_p; + else if (!bitRead(ZXcols[5], 1)) injectKey = !bitRead(ZXcols[0], 0) ? fabgl::VK_O : fabgl::VK_o; + else if (!bitRead(ZXcols[5], 2)) injectKey = !bitRead(ZXcols[0], 0) ? fabgl::VK_I : fabgl::VK_i; + else if (!bitRead(ZXcols[5], 3)) injectKey = !bitRead(ZXcols[0], 0) ? fabgl::VK_U : fabgl::VK_u; + else if (!bitRead(ZXcols[5], 4)) injectKey = !bitRead(ZXcols[0], 0) ? fabgl::VK_Y : fabgl::VK_y; + + else if (!bitRead(ZXcols[6], 1)) injectKey = !bitRead(ZXcols[0], 0) ? fabgl::VK_L : fabgl::VK_l; + else if (!bitRead(ZXcols[6], 2)) injectKey = !bitRead(ZXcols[0], 0) ? fabgl::VK_K : fabgl::VK_k; + else if (!bitRead(ZXcols[6], 3)) injectKey = !bitRead(ZXcols[0], 0) ? fabgl::VK_J : fabgl::VK_j; + else if (!bitRead(ZXcols[6], 4)) injectKey = !bitRead(ZXcols[0], 0) ? fabgl::VK_H : fabgl::VK_h; + + else if (!bitRead(ZXcols[7], 2)) injectKey = !bitRead(ZXcols[0], 0) ? fabgl::VK_M : fabgl::VK_m; + else if (!bitRead(ZXcols[7], 3)) injectKey = !bitRead(ZXcols[0], 0) ? fabgl::VK_N : fabgl::VK_n; + else if (!bitRead(ZXcols[7], 4)) injectKey = !bitRead(ZXcols[0], 0) ? fabgl::VK_B : fabgl::VK_b; + + else if (!bitRead(ZXcols[3], 0)) injectKey = fabgl::VK_1; + else if (!bitRead(ZXcols[3], 1)) injectKey = fabgl::VK_2; + else if (!bitRead(ZXcols[3], 2)) injectKey = fabgl::VK_3; + else if (!bitRead(ZXcols[3], 3)) injectKey = fabgl::VK_4; + else if (!bitRead(ZXcols[3], 4)) injectKey = fabgl::VK_5; + + else if (!bitRead(ZXcols[4], 0)) injectKey = fabgl::VK_0; + else if (!bitRead(ZXcols[4], 1)) injectKey = fabgl::VK_9; + else if (!bitRead(ZXcols[4], 2)) injectKey = fabgl::VK_8; + else if (!bitRead(ZXcols[4], 3)) injectKey = fabgl::VK_7; + else if (!bitRead(ZXcols[4], 4)) injectKey = fabgl::VK_6; + + } + + if (injectKey != fabgl::VK_NONE) { + if (zxDel == 0) { + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(injectKey, true, false); + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(injectKey, false, false); + zxDel = lastzxK == injectKey ? REPPER : REPDEL; + lastzxK = injectKey; + } + } else { + zxDel = 0; + lastzxK = fabgl::VK_NONE; + } + + if (zxDel > 0) zxDel--; + +} From 7dc8dbf6cb1e5a4a5a137af7bd7fcfe138215cdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Iborra?= Date: Tue, 12 Dec 2023 09:57:31 +0100 Subject: [PATCH 23/30] New fileDialog string search --- include/Config.h | 10 ++ include/FileUtils.h | 2 + include/OSDMain.h | 4 + src/Config.cpp | 70 ++++++++++ src/ESPectrum.cpp | 6 + src/FileUtils.cpp | 6 +- src/OSDFile.cpp | 329 ++++++++++++++++++++++++++++++++++++-------- src/Snapshot.cpp | 24 ++++ src/Video.cpp | 117 ++++++++-------- 9 files changed, 448 insertions(+), 120 deletions(-) diff --git a/include/Config.h b/include/Config.h index 4c70ff89..c8f84685 100644 --- a/include/Config.h +++ b/include/Config.h @@ -72,15 +72,25 @@ class Config static bool CursorAsJoy; static int8_t CenterH; static int8_t CenterV; + static string SNA_Path; static string TAP_Path; static string DSK_Path; + static uint16_t SNA_begin_row; static uint16_t SNA_focus; + static uint8_t SNA_fdMode; + static string SNA_fileSearch; + static uint16_t TAP_begin_row; static uint16_t TAP_focus; + static uint8_t TAP_fdMode; + static string TAP_fileSearch; + static uint16_t DSK_begin_row; static uint16_t DSK_focus; + static uint8_t DSK_fdMode; + static string DSK_fileSearch; // config persistence static void load(); diff --git a/include/FileUtils.h b/include/FileUtils.h index fc2c87a1..4f6d1160 100644 --- a/include/FileUtils.h +++ b/include/FileUtils.h @@ -57,6 +57,8 @@ struct DISK_FTYPE { string indexFilename; int begin_row; int focus; + uint8_t fdMode; + string fileSearch; }; class FileUtils diff --git a/include/OSDMain.h b/include/OSDMain.h index aeaf7ac0..3b10c951 100644 --- a/include/OSDMain.h +++ b/include/OSDMain.h @@ -154,6 +154,10 @@ class OSD { static uint8_t last_focus; // To check for changes static unsigned short last_begin_row; // To check for changes + static uint8_t fdCursorFlash; + static bool fdSearchRefresh; + static unsigned int fdSearchElements; + }; // trim from start (in place) diff --git a/src/Config.cpp b/src/Config.cpp index 7449c22e..2dc6a62f 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -71,15 +71,25 @@ uint8_t Config::ps2_dev2 = 0; // Second port PS/2 device: 0 -> None, 1 -> PS/2 bool Config::CursorAsJoy = false; int8_t Config::CenterH = 0; int8_t Config::CenterV = 0; + string Config::SNA_Path = "/"; string Config::TAP_Path = "/"; string Config::DSK_Path = "/"; + uint16_t Config::SNA_begin_row = 1; uint16_t Config::SNA_focus = 1; +uint8_t Config::SNA_fdMode = 0; +string Config::SNA_fileSearch = ""; + uint16_t Config::TAP_begin_row = 1; uint16_t Config::TAP_focus = 1; +uint8_t Config::TAP_fdMode = 0; +string Config::TAP_fileSearch = ""; + uint16_t Config::DSK_begin_row = 1; uint16_t Config::DSK_focus = 1; +uint8_t Config::DSK_fdMode = 0; +string Config::DSK_fileSearch = ""; // erase control characters (in place) static inline void erase_cntrl(std::string &s) { @@ -333,6 +343,48 @@ void Config::load() { // printf("DSK_focus:%u\n",Config::DSK_focus); } + err = nvs_get_u8(handle, "SNA_fdMode", &Config::SNA_fdMode); + if (err == ESP_OK) { + // printf("SNA_fdMode:%u\n",Config::SNA_fdMode); + } + + err = nvs_get_u8(handle, "TAP_fdMode", &Config::TAP_fdMode); + if (err == ESP_OK) { + // printf("TAP_fdMode:%u\n",Config::TAP_fdMode); + } + + err = nvs_get_u8(handle, "DSK_fdMode", &Config::DSK_fdMode); + if (err == ESP_OK) { + // printf("DSK_fdMode:%u\n",Config::DSK_fdMode); + } + + err = nvs_get_str(handle, "SNA_fileSearch", NULL, &required_size); + if (err == ESP_OK) { + str_data = (char *)malloc(required_size); + nvs_get_str(handle, "SNA_fileSearch", str_data, &required_size); + // printf("SNA_fileSearch:%s\n",str_data); + SNA_fileSearch = str_data; + free(str_data); + } + + err = nvs_get_str(handle, "TAP_fileSearch", NULL, &required_size); + if (err == ESP_OK) { + str_data = (char *)malloc(required_size); + nvs_get_str(handle, "TAP_fileSearch", str_data, &required_size); + // printf("TAP_fileSearch:%s\n",str_data); + TAP_fileSearch = str_data; + free(str_data); + } + + err = nvs_get_str(handle, "DSK_fileSearch", NULL, &required_size); + if (err == ESP_OK) { + str_data = (char *)malloc(required_size); + nvs_get_str(handle, "DSK_fileSearch", str_data, &required_size); + // printf("DSK_fileSearch:%s\n",str_data); + DSK_fileSearch = str_data; + free(str_data); + } + // Close nvs_close(handle); } @@ -445,6 +497,24 @@ void Config::save(string value) { if((value=="DSK_focus") || (value=="all")) nvs_set_u16(handle,"DSK_focus",Config::DSK_focus); + if((value=="SNA_fdMode") || (value=="all")) + nvs_set_u8(handle,"SNA_fdMode",Config::SNA_fdMode); + + if((value=="TAP_fdMode") || (value=="all")) + nvs_set_u8(handle,"TAP_fdMode",Config::TAP_fdMode); + + if((value=="DSK_fdMode") || (value=="all")) + nvs_set_u8(handle,"DSK_fdMode",Config::DSK_fdMode); + + if((value=="SNA_fileSearch") || (value=="all")) + nvs_set_str(handle,"SNA_fileSearch",Config::SNA_fileSearch.c_str()); + + if((value=="TAP_fileSearch") || (value=="all")) + nvs_set_str(handle,"TAP_fileSearch",Config::TAP_fileSearch.c_str()); + + if((value=="DSK_fileSearch") || (value=="all")) + nvs_set_str(handle,"DSK_fileSearch",Config::DSK_fileSearch.c_str()); + // printf("Committing updates in NVS ... "); err = nvs_commit(handle); diff --git a/src/ESPectrum.cpp b/src/ESPectrum.cpp index 82494bf0..01ca6300 100644 --- a/src/ESPectrum.cpp +++ b/src/ESPectrum.cpp @@ -535,14 +535,20 @@ void ESPectrum::setup() FileUtils::SNA_Path = Config::SNA_Path; FileUtils::fileTypes[DISK_SNAFILE].begin_row = Config::SNA_begin_row; FileUtils::fileTypes[DISK_SNAFILE].focus = Config::SNA_focus; + FileUtils::fileTypes[DISK_SNAFILE].fdMode = Config::SNA_fdMode; + FileUtils::fileTypes[DISK_SNAFILE].fileSearch = Config::SNA_fileSearch; FileUtils::TAP_Path = Config::TAP_Path; FileUtils::fileTypes[DISK_TAPFILE].begin_row = Config::TAP_begin_row; FileUtils::fileTypes[DISK_TAPFILE].focus = Config::TAP_focus; + FileUtils::fileTypes[DISK_TAPFILE].fdMode = Config::TAP_fdMode; + FileUtils::fileTypes[DISK_TAPFILE].fileSearch = Config::TAP_fileSearch; FileUtils::DSK_Path = Config::DSK_Path; FileUtils::fileTypes[DISK_DSKFILE].begin_row = Config::DSK_begin_row; FileUtils::fileTypes[DISK_DSKFILE].focus = Config::DSK_focus; + FileUtils::fileTypes[DISK_DSKFILE].fdMode = Config::DSK_fdMode; + FileUtils::fileTypes[DISK_DSKFILE].fileSearch = Config::DSK_fileSearch; LoadSnapshot(Config::ram_file,""); diff --git a/src/FileUtils.cpp b/src/FileUtils.cpp index 882360f4..fc371e02 100644 --- a/src/FileUtils.cpp +++ b/src/FileUtils.cpp @@ -65,9 +65,9 @@ string FileUtils::SNA_Path = "/"; // DISK_SNA_DIR; // Current path on the SD (fo string FileUtils::TAP_Path = "/"; // DISK_TAP_DIR; // Current path on the SD (for future folder support) string FileUtils::DSK_Path = "/"; // DISK_DSK_DIR; // Current path on the SD (for future folder support) DISK_FTYPE FileUtils::fileTypes[3] = { - {".sna,.SNA,.z80,.Z80",".s",2,2}, - {".tap,.TAP",".t",2,2}, - {".trd,.TRD,.scl,.SCL",".d",2,2} + {".sna,.SNA,.z80,.Z80",".s",2,2,0,""}, + {".tap,.TAP",".t",2,2,0,""}, + {".trd,.TRD,.scl,.SCL",".d",2,2,0,""} }; void FileUtils::initFileSystem() { diff --git a/src/OSDFile.cpp b/src/OSDFile.cpp index 972e45fc..4cef5424 100644 --- a/src/OSDFile.cpp +++ b/src/OSDFile.cpp @@ -34,6 +34,7 @@ visit https://zxespectrum.speccy.org/contacto */ #include +#include #include #include #include "errno.h" @@ -58,10 +59,13 @@ using namespace std; FILE *dirfile; unsigned int OSD::elements; +unsigned int OSD::fdSearchElements; unsigned int OSD::ndirs; int8_t OSD::fdScrollPos; int OSD::timeStartScroll; int OSD::timeScroll; +uint8_t OSD::fdCursorFlash; +bool OSD::fdSearchRefresh; // Run a new file menu string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols, uint8_t mfrows) { @@ -69,7 +73,7 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols // struct stat stat_buf; long dirfilesize; bool reIndex; - + // Position if (menu_level == 0) { x = (Config::aspect_16_9 ? 24 : 4); @@ -112,8 +116,13 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols VIDEO::vga.print(std::string(cols, ' ').c_str()); // char fsessid[32]; + + // fdSearchRefresh = true; + while(1) { + fdCursorFlash = 0; + reIndex = false; string filedir = FileUtils::MountPoint + fdir; @@ -246,13 +255,71 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols } - // real_rows = (stat_buf.st_size / 64) + 2; // Add 2 for title and status bar - real_rows = (dirfilesize / 64) + 2; // Add 2 for title and status bar - virtual_rows = (real_rows > mf_rows ? mf_rows : real_rows); - - // printf("Real rows: %d; st_size: %d; Virtual rows: %d\n",real_rows,stat_buf.st_size,virtual_rows); + if (FileUtils::fileTypes[ftype].fdMode) { + + // menuAt(mfrows + (Config::aspect_16_9 ? 0 : 1), 1); + // VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(5, 0)); + // VIDEO::vga.print("Find: "); + // VIDEO::vga.print(FileUtils::fileTypes[ftype].fileSearch.c_str()); + // if (++fdCursorFlash > 63) { + // VIDEO::vga.setTextColor(zxColor(5, 0), zxColor(7, 1)); + // if (fdCursorFlash == 128) fdCursorFlash = 0; + // } + // VIDEO::vga.print("K"); + // VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(5, 0)); + // VIDEO::vga.print(std::string(10 - FileUtils::fileTypes[ftype].fileSearch.size(), ' ').c_str()); + + // if (fdSearchRefresh) { + + // Recalc items number + long prevpos = ftell(dirfile); + + unsigned int foundcount = 0; + fdSearchElements = 0; + rewind(dirfile); + char buf[128]; + while(1) { + fgets(buf, sizeof(buf), dirfile); + if (feof(dirfile)) break; + if (buf[0] == ASCII_SPC) { + foundcount++; + // printf("%s",buf); + }else { + char *pch = strstr(buf, FileUtils::fileTypes[ftype].fileSearch.c_str()); + if (pch != NULL) { + foundcount++; + fdSearchElements++; + // printf("%s",buf); + } + } + } + + if (foundcount) { + // Redraw rows + real_rows = foundcount + 2; // Add 2 for title and status bar + virtual_rows = (real_rows > mf_rows ? mf_rows : real_rows); + last_begin_row = last_focus = 0; + // FileUtils::fileTypes[ftype].focus = 2; + // FileUtils::fileTypes[ftype].begin_row = 2; + // fd_Redraw(title, fdir, ftype); + } else { + fseek(dirfile,prevpos,SEEK_SET); + } + + fdSearchRefresh = false; + + // } - last_begin_row = last_focus = 0; + } else { + + // real_rows = (stat_buf.st_size / 64) + 2; // Add 2 for title and status bar + real_rows = (dirfilesize / 64) + 2; // Add 2 for title and status bar + virtual_rows = (real_rows > mf_rows ? mf_rows : real_rows); + // printf("Real rows: %d; st_size: %d; Virtual rows: %d\n",real_rows,stat_buf.st_size,virtual_rows); + + last_begin_row = last_focus = 0; + + } // printf("Focus: %d, Begin_row: %d, real_rows: %d, mf_rows: %d\n",(int) FileUtils::fileTypes[ftype].focus,(int) FileUtils::fileTypes[ftype].begin_row,(int) real_rows, (int) mf_rows); if ((real_rows > mf_rows) && ((FileUtils::fileTypes[ftype].begin_row + mf_rows - 2) > real_rows)) { @@ -267,6 +334,7 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols fdScrollPos = 0; timeStartScroll = 0; timeScroll = 0; + fabgl::VirtualKeyItem Menukey; while (1) { @@ -283,11 +351,13 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols // Print elements VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(5, 0)); - if (elements) { + + unsigned int elem = FileUtils::fileTypes[ftype].fdMode ? fdSearchElements : elements; + if (elem) { menuAt(mfrows + (Config::aspect_16_9 ? 0 : 1), cols - (real_rows > virtual_rows ? 13 : 12)); char elements_txt[13]; int nitem = (FileUtils::fileTypes[ftype].begin_row + FileUtils::fileTypes[ftype].focus ) - (4 + ndirs) + (fdir.length() == 1); - snprintf(elements_txt, sizeof(elements_txt), "%d/%d ", nitem > 0 ? nitem : 0 , elements); + snprintf(elements_txt, sizeof(elements_txt), "%d/%d ", nitem > 0 ? nitem : 0 , elem); VIDEO::vga.print(std::string(12 - strlen(elements_txt), ' ').c_str()); VIDEO::vga.print(elements_txt); } else { @@ -301,6 +371,7 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols // Search first ocurrence of letter if we're not on that letter yet if (((Menukey.vk >= fabgl::VK_a) && (Menukey.vk <= fabgl::VK_Z)) || ((Menukey.vk >= fabgl::VK_0) && (Menukey.vk <= fabgl::VK_9))) { + int fsearch; if (Menukey.vk<=fabgl::VK_9) fsearch = Menukey.vk + 46; @@ -308,43 +379,94 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols fsearch = Menukey.vk + 75; else if (Menukey.vk<=fabgl::VK_Z) fsearch = Menukey.vk + 17; - uint8_t letra = rowGet(menu,FileUtils::fileTypes[ftype].focus).at(0); - // printf("%d %d\n",(int)letra,fsearch); - if (letra != fsearch) { - // Seek first ocurrence of letter/number - long prevpos = ftell(dirfile); - char buf[128]; - int cnt = 0; - fseek(dirfile,0,SEEK_SET); - while(!feof(dirfile)) { - fgets(buf, sizeof(buf), dirfile); - // printf("%c %d\n",buf[0],int(buf[0])); - if (buf[0] == char(fsearch)) break; - cnt++; + + if (FileUtils::fileTypes[ftype].fdMode) { + + if (FileUtils::fileTypes[ftype].fileSearch.length()<10) { + FileUtils::fileTypes[ftype].fileSearch += char(fsearch); + fdSearchRefresh = true; + click(); } - // printf("Cnt: %d Letra: %d\n",cnt,int(letra)); - if (!feof(dirfile)) { - last_begin_row = FileUtils::fileTypes[ftype].begin_row; - last_focus = FileUtils::fileTypes[ftype].focus; - if (real_rows > virtual_rows) { - int m = cnt + virtual_rows - real_rows; - if (m > 0) { - FileUtils::fileTypes[ftype].focus = m + 2; - FileUtils::fileTypes[ftype].begin_row = cnt - m + 2; + + } else { + uint8_t letra = rowGet(menu,FileUtils::fileTypes[ftype].focus).at(0); + // printf("%d %d\n",(int)letra,fsearch); + if (letra != fsearch) { + // Seek first ocurrence of letter/number + long prevpos = ftell(dirfile); + char buf[128]; + int cnt = 0; + fseek(dirfile,0,SEEK_SET); + while(!feof(dirfile)) { + fgets(buf, sizeof(buf), dirfile); + // printf("%c %d\n",buf[0],int(buf[0])); + if (buf[0] == char(fsearch)) break; + cnt++; + } + // printf("Cnt: %d Letra: %d\n",cnt,int(letra)); + if (!feof(dirfile)) { + last_begin_row = FileUtils::fileTypes[ftype].begin_row; + last_focus = FileUtils::fileTypes[ftype].focus; + if (real_rows > virtual_rows) { + int m = cnt + virtual_rows - real_rows; + if (m > 0) { + FileUtils::fileTypes[ftype].focus = m + 2; + FileUtils::fileTypes[ftype].begin_row = cnt - m + 2; + } else { + FileUtils::fileTypes[ftype].focus = 2; + FileUtils::fileTypes[ftype].begin_row = cnt + 2; + } } else { - FileUtils::fileTypes[ftype].focus = 2; - FileUtils::fileTypes[ftype].begin_row = cnt + 2; + FileUtils::fileTypes[ftype].focus = cnt + 2; + FileUtils::fileTypes[ftype].begin_row = 2; } - } else { - FileUtils::fileTypes[ftype].focus = cnt + 2; - FileUtils::fileTypes[ftype].begin_row = 2; - } - // printf("Real rows: %d; Virtual rows: %d\n",real_rows,virtual_rows); - // printf("Focus: %d, Begin_row: %d\n",(int) FileUtils::fileTypes[ftype].focus,(int) FileUtils::fileTypes[ftype].begin_row); - fd_Redraw(title,fdir,ftype); - } else - fseek(dirfile,prevpos,SEEK_SET); + // printf("Real rows: %d; Virtual rows: %d\n",real_rows,virtual_rows); + // printf("Focus: %d, Begin_row: %d\n",(int) FileUtils::fileTypes[ftype].focus,(int) FileUtils::fileTypes[ftype].begin_row); + fd_Redraw(title,fdir,ftype); + click(); + } else + fseek(dirfile,prevpos,SEEK_SET); + + } } + + } else if (Menukey.vk == fabgl::VK_F3) { + + FileUtils::fileTypes[ftype].fdMode ^= 1; + + if (FileUtils::fileTypes[ftype].fdMode) { + + fdCursorFlash = 0; + menuAt(mfrows + (Config::aspect_16_9 ? 0 : 1), 1); + VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(5, 0)); + VIDEO::vga.print("Find: "); + VIDEO::vga.print(FileUtils::fileTypes[ftype].fileSearch.c_str()); + VIDEO::vga.print("K"); + VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(5, 0)); + VIDEO::vga.print(std::string(10 - FileUtils::fileTypes[ftype].fileSearch.size(), ' ').c_str()); + + fdSearchRefresh = FileUtils::fileTypes[ftype].fileSearch != ""; + + } else { + + menuAt(mfrows + (Config::aspect_16_9 ? 0 : 1), 1); + VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(5, 0)); + VIDEO::vga.print(" " " "); + + if (FileUtils::fileTypes[ftype].fileSearch != "") { + // FileUtils::fileTypes[ftype].fileSearch=""; + real_rows = (dirfilesize / 64) + 2; // Add 2 for title and status bar + virtual_rows = (real_rows > mf_rows ? mf_rows : real_rows); + last_begin_row = last_focus = 0; + FileUtils::fileTypes[ftype].focus = 2; + FileUtils::fileTypes[ftype].begin_row = 2; + fd_Redraw(title, fdir, ftype); + } + + } + + click(); + } else if (Menukey.vk == fabgl::VK_UP) { if (FileUtils::fileTypes[ftype].focus == 2 && FileUtils::fileTypes[ftype].begin_row > 2) { last_begin_row = FileUtils::fileTypes[ftype].begin_row; @@ -405,19 +527,30 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols fd_Redraw(title, fdir, ftype); click(); } else if (Menukey.vk == fabgl::VK_BACKSPACE) { - if (fdir != "/") { + if (FileUtils::fileTypes[ftype].fdMode) { + if (FileUtils::fileTypes[ftype].fileSearch.length()) { + FileUtils::fileTypes[ftype].fileSearch.pop_back(); + fdSearchRefresh = true; + click(); + } + } else { + if (fdir != "/") { - fclose(dirfile); - dirfile = NULL; + fclose(dirfile); + dirfile = NULL; - fdir.pop_back(); - fdir = fdir.substr(0,fdir.find_last_of("/") + 1); + fdir.pop_back(); + fdir = fdir.substr(0,fdir.find_last_of("/") + 1); - FileUtils::fileTypes[ftype].begin_row = FileUtils::fileTypes[ftype].focus = 2; - // printf("Fdir: %s\n",fdir.c_str()); - break; + FileUtils::fileTypes[ftype].begin_row = FileUtils::fileTypes[ftype].focus = 2; + // printf("Fdir: %s\n",fdir.c_str()); + + click(); + + break; - } + } + } } else if (Menukey.vk == fabgl::VK_RETURN || Menukey.vk == fabgl::VK_SPACE) { fclose(dirfile); @@ -497,6 +630,62 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols } } + if (FileUtils::fileTypes[ftype].fdMode) { + + menuAt(mfrows + (Config::aspect_16_9 ? 0 : 1), 1); + VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(5, 0)); + VIDEO::vga.print("Find: "); + VIDEO::vga.print(FileUtils::fileTypes[ftype].fileSearch.c_str()); + if (++fdCursorFlash > 63) { + VIDEO::vga.setTextColor(zxColor(5, 0), zxColor(7, 1)); + if (fdCursorFlash == 128) fdCursorFlash = 0; + } + VIDEO::vga.print("K"); + VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(5, 0)); + VIDEO::vga.print(std::string(10 - FileUtils::fileTypes[ftype].fileSearch.size(), ' ').c_str()); + + if (fdSearchRefresh) { + + // Recalc items number + long prevpos = ftell(dirfile); + + unsigned int foundcount = 0; + fdSearchElements = 0; + rewind(dirfile); + char buf[128]; + while(1) { + fgets(buf, sizeof(buf), dirfile); + if (feof(dirfile)) break; + if (buf[0] == ASCII_SPC) { + foundcount++; + // printf("%s",buf); + }else { + char *pch = strstr(buf, FileUtils::fileTypes[ftype].fileSearch.c_str()); + if (pch != NULL) { + foundcount++; + fdSearchElements++; + // printf("%s",buf); + } + } + } + + if (foundcount) { + // Redraw rows + real_rows = foundcount + 2; // Add 2 for title and status bar + virtual_rows = (real_rows > mf_rows ? mf_rows : real_rows); + last_begin_row = last_focus = 0; + FileUtils::fileTypes[ftype].focus = 2; + FileUtils::fileTypes[ftype].begin_row = 2; + fd_Redraw(title, fdir, ftype); + } else { + fseek(dirfile,prevpos,SEEK_SET); + } + + fdSearchRefresh = false; + } + + } + vTaskDelay(5 / portTICK_PERIOD_MS); } @@ -513,13 +702,39 @@ void OSD::fd_Redraw(string title, string fdir, uint8_t ftype) { // printf("fd_Redraw\n"); // Read bunch of rows - fseek(dirfile, (FileUtils::fileTypes[ftype].begin_row - 2) * 64,SEEK_SET); menu = title + "\n" + ( fdir.length() == 1 ? fdir : fdir.substr(0,fdir.length()-1)) + "\n"; - for (int i = 2; i < virtual_rows; i++) { - char buf[128]; - fgets(buf, sizeof(buf), dirfile); - if (feof(dirfile)) break; - menu += buf; + char buf[128]; + if (FileUtils::fileTypes[ftype].fdMode == 0 || FileUtils::fileTypes[ftype].fileSearch == "") { + fseek(dirfile, (FileUtils::fileTypes[ftype].begin_row - 2) * 64,SEEK_SET); + for (int i = 2; i < virtual_rows; i++) { + fgets(buf, sizeof(buf), dirfile); + if (feof(dirfile)) break; + menu += buf; + } + } else { + rewind(dirfile); + int i = 2; + int count = 2; + while (1) { + fgets(buf, sizeof(buf), dirfile); + if (feof(dirfile)) break; + if (buf[0] == ASCII_SPC) { + if (i >= FileUtils::fileTypes[ftype].begin_row) { + menu += buf; + if (++count == virtual_rows) break; + } + i++; + } else { + char *pch = strstr(buf, FileUtils::fileTypes[ftype].fileSearch.c_str()); + if (pch != NULL) { + if (i >= FileUtils::fileTypes[ftype].begin_row) { + menu += buf; + if (++count == virtual_rows) break; + } + i++; + } + } + } } fd_PrintRow(1, IS_INFO); // Print status bar diff --git a/src/Snapshot.cpp b/src/Snapshot.cpp index a440105c..e64cca59 100644 --- a/src/Snapshot.cpp +++ b/src/Snapshot.cpp @@ -149,14 +149,20 @@ bool FileSNA::load(string sna_fn, string force_arch) { Config::SNA_Path = FileUtils::SNA_Path; Config::SNA_begin_row = FileUtils::fileTypes[DISK_SNAFILE].begin_row; Config::SNA_focus = FileUtils::fileTypes[DISK_SNAFILE].focus; + Config::SNA_fdMode = FileUtils::fileTypes[DISK_SNAFILE].fdMode; + Config::SNA_fileSearch = FileUtils::fileTypes[DISK_SNAFILE].fileSearch; Config::TAP_Path = FileUtils::TAP_Path; Config::TAP_begin_row = FileUtils::fileTypes[DISK_TAPFILE].begin_row; Config::TAP_focus = FileUtils::fileTypes[DISK_TAPFILE].focus; + Config::TAP_fdMode = FileUtils::fileTypes[DISK_TAPFILE].fdMode; + Config::TAP_fileSearch = FileUtils::fileTypes[DISK_TAPFILE].fileSearch; Config::DSK_Path = FileUtils::DSK_Path; Config::DSK_begin_row = FileUtils::fileTypes[DISK_DSKFILE].begin_row; Config::DSK_focus = FileUtils::fileTypes[DISK_DSKFILE].focus; + Config::DSK_fdMode = FileUtils::fileTypes[DISK_DSKFILE].fdMode; + Config::DSK_fileSearch = FileUtils::fileTypes[DISK_DSKFILE].fileSearch; Config::ram_file = sna_fn; Config::save(); @@ -179,14 +185,20 @@ bool FileSNA::load(string sna_fn, string force_arch) { Config::SNA_Path = FileUtils::SNA_Path; Config::SNA_begin_row = FileUtils::fileTypes[DISK_SNAFILE].begin_row; Config::SNA_focus = FileUtils::fileTypes[DISK_SNAFILE].focus; + Config::SNA_fdMode = FileUtils::fileTypes[DISK_SNAFILE].fdMode; + Config::SNA_fileSearch = FileUtils::fileTypes[DISK_SNAFILE].fileSearch; Config::TAP_Path = FileUtils::TAP_Path; Config::TAP_begin_row = FileUtils::fileTypes[DISK_TAPFILE].begin_row; Config::TAP_focus = FileUtils::fileTypes[DISK_TAPFILE].focus; + Config::TAP_fdMode = FileUtils::fileTypes[DISK_TAPFILE].fdMode; + Config::TAP_fileSearch = FileUtils::fileTypes[DISK_TAPFILE].fileSearch; Config::DSK_Path = FileUtils::DSK_Path; Config::DSK_begin_row = FileUtils::fileTypes[DISK_DSKFILE].begin_row; Config::DSK_focus = FileUtils::fileTypes[DISK_DSKFILE].focus; + Config::DSK_fdMode = FileUtils::fileTypes[DISK_DSKFILE].fdMode; + Config::DSK_fileSearch = FileUtils::fileTypes[DISK_DSKFILE].fileSearch; Config::ram_file = sna_fn; Config::save(); @@ -214,14 +226,20 @@ bool FileSNA::load(string sna_fn, string force_arch) { Config::SNA_Path = FileUtils::SNA_Path; Config::SNA_begin_row = FileUtils::fileTypes[DISK_SNAFILE].begin_row; Config::SNA_focus = FileUtils::fileTypes[DISK_SNAFILE].focus; + Config::SNA_fdMode = FileUtils::fileTypes[DISK_SNAFILE].fdMode; + Config::SNA_fileSearch = FileUtils::fileTypes[DISK_SNAFILE].fileSearch; Config::TAP_Path = FileUtils::TAP_Path; Config::TAP_begin_row = FileUtils::fileTypes[DISK_TAPFILE].begin_row; Config::TAP_focus = FileUtils::fileTypes[DISK_TAPFILE].focus; + Config::TAP_fdMode = FileUtils::fileTypes[DISK_TAPFILE].fdMode; + Config::TAP_fileSearch = FileUtils::fileTypes[DISK_TAPFILE].fileSearch; Config::DSK_Path = FileUtils::DSK_Path; Config::DSK_begin_row = FileUtils::fileTypes[DISK_DSKFILE].begin_row; Config::DSK_focus = FileUtils::fileTypes[DISK_DSKFILE].focus; + Config::DSK_fdMode = FileUtils::fileTypes[DISK_DSKFILE].fdMode; + Config::DSK_fileSearch = FileUtils::fileTypes[DISK_DSKFILE].fileSearch; Config::ram_file = sna_fn; Config::save(); @@ -617,14 +635,20 @@ bool FileZ80::load(string z80_fn) { Config::SNA_Path = FileUtils::SNA_Path; Config::SNA_begin_row = FileUtils::fileTypes[DISK_SNAFILE].begin_row; Config::SNA_focus = FileUtils::fileTypes[DISK_SNAFILE].focus; + Config::SNA_fdMode = FileUtils::fileTypes[DISK_SNAFILE].fdMode; + Config::SNA_fileSearch = FileUtils::fileTypes[DISK_SNAFILE].fileSearch; Config::TAP_Path = FileUtils::TAP_Path; Config::TAP_begin_row = FileUtils::fileTypes[DISK_TAPFILE].begin_row; Config::TAP_focus = FileUtils::fileTypes[DISK_TAPFILE].focus; + Config::TAP_fdMode = FileUtils::fileTypes[DISK_TAPFILE].fdMode; + Config::TAP_fileSearch = FileUtils::fileTypes[DISK_TAPFILE].fileSearch; Config::DSK_Path = FileUtils::DSK_Path; Config::DSK_begin_row = FileUtils::fileTypes[DISK_DSKFILE].begin_row; Config::DSK_focus = FileUtils::fileTypes[DISK_DSKFILE].focus; + Config::DSK_fdMode = FileUtils::fileTypes[DISK_DSKFILE].fdMode; + Config::DSK_fileSearch = FileUtils::fileTypes[DISK_DSKFILE].fileSearch; Config::ram_file = z80_fn; Config::save(); diff --git a/src/Video.cpp b/src/Video.cpp index 09014d1c..b5083a5f 100644 --- a/src/Video.cpp +++ b/src/Video.cpp @@ -437,31 +437,24 @@ IRAM_ATTR void VIDEO::TopBorder(unsigned int statestoadd, bool contended) { video_rest = statestoadd & 0x03; // Mod 4 - int loopCount = statestoadd >> 2; - - if (coldraw_cnt + loopCount > 39) { + unsigned int i = coldraw_cnt; - for (;;) { - + coldraw_cnt += statestoadd >> 2; + + if (coldraw_cnt > 39) { + + for (;i < 40;i++) { *lineptr32++ = brd; *lineptr32++ = brd; - - if (++coldraw_cnt > 39) { - Draw = ++linedraw_cnt == (is169 ? 4 : 24) ? &MainScreen_Blank : &TopBorder_Blank; - return; - } - } - } else { - - coldraw_cnt += loopCount; + Draw = ++linedraw_cnt == (is169 ? 4 : 24) ? &MainScreen_Blank : &TopBorder_Blank; - for (;loopCount > 0 ; loopCount--) { + } else { + for (;i < coldraw_cnt; i++) { *lineptr32++ = brd; *lineptr32++ = brd; - } } @@ -614,40 +607,42 @@ void VIDEO::MainScreen(unsigned int statestoadd, bool contended) { int loopCount = statestoadd >> 2; - if (loopCount == 0) return; + if (loopCount) { - if (coldraw_cnt + loopCount > 35) { + if (coldraw_cnt + loopCount > 35) { - for (;;) { + for (;;) { - att = grmem[attOffset++]; - bmp = (att & flashing) ? ~grmem[bmpOffset++] : grmem[bmpOffset++]; + att = grmem[attOffset++]; + bmp = (att & flashing) ? ~grmem[bmpOffset++] : grmem[bmpOffset++]; - *lineptr32++ = AluBytes[bmp >> 4][att]; - *lineptr32++ = AluBytes[bmp & 0xF][att]; + *lineptr32++ = AluBytes[bmp >> 4][att]; + *lineptr32++ = AluBytes[bmp & 0xF][att]; - loopCount--; + loopCount--; + + if (++coldraw_cnt > 35) { + Draw = MainScreenRB; + video_rest += loopCount << 2; + MainScreenRB(0,false); + return; + } - if (++coldraw_cnt > 35) { - Draw = MainScreenRB; - video_rest += loopCount << 2; - MainScreenRB(0,false); - return; } - } + } else { - } else { + coldraw_cnt += loopCount; - coldraw_cnt += loopCount; + for (;loopCount > 0 ; loopCount--) { - for (;loopCount > 0 ; loopCount--) { + att = grmem[attOffset++]; // get attribute byte + bmp = (att & flashing) ? ~grmem[bmpOffset++] : grmem[bmpOffset++]; - att = grmem[attOffset++]; // get attribute byte - bmp = (att & flashing) ? ~grmem[bmpOffset++] : grmem[bmpOffset++]; + *lineptr32++ = AluBytes[bmp >> 4][att]; + *lineptr32++ = AluBytes[bmp & 0xF][att]; - *lineptr32++ = AluBytes[bmp >> 4][att]; - *lineptr32++ = AluBytes[bmp & 0xF][att]; + } } @@ -668,41 +663,43 @@ void VIDEO::MainScreen_Pentagon(unsigned int statestoadd, bool contended) { int loopCount = statestoadd >> 2; - if (loopCount == 0) return; + if (loopCount) { - if (coldraw_cnt + loopCount > 35) { + if (coldraw_cnt + loopCount > 35) { - for (;;) { - - att = grmem[attOffset++]; - bmp = (att & flashing) ? ~grmem[bmpOffset++] : grmem[bmpOffset++]; + for (;;) { + + att = grmem[attOffset++]; + bmp = (att & flashing) ? ~grmem[bmpOffset++] : grmem[bmpOffset++]; - *lineptr32++ = AluBytes[bmp >> 4][att]; - *lineptr32++ = AluBytes[bmp & 0xF][att]; + *lineptr32++ = AluBytes[bmp >> 4][att]; + *lineptr32++ = AluBytes[bmp & 0xF][att]; - loopCount--; + loopCount--; + + if (++coldraw_cnt > 35) { + coldraw_cnt = 0; + Draw = MainScreen_Pentagon_delay; + video_rest += loopCount << 2; + MainScreen_Pentagon_delay(0,false); + return; + } - if (++coldraw_cnt > 35) { - coldraw_cnt = 0; - Draw = MainScreen_Pentagon_delay; - video_rest += loopCount << 2; - MainScreen_Pentagon_delay(0,false); - return; } - } + } else { - } else { + coldraw_cnt += loopCount; - coldraw_cnt += loopCount; + for (;loopCount > 0 ; loopCount--) { - for (;loopCount > 0 ; loopCount--) { + att = grmem[attOffset++]; + bmp = (att & flashing) ? ~grmem[bmpOffset++] : grmem[bmpOffset++]; - att = grmem[attOffset++]; - bmp = (att & flashing) ? ~grmem[bmpOffset++] : grmem[bmpOffset++]; + *lineptr32++ = AluBytes[bmp >> 4][att]; + *lineptr32++ = AluBytes[bmp & 0xF][att]; - *lineptr32++ = AluBytes[bmp >> 4][att]; - *lineptr32++ = AluBytes[bmp & 0xF][att]; + } } From 2710b7c2bc5b8a0c33fb11727e1f0445ba4caa53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Iborra?= Date: Tue, 12 Dec 2023 10:05:43 +0100 Subject: [PATCH 24/30] Language fix for fdSearch --- src/OSDFile.cpp | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/src/OSDFile.cpp b/src/OSDFile.cpp index 4cef5424..0bf0fb05 100644 --- a/src/OSDFile.cpp +++ b/src/OSDFile.cpp @@ -257,20 +257,6 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols if (FileUtils::fileTypes[ftype].fdMode) { - // menuAt(mfrows + (Config::aspect_16_9 ? 0 : 1), 1); - // VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(5, 0)); - // VIDEO::vga.print("Find: "); - // VIDEO::vga.print(FileUtils::fileTypes[ftype].fileSearch.c_str()); - // if (++fdCursorFlash > 63) { - // VIDEO::vga.setTextColor(zxColor(5, 0), zxColor(7, 1)); - // if (fdCursorFlash == 128) fdCursorFlash = 0; - // } - // VIDEO::vga.print("K"); - // VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(5, 0)); - // VIDEO::vga.print(std::string(10 - FileUtils::fileTypes[ftype].fileSearch.size(), ' ').c_str()); - - // if (fdSearchRefresh) { - // Recalc items number long prevpos = ftell(dirfile); @@ -308,8 +294,6 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols fdSearchRefresh = false; - // } - } else { // real_rows = (stat_buf.st_size / 64) + 2; // Add 2 for title and status bar @@ -439,7 +423,7 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols fdCursorFlash = 0; menuAt(mfrows + (Config::aspect_16_9 ? 0 : 1), 1); VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(5, 0)); - VIDEO::vga.print("Find: "); + VIDEO::vga.print(Config::lang ? "Busq: " : "Find: "); VIDEO::vga.print(FileUtils::fileTypes[ftype].fileSearch.c_str()); VIDEO::vga.print("K"); VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(5, 0)); @@ -634,7 +618,7 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols menuAt(mfrows + (Config::aspect_16_9 ? 0 : 1), 1); VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(5, 0)); - VIDEO::vga.print("Find: "); + VIDEO::vga.print(Config::lang ? "Busq: " : "Find: "); VIDEO::vga.print(FileUtils::fileTypes[ftype].fileSearch.c_str()); if (++fdCursorFlash > 63) { VIDEO::vga.setTextColor(zxColor(5, 0), zxColor(7, 1)); From 0da1665013a4cfae338532c537aeed06eabb76ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Iborra?= Date: Tue, 12 Dec 2023 14:14:03 +0100 Subject: [PATCH 25/30] Road to 1.0 --- src/OSDFile.cpp | 2 ++ src/OSDMain.cpp | 2 +- src/Snapshot.cpp | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/OSDFile.cpp b/src/OSDFile.cpp index 0bf0fb05..450f0bfd 100644 --- a/src/OSDFile.cpp +++ b/src/OSDFile.cpp @@ -303,6 +303,8 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols last_begin_row = last_focus = 0; + fdSearchElements = elements; + } // printf("Focus: %d, Begin_row: %d, real_rows: %d, mf_rows: %d\n",(int) FileUtils::fileTypes[ftype].focus,(int) FileUtils::fileTypes[ftype].begin_row,(int) real_rows, (int) mf_rows); diff --git a/src/OSDMain.cpp b/src/OSDMain.cpp index 179adadf..c327aa51 100644 --- a/src/OSDMain.cpp +++ b/src/OSDMain.cpp @@ -1379,7 +1379,7 @@ void OSD::osdCenteredMsg(string msg, uint8_t warn_level, uint16_t millispause) { unsigned short ink; unsigned int j; - if (msg.length() > (scrW / 6) - 4) msg = msg.substr(0,(scrW / 6) - 4); + if (msg.length() > (scrW / 6) - 10) msg = msg.substr(0,(scrW / 6) - 10); const unsigned short w = (msg.length() + 2) * OSD_FONT_W; const unsigned short x = scrAlignCenterX(w); diff --git a/src/Snapshot.cpp b/src/Snapshot.cpp index e64cca59..a015bab7 100644 --- a/src/Snapshot.cpp +++ b/src/Snapshot.cpp @@ -72,13 +72,13 @@ bool LoadSnapshot(string filename, string force_arch) { if (FileUtils::hasSNAextension(filename)) { - OSD::osdCenteredMsg(MSG_LOADING_SNA + (string) ": " + filename.substr(filename.find_last_of("/") + 1), LEVEL_INFO, 0); + // OSD::osdCenteredMsg(MSG_LOADING_SNA + (string) ": " + filename.substr(filename.find_last_of("/") + 1), LEVEL_INFO, 0); res = FileSNA::load(filename, force_arch); } else if (FileUtils::hasZ80extension(filename)) { - OSD::osdCenteredMsg(MSG_LOADING_Z80 + (string) ": " + filename.substr(filename.find_last_of("/") + 1), LEVEL_INFO, 0); + // OSD::osdCenteredMsg(MSG_LOADING_Z80 + (string) ": " + filename.substr(filename.find_last_of("/") + 1), LEVEL_INFO, 0); res = FileZ80::load(filename); From 89d8b86d87c6a05a1f3a353f11e4189a7416f7e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Iborra?= Date: Tue, 12 Dec 2023 14:29:07 +0100 Subject: [PATCH 26/30] Road to 1.0 --- include/messages.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/messages.h b/include/messages.h index a9ad3923..b4626613 100644 --- a/include/messages.h +++ b/include/messages.h @@ -42,7 +42,7 @@ visit https://zxespectrum.speccy.org/contacto #define MSG_SAVE_CONFIG "Saving config file" #define MSG_VGA_INIT "Initializing VGA" // #define EMU_VERSION " v1.0 " -#define EMU_VERSION " Dev 061223 " +#define EMU_VERSION " Dev 121223 " // Error #define ERROR_TITLE " !!! ERROR - CLIVE MEDITATION !!! " From b646d8852a4a9cbc75db6d2deb9ca93c373de776 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Iborra?= Date: Tue, 12 Dec 2023 17:54:02 +0100 Subject: [PATCH 27/30] Add search key to ZXKbd --- src/OSDFile.cpp | 1 - src/ZXKeyb.cpp | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OSDFile.cpp b/src/OSDFile.cpp index 450f0bfd..1232797c 100644 --- a/src/OSDFile.cpp +++ b/src/OSDFile.cpp @@ -34,7 +34,6 @@ visit https://zxespectrum.speccy.org/contacto */ #include -#include #include #include #include "errno.h" diff --git a/src/ZXKeyb.cpp b/src/ZXKeyb.cpp index e1052832..253cb10d 100644 --- a/src/ZXKeyb.cpp +++ b/src/ZXKeyb.cpp @@ -151,6 +151,7 @@ void ZXKeyb::ZXKbdRead() { else if (!bitRead(ZXcols[4], 2)) injectKey = fabgl::VK_RIGHT; // 8 -> PGDOWN else if (!bitRead(ZXcols[1], 1)) injectKey = fabgl::VK_PRINTSCREEN; // S -> PRINTSCREEN else if (!bitRead(ZXcols[5], 0)) injectKey = fabgl::VK_PAUSE; // P -> PAUSE + else if ((!bitRead(ZXcols[7], 4)) || (!bitRead(ZXcols[1], 3))) injectKey = fabgl::VK_F3; // F,B -> FIND / BUSQUEDA } else { From d6e8357fee7eb9a66dad04ca21c63bebe93807bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Iborra?= Date: Wed, 13 Dec 2023 06:17:05 +0100 Subject: [PATCH 28/30] fileDialog search case insensitive --- src/OSDFile.cpp | 61 +++++++++++++++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 22 deletions(-) diff --git a/src/OSDFile.cpp b/src/OSDFile.cpp index 1232797c..3cdb3bf3 100644 --- a/src/OSDFile.cpp +++ b/src/OSDFile.cpp @@ -158,7 +158,7 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols // Read dir hash from file // fseek(dirfile, (stat_buf.st_size >> 5) << 5,SEEK_SET); - fseek(dirfile, (dirfilesize >> 5) << 5,SEEK_SET); + fseek(dirfile, (dirfilesize >> 6) << 6,SEEK_SET); char fhash[32]; fgets(fhash, sizeof(fhash), dirfile); @@ -263,6 +263,9 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols fdSearchElements = 0; rewind(dirfile); char buf[128]; + char upperbuf[128]; + string search = FileUtils::fileTypes[ftype].fileSearch; + transform(search.begin(), search.end(), search.begin(), ::toupper); while(1) { fgets(buf, sizeof(buf), dirfile); if (feof(dirfile)) break; @@ -270,7 +273,8 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols foundcount++; // printf("%s",buf); }else { - char *pch = strstr(buf, FileUtils::fileTypes[ftype].fileSearch.c_str()); + for(int i=0;i 63) { - VIDEO::vga.setTextColor(zxColor(5, 0), zxColor(7, 1)); - if (fdCursorFlash == 128) fdCursorFlash = 0; + if ((++fdCursorFlash & 0x15) == 0) { + + menuAt(mfrows + (Config::aspect_16_9 ? 0 : 1), 1); + VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(5, 0)); + VIDEO::vga.print(Config::lang ? "Busq: " : "Find: "); + VIDEO::vga.print(FileUtils::fileTypes[ftype].fileSearch.c_str()); + if (fdCursorFlash > 63) { + VIDEO::vga.setTextColor(zxColor(5, 0), zxColor(7, 1)); + if (fdCursorFlash == 128) fdCursorFlash = 0; + } + VIDEO::vga.print("K"); + VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(5, 0)); + VIDEO::vga.print(std::string(10 - FileUtils::fileTypes[ftype].fileSearch.size(), ' ').c_str()); } - VIDEO::vga.print("K"); - VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(5, 0)); - VIDEO::vga.print(std::string(10 - FileUtils::fileTypes[ftype].fileSearch.size(), ' ').c_str()); if (fdSearchRefresh) { @@ -638,6 +647,9 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols fdSearchElements = 0; rewind(dirfile); char buf[128]; + char upperbuf[128]; + string search = FileUtils::fileTypes[ftype].fileSearch; + transform(search.begin(), search.end(), search.begin(), ::toupper); while(1) { fgets(buf, sizeof(buf), dirfile); if (feof(dirfile)) break; @@ -645,7 +657,8 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols foundcount++; // printf("%s",buf); }else { - char *pch = strstr(buf, FileUtils::fileTypes[ftype].fileSearch.c_str()); + for(int i=0;i= FileUtils::fileTypes[ftype].begin_row) { menu += buf; From 6352d7f3401273e276580731bac4023569fcc377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Iborra?= Date: Fri, 29 Dec 2023 11:51:23 +0100 Subject: [PATCH 29/30] 1.0 ready! New joystick system and more --- ESPecpart2.csv | 5 +- .../fabgl/src/devdrivers/kbdlayouts.cpp | 29 + components/fabgl/src/devdrivers/kbdlayouts.h | 1 + .../fabgl/src/devdrivers/kbjoystick.cpp | 39 + components/fabgl/src/devdrivers/kbjoystick.h | 1 + components/fabgl/src/devdrivers/keyboard.cpp | 36 + components/fabgl/src/devdrivers/keyboard.h | 1 + components/fabgl/src/fabutils.h | 38 + include/Config.h | 22 +- include/ESPectrum.h | 1 + include/OSDMain.h | 6 +- include/Video.h | 95 +- include/messages.h | 132 ++- src/Config.cpp | 224 ++++- src/ESPectrum.cpp | 426 +++++--- src/OSDFile.cpp | 20 +- src/OSDMain.cpp | 914 +++++++++++++++++- src/OSDMenu.cpp | 213 +++- src/Ports.cpp | 5 +- src/Video.cpp | 41 +- 20 files changed, 1918 insertions(+), 331 deletions(-) diff --git a/ESPecpart2.csv b/ESPecpart2.csv index fff8818b..cc65f9b7 100644 --- a/ESPecpart2.csv +++ b/ESPecpart2.csv @@ -1,6 +1,7 @@ # Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, 0x9000, 0x5000, +# nvs, data, nvs, 0x9000, 0x5000, otadata, data, ota, 0xe000, 0x2000, 48k, app, ota_0, 0x10000, 0xC0000, 128k, app, ota_1, 0xD0000, 0xC0000, -spiffs, data, spiffs, 0x190000,0x270000, +nvs, data, nvs, 0x190000, 0xC000, +spiffs, data, spiffs, 0x19C000,0x264000, diff --git a/components/fabgl/src/devdrivers/kbdlayouts.cpp b/components/fabgl/src/devdrivers/kbdlayouts.cpp index 72325446..9a72c6b5 100644 --- a/components/fabgl/src/devdrivers/kbdlayouts.cpp +++ b/components/fabgl/src/devdrivers/kbdlayouts.cpp @@ -258,6 +258,35 @@ const KeyboardLayout USLayout { // deadkeys translation { }, + + // extended joy scancodes (0xE2..) + { + { 0x40, VK_JOY1LEFT }, + { 0x41, VK_JOY1RIGHT }, + { 0x42, VK_JOY1UP }, + { 0x43, VK_JOY1DOWN }, + { 0x44, VK_JOY1START }, + { 0x45, VK_JOY1MODE }, + { 0x46, VK_JOY1A }, + { 0x47, VK_JOY1B }, + { 0x48, VK_JOY1C }, + { 0x49, VK_JOY1X }, + { 0x4A, VK_JOY1Y }, + { 0x4B, VK_JOY1Z }, + { 0x4C, VK_JOY2LEFT }, + { 0x4D, VK_JOY2RIGHT }, + { 0x4E, VK_JOY2UP }, + { 0x4F, VK_JOY2DOWN }, + { 0x50, VK_JOY2START }, + { 0x51, VK_JOY2MODE }, + { 0x52, VK_JOY2A }, + { 0x53, VK_JOY2B }, + { 0x54, VK_JOY2C }, + { 0x55, VK_JOY2X }, + { 0x56, VK_JOY2Y }, + { 0x57, VK_JOY2Z }, + }, + }; diff --git a/components/fabgl/src/devdrivers/kbdlayouts.h b/components/fabgl/src/devdrivers/kbdlayouts.h index 12c0369c..265c7c90 100644 --- a/components/fabgl/src/devdrivers/kbdlayouts.h +++ b/components/fabgl/src/devdrivers/kbdlayouts.h @@ -89,6 +89,7 @@ struct KeyboardLayout { VirtualKey deadKeysVK[8]; /**< Dead keys identifiers. */ DeadKeyVirtualKeyDef deadkeysToVK[60]; /**< Translation dead key + virtual key = replaced virtual key */ + VirtualKeyDef exJoyScancodeToVK[24]; /**< Direct extended-joy-scancode->virtualkey associations. Extended joy scancodes begin with 0xE1. */ }; diff --git a/components/fabgl/src/devdrivers/kbjoystick.cpp b/components/fabgl/src/devdrivers/kbjoystick.cpp index 8b479109..cee43c84 100644 --- a/components/fabgl/src/devdrivers/kbjoystick.cpp +++ b/components/fabgl/src/devdrivers/kbjoystick.cpp @@ -335,6 +335,28 @@ VirtualKey KeybJoystick::scancodeToVK(uint8_t scancode, bool isExtended, Keyboar return vk; } +VirtualKey KeybJoystick::scancodeTojoyVK(uint8_t scancode, KeyboardLayout const * layout) +{ + VirtualKey vk = VK_NONE; + + if (layout == nullptr) + layout = m_layout; + + VirtualKeyDef const * def = layout->exJoyScancodeToVK; + for (; def->scancode; ++def) + if (def->scancode == scancode) { + vk = def->virtualKey; + break; + } + + if (vk == VK_NONE && layout->inherited) + vk = scancodeTojoyVK(scancode, layout->inherited); + + // printf("VK: %d\n",vk); + + return vk; + +} VirtualKey KeybJoystick::manageCAPSLOCK(VirtualKey vk) { @@ -410,7 +432,9 @@ bool KeybJoystick::blockingGetVirtualKey(VirtualKeyItem * item) uint8_t * scode = item->scancode; *scode = getNextScancode(); + // printf("Scode: %x\n",*scode); if (*scode == 0xE0) { + // printf(" E0 Scode: %x\n",*scode); // two bytes scancode *(++scode) = getNextScancode(100, true); if (*scode == 0xF0) { @@ -432,6 +456,21 @@ bool KeybJoystick::blockingGetVirtualKey(VirtualKeyItem * item) else if (i == sizeof(PAUSECODES) - 1) item->vk = VK_PAUSE; } + } else if (*scode == 0xE2) { + // printf(" E2 Scode: %x\n",*scode); + // two bytes joy scancode + *(++scode) = getNextScancode(100, true); + // printf(" E2 Scode: %x\n",*scode); + if (*scode == 0xF0) { + // two bytes scancode key up + *(++scode) = getNextScancode(100, true); + // printf(" E2 Scode: %x\n",*scode); + item->vk = scancodeTojoyVK(*scode); + item->down = false; + } else { + // two bytes scancode key down + item->vk = scancodeTojoyVK(*scode); + } } else if (*scode == 0xF0) { // one byte scancode, key up *(++scode) = getNextScancode(100, true); diff --git a/components/fabgl/src/devdrivers/kbjoystick.h b/components/fabgl/src/devdrivers/kbjoystick.h index f2feee8b..6a814ffd 100644 --- a/components/fabgl/src/devdrivers/kbjoystick.h +++ b/components/fabgl/src/devdrivers/kbjoystick.h @@ -403,6 +403,7 @@ class KeybJoystick : public PS2Device { private: VirtualKey scancodeToVK(uint8_t scancode, bool isExtended, KeyboardLayout const * layout = nullptr); + VirtualKey scancodeTojoyVK(uint8_t scancode, KeyboardLayout const * layout = nullptr); VirtualKey VKtoAlternateVK(VirtualKey in_vk, bool down, KeyboardLayout const * layout = nullptr); VirtualKey manageCAPSLOCK(VirtualKey vk); void updateLEDs(); diff --git a/components/fabgl/src/devdrivers/keyboard.cpp b/components/fabgl/src/devdrivers/keyboard.cpp index 40a88a82..941710e9 100644 --- a/components/fabgl/src/devdrivers/keyboard.cpp +++ b/components/fabgl/src/devdrivers/keyboard.cpp @@ -341,6 +341,27 @@ VirtualKey Keyboard::scancodeToVK(uint8_t scancode, bool isExtended, KeyboardLay } +VirtualKey Keyboard::scancodeTojoyVK(uint8_t scancode, KeyboardLayout const * layout) +{ + VirtualKey vk = VK_NONE; + + if (layout == nullptr) + layout = m_layout; + + VirtualKeyDef const * def = layout->exJoyScancodeToVK; + for (; def->scancode; ++def) + if (def->scancode == scancode) { + vk = def->virtualKey; + break; + } + + if (vk == VK_NONE && layout->inherited) + vk = scancodeTojoyVK(scancode, layout->inherited); + + return vk; + +} + VirtualKey Keyboard::manageCAPSLOCK(VirtualKey vk) { if (m_CAPSLOCK) { @@ -415,8 +436,10 @@ bool Keyboard::blockingGetVirtualKey(VirtualKeyItem * item) uint8_t * scode = item->scancode; *scode = getNextScancode(); + // printf("Scode: %x\n",*scode); if (*scode == 0xE0) { // two bytes scancode + // printf(" E0 Scode: %x\n",*scode); *(++scode) = getNextScancode(100, true); if (*scode == 0xF0) { // two bytes scancode key up @@ -437,6 +460,19 @@ bool Keyboard::blockingGetVirtualKey(VirtualKeyItem * item) else if (i == sizeof(PAUSECODES) - 1) item->vk = VK_PAUSE; } + } else if (*scode == 0xE2) { + // printf(" E2 Scode: %x\n",*scode); + // two bytes joy scancode + *(++scode) = getNextScancode(100, true); + if (*scode == 0xF0) { + // two bytes scancode key up + *(++scode) = getNextScancode(100, true); + item->vk = scancodeTojoyVK(*scode); + item->down = false; + } else { + // two bytes scancode key down + item->vk = scancodeTojoyVK(*scode); + } } else if (*scode == 0xF0) { // one byte scancode, key up *(++scode) = getNextScancode(100, true); diff --git a/components/fabgl/src/devdrivers/keyboard.h b/components/fabgl/src/devdrivers/keyboard.h index 49033db8..9ab9beec 100644 --- a/components/fabgl/src/devdrivers/keyboard.h +++ b/components/fabgl/src/devdrivers/keyboard.h @@ -403,6 +403,7 @@ class Keyboard : public PS2Device { private: VirtualKey scancodeToVK(uint8_t scancode, bool isExtended, KeyboardLayout const * layout = nullptr); + VirtualKey scancodeTojoyVK(uint8_t scancode, KeyboardLayout const * layout = nullptr); VirtualKey VKtoAlternateVK(VirtualKey in_vk, bool down, KeyboardLayout const * layout = nullptr); VirtualKey manageCAPSLOCK(VirtualKey vk); void updateLEDs(); diff --git a/components/fabgl/src/fabutils.h b/components/fabgl/src/fabutils.h index 0d8c7d9a..e9d7bb39 100644 --- a/components/fabgl/src/fabutils.h +++ b/components/fabgl/src/fabutils.h @@ -1491,6 +1491,44 @@ enum VirtualKey { VK_HANKAKU_ZENKAKU_KANJI, VK_SHIFT_0, + VK_JOY1LEFT, + VK_JOY1RIGHT, + VK_JOY1UP, + VK_JOY1DOWN, + VK_JOY1START, + VK_JOY1MODE, + VK_JOY1A, + VK_JOY1B, + VK_JOY1C, + VK_JOY1X, + VK_JOY1Y, + VK_JOY1Z, + VK_JOY2LEFT, + VK_JOY2RIGHT, + VK_JOY2UP, + VK_JOY2DOWN, + VK_JOY2START, + VK_JOY2MODE, + VK_JOY2A, + VK_JOY2B, + VK_JOY2C, + VK_JOY2X, + VK_JOY2Y, + VK_JOY2Z, + + VK_KEMPSTON_RIGHT, + VK_KEMPSTON_LEFT, + VK_KEMPSTON_DOWN, + VK_KEMPSTON_UP, + VK_KEMPSTON_FIRE, + VK_KEMPSTON_ALTFIRE, + + VK_FULLER_RIGHT, + VK_FULLER_LEFT, + VK_FULLER_DOWN, + VK_FULLER_UP, + VK_FULLER_FIRE, + VK_ASCII, /**< Specifies an ASCII code - used when virtual key is embedded in VirtualKeyItem structure and VirtualKeyItem.ASCII is valid */ VK_LAST, // marks the last virtual key diff --git a/include/Config.h b/include/Config.h index c8f84685..65ced419 100644 --- a/include/Config.h +++ b/include/Config.h @@ -44,6 +44,21 @@ using namespace std; // #include "esp_attr.h" +#define JOY_CURSOR 0 +#define JOY_KEMPSTON 1 +#define JOY_SINCLAIR1 2 +#define JOY_SINCLAIR2 3 +#define JOY_FULLER 4 +#define JOY_CUSTOM 5 +#define JOY_NONE 6 + +#define JOYPS2_CURSOR 0 +#define JOYPS2_KEMPSTON 1 +#define JOYPS2_SINCLAIR1 2 +#define JOYPS2_SINCLAIR2 3 +#define JOYPS2_FULLER 4 +#define JOYPS2_CUSTOM 5 +#define JOYPS2_NONE 6 class Config { public: @@ -51,6 +66,8 @@ class Config // machine type change request static void requestMachine(string newArch, string newRomSet); + static void setJoyMap(uint8_t joynum, uint8_t joy_type); + static const string archnames[3]; // config variables @@ -65,7 +82,10 @@ class Config static bool AY48; static bool Issue2; static bool flashload; - static uint8_t joystick; + static uint8_t joystick1; + static uint8_t joystick2; + static uint16_t joydef[24]; + static uint8_t joyPS2; static uint8_t videomode; static uint8_t AluTiming; static uint8_t ps2_dev2; diff --git a/include/ESPectrum.h b/include/ESPectrum.h index 4cfb623e..fb8e9451 100644 --- a/include/ESPectrum.h +++ b/include/ESPectrum.h @@ -73,6 +73,7 @@ class ESPectrum static bool readKbd(fabgl::VirtualKeyItem *Nextkey); static void readKbdJoy(); static fabgl::PS2Controller PS2Controller; + static fabgl::VirtualKey JoyVKTranslation[24]; // Audio static uint8_t audioBuffer[ESP_AUDIO_SAMPLES_PENTAGON]; diff --git a/include/OSDMain.h b/include/OSDMain.h index 3b10c951..73f11b32 100644 --- a/include/OSDMain.h +++ b/include/OSDMain.h @@ -66,8 +66,6 @@ using namespace std; class OSD { public: - // ZX Color - static uint16_t zxColor(uint8_t color, uint8_t bright); // Screen size to be set at initialization static unsigned short scrW; @@ -86,7 +84,7 @@ class OSD { static void osdAt(uint8_t row, uint8_t col); static void drawOSD(bool bottom_info); static void drawStats(); - static void do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL); + static void do_OSD(fabgl::VirtualKey KeytoESP, bool CTRL); static void HWInfo(); // Error @@ -102,6 +100,7 @@ class OSD { static void menuRedraw(); static void WindowDraw(); static unsigned short menuRun(string new_menu); + static unsigned short simpleMenuRun(string new_menu, uint16_t posx, uint16_t posy); static string fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols, uint8_t mfrows); static int menuTape(string title); static void menuScroll(bool up); @@ -126,6 +125,7 @@ class OSD { static uint8_t msgDialog(string title, string msg); static void progressDialog(string title, string msg, int percent, int action); string inputBox(int x, int y, string text); + static void joyDialog(uint8_t joynum); // Rows static unsigned short rowCount(string menu); diff --git a/include/Video.h b/include/Video.h index 6d95eb20..fc53160c 100644 --- a/include/Video.h +++ b/include/Video.h @@ -57,6 +57,37 @@ visit https://zxespectrum.speccy.org/contacto // ( ADDITIONAL -2 SEEMS NEEDED IF NOT USING 2 TSTATES AT A TIME PAPER DRAWING VERSION) #define TS_SCREEN_360x200_PENTAGON 17074 // START OF VISIBLE ULA DRAW PENTAGON @ 360x200, SCANLINE 76, +48TS + 2TS (NEEDED TO FIT BORDER) +// Colors for 6 bit mode +// // BB GGRR +#define BLACK 0xC0 // 1100 0000 +#define BLUE 0xE0 // 1110 0000 +#define RED 0xC2 // 1100 0010 +#define MAGENTA 0xE2 // 1110 0010 +#define GREEN 0xC8 // 1100 1000 +#define CYAN 0xE8 // 1110 1000 +#define YELLOW 0xCA // 1100 1010 +#define WHITE 0xEA // 1110 1010 +#define BRI_BLACK 0xC0 // 1100 0000 +#define BRI_BLUE 0xF0 // 1111 0000 +#define BRI_RED 0xC3 // 1100 0011 +#define BRI_MAGENTA 0xF3 // 1111 0011 +#define BRI_GREEN 0xCC // 1100 1100 +#define BRI_CYAN 0xFC // 1111 1100 +#define BRI_YELLOW 0xCF // 1100 1111 +#define BRI_WHITE 0xFF // 1111 1111 + +// used in ESPectrum logo text +#define ESP_ORANGE 0xC7 // 1100 0111 + +#define NUM_SPECTRUM_COLORS 16 + +static uint16_t spectrum_colors[NUM_SPECTRUM_COLORS] = { + BLACK, BLUE, RED, MAGENTA, GREEN, CYAN, YELLOW, WHITE, + BRI_BLACK, BRI_BLUE, BRI_RED, BRI_MAGENTA, BRI_GREEN, BRI_CYAN, BRI_YELLOW, BRI_WHITE, +}; + +#define zxColor(color,bright) spectrum_colors[bright ? color + 8 : color] + class VIDEO { public: @@ -107,9 +138,9 @@ class VIDEO static uint8_t getFloatBusData48(); static uint8_t getFloatBusData128(); - static void (*Draw)(unsigned int, bool contended); - static void (*DrawOSD43)(unsigned int, bool contended); - static void (*DrawOSD169)(unsigned int, bool contended); + static void (*Draw)(unsigned int, bool); + static void (*DrawOSD43)(unsigned int, bool); + static void (*DrawOSD169)(unsigned int, bool); static void vgataskinit(void *unused); @@ -124,9 +155,6 @@ class VIDEO static uint8_t tStatesPerLine; static int tStatesScreen; - // static unsigned int tstateDraw; // Drawing start point (in Tstates) - // static unsigned int linedraw_cnt; - static uint8_t flashing; static uint8_t flash_ctr; @@ -147,59 +175,4 @@ class VIDEO }; -static unsigned int is169; - -static uint16_t offBmp[SPEC_H]; -static uint16_t offAtt[SPEC_H]; - -// Colors for 6 bit mode -// // BB GGRR -#define BLACK 0xC0 // 1100 0000 -#define BLUE 0xE0 // 1110 0000 -#define RED 0xC2 // 1100 0010 -#define MAGENTA 0xE2 // 1110 0010 -#define GREEN 0xC8 // 1100 1000 -#define CYAN 0xE8 // 1110 1000 -#define YELLOW 0xCA // 1100 1010 -#define WHITE 0xEA // 1110 1010 -#define BRI_BLACK 0xC0 // 1100 0000 -#define BRI_BLUE 0xF0 // 1111 0000 -#define BRI_RED 0xC3 // 1100 0011 -#define BRI_MAGENTA 0xF3 // 1111 0011 -#define BRI_GREEN 0xCC // 1100 1100 -#define BRI_CYAN 0xFC // 1111 1100 -#define BRI_YELLOW 0xCF // 1100 1111 -#define BRI_WHITE 0xFF // 1111 1111 - -// used in ESPectrum logo text -#define ESP_ORANGE 0xC7 // 1100 0111 - -#define NUM_SPECTRUM_COLORS 16 -static uint16_t spectrum_colors[NUM_SPECTRUM_COLORS] = { - BLACK, BLUE, RED, MAGENTA, GREEN, CYAN, YELLOW, WHITE, - BRI_BLACK, BRI_BLUE, BRI_RED, BRI_MAGENTA, BRI_GREEN, BRI_CYAN, BRI_YELLOW, BRI_WHITE, -}; - -// uint16_t zxColor(uint8_t color, uint8_t bright); - -static uint32_t* AluBytes[16]; - -// static unsigned char DrawStatus; - -static uint32_t* lineptr32; -static uint16_t* lineptr16; - -static unsigned int tstateDraw; // Drawing start point (in Tstates) -static unsigned int linedraw_cnt; -static unsigned int lin_end; -// static unsigned int mainscrline_cnt; -static unsigned int coldraw_cnt; -static unsigned int col_end; -static unsigned int video_rest; - -static unsigned int bmpOffset; // offset for bitmap in graphic memory -static unsigned int attOffset; // offset for attrib in graphic memory - -void precalcAluBytes(); - #endif // VIDEO_h diff --git a/include/messages.h b/include/messages.h index b4626613..7025ae37 100644 --- a/include/messages.h +++ b/include/messages.h @@ -41,8 +41,8 @@ visit https://zxespectrum.speccy.org/contacto #define MSG_LOADING_Z80 "Loading Z80 file" #define MSG_SAVE_CONFIG "Saving config file" #define MSG_VGA_INIT "Initializing VGA" -// #define EMU_VERSION " v1.0 " -#define EMU_VERSION " Dev 121223 " + +#define EMU_VERSION " v1.0 " // Error #define ERROR_TITLE " !!! ERROR - CLIVE MEDITATION !!! " @@ -119,6 +119,18 @@ static const char *OSD_FIRMW_UPDATE[2] = { OSD_FIRMW_UPDATE_EN,OSD_FIRMW_UPDATE_ #define OSD_DLG_SURE_ES "\xA8" "Desea continuar?" static const char *OSD_DLG_SURE[2] = { OSD_DLG_SURE_EN, OSD_DLG_SURE_ES}; +#define OSD_DLG_JOYSAVE_EN "Save changes?" +#define OSD_DLG_JOYSAVE_ES "\xA8" "Guardar cambios?" +static const char *OSD_DLG_JOYSAVE[2] = { OSD_DLG_JOYSAVE_EN, OSD_DLG_JOYSAVE_ES}; + +#define OSD_DLG_JOYDISCARD_EN "Discard changes?" +#define OSD_DLG_JOYDISCARD_ES "\xA8" "Descartar cambios?" +static const char *OSD_DLG_JOYDISCARD[2] = { OSD_DLG_JOYDISCARD_EN, OSD_DLG_JOYDISCARD_ES}; + +#define OSD_DLG_SETJOYMAPDEFAULTS_EN "Load joy type default map?" +#define OSD_DLG_SETJOYMAPDEFAULTS_ES "\xA8" "Cargar mapeo por defecto?" +static const char *OSD_DLG_SETJOYMAPDEFAULTS[2] = { OSD_DLG_SETJOYMAPDEFAULTS_EN, OSD_DLG_SETJOYMAPDEFAULTS_ES}; + #define OSD_FIRMW_EN "Updating firmware" #define OSD_FIRMW_ES "Actualizando firmware" static const char *OSD_FIRMW[2] = { OSD_FIRMW_EN,OSD_FIRMW_ES}; @@ -227,6 +239,7 @@ static const char *MENU_MAIN[2] = { MENU_MAIN_EN,MENU_MAIN_ES }; "Storage\t>\n"\ "Machine\t>\n"\ "Aspect ratio\t>\n"\ + "Joystick\t>\n"\ "PS/2 Joystick\t>\n"\ "Language\t>\n"\ "Other\t>\n"\ @@ -236,6 +249,7 @@ static const char *MENU_MAIN[2] = { MENU_MAIN_EN,MENU_MAIN_ES }; "Almacenamiento\t>\n"\ "Modelo\t>\n"\ "Rel. aspecto\t>\n"\ + "Joystick\t>\n"\ "Joystick PS/2\t>\n"\ "Idioma\t>\n"\ "Otros\t>\n"\ @@ -376,15 +390,49 @@ static const char *MENU_ARCH[2] = { MENU_ARCH_EN, MENU_ARCH_ES }; "Espanol\t[ ]\n" static const char *MENU_INTERFACE_LANG[2] = { MENU_INTERFACE_LANG_EN, MENU_INTERFACE_LANG_ES }; -#define MENU_JOY_EN "PS/2 Joystick\n"\ +#define MENU_JOY_EN "Joystick menu\n"\ + "Joystick 1\n"\ + "Joystick 2\n" +#define MENU_JOY_ES "Menu Joystick\n"\ + "Joystick 1\n"\ + "Joystick 2\n" +static const char *MENU_JOY[2] = { MENU_JOY_EN, MENU_JOY_ES }; + +#define MENU_DEFJOY_EN "Joystick#\n"\ + "Cursor\t[ ]\n"\ + "Kempston\t[ ]\n"\ + "Sinclair 1\t[ ]\n"\ + "Sinclair 2\t[ ]\n"\ + "Fuller\t[ ]\n"\ + "Assign keys\n" + // "Load map\n"\ + // "Save map\n" +#define MENU_DEFJOY_ES "Joystick#\n"\ + "Cursor\t[ ]\n"\ + "Kempston\t[ ]\n"\ + "Sinclair 1\t[ ]\n"\ + "Sinclair 2\t[ ]\n"\ + "Fuller\t[ ]\n"\ + "Definir\n" + // "Cargar mapa\n"\ + // "Guardar mapa\n" +static const char *MENU_DEFJOY[2] = { MENU_DEFJOY_EN, MENU_DEFJOY_ES }; + +#define MENU_JOYPS2_EN "PS/2 Joystick\n"\ "Cursor\t[ ]\n"\ "Kempston\t[ ]\n"\ + "Sinclair 1\t[ ]\n"\ + "Sinclair 2\t[ ]\n"\ + "Fuller\t[ ]\n"\ "Cursor Keys as Joy\t>\n" -#define MENU_JOY_ES "Joystick PS/2\n"\ +#define MENU_JOYPS2_ES "Joystick PS/2\n"\ "Cursor\t[ ]\n"\ "Kempston\t[ ]\n"\ + "Sinclair 1\t[ ]\n"\ + "Sinclair 2\t[ ]\n"\ + "Fuller\t[ ]\n"\ "Joy en teclas de cursor\t>\n" -static const char *MENU_JOY[2] = { MENU_JOY_EN, MENU_JOY_ES }; +static const char *MENU_JOYPS2[2] = { MENU_JOYPS2_EN, MENU_JOYPS2_ES }; #define MENU_CURSORJOY_EN "Cursor as Joy\n"\ "Yes\t[Y]\n"\ @@ -415,40 +463,26 @@ static const char *MENU_CURSORJOY[2] = { MENU_CURSORJOY_EN, MENU_CURSORJOY_ES }; "\r"\ "\nA1The Jet Set Willys:\r"\ "\r"\ - "\nD1DopierRex \nE1Igor Peruchi \nB1Inacio Santos\r"\ - "\r"\ + "\nD1DopierRex \nE1Eduard Ruiz \nC1Igor Peruchi\r"\ + "\nB1Inacio Santos\r"\ "\r"\ "\r"\ "\r" - // Lencio Asimov - // Fernando Bonilla - // Elena Collantes - // Jorge Garcia - // kounch - // Ignacio Monge - // Javi Ortiz - // Jordi Ramos - // Jose Maria Rodriguez - // Santiago Romero - // Julia Salvador - // Marta Sicilia - // Radek Wojciechowski - #define PATREONS2 "\r"\ "\nA1The Manic Miners:\r"\ "\r"\ "\nE1Lencio Asimov \nB1Fernando Bonilla\r"\ - "\nC1Elena Collantes \nD1Jorge Garcia\r"\ - "\nB1kounch \nC1Ignacio Monge \nD1Javi Ortiz\r"\ + "\nC1Elena Collantes \nD1Fidel Fernandez\r"\ + "\nE1Jorge Garcia \nC1Jose Luis Garcia\r"\ + "\nB1kounch \nD1Ignacio Monge \nE1Serafin Moraton\r"\ + "\nB1Javi Ortiz \nC1Miguel Angel Perez\r"\ "\nD1Jordi Ramos \nE1Jose Maria Rodriguez\r"\ - "\nB1Santiago Romero \nC1Julia Salvador\r"\ - "\nD1Marta Sicilia \nE1Radek Wojciechowski\r"\ - "\r"\ - "\r"\ - "\r" + "\nB1Marco A. Rodriguez \nC1Santiago Romero\r"\ + "\nD1Julia Salvador \nE1Marta Sicilia\r"\ + "\nB1Radek Wojciechowski\r" -static const char *AboutMsg[2][6] = { +static const char *AboutMsg[2][7] = { { "\nF1(C)2023 Victor Iborra \"Eremus\"\r"\ " David Crespo \"dcrespo3d\"\r"\ @@ -475,7 +509,7 @@ static const char *AboutMsg[2][6] = { "\nB1J. L. Sanchez \nF1Z80 core improvements\r"\ "\nC1Antonio Villena \nF1Hardware support\r"\ "\nD1ZjoyKiLer \nF1Testing & ideas\r"\ - "\r" + "\r"\ "\r" , "\nF1Big thanks to our Patreons:\r"\ @@ -484,19 +518,33 @@ static const char *AboutMsg[2][6] = { "\nF1Big thanks to our Patreons:\r"\ PATREONS2 , + "\nF1Thanks for help and donations to:\r"\ + "\r"\ + "\nA1Abel Bayon @Baycorps \nF1Amstrad Eterno\r"\ + "\nB1Pablo Forcen Soler \nF1AUA\r"\ + "\nC1Jordi Ramos Montes\r" + "\nD1Tsvetan Usunov \nF1Olimex Ltd.\r"\ + "\r"\ + "\r"\ + "\r"\ + "\r"\ + "\r"\ + "\r"\ + "\r" + , "\nF1Thanks also to:\r"\ "\r"\ "\nA1Retrowiki.es \nF1and its great community\r"\ "\nB1Ron \nF1for his cool RetroCrypta\r"\ "\nC1Viejoven FX\nF1, \nD1J.Ortiz \"El Spectrumero\"\r" "\nE1J.C. Gonzalez Amestoy \nF1for RVM\r"\ - "\nA1Tsvetan Usunov from Olimex Ltd.\r"\ "\nB1All creators in ZX Spectrum server at\r"\ "Discord\r"\ "\r"\ + "\r"\ "\nF1and, of course, to:\r"\ "\r"\ - "\nD1Sir Clive Sinclair \nF1& \nA1M\nE1a\nC1t\nD1t\nB1h\nA1e\nE1w \nC1S\nD1m\nB1i\nA1t\nE1h\r"\ + "\nD1Sir Clive Sinclair \nF1& \nA1M\nE1a\nC1t\nD1t\nB1h\nA1e\nE1w \nC1S\nD1m\nB1i\nA1t\nE1h\r" , DEDICATORIA }, @@ -526,7 +574,7 @@ static const char *AboutMsg[2][6] = { "\nB1J. L. Sanchez \nF1Mejoras core Z80\r"\ "\nC1Antonio Villena \nF1Soporte hardware\r"\ "\nD1ZjoyKiLer \nF1Testing e ideas\r"\ - "\r" + "\r"\ "\r" , "\nF1Muchas gracias a nuestros Patreons:\r"\ @@ -535,19 +583,33 @@ static const char *AboutMsg[2][6] = { "\nF1Muchas gracias a nuestros Patreons:\r"\ PATREONS2 , + "\nF1Gracias por su ayuda y donaciones a:\r"\ + "\r"\ + "\nA1Abel Bayon @Baycorps \nF1Amstrad Eterno\r"\ + "\nB1Pablo Forcen Soler \nF1AUA\r"\ + "\nC1Jordi Ramos Montes\r" + "\nD1Tsvetan Usunov \nF1Olimex Ltd.\r"\ + "\r"\ + "\r"\ + "\r"\ + "\r"\ + "\r"\ + "\r"\ + "\r" + , "\nF1Gracias tambien a:\r"\ "\r"\ "\nA1Retrowiki.es \nF1y su magnifica comunidad\r"\ "\nB1Ron \nF1por su genial RetroCrypta\r"\ "\nC1Viejoven FX\nF1, \nD1J.Ortiz \"El Spectrumero\"\r" "\nE1J.C. Gonzalez Amestoy \nF1por RVM\r"\ - "\nA1Tsvetan Usunov de Olimex Ltd.\r"\ "\nB1Todos los creadores en el servidor\r"\ "ZX Spectrum en Discord\r"\ "\r"\ + "\r"\ "\nF1y, por supuesto, a:\r"\ "\r"\ - "\nD1Sir Clive Sinclair \nF1& \nA1M\nE1a\nC1t\nD1t\nB1h\nA1e\nE1w \nC1S\nD1m\nB1i\nA1t\nE1h\r"\ + "\nD1Sir Clive Sinclair \nF1& \nA1M\nE1a\nC1t\nD1t\nB1h\nA1e\nE1w \nC1S\nD1m\nB1i\nA1t\nE1h\r" , DEDICATORIA } diff --git a/src/Config.cpp b/src/Config.cpp index 2dc6a62f..0d0481fb 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -51,6 +51,7 @@ visit https://zxespectrum.speccy.org/contacto #include "ESPectrum.h" #include "pwm_audio.h" #include "roms.h" +#include "OSDMain.h" const string Config::archnames[3] = { "48K", "128K", "Pentagon"}; string Config::arch = "48K"; @@ -65,7 +66,37 @@ uint8_t Config::lang = 0; bool Config::AY48 = true; bool Config::Issue2 = true; bool Config::flashload = true; -uint8_t Config::joystick = 1; // 0 -> Cursor, 1 -> Kempston + +uint8_t Config::joystick1 = JOY_SINCLAIR1; +uint8_t Config::joystick2 = JOY_SINCLAIR2; +uint16_t Config::joydef[24] = { + fabgl::VK_6, + fabgl::VK_7, + fabgl::VK_9, + fabgl::VK_8, + fabgl::VK_NONE, + fabgl::VK_NONE, + fabgl::VK_0, + fabgl::VK_NONE, + fabgl::VK_NONE, + fabgl::VK_NONE, + fabgl::VK_NONE, + fabgl::VK_NONE, + fabgl::VK_1, + fabgl::VK_2, + fabgl::VK_4, + fabgl::VK_3, + fabgl::VK_NONE, + fabgl::VK_NONE, + fabgl::VK_5, + fabgl::VK_NONE, + fabgl::VK_NONE, + fabgl::VK_NONE, + fabgl::VK_NONE, + fabgl::VK_NONE +}; + +uint8_t Config::joyPS2 = JOY_KEMPSTON; uint8_t Config::AluTiming = 0; uint8_t Config::ps2_dev2 = 0; // Second port PS/2 device: 0 -> None, 1 -> PS/2 keyboard, 2 -> PS/2 Mouse (TO DO) bool Config::CursorAsJoy = false; @@ -99,25 +130,25 @@ static inline void erase_cntrl(std::string &s) { s.end()); } -// trim from start (in place) -static inline void ltrim(std::string &s) { - s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { - return !std::isspace(ch); - })); -} - -// trim from end (in place) -static inline void rtrim(std::string &s) { - s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { - return !std::isspace(ch); - }).base(), s.end()); -} - -// trim from both ends (in place) -static inline void trim(std::string &s) { - rtrim(s); - ltrim(s); -} +// // trim from start (in place) +// static inline void ltrim(std::string &s) { +// s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { +// return !std::isspace(ch); +// })); +// } + +// // trim from end (in place) +// static inline void rtrim(std::string &s) { +// s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { +// return !std::isspace(ch); +// }).base(), s.end()); +// } + +// // trim from both ends (in place) +// static inline void trim(std::string &s) { +// rtrim(s); +// ltrim(s); +// } // Read config from FS void Config::load() { @@ -252,9 +283,30 @@ void Config::load() { free(str_data); } - err = nvs_get_u8(handle, "joystick", &Config::joystick); + err = nvs_get_u8(handle, "joystick1", &Config::joystick1); + if (err == ESP_OK) { + // printf("joystick1:%u\n",Config::joystick1); + } + + err = nvs_get_u8(handle, "joystick2", &Config::joystick2); + if (err == ESP_OK) { + // printf("joystick2:%u\n",Config::joystick2); + } + + // Read joystick definition + for (int n=0; n < 24; n++) { + char joykey[9]; + sprintf(joykey,"joydef%02u",n); + // printf("%s\n",joykey); + err = nvs_get_u16(handle, joykey, &Config::joydef[n]); + if (err == ESP_OK) { + // printf("joydef00:%u\n",Config::joydef[n]); + } + } + + err = nvs_get_u8(handle, "joyPS2", &Config::joyPS2); if (err == ESP_OK) { - // printf("joystick:%u\n",Config::joystick); + // printf("joyPS2:%u\n",Config::joyPS2); } err = nvs_get_u8(handle, "AluTiming", &Config::AluTiming); @@ -452,8 +504,24 @@ void Config::save(string value) { if((value=="flashload") || (value=="all")) nvs_set_str(handle,"flashload",flashload ? "true" : "false"); - if((value=="joystick") || (value=="all")) - nvs_set_u8(handle,"joystick",Config::joystick); + if((value=="joystick1") || (value=="all")) + nvs_set_u8(handle,"joystick1",Config::joystick1); + + if((value=="joystick2") || (value=="all")) + nvs_set_u8(handle,"joystick2",Config::joystick2); + + // Write joystick definition + for (int n=0; n < 24; n++) { + char joykey[9]; + sprintf(joykey,"joydef%02u",n); + if((value == joykey) || (value=="all")) { + nvs_set_u16(handle,joykey,Config::joydef[n]); + // printf("%s %u\n",joykey, joydef[n]); + } + } + + if((value=="joyPS2") || (value=="all")) + nvs_set_u8(handle,"joyPS2",Config::joyPS2); if((value=="AluTiming") || (value=="all")) nvs_set_u8(handle,"AluTiming",Config::AluTiming); @@ -567,3 +635,111 @@ void Config::requestMachine(string newArch, string newRomSet) MemESP::ramContended[3] = false; } + +// fabgl::VK_FULLER_LEFT, // Left +// fabgl::VK_FULLER_RIGHT, // Right +// fabgl::VK_FULLER_UP, // Up +// fabgl::VK_FULLER_DOWN, // Down +// fabgl::VK_S, // Start +// fabgl::VK_M, // Mode +// fabgl::VK_FULLER_FIRE, // A +// fabgl::VK_9, // B +// fabgl::VK_SPACE, // C +// fabgl::VK_X, // X +// fabgl::VK_Y, // Y +// fabgl::VK_Z, // Z + +void Config::setJoyMap(uint8_t joynum, uint8_t joytype) { + +fabgl::VirtualKey newJoy[12]; + +for (int n=0; n < 12; n++) newJoy[n] = fabgl::VK_NONE; + +// Ask to overwrite map with default joytype values +string title = (joynum == 1 ? "Joystick 1" : "Joystick 2"); +string msg = OSD_DLG_SETJOYMAPDEFAULTS[Config::lang]; +uint8_t res = OSD::msgDialog(title,msg); +if (res == DLG_YES) { + + switch (joytype) { + case JOY_CURSOR: + newJoy[0] = fabgl::VK_5; + newJoy[1] = fabgl::VK_8; + newJoy[2] = fabgl::VK_7; + newJoy[3] = fabgl::VK_6; + newJoy[6] = fabgl::VK_0; + break; + case JOY_KEMPSTON: + newJoy[0] = fabgl::VK_KEMPSTON_LEFT; + newJoy[1] = fabgl::VK_KEMPSTON_RIGHT; + newJoy[2] = fabgl::VK_KEMPSTON_UP; + newJoy[3] = fabgl::VK_KEMPSTON_DOWN; + newJoy[6] = fabgl::VK_KEMPSTON_FIRE; + newJoy[7] = fabgl::VK_KEMPSTON_ALTFIRE; + break; + case JOY_SINCLAIR1: + newJoy[0] = fabgl::VK_6; + newJoy[1] = fabgl::VK_7; + newJoy[2] = fabgl::VK_9; + newJoy[3] = fabgl::VK_8; + newJoy[6] = fabgl::VK_0; + break; + case JOY_SINCLAIR2: + newJoy[0] = fabgl::VK_1; + newJoy[1] = fabgl::VK_2; + newJoy[2] = fabgl::VK_4; + newJoy[3] = fabgl::VK_3; + newJoy[6] = fabgl::VK_5; + break; + case JOY_FULLER: + newJoy[0] = fabgl::VK_FULLER_LEFT; + newJoy[1] = fabgl::VK_FULLER_RIGHT; + newJoy[2] = fabgl::VK_FULLER_UP; + newJoy[3] = fabgl::VK_FULLER_DOWN; + newJoy[6] = fabgl::VK_FULLER_FIRE; + break; + } + +} + +// Fill joystick values in Config and clean Kempston or Fuller values if needed +int m = (joynum == 1) ? 0 : 12; + +for (int n = m; n < m + 12; n++) { + + bool save = false; + if (newJoy[n - m] != fabgl::VK_NONE) { + ESPectrum::JoyVKTranslation[n] = newJoy[n - m]; + save = true; + } else { + + if (joytype != JOY_KEMPSTON) { + if (ESPectrum::JoyVKTranslation[n] >= fabgl::VK_KEMPSTON_RIGHT && ESPectrum::JoyVKTranslation[n] <= fabgl::VK_KEMPSTON_ALTFIRE) { + ESPectrum::JoyVKTranslation[n] = fabgl::VK_NONE; + save = true; + } + } + + if (joytype != JOY_FULLER) { + if (ESPectrum::JoyVKTranslation[n] >= fabgl::VK_FULLER_RIGHT && ESPectrum::JoyVKTranslation[n] <= fabgl::VK_FULLER_FIRE) { + ESPectrum::JoyVKTranslation[n] = fabgl::VK_NONE; + save = true; + } + } + + } + + if (save) { + // Save to config (only changes) + if (Config::joydef[n] != (uint16_t) ESPectrum::JoyVKTranslation[n]) { + Config::joydef[n] = (uint16_t) ESPectrum::JoyVKTranslation[n]; + char joykey[9]; + sprintf(joykey,"joydef%02u",n); + Config::save(joykey); + // printf("%s %u\n",joykey, joydef[n]); + } + } + +} + +} diff --git a/src/ESPectrum.cpp b/src/ESPectrum.cpp index 01ca6300..6744aefd 100644 --- a/src/ESPectrum.cpp +++ b/src/ESPectrum.cpp @@ -226,6 +226,8 @@ void ESPectrum::bootKeyboard() { int i = 0; string s = "00"; + // printf("Boot kbd!\n"); + for (; i < 200; i++) { if (ZXKeyb::Exists) { @@ -290,6 +292,8 @@ void ESPectrum::bootKeyboard() { } + // printf("Boot kbd end!\n"); + if (i < 200) { Config::videomode = (s[0] == '1') ? 0 : (s[0] == '2') ? 1 : 2; Config::aspect_16_9 = (s[1] == 'Q') ? false : true; @@ -518,7 +522,12 @@ void ESPectrum::setup() // Set Ports starting values for (int i = 0; i < 128; i++) Ports::port[i] = 0xBF; - if (Config::joystick) Ports::port[0x1f] = 0; // Kempston + if (Config::joystick1 == JOY_KEMPSTON || Config::joystick2 == JOY_KEMPSTON || Config::joyPS2 == JOYPS2_KEMPSTON) Ports::port[0x1f] = 0; // Kempston + if (Config::joystick1 == JOY_FULLER || Config::joystick2 == JOY_FULLER || Config::joyPS2 == JOYPS2_FULLER) Ports::port[0x7f] = 0xff; // Fuller + + // Read joystick default definition + for (int n = 0; n < 24; n++) + ESPectrum::JoyVKTranslation[n] = (fabgl::VirtualKey) Config::joydef[n]; // Init disk controller Betadisk.Init(); @@ -575,7 +584,12 @@ void ESPectrum::reset() // Ports for (int i = 0; i < 128; i++) Ports::port[i] = 0xBF; - if (Config::joystick) Ports::port[0x1f] = 0; // Kempston + if (Config::joystick1 == JOY_KEMPSTON || Config::joystick2 == JOY_KEMPSTON || Config::joyPS2 == JOYPS2_KEMPSTON) Ports::port[0x1f] = 0; // Kempston + if (Config::joystick1 == JOY_FULLER || Config::joystick2 == JOY_FULLER || Config::joyPS2 == JOYPS2_FULLER) Ports::port[0x7f] = 0xff; // Fuller + + // Read joystick default definition + for (int n = 0; n < 24; n++) + ESPectrum::JoyVKTranslation[n] = (fabgl::VirtualKey) Config::joydef[n]; // Memory MemESP::bankLatch = 0; @@ -601,8 +615,9 @@ void ESPectrum::reset() VIDEO::Reset(); // Reinit disk controller - Betadisk.ShutDown(); - Betadisk.Init(); + // Betadisk.ShutDown(); + // Betadisk.Init(); + Betadisk.EnterIdle(); Tape::tapeFileName = "none"; if (Tape::tape != NULL) { @@ -715,7 +730,22 @@ IRAM_ATTR void ESPectrum::readKbdJoy() { } +fabgl::VirtualKey ESPectrum::JoyVKTranslation[24]; +// fabgl::VK_FULLER_LEFT, // Left +// fabgl::VK_FULLER_RIGHT, // Right +// fabgl::VK_FULLER_UP, // Up +// fabgl::VK_FULLER_DOWN, // Down +// fabgl::VK_S, // Start +// fabgl::VK_M, // Mode +// fabgl::VK_FULLER_FIRE, // A +// fabgl::VK_9, // B +// fabgl::VK_SPACE, // C +// fabgl::VK_X, // X +// fabgl::VK_Y, // Y +// fabgl::VK_Z, // Z + IRAM_ATTR void ESPectrum::processKeyboard() { +// void ESPectrum::processKeyboard() { static uint8_t PS2cols[8] = { 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf }; static int zxDelay = 0; @@ -724,11 +754,17 @@ IRAM_ATTR void ESPectrum::processKeyboard() { fabgl::VirtualKey KeytoESP; bool Kdown; bool r = false; - bool jLeft = true; - bool jRight = true; - bool jUp = true; - bool jDown = true; - bool jFire = true; + bool j[10] = { true, true, true, true, true, true, true, true, true, true }; + // bool j1 = true; + // bool j2 = true; + // bool j3 = true; + // bool j4 = true; + // bool j5 = true; + // bool j6 = true; + // bool j7 = true; + // bool j8 = true; + // bool j9 = true; + // bool j0 = true; bool jShift = true; readKbdJoy(); @@ -742,9 +778,15 @@ IRAM_ATTR void ESPectrum::processKeyboard() { KeytoESP = NextKey.vk; Kdown = NextKey.down; + if (KeytoESP >= fabgl::VK_JOY1LEFT && KeytoESP <= fabgl::VK_JOY2Z) { + // printf("KeytoESP: %d\n",KeytoESP); + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(JoyVKTranslation[KeytoESP - 248], Kdown, false); + continue; + } + if ((Kdown) && ((KeytoESP >= fabgl::VK_F1 && KeytoESP <= fabgl::VK_F12) || KeytoESP == fabgl::VK_PAUSE)) { - OSD::do_OSD(KeytoESP,NextKey.CTRL); + OSD::do_OSD(KeytoESP, Kbd->isVKDown(fabgl::VK_LCTRL) || Kbd->isVKDown(fabgl::VK_RCTRL)); Kbd->emptyVirtualKeyQueue(); @@ -760,160 +802,289 @@ IRAM_ATTR void ESPectrum::processKeyboard() { } - if (Config::CursorAsJoy) { - - // Kempston Joystick emulation - if (Config::joystick) { + if (Config::joystick1 == JOY_KEMPSTON || Config::joystick2 == JOY_KEMPSTON || Config::joyPS2 == JOYPS2_KEMPSTON) Ports::port[0x1f] = 0; + if (Config::joystick1 == JOY_FULLER || Config::joystick2 == JOY_FULLER || Config::joyPS2 == JOYPS2_FULLER) Ports::port[0x7f] = 0xff; - Ports::port[0x1f] = 0; + if (Config::joystick1 == JOY_KEMPSTON || Config::joystick2 == JOY_KEMPSTON) { - jShift = !(Kbd->isVKDown(fabgl::VK_LSHIFT) || Kbd->isVKDown(fabgl::VK_RSHIFT)); + for (int i = fabgl::VK_KEMPSTON_RIGHT; i <= fabgl::VK_KEMPSTON_ALTFIRE; i++) + if (Kbd->isVKDown((fabgl::VirtualKey) i)) + bitWrite(Ports::port[0x1f], i - fabgl::VK_KEMPSTON_RIGHT, 1); - if (Kbd->isVKDown(fabgl::VK_RIGHT) || Kbd->isVKDown(fabgl::VK_KP_RIGHT)) { - jRight = jShift; - bitWrite(Ports::port[0x1f], 0, 1); - } + } - if (Kbd->isVKDown(fabgl::VK_LEFT) || Kbd->isVKDown(fabgl::VK_KP_LEFT)) { - jLeft = jShift; - bitWrite(Ports::port[0x1f], 1, 1); - } + if (Config::joystick1 == JOY_FULLER || Config::joystick2 == JOY_FULLER) { - if (Kbd->isVKDown(fabgl::VK_DOWN) || Kbd->isVKDown(fabgl::VK_KP_DOWN) || Kbd->isVKDown(fabgl::VK_KP_CENTER)) { - jDown = jShift; - bitWrite(Ports::port[0x1f], 2, 1); - } + // Fuller + if (Kbd->isVKDown(fabgl::VK_FULLER_RIGHT)) { + bitWrite(Ports::port[0x7f], 3, 0); + } - if (Kbd->isVKDown(fabgl::VK_UP) || Kbd->isVKDown(fabgl::VK_KP_UP)) { - jUp = jShift; - bitWrite(Ports::port[0x1f], 3, 1); - } + if (Kbd->isVKDown(fabgl::VK_FULLER_LEFT)) { + bitWrite(Ports::port[0x7f], 2, 0); + } - if (Kbd->isVKDown(fabgl::VK_RALT)) { - jFire = jShift; - bitWrite(Ports::port[0x1f], 4, 1); - } + if (Kbd->isVKDown(fabgl::VK_FULLER_DOWN)) { + bitWrite(Ports::port[0x7f], 1, 0); + } - if (Kbd->isVKDown(fabgl::VK_SLASH) || Kbd->isVKDown(fabgl::VK_RGUI)) { - bitWrite(Ports::port[0x1f], 5, 1); - } + if (Kbd->isVKDown(fabgl::VK_FULLER_UP)) { + bitWrite(Ports::port[0x7f], 0, 0); + } + if (Kbd->isVKDown(fabgl::VK_FULLER_FIRE)) { + bitWrite(Ports::port[0x7f], 7, 0); } - bitWrite(PS2cols[3], 4, (!Kbd->isVKDown(fabgl::VK_5)) & (!Kbd->isVKDown(fabgl::VK_PERCENT)) - & ((Config::joystick) | (!Kbd->isVKDown(fabgl::VK_LEFT) & !Kbd->isVKDown(fabgl::VK_KP_LEFT))) - & (jLeft) - ); // Cursor joystick Left - bitWrite(PS2cols[4], 0, (!Kbd->isVKDown(fabgl::VK_0)) & (!Kbd->isVKDown(fabgl::VK_RIGHTPAREN)) - & (!Kbd->isVKDown(fabgl::VK_BACKSPACE)) - & ((Config::joystick) | (!Kbd->isVKDown(fabgl::VK_RALT))) - & (jFire) - ); // Cursor joystick Fire - bitWrite(PS2cols[4], 2, (!Kbd->isVKDown(fabgl::VK_8)) & (!Kbd->isVKDown(fabgl::VK_ASTERISK)) - & ((Config::joystick) | (!Kbd->isVKDown(fabgl::VK_RIGHT) & !Kbd->isVKDown(fabgl::VK_KP_RIGHT))) - & (jRight) - ); // Cursor joystick Right - bitWrite(PS2cols[4], 3, (!Kbd->isVKDown(fabgl::VK_7)) & (!Kbd->isVKDown(fabgl::VK_AMPERSAND)) - & ((Config::joystick) | (!Kbd->isVKDown(fabgl::VK_UP) & !Kbd->isVKDown(fabgl::VK_KP_UP))) - & (jUp) - ); // Cursor joystick Up - bitWrite(PS2cols[4], 4, (!Kbd->isVKDown(fabgl::VK_6)) & (!Kbd->isVKDown(fabgl::VK_CARET)) - & ((Config::joystick) | (!Kbd->isVKDown(fabgl::VK_DOWN) & !Kbd->isVKDown(fabgl::VK_KP_DOWN) & !Kbd->isVKDown(fabgl::VK_KP_CENTER))) - & (jDown) - ); // Cursor joystick Down + } - } else { + jShift = !(Kbd->isVKDown(fabgl::VK_LSHIFT) || Kbd->isVKDown(fabgl::VK_RSHIFT)); + + if (Config::CursorAsJoy) { // Kempston Joystick emulation - if (Config::joystick) { + if (Config::joyPS2 == JOYPS2_KEMPSTON) { + + if (Kbd->isVKDown(fabgl::VK_RIGHT)) { + j[8] = jShift; + bitWrite(Ports::port[0x1f], 0, j[8]); + } + + if (Kbd->isVKDown(fabgl::VK_LEFT)) { + j[5] = jShift; + bitWrite(Ports::port[0x1f], 1, j[5]); + } + + if (Kbd->isVKDown(fabgl::VK_DOWN)) { + j[6] = jShift; + bitWrite(Ports::port[0x1f], 2, j[6]); + } - // printf("VK: %d\n",(int)KeytoESP); + if (Kbd->isVKDown(fabgl::VK_UP)) { + j[7] = jShift; + bitWrite(Ports::port[0x1f], 3, j[7]); + } - Ports::port[0x1f] = 0; + // Fuller Joystick emulation + } else if (Config::joyPS2 == JOYPS2_FULLER) { - if (Kbd->isVKDown(fabgl::VK_KP_RIGHT)) { - bitWrite(Ports::port[0x1f], 0, 1); + if (Kbd->isVKDown(fabgl::VK_RIGHT)) { + j[8] = jShift; + bitWrite(Ports::port[0x7f], 3, !j[8]); } - if (Kbd->isVKDown(fabgl::VK_KP_LEFT)) { - bitWrite(Ports::port[0x1f], 1, 1); + if (Kbd->isVKDown(fabgl::VK_LEFT)) { + j[5] = jShift; + bitWrite(Ports::port[0x7f], 2, !j[5]); } - if (Kbd->isVKDown(fabgl::VK_KP_DOWN) || Kbd->isVKDown(fabgl::VK_KP_CENTER)) { - bitWrite(Ports::port[0x1f], 2, 1); + if (Kbd->isVKDown(fabgl::VK_DOWN)) { + j[6] = jShift; + bitWrite(Ports::port[0x7f], 1, !j[6]); } - if (Kbd->isVKDown(fabgl::VK_KP_UP)) { - bitWrite(Ports::port[0x1f], 3, 1); + if (Kbd->isVKDown(fabgl::VK_UP)) { + j[7] = jShift; + bitWrite(Ports::port[0x7f], 0, !j[7]); } - if (Kbd->isVKDown(fabgl::VK_RALT)) { - bitWrite(Ports::port[0x1f], 4, 1); + } else if (Config::joyPS2 == JOYPS2_CURSOR) { + + j[5] = !Kbd->isVKDown(fabgl::VK_LEFT); + j[8] = !Kbd->isVKDown(fabgl::VK_RIGHT); + j[7] = !Kbd->isVKDown(fabgl::VK_UP); + j[6] = !Kbd->isVKDown(fabgl::VK_DOWN); + + } else if (Config::joyPS2 == JOYPS2_SINCLAIR1) { // Right Sinclair + + if (jShift) { + j[9] = !Kbd->isVKDown(fabgl::VK_UP); + j[8] = !Kbd->isVKDown(fabgl::VK_DOWN); + j[7] = !Kbd->isVKDown(fabgl::VK_RIGHT); + j[6] = !Kbd->isVKDown(fabgl::VK_LEFT); + } else { + j[5] = !Kbd->isVKDown(fabgl::VK_LEFT); + j[8] = !Kbd->isVKDown(fabgl::VK_RIGHT); + j[7] = !Kbd->isVKDown(fabgl::VK_UP); + j[6] = !Kbd->isVKDown(fabgl::VK_DOWN); } - if (Kbd->isVKDown(fabgl::VK_SLASH) || Kbd->isVKDown(fabgl::VK_RGUI)) { - bitWrite(Ports::port[0x1f], 5, 1); + } else if (Config::joyPS2 == JOYPS2_SINCLAIR2) { // Left Sinclair + + if (jShift) { + j[4] = !Kbd->isVKDown(fabgl::VK_UP); + j[3] = !Kbd->isVKDown(fabgl::VK_DOWN); + j[2] = !Kbd->isVKDown(fabgl::VK_RIGHT); + j[1] = !Kbd->isVKDown(fabgl::VK_LEFT); + } else { + j[5] = !Kbd->isVKDown(fabgl::VK_LEFT); + j[8] = !Kbd->isVKDown(fabgl::VK_RIGHT); + j[7] = !Kbd->isVKDown(fabgl::VK_UP); + j[6] = !Kbd->isVKDown(fabgl::VK_DOWN); } } + } else { + // Cursor Keys if (Kbd->isVKDown(fabgl::VK_RIGHT)) { jShift = false; - jRight = jShift; + j[8] = jShift; } if (Kbd->isVKDown(fabgl::VK_LEFT)) { jShift = false; - jLeft = jShift; + j[5] = jShift; } if (Kbd->isVKDown(fabgl::VK_DOWN)) { jShift = false; - jDown = jShift; + j[6] = jShift; } if (Kbd->isVKDown(fabgl::VK_UP)) { jShift = false; - jUp = jShift; + j[7] = jShift; + } + + } + + // Keypad PS/2 Joystick emulation + if (Config::joyPS2 == JOYPS2_KEMPSTON) { + + if (Kbd->isVKDown(fabgl::VK_KP_RIGHT)) { + bitWrite(Ports::port[0x1f], 0, 1); } + if (Kbd->isVKDown(fabgl::VK_KP_LEFT)) { + bitWrite(Ports::port[0x1f], 1, 1); + } + + if (Kbd->isVKDown(fabgl::VK_KP_DOWN) || Kbd->isVKDown(fabgl::VK_KP_CENTER)) { + bitWrite(Ports::port[0x1f], 2, 1); + } - bitWrite(PS2cols[3], 4, (!Kbd->isVKDown(fabgl::VK_5)) & (!Kbd->isVKDown(fabgl::VK_PERCENT)) - & ((Config::joystick) | (!Kbd->isVKDown(fabgl::VK_KP_LEFT))) - & (jLeft) - ); // Cursor joystick Left + if (Kbd->isVKDown(fabgl::VK_KP_UP)) { + bitWrite(Ports::port[0x1f], 3, 1); + } - bitWrite(PS2cols[4], 0, (!Kbd->isVKDown(fabgl::VK_0)) & (!Kbd->isVKDown(fabgl::VK_RIGHTPAREN)) - & (!Kbd->isVKDown(fabgl::VK_BACKSPACE)) - & ((Config::joystick) | (!Kbd->isVKDown(fabgl::VK_RALT))) - ); // Cursor joystick Fire + if (Kbd->isVKDown(fabgl::VK_RALT)) { + bitWrite(Ports::port[0x1f], 4, 1); + } - bitWrite(PS2cols[4], 2, (!Kbd->isVKDown(fabgl::VK_8)) & (!Kbd->isVKDown(fabgl::VK_ASTERISK)) - & ((Config::joystick) | (!Kbd->isVKDown(fabgl::VK_KP_RIGHT))) - & (jRight) - ); // Cursor joystick Right + if (Kbd->isVKDown(fabgl::VK_SLASH) || Kbd->isVKDown(fabgl::VK_QUESTION) || Kbd->isVKDown(fabgl::VK_RGUI) || Kbd->isVKDown(fabgl::VK_APPLICATION) ) { + bitWrite(Ports::port[0x1f], 5, 1); + } - bitWrite(PS2cols[4], 3, (!Kbd->isVKDown(fabgl::VK_7)) & (!Kbd->isVKDown(fabgl::VK_AMPERSAND)) - & ((Config::joystick) | (!Kbd->isVKDown(fabgl::VK_KP_UP))) - & (jUp) - ); // Cursor joystick Up + } else if (Config::joyPS2 == JOYPS2_FULLER) { - bitWrite(PS2cols[4], 4, (!Kbd->isVKDown(fabgl::VK_6)) & (!Kbd->isVKDown(fabgl::VK_CARET)) - & ((Config::joystick) | (!Kbd->isVKDown(fabgl::VK_KP_DOWN) & !Kbd->isVKDown(fabgl::VK_KP_CENTER)) ) - & (jDown) - ); // Cursor joystick Down + if (Kbd->isVKDown(fabgl::VK_KP_RIGHT)) { + bitWrite(Ports::port[0x7f], 3, 0); + } + + if (Kbd->isVKDown(fabgl::VK_KP_LEFT)) { + bitWrite(Ports::port[0x7f], 2, 0); + } + + if (Kbd->isVKDown(fabgl::VK_KP_DOWN) || Kbd->isVKDown(fabgl::VK_KP_CENTER)) { + bitWrite(Ports::port[0x7f], 1, 0); + } + + if (Kbd->isVKDown(fabgl::VK_KP_UP)) { + bitWrite(Ports::port[0x7f], 0, 0); + } + + if (Kbd->isVKDown(fabgl::VK_RALT)) { + bitWrite(Ports::port[0x7f], 7, 0); + } + + } else if (Config::joyPS2 == JOYPS2_CURSOR) { + + if (Kbd->isVKDown(fabgl::VK_KP_LEFT)) { + jShift = true; + j[5] = false; + }; + + if (Kbd->isVKDown(fabgl::VK_KP_RIGHT)) { + jShift = true; + j[8] = false; + }; + + if (Kbd->isVKDown(fabgl::VK_KP_UP)) { + jShift = true; + j[7] = false; + }; + + if (Kbd->isVKDown(fabgl::VK_KP_DOWN) || Kbd->isVKDown(fabgl::VK_KP_CENTER)) { + jShift = true; + j[6] = false; + }; + + if (Kbd->isVKDown(fabgl::VK_RALT)) { + jShift = true; + j[0] = false; + }; + + } else if (Config::joyPS2 == JOYPS2_SINCLAIR1) { // Right Sinclair + + if (Kbd->isVKDown(fabgl::VK_KP_LEFT)) { + jShift = true; + j[6] = false; + }; + + if (Kbd->isVKDown(fabgl::VK_KP_RIGHT)) { + jShift = true; + j[7] = false; + }; + + if (Kbd->isVKDown(fabgl::VK_KP_UP)) { + jShift = true; + j[9] = false; + }; + + if (Kbd->isVKDown(fabgl::VK_KP_DOWN) || Kbd->isVKDown(fabgl::VK_KP_CENTER)) { + jShift = true; + j[8] = false; + }; + + if (Kbd->isVKDown(fabgl::VK_RALT)) { + jShift = true; + j[0] = false; + }; + + } else if (Config::joyPS2 == JOYPS2_SINCLAIR2) { // Left Sinclair + + if (Kbd->isVKDown(fabgl::VK_KP_LEFT)) { + jShift = true; + j[1] = false; + }; + + if (Kbd->isVKDown(fabgl::VK_KP_RIGHT)) { + jShift = true; + j[2] = false; + }; + + if (Kbd->isVKDown(fabgl::VK_KP_UP)) { + jShift = true; + j[4] = false; + }; + + if (Kbd->isVKDown(fabgl::VK_KP_DOWN) || Kbd->isVKDown(fabgl::VK_KP_CENTER)) { + jShift = true; + j[3] = false; + }; + + if (Kbd->isVKDown(fabgl::VK_RALT)) { + jShift = true; + j[5] = false; + }; } // Check keyboard status and map it to Spectrum Ports - bitWrite(PS2cols[0], 0, (!Kbd->isVKDown(fabgl::VK_LSHIFT)) - & (!Kbd->isVKDown(fabgl::VK_RSHIFT)) - & (!Kbd->isVKDown(fabgl::VK_BACKSPACE)) // Backspace - & (jShift) - ); // CAPS SHIFT - + bitWrite(PS2cols[0], 0, (jShift) & (!Kbd->isVKDown(fabgl::VK_BACKSPACE))); // CAPS SHIFT bitWrite(PS2cols[0], 1, (!Kbd->isVKDown(fabgl::VK_Z)) & (!Kbd->isVKDown(fabgl::VK_z))); bitWrite(PS2cols[0], 2, (!Kbd->isVKDown(fabgl::VK_X)) & (!Kbd->isVKDown(fabgl::VK_x))); bitWrite(PS2cols[0], 3, (!Kbd->isVKDown(fabgl::VK_C)) & (!Kbd->isVKDown(fabgl::VK_c))); @@ -931,38 +1102,17 @@ IRAM_ATTR void ESPectrum::processKeyboard() { bitWrite(PS2cols[2], 3, (!Kbd->isVKDown(fabgl::VK_R)) & (!Kbd->isVKDown(fabgl::VK_r))); bitWrite(PS2cols[2], 4, (!Kbd->isVKDown(fabgl::VK_T)) & (!Kbd->isVKDown(fabgl::VK_t))); - bitWrite(PS2cols[3], 0, (!Kbd->isVKDown(fabgl::VK_1)) & (!Kbd->isVKDown(fabgl::VK_EXCLAIM))); - bitWrite(PS2cols[3], 1, (!Kbd->isVKDown(fabgl::VK_2)) & (!Kbd->isVKDown(fabgl::VK_AT))); - bitWrite(PS2cols[3], 2, (!Kbd->isVKDown(fabgl::VK_3)) & (!Kbd->isVKDown(fabgl::VK_HASH))); - bitWrite(PS2cols[3], 3, (!Kbd->isVKDown(fabgl::VK_4)) & (!Kbd->isVKDown(fabgl::VK_DOLLAR))); - - // bitWrite(PS2cols[3], 4, (!Kbd->isVKDown(fabgl::VK_5)) & (!Kbd->isVKDown(fabgl::VK_PERCENT)) - // & ((Config::joystick) | (!Kbd->isVKDown(fabgl::VK_KP_LEFT))) - // & (jLeft) - // ); // Cursor joystick Left - - // bitWrite(PS2cols[4], 0, (!Kbd->isVKDown(fabgl::VK_0)) & (!Kbd->isVKDown(fabgl::VK_RIGHTPAREN)) - // & (!Kbd->isVKDown(fabgl::VK_BACKSPACE)) - // & ((Config::joystick) | (!Kbd->isVKDown(fabgl::VK_RALT))) - // // & (jFire) - // ); // Cursor joystick Fire - - bitWrite(PS2cols[4], 1, !Kbd->isVKDown(fabgl::VK_9) & (!Kbd->isVKDown(fabgl::VK_LEFTPAREN))); - - // bitWrite(PS2cols[4], 2, (!Kbd->isVKDown(fabgl::VK_8)) & (!Kbd->isVKDown(fabgl::VK_ASTERISK)) - // & ((Config::joystick) | (!Kbd->isVKDown(fabgl::VK_KP_RIGHT))) - // & (jRight) - // ); // Cursor joystick Right - - // bitWrite(PS2cols[4], 3, (!Kbd->isVKDown(fabgl::VK_7)) & (!Kbd->isVKDown(fabgl::VK_AMPERSAND)) - // & ((Config::joystick) | (!Kbd->isVKDown(fabgl::VK_KP_UP))) - // & (jUp) - // ); // Cursor joystick Up - - // bitWrite(PS2cols[4], 4, (!Kbd->isVKDown(fabgl::VK_6)) & (!Kbd->isVKDown(fabgl::VK_CARET)) - // & ((Config::joystick) | (!Kbd->isVKDown(fabgl::VK_KP_DOWN) & !Kbd->isVKDown(fabgl::VK_KP_CENTER)) ) - // & (jDown) - // ); // Cursor joystick Down + bitWrite(PS2cols[3], 0, (!Kbd->isVKDown(fabgl::VK_1)) & (!Kbd->isVKDown(fabgl::VK_EXCLAIM)) & (j[1])); + bitWrite(PS2cols[3], 1, (!Kbd->isVKDown(fabgl::VK_2)) & (!Kbd->isVKDown(fabgl::VK_AT)) & (j[2])); + bitWrite(PS2cols[3], 2, (!Kbd->isVKDown(fabgl::VK_3)) & (!Kbd->isVKDown(fabgl::VK_HASH)) & (j[3])); + bitWrite(PS2cols[3], 3, (!Kbd->isVKDown(fabgl::VK_4)) & (!Kbd->isVKDown(fabgl::VK_DOLLAR)) & (j[4])); + bitWrite(PS2cols[3], 4, (!Kbd->isVKDown(fabgl::VK_5)) & (!Kbd->isVKDown(fabgl::VK_PERCENT)) & (j[5])); + + bitWrite(PS2cols[4], 0, (!Kbd->isVKDown(fabgl::VK_0)) & (!Kbd->isVKDown(fabgl::VK_RIGHTPAREN)) & (!Kbd->isVKDown(fabgl::VK_BACKSPACE)) & (j[0])); + bitWrite(PS2cols[4], 1, !Kbd->isVKDown(fabgl::VK_9) & (!Kbd->isVKDown(fabgl::VK_LEFTPAREN)) & (j[9])); + bitWrite(PS2cols[4], 2, (!Kbd->isVKDown(fabgl::VK_8)) & (!Kbd->isVKDown(fabgl::VK_ASTERISK)) & (j[8])); + bitWrite(PS2cols[4], 3, (!Kbd->isVKDown(fabgl::VK_7)) & (!Kbd->isVKDown(fabgl::VK_AMPERSAND)) & (j[7])); + bitWrite(PS2cols[4], 4, (!Kbd->isVKDown(fabgl::VK_6)) & (!Kbd->isVKDown(fabgl::VK_CARET)) & (j[6])); bitWrite(PS2cols[5], 0, (!Kbd->isVKDown(fabgl::VK_P)) & (!Kbd->isVKDown(fabgl::VK_p))); bitWrite(PS2cols[5], 1, (!Kbd->isVKDown(fabgl::VK_O)) & (!Kbd->isVKDown(fabgl::VK_o))); diff --git a/src/OSDFile.cpp b/src/OSDFile.cpp index 3cdb3bf3..8a840bbd 100644 --- a/src/OSDFile.cpp +++ b/src/OSDFile.cpp @@ -265,7 +265,7 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols char buf[128]; char upperbuf[128]; string search = FileUtils::fileTypes[ftype].fileSearch; - transform(search.begin(), search.end(), search.begin(), ::toupper); + std::transform(search.begin(), search.end(), search.begin(), ::toupper); while(1) { fgets(buf, sizeof(buf), dirfile); if (feof(dirfile)) break; @@ -458,7 +458,7 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols click(); - } else if (Menukey.vk == fabgl::VK_UP) { + } else if (Menukey.vk == fabgl::VK_UP || Menukey.vk == fabgl::VK_JOY1UP || Menukey.vk == fabgl::VK_JOY2UP) { if (FileUtils::fileTypes[ftype].focus == 2 && FileUtils::fileTypes[ftype].begin_row > 2) { last_begin_row = FileUtils::fileTypes[ftype].begin_row; FileUtils::fileTypes[ftype].begin_row--; @@ -470,7 +470,7 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols // printf("Focus: %d, Lastfocus: %d\n",FileUtils::fileTypes[ftype].focus,(int) last_focus); } click(); - } else if (Menukey.vk == fabgl::VK_DOWN) { + } else if (Menukey.vk == fabgl::VK_DOWN || Menukey.vk == fabgl::VK_JOY1DOWN || Menukey.vk == fabgl::VK_JOY2DOWN) { if (FileUtils::fileTypes[ftype].focus == virtual_rows - 1 && FileUtils::fileTypes[ftype].begin_row + virtual_rows - 2 < real_rows) { last_begin_row = FileUtils::fileTypes[ftype].begin_row; FileUtils::fileTypes[ftype].begin_row++; @@ -482,7 +482,7 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols // printf("Focus: %d, Lastfocus: %d\n",FileUtils::fileTypes[ftype].focus,(int) last_focus); } click(); - } else if ((Menukey.vk == fabgl::VK_PAGEUP) || (Menukey.vk == fabgl::VK_LEFT)) { + } else if (Menukey.vk == fabgl::VK_PAGEUP || Menukey.vk == fabgl::VK_LEFT || Menukey.vk == fabgl::VK_JOY1LEFT || Menukey.vk == fabgl::VK_JOY2LEFT) { if (FileUtils::fileTypes[ftype].begin_row > virtual_rows) { FileUtils::fileTypes[ftype].focus = 2; FileUtils::fileTypes[ftype].begin_row -= virtual_rows - 2; @@ -492,7 +492,7 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols } fd_Redraw(title, fdir, ftype); click(); - } else if ((Menukey.vk == fabgl::VK_PAGEDOWN) || (Menukey.vk == fabgl::VK_RIGHT)) { + } else if (Menukey.vk == fabgl::VK_PAGEDOWN || Menukey.vk == fabgl::VK_RIGHT || Menukey.vk == fabgl::VK_JOY1RIGHT || Menukey.vk == fabgl::VK_JOY2RIGHT) { if (real_rows - FileUtils::fileTypes[ftype].begin_row - virtual_rows > virtual_rows) { FileUtils::fileTypes[ftype].focus = 2; FileUtils::fileTypes[ftype].begin_row += virtual_rows - 2; @@ -542,7 +542,7 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols } } - } else if (Menukey.vk == fabgl::VK_RETURN || Menukey.vk == fabgl::VK_SPACE) { + } else if (Menukey.vk == fabgl::VK_RETURN || Menukey.vk == fabgl::VK_SPACE || Menukey.vk == fabgl::VK_JOY1B || Menukey.vk == fabgl::VK_JOY2B || Menukey.vk == fabgl::VK_JOY1C || Menukey.vk == fabgl::VK_JOY2C) { fclose(dirfile); dirfile = NULL; @@ -579,10 +579,10 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols rtrim(filedir); click(); - return (Menukey.vk == fabgl::VK_RETURN ? "R" : "S") + filedir; + return (Menukey.vk == fabgl::VK_RETURN || Menukey.vk == fabgl::VK_JOY1B || Menukey.vk == fabgl::VK_JOY2B ? "R" : "S") + filedir; } - } else if (Menukey.vk == fabgl::VK_ESCAPE) { + } else if (Menukey.vk == fabgl::VK_ESCAPE || Menukey.vk == fabgl::VK_JOY1A || Menukey.vk == fabgl::VK_JOY2A) { // Restore backbuffer data if (menu_saverect) { @@ -649,7 +649,7 @@ string OSD::fileDialog(string &fdir, string title, uint8_t ftype, uint8_t mfcols char buf[128]; char upperbuf[128]; string search = FileUtils::fileTypes[ftype].fileSearch; - transform(search.begin(), search.end(), search.begin(), ::toupper); + std::transform(search.begin(), search.end(), search.begin(), ::toupper); while(1) { fgets(buf, sizeof(buf), dirfile); if (feof(dirfile)) break; @@ -714,7 +714,7 @@ void OSD::fd_Redraw(string title, string fdir, uint8_t ftype) { int i = 2; int count = 2; string search = FileUtils::fileTypes[ftype].fileSearch; - transform(search.begin(), search.end(), search.begin(), ::toupper); + std::transform(search.begin(), search.end(), search.begin(), ::toupper); char upperbuf[128]; while (1) { fgets(buf, sizeof(buf), dirfile); diff --git a/src/OSDMain.cpp b/src/OSDMain.cpp index c327aa51..b1e309ed 100644 --- a/src/OSDMain.cpp +++ b/src/OSDMain.cpp @@ -303,7 +303,7 @@ static bool persistLoad(uint8_t slotnumber) } // OSD Main Loop -void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL) { +void OSD::do_OSD(fabgl::VirtualKey KeytoESP, bool CTRL) { static uint8_t last_sna_row = 0; fabgl::VirtualKeyItem Nextkey; @@ -352,7 +352,7 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL) { if (ESPectrum::PS2Controller.keyboard()->virtualKeyAvailable()) { if (ESPectrum::readKbd(&Nextkey)) { if(!Nextkey.down) continue; - if (Nextkey.vk == fabgl::VK_RETURN || Nextkey.vk == fabgl::VK_ESCAPE || Nextkey.vk == fabgl::VK_PAUSE) { + if (Nextkey.vk == fabgl::VK_RETURN || Nextkey.vk == fabgl::VK_ESCAPE || Nextkey.vk == fabgl::VK_JOY1A || Nextkey.vk == fabgl::VK_JOY2A || Nextkey.vk == fabgl::VK_JOY1B || Nextkey.vk == fabgl::VK_JOY2B || Nextkey.vk == fabgl::VK_PAUSE) { click(); break; } else @@ -536,7 +536,6 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL) { menu_saverect = false; menu_level = 0; uint8_t opt = menuRun("ESPectrum " + Config::getArch() + "\n" + MENU_MAIN[Config::lang]); - // uint8_t opt = menuRun(Config::getArch() + "\n" + MENU_MAIN[Config::lang]); if (opt == 1) { // *********************************************************************************** @@ -913,10 +912,63 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL) { while (1) { // joystick string Mnustr = MENU_JOY[Config::lang]; + uint8_t opt2 = menuRun(Mnustr); + if (opt2) { + // Joystick customization + menu_level = 3; + menu_curopt = 1; + menu_saverect = true; + while (1) { + string joy_menu = MENU_DEFJOY[Config::lang]; + joy_menu.replace(joy_menu.find("#",0),1,(string)" " + char(48 + opt2)); + std::size_t pos = joy_menu.find("[",0); + int nfind = 0; + while (pos != string::npos) { + if (nfind == (opt2 == 1 ? Config::joystick1 : Config::joystick2)) { + joy_menu.replace(pos,2,"[*"); + break; + } + pos = joy_menu.find("[",pos + 1); + nfind++; + } + uint8_t optjoy = menuRun(joy_menu); + if (optjoy>0 && optjoy<6) { + if (opt2 == 1) { + Config::joystick1 = optjoy - 1; + Config::save("joystick1"); + } else { + Config::joystick2 = optjoy - 1; + Config::save("joystick2"); + } + Config::setJoyMap(opt2,optjoy - 1); + menu_curopt = optjoy; + menu_saverect = false; + } else if (optjoy == 6) { + joyDialog(opt2); + return; + } else { + menu_curopt = opt2; + menu_level = 2; + break; + } + } + } else { + menu_curopt = 4; + break; + } + } + } + else if (options_num == 5) { + menu_level = 2; + menu_curopt = 1; + menu_saverect = true; + while (1) { + // joystick + string Mnustr = MENU_JOYPS2[Config::lang]; std::size_t pos = Mnustr.find("[",0); int nfind = 0; while (pos != string::npos) { - if (nfind == Config::joystick) { + if (nfind == Config::joyPS2) { Mnustr.replace(pos,2,"[*"); break; } @@ -924,10 +976,10 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL) { nfind++; } uint8_t opt2 = menuRun(Mnustr); - if (opt2 > 0 && opt2 < 3) { - if (Config::joystick != (opt2 - 1)) { - Config::joystick = opt2 - 1; - Config::save("joystick"); + if (opt2 > 0 && opt2 < 6) { + if (Config::joyPS2 != (opt2 - 1)) { + Config::joyPS2 = opt2 - 1; + Config::save("joyPS2"); } menu_curopt = opt2; menu_saverect = false; @@ -961,20 +1013,20 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL) { menu_curopt = opt2; menu_saverect = false; } else { - menu_curopt = 3; + menu_curopt = 6; menu_level = 2; break; } } } else { - menu_curopt = 4; + menu_curopt = 5; break; } } } } - else if (options_num == 5) { + else if (options_num == 6) { menu_level = 2; menu_curopt = 1; menu_saverect = true; @@ -1002,12 +1054,12 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL) { menu_curopt = opt2; menu_saverect = false; } else { - menu_curopt = 5; + menu_curopt = 6; break; } } } - else if (options_num == 6) { + else if (options_num == 7) { menu_level = 2; menu_curopt = 1; menu_saverect = true; @@ -1149,11 +1201,11 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL) { } } } else { - menu_curopt = 6; + menu_curopt = 7; break; } } - } else if (options_num == 7) { + } else if (options_num == 8) { menu_level = 2; @@ -1180,7 +1232,7 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL) { } else { - menu_curopt = 7; + menu_curopt = 8; menu_saverect = false; } @@ -1210,7 +1262,7 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL) { if (ESPectrum::PS2Controller.keyboard()->virtualKeyAvailable()) { if (ESPectrum::readKbd(&Nextkey)) { if(!Nextkey.down) continue; - if ((Nextkey.vk == fabgl::VK_F1) || (Nextkey.vk == fabgl::VK_ESCAPE) || (Nextkey.vk == fabgl::VK_RETURN)) break; + if (Nextkey.vk == fabgl::VK_F1 || Nextkey.vk == fabgl::VK_ESCAPE || Nextkey.vk == fabgl::VK_RETURN || Nextkey.vk == fabgl::VK_JOY1A || Nextkey.vk == fabgl::VK_JOY1B || Nextkey.vk == fabgl::VK_JOY2A || Nextkey.vk == fabgl::VK_JOY2B) break; } } @@ -1294,7 +1346,7 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL) { osdRow = 0; msgChar = 0; msgIndex++; - if (msgIndex==6) msgIndex = 0; + if (msgIndex==7) msgIndex = 0; } } @@ -1314,7 +1366,7 @@ void OSD::do_OSD(fabgl::VirtualKey KeytoESP, uint8_t CTRL) { if (ESPectrum::PS2Controller.keyboard()->virtualKeyAvailable()) { if (ESPectrum::readKbd(&Nextkey)) { if(!Nextkey.down) continue; - if ((Nextkey.vk == fabgl::VK_F1) || (Nextkey.vk == fabgl::VK_ESCAPE) || (Nextkey.vk == fabgl::VK_RETURN)) break; + if (Nextkey.vk == fabgl::VK_F1 || Nextkey.vk == fabgl::VK_ESCAPE || Nextkey.vk == fabgl::VK_RETURN || Nextkey.vk == fabgl::VK_JOY1A || Nextkey.vk == fabgl::VK_JOY1B || Nextkey.vk == fabgl::VK_JOY2A || Nextkey.vk == fabgl::VK_JOY2B) break; } } @@ -1584,7 +1636,7 @@ void OSD::HWInfo() { if (ESPectrum::PS2Controller.keyboard()->virtualKeyAvailable()) { ESPectrum::PS2Controller.keyboard()->getNextVirtualKey(&Nextkey); if(!Nextkey.down) continue; - if (Nextkey.vk == fabgl::VK_RETURN || Nextkey.vk == fabgl::VK_ESCAPE || Nextkey.vk == fabgl::VK_F1) { + if (Nextkey.vk == fabgl::VK_F1 || Nextkey.vk == fabgl::VK_ESCAPE || Nextkey.vk == fabgl::VK_RETURN || Nextkey.vk == fabgl::VK_JOY1A || Nextkey.vk == fabgl::VK_JOY1B || Nextkey.vk == fabgl::VK_JOY2A || Nextkey.vk == fabgl::VK_JOY2B) { click(); break; } @@ -1818,7 +1870,7 @@ uint8_t OSD::msgDialog(string title, string msg) { } // printf("SaveRectPos: %04X\n",SaveRectpos << 2); - // Set font + // Set font VIDEO::vga.setFont(Font6x8); // Menu border @@ -1883,7 +1935,7 @@ uint8_t OSD::msgDialog(string title, string msg) { if (ESPectrum::readKbd(&Menukey)) { if (!Menukey.down) continue; - if (Menukey.vk == fabgl::VK_LEFT) { + if (Menukey.vk == fabgl::VK_LEFT || Menukey.vk == fabgl::VK_JOY1LEFT || Menukey.vk == fabgl::VK_JOY2LEFT) { // Yes VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(5, 1)); VIDEO::vga.setCursor(scrAlignCenterX(6 * OSD_FONT_W) - (w >> 2), y + 1 + (OSD_FONT_H * 4)); @@ -1894,7 +1946,7 @@ uint8_t OSD::msgDialog(string title, string msg) { VIDEO::vga.print(" No "); click(); res = DLG_YES; - } else if (Menukey.vk == fabgl::VK_RIGHT) { + } else if (Menukey.vk == fabgl::VK_RIGHT || Menukey.vk == fabgl::VK_JOY1RIGHT || Menukey.vk == fabgl::VK_JOY2RIGHT) { // Yes VIDEO::vga.setTextColor(zxColor(0, 0), zxColor(7, 1)); VIDEO::vga.setCursor(scrAlignCenterX(6 * OSD_FONT_W) - (w >> 2), y + 1 + (OSD_FONT_H * 4)); @@ -1905,9 +1957,9 @@ uint8_t OSD::msgDialog(string title, string msg) { VIDEO::vga.print(" No "); click(); res = DLG_NO; - } else if (Menukey.vk == fabgl::VK_RETURN || Menukey.vk == fabgl::VK_SPACE) { + } else if (Menukey.vk == fabgl::VK_RETURN || Menukey.vk == fabgl::VK_SPACE || Menukey.vk == fabgl::VK_JOY1B || Menukey.vk == fabgl::VK_JOY2B || Menukey.vk == fabgl::VK_JOY1C || Menukey.vk == fabgl::VK_JOY2C) { break; - } else if (Menukey.vk == fabgl::VK_ESCAPE) { + } else if (Menukey.vk == fabgl::VK_ESCAPE || Menukey.vk == fabgl::VK_JOY1A || Menukey.vk == fabgl::VK_JOY2A) { res = DLG_CANCEL; break; } @@ -1940,3 +1992,815 @@ string OSD::inputBox(int x, int y, string text) { return text; } + +#define MENU_JOYSELKEY_EN "Key \n"\ + "A-Z \n"\ + "1-0 \n"\ + "Special \n"\ + "PS/2 \n" +#define MENU_JOYSELKEY_ES "Tecla \n"\ + "A-Z \n"\ + "1-0 \n"\ + "Especial \n"\ + "PS/2 \n" +static const char *MENU_JOYSELKEY[2] = { MENU_JOYSELKEY_EN, MENU_JOYSELKEY_ES }; + +#define MENU_JOY_AZ "A-Z\n"\ + "A\n"\ + "B\n"\ + "C\n"\ + "D\n"\ + "E\n"\ + "F\n"\ + "G\n"\ + "H\n"\ + "I\n"\ + "J\n"\ + "K\n"\ + "L\n"\ + "M\n"\ + "N\n"\ + "O\n"\ + "P\n"\ + "Q\n"\ + "R\n"\ + "S\n"\ + "T\n"\ + "U\n"\ + "V\n"\ + "W\n"\ + "X\n"\ + "Y\n"\ + "Z\n" + +#define MENU_JOY_09 "0-9\n"\ + "0\n"\ + "1\n"\ + "2\n"\ + "3\n"\ + "4\n"\ + "5\n"\ + "6\n"\ + "7\n"\ + "8\n"\ + "9\n" + +#define MENU_JOY_SPECIAL "Enter\n"\ + "Caps\n"\ + "SymbShift\n"\ + "Brk/Space\n"\ + "None\n" + +#define MENU_JOY_PS2 "PS/2\n"\ + "F1\n"\ + "F2\n"\ + "F3\n"\ + "F4\n"\ + "F5\n"\ + "F6\n"\ + "F7\n"\ + "F8\n"\ + "F9\n"\ + "F10\n"\ + "F11\n"\ + "F12\n"\ + "Pause\n"\ + "PrtScr\n"\ + "Left\n"\ + "Right\n"\ + "Up\n"\ + "Down\n" + +#define MENU_JOY_KEMPSTON "Kempston\n"\ + "Left\n"\ + "Right\n"\ + "Up\n"\ + "Down\n"\ + "Fire 1\n"\ + "Fire 2\n" + +#define MENU_JOY_FULLER "Fuller\n"\ + "Left\n"\ + "Right\n"\ + "Up\n"\ + "Down\n"\ + "Fire\n" + +string vkToText(int key) { + +fabgl::VirtualKey vk = (fabgl::VirtualKey) key; + +switch (vk) +{ +case fabgl::VK_0: + return " 0 "; +case fabgl::VK_1: + return " 1 "; +case fabgl::VK_2: + return " 2 "; +case fabgl::VK_3: + return " 3 "; +case fabgl::VK_4: + return " 4 "; +case fabgl::VK_5: + return " 5 "; +case fabgl::VK_6: + return " 6 "; +case fabgl::VK_7: + return " 7 "; +case fabgl::VK_8: + return " 8 "; +case fabgl::VK_9: + return " 9 "; +case fabgl::VK_A: + return " A "; +case fabgl::VK_B: + return " B "; +case fabgl::VK_C: + return " C "; +case fabgl::VK_D: + return " D "; +case fabgl::VK_E: + return " E "; +case fabgl::VK_F: + return " F "; +case fabgl::VK_G: + return " G "; +case fabgl::VK_H: + return " H "; +case fabgl::VK_I: + return " I "; +case fabgl::VK_J: + return " J "; +case fabgl::VK_K: + return " K "; +case fabgl::VK_L: + return " L "; +case fabgl::VK_M: + return " M "; +case fabgl::VK_N: + return " N "; +case fabgl::VK_O: + return " O "; +case fabgl::VK_P: + return " P "; +case fabgl::VK_Q: + return " Q "; +case fabgl::VK_R: + return " R "; +case fabgl::VK_S: + return " S "; +case fabgl::VK_T: + return " T "; +case fabgl::VK_U: + return " U "; +case fabgl::VK_V: + return " V "; +case fabgl::VK_W: + return " W "; +case fabgl::VK_X: + return " X "; +case fabgl::VK_Y: + return " Y "; +case fabgl::VK_Z: + return " Z "; +case fabgl::VK_RETURN: + return " Enter "; +case fabgl::VK_SPACE: + return "Brk/Space"; +case fabgl::VK_LSHIFT: + return " Caps "; +case fabgl::VK_LCTRL: + return "SymbShift"; +case fabgl::VK_F1: + return " F1 "; +case fabgl::VK_F2: + return " F2 "; +case fabgl::VK_F3: + return " F3 "; +case fabgl::VK_F4: + return " F4 "; +case fabgl::VK_F5: + return " F5 "; +case fabgl::VK_F6: + return " F6 "; +case fabgl::VK_F7: + return " F7 "; +case fabgl::VK_F8: + return " F8 "; +case fabgl::VK_F9: + return " F9 "; +case fabgl::VK_F10: + return " F10 "; +case fabgl::VK_F11: + return " F11 "; +case fabgl::VK_F12: + return " F12 "; +case fabgl::VK_PAUSE: + return " Pause "; +case fabgl::VK_PRINTSCREEN: + return " PrtScr "; +case fabgl::VK_LEFT: + return " Left "; +case fabgl::VK_RIGHT: + return " Right "; +case fabgl::VK_UP: + return " Up "; +case fabgl::VK_DOWN: + return " Down "; +case fabgl::VK_KEMPSTON_LEFT: + return "Kmp.Left "; +case fabgl::VK_KEMPSTON_RIGHT: + return "Kmp.Right"; +case fabgl::VK_KEMPSTON_UP: + return " Kmp.Up "; +case fabgl::VK_KEMPSTON_DOWN: + return "Kmp.Down "; +case fabgl::VK_KEMPSTON_FIRE: + return "Kmp.Fire1"; +case fabgl::VK_KEMPSTON_ALTFIRE: + return "Kmp.Fire2"; +case fabgl::VK_FULLER_LEFT: + return "Fll.Left "; +case fabgl::VK_FULLER_RIGHT: + return "Fll.Right"; +case fabgl::VK_FULLER_UP: + return " Fll.Up "; +case fabgl::VK_FULLER_DOWN: + return "Fll.Down "; +case fabgl::VK_FULLER_FIRE: + return "Fll.Fire "; +default: + return " None "; +} + +} + +unsigned int joyControl[12][3]={ + {34,55,zxColor(0,0)}, // Left + {87,55,zxColor(0,0)}, // Right + {63,30,zxColor(0,0)}, // Up + {63,78,zxColor(0,0)}, // Down + {49,109,zxColor(0,0)}, // Start + {136,109,zxColor(0,0)}, // Mode + {145,69,zxColor(0,0)}, // A + {205,69,zxColor(0,0)}, // B + {265,69,zxColor(0,0)}, // C + {145,37,zxColor(0,0)}, // X + {205,37,zxColor(0,0)}, // Y + {265,37,zxColor(0,0)} // Z +}; + +void DrawjoyControls(unsigned short x, unsigned short y) { + + // Draw joy controls + + // Left arrow + for (int i = 0; i <= 5; i++) { + VIDEO::vga.line(x + joyControl[0][0] + i, y + joyControl[0][1] - i, x + joyControl[0][0] + i, y + joyControl[0][1] + i, joyControl[0][2]); + } + + // Right arrow + for (int i = 0; i <= 5; i++) { + VIDEO::vga.line(x + joyControl[1][0] + i, y + joyControl[1][1] - ( 5 - i), x + joyControl[1][0] + i, y + joyControl[1][1] + ( 5 - i), joyControl[1][2]); + } + + // Up arrow + for (int i = 0; i <= 6; i++) { + VIDEO::vga.line(x + joyControl[2][0] - i, y + joyControl[2][1] + i, x + joyControl[2][0] + i, y + joyControl[2][1] + i, joyControl[2][2]); + } + + // Down arrow + for (int i = 0; i <= 6; i++) { + VIDEO::vga.line(x + joyControl[3][0] - (6 - i), y + joyControl[3][1] + i, x + joyControl[3][0] + ( 6 - i), y + joyControl[3][1] + i, joyControl[3][2]); + } + + // START text + VIDEO::vga.setTextColor(joyControl[4][2], zxColor(7, 1)); + VIDEO::vga.setCursor(x + joyControl[4][0], y + joyControl[4][1]); + VIDEO::vga.print("START"); + + // MODE text + VIDEO::vga.setTextColor(joyControl[5][2], zxColor(7, 1)); + VIDEO::vga.setCursor(x + joyControl[5][0], y + joyControl[5][1]); + VIDEO::vga.print("MODE"); + + // Text A + VIDEO::vga.setTextColor( joyControl[6][2],zxColor(7, 1)); + VIDEO::vga.setCursor(x + joyControl[6][0], y + joyControl[6][1]); + VIDEO::vga.circle(x + joyControl[6][0] + 3, y + joyControl[6][1] + 3, 6, joyControl[6][2]); + VIDEO::vga.print("A"); + + // Text B + VIDEO::vga.setTextColor(joyControl[7][2],zxColor(7, 1)); + VIDEO::vga.setCursor(x + joyControl[7][0], y + joyControl[7][1]); + VIDEO::vga.circle(x + joyControl[7][0] + 3, y + joyControl[7][1] + 3, 6, joyControl[7][2]); + VIDEO::vga.print("B"); + + // Text C + VIDEO::vga.setTextColor(joyControl[8][2],zxColor(7, 1)); + VIDEO::vga.setCursor(x + joyControl[8][0], y + joyControl[8][1]); + VIDEO::vga.circle(x + joyControl[8][0] + 3, y + joyControl[8][1] + 3, 6, joyControl[8][2]); + VIDEO::vga.print("C"); + + // Text X + VIDEO::vga.setTextColor(joyControl[9][2],zxColor(7, 1)); + VIDEO::vga.setCursor(x + joyControl[9][0], y + joyControl[9][1]); + VIDEO::vga.circle(x + joyControl[9][0] + 3, y + joyControl[9][1] + 3, 6, joyControl[9][2]); + VIDEO::vga.print("X"); + + // Text Y + VIDEO::vga.setTextColor(joyControl[10][2],zxColor(7, 1)); + VIDEO::vga.setCursor(x + joyControl[10][0], y + joyControl[10][1]); + VIDEO::vga.circle(x + joyControl[10][0] + 3, y + joyControl[10][1] + 3, 6, joyControl[10][2]); + VIDEO::vga.print("Y"); + + // Text Z + VIDEO::vga.setTextColor(joyControl[11][2],zxColor(7, 1)); + VIDEO::vga.setCursor(x + joyControl[11][0], y + joyControl[11][1]); + VIDEO::vga.circle(x + joyControl[11][0] + 3, y + joyControl[11][1] + 3, 6, joyControl[11][2]); + VIDEO::vga.print("Z"); + +} + +void OSD::joyDialog(uint8_t joynum) { + + int joyDropdown[14][7]={ + {7,65,-1,1,2,3,0}, // Left + {67,65,0,-1,2,3,0}, // Right + {37,17,-1,9,-1,0,0}, // Up + {37,89,-1,6,0,4,0}, // Down + {37,121,-1,5,3,-1,0}, // Start + {121,121,4,12,6,-1,0}, // Mode + {121,89,3,7,9,5,0}, // A + {181,89,6,8,10,12,0}, // B + {241,89,7,-1,11,13,0}, // C + {121,17,2,10,-1,6,0}, // X + {181,17,9,11,-1,7,0}, // Y + {241,17,10,-1,-1,8,0}, // Z + {181,121,5,13,7,-1,0}, // Ok + {241,121,12,-1,8,-1,0} // Test + }; + + string keymenu = MENU_JOYSELKEY[Config::lang]; + int joytype = joynum == 1 ? Config::joystick1 : Config::joystick2; + + string selkeymenu[5] = { + MENU_JOY_AZ, + MENU_JOY_09, + "", + MENU_JOY_PS2, + "" + }; + + selkeymenu[2] = (Config::lang ? "Especial\n" : "Special\n"); + selkeymenu[2] += MENU_JOY_SPECIAL; + + if (joytype == JOY_KEMPSTON) { + keymenu += "Kempston \n"; + selkeymenu[4] = MENU_JOY_KEMPSTON; + } else + if (joytype == JOY_FULLER) { + keymenu += "Fuller \n"; + selkeymenu[4] = MENU_JOY_FULLER; + } + + int curDropDown = 2; + uint8_t joyDialogMode = 0; // 0 -> Define, 1 -> Test + + const unsigned short h = (OSD_FONT_H * 18) + 2; + const unsigned short y = scrAlignCenterY(h) - 8; + + const unsigned short w = (50 * OSD_FONT_W) + 2; + const unsigned short x = scrAlignCenterX(w) - 3; + + // Set font + VIDEO::vga.setFont(Font6x8); + + // Menu border + VIDEO::vga.rect(x, y, w, h, zxColor(0, 0)); + + VIDEO::vga.fillRect(x + 1, y + 1, w - 2, OSD_FONT_H, zxColor(0,0)); + VIDEO::vga.fillRect(x + 1, y + 1 + OSD_FONT_H, w - 2, h - OSD_FONT_H - 2, zxColor(7,1)); + + // Title + VIDEO::vga.setTextColor(zxColor(7, 1), zxColor(0, 0)); + VIDEO::vga.setCursor(x + OSD_FONT_W + 1, y + 1); + VIDEO::vga.print((joynum == 1 ? "Joystick 1" : "Joystick 2")); + + // Rainbow + unsigned short rb_y = y + 8; + unsigned short rb_paint_x = x + w - 30; + uint8_t rb_colors[] = {2, 6, 4, 5}; + for (uint8_t c = 0; c < 4; c++) { + for (uint8_t i = 0; i < 5; i++) { + VIDEO::vga.line(rb_paint_x + i, rb_y, rb_paint_x + 8 + i, rb_y - 8, zxColor(rb_colors[c], 1)); + } + rb_paint_x += 5; + } + + // Read joy definition into joyDropdown + for (int n=0; n<12; n++) + joyDropdown[n][6] = ESPectrum::JoyVKTranslation[n + (joynum == 1 ? 0 : 12)]; + + // Draw Joy DropDowns + for (int n=0; n<12; n++) { + VIDEO::vga.rect(x + joyDropdown[n][0] - 2, y + joyDropdown[n][1] - 2, 58, 12, zxColor(0, 0)); + if (n == curDropDown) + VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(5, 1)); + else + VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(7, 1)); + VIDEO::vga.setCursor(x + joyDropdown[n][0], y + joyDropdown[n][1]); + VIDEO::vga.print(vkToText(joyDropdown[n][6]).c_str()); + } + + // Draw dialog buttons + VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(7, 1)); + VIDEO::vga.setCursor(x + joyDropdown[12][0], y + joyDropdown[12][1]); + VIDEO::vga.print(" Ok "); + VIDEO::vga.setCursor(x + joyDropdown[13][0], y + joyDropdown[13][1]); + VIDEO::vga.print(" JoyTest "); + + // // Ruler + // VIDEO::vga.setTextColor(zxColor(0, 0), zxColor(7, 1)); + // VIDEO::vga.setCursor(x + 1, y + 1 + (OSD_FONT_H * 3)); + // VIDEO::vga.print("123456789012345678901234567"); + + DrawjoyControls(x,y); + + // Wait for key + fabgl::VirtualKeyItem Nextkey; + + int joyTestExitCount1 = 0; + int joyTestExitCount2 = 0; + + while (1) { + + if (joyDialogMode) { + DrawjoyControls(x,y); + } + + if (ZXKeyb::Exists) ZXKeyb::ZXKbdRead(); + + ESPectrum::readKbdJoy(); + + if (ESPectrum::PS2Controller.keyboard()->virtualKeyAvailable()) { + ESPectrum::PS2Controller.keyboard()->getNextVirtualKey(&Nextkey); + if(!Nextkey.down) continue; + + if (Nextkey.vk == fabgl::VK_LEFT || Nextkey.vk == fabgl::VK_JOY1LEFT || Nextkey.vk == fabgl::VK_JOY2LEFT) { + + if (joyDialogMode == 0 && joyDropdown[curDropDown][2] >= 0) { + + VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(7, 1)); + VIDEO::vga.setCursor(x + joyDropdown[curDropDown][0], y + joyDropdown[curDropDown][1]); + if (curDropDown < 12) + VIDEO::vga.print(vkToText(joyDropdown[curDropDown][6]).c_str()); + else + VIDEO::vga.print(curDropDown == 12 ? " Ok " : " JoyTest "); + + curDropDown = joyDropdown[curDropDown][2]; + + VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(5, 1)); + VIDEO::vga.setCursor(x + joyDropdown[curDropDown][0], y + joyDropdown[curDropDown][1]); + if (curDropDown < 12) + VIDEO::vga.print(vkToText(joyDropdown[curDropDown][6]).c_str()); + else + VIDEO::vga.print(curDropDown == 12 ? " Ok " : " JoyTest "); + + click(); + + } + + } else + if (Nextkey.vk == fabgl::VK_RIGHT || Nextkey.vk == fabgl::VK_JOY1RIGHT || Nextkey.vk == fabgl::VK_JOY2RIGHT) { + + if (joyDialogMode == 0 && joyDropdown[curDropDown][3] >= 0) { + + VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(7, 1)); + VIDEO::vga.setCursor(x + joyDropdown[curDropDown][0], y + joyDropdown[curDropDown][1]); + if (curDropDown < 12) + VIDEO::vga.print(vkToText(joyDropdown[curDropDown][6]).c_str()); + else + VIDEO::vga.print(curDropDown == 12 ? " Ok " : " JoyTest "); + + curDropDown = joyDropdown[curDropDown][3]; + + VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(5, 1)); + VIDEO::vga.setCursor(x + joyDropdown[curDropDown][0], y + joyDropdown[curDropDown][1]); + if (curDropDown < 12) + VIDEO::vga.print(vkToText(joyDropdown[curDropDown][6]).c_str()); + else + VIDEO::vga.print(curDropDown == 12 ? " Ok " : " JoyTest "); + + click(); + + } + + } else + if (Nextkey.vk == fabgl::VK_UP || Nextkey.vk == fabgl::VK_JOY1UP || Nextkey.vk == fabgl::VK_JOY2UP) { + + if (joyDialogMode == 0 && joyDropdown[curDropDown][4] >= 0) { + + VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(7, 1)); + VIDEO::vga.setCursor(x + joyDropdown[curDropDown][0], y + joyDropdown[curDropDown][1]); + if (curDropDown < 12) + VIDEO::vga.print(vkToText(joyDropdown[curDropDown][6]).c_str()); + else + VIDEO::vga.print(curDropDown == 12 ? " Ok " : " JoyTest "); + + curDropDown = joyDropdown[curDropDown][4]; + + VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(5, 1)); + VIDEO::vga.setCursor(x + joyDropdown[curDropDown][0], y + joyDropdown[curDropDown][1]); + if (curDropDown < 12) + VIDEO::vga.print(vkToText(joyDropdown[curDropDown][6]).c_str()); + else + VIDEO::vga.print(curDropDown == 12 ? " Ok " : " JoyTest "); + + click(); + + } + + } else + if (Nextkey.vk == fabgl::VK_DOWN || Nextkey.vk == fabgl::VK_JOY1DOWN || Nextkey.vk == fabgl::VK_JOY2DOWN) { + + if (joyDialogMode == 0 && joyDropdown[curDropDown][5] >= 0) { + + VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(7, 1)); + VIDEO::vga.setCursor(x + joyDropdown[curDropDown][0], y + joyDropdown[curDropDown][1]); + if (curDropDown < 12) + VIDEO::vga.print(vkToText(joyDropdown[curDropDown][6]).c_str()); + else + VIDEO::vga.print(curDropDown == 12 ? " Ok " : " JoyTest "); + + curDropDown = joyDropdown[curDropDown][5]; + + VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(5, 1)); + VIDEO::vga.setCursor(x + joyDropdown[curDropDown][0], y + joyDropdown[curDropDown][1]); + if (curDropDown < 12) + VIDEO::vga.print(vkToText(joyDropdown[curDropDown][6]).c_str()); + else + VIDEO::vga.print(curDropDown == 12 ? " Ok " : " JoyTest "); + + click(); + + } + + } else + if (Nextkey.vk == fabgl::VK_RETURN || Nextkey.vk == fabgl::VK_JOY1B || Nextkey.vk == fabgl::VK_JOY2B) { + + if (joyDialogMode == 0) { + + if (curDropDown>=0 && curDropDown<12) { + + click(); + + // Launch assign menu + menu_curopt = 1; + while (1) { + menu_level = 0; + menu_saverect = true; + uint8_t opt = simpleMenuRun(keymenu,x + joyDropdown[curDropDown][0],y + joyDropdown[curDropDown][1]); + if(opt!=0) { + // Key select menu + menu_saverect = true; + menu_level = 0; + menu_curopt = 1; + uint8_t opt2 = simpleMenuRun(selkeymenu[opt - 1],x + joyDropdown[curDropDown][0],y + joyDropdown[curDropDown][1]); + if(opt2!=0) { + + if (opt == 1) {// A-Z + joyDropdown[curDropDown][6] = (fabgl::VirtualKey) 47 + opt2; + } else + if (opt == 2) {// 1-0 + joyDropdown[curDropDown][6] = (fabgl::VirtualKey) 1 + opt2; + } else + if (opt == 3) {// Special + if (opt2 == 1) { + joyDropdown[curDropDown][6] = fabgl::VK_RETURN; + } else + if (opt2 == 2) { + joyDropdown[curDropDown][6] = fabgl::VK_LSHIFT; + } else + if (opt2 == 3) { + joyDropdown[curDropDown][6] = fabgl::VK_LCTRL; + } else + if (opt2 == 4) { + joyDropdown[curDropDown][6] = fabgl::VK_SPACE; + } else + if (opt2 == 5) { + joyDropdown[curDropDown][6] = fabgl::VK_NONE; + } + } else + if (opt == 4) {// PS/2 + if (opt2 < 13) { + joyDropdown[curDropDown][6] = (fabgl::VirtualKey) 158 + opt2; + } else + if (opt2 == 13) { + joyDropdown[curDropDown][6] = fabgl::VirtualKey::VK_PAUSE; + } else + if (opt2 == 14) { + joyDropdown[curDropDown][6] = fabgl::VirtualKey::VK_PRINTSCREEN; + } else + if (opt2 == 15) { + joyDropdown[curDropDown][6] = fabgl::VirtualKey::VK_LEFT; + } else + if (opt2 == 16) { + joyDropdown[curDropDown][6] = fabgl::VirtualKey::VK_RIGHT; + } else + if (opt2 == 17) { + joyDropdown[curDropDown][6] = fabgl::VirtualKey::VK_UP; + } else + if (opt2 == 18) { + joyDropdown[curDropDown][6] = fabgl::VirtualKey::VK_DOWN; + } + } else + if (opt == 5) {// Kempston / Fuller + if (opt2 == 1) { + joyDropdown[curDropDown][6] = fabgl::VirtualKey::VK_KEMPSTON_LEFT; + } else + if (opt2 == 2) { + joyDropdown[curDropDown][6] = fabgl::VirtualKey::VK_KEMPSTON_RIGHT; + } else + if (opt2 == 3) { + joyDropdown[curDropDown][6] = fabgl::VirtualKey::VK_KEMPSTON_UP; + } else + if (opt2 == 4) { + joyDropdown[curDropDown][6] = fabgl::VirtualKey::VK_KEMPSTON_DOWN; + } else + if (opt2 == 5) { + joyDropdown[curDropDown][6] = fabgl::VirtualKey::VK_KEMPSTON_FIRE; + } else + if (opt2 == 6) { + joyDropdown[curDropDown][6] = fabgl::VirtualKey::VK_KEMPSTON_ALTFIRE; + } + + if (joytype == JOY_FULLER) + joyDropdown[curDropDown][6] += 6; + + } + + VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(5, 1)); + VIDEO::vga.setCursor(x + joyDropdown[curDropDown][0], y + joyDropdown[curDropDown][1]); + VIDEO::vga.print(vkToText(joyDropdown[curDropDown][6]).c_str()); + + break; + + } + } else break; + menu_curopt = opt; + } + + } else + if (curDropDown == 12) { + // Ok button + + // Check if there are changes and ask to save them + bool changed = false; + for (int n = 0; n < 12; n++) { + if (ESPectrum::JoyVKTranslation[n + (joynum == 1 ? 0 : 12)] != joyDropdown[n][6]) { + changed = true; + break; + } + } + + // Ask to save changes + if (changed) { + + string title = (joynum == 1 ? "Joystick 1" : "Joystick 2"); + string msg = OSD_DLG_JOYSAVE[Config::lang]; + uint8_t res = OSD::msgDialog(title,msg); + if (res == DLG_YES) { + + // Fill joystick values in Config + int m = (joynum == 1) ? 0 : 12; + for (int n = m; n < m + 12; n++) { + // Save to config (only changes) + if (Config::joydef[n] != (uint16_t) joyDropdown[n - m][6]) { + ESPectrum::JoyVKTranslation[n] = (fabgl::VirtualKey) joyDropdown[n - m][6]; + Config::joydef[n] = (uint16_t) joyDropdown[n - m][6]; + char joykey[9]; + sprintf(joykey,"joydef%02u",n); + Config::save(joykey); + // printf("%s %u\n",joykey, joydef[n]); + } + } + + click(); + break; + + } else + if (res == DLG_NO) { + click(); + break; + } + + } else { + click(); + break; + } + + } else + if (curDropDown == 13) { + // Enable joyTest + joyDialogMode = 1; + + joyTestExitCount1 = 0; + joyTestExitCount2 = 0; + + VIDEO::vga.setTextColor(zxColor(4, 1), zxColor(5, 1)); + VIDEO::vga.setCursor(x + joyDropdown[13][0], y + joyDropdown[13][1]); + VIDEO::vga.print(" JoyTest "); + + click(); + + } + + } + + } else + if (Nextkey.vk == fabgl::VK_ESCAPE || Nextkey.vk == fabgl::VK_JOY1A || Nextkey.vk == fabgl::VK_JOY2A) { + + if (joyDialogMode) { + + if (Nextkey.vk == fabgl::VK_ESCAPE) { + + // Disable joyTest + joyDialogMode = 0; + + VIDEO::vga.setTextColor(zxColor(0, 1), zxColor(5, 1)); + VIDEO::vga.setCursor(x + joyDropdown[13][0], y + joyDropdown[13][1]); + VIDEO::vga.print(" JoyTest "); + + for (int n = 0; n < 12; n++) + joyControl[n][2] = zxColor(0,0); + + DrawjoyControls(x,y); + + click(); + + } + + } else { + + // Check if there are changes and ask to discard them + bool changed = false; + for (int n = 0; n < 12; n++) { + if (ESPectrum::JoyVKTranslation[n + (joynum == 1 ? 0 : 12)] != joyDropdown[n][6]) { + changed = true; + break; + } + } + + // Ask to discard changes + if (changed) { + string title = (joynum == 1 ? "Joystick 1" : "Joystick 2"); + string msg = OSD_DLG_JOYDISCARD[Config::lang]; + uint8_t res = OSD::msgDialog(title,msg); + if (res == DLG_YES) { + click(); + break; + } + } else { + click(); + break; + } + + } + + } + + } + + // Joy Test Mode: Check joy status and color controls + if (joyDialogMode) { + + for (int n = (joynum == 1 ? fabgl::VK_JOY1LEFT : fabgl::VK_JOY2LEFT); n <= (joynum == 1 ? fabgl::VK_JOY1Z : fabgl::VK_JOY2Z); n++) { + if (ESPectrum::PS2Controller.keyboard()->isVKDown((fabgl::VirtualKey) n)) + joyControl[n - (joynum == 1 ? 248 : 260)][2] = zxColor(4,1); + else + joyControl[n - (joynum == 1 ? 248 : 260)][2] = zxColor(0,0); + } + + if (ESPectrum::PS2Controller.keyboard()->isVKDown(fabgl::VK_JOY1A)) { + joyTestExitCount1++; + if (joyTestExitCount1 == 30) + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_ESCAPE,true,false); + } else + joyTestExitCount1 = 0; + + if (ESPectrum::PS2Controller.keyboard()->isVKDown(fabgl::VK_JOY2A)) { + joyTestExitCount2++; + if (joyTestExitCount2 == 30) + ESPectrum::PS2Controller.keyboard()->injectVirtualKey(fabgl::VK_ESCAPE,true,false); + } else + joyTestExitCount2 = 0; + + } + + vTaskDelay(50 / portTICK_PERIOD_MS); + + } + +} \ No newline at end of file diff --git a/src/OSDMenu.cpp b/src/OSDMenu.cpp index 123abb30..f529780c 100644 --- a/src/OSDMenu.cpp +++ b/src/OSDMenu.cpp @@ -64,11 +64,6 @@ using namespace std; extern Font Font6x8; -uint16_t OSD::zxColor(uint8_t color, uint8_t bright) { - if (bright) color += 8; - return spectrum_colors[color]; -} - // Get real row number for a virtual one unsigned short OSD::menuRealRowFor(uint8_t virtual_row_num) { return begin_row + virtual_row_num - 1; } @@ -248,7 +243,7 @@ unsigned short OSD::menuRun(string new_menu) { if (ESPectrum::PS2Controller.keyboard()->virtualKeyAvailable()) { if (ESPectrum::readKbd(&Menukey)) { if (!Menukey.down) continue; - if (Menukey.vk == fabgl::VK_UP) { + if (Menukey.vk == fabgl::VK_UP || Menukey.vk == fabgl::VK_JOY1UP || Menukey.vk == fabgl::VK_JOY2UP) { if (focus == 1 and begin_row > 1) { menuScroll(DOWN); } else { @@ -267,7 +262,7 @@ unsigned short OSD::menuRun(string new_menu) { } } click(); - } else if (Menukey.vk == fabgl::VK_DOWN) { + } else if (Menukey.vk == fabgl::VK_DOWN || Menukey.vk == fabgl::VK_JOY1DOWN || Menukey.vk == fabgl::VK_JOY2DOWN) { if (focus == virtual_rows - 1 && virtual_rows + begin_row - 1 < real_rows) { menuScroll(UP); } else { @@ -286,7 +281,7 @@ unsigned short OSD::menuRun(string new_menu) { } } click(); - } else if ((Menukey.vk == fabgl::VK_PAGEUP) || (Menukey.vk == fabgl::VK_LEFT)) { + } else if (Menukey.vk == fabgl::VK_PAGEUP || Menukey.vk == fabgl::VK_LEFT || Menukey.vk == fabgl::VK_JOY1LEFT || Menukey.vk == fabgl::VK_JOY2LEFT) { if (begin_row > virtual_rows) { focus = 1; begin_row -= virtual_rows - 1; @@ -296,7 +291,7 @@ unsigned short OSD::menuRun(string new_menu) { } menuRedraw(); click(); - } else if ((Menukey.vk == fabgl::VK_PAGEDOWN) || (Menukey.vk == fabgl::VK_RIGHT)) { + } else if (Menukey.vk == fabgl::VK_PAGEDOWN || Menukey.vk == fabgl::VK_RIGHT || Menukey.vk == fabgl::VK_JOY1RIGHT || Menukey.vk == fabgl::VK_JOY2RIGHT) { if (real_rows - begin_row - virtual_rows > virtual_rows) { focus = 1; begin_row += virtual_rows - 1; @@ -316,11 +311,11 @@ unsigned short OSD::menuRun(string new_menu) { begin_row = real_rows - virtual_rows + 1; menuRedraw(); click(); - } else if (Menukey.vk == fabgl::VK_RETURN || Menukey.vk == fabgl::VK_SPACE) { + } else if (Menukey.vk == fabgl::VK_RETURN || Menukey.vk == fabgl::VK_SPACE || Menukey.vk == fabgl::VK_JOY1B || Menukey.vk == fabgl::VK_JOY1C || Menukey.vk == fabgl::VK_JOY2B || Menukey.vk == fabgl::VK_JOY2C) { click(); menu_prevopt = menuRealRowFor(focus); return menu_prevopt; - } else if ((Menukey.vk == fabgl::VK_ESCAPE) || (Menukey.vk == fabgl::VK_F1)) { + } else if (Menukey.vk == fabgl::VK_ESCAPE || Menukey.vk == fabgl::VK_F1 || Menukey.vk == fabgl::VK_JOY1A || Menukey.vk == fabgl::VK_JOY2A) { if (menu_level!=0) { // Restore backbuffer data @@ -350,6 +345,190 @@ unsigned short OSD::menuRun(string new_menu) { } +// Run a new menu +unsigned short OSD::simpleMenuRun(string new_menu, uint16_t posx, uint16_t posy) { + + fabgl::VirtualKeyItem Menukey; + + menu = new_menu; + + x = posx; + y = posy; + + // Rows + real_rows = rowCount(menu); + virtual_rows = real_rows > 6 ? 6 : real_rows; + + // Columns + cols = 11; + + // Size + w = (cols * OSD_FONT_W) + 2; + h = (virtual_rows * OSD_FONT_H) + 2; + + // Set font + VIDEO::vga.setFont(Font6x8); + + if (menu_saverect) { + + if (menu_level == 0) SaveRectpos = 0; + + // Save backbuffer data + VIDEO::SaveRect[SaveRectpos] = x; + VIDEO::SaveRect[SaveRectpos + 1] = y; + VIDEO::SaveRect[SaveRectpos + 2] = w; + VIDEO::SaveRect[SaveRectpos + 3] = h; + SaveRectpos += 4; + for (int m = y; m < y + h; m++) { + uint32_t *backbuffer32 = (uint32_t *)(VIDEO::vga.backBuffer[m]); + for (int n = x >> 2; n < ((x + w) >> 2) + 1; n++) { + VIDEO::SaveRect[SaveRectpos] = backbuffer32[n]; + SaveRectpos++; + } + } + // printf("SaveRectPos: %04X\n",SaveRectpos << 2); + } + + // Menu border + VIDEO::vga.rect(x, y, w, h, zxColor(0, 0)); + + // Title + PrintRow(0, IS_TITLE); + + begin_row = 1; + focus = menu_curopt; + last_begin_row = last_focus = 0; + + menuRedraw(); // Draw menu content + + while (1) { + + if (ZXKeyb::Exists) ZXKeyb::ZXKbdRead(); + + ESPectrum::readKbdJoy(); + + // Process external keyboard + if (ESPectrum::PS2Controller.keyboard()->virtualKeyAvailable()) { + if (ESPectrum::readKbd(&Menukey)) { + if (!Menukey.down) continue; + if (Menukey.vk == fabgl::VK_UP || Menukey.vk == fabgl::VK_JOY1UP || Menukey.vk == fabgl::VK_JOY2UP) { + if (focus == 1 and begin_row > 1) { + menuScroll(DOWN); + } else { + last_focus = focus; + focus--; + if (focus < 1) { + focus = virtual_rows - 1; + last_begin_row = begin_row; + begin_row = real_rows - virtual_rows + 1; + menuRedraw(); + menuPrintRow(focus, IS_FOCUSED); + } + else { + menuPrintRow(focus, IS_FOCUSED); + menuPrintRow(last_focus, IS_NORMAL); + } + } + click(); + } else if (Menukey.vk == fabgl::VK_DOWN || Menukey.vk == fabgl::VK_JOY1DOWN || Menukey.vk == fabgl::VK_JOY2DOWN) { + if (focus == virtual_rows - 1 && virtual_rows + begin_row - 1 < real_rows) { + menuScroll(UP); + } else { + last_focus = focus; + focus++; + if (focus > virtual_rows - 1) { + focus = 1; + last_begin_row = begin_row; + begin_row = 1; + menuRedraw(); + menuPrintRow(focus, IS_FOCUSED); + } + else { + menuPrintRow(focus, IS_FOCUSED); + menuPrintRow(last_focus, IS_NORMAL); + } + } + click(); + } else if (Menukey.vk == fabgl::VK_PAGEUP || Menukey.vk == fabgl::VK_LEFT || Menukey.vk == fabgl::VK_JOY1LEFT || Menukey.vk == fabgl::VK_JOY2LEFT) { + if (begin_row > virtual_rows) { + focus = 1; + begin_row -= virtual_rows - 1; + } else { + focus = 1; + begin_row = 1; + } + menuRedraw(); + click(); + } else if (Menukey.vk == fabgl::VK_PAGEDOWN || Menukey.vk == fabgl::VK_RIGHT || Menukey.vk == fabgl::VK_JOY1RIGHT || Menukey.vk == fabgl::VK_JOY2RIGHT) { + if (real_rows - begin_row - virtual_rows > virtual_rows) { + focus = 1; + begin_row += virtual_rows - 1; + } else { + focus = virtual_rows - 1; + begin_row = real_rows - virtual_rows + 1; + } + menuRedraw(); + click(); + } else if (Menukey.vk == fabgl::VK_HOME) { + focus = 1; + begin_row = 1; + menuRedraw(); + click(); + } else if (Menukey.vk == fabgl::VK_END) { + focus = virtual_rows - 1; + begin_row = real_rows - virtual_rows + 1; + menuRedraw(); + click(); + } else if (Menukey.vk == fabgl::VK_RETURN || Menukey.vk == fabgl::VK_SPACE || Menukey.vk == fabgl::VK_JOY1B || Menukey.vk == fabgl::VK_JOY1C || Menukey.vk == fabgl::VK_JOY2B || Menukey.vk == fabgl::VK_JOY2C) { + // if (menu_saverect) { + // Restore backbuffer data + int j = SaveRectpos - (((w >> 2) + 1) * h); + //printf("SaveRectpos: %d; J b4 restore: %d\n",SaveRectpos, j); + SaveRectpos = j - 4; + for (int m = y; m < y + h; m++) { + uint32_t *backbuffer32 = (uint32_t *)(VIDEO::vga.backBuffer[m]); + for (int n = x >> 2; n < ((x + w) >> 2) + 1; n++) { + backbuffer32[n] = VIDEO::SaveRect[j]; + j++; + } + } + //printf("SaveRectpos: %d; J b4 restore: %d\n",SaveRectpos, j); + menu_saverect = false; + // } + + click(); + menu_prevopt = menuRealRowFor(focus); + return menu_prevopt; + } else if (Menukey.vk == fabgl::VK_ESCAPE || Menukey.vk == fabgl::VK_F1 || Menukey.vk == fabgl::VK_JOY1A || Menukey.vk == fabgl::VK_JOY2A) { + + // if (menu_saverect) { + // Restore backbuffer data + int j = SaveRectpos - (((w >> 2) + 1) * h); + //printf("SaveRectpos: %d; J b4 restore: %d\n",SaveRectpos, j); + SaveRectpos = j - 4; + for (int m = y; m < y + h; m++) { + uint32_t *backbuffer32 = (uint32_t *)(VIDEO::vga.backBuffer[m]); + for (int n = x >> 2; n < ((x + w) >> 2) + 1; n++) { + backbuffer32[n] = VIDEO::SaveRect[j]; + j++; + } + } + //printf("SaveRectpos: %d; J b4 restore: %d\n",SaveRectpos, j); + menu_saverect = false; + // } + + click(); + return 0; + } + } + } + + vTaskDelay(5 / portTICK_PERIOD_MS); + + } + +} + // Scroll void OSD::menuScroll(bool dir) { if ((dir == DOWN) && (begin_row > 1)) { @@ -662,7 +841,7 @@ int OSD::menuTape(string title) { if (!Menukey.down) continue; - if (Menukey.vk == fabgl::VK_UP) { + if (Menukey.vk == fabgl::VK_UP || Menukey.vk == fabgl::VK_JOY1UP || Menukey.vk == fabgl::VK_JOY2UP) { if (focus == 1 and begin_row > 1) { if (begin_row > 1) { last_begin_row = begin_row; @@ -677,7 +856,7 @@ int OSD::menuTape(string title) { PrintRow(focus + 1, IS_NORMAL); click(); } - } else if (Menukey.vk == fabgl::VK_DOWN) { + } else if (Menukey.vk == fabgl::VK_DOWN || Menukey.vk == fabgl::VK_JOY1DOWN || Menukey.vk == fabgl::VK_JOY2DOWN) { if (focus == virtual_rows - 1) { if ((begin_row + virtual_rows - 1) < real_rows) { last_begin_row = begin_row; @@ -692,7 +871,7 @@ int OSD::menuTape(string title) { PrintRow(focus - 1, IS_NORMAL); click(); } - } else if ((Menukey.vk == fabgl::VK_PAGEUP) || (Menukey.vk == fabgl::VK_LEFT)) { + } else if (Menukey.vk == fabgl::VK_PAGEUP || Menukey.vk == fabgl::VK_LEFT || Menukey.vk == fabgl::VK_JOY1LEFT || Menukey.vk == fabgl::VK_JOY2LEFT) { // printf("%u\n",begin_row); if (begin_row > virtual_rows) { last_focus = focus; @@ -709,7 +888,7 @@ int OSD::menuTape(string title) { tapemenuRedraw(title); click(); } - } else if ((Menukey.vk == fabgl::VK_PAGEDOWN) || (Menukey.vk == fabgl::VK_RIGHT)) { + } else if (Menukey.vk == fabgl::VK_PAGEDOWN || Menukey.vk == fabgl::VK_RIGHT || Menukey.vk == fabgl::VK_JOY1RIGHT || Menukey.vk == fabgl::VK_JOY2RIGHT) { if (real_rows - begin_row - virtual_rows > virtual_rows) { last_focus = focus; last_begin_row = begin_row; @@ -739,12 +918,12 @@ int OSD::menuTape(string title) { begin_row = real_rows - virtual_rows + 1; tapemenuRedraw(title); click(); - } else if (Menukey.vk == fabgl::VK_RETURN || Menukey.vk == fabgl::VK_SPACE) { + } else if (Menukey.vk == fabgl::VK_RETURN || Menukey.vk == fabgl::VK_SPACE || Menukey.vk == fabgl::VK_JOY1B || Menukey.vk == fabgl::VK_JOY2B || Menukey.vk == fabgl::VK_JOY1C || Menukey.vk == fabgl::VK_JOY2C) { click(); Tape::CalcTapBlockPos(begin_row + focus - 2); // printf("Ret value: %d\n", begin_row + focus - 2); return (begin_row + focus - 2); - } else if (Menukey.vk == fabgl::VK_ESCAPE) { + } else if (Menukey.vk == fabgl::VK_ESCAPE || Menukey.vk == fabgl::VK_JOY1A || Menukey.vk == fabgl::VK_JOY2A) { // if (Tape::tapeStatus==TAPE_LOADING) { fseek(Tape::tape, tapeBckPos, SEEK_SET); diff --git a/src/Ports.cpp b/src/Ports.cpp index aa878a08..b0ff1d23 100644 --- a/src/Ports.cpp +++ b/src/Ports.cpp @@ -140,7 +140,10 @@ IRAM_ATTR uint8_t Ports::input(uint16_t address) { } // Kempston Joystick - if ((Config::joystick) && ((address & 0x00E0) == 0 || (address & 0xFF) == 0xDF)) return port[0x1f]; + if ((Config::joystick1 == JOY_KEMPSTON || Config::joystick2 == JOY_KEMPSTON || Config::joyPS2 == JOYPS2_KEMPSTON) && ((address & 0x00E0) == 0 || (address & 0xFF) == 0xDF)) return port[0x1f]; + + // Fuller Joystick + if ((Config::joystick1 == JOY_FULLER || Config::joystick2 == JOY_FULLER || Config::joyPS2 == JOYPS2_FULLER) && (address & 0xFF) == 0x7F) return port[0x7f]; // Sound (AY-3-8912) if (ESPectrum::AY_emu) { diff --git a/src/Video.cpp b/src/Video.cpp index b5083a5f..8b1ddfb5 100644 --- a/src/Video.cpp +++ b/src/Video.cpp @@ -55,14 +55,33 @@ uint8_t VIDEO::flash_ctr= 0; bool VIDEO::OSD = false; uint8_t VIDEO::tStatesPerLine; int VIDEO::tStatesScreen; -// unsigned int VIDEO::tstateDraw; // Drawing start point (in Tstates) -// unsigned int VIDEO::linedraw_cnt; uint8_t* VIDEO::grmem; uint32_t* VIDEO::SaveRect; int VIDEO::VsyncFinetune[2]; -// uint8_t VIDEO::dispUpdCycle; uint32_t VIDEO::framecnt = 0; +static unsigned int is169; + +static uint16_t offBmp[SPEC_H]; +static uint16_t offAtt[SPEC_H]; + +static uint32_t* AluBytes[16]; + +// static unsigned char DrawStatus; + +static uint32_t* lineptr32; +static uint16_t* lineptr16; + +static unsigned int tstateDraw; // Drawing start point (in Tstates) +static unsigned int linedraw_cnt; +static unsigned int lin_end; +static unsigned int coldraw_cnt; +static unsigned int col_end; +static unsigned int video_rest; + +static unsigned int bmpOffset; // offset for bitmap in graphic memory +static unsigned int attOffset; // offset for attrib in graphic memory + DRAM_ATTR static const uint8_t wait_st[243] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 5, 4, 3, 2, 1, 0, 0, 6, 5, 4, 3, 2, 1, 0, 0, @@ -141,9 +160,8 @@ void (*VIDEO::DrawOSD169)(unsigned int, bool) = &VIDEO::MainScreen; void precalcColors() { - for (int i = 0; i < NUM_SPECTRUM_COLORS; i++) { + for (int i = 0; i < NUM_SPECTRUM_COLORS; i++) spectrum_colors[i] = (spectrum_colors[i] & VIDEO::vga.RGBAXMask) | VIDEO::vga.SBits; - } } @@ -184,9 +202,9 @@ void precalcAluBytes() { } // Alloc ALUbytes - for (int i = 0; i < 16; i++) { - AluBytes[i] = (uint32_t *) heap_caps_malloc(0x400, MALLOC_CAP_INTERNAL | MALLOC_CAP_32BIT); - } + for (int i = 0; i < 16; i++) { + AluBytes[i] = (uint32_t *) heap_caps_malloc(0x400, MALLOC_CAP_INTERNAL | MALLOC_CAP_32BIT); + } for (int i = 0; i < 16; i++) { for (int n = 0; n < 256; n++) { @@ -202,11 +220,6 @@ void precalcAluBytes() { } -uint16_t zxColor(uint8_t color, uint8_t bright) { - if (bright) color += 8; - return spectrum_colors[color]; -} - // Precalc ULA_SWAP #define ULA_SWAP(y) ((y & 0xC0) | ((y & 0x38) >> 3) | ((y & 0x07) << 3)) void precalcULASWAP() { @@ -605,7 +618,7 @@ void VIDEO::MainScreen(unsigned int statestoadd, bool contended) { video_rest = statestoadd & 0x03; - int loopCount = statestoadd >> 2; + unsigned int loopCount = statestoadd >> 2; if (loopCount) { From eb0f68663897edd6d8a0f848287f5667c5b611b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Iborra?= Date: Fri, 29 Dec 2023 12:07:27 +0100 Subject: [PATCH 30/30] Readme.md update --- README.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 02dd0d55..60506175 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,8 @@ This project is based on David Crespo excellent work on [ZX-ESPectrum-Wiimote](h ## Features -- ZX Spectrum 48K, 128K and Pentagon 128K emulation (no PSRAM needed). -- Accurate Z80 emulation (Authored by [José Luis Sánchez](https://github.com/jsanchezv/z80cpp)) +- ZX Spectrum 48K, 128K and Pentagon 128K 100% cycle accurate emulation (no PSRAM needed). +- Perfect Z80 emulation (Authored by [José Luis Sánchez](https://github.com/jsanchezv/z80cpp)) - 6 bpp VGA output in three modes: Standard VGA (60 and 70hz), VGA 50hz and CRT 15khz 50hz. - Support for two aspect ratios: 16:9 or 4:3 monitors (using 360x200 or 320x240 modes) - Multicolor attribute effects emulated (Bifrost*2, Nirvana and Nirvana+ engines). @@ -21,19 +21,19 @@ This project is based on David Crespo excellent work on [ZX-ESPectrum-Wiimote](h - AY-3-8912 sound emulation. - Beeper & Mic emulation (Cobra’s Arc). - Dual PS/2 keyboard support: you can connect two devices using PS/2 protocol at the same time. -- Kempston and Cursor type Joystick emulation. +- PS/2 Joystick emulation (Cursor, Sinclair, Kempston and Fuller). +- Two real joysticks support (Up to 8 button joysticks) using [ESPjoy adapter](https://antoniovillena.es/store/product/espjoy-for-espectrum/) or DIY DB9 to PS/2 converter. - Emulation of Betadisk interface with four drives and TRD (read and write) and SCL (read only) support. - Realtime and fast TAP file loading. - TAP file saving to SD card. - SNA and Z80 snapshot loading. - Snapshot saving and loading. +- Complete file navigation system with autoindexing, folder support and search functions. - Complete OSD menu in two languages: English & Spanish. - BMP screen capture to SD Card (thanks David Crespo 😉). ## Work in progress -- Folder support. -- Customizable joystick maps. - On screen keyboard. - TZX support. @@ -67,13 +67,8 @@ Run these tasks (`Upload` also does a `Build`) whenever you make any change in t The SD card should be formatted in FAT16 / FAT32 and you must create the following folders in root directory: -- "p" folder -> Will be used for persist snapshots. -- "s" folder -> Put .SNA and .Z80 files here. -- "d" folder -> Put .TRD and .SCL files here. -- "t" folder -> Put .TAP files here. -- "c" folder -> For SCR (not yet!) and BMP screen captures. - -First time the emulator access sna, tape or disk directories, it will create and index for sorting the files in it. It may take some time if you put many archives (15-20 seconds in my tests for about 1000 files, about 3 minutes for about 6000 files). Once created, file dialogs will open fast but if you extract the card and add files, you must later use "Options/Storage/Refresh directories" to be able to view new files on the files dialogs. +- ".p" folder -> Will be used for persist snapshots. +- ".c" folder -> For BMP screen captures. ## PS/2 Keyboard functions @@ -91,6 +86,8 @@ First time the emulator access sna, tape or disk directories, it will create and - F12 Reset ESP32 - Pause Pause - PrntScr BMP screen capture (Folder /c at SDCard) +- CTRL + F1 Hardware info +- CTRL + F5..F8 Screen centering in CRT 15K/50hz mode ## ZX Keyboard functions @@ -109,7 +106,10 @@ Press CAPS SHIFT + SYMBOL SHIFT and: - Q Hard reset - W Reset ESP32 - P Pause -- C BMP screen capture (Folder /c at SDCard) +- S BMP screen capture (Folder /c at SDCard) +- I Hardware info +- Z, X, C and V Screen centering in CRT 15K/50hz mode + ## Hardware configuration and pinout