diff --git a/README.md b/README.md index dadfb6eaa..3f2706f03 100644 --- a/README.md +++ b/README.md @@ -28,11 +28,12 @@ _Note that Release builds have an update checking feature, which is disabled in * Full screen modes (F11 key) * Emulation states for efficient saving / restoring * CE skins (colors like the real devices) -* Available in English, French, Spanish, and Dutch +* Available in English, French, Spanish, Dutch, and Chinese ### _Developer features_ * Main options available via CLI arguments * IPC features when launching several CEmu processes +* Choice of ASIC / HW revision emulation (A, pre-I, M+...) * Import/Export RAM, ROM, images... * Custom display refresh rate, FPS indicator * Custom emulation speed/throttling @@ -45,6 +46,7 @@ _Note that Release builds have an update checking feature, which is disabled in * Memory viewer/editor * CPU state/registers viewer/editor * LCD state/parameters viewer/editor +* Tracking of flash cache misses and average access time * Memory visualizer (as fully customizable virtual LCDs) * Stack viewer * OP1-7 viewer @@ -52,11 +54,11 @@ _Note that Release builds have an update checking feature, which is disabled in * Variable Allocation Table (VAT) viewer * Variable list with preview and program launcher * TI-Basic program viewer with syntax-highlight and reformatting +* TI-Basic program debugger with line-by-line stepping * Recent files list with ability to quickly resend * Cycle counter for benchmarking/profiling * Emulation of DMA and SPI (for optimal accuracy) * Misc. emulation (backlight, battery...) -* Pre-I HW Rev. emulation toggle (IM 2 compatibility) * "Autotester" (automated unit testing, light scripting) ## How to build diff --git a/core/asic.c b/core/asic.c index 5276185a3..d37eb3f32 100644 --- a/core/asic.c +++ b/core/asic.c @@ -3,7 +3,9 @@ #include "misc.h" #include "mem.h" #include "lcd.h" +#include "panel.h" #include "spi.h" +#include "uart.h" #include "usb/usb.h" #include "bus.h" #include "emu.h" @@ -17,7 +19,9 @@ #include "backlight.h" #include "realclock.h" #include "defines.h" +#include "cert.h" +#include #include #include #include @@ -56,13 +60,14 @@ static void plug_devices(void) { port_map[0xB] = init_backlight(); port_map[0xC] = init_cxxx(); port_map[0xD] = init_spi(); - port_map[0xE] = init_exxx(); + port_map[0xE] = init_uart(); port_map[0xF] = init_fxxx(); reset_proc_count = 0; /* Populate reset callbacks */ add_reset_proc(sched_reset); + add_reset_proc(flash_reset); add_reset_proc(mem_reset); add_reset_proc(lcd_reset); add_reset_proc(keypad_reset); @@ -75,11 +80,48 @@ static void plug_devices(void) { add_reset_proc(usb_reset); add_reset_proc(control_reset); add_reset_proc(backlight_reset); + add_reset_proc(panel_reset); add_reset_proc(spi_reset); + add_reset_proc(uart_reset); gui_console_printf("[CEmu] Initialized Advanced Peripheral Bus...\n"); } +static asic_rev_t report_reset(asic_rev_t loaded_rev, bool* python) { + /* Parse boot code routines to determine version. */ + asic_rev_t default_rev = ASIC_REV_A; + boot_ver_t boot_ver; + bool gotVer = bootver_parse(mem.flash.block, &boot_ver); + if (gotVer) { + gui_console_printf("[CEmu] Boot code version: %u.%u.%u.%04u\n", + boot_ver.major, boot_ver.minor, boot_ver.revision, boot_ver.build); + + /* Determine the newest ASIC revision that is compatible */ + for (int rev = ASIC_REV_A; rev <= ASIC_REV_M; rev++) { + if (bootver_check_rev(&boot_ver, (asic_rev_t)rev)) { + default_rev = rev; + } + } + + /* By default, ignore Python Edition in certificate if boot code is too old */ + if (loaded_rev == ASIC_REV_AUTO && default_rev < ASIC_REV_M) { + *python = false; + } + } + else { + gui_console_printf("[CEmu] Could not determine boot code version.\n"); + } + gui_console_printf("[CEmu] Default ASIC revision is Rev %c.\n", "AIM"[(int)default_rev - 1]); + + loaded_rev = gui_handle_reset((gotVer ? &boot_ver : NULL), loaded_rev, default_rev, python); + return (loaded_rev != ASIC_REV_AUTO) ? loaded_rev : default_rev; +} + +static void set_features() { + asic.im2 = (asic.revision < ASIC_REV_I); + asic.serFlash = (asic.revision >= ASIC_REV_M); +} + void asic_init(void) { /* First, initilize memory and CPU */ mem_init(); @@ -102,9 +144,13 @@ void asic_free(void) { } void asic_reset(void) { - unsigned int i; + /* Update the Python state first, so it can be read by the reset handler if needed */ + static const uint16_t path[] = { 0x0330, 0x0430 }; + asic.python = !cert_field_find_path(mem.flash.block + 0x3B0001, SIZE_FLASH_SECTOR_64K, path, 2, NULL, NULL); + asic.revision = report_reset(ASIC_REV_AUTO, &asic.python); + set_features(); - for(i = 0; i < reset_proc_count; i++) { + for (unsigned int i = 0; i < reset_proc_count; i++) { reset_procs[i](); } } @@ -117,35 +163,53 @@ ti_device_t EMSCRIPTEN_KEEPALIVE get_device_type(void) { return asic.device; } +asic_rev_t EMSCRIPTEN_KEEPALIVE get_asic_revision(void) { + return asic.revision; +} + +bool EMSCRIPTEN_KEEPALIVE get_asic_python(void) { + return asic.python; +} + void set_cpu_clock(uint32_t new_rate) { sched_set_clock(CLOCK_CPU, new_rate); } bool asic_restore(FILE *image) { - return fread(&asic.device, sizeof(asic.device), 1, image) == 1 - && backlight_restore(image) - && control_restore(image) - && cpu_restore(image) - && flash_restore(image) - && intrpt_restore(image) - && keypad_restore(image) - && lcd_restore(image) - && mem_restore(image) - && watchdog_restore(image) - && protect_restore(image) - && rtc_restore(image) - && sha256_restore(image) - && gpt_restore(image) - && usb_restore(image) - && cxxx_restore(image) - && spi_restore(image) - && exxx_restore(image) - && sched_restore(image) - && fgetc(image) == EOF; + if (fread(&asic, offsetof(asic_state_t, im2), 1, image) != 1) { + return false; + } + set_features(); + if (backlight_restore(image) + && control_restore(image) + && cpu_restore(image) + && flash_restore(image) + && intrpt_restore(image) + && keypad_restore(image) + && lcd_restore(image) + && mem_restore(image) + && watchdog_restore(image) + && protect_restore(image) + && rtc_restore(image) + && sha256_restore(image) + && gpt_restore(image) + && usb_restore(image) + && cxxx_restore(image) + && panel_restore(image) + && spi_restore(image) + && uart_restore(image) + && sched_restore(image) + && fgetc(image) == EOF) + { + bool python = asic.python; + (void)report_reset(asic.revision, &python); + return true; + } + return false; } bool asic_save(FILE *image) { - return fwrite(&asic.device, sizeof(asic.device), 1, image) == 1 + return fwrite(&asic, offsetof(asic_state_t, im2), 1, image) == 1 && backlight_save(image) && control_save(image) && cpu_save(image) @@ -161,7 +225,8 @@ bool asic_save(FILE *image) { && gpt_save(image) && usb_save(image) && cxxx_save(image) + && panel_save(image) && spi_save(image) - && exxx_save(image) + && uart_save(image) && sched_save(image); } diff --git a/core/asic.h b/core/asic.h index ab4889b4a..85ef6cdf4 100644 --- a/core/asic.h +++ b/core/asic.h @@ -14,8 +14,21 @@ typedef enum { TI83PCE = 1 } ti_device_t; +typedef enum { + ASIC_REV_AUTO = 0, /* Used only with set_asic_revision() */ + ASIC_REV_A = 1, + ASIC_REV_I = 2, + ASIC_REV_M = 3 +} asic_rev_t; + typedef struct asic_state { ti_device_t device; + /* Only updated on reset */ + asic_rev_t revision; + bool python; + /* Populated based on revision */ + bool im2; + bool serFlash; } asic_state_t; extern asic_state_t asic; @@ -28,6 +41,8 @@ bool asic_save(FILE *image); void set_cpu_clock(uint32_t new_rate); void set_device_type(ti_device_t device); ti_device_t get_device_type(void); +asic_rev_t get_asic_revision(void); +bool get_asic_python(void); #ifdef __cplusplus } diff --git a/core/bootver.c b/core/bootver.c new file mode 100644 index 000000000..a58f47aef --- /dev/null +++ b/core/bootver.c @@ -0,0 +1,125 @@ +#include "bootver.h" + +static const boot_ver_t asic_min_ver[] = { + { 5, 0, 0, 0 }, /* Rev A */ + { 5, 0, 0, 0 }, /* Rev I */ + { 5, 3, 6, 0 }, /* Rev M */ +}; + +static const boot_ver_t asic_max_ver[] = { + { 5, 3, 5, 65535 }, /* Rev A */ + { 5, 3, 5, 65535 }, /* Rev I */ + { 255, 255, 255, 65535 }, /* Rev M */ +}; + +static bool parse_entry(const uint8_t* data, uint32_t entry, uint32_t* addr) { + if (entry + 4 >= SIZE_BOOTCODE) { + return false; + } + data += entry; + + /* JP addr */ + if (data[0] != 0xC3) { + return false; + } + + *addr = data[1] | (((uint32_t)data[2]) << 8) | (((uint32_t)data[3]) << 16); + return true; +} + +static bool parse_ver_8(const uint8_t* data, uint32_t addr, uint8_t* ver) { + if (addr + 3 >= SIZE_BOOTCODE) { + return false; + } + data += addr; + + /* LD A,version; RET */ + if ((data[0] != 0x3E) || (data[2] != 0xC9)) { + return false; + } + + *ver = data[1]; + return true; +} + +static bool parse_ver_16(const uint8_t* data, uint32_t addr, uint16_t* ver) { + if (addr + 5 >= SIZE_BOOTCODE) { + return false; + } + data += addr; + + /* LD A,versionHigh; LD B,versionLow; RET */ + if ((data[0] != 0x3E) || (data[2] != 0x06) || (data[4] != 0xC9)) { + return false; + } + + *ver = (((uint16_t)data[1]) << 8) | data[3]; + return true; +} + +bool bootver_parse(const uint8_t* data, boot_ver_t* boot_ver) { + uint32_t addr; + uint16_t major_minor, build; + uint8_t revision; + + if (!data) { + return false; + } + + /* _boot_GetBootVerMajor */ + if (!parse_entry(data, 0x000080, &addr)) { + return false; + } + if (!parse_ver_16(data, addr, &major_minor)) { + return false; + } + + /* _boot_GetBootVerMinor */ + if (!parse_entry(data, 0x00008C, &addr)) { + return false; + } + if (!parse_ver_8(data, addr, &revision)) { + return false; + } + + /* _boot_GetBootVerBuild */ + if (!parse_entry(data, 0x000090, &addr)) { + return false; + } + if (!parse_ver_16(data, addr, &build)) { + return false; + } + + boot_ver->major = (uint8_t)(major_minor >> 8); + boot_ver->minor = (uint8_t)major_minor; + boot_ver->revision = revision; + boot_ver->build = build; + return true; +} + +bool bootver_check_ver(const boot_ver_t* ver, const boot_ver_t* check) { + if (ver->major != check->major) { + return ver->major > check->major; + } + if (ver->minor != check->minor) { + return ver->minor > check->minor; + } + if (ver->revision != check->revision) { + return ver->revision > check->revision; + } + return ver->build >= check->build; +} + +bool bootver_check_rev(const boot_ver_t* ver, asic_rev_t rev) { + unsigned int index = (unsigned int)rev - 1; + if (index >= sizeof(asic_min_ver) / sizeof(boot_ver_t)) { + return false; + } + + if (!ver) { + return true; + } + + return bootver_check_ver(ver, asic_min_ver + index) + && bootver_check_ver(asic_max_ver + index, ver); +} diff --git a/core/bootver.h b/core/bootver.h new file mode 100644 index 000000000..736d8e79c --- /dev/null +++ b/core/bootver.h @@ -0,0 +1,33 @@ +#ifndef BOOTVER_H +#define BOOTVER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include "asic.h" + +#define SIZE_BOOTCODE 0x20000 + + typedef struct { + uint8_t major; + uint8_t minor; + uint8_t revision; + uint16_t build; + } boot_ver_t; + + bool bootver_parse(const uint8_t* data, boot_ver_t* boot_ver); + + bool bootver_check_ver(const boot_ver_t* ver, const boot_ver_t* check); + + bool bootver_check_rev(const boot_ver_t* ver, asic_rev_t rev); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/core/control.c b/core/control.c index fe2e335e2..20192b575 100644 --- a/core/control.c +++ b/core/control.c @@ -4,6 +4,8 @@ #include "mem.h" #include "cpu.h" #include "lcd.h" +#include "panel.h" +#include "spi.h" #include "bus.h" #include "debug/debug.h" #include "usb/usb.h" @@ -27,7 +29,7 @@ static uint8_t control_read(const uint16_t pio, bool peek) { value = control.readBatteryStatus; break; case 0x03: - value = get_device_type(); + value = get_device_type() | asic.serFlash << 4; break; case 0x06: value = control.protectedPortsUnlocked; @@ -137,6 +139,7 @@ static void control_write(const uint16_t pio, const uint8_t byte, bool poke) { break; case 0x07: control.readBatteryStatus = (byte & 0x90) ? 1 : 0; + control.ports[index] = byte; break; case 0x09: switch (control.readBatteryStatus) { @@ -144,25 +147,26 @@ static void control_write(const uint16_t pio, const uint8_t byte, bool poke) { control.readBatteryStatus = control.setBatteryStatus == BATTERY_DISCHARGED ? 0 : byte & 0x80 ? 0 : 3; break; } + if ((control.ports[index] & ~byte) >> 2 & 1) { + panel_hw_reset(); + } + if (asic.python && (control.ports[index] ^ byte) >> 4 & 1) { + spi_device_select(byte >> 4 & 1); + } control.ports[index] = byte; - if (byte == 0xD4) { + if ((byte & ~(1 << 4)) == 0xC4) { control.ports[0] |= 1 << 6; cpu_crash("entering sleep mode"); -#ifdef DEBUG_SUPPORT - if (debug.openOnReset) { - debug_open(DBG_MISC_RESET, cpu.registers.PC); - } -#endif } break; case 0x0A: control.readBatteryStatus += (control.readBatteryStatus == 3) ? 1 : 0; control.ports[index] = byte; break; - case 0x0B: case 0x0C: control.readBatteryStatus = 0; + control.ports[index] = byte; break; case 0x0D: /* This bit disables vram and makes it garbage */ @@ -258,7 +262,10 @@ bool flash_unlocked(void) { } bool unprivileged_code(void) { - return cpu.registers.rawPC > control.privileged && - (cpu.registers.rawPC < control.protectedStart || - cpu.registers.rawPC > control.protectedEnd); + /* rawPC the PC after the next prefetch (which we do late), before (after on revM) adding MBASE. */ + uint32_t rawPC = cpu.registers.PC + 1; + bool mode = cpu.ADL; + rawPC = asic.serFlash ? cpu_address_mode(rawPC, mode) : cpu_mask_mode(rawPC, mode); + return rawPC > control.privileged && (rawPC < control.protectedStart || + rawPC > control.protectedEnd); } diff --git a/core/cpu.c b/core/cpu.c index c5e369137..d7f9b1b05 100644 --- a/core/cpu.c +++ b/core/cpu.c @@ -56,8 +56,6 @@ uint32_t cpu_address_mode(uint32_t address, bool mode) { } static void cpu_prefetch(uint32_t address, bool mode) { cpu.ADL = mode; - /* rawPC the PC after the next prefetch (which we do late), before adding MBASE. */ - cpu.registers.rawPC = cpu_mask_mode(address + 1, mode); cpu.registers.PC = cpu_address_mode(address, mode); cpu.prefetch = mem_read_cpu(cpu.registers.PC, true); } @@ -814,9 +812,7 @@ void cpu_init(void) { } void cpu_reset(void) { - bool preI = cpu.preI; memset(&cpu, 0, sizeof(cpu)); - cpu.preI = preI; cpu_restore_next(); cpu_flush(0, false); gui_console_printf("[CEmu] CPU reset.\n"); @@ -870,7 +866,7 @@ static void cpu_halt(void) { void cpu_restore_next(void) { if (cpu.NMI || (cpu.IEF1 && (intrpt->status & intrpt->enabled)) || cpu.abort != CPU_ABORT_NONE) { - cpu.next = cpu.cycles; + cpu.next = 0; /* always applies, even after cycle rewind during port writes */ } else if (cpu.IEF_wait) { cpu.next = cpu.eiDelay; /* execute one instruction */ } else { @@ -902,8 +898,12 @@ void cpu_execute(void) { cpu.IEF1 = cpu.IEF2 = true; } if (cpu.NMI || (cpu.IEF1 && (intrpt->status & intrpt->enabled))) { - cpu_prefetch_discard(); - cpu.cycles += 2; + if (cpu.halted) { + cpu.cycles++; + } else { + cpu_prefetch_discard(); + } + cpu.cycles++; cpu.L = cpu.IL = cpu.ADL || cpu.MADL; cpu.IEF1 = cpu.halted = cpu.inBlock = false; if (cpu.NMI) { @@ -914,7 +914,7 @@ void cpu_execute(void) { if (cpu.IM == 2) { cpu_interrupt(0x38); } else { - if (cpu.preI && cpu.IM == 3) { + if (asic.im2 && cpu.IM == 3) { cpu.cycles++; cpu_interrupt(cpu_read_word(r->I << 8 | (bus_rand() & 0xFF))); } else { @@ -1130,6 +1130,7 @@ void cpu_execute(void) { continue; } if (context.z == 6) { /* HALT */ + cpu.cycles++; cpu_halt(); } } else { @@ -1497,6 +1498,7 @@ void cpu_execute(void) { r->A = r->MBASE; break; case 6: /* SLP */ + cpu.cycles++; cpu_halt(); break; case 7: /* RSMIX */ diff --git a/core/cpu.h b/core/cpu.h index 654860a71..a27642d7d 100644 --- a/core/cpu.h +++ b/core/cpu.h @@ -63,6 +63,8 @@ typedef struct eZ80cpu { eZ80context_t context; uint32_t seconds, cycles, eiDelay, next; uint64_t baseCycles, haltCycles, dmaCycles; + uint32_t flashTotalAccesses, flashCacheMisses; + int64_t flashDelayCycles; uint8_t prefetch; _Atomic(uint8_t) abort; struct { @@ -83,7 +85,6 @@ typedef struct eZ80cpu { bool IEF_wait : 1; /* Wait for interrupt enable */ bool halted : 1; /* Have we halted the CPU? */ bool inBlock : 1; /* Are we processing a block instruction? */ - bool preI : 1; }; } eZ80cpu_t; diff --git a/core/debug/debug.c b/core/debug/debug.c index fcda3768b..8c744c234 100644 --- a/core/debug/debug.c +++ b/core/debug/debug.c @@ -4,6 +4,7 @@ #include "../mem.h" #include "../emu.h" #include "../cpu.h" +#include "../flash.h" #include "../vat.h" #include @@ -79,6 +80,10 @@ void debug_open(int reason, uint32_t data) { debug.cpuHaltCycles = cpu.haltCycles; debug.totalCycles += sched_total_cycles(); debug.dmaCycles += cpu.dmaCycles; + debug.flashCacheMisses = cpu.flashCacheMisses; + debug.flashTotalAccesses = cpu.flashTotalAccesses; + debug.flashWaitStates = flash.waitStates; + debug.flashDelayCycles = cpu.flashDelayCycles; debug.open = true; gui_debug_open(reason, data); @@ -90,6 +95,9 @@ void debug_open(int reason, uint32_t data) { cpu.haltCycles = debug.cpuHaltCycles; debug.dmaCycles -= cpu.dmaCycles; debug.totalCycles -= sched_total_cycles(); + cpu.flashCacheMisses = debug.flashCacheMisses; + cpu.flashTotalAccesses = debug.flashTotalAccesses; + cpu.flashDelayCycles = debug.flashDelayCycles; } void debug_watch(uint32_t addr, int mask, bool set) { diff --git a/core/debug/debug.h b/core/debug/debug.h index ea068c787..1235bbff3 100644 --- a/core/debug/debug.h +++ b/core/debug/debug.h @@ -138,6 +138,8 @@ typedef struct { uint64_t cpuBaseCycles; uint64_t cpuHaltCycles; int64_t totalCycles, dmaCycles; + uint32_t flashCacheMisses, flashTotalAccesses, flashWaitStates; + int64_t flashDelayCycles; bool step, stepOver; uint32_t tempExec, stepOut; diff --git a/core/emu.c b/core/emu.c index 893b87618..a76a13ff0 100644 --- a/core/emu.c +++ b/core/emu.c @@ -17,7 +17,7 @@ #include #endif -#define IMAGE_VERSION 0xCECE0015 +#define IMAGE_VERSION 0xCECE0016 void EMSCRIPTEN_KEEPALIVE emu_exit(void) { cpu.abort = CPU_ABORT_EXIT; diff --git a/core/emu.h b/core/emu.h index 81d853631..1d382a2d8 100644 --- a/core/emu.h +++ b/core/emu.h @@ -8,7 +8,7 @@ extern "C" { #endif #include "schedule.h" -#include "asic.h" +#include "bootver.h" #include #include #include @@ -42,6 +42,16 @@ void gui_console_clear(void); /* sent to clear the c void gui_console_printf(const char *format, ...); /* printf from the core to stdout */ void gui_console_err_printf(const char *format, ...); /* printf from the core to stderr */ +/* called at reset or state load, indicates hardware info and allows specifying a revision on reset + * params: + * boot_ver: boot code version if found, else NULL + * loaded_rev: ASIC_REV_AUTO on reset, or loaded revision on state load + * default_rev: default revision to use when returning ASIC_REV_AUTO + * python: determined edition based on certificate, can be overridden by updating its value + * returns: + * hardware revision to use; ignored when loading state */ +asic_rev_t gui_handle_reset(const boot_ver_t* boot_ver, asic_rev_t loaded_rev, asic_rev_t default_rev, bool* python); + #ifdef DEBUG_SUPPORT void gui_debug_open(int reason, uint32_t data); /* open the gui debugger */ void gui_debug_close(void); /* disable the gui debugger if called */ diff --git a/core/flash.c b/core/flash.c index bbbd5dabb..55682a24e 100644 --- a/core/flash.c +++ b/core/flash.c @@ -1,75 +1,329 @@ #include "flash.h" +#include "control.h" +#include "cpu.h" #include "emu.h" +#include "mem.h" #include "os/os.h" -#include +#include #include +#include /* Global flash state */ flash_state_t flash; -static void flash_set_map(uint8_t map) { - flash.map = map & 0x0F; - if (map & 8) { - flash.mask = 0xFFFF; +static void flash_set_map(void) { + /* Determine how many bytes of flash are mapped */ + if (flash.ports[0x00] == 0 || flash.ports[0x01] > 0x3F) { + flash.mappedBytes = 0; + } else { + uint8_t map = flash.ports[0x02]; + flash.mappedBytes = 0x10000 << (map < 8 ? map : 0); + } + + /* Set new waitstates and adjust recorded delay cycles */ + uint32_t waitStates = flash.ports[0x05] + 6; + cpu.flashDelayCycles += (int64_t)cpu.flashTotalAccesses * (int32_t)(flash.waitStates - waitStates); + flash.waitStates = waitStates; + + /* Set the effective access bounds, overriding for low wait states */ + if (unlikely(flash.waitStates == 6)) { + flash.mask = 0; } else { - flash.mask = ((0x10000 << (map & 7)) - 1) & 0x3FFFFF; + flash.mask = flash.mappedBytes; } } -/* Read from the 0x1000 range of ports */ -static uint8_t flash_read(const uint16_t pio, bool peek) { - uint8_t index = (uint8_t)pio; - uint8_t value; - (void)peek; +static void flash_set_mask(void) { + uint32_t value = flash.maskReg[0] + | flash.maskReg[1] << 8 + | flash.maskReg[2] << 16 + | flash.maskReg[3] << 24; + flash.mask = ~value & (SIZE_FLASH - 1); +} + +void flash_flush_cache(void) { + /* Flush only if the cache has been used since the last flush */ + if (flash.lastCacheLine != FLASH_CACHE_INVALID_LINE) { + flash.lastCacheLine = FLASH_CACHE_INVALID_LINE; + for (unsigned int set = 0; set < FLASH_CACHE_SETS; set++) { + flash.cacheTags[set].mru = flash.cacheTags[set].lru = FLASH_CACHE_INVALID_TAG; + } + } +} + +uint32_t flash_touch_cache(uint32_t addr) { + if (unlikely(++cpu.flashTotalAccesses == 0)) { + cpu.flashTotalAccesses = 0x80000000; + cpu.flashDelayCycles >>= 1; + } + uint32_t line = addr >> FLASH_CACHE_LINE_BITS; + if (likely(line == flash.lastCacheLine)) { + return 2; + } else { + flash.lastCacheLine = line; + flash_cache_set_t* set = &flash.cacheTags[line & (FLASH_CACHE_SETS - 1)]; + uint16_t tag = (uint16_t)(line >> FLASH_CACHE_SET_BITS); + if (likely(set->mru == tag)) { + cpu.flashDelayCycles++; + return 3; + } else if (likely(set->lru == tag)) { + /* Swap to track most-recently-used */ + set->lru = set->mru; + set->mru = tag; + cpu.flashDelayCycles++; + return 3; + } else { + /* Handle a cache miss by replacing least-recently-used */ + set->lru = tag; + cpu.flashCacheMisses++; + /* Supposedly this takes from 195-201 cycles, but typically seems to be 196-197 */ + cpu.flashDelayCycles += 195; + return 197; + } + } +} + +static void flash_finish_command(void) { + flash.commandStatus[0] |= 1 << 0; + switch (flash.command[0xF]) { + // Reset instructions + case 0x04: // Write Disable + case 0x01: // Write Status Register-1 + case 0x31: // Write Status Register-2 + case 0x11: // Write Status Register-3 + case 0x02: // Page Program + case 0x32: // Quad Input Page Program + case 0x20: // Sector Erase + case 0x52: // 32KB Block Erase + case 0xD8: // 64KB Block Erase + case 0xC7: case 0x60: // Chip Erase + case 0x44: // Erase Security Registers + case 0x42: // Program Security Registers + case 0x99: // Reset + flash.commandStatus[1] &= ~(1 << 1); + break; + } +} + +static void flash_erase(uint32_t size) { + assert(!(size & (size - 1))); + if (flash.commandStatus[1] & 1 << 1) { + memset(&mem.flash.block[flash.commandAddress & (SIZE_FLASH - 1) & -size], 0xFF, size); + } + flash_finish_command(); +} - switch (index) { - case 0x00: - value = flash.mapped; +static void flash_execute_command(void) { + flash_flush_cache(); + flash.commandAddress = flash.command[0] | flash.command[1] << 8 | flash.command[2] << 16; + flash.commandLength = flash.command[8] | flash.command[9] << 8 | flash.command[10] << 16; + flash.commandStatus[0] &= ~7; + switch (flash.command[0xF]) { + case 0x06: // Write Enable + flash.commandStatus[1] |= 1 << 1; + // fallthrough + case 0x04: // Write Disable + flash_finish_command(); break; - case 0x02: - value = flash.map; + case 0x94: // Read Manufacturer / Device ID Quad I/O + case 0x4B: // Read Unique ID Number + case 0x9F: // Read JEDEC ID + flash.commandAddress = 0; + // fallthrough + case 0x05: // Read Status Register-1 + case 0x35: // Read Status Register-2 + case 0x15: // Read Status Register-3 + flash.commandStatus[0] |= 1 << 2; break; - case 0x05: - value = flash.waitStates - 6; + case 0x32: // Quad Input Page Program + flash.commandStatus[0] |= 1 << 1; break; - default: - value = flash.ports[index]; + case 0x20: // Sector Erase + flash_erase(4 << 10); + break; + case 0x52: // 32KB Block Erase + flash_erase(32 << 10); + break; + case 0xD8: // 64KB Block Erase + flash_erase(64 << 10); break; + case 0xC7: case 0x60: // Chip Erase + flash_erase(SIZE_FLASH); + break; + } + if (!flash.commandLength && flash.commandStatus[0] & 3 << 1) { + flash_finish_command(); + } +} +static uint8_t flash_read_command(bool peek) { + switch (flash.command[0xF]) { + case 0x05: // Read Status Register-1 + return flash.commandStatus[1]; + case 0x35: // Read Status Register-2 + return flash.commandStatus[2]; + case 0x15: // Read Status Register-3 + return flash.commandStatus[3]; + case 0xAB: // Release Power-down / Device ID + flash.commandAddress = 1; + // fallthrough + case 0x90: // Read Manufacturer / Device ID + case 0x92: // Read Manufacturer / Device ID Dual I/O + case 0x94: // Read Manufacturer / Device ID Quad I/O + return 0xEF15 >> (~flash.commandAddress++ & 1) * 8; + case 0x4B: // Read Unique ID Number + return flash.uniqueID >> (~flash.commandAddress++ & 7) * 8; + case 0x9F: // Read JEDEC ID + if (flash.commandAddress >= 3) { + flash.commandAddress = 0; + } + return 0xEF4016 >> (2 - flash.commandAddress++) * 8; + } + return 0; +} +static void flash_write_command(uint8_t byte) { + switch (flash.command[0xF]) { + case 0x32: // Quad Input Page Program + mem.flash.block[flash.commandAddress & (SIZE_FLASH - 1)] &= byte; + flash.commandAddress = (flash.commandAddress & ~0xFF) | ((flash.commandAddress + 1) & 0xFF); + break; + } +} +static void flash_command_byte_transferred(void) { + if (!--flash.commandLength) { + flash.commandStatus[0] &= ~(3 << 1); + flash_finish_command(); + } +} + +/* Read from the 0x1000 range of ports */ +static uint8_t flash_read(const uint16_t pio, bool peek) { + uint8_t value; + if (asic.serFlash) { + if (pio & 0x800) { + uint16_t index = pio & 0x7FF; + if (index < 0x10) { + value = flash.command[index]; + } else { + switch (index) { + case 0x18: + value = flash.commandStatus[0] >> 1 & 3; + break; + case 0x24: + value = flash.commandStatus[0] >> 0 & 1; + break; + case 0x2C: case 0x2D: case 0x2E: case 0x2F: + value = flash.maskReg[index - 0x2C]; + break; + case 0x30: + value = 0x04; + break; + case 0x31: + value = 0xB4; + break; + case 0x32: + value = 0x0E; + break; + case 0x33: + value = 0x1F; + break; + case 0x100: + if (!(flash.command[0xC] & 1 << 1) && flash.commandLength) { + value = flash_read_command(peek); + if (!peek) { + flash_command_byte_transferred(); + } + } else { + value = 0; + } + break; + default: + value = 0; + break; + } + } + } else { + uint8_t index = pio & 0x7F; + value = flash.ports[index]; + } + } else { + uint8_t index = pio; + value = flash.ports[index]; } return value; } /* Write to the 0x1000 range of ports */ static void flash_write(const uint16_t pio, const uint8_t byte, bool poke) { - uint8_t index = (uint8_t)pio; (void)poke; + if (asic.serFlash) { + if (!flash_unlocked()) { + return; + } - switch (index) { - case 0x00: - flash.mapped = byte; - break; - case 0x01: - flash.ports[index] = byte; - if (byte > 0x3F) { - flash.mapped = 0; + if (pio & 0x800) { + uint16_t index = pio & 0x7FF; + if (index < 0x10) { + flash.command[index] = byte; + if (index == 0xF) { + flash_execute_command(); + } } - break; - case 0x02: - flash_set_map(byte); - break; - case 0x05: - flash.waitStates = byte + 6; - break; - case 0x08: - flash.ports[index] = byte & 1; - break; - case 0x10: - flash.ports[index] = byte & 1; - break; - default: - flash.ports[index] = byte; - break; + else { + switch (index) { + case 0x24: + flash.commandStatus[0] &= ~(byte & 1); + break; + case 0x2C: case 0x2D: case 0x2E: case 0x2F: + flash.maskReg[index - 0x2C] = byte; + flash_set_mask(); + break; + case 0x100: + if ((flash.command[0xC] & 1 << 1) && flash.commandLength) { + flash_write_command(byte); + flash_command_byte_transferred(); + } + break; + } + } + return; + } else { + uint8_t index = pio & 0x7F; + switch (index) { + case 0x10: + flash.ports[index] = byte & 1; + break; + default: + flash.ports[index] = byte; + break; + } + } + } else { + uint8_t index = pio; + switch (index) { + case 0x00: + flash.ports[index] = byte & 1; + flash_set_map(); + break; + case 0x01: + flash.ports[index] = byte; + flash_set_map(); + break; + case 0x02: + flash.ports[index] = byte & 0xF; + flash_set_map(); + break; + case 0x05: + flash.ports[index] = byte; + flash_set_map(); + break; + case 0x08: + flash.ports[index] = byte & 1; + break; + default: + flash.ports[index] = byte; + break; + } } } @@ -79,18 +333,34 @@ static const eZ80portrange_t device = { }; eZ80portrange_t init_flash(void) { - memset(flash.ports, 0, sizeof flash.ports); - - flash.ports[0x00] = 0x01; - flash.ports[0x07] = 0xFF; - flash.waitStates = 10; - flash.mapped = 1; - flash_set_map(6); - + flash.uniqueID = UINT64_C(0xFFFFFFFFDE680000) | (rand() & 0xFFFF); gui_console_printf("[CEmu] Initialized Flash...\n"); return device; } +void flash_reset(void) { + memset((uint8_t*)&flash + sizeof(flash.uniqueID), 0, sizeof(flash) - sizeof(flash.uniqueID)); + flash.commandStatus[1] = 0x28; + flash.commandStatus[2] = 0x03; + flash.commandStatus[3] = 0x60; + if (asic.serFlash) { + flash.maskReg[0] = 0x00; + flash.maskReg[1] = 0x00; + flash.maskReg[2] = 0xC0; + flash.maskReg[3] = 0xFF; + flash_set_mask(); + flash_flush_cache(); + flash.waitStates = 2; /* Used only for calculating average cycles per access */ + } else { + flash.ports[0x00] = 0x01; + flash.ports[0x02] = 0x06; + flash.ports[0x05] = 0x04; + flash.ports[0x07] = 0xFF; + flash_set_map(); + } + gui_console_printf("[CEmu] Flash reset.\n"); +} + bool flash_save(FILE *image) { return fwrite(&flash, sizeof(flash), 1, image) == 1; } diff --git a/core/flash.h b/core/flash.h index b885aef9e..8dd04a19a 100644 --- a/core/flash.h +++ b/core/flash.h @@ -10,17 +10,41 @@ extern "C" { #include #include +#define FLASH_CACHE_LINE_BITS 5 +#define FLASH_CACHE_LINE_SIZE (1 << FLASH_CACHE_LINE_BITS) +#define FLASH_CACHE_SET_BITS 7 +#define FLASH_CACHE_SETS (1 << FLASH_CACHE_SET_BITS) + +#define FLASH_CACHE_INVALID_LINE 0xFFFFFFFF +#define FLASH_CACHE_INVALID_TAG 0xFFFF + +typedef struct flash_cache_set { + uint16_t mru; + uint16_t lru; +} flash_cache_set_t; + typedef struct flash_state { - uint8_t ports[0x100]; + uint64_t uniqueID; uint32_t waitStates; + uint32_t mappedBytes; uint32_t mask; - uint8_t mapped : 1; - uint8_t map : 4; + uint32_t lastCacheLine; + uint32_t commandAddress; + uint32_t commandLength; + uint8_t command[0x10]; + uint8_t commandStatus[4]; + uint8_t maskReg[4]; + flash_cache_set_t cacheTags[FLASH_CACHE_SETS]; + uint8_t ports[0x100]; } flash_state_t; extern flash_state_t flash; +void flash_flush_cache(void); +uint32_t flash_touch_cache(uint32_t addr); + eZ80portrange_t init_flash(void); +void flash_reset(void); bool flash_restore(FILE *image); bool flash_save(FILE *image); diff --git a/core/interrupt.h b/core/interrupt.h index 8302e91d9..7f54cade7 100644 --- a/core/interrupt.h +++ b/core/interrupt.h @@ -20,6 +20,8 @@ extern "C" { #define INT_RTC (1 << 12) #define INT_USB (1 << 13) #define INT_PWR (1 << 15) +#define INT_UART (1 << 16) +#define INT_SPI (1 << 18) #define INT_WAKE (1 << 19) typedef struct interrupt_state { diff --git a/core/lcd.c b/core/lcd.c index 6955cd206..d7ed1b8c7 100644 --- a/core/lcd.c +++ b/core/lcd.c @@ -8,6 +8,7 @@ #include "control.h" #include "schedule.h" #include "interrupt.h" +#include "panel.h" #include #include @@ -70,7 +71,7 @@ void emu_lcd_drawmem(void *output, void *data, void *data_end, uint32_t lcd_cont uint32_t *dat_end; if (use_spi) { - memcpy(output, spi.display, sizeof(spi.display)); + memcpy(output, panel.display, sizeof(panel.display)); return; } @@ -158,18 +159,18 @@ static uint32_t lcd_process_pixel(uint8_t red, uint8_t green, uint8_t blue) { if (!likely(lcd.curCol)) { if (!likely(lcd.curRow)) { for (v = lcd.VBP; v; v--) { - for (h = lcd.HBP + lcd.CPL + lcd.HFP; h && spi_refresh_pixel(); h--) { + for (h = lcd.HBP + lcd.CPL + lcd.HFP; h && panel_refresh_pixel(); h--) { } - if (!spi_hsync()) { + if (!panel_hsync()) { break; } } } - for (h = lcd.HBP; h && spi_refresh_pixel(); h--) { + for (h = lcd.HBP; h && panel_refresh_pixel(); h--) { } } - spi_refresh_pixel(); - if (likely(lcd.curCol < lcd.PPL && spi.ifCtl & SPI_IC_CTRL_DATA)) { + panel_refresh_pixel(); + if (likely(lcd.curCol < lcd.PPL && panel.ifCtl & PANEL_IC_CTRL_DATA)) { if (!likely(lcd.control & 1 << 11)) { red = green = blue = 0; } else if (likely(lcd.BGR)) { @@ -177,17 +178,17 @@ static uint32_t lcd_process_pixel(uint8_t red, uint8_t green, uint8_t blue) { red = blue; blue = temp; } - spi_update_pixel_16bpp(red, green, blue); + panel_update_pixel_16bpp(red, green, blue); } if (unlikely(++lcd.curCol >= lcd.PPL)) { - for (h = lcd.HFP; h && spi_refresh_pixel(); h--) { + for (h = lcd.HFP; h && panel_refresh_pixel(); h--) { } - spi_hsync(); + panel_hsync(); if (unlikely(++lcd.curRow >= lcd.LPP)) { for (v = lcd.VFP; v; v--) { - for (h = lcd.HBP + lcd.CPL + lcd.HFP; h && spi_refresh_pixel(); h--) { + for (h = lcd.HBP + lcd.CPL + lcd.HFP; h && panel_refresh_pixel(); h--) { } - if (!spi_hsync()) { + if (!panel_hsync()) { break; } } @@ -309,7 +310,7 @@ static void lcd_event(enum sched_item_id id) { if (lcd.spi) { lcd.pos = 0; lcd.curRow = lcd.curCol = 0; - spi_vsync(); + panel_vsync(); sched_repeat_relative(SCHED_LCD_DMA, SCHED_LCD, duration, 0); } lcd.compare = LCD_LNBU; @@ -397,9 +398,16 @@ static uint8_t lcd_read(const uint16_t pio, bool peek) { } if (index < 0x034 && index >= 0x030) { return read8(lcd.lpcurr, bit_offset); } } else if (index < 0x400) { + if (!peek) { + cpu.cycles++; + } return *((uint8_t *)lcd.palette + index - 0x200); - } else if (index < 0xC30) { - if (index < 0xC00 && index >= 0x800) { return read8(lcd.crsrImage[((pio-0x800) & 0x3FF) >> 2], bit_offset); } + } else if (index < 0xC00) { + if (index >= 0x800) { return read8(lcd.crsrImage[((pio - 0x800) & 0x3FF) >> 2], bit_offset); } + } else if (index < 0xE00) { + if (!peek) { + cpu.cycles--; + } if (index == 0xC00) { return read8(lcd.crsrControl, bit_offset); } if (index == 0xC04) { return read8(lcd.crsrConfig, bit_offset); } if (index < 0xC0C && index >= 0xC08) { return read8(lcd.crsrPalette0, bit_offset); } @@ -481,6 +489,52 @@ void emu_set_lcd_ptrs(uint32_t **dat, uint32_t **dat_end, int width, int height, *dat_end = (uint32_t*)data_end; } +static void lcd_write_ctrl_delay() { + switch (control.cpuSpeed) { + case 0: + cpu.cycles += (10 - 2); + break; + case 1: + cpu.cycles += (12 - 2); + break; + case 2: + cpu.cycles += asic.serFlash ? (14 - 2) : (16 - 2); + break; + case 3: + if (asic.serFlash) { + cpu.cycles += (23 - 2); + } else { + cpu.cycles += (21 - 2); + /* Align CPU to LCD clock */ + cpu.cycles |= 1; + } + break; + } +} + +static void lcd_write_crsr_delay() { + switch (control.cpuSpeed) { + case 0: + cpu.cycles += (9 - 2); + break; + case 1: + cpu.cycles += asic.serFlash ? (9 - 2) : (11 - 2); + break; + case 2: + cpu.cycles += asic.serFlash ? (11 - 2) : (13 - 2); + break; + case 3: + if (asic.serFlash) { + cpu.cycles += (14 - 2); + } else { + cpu.cycles += (16 - 2); + /* Align CPU to LCD clock */ + cpu.cycles |= 1; + } + break; + } +} + static void lcd_write(const uint16_t pio, const uint8_t value, bool poke) { uint16_t index = pio & 0xFFC; @@ -489,19 +543,20 @@ static void lcd_write(const uint16_t pio, const uint8_t value, bool poke) { uint32_t old; - (void)poke; - if (index < 0x200) { if (index < 0x010) { write8(lcd.timing[index >> 2], bit_offset, value); - } else if (index < 0x014 && index >= 0x010) { + if (!poke) { + lcd_write_ctrl_delay(); + } + } else if (index == 0x010) { write8(lcd.upbase, bit_offset, value); if (lcd.upbase & 7) { gui_console_printf("[CEmu] Warning: Aligning LCD panel\n"); } lcd.upbase &= ~7U; lcd_update(); - } else if (index < 0x018 && index >= 0x014) { + } else if (index == 0x014) { write8(lcd.lpbase, bit_offset, value); lcd.lpbase &= ~7U; } else if (index == 0x018) { @@ -515,6 +570,9 @@ static void lcd_write(const uint16_t pio, const uint8_t value, bool poke) { sched_clear(SCHED_LCD); } } + if (!poke) { + lcd_write_ctrl_delay(); + } } else if (index == 0x01C) { write8(lcd.imsc, bit_offset, value); lcd.imsc &= 0x1E; @@ -526,10 +584,14 @@ static void lcd_write(const uint16_t pio, const uint8_t value, bool poke) { lcd_update(); } else if (index < 0x400) { write8(lcd.palette[pio >> 1 & 0xFF], (pio & 1) << 3, value); - } else if (index < 0xC30) { - if (index < 0xC00 && index >= 0x800) { - write8(lcd.crsrImage[((pio-0x800) & 0x3FF) >> 2], bit_offset, value); + if (!poke) { + cpu.cycles += (4 - 2); } + } else if (index < 0xC00) { + if (index >= 0x800) { + write8(lcd.crsrImage[((pio - 0x800) & 0x3FF) >> 2], bit_offset, value); + } + } else if (index < 0xE00) { if (index == 0xC00) { write8(lcd.crsrControl, bit_offset, value); } @@ -537,17 +599,17 @@ static void lcd_write(const uint16_t pio, const uint8_t value, bool poke) { write8(lcd.crsrConfig, bit_offset, value); lcd.crsrConfig &= 0xF; } - if (index < 0xC0B && index >= 0xC08) { + if (index == 0xC08) { write8(lcd.crsrPalette0, bit_offset, value); } - if (index < 0xC0F && index >= 0xC0C) { + if (index == 0xC0C) { write8(lcd.crsrPalette1, bit_offset, value); } - if (index < 0xC14 && index >= 0xC10) { + if (index == 0xC10) { write8(lcd.crsrXY, bit_offset, value); lcd.crsrXY &= (0xFFF | (0xFFF << 16)); } - if (index < 0xC16 && index >= 0xC14) { + if (index == 0xC14) { write8(lcd.crsrClip, bit_offset, value); lcd.crsrClip &= (0x3F | (0x3F << 8)); } @@ -559,6 +621,9 @@ static void lcd_write(const uint16_t pio, const uint8_t value, bool poke) { lcd.crsrRis &= ~(value << bit_offset); lcd.crsrRis &= 0xF; } + if (!poke) { + lcd_write_crsr_delay(); + } } } diff --git a/core/lcd.h b/core/lcd.h index e13d0d277..11342993c 100644 --- a/core/lcd.h +++ b/core/lcd.h @@ -8,7 +8,6 @@ extern "C" { #endif #include "port.h" -#include "spi.h" #include #include #include diff --git a/core/mem.c b/core/mem.c index 4bf9d56c6..7e12b3125 100644 --- a/core/mem.c +++ b/core/mem.c @@ -18,6 +18,11 @@ /* Global MEMORY state */ mem_state_t mem; +static uint8_t (*mem_read_flash)(uint32_t); + +static uint8_t mem_read_flash_parallel(uint32_t); +static uint8_t mem_read_flash_serial(uint32_t); + void mem_init(void) { unsigned int i; @@ -57,26 +62,10 @@ void mem_free(void) { void mem_reset(void) { memset(mem.ram.block, 0, SIZE_RAM); mem.flash.command = FLASH_NO_COMMAND; + mem_read_flash = asic.serFlash ? mem_read_flash_serial : mem_read_flash_parallel; gui_console_printf("[CEmu] Memory reset.\n"); } -static uint32_t flash_block(uint32_t *addr, uint32_t *size) { - uint32_t mask = flash.mask; - if (size) { - *size = mask + 1; - } - if (*addr <= mask && flash.mapped) { - /* assume this will crash */ - if (flash.waitStates == 6) { - flash.waitStates = 10; - cpu_crash("[CEmu] Reset triggered, flash data not latched.\n"); - } - return flash.waitStates; - } - *addr &= mask; - return 258; -} - static void fix_size(uint32_t *addr, int32_t *size) { if (*size < 0) { *addr += *size; @@ -86,7 +75,7 @@ static void fix_size(uint32_t *addr, int32_t *size) { static uint32_t addr_block(uint32_t *addr, int32_t size, void **block, uint32_t *block_size) { if (*addr < 0xD00000) { - flash_block(addr, block_size); + *addr &= asic.serFlash ? flash.mask : flash.mappedBytes - 1; *block = mem.flash.block; *block_size = SIZE_FLASH; } else if (*addr < 0xE00000) { @@ -463,15 +452,20 @@ static flash_write_pattern_t patterns[] = { }; -static uint8_t mem_read_flash(uint32_t addr) { +static uint8_t mem_read_flash_parallel(uint32_t addr) { uint8_t value = 0; unsigned int selected; - cpu.cycles += flash_block(&addr, NULL); - if (flash.mapped) { + if (likely(addr < flash.mask)) { + if (unlikely(++cpu.flashTotalAccesses == 0)) { + cpu.flashTotalAccesses = 0x80000000; + cpu.flashDelayCycles >>= 1; + } + cpu.cycles += flash.waitStates; + switch(mem.flash.command) { case FLASH_NO_COMMAND: - value = mem.flash.block[addr]; + value = mem.flash.block[addr & (SIZE_FLASH - 1)]; break; case FLASH_SECTOR_ERASE: value = 0x80; @@ -537,23 +531,50 @@ static uint8_t mem_read_flash(uint32_t addr) { break; } } else { + if (likely(addr >= flash.mappedBytes)) { + cpu.cycles += 258; + } else { + /* wait states too low, assume this will crash */ + cpu_crash("[CEmu] Reset triggered, flash data not latched.\n"); + } value = mem_read_unmapped_flash(true); } return value; } +static uint8_t mem_read_flash_serial(uint32_t addr) { + cpu.cycles += flash_touch_cache(addr); + return mem.flash.block[addr & flash.mask]; +} + static void mem_write_flash(uint32_t addr, uint8_t byte) { + if (asic.serFlash) { + /* Writes cause a cache touch, but the write does not modify the cache */ + cpu.cycles += flash_touch_cache(addr); + return; + } + + if (unlikely(addr >= flash.mappedBytes)) { + cpu.cycles += 258; + return; + } + + if (unlikely(++cpu.flashTotalAccesses == 0)) { + cpu.flashTotalAccesses = 0x80000000; + cpu.flashDelayCycles >>= 1; + } + cpu.cycles += flash.waitStates; + if (!flash_unlocked()) { + /* privileged writes with flash locked are probably ignored */ + return; + } + int i; int partial_match = 0; flash_write_t *w; flash_write_pattern_t *pattern; - cpu.cycles += flash_block(&addr, NULL); - if (!flash.mapped) { - return; - } - if (mem.flash.command != FLASH_NO_COMMAND) { if ((mem.flash.command != FLASH_DEEP_POWER_DOWN && byte == 0xF0) || (mem.flash.command == FLASH_DEEP_POWER_DOWN && byte == 0xAB)) { @@ -592,7 +613,7 @@ static bool detect_flash_unlock_sequence(uint8_t current) { static const uint8_t flash_unlock_sequence[] = { 0xF3, 0x18, 0x00, 0xF3, 0xF3, 0xED, 0x7E, 0xED, 0x56, 0xED, 0x39, 0x28, 0xED, 0x38, 0x28, 0xCB, 0x57 }; uint8_t i; if (current != flash_unlock_sequence[sizeof(flash_unlock_sequence) - 1] || - !protected_ports_unlocked() || unprivileged_code()) { + !protected_ports_unlocked()) { return false; } for (i = 1; i != sizeof(flash_unlock_sequence); i++) { @@ -600,6 +621,9 @@ static bool detect_flash_unlock_sequence(uint8_t current) { return false; } } + if (unprivileged_code()) { + return false; + } return true; } @@ -630,6 +654,7 @@ uint8_t mem_read_cpu(uint32_t addr, bool fetch) { /* FLASH */ case 0x0: case 0x1: case 0x2: case 0x3: case 0x4: case 0x5: case 0x6: case 0x7: + case 0x8: case 0x9: case 0xA: case 0xB: value = mem_read_flash(addr); if (fetch && detect_flash_unlock_sequence(value)) { control.flashUnlocked |= 1 << 3; @@ -637,9 +662,9 @@ uint8_t mem_read_cpu(uint32_t addr, bool fetch) { break; /* UNMAPPED */ - case 0x8: case 0x9: case 0xA: case 0xB: case 0xC: + case 0xC: value = mem_read_unmapped_other(true); - cpu.cycles += 258; + cpu.cycles += asic.serFlash ? 2 : 258; break; /* RAM */ @@ -668,7 +693,7 @@ uint8_t mem_read_cpu(uint32_t addr, bool fetch) { } if (fetch) { mem.buffer[++mem.fetch] = value; - if (unprivileged_code()) { + if (control.flashUnlocked & 1 << 3 && unprivileged_code()) { control.flashUnlocked &= ~(1 << 3); } } else if (addr >= control.protectedStart && addr <= control.protectedEnd && unprivileged_code()) { @@ -706,14 +731,19 @@ void mem_write_cpu(uint32_t addr, uint8_t value) { control.protectionStatus |= 2; gui_console_printf("[CEmu] NMI reset caused by writing to flash at address %#06x from unprivileged code. Hint: Possibly a null pointer dereference.\n", addr); cpu_nmi(); - } else if (flash_unlocked()) { + } else { mem_write_flash(addr, value); - } /* privileged writes with flash locked are probably ignored */ + } + break; + + /* UNMAPPED/FLASH */ + case 0x8: case 0x9: case 0xA: case 0xB: + cpu.cycles += asic.serFlash ? flash_touch_cache(addr) : 258; break; /* UNMAPPED */ - case 0x8: case 0x9: case 0xA: case 0xB: case 0xC: - cpu.cycles += 258; + case 0xC: + cpu.cycles += asic.serFlash ? 2 : 258; break; /* RAM */ @@ -897,5 +927,7 @@ bool mem_restore(FILE *image) { mem.flash.sector[i].ptr = &mem.flash.block[i*SIZE_FLASH_SECTOR_64K]; } + mem_read_flash = asic.serFlash ? mem_read_flash_serial : mem_read_flash_parallel; + return ret; } diff --git a/core/misc.c b/core/misc.c index fec4dda7b..ce0ce8761 100644 --- a/core/misc.c +++ b/core/misc.c @@ -13,7 +13,6 @@ watchdog_state_t watchdog; protected_state_t protect; cxxx_state_t cxxx; /* Global CXXX state */ -exxx_state_t exxx; /* Global EXXX state */ fxxx_state_t fxxx; /* Global FXXX state */ static void watchdog_event(enum sched_item_id id) { @@ -234,49 +233,6 @@ bool cxxx_restore(FILE *image) { /* ============================================= */ -/* Read from the 0xEXXX range of ports */ -static uint8_t exxx_read(const uint16_t pio, bool peek) { - uint8_t index = pio & 0x7F; - uint8_t read_byte; - (void)peek; - - switch (index) { - case 0x14: - read_byte = 32 | exxx.ports[index]; - break; - default: - read_byte = exxx.ports[index]; - break; - } - return read_byte; -} - -/* Write to the 0xEXXX range of ports */ -static void exxx_write(const uint16_t pio, const uint8_t byte, bool poke) { - (void)poke; - exxx.ports[pio & 0x7F] = byte; -} - -static const eZ80portrange_t pexxx = { - .read = exxx_read, - .write = exxx_write -}; - -eZ80portrange_t init_exxx(void) { - memset(&exxx, 0, sizeof(exxx)); - return pexxx; -} - -bool exxx_save(FILE *image) { - return fwrite(&exxx, sizeof(exxx), 1, image) == 1; -} - -bool exxx_restore(FILE *image) { - return fread(&exxx, sizeof(exxx), 1, image) == 1; -} - -/* ============================================= */ - /* Write to the 0xFXXX range of ports */ static void fxxx_write(const uint16_t pio, const uint8_t value, bool poke) { (void)poke; diff --git a/core/misc.h b/core/misc.h index cf910a63d..0c569bb5d 100644 --- a/core/misc.h +++ b/core/misc.h @@ -30,9 +30,7 @@ typedef struct protected_state { typedef struct cxxx_state { uint8_t ports[0x100]; } cxxx_state_t; -typedef struct exxx_state { - uint8_t ports[0x80]; -} exxx_state_t; + typedef struct fxxx_state { uint8_t dummy; } fxxx_state_t; @@ -40,13 +38,11 @@ typedef struct fxxx_state { extern watchdog_state_t watchdog; extern protected_state_t protect; extern cxxx_state_t cxxx; -extern exxx_state_t exxx; extern fxxx_state_t fxxx; eZ80portrange_t init_watchdog(void); eZ80portrange_t init_protected(void); eZ80portrange_t init_cxxx(void); -eZ80portrange_t init_exxx(void); eZ80portrange_t init_fxxx(void); void watchdog_reset(void); bool watchdog_restore(FILE *image); @@ -55,8 +51,6 @@ bool protect_restore(FILE *image); bool protect_save(FILE *image); bool cxxx_restore(FILE *image); bool cxxx_save(FILE *image); -bool exxx_restore(FILE *image); -bool exxx_save(FILE *image); #ifdef __cplusplus } diff --git a/core/panel.c b/core/panel.c new file mode 100644 index 000000000..73a76219e --- /dev/null +++ b/core/panel.c @@ -0,0 +1,505 @@ +#include "panel.h" +#include "bus.h" + +#include +#include +#include +#include + +panel_state_t panel; + +static bool panel_scan_line(uint16_t row) { + if (unlikely(row > PANEL_LAST_ROW)) { + if (likely((panel.ifCtl & PANEL_IC_DM_MASK) != PANEL_IC_DM_MCU)) { + panel.mode |= PANEL_MODE_IGNORE; + return false; + } else { + panel.activeScroll = panel.pendingScroll; + row = 0; + } + } + panel.mode &= ~PANEL_MODE_IGNORE; + if (unlikely(panel.mode & PANEL_MODE_PARTIAL) && + panel.activeScroll.partialStart > panel.activeScroll.partialEnd ? + panel.activeScroll.partialStart > row && row > panel.activeScroll.partialEnd : + panel.activeScroll.partialStart > row || row > panel.activeScroll.partialEnd) { + panel.mode |= PANEL_MODE_BLANK; + } else { + panel.mode &= ~PANEL_MODE_BLANK; + } + panel.row = panel.dstRow = panel.srcRow = row; + if (unlikely(panel.mode & PANEL_MODE_SCROLL)) { + uint16_t top = panel.activeScroll.topArea, bot = PANEL_LAST_ROW - panel.activeScroll.bottomArea; + if (row >= top && row <= bot) { + panel.srcRow += panel.activeScroll.scrollStart - top; + if (panel.srcRow > bot) { + panel.srcRow -= (bot + 1 - top); + } + panel.srcRow &= 0x1FF; + } + } + if (unlikely(panel.gateConfig & PANEL_GATE_INTERLACE)) { + panel.dstRow *= 2; + if (panel.dstRow >= PANEL_NUM_ROWS) { + panel.dstRow -= (PANEL_NUM_ROWS - 1); + } + } + if (unlikely(panel.mac & PANEL_MAC_VRO)) { + panel.dstRow = PANEL_LAST_ROW - panel.dstRow; + panel.srcRow = PANEL_LAST_ROW - panel.srcRow; + } + if (unlikely(panel.mac & PANEL_MAC_HRO)) { + panel.col = PANEL_LAST_COL; + panel.colDir = -1; + } else { + panel.col = 0; + panel.colDir = 1; + } + return true; +} + +bool panel_hsync(void) { + if (likely((panel.ifCtl & PANEL_IC_DM_MASK) == PANEL_IC_DM_RGB)) { + return panel_scan_line(panel.row + 1); + } else { + return !(panel.mode & PANEL_MODE_IGNORE); + } +} + +static void panel_reset_mregs(void) { + if (unlikely(panel.mac & PANEL_MAC_RCX)) { + panel.rowReg = panel.rowStart; + panel.colReg = panel.colStart; + } else { + panel.rowReg = panel.colStart; + panel.colReg = panel.rowStart; + } +} + +bool panel_vsync(void) { + if (likely(panel.ifCtl & PANEL_IC_CTRL_DATA)) { + panel_reset_mregs(); + } + if (likely((panel.ifCtl & PANEL_IC_DM_MASK) != PANEL_IC_DM_MCU)) { + panel.activeScroll = panel.pendingScroll; + return panel_scan_line(0); + } else { + return true; + } +} + +bool panel_refresh_pixel(void) { + uint8_t *pixel, red, green, blue; + if (unlikely(panel.mode & PANEL_MODE_IGNORE)) { + return false; + } + if (unlikely(panel.mode & (PANEL_MODE_SLEEP | PANEL_MODE_OFF | PANEL_MODE_BLANK))) { + red = green = blue = ~0; + } else { + if (unlikely(panel.srcRow > PANEL_LAST_ROW)) { + red = bus_rand(); + green = bus_rand(); + blue = bus_rand(); + } else { + pixel = panel.frame[panel.srcRow][panel.col]; + red = pixel[PANEL_RED]; + green = pixel[PANEL_GREEN]; + blue = pixel[PANEL_BLUE]; + } + if (!likely(panel.mac & PANEL_MAC_BGR)) { /* eor */ + uint8_t temp = red; + red = blue; + blue = temp; + } + if (unlikely(panel.mode & PANEL_MODE_INVERT)) { + red = ~red; + green = ~green; + blue = ~blue; + } + if (unlikely(panel.mode & PANEL_MODE_IDLE)) { + red = (int8_t)red >> 7; + green = (int8_t)green >> 7; + blue = (int8_t)blue >> 7; + } + } + pixel = panel.display[panel.col][panel.dstRow]; + pixel[PANEL_RED] = red; + pixel[PANEL_GREEN] = green; + pixel[PANEL_BLUE] = blue; + pixel[PANEL_ALPHA] = ~0; + panel.col += panel.colDir; + if (unlikely(panel.col > PANEL_LAST_COL)) { + if (likely((panel.ifCtl & PANEL_IC_DM_MASK) == PANEL_IC_DM_RGB)) { + panel.mode |= PANEL_MODE_IGNORE; + return false; + } else { + return panel_scan_line(panel.row + 1); + } + } + return true; +} + +static void panel_update_pixel(uint8_t red, uint8_t green, uint8_t blue) { + if (likely(panel.rowReg < 320 && panel.colReg < 240)) { + uint8_t *pixel = panel.frame[panel.rowReg][panel.colReg]; + pixel[PANEL_RED] = red; + pixel[PANEL_GREEN] = green; + pixel[PANEL_BLUE] = blue; + } + if (unlikely(panel.mac & PANEL_MAC_RCX)) { + if (unlikely(panel.colReg == panel.colEnd)) { + if (unlikely(panel.rowReg == panel.rowEnd && panel.rowStart <= panel.rowEnd)) { + panel.rowReg = panel.colReg = ~0; + } else { + panel.colReg = panel.colStart; + panel.rowReg = (panel.rowReg + 1 - (panel.mac >> 6 & 2)) & 0x1FF; + } + } else if (panel.colReg < 0x100) { + panel.colReg = (panel.colReg + 1 - (panel.mac >> 5 & 2)) & 0xFF; + } + } else { + if (unlikely(panel.rowReg == panel.colEnd)) { + if (unlikely(panel.colReg == panel.rowEnd && panel.rowStart <= panel.rowEnd)) { + panel.rowReg = panel.colReg = ~0; + } else { + panel.rowReg = panel.colStart; + panel.colReg = (panel.colReg + 1 - (panel.mac >> 5 & 2)) & 0xFF; + } + } else if (panel.rowReg < 0x200) { + panel.rowReg = (panel.rowReg + 1 - (panel.mac >> 6 & 2)) & 0x1FF; + } + } +} + +void panel_update_pixel_18bpp(uint8_t red, uint8_t green, uint8_t blue) { + assert(red < 64 && green < 64 && blue < 64); + panel_update_pixel(red << 2 | red >> 4, green << 2 | green >> 4, blue << 2 | blue >> 4); +} + +void panel_update_pixel_16bpp(uint8_t red, uint8_t green, uint8_t blue) { + assert(red < 32 && green < 64 && blue < 32); + panel_update_pixel(panel.lut[red + 0], panel.lut[green + 32], panel.lut[blue + 96]); +} + +void panel_update_pixel_12bpp(uint8_t red, uint8_t green, uint8_t blue) { + assert(red < 16 && green < 16 && blue < 16); + panel_update_pixel(panel.lut[(red << 1) + 0], panel.lut[(green << 2) + 32], panel.lut[(blue << 1) + 96]); +} + +static void panel_sw_reset(void) { + panel.cmd = 0; + panel.param = 0; + panel.gamma = 1; + panel.mode = PANEL_MODE_SLEEP | PANEL_MODE_OFF | PANEL_MODE_IGNORE; + panel.colStart = 0; + panel.colEnd = panel.mac & PANEL_MAC_RCX ? PANEL_LAST_COL : PANEL_LAST_ROW; + panel.rowStart = 0; + panel.rowEnd = panel.mac & PANEL_MAC_RCX ? PANEL_LAST_ROW : PANEL_LAST_COL; + panel.pendingScroll.partialStart = 0; + panel.pendingScroll.partialEnd = PANEL_LAST_ROW; + panel.pendingScroll.topArea = 0; + panel.pendingScroll.scrollArea = PANEL_NUM_ROWS; + panel.pendingScroll.bottomArea = 0; + panel.pendingScroll.scrollStart = 0; + panel.activeScroll = panel.pendingScroll; + panel.gateCount = PANEL_NUM_ROWS / 8 - 1; + panel.gateStart = 0 / 8; + panel.gateConfig = PANEL_GATE_MIRROR; + panel.tear = false; +} + +void panel_hw_reset(void) { + panel.mac = 0; + panel.ifBpp = 0x66; + panel_sw_reset(); +} + +static void panel_write_cmd(uint8_t value) { + panel.cmd = value; + panel.param = 0; + + switch (panel.cmd) { + case 0x00: + break; + case 0x01: + panel_sw_reset(); + break; + case 0x10: + panel.mode |= PANEL_MODE_SLEEP; + break; + case 0x11: + panel.mode &= ~PANEL_MODE_SLEEP; + break; + case 0x12: + panel.mode |= PANEL_MODE_PARTIAL; + panel.pendingScroll.scrollStart = 0; + break; + case 0x13: + panel.mode &= ~(PANEL_MODE_PARTIAL | PANEL_MODE_SCROLL); + panel.pendingScroll.scrollStart = 0; + break; + case 0x20: + panel.mode &= ~PANEL_MODE_INVERT; + break; + case 0x21: + panel.mode |= PANEL_MODE_INVERT; + break; + case 0x28: + panel.mode |= PANEL_MODE_OFF; + break; + case 0x29: + panel.mode &= ~PANEL_MODE_OFF; + break; + case 0x2C: + panel_reset_mregs(); + break; + case 0x34: + panel.tear = false; + break; + case 0x35: + panel.tear = true; + break; + case 0x38: + panel.mode &= ~PANEL_MODE_IDLE; + break; + case 0x39: + panel.mode |= PANEL_MODE_IDLE; + break; + default: + break; + } +} + +static void panel_write_param(uint8_t value) { + uint8_t word_param = panel.param >> 1; + uint8_t bit_offset = ~panel.param << 3 & 8; + + switch (panel.cmd) { + case 0x26: + if (panel.param == 0) { + panel.gamma = value; + } + break; + case 0x2A: + switch (word_param) { + case 0: + write8(panel.colStart, bit_offset, value & 0x1FF >> bit_offset); + break; + case 1: + write8(panel.colEnd, bit_offset, value & 0x1FF >> bit_offset); + break; + default: + break; + } + break; + case 0x2B: + switch (word_param) { + case 0: + write8(panel.rowStart, bit_offset, value & 0x1FF >> bit_offset); + break; + case 1: + write8(panel.rowEnd, bit_offset, value & 0x1FF >> bit_offset); + break; + default: + break; + } + break; + case 0x2C: + case 0x3C: + if (unlikely(!(panel.ifCtl & PANEL_IC_CTRL_DATA))) { + switch (panel.ifBpp & 7) { + default: + case 6: /* 18bpp */ + switch (panel.param) { + case 0: + panel.ifBlue = value >> 2; + break; + case 1: + panel.ifGreen = value >> 2; + break; + case 2: + panel.ifRed = value >> 2; + panel_update_pixel_18bpp(panel.ifRed, panel.ifGreen, panel.ifBlue); + panel.param = (uint8_t)-1; + break; + } + break; + case 5: /* 16bpp */ + switch (panel.param) { + case 0: + panel.ifBlue = value >> 3; + panel.ifGreen = value << 3 & 0x38; + break; + case 1: + panel.ifGreen |= value >> 5; + panel.ifRed = value & 0x1F; + panel_update_pixel_16bpp(panel.ifRed, panel.ifGreen, panel.ifBlue); + panel.param = (uint8_t)-1; + break; + } + break; + case 3: /* 12bpp */ + switch (panel.param) { + case 0: + panel.ifBlue = value >> 4; + panel.ifGreen = value & 0xF; + break; + case 1: + panel.ifRed = value >> 4; + panel_update_pixel_12bpp(panel.ifRed, panel.ifGreen, panel.ifBlue); + panel.ifBlue = value & 0xF; + break; + case 2: + panel.ifGreen = value >> 4; + panel.ifRed = value & 0xF; + panel_update_pixel_12bpp(panel.ifRed, panel.ifGreen, panel.ifBlue); + panel.param = (uint8_t)-1; + break; + } + break; + } + } + break; + case 0x2D: + break; + case 0x30: + switch (word_param) { + case 0: + write8(panel.pendingScroll.partialStart, bit_offset, value & 0x1FF >> bit_offset); + break; + case 1: + write8(panel.pendingScroll.partialEnd, bit_offset, value & 0x1FF >> bit_offset); + break; + default: + break; + } + break; + case 0x33: + switch (word_param) { + case 0: + write8(panel.pendingScroll.topArea, bit_offset, value & 0x1FF >> bit_offset); + break; + case 1: + write8(panel.pendingScroll.scrollArea, bit_offset, value & 0x1FF >> bit_offset); + break; + case 2: + write8(panel.pendingScroll.bottomArea, bit_offset, value & 0x1FF >> bit_offset); + break; + default: + break; + } + break; + case 0x36: + if (panel.param == 0) { + panel.mac = value; + } + break; + case 0x37: + switch (word_param) { + case 0: + write8(panel.pendingScroll.scrollStart, bit_offset, value & 0x1FF >> bit_offset); + panel.mode |= PANEL_MODE_SCROLL; + break; + default: + break; + } + break; + case 0x3A: + switch (panel.param) { + case 0: + panel.ifBpp = value; + break; + default: + break; + } + break; + case 0xB0: + switch (panel.param) { + case 0: + panel.ifCtl = value; + break; + case 1: + break; + default: + break; + } + break; + case 0xE0: + if (panel.param < 16) { + panel.gammaCorrection[0][panel.param] = value; + } + break; + case 0xE1: + if (panel.param < 16) { + panel.gammaCorrection[1][panel.param] = value; + } + break; + case 0xE4: + switch (panel.param) { + case 0: + panel.gateConfig = value; + break; + case 1: + panel.gateStart = value; + break; + case 2: + panel.gateConfig = value; + break; + default: + break; + } + break; + default: + break; + } + + panel.param++; +} + +uint8_t panel_spi_select(uint32_t* rxData) { + (void)rxData; + /* The first transfer frame is always 9 bits */ + return 9; +} + +uint8_t panel_spi_transfer(uint32_t txData, uint32_t* rxData) { + (void)rxData; + (txData & 0x100 ? panel_write_param : panel_write_cmd)((uint8_t)txData); + /* TODO: return different frame length after read commands */ + return 9; +} + +void panel_spi_deselect(void) { +} + +static void panel_init_luts(void) { + uint8_t i = 0, c; + for (c = 0; c < 1 << 5; c++) { + panel.lut[i++] = c << 3 | c >> 2; + } + for (c = 0; c < 1 << 6; c++) { + panel.lut[i++] = c << 2 | c >> 4; + } + for (c = 0; c < 1 << 5; c++) { + panel.lut[i++] = c << 3 | c >> 2; + } +} + +void panel_reset(void) { + memset(&panel, 0, sizeof(panel)); + panel_init_luts(); + + panel_hw_reset(); +} + +bool panel_save(FILE *image) { + return fwrite(&panel, offsetof(panel_state_t, lut), 1, image) == 1; +} + +bool panel_restore(FILE *image) { + if (fread(&panel, offsetof(panel_state_t, lut), 1, image) == 1) { + panel_init_luts(); + return true; + } + return false; +} diff --git a/core/panel.h b/core/panel.h new file mode 100644 index 000000000..a443900e1 --- /dev/null +++ b/core/panel.h @@ -0,0 +1,102 @@ +#ifndef PANEL_H +#define PANEL_H + +#include "defines.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#define PANEL_RED 0 +#define PANEL_GREEN 1 +#define PANEL_BLUE 2 +#define PANEL_ALPHA 3 +#define PANEL_NUM_ROWS 320 +#define PANEL_LAST_ROW 319 +#define PANEL_NUM_COLS 240 +#define PANEL_LAST_COL 239 + +enum panel_mode { + PANEL_MODE_SLEEP = 1 << 0, + PANEL_MODE_OFF = 1 << 1, + PANEL_MODE_BLANK = 1 << 2, + PANEL_MODE_PARTIAL = 1 << 3, + PANEL_MODE_INVERT = 1 << 4, + PANEL_MODE_IDLE = 1 << 5, + PANEL_MODE_SCROLL = 1 << 6, + PANEL_MODE_IGNORE = 1 << 7 +}; + +enum panel_mac { + PANEL_MAC_HRO = 1 << 2, + PANEL_MAC_BGR = 1 << 3, + PANEL_MAC_VRO = 1 << 4, + PANEL_MAC_RCX = 1 << 5, + PANEL_MAC_CAO = 1 << 6, + PANEL_MAC_RAO = 1 << 7 +}; + +enum panel_ic { + PANEL_IC_DM_MASK = 3 << 0, + PANEL_IC_DM_MCU = 0 << 0, + PANEL_IC_DM_RGB = 1 << 0, + PANEL_IC_DM_VSYNC = 2 << 0, + PANEL_IC_CTRL_DATA = 1 << 4, + PANEL_IC_GRAM_BYPASS = 1 << 7 +}; + +enum panel_gate { + PANEL_GATE_SCANDIR = 1 << 0, + PANEL_GATE_INTERLACE = 1 << 2, + PANEL_GATE_MIRROR = 1 << 4 +}; + +typedef struct panel_scroll_regs { + uint16_t partialStart, partialEnd, topArea, scrollArea, bottomArea, scrollStart; +} panel_scroll_regs_t; + +typedef struct panel_state { + uint16_t row, dstRow, srcRow; + uint8_t cmd, param, col, colDir; + + uint32_t rowReg, colReg; + uint16_t rowStart, rowEnd, colStart, colEnd; + panel_scroll_regs_t pendingScroll, activeScroll; + uint8_t mode, ifBpp, ifCtl, ifRed, ifGreen, ifBlue, mac, gamma; + uint8_t gateCount, gateStart, gateConfig; + uint8_t frame[320][240][3], display[240][320][4]; + + bool tear; + uint8_t gammaCorrection[2][16]; + + /* Below state is initialized at runtime */ + uint8_t lut[128]; +} panel_state_t; + +extern panel_state_t panel; + +void panel_reset(void); +bool panel_restore(FILE *image); +bool panel_save(FILE *image); + +void panel_hw_reset(void); +bool panel_hsync(void); +bool panel_vsync(void); +bool panel_refresh_pixel(void); +void panel_update_pixel_18bpp(uint8_t r, uint8_t g, uint8_t b); +void panel_update_pixel_16bpp(uint8_t r, uint8_t g, uint8_t b); +void panel_update_pixel_12bpp(uint8_t r, uint8_t g, uint8_t b); + +uint8_t panel_spi_select(uint32_t* rxData); +uint8_t panel_spi_transfer(uint32_t txData, uint32_t* rxData); +void panel_spi_deselect(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/core/port.c b/core/port.c index e5997f78c..bd1bf3e83 100644 --- a/core/port.c +++ b/core/port.c @@ -1,7 +1,8 @@ #include "port.h" +#include "asic.h" #include "cpu.h" -#include "schedule.h" #include "debug/debug.h" +#include "schedule.h" #define PORT_READ_DELAY 2 #define PORT_WRITE_DELAY 4 @@ -11,10 +12,13 @@ eZ80portrange_t port_map[0x10]; #define port_range(a) (((a)>>12)&0xF) /* converts an address to a port range 0x0-0xF */ -static const uint32_t port_mirrors[0x10] = {0xFF,0xFF,0xFF,0x1FF,0xFFF,0xFF,0x1F,0xFF,0x7F,0xFFF,0x7F,0xFFF,0xFF,0x7F,0x7F,0xFFF}; +static const uint32_t port_mirrors[2][0x10] = { + {0xFF, 0xFF,0xFF,0x1FF,0xFFF,0xFF,0x1F,0xFF,0x7F,0xFFF,0x7F,0xFFF,0xFF,0x7F,0x7F,0xFFF}, + {0xFF,0xFFF,0xFF,0x1FF,0xFFF,0xFF,0x1F,0xFF,0x7F,0xFFF,0x7F,0xFFF,0xFF,0x7F,0x7F,0xFFF}, +}; static uint8_t port_read(uint16_t address, uint8_t loc, bool peek) { - return port_map[loc].read(address & port_mirrors[loc], peek); + return port_map[loc].read(address & port_mirrors[asic.serFlash][loc], peek); } uint8_t port_peek_byte(uint16_t address) { return port_read(address, port_range(address), true); @@ -38,7 +42,7 @@ uint8_t port_read_byte(uint16_t address) { } static void port_write(uint16_t address, uint8_t loc, uint8_t value, bool peek) { - port_map[loc].write(address & port_mirrors[loc], value, peek); + port_map[loc].write(address & port_mirrors[asic.serFlash][loc], value, peek); } void port_poke_byte(uint16_t address, uint8_t value) { port_write(address, port_range(address), value, true); @@ -61,5 +65,5 @@ void port_write_byte(uint16_t address, uint8_t value) { cpu.cycles += PORT_WRITE_DELAY; sched_process_pending_events(); /* make io ports consistent with mid-instruction state */ port_write(address, port_loc, value, false); - cpu.cycles -= PORT_WRITE_DELAY - port_write_cycles[port_loc]; + sched_rewind_cpu(PORT_WRITE_DELAY - port_write_cycles[port_loc]); /* safely handle underflow */ } diff --git a/core/registers.h b/core/registers.h index 778191f38..7a02167f2 100644 --- a/core/registers.h +++ b/core/registers.h @@ -144,7 +144,6 @@ typedef struct { uint8_t PCU; }; }; - uint32_t rawPC; uint16_t I; uint8_t R, MBASE; } eZ80registers_t; diff --git a/core/schedule.c b/core/schedule.c index b7d0615df..25a54e5ec 100644 --- a/core/schedule.c +++ b/core/schedule.c @@ -197,6 +197,27 @@ static void sched_second(enum sched_item_id id) { sched_update(SCHED_SECOND); } +void sched_rewind_cpu(uint8_t duration) { + if (likely(cpu.cycles >= duration)) { + cpu.cycles -= duration; + return; + } + + /* Cycles would underflow, so undo the effects of sched_second */ + enum sched_item_id id; + for (id = SCHED_SECOND; id < SCHED_NUM_ITEMS; id++) { + if (sched_active(id)) { + sched.items[id].second++; + } + } + cpu.seconds--; + cpu.cycles += sched.clockRates[CLOCK_CPU] - duration; + cpu.eiDelay += sched.clockRates[CLOCK_CPU]; + cpu.baseCycles -= sched.clockRates[CLOCK_CPU]; + sched.items[SCHED_SECOND].second = 0; /* Don't use sched_repeat! */ + sched_update_next(SCHED_SECOND); /* All other events are >= 1 second now */ +} + void sched_set_clock(enum clock_id clock, uint32_t new_rate) { enum sched_item_id id; struct sched_item *item; @@ -232,7 +253,7 @@ uint32_t sched_get_clock_rate(enum clock_id clock) { } void sched_reset(void) { - const uint32_t def_rates[CLOCK_NUM_ITEMS] = { 48000000, 60, 48000000, 24000000, 12000000, 6000000, 1000000, 32768, 1 }; + const uint32_t def_rates[CLOCK_NUM_ITEMS] = { 48000000, 60, 48000000, 24000000, 12000000, 6000000, 3000000, 1000000, 32768, 1 }; struct sched_item usb_device_item = sched.items[SCHED_USB_DEVICE]; diff --git a/core/schedule.h b/core/schedule.h index b1f211ae5..b3cecd191 100644 --- a/core/schedule.h +++ b/core/schedule.h @@ -9,8 +9,8 @@ extern "C" { #include #include -enum clock_id { CLOCK_CPU, CLOCK_RUN, CLOCK_48M, CLOCK_24M, CLOCK_12M, CLOCK_6M, CLOCK_1M, - CLOCK_32K, CLOCK_1, CLOCK_NUM_ITEMS }; +enum clock_id { CLOCK_CPU, CLOCK_RUN, CLOCK_48M, CLOCK_24M, CLOCK_12M, CLOCK_6M, CLOCK_3M, + CLOCK_1M, CLOCK_32K, CLOCK_1, CLOCK_NUM_ITEMS }; enum sched_item_id { SCHED_SECOND, @@ -27,9 +27,11 @@ enum sched_item_id { SCHED_RTC, SCHED_USB, SCHED_USB_DEVICE, + SCHED_SPI, + SCHED_UART, SCHED_FIRST_EVENT = SCHED_RUN, - SCHED_LAST_EVENT = SCHED_USB_DEVICE, + SCHED_LAST_EVENT = SCHED_UART, SCHED_PREV_MA, @@ -75,6 +77,7 @@ uint32_t sched_event_next_cycle(void); uint32_t sched_dma_next_cycle(void); void sched_process_pending_events(void); void sched_process_pending_dma(uint8_t duration); +void sched_rewind_cpu(uint8_t duration); void sched_clear(enum sched_item_id id); void sched_set(enum sched_item_id id, uint64_t ticks); void sched_repeat(enum sched_item_id id, uint64_t ticks); diff --git a/core/spi.c b/core/spi.c index 76c02d56e..5d7cabb73 100644 --- a/core/spi.c +++ b/core/spi.c @@ -1,433 +1,257 @@ #include "spi.h" +#include "cpu.h" +#include "debug/debug.h" #include "emu.h" -#include "bus.h" +#include "interrupt.h" +#include "panel.h" #include "schedule.h" #include +#include #include #include spi_state_t spi; -static bool spi_scan_line(uint16_t row) { - if (unlikely(row > SPI_LAST_ROW)) { - spi.mode |= SPI_MODE_IGNORE; - return false; - } - spi.mode &= ~SPI_MODE_IGNORE; - if (unlikely(spi.mode & SPI_MODE_PARTIAL) && - spi.partialStart > spi.partialEnd ? - spi.partialStart > row && row > spi.partialEnd : - spi.partialStart > row || row > spi.partialEnd) { - spi.mode |= SPI_MODE_BLANK; - } else { - spi.mode &= ~SPI_MODE_BLANK; - } - spi.row = spi.dstRow = spi.srcRow = row; - if (unlikely(spi.mode & SPI_MODE_SCROLL)) { - uint16_t top = spi.topArea, bot = SPI_LAST_ROW - spi.bottomArea; - if (row >= top && row <= bot) { - spi.srcRow += spi.scrollStart - top; - if (spi.srcRow > bot) { - spi.srcRow -= SPI_NUM_ROWS - spi.topArea - spi.bottomArea; - } - spi.srcRow &= 0x1FF; - } - } - if (unlikely(spi.mac & SPI_MAC_VRO)) { - spi.dstRow = SPI_LAST_ROW - spi.dstRow; - spi.srcRow = SPI_LAST_ROW - spi.srcRow; - } - if (unlikely(spi.mac & SPI_MAC_HRO)) { - spi.col = SPI_LAST_COL; - spi.colDir = -1; - } else { - spi.col = 0; - spi.colDir = 1; - } - return true; +static uint8_t null_spi_select(uint32_t* rxData) { + (void)rxData; + /* Set the device frame to the transfer size, to reduce scheduling */ + return (spi.cr1 >> 16 & 0x1F) + 1; +} + +static uint8_t null_spi_transfer(uint32_t txData, uint32_t* rxData) { + (void)txData; + return null_spi_select(rxData); } -bool spi_hsync(void) { - return spi_scan_line(spi.row + 1); +static void null_spi_deselect(void) { } -static void spi_reset_mregs(void) { - if (unlikely(spi.mac & SPI_MAC_RCX)) { - spi.rowReg = spi.rowStart; - spi.colReg = spi.colStart; +static void spi_set_device_funcs(void) { + if (spi.arm) { + spi.device_select = null_spi_select; + spi.device_transfer = null_spi_transfer; + spi.device_deselect = null_spi_deselect; } else { - spi.rowReg = spi.colStart; - spi.colReg = spi.rowStart; + spi.device_select = panel_spi_select; + spi.device_transfer = panel_spi_transfer; + spi.device_deselect = panel_spi_deselect; } } -bool spi_vsync(void) { - if (likely(spi.ifCtl & SPI_IC_CTRL_DATA)) { - spi_reset_mregs(); - } - return spi_scan_line(0); +static uint8_t spi_get_threshold_status(void) { + uint8_t txThreshold = spi.intCtrl >> 12 & 0x1F; + uint8_t rxThreshold = spi.intCtrl >> 7 & 0x1F; + return (txThreshold && spi.tfve <= txThreshold) << 3 + | (rxThreshold && spi.rfve >= rxThreshold) << 2; } -bool spi_refresh_pixel(void) { - uint8_t *pixel, red, green, blue; - if (unlikely(spi.mode & SPI_MODE_IGNORE)) { - return false; - } - if (unlikely(spi.mode & (SPI_MODE_SLEEP | SPI_MODE_OFF | SPI_MODE_BLANK))) { - red = green = blue = ~0; - } else { - if (unlikely(spi.srcRow > SPI_LAST_ROW)) { - red = bus_rand(); - green = bus_rand(); - blue = bus_rand(); - } else { - pixel = spi.frame[spi.srcRow][spi.col]; - red = pixel[SPI_RED]; - green = pixel[SPI_GREEN]; - blue = pixel[SPI_BLUE]; - } - if (!likely(spi.mac & SPI_MAC_BGR)) { /* eor */ - uint8_t temp = red; - red = blue; - blue = temp; - } - if (unlikely(spi.mode & SPI_MODE_INVERT)) { - red = ~red; - green = ~green; - blue = ~blue; - } - if (unlikely(spi.mode & SPI_MODE_IDLE)) { - red = (int8_t)red >> 7; - green = (int8_t)green >> 7; - blue = (int8_t)blue >> 7; +static void spi_update_thresholds(void) { + if (unlikely(spi.intCtrl & (0x1F << 12 | 0x1F << 7))) { + uint8_t statusDiff = (spi.intStatus & (1 << 3 | 1 << 2)) ^ spi_get_threshold_status(); + if (unlikely(statusDiff)) { + spi.intStatus ^= statusDiff; + intrpt_set(INT_SPI, spi.intCtrl & spi.intStatus); } } - pixel = spi.display[spi.col][spi.dstRow]; - pixel[SPI_RED] = red; - pixel[SPI_GREEN] = green; - pixel[SPI_BLUE] = blue; - pixel[SPI_ALPHA] = ~0; - spi.col += spi.colDir; - if (unlikely(spi.col > SPI_LAST_COL)) { - spi.mode |= SPI_MODE_IGNORE; - return false; - } - return true; } -static void spi_update_pixel(uint8_t red, uint8_t green, uint8_t blue) { - if (likely(spi.rowReg < 320 && spi.colReg < 240)) { - uint8_t *pixel = spi.frame[spi.rowReg][spi.colReg]; - pixel[SPI_RED] = red; - pixel[SPI_GREEN] = green; - pixel[SPI_BLUE] = blue; - } - if (unlikely(spi.mac & SPI_MAC_RCX)) { - if (unlikely(spi.colReg == spi.colEnd)) { - if (unlikely(spi.rowReg == spi.rowEnd && spi.rowStart <= spi.rowEnd)) { - spi.rowReg = spi.colReg = ~0; - } else { - spi.colReg = spi.colStart; - spi.rowReg = (spi.rowReg + 1 - (spi.mac >> 6 & 2)) & 0x1FF; +static uint32_t spi_next_transfer(void) { + if (spi.transferBits == 0) { + bool txAvailable = (spi.cr2 >> 8 & 1) && spi.tfve != 0; + if (unlikely(spi.cr2 >> 7 & 1)) { + /* Odd RX behavior, allow transfer after 15 entries only if TX FIFO is non-empty */ + if (spi.rfve >= SPI_RXFIFO_DEPTH - (spi.tfve == 0)) { + return 0; } - } else if (spi.colReg < 0x100) { - spi.colReg = (spi.colReg + 1 - (spi.mac >> 5 & 2)) & 0xFF; + } else if (!txAvailable) { + /* If not receiving, disallow transfer if TX FIFO is empty or disabled */ + return 0; } - } else { - if (unlikely(spi.rowReg == spi.colEnd)) { - if (unlikely(spi.colReg == spi.rowEnd && spi.rowStart <= spi.rowEnd)) { - spi.rowReg = spi.colReg = ~0; - } else { - spi.rowReg = spi.colStart; - spi.colReg = (spi.colReg + 1 - (spi.mac >> 5 & 2)) & 0xFF; + + spi.transferBits = (spi.cr1 >> 16 & 0x1F) + 1; + if (likely(txAvailable)) { + spi.txFrame = spi.txFifo[spi.tfvi++ & (SPI_TXFIFO_DEPTH - 1)] << (32 - spi.transferBits); + spi.tfve--; + spi_update_thresholds(); + } else { + /* For now, just send 0 bits when no TX data available */ + spi.txFrame = 0; + if (spi.cr2 >> 8 & 1) { + /* Set TX underflow if TX enabled */ + spi.intStatus |= 1 << 1; + intrpt_set(INT_SPI, spi.intCtrl & spi.intStatus); } - } else if (spi.rowReg < 0x200) { - spi.rowReg = (spi.rowReg + 1 - (spi.mac >> 6 & 2)) & 0x1FF; } } -} -void spi_update_pixel_18bpp(uint8_t red, uint8_t green, uint8_t blue) { - assert(red < 64 && green < 64 && blue < 64); - spi_update_pixel(red << 2 | red >> 4, green << 2 | green >> 4, blue << 2 | blue >> 4); + uint8_t bitCount = spi.transferBits < spi.deviceBits ? spi.transferBits : spi.deviceBits; + return bitCount * ((spi.cr1 & 0xFFFF) + 1); } -void spi_update_pixel_16bpp(uint8_t red, uint8_t green, uint8_t blue) { - assert(red < 32 && green < 64 && blue < 32); - spi_update_pixel(spi.lut[red + 0], spi.lut[green + 32], spi.lut[blue + 96]); -} +static void spi_event(enum sched_item_id id) { + uint8_t bitCount = spi.transferBits < spi.deviceBits ? spi.transferBits : spi.deviceBits; + spi.rxFrame <<= bitCount; + /* Handle loopback */ + if (unlikely(spi.cr0 >> 7 & 1)) { + spi.rxFrame |= spi.txFrame >> (32 - bitCount); + } + /* For working receives, use the following: + else if (asic.spiRxAllowed) { + spi.rxFrame |= spi.deviceFrame >> (32 - bitCount); + } + */ + spi.deviceFrame <<= bitCount; + spi.deviceFrame |= spi.txFrame >> (32 - bitCount); + spi.txFrame <<= bitCount; -void spi_update_pixel_12bpp(uint8_t red, uint8_t green, uint8_t blue) { - assert(red < 16 && green < 16 && blue < 16); - spi_update_pixel(spi.lut[(red << 1) + 0], spi.lut[(green << 2) + 32], spi.lut[(blue << 1) + 96]); -} + spi.deviceBits -= bitCount; + if (spi.deviceBits == 0) { + uint32_t rxData = 0; + spi.deviceBits = spi.device_transfer(spi.deviceFrame, &rxData); + spi.deviceFrame = rxData << (32 - spi.deviceBits); + } + + spi.transferBits -= bitCount; + if (spi.transferBits == 0 && unlikely(spi.cr2 >> 7 & 1)) { + /* Note: rfve bound check was performed when starting the transfer */ + spi.rxFifo[(spi.rfvi + spi.rfve++) & (SPI_RXFIFO_DEPTH - 1)] = spi.rxFrame; + spi_update_thresholds(); + } -static void spi_sw_reset(void) { - spi.cmd = 0; - spi.fifo = 1; - spi.param = 0; - spi.gamma = 1; - spi.mode = SPI_MODE_SLEEP | SPI_MODE_OFF; - spi.colStart = 0; - spi.colEnd = spi.mac & SPI_MAC_RCX ? SPI_LAST_COL : SPI_LAST_ROW; - spi.rowStart = 0; - spi.rowEnd = spi.mac & SPI_MAC_RCX ? SPI_LAST_ROW : SPI_LAST_COL; - spi.topArea = 0; - spi.scrollArea = SPI_NUM_ROWS; - spi.bottomArea = 0; - spi.partialStart = 0; - spi.partialEnd = SPI_LAST_ROW; - spi.scrollStart = 0; - spi.tear = false; + uint32_t ticks = spi_next_transfer(); + if (ticks) { + sched_repeat(id, ticks); + } } -static void spi_hw_reset(void) { - spi.mac = 0; - spi_sw_reset(); +static void spi_update(void) { + spi_update_thresholds(); + if (!(spi.cr2 & 1)) { + if (spi.deviceBits != 0) { + spi.device_deselect(); + spi.deviceFrame = spi.deviceBits = 0; + spi.txFrame = spi.transferBits = 0; + sched_clear(SCHED_SPI); + } + } else if (spi.transferBits == 0) { + if (spi.deviceBits == 0) { + uint32_t rxData = 0; + spi.deviceBits = spi.device_select(&rxData); + spi.deviceFrame = rxData << (32 - spi.deviceBits); + } + + uint32_t ticks = spi_next_transfer(); + if (ticks) { + sched_set(SCHED_SPI, ticks); + } + } } -static void spi_write_cmd(uint8_t value) { - spi.cmd = value; - spi.param = 0; +/* Read from the SPI range of ports */ +static uint8_t spi_read(uint16_t addr, bool peek) { + static const uint16_t cr0_masks[16] = { + 0xF18C, 0xF8EF, 0xF0AC, 0xF3FC, 0xF18C, 0xF4C0, 0xF3AC, 0xF3AC, + 0xF18C, 0xFBEF, 0xF0AC, 0xF3FC, 0xF18C, 0xF4C0, 0xF3AC, 0xF3AC + }; - switch (spi.cmd) { - case 0x00: - break; - case 0x01: - spi_sw_reset(); - break; - case 0x10: - spi.mode |= SPI_MODE_SLEEP; + uint32_t shift = (addr & 3) << 3, value = 0; + switch (addr >> 2) { + case 0x00 >> 2: // CR0 + value = spi.cr0; + value &= cr0_masks[value >> 12 & 0xF]; break; - case 0x11: - spi.mode &= ~SPI_MODE_SLEEP; + case 0x04 >> 2: // CR1 + value = spi.cr1; break; - case 0x12: - spi.mode |= SPI_MODE_PARTIAL; - spi.scrollStart = 0; + case 0x08 >> 2: // CR2 + value = spi.cr2; break; - case 0x13: - spi.mode &= ~(SPI_MODE_PARTIAL | SPI_MODE_SCROLL); - spi.scrollStart = 0; + case 0x0C >> 2: // STATUS + value = spi.tfve << 12 | spi.rfve << 4 | + (spi.transferBits != 0) << 2 | + (spi.tfve != SPI_TXFIFO_DEPTH) << 1 | (spi.rfve == SPI_RXFIFO_DEPTH) << 0; break; - case 0x20: - spi.mode &= ~SPI_MODE_INVERT; + case 0x10 >> 2: // INTCTRL + value = spi.intCtrl; break; - case 0x21: - spi.mode |= SPI_MODE_INVERT; - break; - case 0x28: - spi.mode |= SPI_MODE_OFF; - break; - case 0x29: - spi.mode &= ~SPI_MODE_OFF; - break; - case 0x2C: - spi_reset_mregs(); - break; - case 0x34: - spi.tear = false; - break; - case 0x35: - spi.tear = true; + case 0x14 >> 2: // INTSTATUS + value = spi.intStatus; + if (!peek) { + spi.intStatus &= ~(1 << 1 | 1 << 0); + intrpt_set(INT_SPI, spi.intCtrl & spi.intStatus); + } break; - case 0x38: - spi.mode &= ~SPI_MODE_IDLE; + case 0x18 >> 2: // DATA + value = spi.rxFifo[spi.rfvi & (SPI_RXFIFO_DEPTH - 1)]; + if (!peek && !shift && spi.rfve) { + spi.rfvi++; + spi.rfve--; + spi_update(); + } break; - case 0x39: - spi.mode |= SPI_MODE_IDLE; + case 0x60 >> 2: // REVISION + value = 0x01 << 16 | 0x21 << 8 | 0x00 << 0; break; - default: + case 0x1C >> 2: + case 0x64 >> 2: // FEATURE + value = SPI_FEATURES << 24 | (SPI_TXFIFO_DEPTH - 1) << 16 | + (SPI_RXFIFO_DEPTH - 1) << 8 | (SPI_WIDTH - 1) << 0; break; } + return value >> shift; } -static void spi_write_param(uint8_t value) { - uint8_t word_param = spi.param >> 1; - uint8_t bit_offset = ~spi.param << 3 & 8; - - switch (spi.cmd) { - case 0x26: - if (spi.param == 0) { - spi.gamma = value; - } - break; - case 0x2A: - switch (word_param) { - case 0: - write8(spi.colStart, bit_offset, value & 0x1FF >> bit_offset); - break; - case 1: - write8(spi.colEnd, bit_offset, value & 0x1FF >> bit_offset); - break; - default: - break; - } - break; - case 0x2B: - switch (word_param) { - case 0: - write8(spi.rowStart, bit_offset, value & 0x1FF >> bit_offset); - break; - case 1: - write8(spi.rowEnd, bit_offset, value & 0x1FF >> bit_offset); - break; - default: - break; - } - break; - case 0x2C: - case 0x3C: - if (unlikely(!(spi.ifCtl & SPI_IC_CTRL_DATA))) { - switch (spi.ifBpp & 7) { - default: - case 6: /* 18bpp */ - switch (spi.param % 3) { - case 0: - spi.ifBlue = value >> 2; - break; - case 1: - spi.ifGreen = value >> 2; - break; - case 2: - spi.ifRed = value >> 2; - spi_update_pixel_18bpp(spi.ifRed, spi.ifGreen, spi.ifBlue); - break; - } - break; - case 5: /* 16bpp */ - switch (spi.param % 2) { - case 0: - spi.ifBlue = value >> 3; - spi.ifGreen = value << 3 & 0x38; - break; - case 1: - spi.ifGreen |= value >> 5; - spi.ifRed = value & 0x1F; - spi_update_pixel_16bpp(spi.ifRed, spi.ifGreen, spi.ifBlue); - break; - } - break; - case 3: /* 12bpp */ - switch (spi.param % 3) { - case 0: - spi.ifBlue = value >> 4; - spi.ifGreen = value & 0xF; - break; - case 1: - spi.ifRed = value >> 4; - spi_update_pixel_12bpp(spi.ifRed, spi.ifGreen, spi.ifBlue); - spi.ifBlue = value & 0xF; - break; - case 2: - spi.ifGreen = value >> 4; - spi.ifRed = value & 0xF; - spi_update_pixel_12bpp(spi.ifRed, spi.ifGreen, spi.ifBlue); - break; - } - break; - } - } +/* Write to the SPI range of ports */ +static void spi_write(uint16_t addr, uint8_t byte, bool poke) { + uint32_t shift = (addr & 3) << 3, value = (uint32_t)byte << shift, mask = ~(0xFFu << shift); + bool stateChanged = false; + switch (addr >> 2) { + case 0x00 >> 2: // CR0 + value &= 0xFFFF; + spi.cr0 &= mask; + spi.cr0 |= value; break; - case 0x2D: + case 0x04 >> 2: // CR1 + value &= 0x7FFFFF; + spi.cr1 &= mask; + spi.cr1 |= value; break; - case 0x30: - switch (word_param) { - case 0: - write8(spi.partialStart, bit_offset, value & 0x1FF >> bit_offset); - break; - case 1: - write8(spi.partialEnd, bit_offset, value & 0x1FF >> bit_offset); - break; - default: - break; + case 0x08 >> 2: // CR2 + if (value & 1 << 2) { + spi.rfvi = spi.rfve = 0; + stateChanged = true; } - break; - case 0x33: - switch (word_param) { - case 0: - write8(spi.topArea, bit_offset, value & 0x1FF >> bit_offset); - break; - case 1: - write8(spi.scrollArea, bit_offset, value & 0x1FF >> bit_offset); - break; - case 2: - write8(spi.bottomArea, bit_offset, value & 0x1FF >> bit_offset); - break; - default: - break; + if (value & 1 << 3) { + spi.tfvi = spi.tfve = 0; + stateChanged = true; } - break; - case 0x36: - if (spi.param == 0) { - spi.mac = value; - } - break; - case 0x37: - switch (word_param) { - case 0: - write8(spi.scrollStart, bit_offset, value & 0x1FF >> bit_offset); - spi.mode |= SPI_MODE_SCROLL; - break; - default: - break; + if ((spi.cr2 ^ value) & ~mask & (1 << 8 | 1 << 7 | 1 << 0)) { + stateChanged = true; } + value &= 0xF83; + spi.cr2 &= mask; + spi.cr2 |= value; break; - case 0x3A: - switch (word_param) { - case 0: - spi.ifBpp = value; - break; - default: - break; - } + case 0x10 >> 2: // INTCTRL + value &= 0x1FFBF; + spi.intCtrl &= mask; + spi.intCtrl |= value; + spi.intStatus &= ~(1 << 3 | 1 << 2); + spi.intStatus |= spi_get_threshold_status(); + intrpt_set(INT_SPI, spi.intCtrl & spi.intStatus); break; - case 0xB0: - switch (word_param) { - case 0: - spi.ifCtl = value; - break; - case 1: - break; - default: - break; + case 0x18 >> 2: // DATA + spi.dtr &= mask; + spi.dtr |= value; + if (!shift && spi.tfve != SPI_TXFIFO_DEPTH) { + spi.txFifo[(spi.tfvi + spi.tfve++) & (SPI_TXFIFO_DEPTH - 1)] = spi.dtr; + stateChanged = true; } break; - case 0xE0: - spi.gammaCorrection[0][spi.param] = value; - break; - case 0xE1: - spi.gammaCorrection[1][spi.param] = value; - break; - default: - break; } - - spi.param++; -} - -/* Read from the SPI range of ports */ -static uint8_t spi_read(const uint16_t pio, bool peek) { - (void)peek; - (void)pio; - return 0; -} - -/* Write to the SPI range of ports */ -static void spi_write(const uint16_t pio, const uint8_t byte, bool poke) { - (void)poke; - - if (pio == 0x18) { - spi.fifo = spi.fifo << 3 | (byte & 7); - if (spi.fifo & 0x200) { - if (spi.fifo & 0x100) { - spi_write_param(spi.fifo); - } else { - spi_write_cmd(spi.fifo); - } - spi.fifo = 1; - } + if (stateChanged) { + spi_update(); } } @@ -438,17 +262,20 @@ static const eZ80portrange_t pspi = { void spi_reset(void) { - uint8_t i = 0, c; memset(&spi, 0, sizeof(spi)); - spi_hw_reset(); - for (c = 0; c < 1 << 5; c++) { - spi.lut[i++] = c << 3 | c >> 2; - } - for (c = 0; c < 1 << 6; c++) { - spi.lut[i++] = c << 2 | c >> 4; - } - for (c = 0; c < 1 << 5; c++) { - spi.lut[i++] = c << 3 | c >> 2; + spi_set_device_funcs(); + + sched.items[SCHED_SPI].callback.event = spi_event; + sched.items[SCHED_SPI].clock = CLOCK_24M; + sched_clear(SCHED_SPI); +} + +void spi_device_select(bool arm) { + if (spi.arm != arm) { + spi.device_deselect(); + spi.arm = arm; + spi_set_device_funcs(); + spi_update(); } } @@ -459,9 +286,13 @@ eZ80portrange_t init_spi(void) { } bool spi_save(FILE *image) { - return fwrite(&spi, sizeof(spi), 1, image) == 1; + return fwrite(&spi, offsetof(spi_state_t, device_select), 1, image) == 1; } bool spi_restore(FILE *image) { - return fread(&spi, sizeof(spi), 1, image) == 1; + if (fread(&spi, offsetof(spi_state_t, device_select), 1, image) == 1) { + spi_set_device_funcs(); + return true; + } + return false; } diff --git a/core/spi.h b/core/spi.h index 04df56a84..19ad242ba 100644 --- a/core/spi.h +++ b/core/spi.h @@ -11,67 +11,30 @@ extern "C" { #include #include -#define SPI_RED 0 -#define SPI_GREEN 1 -#define SPI_BLUE 2 -#define SPI_ALPHA 3 -#define SPI_NUM_ROWS 320 -#define SPI_LAST_ROW 319 -#define SPI_NUM_COLS 240 -#define SPI_LAST_COL 239 - -enum spi_mode { - SPI_MODE_SLEEP = 1 << 0, - SPI_MODE_OFF = 1 << 1, - SPI_MODE_BLANK = 1 << 2, - SPI_MODE_PARTIAL = 1 << 3, - SPI_MODE_INVERT = 1 << 4, - SPI_MODE_IDLE = 1 << 5, - SPI_MODE_SCROLL = 1 << 6, - SPI_MODE_IGNORE = 1 << 7 -}; - -enum spi_mac { - SPI_MAC_HRO = 1 << 2, - SPI_MAC_BGR = 1 << 3, - SPI_MAC_VRO = 1 << 4, - SPI_MAC_RCX = 1 << 5, - SPI_MAC_CAO = 1 << 6, - SPI_MAC_RAO = 1 << 7 -}; - -enum spi_ic { - SPI_IC_CTRL_SYNC = 1 << 0, - SPI_IC_CTRL_DATA = 1 << 4, - SPI_IC_GRAM_BYPASS = 1 << 7 -}; +#define SPI_WIDTH 32 +#define SPI_RXFIFO_DEPTH 16 +#define SPI_TXFIFO_DEPTH 16 +#define SPI_FEATURES 0xE typedef struct spi_state { - uint32_t param; - uint16_t fifo, row, dstRow, srcRow; - uint8_t cmd, col, colDir; - - uint32_t scanLine, rowReg, colReg; - uint16_t rowStart, rowEnd, colStart, colEnd; - uint16_t partialStart, partialEnd, topArea, scrollArea, bottomArea, scrollStart; - uint8_t mode, ifBpp, ifCtl, ifRed, ifGreen, ifBlue, mac, gamma; - uint8_t lut[128], frame[320][240][3], display[240][320][4]; - - bool tear; - uint16_t tearLine; - uint8_t gammaCorrection[2][16]; + uint32_t cr0, cr1, cr2, intCtrl, dtr; + uint32_t rxFrame, txFrame, deviceFrame; + uint8_t transferBits, deviceBits; + uint8_t rfvi, rfve, tfvi, tfve; + uint8_t intStatus; + bool arm; + uint32_t rxFifo[SPI_RXFIFO_DEPTH], txFifo[SPI_TXFIFO_DEPTH]; + + uint8_t (*device_select)(uint32_t*); + uint8_t (*device_transfer)(uint32_t, uint32_t*); + void (*device_deselect)(void); } spi_state_t; extern spi_state_t spi; eZ80portrange_t init_spi(void); +void spi_device_select(bool arm); void spi_reset(void); -bool spi_hsync(void); -bool spi_vsync(void); -bool spi_refresh_pixel(void); -void spi_update_pixel_18bpp(uint8_t r, uint8_t g, uint8_t b); -void spi_update_pixel_16bpp(uint8_t r, uint8_t g, uint8_t b); -void spi_update_pixel_12bpp(uint8_t r, uint8_t g, uint8_t b); bool spi_restore(FILE *image); bool spi_save(FILE *image); diff --git a/core/uart.c b/core/uart.c new file mode 100644 index 000000000..212ba73e1 --- /dev/null +++ b/core/uart.c @@ -0,0 +1,485 @@ +#include "uart.h" +#include "emu.h" +#include "interrupt.h" +#include "schedule.h" + +#include +#include +#include + +#define UART_RX_TIMEOUT_DELAY 4 + +uart_state_t uart; + +static uart_transfer_t loopback_transfer; +static bool loopback_available = false; + +static bool bit_parity(uint8_t byte) { +#if __has_builtin(__builtin_parity) + return __builtin_parity(byte); +#else + byte ^= byte >> 4; + byte ^= byte >> 2; + byte ^= byte >> 1; + return byte & 1; +#endif +} + +static uint8_t uart_get_modem_inputs(void) { + if (uart.mcr >> 4 & 1) { + /* Loopback mode */ + return uart.mcr & UART_MSR_BITS; + } else { + return uart.msr >> 4 & UART_MSR_BITS; + } +} + +void uart_set_modem_inputs(uint8_t msr) { + uint8_t modemDiff = uart_get_modem_inputs(); + uart.msr &= UART_MSR_BITS; + uart.msr |= msr << 4; + modemDiff ^= uart_get_modem_inputs(); + if (unlikely(modemDiff)) { + uart.msr |= modemDiff; + uart.isr |= 1 << 3; + intrpt_set(INT_UART, uart.ier & uart.isr); + } +} + +static void uart_set_modem_outputs(uint8_t mcr) { + (void)mcr; +} + +static void uart_transmit_loopback(const uart_transfer_t* transfer) { + loopback_transfer = *transfer; + loopback_available = true; +} + +static bool uart_receive_loopback(uart_transfer_t* transfer) { + if (!loopback_available) { + return false; + } + + *transfer = loopback_transfer; + loopback_available = false; + return true; +} + +static void uart_transmit_null(const uart_transfer_t* transfer) { + (void)transfer; +} + +static bool uart_receive_null(uart_transfer_t* transfer) { + (void)transfer; + return false; +} + +static void uart_set_device_funcs(void) { + if (uart.mcr >> 4 & 1) { + /* Loopback mode */ + uart.transmit_char = uart_transmit_loopback; + uart.receive_char = uart_receive_loopback; + /* Modem outputs not seen externally */ + uart_set_modem_outputs(0); + } + else { + /* For non-Python models, discard transmitted characters */ + uart.transmit_char = uart_transmit_null; + uart.receive_char = uart_receive_null; + /* Modem outputs seen externally */ + uart_set_modem_outputs(uart.mcr & UART_MSR_BITS); + } +} + +static bool uart_fifo_enabled(void) { + return uart.fcr >> 0 & 1; +} + +static uint32_t uart_char_ticks(void) { + uint32_t divisor = (uint16_t)(uart.divisor - 1) + 1; + /* Start + Character + Parity + Stop */ + uint32_t bits = 1 + 5 + (uart.lcr >> 0 & 3) + (uart.lcr >> 3 & 1) + 1 + (uart.lcr >> 2 & 1); + return divisor * bits; +} + +static bool uart_timer_should_run(void) { + return uart.txActive || uart.rxTimeoutChars; +} + +static void uart_set_timer(bool force) { + if (uart_timer_should_run()) { + if (force || !sched_active(SCHED_UART)) { + sched_set(SCHED_UART, uart_char_ticks()); + } + } else { + sched_clear(SCHED_UART); + } +} + +static void uart_reset_rx_timeout(void) { + uart.rxTimeout = false; + uart.rxTimeoutChars = uart.rfve && uart_fifo_enabled() ? UART_RX_TIMEOUT_DELAY : 0; + uart_set_timer(false); +} + +static bool uart_receive_char() { + uart_transfer_t transfer; + if (!uart.receive_char(&transfer)) { + /* RX timeout not reset*/ + return false; + } + + uint8_t stat = 0; + if (unlikely(transfer.divisor != uart.divisor)) { + /* Frame error */ + stat = 1 << 3; + } else { + uint8_t lcrDiff = uart.lcr & (UART_LCR_CHAR_LEN | UART_LCR_STOP_BITS | UART_LCR_PARITY_ENABLE | UART_LCR_PARITY_EVEN | UART_LCR_PARITY_STICK); + lcrDiff ^= transfer.lcr; + if (unlikely(lcrDiff)) { + if (lcrDiff & UART_LCR_BREAK) { + /* Break/frame error */ + transfer.ch = 0; + stat = 1 << 4 | 1 << 3; + } else if (lcrDiff & (UART_LCR_CHAR_LEN | UART_LCR_STOP_BITS | UART_LCR_PARITY_ENABLE)) { + /* Frame error */ + stat = 1 << 3; + } else if (transfer.lcr & UART_LCR_PARITY_ENABLE) { + bool parityDiff = lcrDiff & UART_LCR_PARITY_EVEN; + if (lcrDiff & UART_LCR_PARITY_STICK) { + parityDiff ^= bit_parity(transfer.ch); + } + /* Parity error */ + stat = parityDiff << 2; + } + } + } + + size_t index = (uart.rfvi + uart.rfve) & (UART_FIFO_DEPTH - 1); + if (uart_fifo_enabled()) { + /* When FIFO is enabled, received bytes are discarded on overrun */ + if (uart.rfve == UART_FIFO_DEPTH) { + /* Overrun error */ + uart.lsr |= 1 << 1; + uart.isr |= 1 << 2; + /* RX timeout not reset*/ + return false; + } + /* Write new data into the FIFO */ + uart.rxfifo[index] = transfer.ch; + uart.rxstat[index] = stat; + /* On error, track the error count and set the FIFO error bit */ + if (stat) { + uart.rxstatCount++; + uart.lsr |= 1 << 7; + } + /* Restart the timeout counter if not timed out */ + if (!uart.rxTimeoutChars) { + uart.rxTimeoutChars = UART_RX_TIMEOUT_DELAY; + } + } else { + /* When FIFO is disabled, holding register is unconditionally overwritten */ + uart.rxfifo[index] = transfer.ch; + if (uart.rfve == 1) { + /* Overrun error, plus any other errors from the received byte */ + uart.lsr |= 1 << 1 | stat; + uart.isr |= 1 << 2; + /* RX timeout reset */ + return true; + } + } + if (uart.rfve++ == 0) { + /* If this is the first FIFO entry inserted, update LSR */ + uart.lsr |= 1 << 0 | stat; + /* Also trigger an interrupt on error conditions */ + uart.isr |= (stat != 0) << 2; + } + /* RX timeout reset */ + return true; +} + +static void uart_update_rxfifo_trigger(void) { + uint8_t level = 1; + if (uart_fifo_enabled() && !uart.rxTimeout) { + level = 0x0D080401 >> ((uart.fcr >> 6 & 3) * 8); + } + uart.isr &= ~(1 << 0); + uart.isr |= (uart.rfve >= level) << 0; +} + +static void uart_next_transmit(void) { + /* Put character in transmit shift register */ + uint8_t bits = 5 + (uart.lcr >> 0 & 3); + uart.tsr = uart.txfifo[uart.tfvi++ & (UART_FIFO_DEPTH - 1)] & ((1 << bits) - 1); + /* If FIFO is now empty, set corresponding status and interrupt */ + if (!likely(--uart.tfve)) { + uart.lsr |= 1 << 5; + uart.isr |= 1 << 1; + } +} + +static void uart_event(enum sched_item_id id) { + if (uart.txActive) { + /* Finish transmit of current byte */ + uart_transfer_t transfer = { + .ch = uart.tsr, + .lcr = uart.lcr, + .divisor = uart.divisor + }; + uart.transmit_char(&transfer); + + /* Transmit next byte or stop */ + if (likely(uart.tfve)) { + uart_next_transmit(); + } else { + uart.txActive = false; + /* Indicate empty FIFO and empty shift register */ + uart.lsr |= 1 << 6; + } + } + + if (!uart_receive_char() && uart.rxTimeoutChars) { + /* Set the timeout flag if the count expired */ + uart.rxTimeout = (--uart.rxTimeoutChars == 0); + } + + /* Update RX FIFO interrupt based on new fill level and timeout */ + uart_update_rxfifo_trigger(); + intrpt_set(INT_UART, uart.ier & uart.isr); + + if (uart_timer_should_run()) { + sched_repeat(id, uart_char_ticks()); + } +} + +/* Read from the 0xE000 range of ports */ +static uint8_t uart_read(const uint16_t pio, bool peek) { + uint8_t value; + + switch (pio) { + case 0x00: + if (uart.lcr >> 7 & 1) { + value = uart.divisor & 0xFF; + } else if (uart.rfve) { + /* FIFO read */ + size_t index = uart.rfvi & (UART_FIFO_DEPTH - 1); + value = uart.rxfifo[index]; + if (!peek) { + /* Remove the error status tracking from the FIFO */ + if (uart.rxstat[index]) { + if (--uart.rxstatCount == 0) { + uart.lsr &= ~(1 << 7); + } + } + uart.rfvi++; + if (--uart.rfve) { + /* Indicate error status from the next FIFO entry */ + index = uart.rxstat[uart.rfvi & (UART_FIFO_DEPTH - 1)]; + uart.lsr |= uart.rxstat[index]; + uart.isr |= (uart.rxstat[index] != 0) << 2; + } else { + /* Indicate empty FIFO */ + uart.lsr &= ~(1 << 0); + } + /* Reset the timeout status and interrupt based on the new FIFO state */ + uart_reset_rx_timeout(); + uart_update_rxfifo_trigger(); + intrpt_set(INT_UART, uart.ier & uart.isr); + } + } else { + /* Empty FIFO */ + value = 0; + } + break; + case 0x04: + if (uart.lcr >> 7 & 1) { + value = uart.divisor >> 8; + } else { + value = uart.ier; + } + break; + case 0x08: + /* Determine interrupt by priority */ + value = uart.ier & uart.isr; + if (value >> 2 & 1) { + /* Receiver Line Status */ + value = 6; + } else if (value >> 0 & 1) { + /* Received Data Available / Timeout */ + value = 4 | uart.rxTimeout << 3; + } else if (value >> 1 & 1) { + /* Transmitter Holding Register Empty */ + uart.isr &= ~(1 << 1); + intrpt_set(INT_UART, value & ~(1 << 1)); + value = 2; + } else { + /* Modem Status or no interrupt */ + value = !value; + } + /* Indicate FIFO full in Bit 4 (non-standard extension) */ + value |= (uart.tfve & UART_FIFO_DEPTH); + if (uart_fifo_enabled()) { + value |= 0xC0; + } + break; + case 0x0C: + value = uart.lcr; + break; + case 0x10: + value = uart.mcr; + break; + case 0x14: + value = uart.lsr; + /* Clear error bits and pending interrupt */ + uart.lsr &= ~(1 << 4 | 1 << 3 | 1 << 2 | 1 << 1); + if (unlikely(uart.isr >> 2 & 1)) { + uart.isr &= ~(1 << 2); + intrpt_set(INT_UART, uart.ier & uart.isr); + } + break; + case 0x18: + value = uart_get_modem_inputs() << 4; + value |= uart.msr & UART_MSR_BITS; + /* Clear delta bits and pending interrupt */ + uart.msr &= ~UART_MSR_BITS; + if (unlikely(uart.isr >> 3 & 1)) { + uart.isr &= ~(1 << 3); + intrpt_set(INT_UART, uart.ier & uart.isr); + } + break; + case 0x1C: + value = uart.scratch; + break; + /* Revision registers? */ + case 0x68: + value = 0x01; + break; + case 0x6C: + value = 0x01; + break; + case 0x70: + value = 0x10; + break; + default: + value = 0x00; + break; + } + return value; +} + +/* Write to the 0xE000 range of ports */ +static void uart_write(const uint16_t pio, const uint8_t byte, bool poke) { + (void)poke; + switch (pio) { + case 0x00: + if (unlikely(uart.lcr >> 7 & 1)) { + uart.divisor &= ~0xFF; + uart.divisor |= byte; + uart_set_timer(true); + } else { + /* FIFO write */ + if (uart.tfve != (uart_fifo_enabled() ? UART_FIFO_DEPTH : 1)) { + uart.txfifo[(uart.tfvi + uart.tfve++) & (UART_FIFO_DEPTH - 1)] = byte; + uart.lsr &= ~(1 << 6 | 1 << 5); + uart.isr &= ~(1 << 1); + if (!likely(uart.txActive)) { + uart_next_transmit(); + uart.txActive = true; + uart_set_timer(true); + } + intrpt_set(INT_UART, uart.ier & uart.isr); + } + } + break; + case 0x04: + if (unlikely(uart.lcr >> 7 & 1)) { + uart.divisor &= ~(0xFF << 8); + uart.divisor |= byte << 8; + uart_set_timer(true); + } + else { + uart.ier = byte; + intrpt_set(INT_UART, uart.ier & uart.isr); + } + break; + case 0x08: { + const uint8_t diff = uart.fcr ^ byte; + uart.fcr = byte & ~(1 << 2 | 1 << 1); + if (diff & (1 << 2 | 1 << 0)) { + /* Clear TX FIFO */ + uart.tfvi = uart.tfve = 0; + uart.isr |= 1 << 1; + uart.lsr |= (1 << 6 | 1 << 5); + uart.lsr &= ~(uart.txActive << 6); + } + if (diff & (1 << 1 | 1 << 0)) { + /* Clear RX FIFO */ + memset(&uart.rxstat, 0, sizeof(uart.rxstat)); + uart.rfvi = uart.rfve = uart.rxstatCount = 0; + uart.lsr &= ~(1 << 7 | 1 << 0); + uart_reset_rx_timeout(); + } + uart_update_rxfifo_trigger(); + intrpt_set(INT_UART, uart.ier & uart.isr); + break; + } + case 0x0C: { + uint8_t lcrDiff = uart.lcr ^ byte; + uart.lcr = byte; + if (lcrDiff & (UART_LCR_CHAR_LEN | UART_LCR_STOP_BITS | UART_LCR_PARITY_ENABLE)) { + uart_set_timer(true); + } + break; + } + case 0x10: { + uint8_t modemDiff = uart_get_modem_inputs(); + uart.mcr = byte; + modemDiff ^= uart_get_modem_inputs(); + if (unlikely(modemDiff)) { + uart.msr |= modemDiff; + uart.isr |= 1 << 3; + intrpt_set(INT_UART, uart.ier & uart.isr); + } + uart_set_device_funcs(); + break; + } + case 0x1C: + uart.scratch = byte; + break; + default: + break; + } +} + +static const eZ80portrange_t puart = { + .read = uart_read, + .write = uart_write +}; + +eZ80portrange_t init_uart(void) { + gui_console_printf("[CEmu] Initialized UART...\n"); + return puart; +} + +void uart_reset(void) { + memset(&uart, 0, sizeof(uart)); + uart.lsr = 1 << 6 | 1 << 5; + + uart_set_device_funcs(); + + sched.items[SCHED_UART].callback.event = uart_event; + sched.items[SCHED_UART].clock = CLOCK_3M; + uart_set_timer(true); +} + +bool uart_save(FILE *image) { + return fwrite(&uart, offsetof(uart_state_t, transmit_char), 1, image) == 1; +} + +bool uart_restore(FILE *image) { + if (fread(&uart, offsetof(uart_state_t, transmit_char), 1, image) == 1) { + uart_set_device_funcs(); + return true; + } + return false; +} diff --git a/core/uart.h b/core/uart.h new file mode 100644 index 000000000..1b2953370 --- /dev/null +++ b/core/uart.h @@ -0,0 +1,65 @@ +#ifndef UART_H +#define UART_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "port.h" +#include +#include +#include + +#define UART_FIFO_DEPTH 16 + +#define UART_LCR_CHAR_LEN (3 << 0) +#define UART_LCR_STOP_BITS (1 << 2) +#define UART_LCR_PARITY_ENABLE (1 << 3) +#define UART_LCR_PARITY_EVEN (1 << 4) +#define UART_LCR_PARITY_STICK (1 << 5) +#define UART_LCR_BREAK (1 << 6) + +#define UART_MSR_CTS (1 << 0) +#define UART_MSR_DSR (1 << 1) +#define UART_MSR_RI (1 << 2) +#define UART_MSR_DCD (1 << 3) +#define UART_MSR_BITS (UART_MSR_CTS | UART_MSR_DSR | UART_MSR_RI | UART_MSR_DCD) + +typedef struct uart_transfer { + uint8_t ch; + uint8_t lcr; + uint16_t divisor; +} uart_transfer_t; + +typedef struct uart_state { + uint16_t divisor; + uint8_t ier, isr; + uint8_t fcr, lcr, mcr; + uint8_t lsr, msr; + uint8_t scratch; + + bool txActive; + uint8_t tfvi, tfve, tsr; + bool rxTimeout; + uint8_t rxTimeoutChars; + uint8_t rfvi, rfve, rxstatCount; + uint8_t txfifo[UART_FIFO_DEPTH], rxfifo[UART_FIFO_DEPTH], rxstat[UART_FIFO_DEPTH]; + + void (*transmit_char)(const uart_transfer_t *transfer); + bool (*receive_char)(uart_transfer_t *transfer); +} uart_state_t; + +extern uart_state_t uart; + +void uart_set_modem_inputs(uint8_t msr); + +eZ80portrange_t init_uart(void); +void uart_reset(void); +bool uart_restore(FILE *image); +bool uart_save(FILE *image); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/core/usb/usb.c b/core/usb/usb.c index 4c9e4c312..bd1f732f8 100644 --- a/core/usb/usb.c +++ b/core/usb/usb.c @@ -213,7 +213,11 @@ static uint8_t usb_ep0_idx_update(void) { static uint8_t usb_read(uint16_t pio, bool peek) { uint8_t value = 0; if (pio < sizeof usb.regs) { - value = ((uint8_t *)&usb.regs)[pio]; + if (unlikely(usb.regs.dma_ctrl >> 31 & usb.regs.phy_tmsr) && pio >= 0x044 && pio < 0x1C8) { + value = 0; + } else { + value = ((uint8_t *)&usb.regs)[pio]; + } } else if (pio < (peek ? 0x1d8 : 0x1d4)) { value = usb.ep0_data[peek ? (usb.ep0_idx & 4) ^ (pio & 7) : (usb_ep0_idx_update() & 4) | (pio & 3)]; } @@ -223,6 +227,9 @@ static uint8_t usb_read(uint16_t pio, bool peek) { static void usb_write(uint16_t pio, uint8_t value, bool poke) { uint8_t index = pio >> 2; uint8_t bit_offset = (pio & 3) << 3; + if (unlikely(usb.regs.dma_ctrl >> 31 & usb.regs.phy_tmsr) && index >= (0x044 >> 2) && index < (0x1C8 >> 2)) { + return; + } (void)poke; switch (index) { case 0x010 >> 2: // USBCMD - USB Command Register @@ -444,6 +451,7 @@ void usb_reset(void) { usb.regs.otgisr = 0; clear(usb.regs.rsvd3); usb.regs.isr = 0; + usb.regs.imr = 0; clear(usb.regs.rsvd4); usb.regs.dev_ctrl = 0x00000020; usb.regs.dev_addr = 0; diff --git a/gui/qt/CEmu.pro b/gui/qt/CEmu.pro index 65f70eb1e..fbf3c2077 100644 --- a/gui/qt/CEmu.pro +++ b/gui/qt/CEmu.pro @@ -186,6 +186,7 @@ if(macx) { SOURCES += \ ../../tests/autotester/autotester.cpp \ ../../core/asic.c \ + ../../core/bootver.c \ ../../core/cpu.c \ ../../core/keypad.c \ ../../core/lcd.c \ @@ -209,7 +210,9 @@ SOURCES += \ ../../core/vat.c \ ../../core/emu.c \ ../../core/extras.c \ + ../../core/panel.c \ ../../core/spi.c \ + ../../core/uart.c \ ../../core/debug/debug.c \ ../../core/debug/zdis/zdis.c \ ipc.cpp \ @@ -285,6 +288,7 @@ SOURCES += ../../tests/autotester/autotester_cli.cpp \ HEADERS += \ ../../tests/autotester/autotester.h \ ../../core/asic.h \ + ../../core/bootver.h \ ../../core/cpu.h \ ../../core/atomics.h \ ../../core/defines.h \ @@ -298,6 +302,7 @@ HEADERS += \ ../../core/misc.h \ ../../core/schedule.h \ ../../core/timers.h \ + ../../core/uart.h \ ../../core/usb/device.h \ ../../core/usb/fotg210.h \ ../../core/usb/usb.h \ @@ -311,6 +316,7 @@ HEADERS += \ ../../core/vat.h \ ../../core/extras.h \ ../../core/os/os.h \ + ../../core/panel.h \ ../../core/spi.h \ ../../core/debug/debug.h \ ../../core/debug/zdis/zdis.h \ diff --git a/gui/qt/CMakeLists.txt b/gui/qt/CMakeLists.txt index afdf9d1bf..362433759 100644 --- a/gui/qt/CMakeLists.txt +++ b/gui/qt/CMakeLists.txt @@ -73,6 +73,7 @@ qt_add_executable(CEmu ${MAYBE_MACOSX_BUNDLE} ../../core/asic.c ../../core/asic.h ../../core/atomics.h ../../core/backlight.c ../../core/backlight.h + ../../core/bootver.c ../../core/bootver.h ../../core/bus.c ../../core/bus.h ../../core/cert.c ../../core/cert.h ../../core/control.c ../../core/control.h @@ -90,6 +91,7 @@ qt_add_executable(CEmu ${MAYBE_MACOSX_BUNDLE} ../../core/mem.c ../../core/mem.h ../../core/misc.c ../../core/misc.h ../../core/os/os.h + ../../core/panel.c ../../core/panel.h ../../core/port.c ../../core/port.h ../../core/realclock.c ../../core/realclock.h ../../core/registers.c ../../core/registers.h @@ -97,6 +99,7 @@ qt_add_executable(CEmu ${MAYBE_MACOSX_BUNDLE} ../../core/sha256.c ../../core/sha256.h ../../core/spi.c ../../core/spi.h ../../core/timers.c ../../core/timers.h + ../../core/uart.c ../../core/uart.h ../../core/usb/device.h ../../core/usb/disconnected.c ../../core/usb/dusb.c @@ -332,6 +335,8 @@ qt_add_resources(CEmu "skins" FILES "resources/skin/ti83pce.png" "resources/skin/ti84pce.png" + "resources/skin/ti83pce_ep.png" + "resources/skin/ti84pce_py.png" ) qt_add_resources(CEmu "setup" PREFIX "/setup" diff --git a/gui/qt/debugger.cpp b/gui/qt/debugger.cpp index c3cc1ce15..3a37b2660 100644 --- a/gui/qt/debugger.cpp +++ b/gui/qt/debugger.cpp @@ -554,6 +554,7 @@ void MainWindow::debugSync() { cpu.IEF2 = ui->checkIEF2->isChecked(); debug.totalCycles = ui->cycleView->text().toLongLong() + (m_ignoreDmaCycles ? debug.dmaCycles : 0); + debug.flashCacheMisses = ui->flashMissesView->text().toUInt(); uint32_t uiPC = static_cast(hex2int(ui->pcregView->text())); if (cpu.registers.PC != uiPC) { @@ -606,6 +607,8 @@ void MainWindow::debugGuiState(bool state) { ui->groupFlash->setEnabled(state); ui->groupRAM->setEnabled(state); ui->cycleView->setEnabled(state); + ui->flashAvgView->setEnabled(state); + ui->flashMissesView->setEnabled(state); ui->freqView->setEnabled(state); ui->groupRTC->setEnabled(state); ui->groupGPT->setEnabled(state); @@ -757,6 +760,14 @@ void MainWindow::debugPopulate() { ui->cycleView->setPalette(tmp == ui->cycleView->text() ? m_cNone : m_cBack); ui->cycleView->setText(tmp); + tmp = QString::number((double)debug.flashDelayCycles / debug.flashTotalAccesses + debug.flashWaitStates); + ui->flashAvgView->setPalette(tmp == ui->flashAvgView->text() ? m_cNone : m_cBack); + ui->flashAvgView->setText(tmp); + + tmp = QString::number(debug.flashCacheMisses); + ui->flashMissesView->setPalette(tmp == ui->flashMissesView->text() ? m_cNone : m_cBack); + ui->flashMissesView->setText(tmp); + tmp = QString::number(rtc.readSec); ui->seconds->setPalette(tmp == ui->seconds->text() ? m_cNone : m_cBack); ui->seconds->setText(tmp); @@ -849,8 +860,15 @@ void MainWindow::debugPopulate() { void MainWindow::debugZeroCycles() { debug.totalCycles = 0; debug.dmaCycles = 0; + debug.flashCacheMisses = 0; + debug.flashTotalAccesses = 0; + debug.flashDelayCycles = 0; ui->cycleView->setText(QStringLiteral("0")); ui->cycleView->repaint(); + ui->flashMissesView->setText(QStringLiteral("0")); + ui->flashMissesView->repaint(); + ui->flashAvgView->setText(QStringLiteral("nan")); + ui->flashAvgView->repaint(); } // ------------------------------------------------ diff --git a/gui/qt/debugger/hexwidget.cpp b/gui/qt/debugger/hexwidget.cpp index 32a32dae5..ef46efd2b 100644 --- a/gui/qt/debugger/hexwidget.cpp +++ b/gui/qt/debugger/hexwidget.cpp @@ -340,7 +340,7 @@ void HexWidget::paintEvent(QPaintEvent *event) { uint8_t data = static_cast(m_data[addr]); uint8_t flags = debug.addr[addr + m_base]; bool selected = addr >= m_selectStart && addr <= m_selectEnd; - bool modified = m_modified[addr]; + bool modified = !m_modified.isEmpty() && m_modified[addr]; QFont font = painter.font(); const QFont fontorig = painter.font(); diff --git a/gui/qt/emuthread.cpp b/gui/qt/emuthread.cpp index 0ccbe3b07..e2559a412 100644 --- a/gui/qt/emuthread.cpp +++ b/gui/qt/emuthread.cpp @@ -1,5 +1,6 @@ #include "emuthread.h" +#include "../../core/bootver.h" #include "../../core/control.h" #include "../../core/cpu.h" #include "../../core/emu.h" @@ -45,6 +46,10 @@ void gui_debug_close(void) { emu->debugDisable(); } +asic_rev_t gui_handle_reset(const boot_ver_t* boot_ver, asic_rev_t loaded_rev, asic_rev_t default_rev, bool* python) { + return emu->handleReset(boot_ver, loaded_rev, default_rev, python); +} + EmuThread::EmuThread(QObject *parent) : QThread{parent}, write{CONSOLE_BUFFER_SIZE}, m_speed{100}, m_throttle{true}, m_lastTime{std::chrono::steady_clock::now()}, @@ -215,6 +220,32 @@ void EmuThread::unblock() { m_mutex.unlock(); } +asic_rev_t EmuThread::handleReset(const boot_ver_t* bootVer, asic_rev_t loadedRev, asic_rev_t defaultRev, bool* python) { + // Build a list of supported revisions + QList supportedRevs; + supportedRevs.reserve(ASIC_REV_M - ASIC_REV_A + 1); + for (int rev = ASIC_REV_A; rev <= ASIC_REV_M; rev++) { + if (bootver_check_rev(bootVer, (asic_rev_t)rev)) { + supportedRevs.push_back(rev); + } + } + + // If CPU reset, override the ASIC revision + if (loadedRev == ASIC_REV_AUTO) { + loadedRev = (asic_rev_t)m_asicRev.load(); + if (!m_allowAnyRev.load() && !supportedRevs.contains((int)loadedRev)) { + loadedRev = ASIC_REV_AUTO; + } + Qt::CheckState forcePython = m_forcePython.load(); + if (forcePython != Qt::PartiallyChecked) { + *python = (forcePython == Qt::Checked); + } + } + emit sendAsicRevInfo(supportedRevs, (int)loadedRev, (int)defaultRev, *python); + + return (loadedRev != ASIC_REV_AUTO) ? loadedRev : defaultRev; +} + void EmuThread::reset() { req(RequestReset); } @@ -295,6 +326,18 @@ void EmuThread::setThrottle(bool state) { m_throttle = state; } +void EmuThread::setAsicRev(int rev) { + m_asicRev.store(rev); +} + +void EmuThread::setAllowAnyRev(bool allow) { + m_allowAnyRev.store(allow); +} + +void EmuThread::setForcePython(Qt::CheckState state) { + m_forcePython.store(state); +} + void EmuThread::debugOpen(int reason, uint32_t data) { std::unique_lock lock(m_mutexDebug); m_debug = true; diff --git a/gui/qt/emuthread.h b/gui/qt/emuthread.h index d57b12f8d..c70a4da56 100644 --- a/gui/qt/emuthread.h +++ b/gui/qt/emuthread.h @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -31,6 +32,10 @@ class EmuThread : public QThread { void throttleWait(); void setSpeed(int value); void setThrottle(bool state); + void setAsicRev(int rev); + void setAllowAnyRev(bool allow); + void setForcePython(Qt::CheckState state); + asic_rev_t handleReset(const boot_ver_t* bootVer, asic_rev_t loadedRev, asic_rev_t defaultRev, bool* python); void writeConsole(int console, const char *format, va_list args); void debugOpen(int reason, uint32_t addr); void save(emu_data_t fileType, const QString &filePath); @@ -77,6 +82,7 @@ class EmuThread : public QThread { void sendSpeed(int value); // state + void sendAsicRevInfo(const QList& supportedRevs, int loadedRev, int defaultRev, bool python); void tested(int status); void saved(bool success); void loaded(emu_state_t state, emu_data_t type); @@ -131,6 +137,10 @@ public slots: std::mutex m_mutexDebug; std::condition_variable m_cvDebug; // protected by m_mutexDebug + std::atomic_int m_asicRev; + std::atomic_bool m_allowAnyRev; + std::atomic m_forcePython; + QQueue m_keyQueue; QMutex m_keyQueueMutex; }; diff --git a/gui/qt/i18n/es_ES.qm b/gui/qt/i18n/es_ES.qm index b863c041d..4af4fb953 100644 Binary files a/gui/qt/i18n/es_ES.qm and b/gui/qt/i18n/es_ES.qm differ diff --git a/gui/qt/i18n/es_ES.ts b/gui/qt/i18n/es_ES.ts index e12331153..d5b3b3603 100644 --- a/gui/qt/i18n/es_ES.ts +++ b/gui/qt/i18n/es_ES.ts @@ -40,7 +40,7 @@ Print Vertically - Imprimir verticalmente + Mostrar verticalmente @@ -64,193 +64,193 @@ MainWindow - - + + Keypad Teclado - - + + Variables Variables - - + + Watchpoints Puntos de observación - - - + + + Size El Tamaño - - + + OS Variables Variables del SO - + Change Calculator Certificate ID Cambiar la identificación del certificado de la calculadora - + Resend selected Reenviar seleccionado - + Save transferred file paths on exit Guardar las rutas de los archivos transferidos al salir - - - + + + Capture Foto - + Screenshot Copia de LCD - - - - + + + + Settings Ajustes - + General General - + Automatically save and restore state Automáticamente guardar y restaurar estado - + ROM Image Imagen de ROM - + Debug Depurar - + Watchdog timeout Watchdog timeout - + Misc. reset Restablecer Varios - - + + Show ASCII Mostrar ASCII - + Cannot locate calculator ID in the certificate. This is usually due to an improper ROM dump. Please try another ROM dump using a physical calculator. No se puede encontrar la identificación de la calculadora. - + CEmu Change Certificate ID Cambiar la identificación de la calculadora - + Old ID: Identificación anterior: - + Debugging Info (*.ini) Información de depuración (*.ini) - + Invalid Configuration Configuración inválida - + The debugging configuration is incompatible with CEmu La configuración de depuración es incompatible con CEmu - + Debugger Import Importar depuración - + Debugger Export Exportar depuración - + Hit breakpoint Punto de encuentro conocido - + read leer - + write escribir - - + + Hit Encuentro - - + + watchpoint punto de observación - - - + + + Read Leer - + Wrote Escribió - + port puerto - + NMI triggered NMI activado @@ -271,60 +271,60 @@ Ejecutando el programa: - + Equate files (*.inc *.lab *.map) Equiparar archivos (*.inc *.lab *.map) - + All Files (*.*) Todos los archivos (*.*) - + Copy Address Copiar dirección - + Copy Data Copiar datos - + Goto VAT Memory View Ir a la vista de la memoria del VAT - + Goto Disasm View Ir a Desmontaje - - + + Toggle Breakpoint Alternar este punto de interrupción - - + + Toggle Write Watchpoint Alternar este punto de observación de escritura - - + + Toggle Read Watchpoint Alternar este punto de mira leído - - + + Toggle Read/Write Watchpoint Alternar este punto de mira de lectura / escritura @@ -341,54 +341,54 @@ Paso Previo - - - - - + + + + + Goto Ir a - - - + + + Disassembly Desensam - + Preview Previsualización - + Always show on top of other windows Siempre mostrar encima de otras ventanas - + File Locations Ubicación de archivos - + Saved Image Imagen guardada - + Skin Aspecto - + Scale: Tamaño: - + Keypad Skin Color Color de teclado @@ -399,93 +399,93 @@ Siguiente paso - + Equates: Equivale: - + Load... Carga... - - + + Refresh Refrescar - + Clear Limpiar - + Open debugger to view disassembly... Abrir depurador para ver el desensamble... - - - - + + + + Value Valor - - + + Port Monitor Monitor de puerto - + Battery Batería - + Charging Cargando - + PWR - + BEPO - + BGR - + BEBO - - + + Memory Memoria - + dock ensamblar - + console consola - + Auto scroll Desplazamiento automático @@ -495,311 +495,310 @@ Borrar - + Add Breakpoint Añadir - + Add Watchpoint Añadir - - + + Write - Escribir + Escritura - + Low Mínimo - + High Máximo - + Add Port Monitor Añadir - + LCD - + Control Controlar - + Base Base - + Current Presente - + .json file: .json archivo: - + Launch test Iniciar prueba - + Reset before test Restablecer antes de la prueba - + [Clear] before test [Borrar] antes de la prueba - + Get CRC Obtener CRC - + Start (address): Inicio (dirección): - + Size: Tamaño: - + ... or choose a preset: ...O elige un preestablecido: - + (no preset) (Sin preestablecido) - + All VRAM (LCD set in 16bpp) Todo VRAM (LCD 16bpp) - + 1st half of the VRAM (8bpp) 1ª mitad del VRAM (8bpp) - + 2nd half of the VRAM (8bpp) 2ª mitad del VRAM (8bpp) - + textShadow - + cmdShadow - + pixelShadow - + pixelShadow2 - + cmdPixelShadow2 - + plotSScreen - + saveSScreen - + lcdPalette lcdPalette - + cursorImage - + All the RAM Toda la RAM - + Hash (CRC): - + View calc variables Ver variables de la calculadora - + Selected Seleccionado - + Send files to calc Enviar archivos - + Keypad Mapping Asignación del teclado - + Change Instance ID Cambiar ID de CEmu - + Emulate physical LCD SPI drawing Emular SPI LCD real - + Auto. save and restore debug state Guardar y restaurar automáticamente - Emulate Pre-Revision I (IM 2) - Emular Pre-Revisión I (IM 2) + Emular Pre-Revisión I (IM 2) - + Record animated PNG Grabar PNG animado - + Ctrl+Shift+X Ctrl+Shift+X - + Reset calculator Restablecer calculadora - + Save state Guardar Estado - + Restore state Restaurar estado - + Reload ROM Recargar ROM - + Screen Pantalla - + Reload Rom Recargar Rom - - - - - + + + + + Address Dirección - + Automatically check for updates Verificar actualizaciones automáticamente - + Use portable configuration Usar configuración portátil - + Open debugger on reset or nmi Abrir el depurador al reiniciar o NMI - + Enable software commands Habilitar comandos de software - - + + % % - - + + Change path Cambiar ruta - + Saved Debug Depuración guardada - - + + Breakpoints Los puntos de interrupción - + Enabled Habilitado - + Registers Registros @@ -809,13 +808,13 @@ - - + + Frame skip: Salto de frames: - + 1 1 @@ -828,44 +827,44 @@ Terminar - + Add Insertar - + Port Puerto - - - + + + Data Datos - + Freeze Congelar - + Save selected Guardar la sélection - + Throttle: Aceleración: - + Text size: Tamaño del texto: - + Key bindings Atajos del teclado @@ -885,83 +884,83 @@ Ciclos - + VAT View Vista VAT - - + + Sync changes Sincronizar cambios - + Rom setup wizard... Configuración de ROM... - - + + Take PNG screenshot Tomar captura PNG - + Record animated GIF Grabar en GIF - + Check for updates... Buscar actualizaciones... - + About Qt Información de Qt - + Interrupts Interrupciones - + Display Monitor - + Brightness Brillo - - + + Console Consola - - - - - + + + + + Name Nombre - + Type Tipo - - - - - + + + + + Remove Eliminar @@ -976,73 +975,73 @@ - + Stack Apilar - + Flash Memoria de flash - - - - + + + + Search Buscar - - + + Sync Changes Sincronizar cambios - + RAM Memoria RAM - + Memory View Memoria - + &File &Archivo - + Open... Abrir... - + Exit Salir - + Setup wizard Asistente de configuración - + Browse... Examinar... - + Debug Control Control de depuración - + CPU Status Estado de la CPU @@ -1052,94 +1051,94 @@ Trigger de depuración - - + + ADL ADL - + Clear Equates Limpiar equivales - + Autoloads any .inc, .map, or .lab found when transferring files Autocargar cualquier .inc, .map o .lab que se encuentre al transferir archivos - + Autoload Autocargar - + ... ... - - + + Timers Temporizadores - + General Purpose Timers Temporizadores de propósito general - + Timer 3 Temporizador 3 - + Reload Value Recargar valor - + Timer 2 Temporizador 2 - + Timer 1 Temporizador 1 - + Real Time Clock Reloj en tiempo real - + Minutes Minutos - + Days Dias - + Hours Horas - + Seconds Segundos - + OP Number OP Número - + VAT Address Artículo de dirección de VAT @@ -1149,169 +1148,169 @@ MHz - + Data Type Tipo de datos - + OP Variables Variables del OP - - + + String Texto - - + + OS Stacks Pilas de OS - + OP Stack Pilas de OP - + FP Stack Pilas de DP - - + + Miscellaneous Miscelneos - + BPP BPP - + 2 2 - + 4 4 - + 8 8 - + 16 16 - + 24 24 - + 16 (5:6:5) 16 (5:6:5) - + 12 (4:4:4) 12 (4:4:4) - - + + AutoTester Probador automático - + Launch Test Configuration Lanzamiento - + (Re)Load Recargar - + Calculator Receive Calculadora Reciba - + View Calculator Variables Ver variables de la calculadora - + Save group Guardar grupo - + Location Ubicación - + Calculator Send Calculadora Enviar - + Resend Reenviar - + Path Ruta - + Save PNG Guardar PNG - + Copy PNG Copiar PNG - + Screen Recording Grabación de pantalla - - + + Record Grabar - + Optimize Optimizar - + Configuration Configuración - + TI-Basic Debug Depuración de TI-Basic @@ -1351,280 +1350,330 @@ - + Pause emulation on focus change Emulación de pausa cuando el interruptor - + Allow dock grouped dragging Permitir arrastrar grupos de pantallas - + Natural Natural - + Custom Personalizado - + Emulation Emulación - + Display / Emulation Speed LCD / Velocidad de Emulación - + Actual FPS: FPS real: - + Update status: Actualización del estado : - + + Avg. Flash + + + + + Flash Cache Misses + + + + + ASIC revision: + + + + sec - + + Auto + + + + + Rev A + + + + + Rev I + + + + + Rev M + + + + + Current: + + + + Debugging Depuración - + Some debugging views only make sense when in the context of normal OS operation (may not be the case when some ASM programs are running, for instance) Algunas vistas de depuración solo tienen sentido cuando están en el contexto del funcionamiento normal del sistema operativo (puede no ser el caso cuando se ejecutan algunos programas ASM, por ejemplo) - + Assume normal OS operation Asumir el funcionamiento normal del OS - + + May cause problems if boot code is incompatible. Use only if you know what you're doing. + + + + + Allow any ASIC revision + + + + Show data column Mostrar columna de datos - + Use implicit mode Use el modo implícito - + Opcode / operand space Espacio opcode/operando - + Use uppercase font Use letras mayúsculas - + Show address column Mostrar columna de dirección - + Bold disassembly symbols Símbolos de desensamblaje en negrita - - + + State Estado - + Load Carga - + Save Salvar - + Edit Editar - - + + Reset Reiniciar - + Export Exportar - + Import Importar - + Language Idioma - + &Calculator &Calculadora - + Extras Extras - + Ctrl+Shift+O Ctrl+Shift+O - + Ctrl+Shift+Q Ctrl+Shift+Q - - + + About CEmu Información de CEmu - + ROM image Imagen de ROM - - + + Calculator state Estado de la calculadora - - + + Debug state Estado de depuración - + New CEmu instance Nuevo CEmu - + Show key history Historia del teclado - + Bootable CEmu image CEmu imagen - + Hide menu bar Ocultar barra de menú - + Copy screen to clipboard Copiar la pantalla al portapapeles - + Ctrl+Shift+C Ctrl+Shift+C - + Reset GUI docks Restablecer GUI - + Reset CEmu Restablecer CEmu - + English - + Français - + Nederlands - + Español - - + + Window configuration Configuración de disposición - + Report a bug / give feedback Informar un error / dar feedback - + Hide status bar Ocultar barra de estado - - + + RAM image Imagen de RAM - + 简体中文 - + - + Simplified Chinese - + 简体中文 - + Docks Pantallas - + Welcome! CEmu uses a customizable dock-style interface. Drag and drop to move tabs and windows around on the screen, and choose which docks are available in the 'Docks' menu in the topmost bar. Be sure that 'Enable UI edit mode' is selected when laying out your interface. Enjoy! @@ -1637,156 +1686,156 @@ CEmu usa una interfaz de pantalla personalizable. Arrastra y suelta para mover p (Aviso: según su versión, puede arrastrar pestañas agrupadas o una pestaña individual desde su título o barra de pestañas, respectivamente) - - + + PNG images (*.png) Imagenes de PNG (*.png) - - + + Select saved image to restore from Seleccione una imagen guardada para restaurar - - - + + + CEmu images (*.ce);;All files (*.*) CEmu imágenes (*.ce);;Todos los archivos (*.*) - + Information Información - + Set image to save to Establecer imagen para guardar - + Emulated Speed: Velocidad Emulada: - + Checking updates is disabled for development builds Comprobación de actualizaciones está deshabilitado para el desarrollo construye - + You already have the latest CEmu version Ya tiene la última versión de CEmu - + <b>An error occurred while checking for CEmu updates.</b><br/>You can however <a href='https://github.com/CE-Programming/CEmu/releases/latest'>go here</a> to check yourself. <b>Hay un error cuando buscar actualizaciones de CEmu.</b><br/>Sin embargo ir a<a href='https://github.com/CE-Programming/CEmu/releases/latest'></a>para comprobar. - + Recording... Grabación... - + Stop Recording Terminar de grabar - + Can't preview this OS variable No se puede obtener una vista previa de esta variable de la OS - + Select at least one file to transfer Seleccionar al menos un archivo a transferir - + Actual FPS: FPS real: - + Run/Stop Ejecutar / Terminar - + Reload Recargar - + Keypress History Historia del teclado - + Clear History Borrar - + Save Screen Guardar Pantalla - + Failed to save screenshot. Error al guardar la captura de pantalla. - + Saving Recording... Guardando la grabación... - + Save Recorded PNG Guardar PNG grabados - + A failure occured during PNG recording. Error al guardar la captura de pantalla. - + Stop Recording... Terminar de grabar... - + Saving... Guardar... - + Saving Animated PNG... Guardar PNG animado... - + Record animated PNG... Grabar PNG animado... - + Copy version Copiar versión - + %1<h3>CEmu %2</h3><a href='https://github.com/CE-Programming/CEmu'>On GitHub</a><br><br>Main authors:<br>%3<br>Other contributors include:<br>%4<br>Translations provided by:<br>%5<br>Many thanks to the following projects: %6<br>In-program icons are courtesy of %7.<br><br>CEmu is licensed under the %8, and is not a TI product nor is it affiliated to/endorsed by TI.<br><br> %1<h3>CEmu %2</h3><a href='https://github.com/CE-Programming/CEmu'>On GitHub</a><br><br>Autores principales:<br>%3<br>Otros contribuidores incluyen:<br>%4<br>Traducciones proporcionadas por:<br>%5<br>Muchas gracias a los siguientes proyectos: %6<br>Los íconos dentro del programa son cortesía de %7.<br><br>CEmu tiene licencia bajo el %8, y no es un producto de TI ni está afiliado / endosado por TI.<br><br> - + [CEmu] Dock output redirected to stdout. Use the radio button to enable dock. [CEmu] Texte redirigé vers la sortie standard. Utilisez le bouton radio pour activer. @@ -1795,204 +1844,214 @@ CEmu usa una interfaz de pantalla personalizable. Arrastra y suelta para mover p TI Variable (*.8xp *.8xv *.8xl *.8xn *.8xm *.8xy *.8xg *.8xs *.8xd *.8xw *.8xc *.8xl *.8xz *.8xt *.8ca *.8cg *.8ci *.8ek *.b84 *.b83);;Todos Archivos (*.*) - + Empty Vacío - - + + Can't preview this No se puede previsualizar - + Archive Archivo - + Select at least two files to group Seleccione al menos dos archivos - + TI Group (*.8cg);;All Files (*.*) Grupo TI (*.8cg);;Todos los archivos (*.*) - - + + Transfer error, see console for information: File: Error de transferencia, consulte la consola para obtener información: Archivo: - - + + Transfer completed successfully. La transferencia se completó exitosamente. - + Error. No config loaded Error. Sin configuración cargadas - + Error. Couldn't follow the test sequence defined in the configuration Error. No se pudo seguir la secuencia de prueba definido en la configuración - + Error. Unknown one - wat? Error. Desconocido uno? - + See the test config file format and make sure values are correct and referenced files are there. Vea el formato de archivo de configuración de prueba y asegúrese de que los valores sean correctos. - + Make sure you have entered a valid start/size pair or preset. Asegúrate de que los valores sean correctos. - + Could not convert those values into numbers No se pudieron convertir esos valores en números - + Could not retrieve this memory chunk No se pudo recuperar este fragmento de memoria - + Image does not appear to be from a CE. Do you want to attempt to load it anyway? This may cause instability. La imagen no parece ser de un CE. ¿Quieres intentar cargarlo de todos modos? Esto puede causar inestabilidad. - + Set PC Establecer contador de programa - + Run Until Ejecutar hasta - + Goto Disassembly View Ir a Desmontaje - + Enter image path Ingrese ruta de la imagen - + Please choose a json file or type its path. Elija un archivo json o escriba su ruta. - + No translation available for this language :( No hay traducción disponible para este idioma :( - + Add memory view Agregar vista de memoria - + Add memory visualizer Agregar pantalla de memoria - + Memory Visualizer Pantalla de memoria - + CEmu was not able to write to the standard settings location. Portable mode has been activated. CEmu no pudo escribir en la ubicación de configuración estándar. El modo portátil ha sido activado. - + ROM images (*.rom) Imagen de ROM (*.rom) - + Set ROM image to save to Establecer imagen de ROM para guarda - + RAM images (*.ram) Imagen de RAM (*.ram) - + Set RAM image to save to Establecer imagen de RAM para guarda - + Select RAM image to load Seleccionar imagen de RAM - + RAM images (*.ram);;All files (*.*) Imagen de RAM (*.ram);;Todos los archivos (*.*) - + Saving failed. Please check write permissions in settings directory. Error de guardado. - + + Auto (%0) + + + + + Current: %0 (change requires reset) + + + + Version copied! Versión copiada! - + TI Variable (*.8xp *.8xv *.8xl *.8xn *.8xm *.8xy *.8xg *.8xs *.8xd *.8xw *.8xc *.8xl *.8xz *.8xt *.8ca *.8cg *.8ci *.8ek *.8eu *.8pu *.b84 *.b83);;All Files (*.*) Variable de TI (*.8xp *.8xv *.8xl *.8xn *.8xm *.8xy *.8xg *.8xs *.8xd *.8xw *.8xc *.8xl *.8xz *.8xt *.8ca *.8cg *.8ci *.8ek *.8eu *.8pu *.b84 *.b83);;All Files (*.*) - + Couldn't go to where the JSON file is. No se pudo encontrar el archivo JSON. - + Couldn't read JSON file. No se pudo abrir el archivo de JSON. - + Unable to open the file. No se puede abrir el archivo. - + Test results Resultados de la prueba - + Out of %2 tests attempted: %4 passed %6 failed @@ -2001,55 +2060,55 @@ El modo portátil ha sido activado. %6 fallado - + Launch program Ejecute el programa - - - - + + + + Goto Memory View Ir a la memoria - + CEmu Change ID Cambio de ID de CEmu - + New ID: Nueva ID: - + Set saved image to restore from Establecer la imagen guardada para restaurar desde - + Set debugging information path Establecer la ruta de información de depuración - + Debugging information (*.ini);;All files (*.*) Información de depuración (* .ini);;Todos archivos (*. *) - + Enable UI edit mode Habilitar modo de edición - + Warning Advertencia - + A bootable image can be used to start CEmu with predefined configurations, without the need for any extra setup. The bootable image should be placed in the same directory as the CEmu executable. When CEmu is then started, the boot image will be loaded automatically and then removed for convience. @@ -2058,87 +2117,87 @@ The bootable image should be placed in the same directory as the CEmu executable La imagen de inicio debe colocarse en el mismo directorio que el ejecutable CEmu. Cuando se inicia CEmu, la imagen de arranque se cargará automáticamente y luego se eliminará para mayor comodidad. - + Save bootable CEmu image Guardar imagen CEmu de arranque - + Bootable CEmu images (*.cemu); Imágenes booteables de CEmu (* .cemu); - + No update available Sin actualización disponible - + CEmu update Actualización de CEmu - + <b>A new version of CEmu is available!</b><br/>You can <a href='%1'>download it here</a>. <b>Una nueva versión de CEmu está disponible!</b><br/>Puede <a href='%1'>descargarlo aquí</a>. - + Update check failed Fallo la verificación de actualización - + Keymap Config (*.ini);;All files (*.*) Configuración de disposición del teclado (*.ini);;Todos los archivos (*.*) - + Unable to set custom keymap. No se puede establecer un disposición teclas personalizado. - + Keep migratable settings Mantener la configuración migrable - + This version of CEmu is not compatible with your settings, probably made by an older version. Would you like to erase them to prevent any unexpected behavior? Esta versión de CEmu no es compatible con tu configuración, probablemente hecha con una versión anterior. ¿Te gustaría borrarlos para evitar cualquier comportamiento inesperado? - + Window Config (*.ini) Configuración (*.ini) - + Save window configuration Guardar configuración de disposición - + Window Config (*.ini);;All files (*.*) Configuración (*.ini);;Todos los archivos (*.*) - + Keymap Config (*.ini) Configuración de asignación (*.ini) - + Save keymap configuration Guardar la configuración de asignaciónes del teclado - + Check for updates Buscar actualizaciones - + Resume emulation Reanudar emulación @@ -2158,12 +2217,12 @@ La imagen de inicio debe colocarse en el mismo directorio que el ejecutable CEmu Texto no encontrado. - + Error Error - + Toggle Windows Console Activar la consola de Windows @@ -2201,7 +2260,7 @@ Archivo: Transfer issue - + Problema de transferencia diff --git a/gui/qt/i18n/fr_FR.qm b/gui/qt/i18n/fr_FR.qm index e0bac8842..e6581edfd 100644 Binary files a/gui/qt/i18n/fr_FR.qm and b/gui/qt/i18n/fr_FR.qm differ diff --git a/gui/qt/i18n/fr_FR.ts b/gui/qt/i18n/fr_FR.ts index 27f70f126..a156447cb 100644 --- a/gui/qt/i18n/fr_FR.ts +++ b/gui/qt/i18n/fr_FR.ts @@ -40,7 +40,7 @@ Print Vertically - + Afficher verticalement @@ -64,176 +64,176 @@ MainWindow - - + + Keypad Clavier - - + + Variables Variables - - - + + + Size Taille - + Resend selected Renvoyer la sélection - + Save transferred file paths on exit Sauver les chemins des transferts en quittant - - - + + + Capture Capture - + Screenshot Capture d'écran - - - - + + + + Settings Paramètres - + General Général - + Automatically save and restore state Automatiquement sauver et reprendre l'état - + ROM Image Image ROM - + Debug Débogage - + Watchdog timeout Timeout du watchdog - + Misc. reset Reset (autre) - - + + Show ASCII Voir l'ASCII - + Cannot locate calculator ID in the certificate. This is usually due to an improper ROM dump. Please try another ROM dump using a physical calculator. Impossible de trouver l'ID de la calculatrice dans le certificat. Ceci est généralement dû à un dump de ROM incomplet. Veuillez retenter un dump de ROM. - + CEmu Change Certificate ID Changement d'ID certificat - + Old ID: Ancien ID: - + Debugging Info (*.ini) Information de débogage (*.ini) - + Invalid Configuration Configuration invalide - + The debugging configuration is incompatible with CEmu La configuration de debug est incompatible avec CEmu - + Debugger Import Import de débogage - + Debugger Export Export de débogage - + Hit breakpoint Point d'arrêt atteint - + read lecture - + write écriture - - + + Hit Atteint - - + + watchpoint - - - + + + Read Lu - + Wrote Écrit - + port - + NMI triggered NMI déclenché @@ -246,36 +246,36 @@ No Basic Program Executing. - + Aucun programme Basic en cours d'exécution Executing Program: - + Lancement du programme : - + Equate files (*.inc *.lab *.map) Fichier d'equates (*.inc *.lab *.map) - + All Files (*.*) Tous les fichiers (*.*) - + Copy Address Copier l'adresse - + Copy Data Copier les données - + Goto VAT Memory View Localiser en mémoire VAT @@ -291,54 +291,54 @@ - - - - - + + + + + Goto Aller à - - - + + + Disassembly Désassemblage - + Preview Aperçu - + Always show on top of other windows Toujours afficher au premier plan - + File Locations Emplacements des fichiers - + Saved Image Image sauvé - + Skin - + Scale: Échelle : - + Keypad Skin Color Couleur du skin du clavier @@ -349,93 +349,93 @@ - + Equates: - + Load... Charger... - - + + Refresh Actualiser - + Clear Effacer - + Open debugger to view disassembly... Ouvrez le débogueur pour voir le désassemblage... - - - - + + + + Value Valeur - - + + Port Monitor Moniteur de ports - + Battery Batterie - + Charging En charge - + PWR - + BEPO - + BGR - + BEBO - - + + Memory Mémoire - + dock - + console - + Auto scroll Auto-défilement @@ -446,568 +446,617 @@ - + TI-Basic Debug - + Débuggage TI-Basic Show Fetches - + Afficher les fetchs Enable TI-Basic Debugging - + Activer le débuggage TI-Basic Show Temp Parser - + Afficher le parseur temporaire Show Live Execution - + Suivre l'exécution en temps-réel Highlight Source - + Coloration syntaxique Program Debug - + Débuggage du programme Temporary Parser - + Parseur temporaire + + + + Avg. Flash + Flash moy. + + + + Flash Cache Misses + Défauts cache flash - + Clear Equates Enlever les equates - + Autoloads any .inc, .map, or .lab found when transferring files Auto-charger les .inc, .map, .lab trouvés lors de transferts - + Autoload Auto-charger - + Add Breakpoint Ajouter - + Add Watchpoint Ajouter - - + + Write - + Écriture - + Low - + Min - + High - + Max - + Add Port Monitor Ajouter - + LCD - + Control Contrôle - + Base Base - + Current Actuel - + .json file: Fichier .json : - + Launch test Lancer le test - + Reset before test Reset avant le test - + [Clear] before test [Clear] avant le est - + Get CRC Obtenir le CRC - + Start (address): Début (adresse) : - + Size: Taille : - + ... or choose a preset: ... ou choisissez un preset : - + (no preset) (pas de preset) - + All VRAM (LCD set in 16bpp) Toute la VRAM (LCD mis en 16bpp) - + 1st half of the VRAM (8bpp) 1ère moitié de la VRAM (8bpp) - + 2nd half of the VRAM (8bpp) 2ème moitié de la VRAM (8bpp) - + textShadow - + cmdShadow - + pixelShadow - + pixelShadow2 - + cmdPixelShadow2 - + plotSScreen - + saveSScreen - + lcdPalette - + cursorImage - + All the RAM Toute la RAM - + Hash (CRC): Hash (CRC) : - + View calc variables Voir les variables de la calculatrice - + Selected Sélectionné - + Send files to calc Envoyer des fichiers à la calculatrice - + Natural Naturel - + Custom Personnalisé - + + ASIC revision: + Révision de l'ASIC + + + + Auto + + + + + Rev A + + + + + Rev I + + + + + Rev M + + + + + Current: + Actuelle : + + + + May cause problems if boot code is incompatible. Use only if you know what you're doing. + Peut causer des problèmes si le boot-code est incompatible. À utiliser uniquement si vous savez ce que vous faites. + + + + Allow any ASIC revision + Autoriser n'importe quelle révision d'ASIC + + + Keypad Mapping Associations de touches - + Change Instance ID Changer l'ID de l'instance - + 简体中文 - + - + Simplified Chinese - + 简体中文 - + Update status: Mise à jour du statut : - + sec - + Emulate physical LCD SPI drawing Émuler le dessin du LCD par le SPI - + Auto. save and restore debug state Auto. sauver et charger l'état de débogage - Emulate Pre-Revision I (IM 2) - Émulation de révision pre-I (IM 2) + Émulation de révision pre-I (IM 2) - + Some debugging views only make sense when in the context of normal OS operation (may not be the case when some ASM programs are running, for instance) Certaines fenêtres de débogage n'ont de sens que lorsque l'OS est en train de s'exécuter (et non pas lors de l'exécution de certains programmes ASM, par exemple) - + Assume normal OS operation Supposer que l'OS est en cours d'exécution - + Extras Extras - + Record animated PNG Capture PNG animé - + Reset calculator Réinitialiser la calculatrice - + Save state Sauver l'état - + Restore state Restaurer l'état - + Reload ROM Recharger la ROM - - + + Calculator state État de la calculatrice - - + + Window configuration Configuration de la fenêtre - + Report a bug / give feedback Faire part d'un bug / suggestion - + Hide status bar Cacher la barre de statut - + Bootable CEmu image Image CEmu bootable - + Hide menu bar Cacher la barre de menu - + Screen Écran - + Ctrl+Shift+X - + Reload Rom Recharger la ROM - - - - - + + + + + Address Adresse - + Automatically check for updates Vérifier automatiquement les mises à jour - + Use portable configuration Utiliser une configuration portable - + Open debugger on reset or nmi Ouvrir le débuggeur lors d'un reset ou NMI - + Enable software commands Activer les commandes logicielles - - + + % - + Location Emplacement - + Path Chemin - - + + Change path Changer de dossier - + Saved Debug Débogage sauvé - - + + ADL - + ... ... - - + + Breakpoints Points d'arrêt - + Enabled Activé - - + + Watchpoints Watchpoints - - + + OS Variables Variables d'OS - + OP Variables Variables OP - - + + String String - + Change Calculator Certificate ID Changer l'ID du certificat - - + + OS Stacks Piles (stacks) de l'OS - + OP Stack Pile OP - + FP Stack Pile FP - + BPP BPP - + 2 2 - + 4 4 - + 8 8 - + 16 16 - + 24 24 - + 16 (5:6:5) 16 (5:6:5) - + 12 (4:4:4) 12 (4:4:4) - + Registers Registres - + New CEmu instance Nouvelle instance de CEmu - + Show key history Afficher l'historique des touches - + Copy screen to clipboard Capturer l'écran dans le presse-papier - + Ctrl+Shift+C @@ -1017,13 +1066,13 @@ État / drapeaux (flags) - - + + Frame skip: Saut d'image: - + 1 1 @@ -1036,44 +1085,44 @@ Arrêter - + Add Ajouter - + Port Port - - - + + + Data Données - + Freeze Geler - + Save selected Enregistrer la sélection - + Throttle: Limite : - + Text size: Taille du texte: - + Key bindings Raccourcis clavier @@ -1093,83 +1142,83 @@ Cycles - + VAT View Vue de la VAT - - + + Sync changes Synchroniser les changements - + Rom setup wizard... Assistant de config. de la ROM... - - + + Take PNG screenshot Prendre une capture d'écran PNG - + Record animated GIF Prendre une capture GIF - + Check for updates... Vérifier les mises à jour... - + About Qt À propos de Qt - + Interrupts Interruptions - + Display Écran - + Brightness Luminosité - - + + Console Console - - - - - + + + + + Name Nom - + Type Type - - - - - + + + + + Remove Supprimer @@ -1184,89 +1233,89 @@ - + Stack - + Flash - - - - + + + + Search Recherche - - + + Sync Changes Synchroniser les changements - + RAM - + Memory View Vue mémoire - + &File &Fichier - + Open... Ouvrir... - + Exit Quitter - + Setup wizard Assistant de configuration - + OP Number Numéro OP - + VAT Address Adresse en VAT - + Data Type Type de données - - + + Timers Timers - + Debug Control Contrôle de debug - + CPU Status Etat du CPU @@ -1281,316 +1330,316 @@ MHz - + General Purpose Timers - + Timer 3 - + Reload Value Valeur de reload - + Timer 2 - + Timer 1 - + Real Time Clock - + Minutes Minutes - + Days Jours - + Hours Heures - + Seconds Secondes - - + + Miscellaneous Divers - - + + AutoTester - + Launch Test Configuration Configuration du lancement de tests - + Browse... Parcourir... - + (Re)Load (Re)Charger - + Calculator Receive Réception calculatrice - + View Calculator Variables Voir les variables de la calculatrice - + Save group Enregistrer le groupe - + Calculator Send Envoi calculatrice - + Resend Renvoi - + Save PNG Enregistrer en PNG - + Copy PNG Copier en PNG - + Screen Recording Enregistrement d'écran - - + + Record Enregistrer - + Optimize Optimiser - + Configuration Configuration - + Pause emulation on focus change Mettre en pause l'émulation quand le focus est perdu - + Allow dock grouped dragging Drag'n'drop groupé de docks - + Emulation Émulation - + Display / Emulation Speed Affichage / Vitesse d'émulation - + Actual FPS: FPS réel : - + Debugging Debuggage - + Show data column Afficher la colonne des données - + Use implicit mode Utiliser le mode implicite - + Opcode / operand space Espace opcode/opérande - + Use uppercase font Utiliser une police majuscule - + Show address column Afficher la colonne d'adresse - + Bold disassembly symbols Symboles de désassembleur en gras - - + + State État - + Load Charger - + Save Sauver - + Edit Modifier - - + + Reset Reset - + Export Export - + Import Import - + Language Langage - + &Calculator &Calculatrice - + Ctrl+Shift+O - + Ctrl+Shift+Q - - + + About CEmu A propos de CEmu - + ROM image Image ROM - - + + Debug state Etat du debug - + Reset GUI docks Reset des docks de l'interface - + Reset CEmu Reset de CEmu - + English - + Français - + Nederlands - + Español - - + + RAM image Image RAM - + Docks Docks - + Welcome! CEmu uses a customizable dock-style interface. Drag and drop to move tabs and windows around on the screen, and choose which docks are available in the 'Docks' menu in the topmost bar. Be sure that 'Enable UI edit mode' is selected when laying out your interface. Enjoy! @@ -1602,273 +1651,283 @@ CEmu utilise une interface personnalisable de "docks". Glissez-dépose (Attention: depuis la v1.1, vous pouvez déplacer les onglets en groupe ou individuellement, depuis leur barre de titre ou d'onglet, repectivement) - - + + PNG images (*.png) Images PNG (*.png) - - + + Select saved image to restore from Sélectionner l'image pour la restauration - - - + + + CEmu images (*.ce);;All files (*.*) Images CEmu (*.ce);;Tous les fichiers (*.*) - + Information Information - + Set image to save to Emplacement de l'image - + Emulated Speed: Vitesse d'émulation : - + Checking updates is disabled for development builds La vérification de mises-à-jour est désactivée dans les versions de développement - + <b>An error occurred while checking for CEmu updates.</b><br/>You can however <a href='https://github.com/CE-Programming/CEmu/releases/latest'>go here</a> to check yourself. <b>Une erreur est survenue lors de la vérification de mises-à-jour de CEmu.</b><br/>Vous pouvez cependant <a href='https://github.com/CE-Programming/CEmu/releases/latest'>aller vérifier ici</a> par vous-même. - + Recording... Enregistrement... - + Stop Recording Arrêter l'enregistrement - + Can't preview this OS variable Pas d'aperçu pour cette variable OS - - + + Can't preview this Pas d'aperçu - + Actual FPS: FPS réel : - + Save Screen Enregistrer l'écran - + Failed to save screenshot. Erreur d'enregistrement de la capture. - + Saving Recording... Sauvegarde de l'enregistrement... - + Save Recorded PNG Sauver l'enregistrement PNG - + A failure occured during PNG recording. Erreur lors de l'enregistrement PNG. - + Stop Recording... Arrêter l'enregistrement... - + Saving... Sauvegarde... - + Saving Animated PNG... Sauvegarde du PNG animé... - + Record animated PNG... Enregistrer un PNG animé... - + %1<h3>CEmu %2</h3><a href='https://github.com/CE-Programming/CEmu'>On GitHub</a><br><br>Main authors:<br>%3<br>Other contributors include:<br>%4<br>Translations provided by:<br>%5<br>Many thanks to the following projects: %6<br>In-program icons are courtesy of %7.<br><br>CEmu is licensed under the %8, and is not a TI product nor is it affiliated to/endorsed by TI.<br><br> %1<h3>CEmu %2</h3><a href='https://github.com/CE-Programming/CEmu'>Sur GitHub</a><br><br>Auteurs principaux:<br>%3<br>Parmi les autres contributeurs :<br>%4<br>Traductions par:<br>%5<br>Un grand merci aux projets suivants : %6<br>Les icônes proviennent de %7.<br><br>CEmu est sous licence %8, et n'est pas un produit de TI ni en est affilié/endossé.<br><br> - + TI Variable (*.8xp *.8xv *.8xl *.8xn *.8xm *.8xy *.8xg *.8xs *.8xd *.8xw *.8xc *.8xl *.8xz *.8xt *.8ca *.8cg *.8ci *.8ek *.8eu *.8pu *.b84 *.b83);;All Files (*.*) - + Variable TI (*.8xp *.8xv *.8xl *.8xn *.8xm *.8xy *.8xg *.8xs *.8xd *.8xw *.8xc *.8xl *.8xz *.8xt *.8ca *.8cg *.8ci *.8ek *.8eu *.8pu *.b84 *.b83);;Tous les fichiers (*.*) - + Goto Disassembly View Aller au désassembleur - + Enter image path Entrer le chemin de l'image - + Archive Archive - + Select at least two files to group Choisissez au moins deux fichiers à grouper - - + + Transfer error, see console for information: File: Erreur de transfer, voir la console pour plus d'infos. Fichier : - - + + Transfer completed successfully. Transfert effectué avec succès. - + See the test config file format and make sure values are correct and referenced files are there. Regardez le format de fichier des configs de tests, et vérifiez que les valeurs soient correctes ainsi que les fichiers référencés présents. - + Make sure you have entered a valid start/size pair or preset. Vérifiez bien d'avoir entré une paire/preset valide de début/taille. - + Select at least one file to transfer Choisissez au moins un fichier à transférer - + Run/Stop Lancer / Arrêter - + Reload Recharger - + No translation available for this language :( Pas de traduction disponible pour cette langue :( - + Add memory view Ajouter une vue mémoire - + Add memory visualizer Ajouter un visualiseur de mémoire - + Memory Visualizer Visualiseur mémoire - + Keypress History Historique des touches - + Clear History Vider l'historique - + CEmu was not able to write to the standard settings location. Portable mode has been activated. CEmu n'a pas pu écrire à l'endroit standard des paramètres. Le mode portable a été activé. - + ROM images (*.rom) Images ROM (*.rom) - + Set ROM image to save to Emplacement de l'image ROM - + RAM images (*.ram) Images RAM (*.ram) - + Set RAM image to save to Emplacement de l'image RAM - + Select RAM image to load Image RAM à charger - + RAM images (*.ram);;All files (*.*) Images RAM (*.ram);;Tous les fichiers (*.*) - + Saving failed. Please check write permissions in settings directory. La sauvegarde a échouée. Veuillez vérifier les permissions du dossier des paramètres. - + + Auto (%0) + + + + + Current: %0 (change requires reset) + Actuelle : %0 (reset requis après changement) + + + Copy version Copier la version - + Version copied! - + Version copiée ! - + [CEmu] Dock output redirected to stdout. Use the radio button to enable dock. [CEmu] Sortie vers le dock redirigée vers stdout. Utilisez le bouton radio pour l'activer. @@ -1877,57 +1936,57 @@ Le mode portable a été activé. Variable TI (*.8xp *.8xv *.8xl *.8xn *.8xm *.8xy *.8xg *.8xs *.8xd *.8xw *.8xc *.8xl *.8xz *.8xt *.8ca *.8cg *.8ci *.8ek *.b84 *.b83);;Tous les fichiers (*.*) - + Empty Vide - + TI Group (*.8cg);;All Files (*.*) Groupe TI (*.8cg);;Tous les fichiers (*.*) - + Error. No config loaded Erreur. Pas de configuration chargée - + Error. Couldn't follow the test sequence defined in the configuration Erreur. Impossible de faire suivre la séquence de test définie dans la configuration - + Error. Unknown one - wat? Erreur inconnue - wat ? - + Please choose a json file or type its path. Veuillez choisir un fichier JSON ou tapez son chemin. - + Couldn't go to where the JSON file is. Impossible d'aller là où est le fichier JSON. - + Couldn't read JSON file. Impossible de lire le fichier JSON. - + Unable to open the file. Impossible d'ouvrir le fichier. - + Test results Résultats du test - + Out of %2 tests attempted: %4 passed %6 failed @@ -1936,93 +1995,93 @@ Le mode portable a été activé. %6 échoué(s) - + Could not convert those values into numbers Impossible de convertir en nombre - + Could not retrieve this memory chunk Impossible de récupérer cette partie de la mémoire - + Image does not appear to be from a CE. Do you want to attempt to load it anyway? This may cause instability. L'image n'a pas l'air d'être celle d'une CE. Voulez-vous quand même la charger? Ceci peut provoquer des instabilités. - + Set PC Mettre PC à... - - + + Toggle Breakpoint (Dés)activer le breakpoint - - + + Toggle Write Watchpoint (Dés)activer le watchpoint d'une écriture - - + + Toggle Read Watchpoint (Dés)activer le watchpoint d'une lecture - - + + Toggle Read/Write Watchpoint (Dés)activer le watchpoint d'une lecture/écriture - + Run Until Exécuter jusqu'ici - + Launch program Lancer le programme - - - - + + + + Goto Memory View Localiser en mémoire - + Goto Disasm View Aller au désassembleur - + CEmu Change ID Changement d'ID CEmu - + New ID: Nouvel ID: - + Warning Avertissement - + A bootable image can be used to start CEmu with predefined configurations, without the need for any extra setup. The bootable image should be placed in the same directory as the CEmu executable. When CEmu is then started, the boot image will be loaded automatically and then removed for convience. @@ -2031,112 +2090,112 @@ The bootable image should be placed in the same directory as the CEmu executable L'image bootable doit être placée dans le même dossier que CEmu. Au prochain démarrage, l'image bootable sera chargée automatiquement puis enlevée par commodité. - + Save bootable CEmu image Enregistrer l'image CEmu bootable - + Bootable CEmu images (*.cemu); Images CEmu bootables (*.cemu); - + Set saved image to restore from Choix du chemin de sauvegarde pour la reprise - + Set debugging information path Chemin des informations de debug - + Debugging information (*.ini);;All files (*.*) Informations de debug (*.ini);;Tous les fichiers (*.*) - + You already have the latest CEmu version Vous avez déjà la dernière version de CEmu - + Keymap Config (*.ini);;All files (*.*) Configuration des raccourcis (*.ini);;Tous les fichiers (*.*) - + Unable to set custom keymap. Impossible d'importer les raccourcis. - + Keep migratable settings Garder les paramètres migrables - + This version of CEmu is not compatible with your settings, probably made by an older version. Would you like to erase them to prevent any unexpected behavior? Cette version de CEmu n'est pas compatible avec vos paramètres, probablement créés depuis une ancienne version. Voulez-vous les effacer afin d'éviter tout comportement anormal ? - + Window Config (*.ini) Config de l'interface (*.ini) - + Save window configuration Sauver la config de l'interface - + Window Config (*.ini);;All files (*.*) Config de l'interface (*.ini);;Tous les fichiers (*.*) - + Keymap Config (*.ini) Configuration des raccourcis (*.ini) - + Save keymap configuration Sauver la config des raccourcis - + Enable UI edit mode Activer l'édition d'interface - + No update available Pas de mise-à-jour disponible - + CEmu update Mise-à-jour de CEmu - + <b>A new version of CEmu is available!</b><br/>You can <a href='%1'>download it here</a>. <b>Une nouvelle version de CEmu est disponible !</b><br/>Vous pouvez la <a href='%1'>télécharger ici</a>. - + Update check failed Erreur de vérification des mises-à-jour - + Check for updates Vérifier les mises-à-jour - + Resume emulation Reprendre l'émulation @@ -2156,12 +2215,12 @@ L'image bootable doit être placée dans le même dossier que CEmu. Au proc Chaîne non trouvée. - + Error Erreur - + Toggle Windows Console Toggle console Windows @@ -2199,12 +2258,12 @@ Fichier : Transfer issue - + Problème de transfert Transfer issue, see console for information. - + Problème de transfert, voir la console pour plus d'informations. diff --git a/gui/qt/i18n/nl_NL.qm b/gui/qt/i18n/nl_NL.qm index 713d4107b..37a2cacbd 100644 Binary files a/gui/qt/i18n/nl_NL.qm and b/gui/qt/i18n/nl_NL.qm differ diff --git a/gui/qt/i18n/nl_NL.ts b/gui/qt/i18n/nl_NL.ts index 643673228..e2281916d 100644 --- a/gui/qt/i18n/nl_NL.ts +++ b/gui/qt/i18n/nl_NL.ts @@ -40,7 +40,7 @@ Print Vertically - + Verticaal bekijken @@ -64,177 +64,177 @@ MainWindow - - + + Variables Variabelen - + Save selected Selectie opslaan - - - - - + + + + + Name Naam - + Type Type - - - + + + Size Grootte - + Preview Weergeven - - - + + + Capture Opname - + Screenshot Schermopname - - + + Frame skip: Frames overslaan: - - - - + + + + Settings Instellingen - + General Algemeen - + Automatically check for updates Automatisch controleren op updates - + Automatically save and restore state Automatisch status opslaan en laden - + Always show on top of other windows Altijd boven andere vensters weeergeven - + Use portable configuration Portable configuratie gebruiken - + Open debugger on reset or nmi Debugger openen bij een reset of nmi - + Enable software commands Softwarecommando's activeren - + Text size: Tekstgrootte: - + Display Scherm - + Skin Thema - + Scale: Schaal: - - + + % % - + Throttle: Reguleren: - + Key bindings Sneltoetsen - + File Locations Bestandslocaties - - + + Change path Pad aanpassen - + Saved Debug Opgeslagen Debug - + Saved Image Opgeslagen Image - + ROM Image ROM-bestand - + Setup wizard Installatiescherm - + Keypad Skin Color Themakleur toetsenbord - + Debug Debug @@ -270,7 +270,7 @@ - + CPU Status CPU Status @@ -280,200 +280,200 @@ Debug Trigger - - + + ADL ADL - - - + + + Disassembly Disassembly - - - - - + + + + + Goto Ga naar - + Equates: Equates: - + Load... Laden… - - + + Refresh Vernieuwen - + Clear Wissen - + Open debugger to view disassembly... Open debugger om disassembly te zien… - - + + Timers Timers - + General Purpose Timers Algemene Timers - + Timer 3 Timer 3 - + Reload Value Waarde opnieuw laden - + Timer 2 Timer 2 - + Timer 1 Timer 1 - + Real Time Clock Real-Time klok - + Minutes Minuten - + Days Dagen - + Hours Uren - + Seconds Seconden - - + + Breakpoints Breakpoints - - - - - + + + + + Address Adres - + Enabled Ingeschakeld - + Add Toevoegen - - - - - + + + + + Remove Verwijderen - - - - + + + + Value Waarde - - + + Port Monitor Poort-Monitor - + Port Poort - + Freeze Bevriezen - + Battery Batterij - + Charging Opladen - + PWR PWR - + BEPO BEPO - + BGR BGR - + BEBO BEBO - + Brightness Helderheid @@ -484,7 +484,7 @@ - + Debug Control Debug Controle @@ -495,7 +495,7 @@ - + Registers Registers @@ -515,69 +515,69 @@ Cycles - + Stack Stack - + Interrupts Interrupts - + VAT View VAT-weergave - - + + Memory Geheugen - + Flash Flash - - - - + + + + Search Zoeken - - + + Sync changes Aanpassingen doorvoeren - + RAM RAM - + Memory View Geheugen-weergave - + OP Number OP-Nummer - - - + + + Data Data - + VAT Address VAT-Adres @@ -587,249 +587,249 @@ MHz - + Data Type Data-Type - - + + Miscellaneous Diversen - + LCD LCD - + BPP BPP - + 1 1 - + 2 2 - + 4 4 - + 8 8 - + 16 16 - + 24 24 - + 16 (5:6:5) 16 (5:6:5) - + 12 (4:4:4) 12 (4:4:4) - - + + AutoTester AutoTester - + Launch Test Configuration Test-Configuratie starten - + View calc variables Rekenmachine-Variabelen weergeven - + Send files to calc Bestanden naar rekenmachine verzenden - + Emulate physical LCD SPI drawing Fysieke LCD SPI emuleren - + Auto. save and restore debug state Debug-Status automatisch opslaan en laden - + Calculator Receive Rekenmachine Ontvangen - + View Calculator Variables Rekenmachine-Variabelen-weergeven - + Save group Groep opslaan - + Location Locatie - + Calculator Send Rekenmachine Verzenden - + Resend Opnieuw verzenden - + Path Pad - + Screen Recording Schermopname - - + + Record Opnemen - + Optimize Optimaliseren - + Configuration Instellingen - + Pause emulation on focus change Emulatie pauzeren bij focuswisseling - + Emulation Emulatie - + Display / Emulation Speed Scherm- / Emulatiesnelheid - + Actual FPS: Actuele FPS: - - + + Console Console - + dock dock - + console console - + Auto scroll Autoscroll - - + + Calculator state Rekenmachine-Status - - + + Debug state Debug-status - + New CEmu instance Nieuwe CEmu - + Show key history Toetsgeschiedenis weergeven - + Bootable CEmu image Bootable CEmu-image - + Hide menu bar Menubalk verbergen - + Copy screen to clipboard Scherm naar klembord kopiëren - + Ctrl+Shift+C Ctrl+Shift+C - + Reset GUI docks GUI docks herstellen - + Reset CEmu CEmu opnieuw instellen - + .json file: .json-bestand: @@ -839,246 +839,245 @@ Nul - + Clear Equates Equates wissen - + Autoloads any .inc, .map, or .lab found when transferring files Automatisch alle .inc, .map of .lab bestanden laden wanneer bestanden worden overgedragen - + Autoload AutoLaden - + Add Breakpoint Breakpoint toevoegen - + Add Watchpoint Watchpoint toevoegen - - + + Write Schrijven - + Low Laag - + High Hoog - + Add Port Monitor Port monitor toevoegen - + Control Controleren - + Base Base - + Current Huidige - + Browse... Zoeken… - + (Re)Load (Opnieuw) Laden - + Launch test Test uitvoeren - + Reset before test Reset voor test - + [Clear] before test [Clear] voor test - + Get CRC CRC verkrijgen - + Start (address): Start (adres): - + Size: Grootte: - + ... or choose a preset: … of kies een vooraf gedefinieerde: - + (no preset) (geen gedefinieerde) - + All VRAM (LCD set in 16bpp) Gehele VRAM (LCD in 16bpp) - + 1st half of the VRAM (8bpp) 1e helft van VRAM (8bpp) - + 2nd half of the VRAM (8bpp) 2e helft van VRAM (8bpp) - + textShadow textShadow - + cmdShadow cmdShadow - + pixelShadow pixelShadow - + pixelShadow2 pixelShadow2 - + cmdPixelShadow2 cmdPixelShadow2 - + plotSScreen plotSScreen - + saveSScreen saveSScreen - + lcdPalette lcdPalette - + cursorImage cursorImage - + All the RAM Gehele RAM - + Hash (CRC): Hash (CRC): - + Selected Selectie - + Resend selected Selectie opnieuw verzenden - + Save transferred file paths on exit Bestandspaden opslaan - + Update status: Update status: - + sec sec - + Debugging Debuggen - - + + State Status - - + + Window configuration Vensterinstellingen - + Keypad Mapping Keymap-toewijzing - + Change Instance ID Instantie-ID aanpassen - Emulate Pre-Revision I (IM 2) - Emuleer Pre-Revisie I (IM 2) + Emuleer Pre-Revisie I (IM 2) - + TI-Basic Debug @@ -1118,421 +1117,471 @@ - + + Avg. Flash + + + + + Flash Cache Misses + + + + ... - - + + Watchpoints Watchpoints - - + + OS Variables OS-variabelen - + OP Variables OP-variabelen - - + + String String - + Change Calculator Certificate ID Rekenmachine Certificaat-ID aanpassen - - + + OS Stacks OS-stacks - + OP Stack OP-stack - + FP Stack FP-stack - + Save PNG PNG opslaan - + Copy PNG PNG kopiëren - + Allow dock grouped dragging Vensters gegroepeerd slepen toestaan - + Natural Natuurlijk - + Custom Aangepast - + + ASIC revision: + + + + + Auto + + + + + Rev A + + + + + Rev I + + + + + Rev M + + + + + Current: + + + + Some debugging views only make sense when in the context of normal OS operation (may not be the case when some ASM programs are running, for instance) Sommige debugging-vensters zijn alleen zinvol binnen de context van normale OS-uitvoering (behalve wanneer bepaalde ASM-programma's draaien) - + Assume normal OS operation Normale OS-uitvoering aannemen - + + May cause problems if boot code is incompatible. Use only if you know what you're doing. + + + + + Allow any ASIC revision + + + + Show data column Datakolom weergeven - + Use implicit mode Impliciete modus gebruiken - + Opcode / operand space Opcode / operand spatie - + Use uppercase font Hoofdletters gebruiken - + Show address column Adreskolom weergeven - + Bold disassembly symbols Vet gedemonteerde symbolen - + Load Laden - + Save Opslaan - + Edit Bewerken - - + + Reset Reset - - + + Keypad Toetsenbord - + &File &Bestand - + Export Exporteren - + Import Importeren - + Language Taal (Language) - + &Calculator &Rekenmachine - + Extras Extra's - + Screen Scherm - + Open... Openen… - + Ctrl+Shift+O Ctrl+Shift+O - + Exit Sluiten - + Ctrl+Shift+Q Ctrl+Shift+Q - + Rom setup wizard... Rom-installatiescherm… - - + + About CEmu Over CEmu - - + + Take PNG screenshot PNG schermopname maken - + Record animated PNG Animatie-PNG opnemen - + Record animated GIF Animatie-GIF opnemen - + Ctrl+Shift+X Ctrl+Shift+X - + About Qt Over Qt - + Check for updates... Controleren op updates… - + ROM image ROM-bestand - + English English - + Français Français - + Nederlands Nederlands - + Español Español - + Report a bug / give feedback Meld een probleem / Reageer - + Hide status bar Statusbalk verbergen - - + + RAM image RAM-bestand - + 简体中文 - + - + Simplified Chinese - + 简体中文 - + Reload Rom Rom opnieuw laden - + Reset calculator Reset rekenmachine - + Save state Status opslaan - + Restore state Status laden - + Reload ROM ROM opnieuw laden - + Watchdog timeout Watchdog timeout - + Misc. reset Div. reset - - + + Show ASCII ASCII weergeven - + Cannot locate calculator ID in the certificate. This is usually due to an improper ROM dump. Please try another ROM dump using a physical calculator. Kan rekenmachine-ID niet vinden in het certificaat. Dit is waarschijnlijk een gevolg van een slechte ROM-dump. Creëer een nieuwe ROM-dump van een fysieke rekenmachine. - + CEmu Change Certificate ID CEmu Certificaat-ID aanpassen - + Old ID: Oud ID: - + Debugging Info (*.ini) Debugging Info (*.ini) - + Invalid Configuration Ongeldige configuratie - + The debugging configuration is incompatible with CEmu De debug-configuratie is niet compatible met CEmu - + Debugger Import Debugger Importeren - + Debugger Export Debugger Exporteren - + Hit breakpoint Hit breakpoint - + read lezen - + write schrijven - - + + Hit Hit - - + + watchpoint watchpoint - - - + + + Read Gelezen - + Wrote Geschreven - + port poort - + NMI triggered NMI veroorzaakt @@ -1545,7 +1594,7 @@ No Basic Program Executing. - + @@ -1553,66 +1602,66 @@ - + Equate files (*.inc *.lab *.map) Equate-bestanden (*.inc *.lab *.map) - + All Files (*.*) Alle bestanden (*.*) - + Copy Address Adres kopiëren - + Copy Data Data kopiëren - + Goto VAT Memory View Ga naar VAT Geheugen-weergave - + Goto Disasm View Ga naar Disasm-weergave - - + + Toggle Breakpoint Breakpoint aan/uit - - + + Toggle Write Watchpoint Write watchpoint aan/uit - - + + Toggle Read Watchpoint Read watchpoint aan/uit - - + + Toggle Read/Write Watchpoint Read/write watchpoint aan/uit - + Error Fout @@ -1632,43 +1681,43 @@ String niet gevonden. - + Information Informatie - - + + Sync Changes Aanpassingen doorvoeren - + CEmu was not able to write to the standard settings location. Portable mode has been activated. CEmu kon de instellingen niet op de normale locatie opslaan. Portable-modus is ingesteld. - - + + Select saved image to restore from Selecteer opgeslagen image om te laden - - - + + + CEmu images (*.ce);;All files (*.*) CEmu-images (*.ce);;Alle bestanden (*.*) - + Set image to save to Image instellen om op te slaan - + Welcome! CEmu uses a customizable dock-style interface. Drag and drop to move tabs and windows around on the screen, and choose which docks are available in the 'Docks' menu in the topmost bar. Be sure that 'Enable UI edit mode' is selected when laying out your interface. Enjoy! @@ -1681,137 +1730,147 @@ CEmu maakt gebruik van een aanpasbare dock-style interface. Verplaats de tabs en (Let op: afhankelijk van uw versie kan u gegroepeerde tabbladen of een individuele tabblad slepen via de titel of bovenste balk) - + Run/Stop Run/Stop - + Reload Reload - + Add memory view Voeg geheugenweergave toe - + Add memory visualizer Voeg geheugen visualizer toe - + Memory Visualizer Geheugen Visualizer - + Keypress History Toetsgeschiedenis - + Clear History Geschiedenis wissen - + ROM images (*.rom) ROM-bestand (*.rom) - + Set ROM image to save to Stel ROM-image in om op te slaan - + RAM images (*.ram) RAM-bestand (*.ram) - + Set RAM image to save to Stel RAM-image in om op te slaan - + Select RAM image to load Selecteer RAM-bestand om te laden - + RAM images (*.ram);;All files (*.*) RAM-bestanden (*.ram);;Alle bestanden (*.*) - + + Auto (%0) + + + + + Current: %0 (change requires reset) + + + + Actual FPS: Actuele FPS: - + Save Screen Scherm opslaan - + Failed to save screenshot. Schermopname niet kunnen opslaan. - + Saving Recording... Opname opslaan… - + Save Recorded PNG PNG-opname opslaan - + A failure occured during PNG recording. Er is een fout opgetreden bij de PNG-opname. - + Stop Recording... Opname stoppen… - + Saving... Opslaan… - + Saving Animated PNG... Animatie-PNG opslaan… - + Record animated PNG... Animatie-PNG opnemen… - + Copy version Kopieer versie - + Version copied! - + Versie gekopieerd! - + %1<h3>CEmu %2</h3><a href='https://github.com/CE-Programming/CEmu'>On GitHub</a><br><br>Main authors:<br>%3<br>Other contributors include:<br>%4<br>Translations provided by:<br>%5<br>Many thanks to the following projects: %6<br>In-program icons are courtesy of %7.<br><br>CEmu is licensed under the %8, and is not a TI product nor is it affiliated to/endorsed by TI.<br><br> %1<h3>CEmu %2</h3><a href='https://github.com/CE-Programming/CEmu'>On GitHub</a><br><br>Makers:<br>%3<br>Met bijdragen van o.a.:<br>%4<br>Vertalingen aangeleverd door:<br>%5<br>Met dank aan de volgende projecten: %6<br>Programma-ikonen met dank aan %7.<br><br>CEmu is licensed under the %8, and is not a TI product nor is it affiliated to/endorsed by TI.<br><br> - + [CEmu] Dock output redirected to stdout. Use the radio button to enable dock. [CEmu] Dock-output omgeleid naar stdout. Gebruik het keuzerondje om dock in te schakelen. @@ -1820,188 +1879,188 @@ CEmu maakt gebruik van een aanpasbare dock-style interface. Verplaats de tabs en TI-Variabelen (*.8xp *.8xv *.8xl *.8xn *.8xm *.8xy *.8xg *.8xs *.8xd *.8xw *.8xc *.8xl *.8xz *.8xt *.8ca *.8cg *.8ci *.8ek *.b84 *.b83);;Alle Bestanden (*.*) - + Empty Leeg - - + + Can't preview this Kan dit niet weergeven - + Archive Archief - + Select at least two files to group Selecteer tenminste 2 variabelen om te groeperen - - + + Transfer error, see console for information: File: Overdrachtsfout, zie de console voor meer informatie: Bestand: - - + + Transfer completed successfully. Overdracht met succes voltooid. - + See the test config file format and make sure values are correct and referenced files are there. Bestudeer het test configuratie-bestandsformaat en zorg ervoor dat de waarden en de referentiebestanden correct vermeld staan. - + Make sure you have entered a valid start/size pair or preset. Zorg voor een geldige start/grootte-combinatie of gedefinieerde waarde. - + Could not convert those values into numbers Kan deze waarden niet omzetten in getallen - + Could not retrieve this memory chunk Kan dit geheugenblok niet ophalen - + Image does not appear to be from a CE. Do you want to attempt to load it anyway? This may cause instability. Dit image lijkt niet afkomstig van een CE. Wlt u het toch proberen te laden? Dit kan instabiliteit opleveren. - + Set PC PC instellen - + Run Until Run Totdat - + Goto Disassembly View Ga naar Disassembly-weergave - + Enter image path Voer image-pad in - + Emulated Speed: Emulatie-snelheid: - - + + PNG images (*.png) PNG afbeeldingen (*.png) - + Recording... Opnemen… - + Stop Recording Opname stoppen - + Check for updates Controleer op updates - + Resume emulation Emulatie hervatten - + Can't preview this OS variable Kan deze OS-variabele niet weergeven - + Select at least one file to transfer Selecteer tenminste een bestand voor de overdracht - + TI Group (*.8cg);;All Files (*.*) TI-Groep (*.8cg);;Alle Bestanden (*.*) - + No translation available for this language :( Geen vertaling beschikbaar voor deze taal :( - + Saving failed. Please check write permissions in settings directory. Opslaan mislukt. Controleer de toegangsrechten op de instellingenmap. - + TI Variable (*.8xp *.8xv *.8xl *.8xn *.8xm *.8xy *.8xg *.8xs *.8xd *.8xw *.8xc *.8xl *.8xz *.8xt *.8ca *.8cg *.8ci *.8ek *.8eu *.8pu *.b84 *.b83);;All Files (*.*) - + Error. No config loaded Fout. Geen configuratie geladen - + Error. Couldn't follow the test sequence defined in the configuration Fout. Kon de tesopdrachten in de configuratie niet begrijpen - + Error. Unknown one - wat? Fout. Onbekende wie - wat? - + Please choose a json file or type its path. Kies a.u.b. een json-bestand of geef de locatie. - + Couldn't go to where the JSON file is. Kon de locatie van het JSON-bestand niet bereiken. - + Couldn't read JSON file. Kon het JSON-bestand niet lezen. - + Unable to open the file. Niet in staat het bestand te openen. - + Test results Testresultaten - + Out of %2 tests attempted: %4 passed %6 failed @@ -2010,60 +2069,60 @@ Bestand: %6 mislukt - + Launch program Programma uitvoeren - - - - + + + + Goto Memory View Ga naar Geheugen-weergave - + CEmu Change ID CEmu-ID aanpassen - + New ID: Nieuw ID: - + Set saved image to restore from Opgeslagen herstel-image instellen - + Set debugging information path Locatie van debugging-informatie instellen - + Debugging information (*.ini);;All files (*.*) Debugging-informatie (*.ini);;Alle bestanden (*.*) - + Docks Docks - + Enable UI edit mode UI bewerk-modus activeren - + Warning Waarschuwing - + A bootable image can be used to start CEmu with predefined configurations, without the need for any extra setup. The bootable image should be placed in the same directory as the CEmu executable. When CEmu is then started, the boot image will be loaded automatically and then removed for convience. @@ -2072,97 +2131,97 @@ The bootable image should be placed in the same directory as the CEmu executable Plaats hiertoe de bootable image in dezelfde folder als de CEmu executable. Als CEmu wordt gestart, worden de image en instellingen automatisch geladen, waarna de image wordt gewist. - + Save bootable CEmu image Bootable CEmu-image opslaan - + Bootable CEmu images (*.cemu); Bootable CEmu-images (*.cemu); - + Checking updates is disabled for development builds Controle op updates is uitgeschakeld bij ontwikkelingsversies - + No update available Geen update beschikbaar - + You already have the latest CEmu version U hebt al de laatste CEmu versie - + CEmu update CEmu update - + <b>A new version of CEmu is available!</b><br/>You can <a href='%1'>download it here</a>. <b>Een nieuwe versie van CEmu is beschikbaar!</b><br/>U kunt het <a href='%1'>van de website downloaden</a>. - + Update check failed Update-controle mislukt - + <b>An error occurred while checking for CEmu updates.</b><br/>You can however <a href='https://github.com/CE-Programming/CEmu/releases/latest'>go here</a> to check yourself. <b>Er is een fout opgetreden bij de controle op CEmu-updates.</b><br>U kunt ook <a href='https://github.com/CE-Programming/CEmu/releases/latest'>de website bezoeken</a> om het zelf te controleren. - + Keymap Config (*.ini);;All files (*.*) Keymap-instellingen (*.ini);;Alle bestanden (*.*) - + Unable to set custom keymap. Niet in staat aangepaste keymap in te stellen. - + Keep migratable settings Migreerbare instellingen behouden - + This version of CEmu is not compatible with your settings, probably made by an older version. Would you like to erase them to prevent any unexpected behavior? Uw instellingen, waarschijnlijk gemaakt met een oudere versie, komen niet overeen met deze versie van CEmu. Wilt u deze wissen om onverwacht gedrag te voorkomen? - + Window Config (*.ini) Venster-instellingen (*.ini) - + Save window configuration Venster-instellingen opslaan - + Window Config (*.ini);;All files (*.*) Venster-instellingen (*.ini);; Alle bestanden (*.*) - + Keymap Config (*.ini) Keymap-instellingen (*.ini) - + Save keymap configuration Keymap-instellingen opslaan - + Toggle Windows Console Windows Console aan/uit diff --git a/gui/qt/i18n/zh_CN.qm b/gui/qt/i18n/zh_CN.qm index 3f95387ad..61c7b30df 100644 Binary files a/gui/qt/i18n/zh_CN.qm and b/gui/qt/i18n/zh_CN.qm differ diff --git a/gui/qt/i18n/zh_CN.ts b/gui/qt/i18n/zh_CN.ts index beec78cfc..33a7c3492 100644 --- a/gui/qt/i18n/zh_CN.ts +++ b/gui/qt/i18n/zh_CN.ts @@ -67,195 +67,195 @@ MainWindow - - + + Keypad "Virtual keypad" 虚拟键盘 - - + + Variables 变量 - - + + Watchpoints 观察点 - - - + + + Size 大小 - - + + OS Variables 系统变量 - + Change Calculator Certificate ID 更改计算器许可ID - + Resend selected 重新发送选中的内容 - + Save transferred file paths on exit 在退出时保存传输文件路径 - - - + + + Capture 截图 - + Screenshot 截图 - - - - + + + + Settings 设置 - + General 通用 - + Automatically save and restore state 自动保存和恢复状态 - + ROM Image ROM镜像 - + Debug 调试 - + Watchdog timeout Watchdog 超时 - + Misc. reset 重置杂项 - - + + Show ASCII 显示 ASCII - + Cannot locate calculator ID in the certificate. This is usually due to an improper ROM dump. Please try another ROM dump using a physical calculator. "Can't find calculator ID in certificate, this is usually caused by an incorrect ROM dump, please try to get another ROM image from physical calculator." 在许可证书中找不到计算器ID,这通常是由于ROM镜像不正确。请尝试从实体计算器中重新获取ROM镜像。 - + CEmu Change Certificate ID CEmu更改许可ID - + Old ID: 旧ID: - + Debugging Info (*.ini) 调试信息 (*.ini) - + Invalid Configuration 无效的配置 - + The debugging configuration is incompatible with CEmu 调试配置和CEmu不匹配 - + Debugger Import 导入调试信息 - + Debugger Export 导出调试信息 - + Hit breakpoint 命中断点 - + read 读取 - + write 写入 - - + + Hit 命中 - - + + watchpoint 监视点 - - - + + + Read 读取 - + Wrote 写入的 - + port 端口 - + NMI triggered NMI 被触发 @@ -276,61 +276,61 @@ 执行中的 Program: - + Equate files (*.inc *.lab *.map) "ASM header file" 汇编头文件 (*.inc *.lab *.map) - + All Files (*.*) 所有文件 (*.*) - + Copy Address 拷贝地址 - + Copy Data 拷贝数据 - + Goto VAT Memory View 转到VAT内存视图 - + Goto Disasm View 转到反汇编视图 - - + + Toggle Breakpoint 切换断点 - - + + Toggle Write Watchpoint 切换写入监视点 - - + + Toggle Read Watchpoint 切换读取监视点 - - + + Toggle Read/Write Watchpoint 切换读取/写入监视点 @@ -348,55 +348,55 @@ 运行到下一条指令 - - - - - + + + + + Goto 转到 - - - + + + Disassembly 反汇编 - + Preview 预览 - + Always show on top of other windows "Always show on top of other windows" 始终在其他窗口之上显示 - + File Locations 文件路径 - + Saved Image 保存的镜像 - + Skin 外观 - + Scale: 比例: - + Keypad Skin Color 键盘外观颜色 @@ -408,94 +408,94 @@ 运行到下一行反汇编 - + Equates: 汇编定义: - + Load... 加载... - - + + Refresh 刷新 - + Clear 清除 - + Open debugger to view disassembly... "Open debugger and view disassembly" 打开调试器并查看反汇编… - - - - + + + + Value - - + + Port Monitor 端口监视器 - + Battery 电池 - + Charging 充电 - + PWR PWR - + BEPO BEPO - + BGR BGR - + BEBO BEBO - - + + Memory 内存 - + dock dock - + console 控制台 - + Auto scroll 自动滚动 @@ -505,317 +505,316 @@ 清零 - + Add Breakpoint 添加断点 - + Add Watchpoint 添加监视点 - - + + Write 写入 - + Low "lower bound" (of watchpoint range) 下限 - + High "upper bound" (of watchpoint range) 上限 - + Add Port Monitor 添加端口监视器 - + LCD LCD - + Control 控制 - + Base - + Current 当前 - + .json file: .json文件: - + Launch test 启动测试 - + Reset before test 在测试前重置 - + [Clear] before test "automatically press [clear] before test" 在测试之前自动按[clear] - + Get CRC 获取 CRC - + Start (address): 起始地址: - + Size: 大小: - + ... or choose a preset: …或者选择一个预设: - + (no preset) (无预设) - + All VRAM (LCD set in 16bpp) 所有VRAM(LCD 16bpp) - + 1st half of the VRAM (8bpp) 前半部分VRAM (8bpp) - + 2nd half of the VRAM (8bpp) 后半部分VRAM (8bpp) - + textShadow textShadow - + cmdShadow cmdShadow - + pixelShadow pixelShadow - + pixelShadow2 pixelShadow2 - + cmdPixelShadow2 cmdPixelShadow2 - + plotSScreen plotSScreen - + saveSScreen saveSScreen - + lcdPalette lcdPalette - + cursorImage cursorImage - + All the RAM 全部RAM - + Hash (CRC): 哈希值(CRC): - + View calc variables 查看计算器变量 - + Selected 选中的 - + Send files to calc 发送文件到计算器 - + Keypad Mapping 键盘映射 - + Change Instance ID 更改实例ID - + Emulate physical LCD SPI drawing "Emulate physical LCD SPI drawing process" 模拟实体计算器LCD SPI绘制过程 - + Auto. save and restore debug state 自动保存并恢复调试状态 - Emulate Pre-Revision I (IM 2) "Emulate Pre-Revision I hardware (IM 2)" - 模拟Revision I之前的硬件 (IM 2) + 模拟Revision I之前的硬件 (IM 2) - + Record animated PNG 录制PNG动画 - + Ctrl+Shift+X Ctrl+Shift+X - + Reset calculator 重置计算器 - + Save state 保存状态 - + Restore state 恢复状态 - + Reload ROM 重新装载ROM - + Screen 屏幕 - + Reload Rom 重新装载ROM - - - - - + + + + + Address 地址 - + Automatically check for updates 自动检查更新 - + Use portable configuration 使用便携式设置 - + Open debugger on reset or nmi 在重置或NMI时打开调试器 - + Enable software commands 启用软件命令 - - + + % % - - + + Change path 更改路径 - + Saved Debug "saved debug sessions" 保存的调试会话 - - + + Breakpoints 断点 - + Enabled 已启用 - + Registers 寄存器 @@ -825,13 +824,13 @@ 标志位 - - + + Frame skip: 跳帧: - + 1 1 @@ -844,45 +843,45 @@ 停止 - + Add 添加 - + Port 端口 - - - + + + Data 数据 - + Freeze 冻结 - + Save selected 保存所选内容 - + Throttle: "(emulation) speed limit" 限速: - + Text size: 文字大小: - + Key bindings 键盘绑定 @@ -903,83 +902,83 @@ 周期 - + VAT View VAT视图 - - + + Sync changes 同步更改 - + Rom setup wizard... ROM 设置向导… - - + + Take PNG screenshot 以PNG格式截图 - + Record animated GIF 录制GIF动画 - + Check for updates... 检查更新… - + About Qt 关于 Qt - + Interrupts 中断 - + Display 显示 - + Brightness 亮度 - - + + Console 控制台 - - - - - + + + + + Name 名称 - + Type 类型 - - - - - + + + + + Remove 移除 @@ -994,73 +993,73 @@ PC/SP - + Stack - + Flash 闪存 - - - - + + + + Search 搜索 - - + + Sync Changes 同步更改 - + RAM RAM - + Memory View 内存视图 - + &File &文件 - + Open... 打开... - + Exit 退出 - + Setup wizard 设置向导 - + Browse... 浏览... - + Debug Control 调试控制 - + CPU Status CPU状态 @@ -1071,96 +1070,96 @@ 调试事件触发器 - - + + ADL ADL - + Clear Equates "Clear symbol definitions" 清除符号定义 - + Autoloads any .inc, .map, or .lab found when transferring files 传输文件时自动加载任何找到的.inc, .map或者.lab文件 - + Autoload 自动加载 - + ... ... - - + + Timers 定时器 - + General Purpose Timers 通用计时器 - + Timer 3 计时器 3 - + Reload Value "reset value" 重置值 - + Timer 2 计时器 2 - + Timer 1 计时器 1 - + Real Time Clock 实时时钟(RTC) - + Minutes 分钟 - + Days - + Hours 小时 - + Seconds - + OP Number OP编号 - + VAT Address VAT地址 @@ -1170,172 +1169,172 @@ MHz - + Data Type 数据类型 - + OP Variables OP变量 - - + + String 字符 - - + + OS Stacks 系统栈 - + OP Stack OP栈 - + FP Stack 浮点栈 - - + + Miscellaneous 杂项 - + BPP BPP - + 2 2 - + 4 4 - + 8 8 - + 16 16 - + 24 24 - + 16 (5:6:5) 16 (5:6:5) - + 12 (4:4:4) 12 (4:4:4) - - + + AutoTester 自动测试器 - + Launch Test Configuration 运行测试配置 - + (Re)Load (重新)加载 - + Calculator Receive "Receive from calculator" 从计算器接收 - + View Calculator Variables 查看计算器变量 - + Save group "save as group" 保存为组 - + Location 位置 - + Calculator Send "Send to calculator" 发送至计算器 - + Resend 重新发送 - + Path 路径 - + Save PNG 保存PNG - + Copy PNG 拷贝PNG - + Screen Recording 屏幕录制 - - + + Record 录制 - + Optimize 优化 - + Configuration 配置 - + TI-Basic Debug TI-Basic 调试 @@ -1375,282 +1374,332 @@ 临时解析器 - + Pause emulation on focus change 在CEmu不在最上方时暂停模拟 - + Allow dock grouped dragging 允许项目组拖移 - + Natural 自然的 - + Custom 自定义 - + Emulation 模拟 - + Display / Emulation Speed 显示/模拟速度 - + Actual FPS: 实际帧率: - + Update status: 更新状态: - + + Avg. Flash + + + + + Flash Cache Misses + + + + + ASIC revision: + + + + sec - + + Auto + + + + + Rev A + + + + + Rev I + + + + + Rev M + + + + + Current: + + + + Debugging 调试 - + Some debugging views only make sense when in the context of normal OS operation (may not be the case when some ASM programs are running, for instance) "Some of the debugging views only make sense when TIOS is running normally (when some assembly programs are running, it may not make sense, for instance)" 一些调试器视图仅在TIOS正常运行的情况下才有意义(例如在一些汇编语言程序运行时就可能没有意义) - + Assume normal OS operation Asumir el funcionamiento normal del OS 假设操作系统工作正常 - + + May cause problems if boot code is incompatible. Use only if you know what you're doing. + + + + + Allow any ASIC revision + + + + Show data column 显示数据栏 - + Use implicit mode "omit implicit operands" 省略隐含的操作数 - + Opcode / operand space 操作码/操作数空间 - + Use uppercase font 使用大写字母 - + Show address column 显示地址栏 - + Bold disassembly symbols 为汇编符号使用粗体 - - + + State 状态 - + Load 加载 - + Save 保存 - + Edit 编辑 - - + + Reset 重设 - + Export 导出 - + Import 导入 - + Language 语言 - + &Calculator &计算器 - + Extras 其他 - + Ctrl+Shift+O Ctrl+Shift+O - + Ctrl+Shift+Q Ctrl+Shift+Q - - + + About CEmu 关于 CEmu - + ROM image ROM镜像 - - + + Calculator state 计算器状态 - - + + Debug state 调试状态 - + New CEmu instance 新建CEmu实例 - + Show key history 显示按键历史记录 - + Bootable CEmu image 可启动的CEmu镜像 - + Hide menu bar 隐藏菜单栏 - + Copy screen to clipboard 将屏幕图像拷贝到剪贴板 - + Ctrl+Shift+C Ctrl+Shift+C - + Reset GUI docks 重置界面项目 - + Reset CEmu 重置CEmu - + English English - + Français Français - + Nederlands Nederlands - + Español Español - - + + Window configuration 窗口设置 - + Report a bug / give feedback 报告漏洞/反馈意见 - + Hide status bar 隐藏状态栏 - - + + RAM image RAM镜像 - + 简体中文 简体中文 - + Simplified Chinese Simplified Chinese - + Docks 项目 - + Welcome! CEmu uses a customizable dock-style interface. Drag and drop to move tabs and windows around on the screen, and choose which docks are available in the 'Docks' menu in the topmost bar. Be sure that 'Enable UI edit mode' is selected when laying out your interface. Enjoy! @@ -1663,156 +1712,156 @@ CEmu采用可自定义的项目界面,您可以在屏幕上拖拽标签页和 (注意:取决于您的版本,您可以分别从标题或标签栏中拖动一组或单个标签) - - + + PNG images (*.png) PNG图像 (*.png) - - + + Select saved image to restore from 选择用于恢复的已保存的镜像 - - - + + + CEmu images (*.ce);;All files (*.*) CEmu镜像 (*.ce);;所有文件 (*.*) - + Information 信息 - + Set image to save to 设置镜像保存路径 - + Emulated Speed: 模拟速度: - + Checking updates is disabled for development builds 在测试版CEmu中不能检查更新 - + You already have the latest CEmu version 您已经安装了最新的CEmu版本 - + <b>An error occurred while checking for CEmu updates.</b><br/>You can however <a href='https://github.com/CE-Programming/CEmu/releases/latest'>go here</a> to check yourself. <b>CEmu在检查更新时出现了错误.</b><br/>您可以到<a href='https://github.com/CE-Programming/CEmu/releases/latest'></a>来自行查看。 - + Recording... 录制中... - + Stop Recording 停止录制 - + Can't preview this OS variable 无法预览该系统变量 - + Select at least one file to transfer 选择至少一个文件以传输 - + Actual FPS: 实际帧率: - + Run/Stop 运行/停止 - + Reload 重新加载 - + Keypress History 键盘历史记录 - + Clear History 清除历史记录 - + Save Screen 保存屏幕 - + Failed to save screenshot. 无法保存截屏。 - + Saving Recording... 保存录制文件... - + Save Recorded PNG 保存录制的PNG - + A failure occured during PNG recording. PNG录制时出现了错误。 - + Stop Recording... 停止录制… - + Saving... 保存中… - + Saving Animated PNG... 保存PNG动画... - + Record animated PNG... 录制PNG动画... - + Copy version 拷贝版本 - + %1<h3>CEmu %2</h3><a href='https://github.com/CE-Programming/CEmu'>On GitHub</a><br><br>Main authors:<br>%3<br>Other contributors include:<br>%4<br>Translations provided by:<br>%5<br>Many thanks to the following projects: %6<br>In-program icons are courtesy of %7.<br><br>CEmu is licensed under the %8, and is not a TI product nor is it affiliated to/endorsed by TI.<br><br> %1<h3>CEmu %2</h3><a href='https://github.com/CE-Programming/CEmu'>GitHub 主页</a><br><br>开发者:<br>%3<br>其他贡献者:<br>%4<br>翻译:<br>%5<br>感谢以下项目: %6<br>应用内图标由 %7 提供<br><br>CEmu遵循 %8 协议,且不是一个德州仪器的产品,同时也不隶属于德州仪器<br><br> - + [CEmu] Dock output redirected to stdout. Use the radio button to enable dock. [CEmu] 项目输出重定向到标准输出,使用单选按钮启用项目。 @@ -1821,206 +1870,216 @@ CEmu采用可自定义的项目界面,您可以在屏幕上拖拽标签页和 TI 文件 (*.8xp *.8xv *.8xl *.8xn *.8xm *.8xy *.8xg *.8xs *.8xd *.8xw *.8xc *.8xl *.8xz *.8xt *.8ca *.8cg *.8ci *.8ek *.b84 *.b83);;Todos Archivos (*.*) - + Empty - - + + Can't preview this 无法预览 - + Archive 存档 - + Select at least two files to group 选择至少两个文件以组合 - + TI Group (*.8cg);;All Files (*.*) TI Group (*.8cg);;所有文件 (*.*) - - + + Transfer error, see console for information: File: 传输失败,请打开控制台查看更多信息: 文件: - - + + Transfer completed successfully. 传输已完成。 - + Error. No config loaded 错误。无加载配置 - + Error. Couldn't follow the test sequence defined in the configuration 错误。无法跟随在配置中的测试序列 - + Error. Unknown one - wat? 未知错误—啥? - + See the test config file format and make sure values are correct and referenced files are there. "Please see the test config file format and check if all values are correct and referenced files exist." 请参见测试配置文件的格式并确认数值都是正确的且被引用的文件均存在。 - + Make sure you have entered a valid start/size pair or preset. "Make sure the entered `start`/`size` value or preset is valid" 确保输入的`开始`/`大小`值或预设有效。 - + Could not convert those values into numbers 无法将值转换为数字 - + Could not retrieve this memory chunk 无法检索该内存块 - + Image does not appear to be from a CE. Do you want to attempt to load it anyway? This may cause instability. 该图像似乎不是来自一台CE,您确定要加载它吗?这可能导致不稳定。 - + Set PC 设置PC - + Run Until 运行直到 - + Goto Disassembly View 查看反汇编视图 - + Enter image path 输入图像路径 - + Please choose a json file or type its path. 请选择一个json文件或输入其路径。 - + No translation available for this language :( 该语言目前没有翻译:( - + Add memory view 添加内存视图 - + Add memory visualizer 添加内存可视化器 - + Memory Visualizer 内存可视化器 - + CEmu was not able to write to the standard settings location. Portable mode has been activated. CEmu无法写入到标准设置位置 便携式模式已启用。 - + ROM images (*.rom) ROM镜像 (*.rom) - + Set ROM image to save to 设置ROM镜像保存路径 - + RAM images (*.ram) RAM镜像 (*.ram) - + Set RAM image to save to 设置内存(RAM)镜像保存路径 - + Select RAM image to load 选择加载的内存(RAM)镜像 - + RAM images (*.ram);;All files (*.*) RAM镜像 (*.ram);;所有文件 (*.*) - + Saving failed. Please check write permissions in settings directory. 保存失败,请检查写入权限设置. - + + Auto (%0) + + + + + Current: %0 (change requires reset) + + + + Version copied! 已拷贝版本信息! - + TI Variable (*.8xp *.8xv *.8xl *.8xn *.8xm *.8xy *.8xg *.8xs *.8xd *.8xw *.8xc *.8xl *.8xz *.8xt *.8ca *.8cg *.8ci *.8ek *.8eu *.8pu *.b84 *.b83);;All Files (*.*) TI Variable (*.8xp *.8xv *.8xl *.8xn *.8xm *.8xy *.8xg *.8xs *.8xd *.8xw *.8xc *.8xl *.8xz *.8xt *.8ca *.8cg *.8ci *.8ek *.8eu *.8pu *.b84 *.b83);;All Files (*.*) - + Couldn't go to where the JSON file is. 无法进入JSON文件位置。 - + Couldn't read JSON file. 无法读取JSON文件。 - + Unable to open the file. 无法打开文件。 - + Test results 测试结果 - + Out of %2 tests attempted: %4 passed %6 failed @@ -2029,55 +2088,55 @@ Portable mode has been activated. %6 失败 - + Launch program 启动程序 - - - - + + + + Goto Memory View 显示内存视图 - + CEmu Change ID 更改CEmu ID - + New ID: 新ID: - + Set saved image to restore from 让保存的镜像恢复的位置 - + Set debugging information path 设置调试信息路径 - + Debugging information (*.ini);;All files (*.*) 调试信息 (* .ini);;所有文件 (*. *) - + Enable UI edit mode 启用UI编辑模式 - + Warning 警告 - + A bootable image can be used to start CEmu with predefined configurations, without the need for any extra setup. The bootable image should be placed in the same directory as the CEmu executable. When CEmu is then started, the boot image will be loaded automatically and then removed for convience. @@ -2086,88 +2145,88 @@ The bootable image should be placed in the same directory as the CEmu executable 可启动镜像应放置在与 CEmu 可执行文件相同的目录中。当 CEmu 启动时将自动加载并移除启动镜像以方便使用。 - + Save bootable CEmu image 保存可启动的CEmu镜像 - + Bootable CEmu images (*.cemu); 可启动的CEmu镜像 (* .cemu); - + No update available 无可用更新 - + CEmu update CEmu更新 - + <b>A new version of CEmu is available!</b><br/>You can <a href='%1'>download it here</a>. <b>Cemu有新版本可用!</b><br/>您可以在 <a href='%1'>下载</a>. - + Update check failed 检查更新失败 - + Keymap Config (*.ini);;All files (*.*) 键盘映射配置 (*.ini);;所有文件 (*.*) - + Unable to set custom keymap. 无法自定义键盘映射。 - + Keep migratable settings 保留可迁移设置 - + This version of CEmu is not compatible with your settings, probably made by an older version. Would you like to erase them to prevent any unexpected behavior? "This version of CEmu does not match with your settings, it's probably from an older version. Would you like to delete them to prevent unexpected things to happen?" 该CEmu版本和您的设置不匹配,其可能来源于一个历史版本。您愿意删除它们以防止意外情况发生吗? - + Window Config (*.ini) 窗口设置 (*.ini) - + Save window configuration 保存窗口设置 - + Window Config (*.ini);;All files (*.*) 窗口设置 (*.ini);;所有文件 (*.*) - + Keymap Config (*.ini) 键盘映射配置 (*.ini) - + Save keymap configuration 保存键盘映射 - + Check for updates 检查更新 - + Resume emulation 恢复模拟 @@ -2188,12 +2247,12 @@ The bootable image should be placed in the same directory as the CEmu executable 没有找到字符串。 - + Error 错误 - + Toggle Windows Console 切换窗口控制台 diff --git a/gui/qt/mainwindow.cpp b/gui/qt/mainwindow.cpp index 6cba1678e..794b34ab9 100644 --- a/gui/qt/mainwindow.cpp +++ b/gui/qt/mainwindow.cpp @@ -140,6 +140,7 @@ MainWindow::MainWindow(CEmuOpts &cliOpts, QWidget *p) : QMainWindow(p), ui(new U // emulator -> gui (Should be queued) connect(&emu, &EmuThread::consoleStr, this, &MainWindow::consoleStr, Qt::UniqueConnection); connect(&emu, &EmuThread::consoleClear, this, &MainWindow::consoleClear, Qt::QueuedConnection); + connect(&emu, &EmuThread::sendAsicRevInfo, this, &MainWindow::showAsicRevInfo, Qt::QueuedConnection); connect(&emu, &EmuThread::sendSpeed, this, &MainWindow::showEmuSpeed, Qt::QueuedConnection); connect(&emu, &EmuThread::debugDisable, this, &MainWindow::debugDisable, Qt::QueuedConnection); connect(&emu, &EmuThread::debugCommand, this, &MainWindow::debugCommand, Qt::QueuedConnection); @@ -365,7 +366,9 @@ MainWindow::MainWindow(CEmuOpts &cliOpts, QWidget *p) : QMainWindow(p), ui(new U connect(ui->scaleLCD, static_cast(&QSpinBox::valueChanged), this, &MainWindow::setLcdScale); connect(ui->guiSkip, static_cast(&QSpinBox::valueChanged), this, &MainWindow::setGuiSkip); connect(ui->checkSkin, &QCheckBox::stateChanged, this, &MainWindow::setSkinToggle); + connect(ui->comboBoxAsicRev, static_cast(&QComboBox::currentIndexChanged), this, &MainWindow::setAsicRevision); connect(ui->checkSpi, &QCheckBox::toggled, this, &MainWindow::setLcdSpi); + connect(ui->checkPythonEdition, &QCheckBox::stateChanged, this, &MainWindow::setPythonEdition); connect(ui->checkAlwaysOnTop, &QCheckBox::stateChanged, this, &MainWindow::setTop); connect(ui->emulationSpeed, &QSlider::valueChanged, this, &MainWindow::setEmuSpeed); connect(ui->emulationSpeedSpin, static_cast(&QSpinBox::valueChanged), this, &MainWindow::setEmuSpeed); @@ -378,7 +381,7 @@ MainWindow::MainWindow(CEmuOpts &cliOpts, QWidget *p) : QMainWindow(p), ui(new U connect(ui->buttonChangeSavedImagePath, &QPushButton::clicked, this, &MainWindow::setImagePath); connect(ui->buttonChangeSavedDebugPath, &QPushButton::clicked, this, &MainWindow::setDebugPath); connect(ui->checkFocus, &QCheckBox::stateChanged, this, &MainWindow::setFocusSetting); - connect(ui->checkPreI, &QCheckBox::stateChanged, this, &MainWindow::setPreRevisionI); + connect(ui->checkAllowAnyRev, &QCheckBox::stateChanged, this, &MainWindow::setAllowAnyRev); connect(ui->checkNormOs, &QCheckBox::stateChanged, this, &MainWindow::setNormalOs); connect(ui->flashBytes, static_cast(&QSpinBox::valueChanged), ui->flashEdit, &HexWidget::setBytesPerLine); connect(ui->ramBytes, static_cast(&QSpinBox::valueChanged), ui->ramEdit, &HexWidget::setBytesPerLine); @@ -580,7 +583,8 @@ MainWindow::MainWindow(CEmuOpts &cliOpts, QWidget *p) : QMainWindow(p), ui(new U setDebugResetTrigger(m_config->value(SETTING_DEBUGGER_RESET_OPENS, false).toBool()); setDebugIgnoreBreakpoints(m_config->value(SETTING_DEBUGGER_BREAK_IGNORE, false).toBool()); setDebugSoftCommands(m_config->value(SETTING_DEBUGGER_ENABLE_SOFT, true).toBool()); - setPreRevisionI(m_config->value(SETTING_DEBUGGER_PRE_I, false).toBool()); + setAllowAnyRev(m_config->value(SETTING_DEBUGGER_ALLOW_ANY_REV, false).toBool()); + setPythonEdition(qvariant_cast(m_config->value(SETTING_PYTHON_EDITION, Qt::PartiallyChecked))); setNormalOs(m_config->value(SETTING_DEBUGGER_NORM_OS, true).toBool()); setLcdDma(m_config->value(SETTING_DEBUGGER_IGNORE_DMA, true).toBool()); setFocusSetting(m_config->value(SETTING_PAUSE_FOCUS, false).toBool()); @@ -1503,6 +1507,26 @@ void MainWindow::consoleStr() { } } +void MainWindow::showAsicRevInfo(const QList& supportedRevs, int loadedRev, int defaultRev, bool python) { + QString defaultRevStr = ui->comboBoxAsicRev->itemText(defaultRev); + ui->comboBoxAsicRev->setItemText(0, tr("Auto (%0)").arg(defaultRevStr)); + + QString loadedRevStr = ui->comboBoxAsicRev->itemText(loadedRev); + if (python) { + loadedRevStr += " Python"; + } + ui->currAsicRev->setText(tr("Current: %0 (change requires reset)").arg(loadedRevStr)); + + m_supportedRevs = supportedRevs; + setAsicValidRevisions(); + + if (loadedRev != defaultRev || ui->comboBoxAsicRev->currentIndex() != 0) { + ui->comboBoxAsicRev->setCurrentIndex(loadedRev); + } + + setCalcSkinTopFromType(python); +} + void MainWindow::showEmuSpeed(int speed) { if (m_timerEmuTriggered) { m_speedLabel.setText(QStringLiteral(" ") + tr("Emulated Speed: ") + QString::number(speed) + QStringLiteral("%")); @@ -2259,7 +2283,6 @@ void MainWindow::emuCheck(emu_state_t state, emu_data_t type) { if (state == EMU_STATE_VALID) { ui->lcd->setMain(); - setCalcSkinTopFromType(); setKeypadColor(m_config->value(SETTING_KEYPAD_COLOR, get_device_type() ? KEYPAD_WHITE : KEYPAD_BLACK).toUInt()); for (const auto &dock : findChildren()) { if (dock->windowTitle() == TXT_VISUALIZER_DOCK) { diff --git a/gui/qt/mainwindow.h b/gui/qt/mainwindow.h index 0bcb9c7f0..1cb5ec577 100644 --- a/gui/qt/mainwindow.h +++ b/gui/qt/mainwindow.h @@ -400,7 +400,7 @@ class MainWindow : public QMainWindow { void setKeymap(const QString &value); void setKeypadColor(unsigned int color); void setKeypadHolding(bool enabled); - void setCalcSkinTopFromType(); + void setCalcSkinTopFromType(bool python); // settings void setRom(const QString &path); @@ -418,7 +418,10 @@ class MainWindow : public QMainWindow { void setMenuBarState(bool state); void setStatusBarState(bool state); void setUIBoundaries(bool state); - void setPreRevisionI(bool state); + void setAsicValidRevisions(); + void setAsicRevision(int index); + void setAllowAnyRev(bool state); + void setPythonEdition(int state); void setNormalOs(bool state); void setRecentSave(bool state); void setPortable(bool state); @@ -432,6 +435,7 @@ class MainWindow : public QMainWindow { void setUIEditMode(bool mode); void setFullscreen(int value); void iconsLoad(); + void showAsicRevInfo(const QList& supportedRevs, int loadedRev, int defaultRev, bool python); // speed settings void setEmuSpeed(int value); @@ -668,6 +672,8 @@ class MainWindow : public QMainWindow { bool m_optimizeRecording; bool m_portableActivated = false; bool m_ignoreDmaCycles; + QList m_supportedRevs; + bool m_allowAnyRev; bool m_normalOs; bool m_setup = false; int m_fullscreen = FULLSCREEN_NONE; @@ -721,8 +727,9 @@ class MainWindow : public QMainWindow { static const QString SETTING_DEBUGGER_BREAK_IGNORE; static const QString SETTING_DEBUGGER_IGNORE_DMA; static const QString SETTING_DEBUGGER_AUTO_EQUATES; - static const QString SETTING_DEBUGGER_PRE_I; + static const QString SETTING_DEBUGGER_ALLOW_ANY_REV; static const QString SETTING_DEBUGGER_NORM_OS; + static const QString SETTING_PYTHON_EDITION; static const QString SETTING_SCREEN_FRAMESKIP; static const QString SETTING_SCREEN_SCALE; static const QString SETTING_SCREEN_SKIN; diff --git a/gui/qt/mainwindow.ui b/gui/qt/mainwindow.ui index 06162fa93..36011c7a7 100644 --- a/gui/qt/mainwindow.ui +++ b/gui/qt/mainwindow.ui @@ -3091,6 +3091,103 @@ + + + + + 0 + 0 + + + + + 65 + 20 + + + + + 75 + 20 + + + + + MS Shell Dlg 2 + 9 + + + + + + + + + + 32767 + + + true + + + true + + + + + + + Avg. Flash + + + + + + + + 0 + 0 + + + + + 65 + 20 + + + + + 75 + 20 + + + + + MS Shell Dlg 2 + 9 + + + + + + + + + + 32767 + + + true + + + + + + + Flash Cache Misses + + + @@ -9227,53 +9324,10 @@ QPushButton:pressed { - - - - - 0 - 0 - - - - Qt::ClickFocus - - - - - - 0 - - - 30 - - - 0 - - - 10 - - - - - - - - 0 - 0 - - - - % - - - 500 - - - 100 - - - 10 + + + + Update status: @@ -9290,16 +9344,6 @@ QPushButton:pressed { - - - - Actual FPS: - - - Qt::PlainText - - - @@ -9349,10 +9393,32 @@ QPushButton:pressed { - - + + + + + 0 + 0 + + + + % + + + 500 + + + 100 + + + 10 + + + + + - Update status: + ASIC revision: @@ -9378,8 +9444,87 @@ QPushButton:pressed { + + + + + 0 + 0 + + + + Qt::ClickFocus + + + + + + 0 + + + 30 + + + 0 + + + 10 + + + + + + + + Auto + + + + + Rev A + + + + + Rev I + + + + + Rev M + + + + + + + + Actual FPS: + + + Qt::PlainText + + + + + + + Current: + + + + + + + Emulate Python Edition + + + true + + + @@ -9482,13 +9627,6 @@ QPushButton:pressed { - - - - Emulate Pre-Revision I (IM 2) - - - @@ -9499,16 +9637,6 @@ QPushButton:pressed { - - - - Some debugging views only make sense when in the context of normal OS operation (may not be the case when some ASM programs are running, for instance) - - - Assume normal OS operation - - - @@ -9529,6 +9657,26 @@ QPushButton:pressed { + + + + Some debugging views only make sense when in the context of normal OS operation (may not be the case when some ASM programs are running, for instance) + + + Assume normal OS operation + + + + + + + May cause problems if boot code is incompatible. Use only if you know what you're doing. + + + Allow any ASIC revision + + + diff --git a/gui/qt/resources.qrc b/gui/qt/resources.qrc index c0883261b..1f5961245 100644 --- a/gui/qt/resources.qrc +++ b/gui/qt/resources.qrc @@ -2,6 +2,8 @@ resources/skin/ti84pce.png resources/skin/ti83pce.png + resources/skin/ti83pce_ep.png + resources/skin/ti84pce_py.png resources/setup/home.png diff --git a/gui/qt/resources/skin/ti83pce_ep.png b/gui/qt/resources/skin/ti83pce_ep.png new file mode 100644 index 000000000..a05a46ccb Binary files /dev/null and b/gui/qt/resources/skin/ti83pce_ep.png differ diff --git a/gui/qt/resources/skin/ti84pce_py.png b/gui/qt/resources/skin/ti84pce_py.png new file mode 100644 index 000000000..cd2e8fb9a Binary files /dev/null and b/gui/qt/resources/skin/ti84pce_py.png differ diff --git a/gui/qt/settings.cpp b/gui/qt/settings.cpp index c2a3df4b9..ae3c1a6c2 100644 --- a/gui/qt/settings.cpp +++ b/gui/qt/settings.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -47,8 +48,9 @@ const QString MainWindow::SETTING_DEBUGGER_RAM_ASCII = QStringLiteral("De const QString MainWindow::SETTING_DEBUGGER_BREAK_IGNORE = QStringLiteral("Debugger/ignore_breakpoints"); const QString MainWindow::SETTING_DEBUGGER_AUTO_EQUATES = QStringLiteral("Debugger/auto_equates"); const QString MainWindow::SETTING_DEBUGGER_IGNORE_DMA = QStringLiteral("Debugger/ignore_dma"); +const QString MainWindow::SETTING_DEBUGGER_ALLOW_ANY_REV = QStringLiteral("Debugger/allow_any_rev"); const QString MainWindow::SETTING_DEBUGGER_NORM_OS = QStringLiteral("Debugger/norm_os"); -const QString MainWindow::SETTING_DEBUGGER_PRE_I = QStringLiteral("Debugger/pre_i"); +const QString MainWindow::SETTING_PYTHON_EDITION = QStringLiteral("python_edition"); const QString MainWindow::SETTING_SCREEN_FRAMESKIP = QStringLiteral("Screen/frameskip"); const QString MainWindow::SETTING_SCREEN_SCALE = QStringLiteral("Screen/scale"); const QString MainWindow::SETTING_SCREEN_SKIN = QStringLiteral("Screen/skin"); @@ -455,6 +457,8 @@ void MainWindow::setFont(int fontSize) { ui->lcdbaseView->setFont(monospace); ui->lcdcurrView->setFont(monospace); ui->cycleView->setFont(monospace); + ui->flashAvgView->setFont(monospace); + ui->flashMissesView->setFont(monospace); } void MainWindow::setKeypadColor(unsigned int color) { @@ -467,10 +471,14 @@ void MainWindow::setKeypadHolding(bool enabled) { m_config->setValue(SETTING_KEYPAD_HOLDING, enabled); } -void MainWindow::setCalcSkinTopFromType() { - bool is83 = get_device_type() == TI83PCE; - ui->calcSkinTop->setStyleSheet(is83 ? QStringLiteral(".QFrame { border-image: url(:/skin/resources/skin/ti83pce.png) 0 0 0 0 stretch stretch; }") - : QStringLiteral(".QFrame { border-image: url(:/skin/resources/skin/ti84pce.png) 0 0 0 0 stretch stretch; }")); +void MainWindow::setCalcSkinTopFromType(bool python) { + QString fileName; + if (get_device_type() == TI83PCE) { + fileName = python ? "ti83pce_ep.png" : "ti83pce.png"; + } else { + fileName = python ? "ti84pce_py.png" : "ti84pce.png"; + } + ui->calcSkinTop->setStyleSheet(".QFrame { border-image: url(:/skin/resources/skin/" + fileName + ") 0 0 0 0 stretch stretch; }"); } void MainWindow::setImagePath() { @@ -930,10 +938,50 @@ void MainWindow::setRecentSave(bool state) { m_config->setValue(SETTING_RECENT_SAVE, state); } -void MainWindow::setPreRevisionI(bool state) { - ui->checkPreI->setChecked(state); - m_config->setValue(SETTING_DEBUGGER_PRE_I, state); - cpu.preI = state; +void MainWindow::setAsicValidRevisions() { + QStandardItemModel* itemModel = qobject_cast(ui->comboBoxAsicRev->model()); + assert(itemModel); + + bool allowAnyRev = ui->checkAllowAnyRev->isChecked(); + int row = 1; + for (int rev : m_supportedRevs) { + while (row < rev) { + itemModel->item(row++)->setEnabled(allowAnyRev); + } + itemModel->item(row++)->setEnabled(true); + } + while (row < itemModel->rowCount()) { + itemModel->item(row++)->setEnabled(allowAnyRev); + } + + if (!itemModel->item(ui->comboBoxAsicRev->currentIndex())->isEnabled()) { + ui->comboBoxAsicRev->setCurrentIndex(0); + } +} + +void MainWindow::setAsicRevision(int index) { + emu.setAsicRev(index); +} + +void MainWindow::setAllowAnyRev(bool state) { + ui->checkAllowAnyRev->setChecked(state); + m_config->setValue(SETTING_DEBUGGER_ALLOW_ANY_REV, state); + emu.setAllowAnyRev(state); + setAsicValidRevisions(); + ui->checkPythonEdition->setEnabled(state); + if (!state) { + ui->checkPythonEdition->setCheckState(Qt::PartiallyChecked); + } +} + +void MainWindow::setPythonEdition(int state) { + Qt::CheckState forcePython = static_cast(state); + if (!ui->checkAllowAnyRev->isChecked()) { + forcePython = Qt::PartiallyChecked; + } + ui->checkPythonEdition->setCheckState(forcePython); + m_config->setValue(SETTING_PYTHON_EDITION, forcePython); + emu.setForcePython(forcePython); } void MainWindow::setNormalOs(bool state) { diff --git a/gui/sdl/main.c b/gui/sdl/main.c index 4b65e2034..bf5fafd80 100644 --- a/gui/sdl/main.c +++ b/gui/sdl/main.c @@ -22,10 +22,21 @@ typedef struct { } cemu_sdl_t; static const cemu_sdl_key_t *keymap = cemu_keymap; +static asic_rev_t asic_rev = ASIC_REV_AUTO; +static int8_t python_rev = -1; void gui_console_clear() {} void gui_console_printf(const char *format, ...) { (void)format; } void gui_console_err_printf(const char *format, ...) { (void)format; } +asic_rev_t gui_handle_reset(const boot_ver_t* boot_ver, asic_rev_t loaded_rev, asic_rev_t default_rev, bool* python) { + (void)boot_ver; + (void)loaded_rev; + (void)default_rev; + if (python_rev >= 0) { + *python = python_rev; + } + return asic; +} void sdl_update_lcd(void *data) { sdl_t *sdl = (sdl_t*)data; @@ -206,10 +217,13 @@ int main(int argc, char **argv) { {"limit", required_argument, 0, 'l' }, {"spi", no_argument, 0, 's' }, {"keymap", required_argument, 0, 'k' }, + {"asic", required_argument, 0, 'a' }, + {"python", no_argument, 0, 'p' }, + {"nonpython", no_argument, 0, 'P' }, {} }; - c = getopt_long(argc, argv, "fr:i:l:sk:", long_options, &option_index); + c = getopt_long(argc, argv, "fr:i:l:sk:a:", long_options, &option_index); if (c == -1) { break; } @@ -250,6 +264,29 @@ int main(int argc, char **argv) { } break; + case 'a': + if (strlen(optarg) == 1) { + char rev = toupper(optarg[0]); + if (rev < 'I') { + asic_rev = ASIC_REV_A; + } else if (rev < 'M') { + asic_rev = ASIC_REV_I; + } else { + asic_rev = ASIC_REV_M; + } + } + break; + + case 'p': + fprintf(stdout, "python: yes\n"); + python_rev = true; + break; + + case 'P': + fprintf(stdout, "python: no\n"); + python_rev = false; + break; + default: break; } diff --git a/tests/autotester/autotester_cli.cpp b/tests/autotester/autotester_cli.cpp index 0b2cde41b..5a7c23758 100644 --- a/tests/autotester/autotester_cli.cpp +++ b/tests/autotester/autotester_cli.cpp @@ -23,11 +23,21 @@ #include "autotester.h" /* As expected by the core */ -extern "C" +namespace cemucore { - void gui_console_clear() {} - void gui_console_printf(const char *format, ...) { (void)format; } - void gui_console_err_printf(const char *format, ...) { (void)format; } + extern "C" + { + void gui_console_clear() {} + void gui_console_printf(const char* format, ...) { (void)format; } + void gui_console_err_printf(const char* format, ...) { (void)format; } + asic_rev_t gui_handle_reset(const boot_ver_t* boot_ver, asic_rev_t loaded_rev, asic_rev_t default_rev, bool* python) { + (void)boot_ver; + (void)loaded_rev; + (void)default_rev; + (void)python; + return ASIC_REV_AUTO; + } + } } int main(int argc, char* argv[])