diff --git a/README.md b/README.md index ca8e07a..7507b73 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Build status](https://github.com/indigodarkwolf/box16/actions/workflows/build.yml/badge.svg)](https://github.com/indigodarkwolf/box16/actions/workflows/build.yml)
[![Release](https://img.shields.io/github/v/release/indigodarkwolf/box16)](https://github.com/indigodarkwolf/box16/releases) -This is an emulator for the Commander X16 computer system. Unlike [the official emulator](https://github.com/commanderx16/x16-emulator), this has a few more dependencies, see the build instructions below. It compiles on Windows, Debian Linux, and Raspbian, and probably +This is an emulator for the Commander X16 computer system. Unlike [the official emulator](https://github.com/X16Community/x16-emulator), this has a few more dependencies, see the build instructions below. It compiles on Windows, Debian Linux, and Raspbian, and probably other Linux-based platforms. Don't expect official "releases" until the physical X16 is out. Until then, there will be "non-releases" of Box16. @@ -49,7 +49,7 @@ loaded from the directory containing the emulator binary, or you can use the `-r > __WARNING:__ Older versions of the ROM might not work in newer versions of the emulator, and vice versa. -You can build a ROM image yourself using the [build instructions](https://github.com/commanderx16/x16-rom#releases-and-building) in the [x16-rom](https://github.com/commanderx16/x16-rom) repo. The `rom.bin` included in the [_latest_ release](https://github.com/commanderx16/x16-emulator/releases) of the emulator may also work with the HEAD of this repo, but this is not guaranteed. +You can build a ROM image yourself using the [build instructions](https://github.com/X16Community/x16-rom#releases-and-building) in the [x16-rom](https://github.com/X16Community/x16-rom) repo. The `rom.bin` included in the [_latest_ release](https://github.com/X16Community/x16-emulator/releases) of the emulator may also work with the HEAD of this repo, but this is not guaranteed. ### Linux Build @@ -355,7 +355,7 @@ Forum Wiki ---- -[https://github.com/commanderx16/x16-emulator/wiki](https://github.com/commanderx16/x16-emulator/wiki) +[https://github.com/commanderx16/x16-emulator/wiki](https://github.com/X16Community/x16-emulator/wiki) License diff --git a/src/boxmon/boxmon.cpp b/src/boxmon/boxmon.cpp index 287ce9d..5ed0f8f 100644 --- a/src/boxmon/boxmon.cpp +++ b/src/boxmon/boxmon.cpp @@ -23,7 +23,6 @@ enum class parse_command_result { void boxmon_system_init() { - boxmon::boxmon_command::finalize_list(); } void boxmon_system_shutdown() diff --git a/src/boxmon/command.cpp b/src/boxmon/command.cpp index a0b3687..2a84032 100644 --- a/src/boxmon/command.cpp +++ b/src/boxmon/command.cpp @@ -1,7 +1,7 @@ #include "command.h" #include -#include +#include #include "boxmon.h" #include "parser.h" @@ -20,7 +20,7 @@ namespace boxmon m_run(fn) { auto &command_list = get_command_list(); - command_list.push_back(this); + command_list.insert({ name, this }); } std::strong_ordering boxmon_command::operator<=>(char const *name) const @@ -48,35 +48,13 @@ namespace boxmon return m_description; } - void boxmon_command::finalize_list() - { - auto &command_list = get_command_list(); - std::sort(begin(command_list), end(command_list), [](const boxmon_command *a, const boxmon_command *b) { return *a > *b; }); - } - const boxmon_command *boxmon_command::find(char const *name) { const auto &command_list = get_command_list(); - - size_t search_min = 0; - size_t search_max = command_list.size()-1; - - while (search_min != search_max) { - const auto search_i = (search_min + search_max) >> 1; - const auto cmp_i = *command_list[search_i] <=> name; - if (is_eq(cmp_i)) { - return command_list[search_i]; - } else if (is_lt(cmp_i)) { - search_max = std::max(search_min, search_i - 1); - } else { - search_min = std::min(search_max, search_i + 1); - } - } - - if (is_eq(*command_list[search_min] <=> name)) { - return command_list[search_min]; + const auto icmd = command_list.find(name); + if (icmd != command_list.end()) { + return icmd->second; } - return nullptr; } @@ -84,7 +62,7 @@ namespace boxmon { const auto &command_list = get_command_list(); for (auto cmd : command_list) { - fn(cmd); + fn(cmd.second); } } @@ -92,17 +70,17 @@ namespace boxmon { const auto &command_list = get_command_list(); for (auto cmd : command_list) { - if (strstr(cmd->get_name(), name) != nullptr) { - fn(cmd); - } else if (strstr(cmd->get_description(), name) != nullptr) { - fn(cmd); + if (strstr(cmd.second->get_name(), name) != nullptr) { + fn(cmd.second); + } else if (strstr(cmd.second->get_description(), name) != nullptr) { + fn(cmd.second); } } } - std::vector &boxmon_command::get_command_list() + std::map &boxmon_command::get_command_list() { - static std::vector command_list; + static std::map command_list; return command_list; } @@ -196,7 +174,7 @@ BOXMON_ALIAS(al, add_label); BOXMON_COMMAND(backtrace, "backtrace") { char const *names[] = { "N", "V", "-", "B", "D", "I", "Z", "C" }; - for (size_t i = 0; i < stack6502.count(); ++i) { + for (size_t i = 0; i < state6502.sp_depth; ++i) { const auto &ss = stack6502[static_cast(i)]; char const *op = [&]() -> char const * { switch (ss.op_type) { @@ -206,6 +184,9 @@ BOXMON_COMMAND(backtrace, "backtrace") case _stack_op_type::irq: return "IRQ"; break; + case _stack_op_type::smart: + return "---"; + break; case _stack_op_type::op: return mnemonics[ss.opcode]; break; diff --git a/src/boxmon/command.h b/src/boxmon/command.h index 7d9654c..729d360 100644 --- a/src/boxmon/command.h +++ b/src/boxmon/command.h @@ -2,6 +2,7 @@ #include #include +#include #include "parser.h" @@ -19,7 +20,6 @@ namespace boxmon char const *get_name() const; char const *get_description() const; - static void finalize_list(); static const boxmon_command *find(char const *name); static void for_each(std::function fn); static void for_each_partial(char const *name, std::function fn); @@ -30,7 +30,7 @@ namespace boxmon std::function m_run; - static std::vector &get_command_list(); + static std::map &get_command_list(); }; class boxmon_alias : public boxmon_command diff --git a/src/boxmon/expression.cpp b/src/boxmon/expression.cpp index 6743b7e..52efaaa 100644 --- a/src/boxmon/expression.cpp +++ b/src/boxmon/expression.cpp @@ -99,6 +99,13 @@ namespace boxmon return namelist.front(); } + bool symbol_expression::is_valid() const + { + auto namelist = symbols_find(m_symbol); + return !namelist.empty(); + } + + unary_expression::unary_expression(expression_type type, const expression_base *param) : expression_base(type), m_param(param) diff --git a/src/boxmon/expression.h b/src/boxmon/expression.h index ccd5856..09488a6 100644 --- a/src/boxmon/expression.h +++ b/src/boxmon/expression.h @@ -93,6 +93,8 @@ namespace boxmon virtual ~symbol_expression() override final; virtual int evaluate() const override final; + bool is_valid() const; + private: std::string m_symbol; }; diff --git a/src/boxmon/parser.cpp b/src/boxmon/parser.cpp index c85c07d..329c64d 100644 --- a/src/boxmon/parser.cpp +++ b/src/boxmon/parser.cpp @@ -576,8 +576,15 @@ namespace boxmon expression_stack.push(new value_expression(num)); return expression_stack.top()->get_type(); } else if (std::string symbol; parse_word(symbol, look)) { - expression_stack.push(new symbol_expression(symbol)); - return expression_stack.top()->get_type(); + symbol_expression *new_symbol = new symbol_expression(symbol); + if (new_symbol->is_valid()) { + expression_stack.push(new_symbol); + return expression_stack.top()->get_type(); + } + if ((flags & expression_parse_flags_suppress_errors) == 0) { + boxmon_error_printf("Expression parse failed (invalid symbol name) at: \"%s\"\n", look); + } + return expression_type::invalid; } else { return expression_type::invalid; } diff --git a/src/cpu/fake6502.cpp b/src/cpu/fake6502.cpp index 3ce864a..7fd4ae9 100644 --- a/src/cpu/fake6502.cpp +++ b/src/cpu/fake6502.cpp @@ -84,6 +84,7 @@ // 6502 CPU registers _state6502 state6502; +_state6502 debug_state6502; // helper variables uint32_t instructions = 0; // keep track of total instructions executed @@ -95,7 +96,8 @@ uint8_t debug6502 = 0; uint8_t penaltyop, penaltyaddr; uint8_t waiting = 0; -ring_buffer<_smart_stack, 256> stack6502; +_smart_stack stack6502[256]; +uint8_t stack6502_underflow = 0; ring_buffer<_cpuhistory, 256> history6502; // externally supplied functions @@ -130,10 +132,12 @@ static void putvalue(uint16_t saveval) void nmi6502() { - auto &ss = stack6502.allocate(); + auto &ss = stack6502[state6502.sp_depth++]; ss.source_pc = state6502.pc; ss.source_bank = bank6502(state6502.pc); - ss.state = state6502; + ss.push_depth = 0; + ss.push_unwind_depth = 0; + state6502.sp_unwind_depth = state6502.sp_depth; push16(state6502.pc); push8(state6502.status & ~FLAG_BREAK); @@ -146,16 +150,21 @@ void nmi6502() ss.dest_pc = state6502.pc; ss.dest_bank = bank6502(state6502.pc); ss.op_type = _stack_op_type::nmi; + ss.pop_type = _stack_pop_type::unknown; ss.opcode = 0; + ss.state = debug_state6502; } void irq6502() { if (!(state6502.status & FLAG_INTERRUPT)) { - auto &ss = stack6502.allocate(); + auto &ss = stack6502[state6502.sp_depth++]; ss.source_pc = state6502.pc; ss.source_bank = bank6502(state6502.pc); - ss.state = state6502; + ss.push_depth = 0; + ss.push_unwind_depth = 0; + state6502.sp_unwind_depth = state6502.sp_depth; + push16(state6502.pc); push8(state6502.status & ~FLAG_BREAK); setinterrupt(); @@ -166,7 +175,9 @@ void irq6502() ss.dest_pc = state6502.pc; ss.dest_bank = bank6502(state6502.pc); ss.op_type = _stack_op_type::irq; + ss.pop_type = _stack_pop_type::unknown; ss.opcode = 0; + ss.state = debug_state6502; } waiting = 0; } @@ -184,7 +195,7 @@ void exec6502(uint32_t tickcount) clockgoal6502 += tickcount; while (clockticks6502 < clockgoal6502) { - const _state6502 debug_state6502 = state6502; + debug_state6502 = state6502; const uint64_t debug_clockticks6502 = clockticks6502; opcode = read6502(state6502.pc++); @@ -231,7 +242,7 @@ void step6502() return; } - const _state6502 debug_state6502 = state6502; + debug_state6502 = state6502; const uint64_t debug_clockticks6502 = clockticks6502; opcode = read6502(state6502.pc++); diff --git a/src/cpu/fake6502.h b/src/cpu/fake6502.h index 7854c52..595fbb1 100644 --- a/src/cpu/fake6502.h +++ b/src/cpu/fake6502.h @@ -13,23 +13,55 @@ struct _state6502 { uint16_t pc; + uint8_t sp_depth; + uint8_t sp_unwind_depth; uint8_t sp, a, x, y, status; }; enum class _stack_op_type : uint8_t { - op, nmi, irq, + op, + smart, +}; + +enum class _stack_pop_type : uint8_t { + unknown, + rts, + rti +}; + +enum class _push_op_type : uint8_t { + unknown, + a, + x, + y, + status, + smart, +}; + +struct _smart_stack_ex { + _push_op_type push_type; + _push_op_type pull_type; + uint8_t value; + uint16_t pc; + uint8_t bank; }; struct _smart_stack { - uint16_t source_pc; - uint16_t dest_pc; - uint8_t source_bank; - uint8_t dest_bank; - _stack_op_type op_type; - uint8_t opcode; - _state6502 state; + uint16_t source_pc; + uint16_t dest_pc; + uint8_t source_bank; + uint8_t dest_bank; + _stack_op_type op_type; + _stack_pop_type pop_type; + uint16_t pop_pc; + uint8_t pop_bank; + uint8_t opcode; + uint8_t push_depth; + uint8_t push_unwind_depth; + _smart_stack_ex pushed_bytes[256]; + _state6502 state; }; struct _cpuhistory { diff --git a/src/cpu/instructions_6502.h b/src/cpu/instructions_6502.h index 583e1b9..a720090 100644 --- a/src/cpu/instructions_6502.h +++ b/src/cpu/instructions_6502.h @@ -372,10 +372,12 @@ jmp() static void jsr() { - auto &ss = stack6502.allocate(); - ss.source_pc = state6502.pc; - ss.source_bank = bank6502(state6502.pc); - ss.state = state6502; + auto &ss = stack6502[state6502.sp_depth++]; + ss.source_pc = state6502.pc; + ss.source_bank = bank6502(state6502.pc); + ss.push_depth = 0; + ss.push_unwind_depth = 0; + state6502.sp_unwind_depth = state6502.sp_depth; push16(state6502.pc - 1); state6502.pc = ea; @@ -383,7 +385,9 @@ jsr() ss.dest_pc = state6502.pc; ss.dest_bank = bank6502(state6502.pc); ss.op_type = _stack_op_type::op; + ss.pop_type = _stack_pop_type::unknown; ss.opcode = opcode; + ss.state = debug_state6502; } static void @@ -467,12 +471,48 @@ static void pha() { push8(state6502.a); + auto &ss = stack6502[(state6502.sp_depth + 255) & 0xff]; + auto &ssx = ss.pushed_bytes[ss.push_depth++]; + ssx.push_type = _push_op_type::a; + ssx.pull_type = _push_op_type::unknown; + ssx.value = state6502.a; + ssx.pc = state6502.pc - 1; + ssx.bank = bank6502(state6502.pc - 1); + ss.push_unwind_depth = ss.push_depth; } static void php() { push8(state6502.status | FLAG_BREAK); + auto &ss = stack6502[(state6502.sp_depth + 255) & 0xff]; + auto &ssx = ss.pushed_bytes[ss.push_depth++]; + ssx.push_type = _push_op_type::status; + ssx.pull_type = _push_op_type::unknown; + ssx.value = state6502.status | FLAG_BREAK; + ssx.pc = state6502.pc - 1; + ssx.bank = bank6502(state6502.pc - 1); + ss.push_unwind_depth = ss.push_depth; + + if ((ss.op_type == _stack_op_type::irq || ss.op_type == _stack_op_type::nmi) && ss.push_depth == 5) { + ss.push_depth -= 3; + for (int i = 0; i < 3; ++i) { + ss.pushed_bytes[ss.push_depth + i].pull_type = _push_op_type::smart; + } + auto &ss2 = stack6502[state6502.sp_depth++]; + ss2.source_pc = (static_cast(ss.pushed_bytes[ss.push_depth].value) << 8) | static_cast(ss.pushed_bytes[ss.push_depth + 1].value); + ss2.source_bank = bank6502(state6502.pc); + ss2.push_depth = 0; + ss2.push_unwind_depth = 0; + state6502.sp_unwind_depth = state6502.sp_depth; + + ss2.dest_pc = state6502.pc; + ss2.dest_bank = bank6502(state6502.pc); + ss2.op_type = _stack_op_type::smart; + ss2.pop_type = _stack_pop_type::unknown; + ss2.opcode = 0; + ss2.state = debug_state6502; + } } static void @@ -482,12 +522,22 @@ pla() zerocalc(state6502.a); signcalc(state6502.a); + + auto &ss = stack6502[(state6502.sp_depth + 255) & 0xff]; + ss.push_depth -= !!ss.push_depth; + auto &ssx = ss.pushed_bytes[ss.push_depth]; + ssx.pull_type = _push_op_type::a; } static void plp() { state6502.status = pull8() | FLAG_CONSTANT; + + auto &ss = stack6502[(state6502.sp_depth + 255) & 0xff]; + ss.push_depth -= !!ss.push_depth; + auto &ssx = ss.pushed_bytes[ss.push_depth]; + ssx.pull_type = _push_op_type::status; } static void @@ -522,18 +572,33 @@ ror() static void rti() { - state6502.status = pull8(); - value = pull16(); - state6502.pc = value; - stack6502.pop_newest(); + const uint16_t old_pc = state6502.pc; + state6502.status = pull8(); + value = pull16(); + state6502.pc = value; + stack6502_underflow |= !state6502.sp_depth; + state6502.sp_depth -= !!state6502.sp_depth; + + auto &ss = stack6502[state6502.sp_depth]; + ss.pop_type = _stack_pop_type::rti; + ss.pop_pc = old_pc - 1; + ss.pop_bank = bank6502(old_pc); } static void rts() { + const uint16_t old_pc = state6502.pc; + value = pull16(); state6502.pc = value + 1; - stack6502.pop_newest(); + stack6502_underflow |= !state6502.sp_depth; + state6502.sp_depth -= !!state6502.sp_depth; + + auto &ss = stack6502[state6502.sp_depth]; + ss.pop_type = _stack_pop_type::rts; + ss.pop_pc = old_pc - 1; + ss.pop_bank = bank6502(old_pc); } static void diff --git a/src/cpu/instructions_65c02.h b/src/cpu/instructions_65c02.h index bff4cb4..d7ed194 100644 --- a/src/cpu/instructions_65c02.h +++ b/src/cpu/instructions_65c02.h @@ -84,6 +84,14 @@ static void phx() { push8(state6502.x); + auto &ss = stack6502[(state6502.sp_depth + 255) & 0xff]; + auto &ssx = ss.pushed_bytes[ss.push_depth++]; + ssx.push_type = _push_op_type::x; + ssx.pull_type = _push_op_type::unknown; + ssx.value = state6502.x; + ssx.pc = state6502.pc - 1; + ssx.bank = bank6502(state6502.pc - 1); + ss.push_unwind_depth = ss.push_depth; } static void @@ -93,12 +101,25 @@ plx() zerocalc(state6502.x); signcalc(state6502.x); + + auto &ss = stack6502[(state6502.sp_depth + 255) & 0xff]; + ss.push_depth -= !!ss.push_depth; + auto &ssx = ss.pushed_bytes[ss.push_depth]; + ssx.pull_type = _push_op_type::x; } static void phy() { push8(state6502.y); + auto &ss = stack6502[(state6502.sp_depth + 255) & 0xff]; + auto &ssx = ss.pushed_bytes[ss.push_depth++]; + ssx.push_type = _push_op_type::y; + ssx.pull_type = _push_op_type::unknown; + ssx.value = state6502.y; + ssx.pc = state6502.pc - 1; + ssx.bank = bank6502(state6502.pc - 1); + ss.push_unwind_depth = ss.push_depth; } static void @@ -108,6 +129,11 @@ ply() zerocalc(state6502.y); signcalc(state6502.y); + + auto &ss = stack6502[(state6502.sp_depth + 255) & 0xff]; + ss.push_depth -= !!ss.push_depth; + auto &ssx = ss.pushed_bytes[ss.push_depth]; + ssx.pull_type = _push_op_type::y; } // ******************************************************************************************* diff --git a/src/cpu/support.h b/src/cpu/support.h index 997b4f7..b6e844d 100644 --- a/src/cpu/support.h +++ b/src/cpu/support.h @@ -85,12 +85,15 @@ uint8_t pull8() void reset6502() { vp6502(); - state6502.pc = (uint16_t)read6502(0xFFFC) | ((uint16_t)read6502(0xFFFD) << 8); - state6502.a = 0; - state6502.x = 0; - state6502.y = 0; - state6502.sp = 0xFD; + state6502.pc = (uint16_t)read6502(0xFFFC) | ((uint16_t)read6502(0xFFFD) << 8); + state6502.sp_depth = 0; + state6502.sp_unwind_depth = 0; + state6502.a = 0; + state6502.x = 0; + state6502.y = 0; + state6502.sp = 0xFD; state6502.status |= FLAG_CONSTANT | FLAG_BREAK; + stack6502_underflow = 0; setinterrupt(); cleardecimal(); waiting = 0; diff --git a/src/debugger.cpp b/src/debugger.cpp index 872cf9b..ac5989a 100644 --- a/src/debugger.cpp +++ b/src/debugger.cpp @@ -223,6 +223,18 @@ void debugger_process_cpu() return; } + const auto [addr, bank] = get_current_pc(); + const auto flags = get_flags(addr, bank); + if (flags & DEBUG6502_CONDITION) { + if (flags & DEBUG6502_EXPRESSION) { + if (!debugger_evaluate_condition(addr, bank)) { + return; + } + } else { + return; + } + } + debugger_pause_execution(); } @@ -375,6 +387,12 @@ bool debugger_evaluate_condition(uint16_t address, uint8_t bank) return false; } +bool debugger_has_valid_expression(uint16_t address, uint8_t bank) +{ + const uint32_t offset = get_offset(address, bank); + return (Breakpoint_flags[offset] & DEBUG6502_EXPRESSION); +} + void debugger_add_breakpoint(uint16_t address, uint8_t bank /* = 0 */, uint8_t flags /* = DEBUG6502_EXEC */) { if (address < 0xa000) { diff --git a/src/debugger.h b/src/debugger.h index 9eeda41..3ed9e82 100644 --- a/src/debugger.h +++ b/src/debugger.h @@ -42,6 +42,7 @@ uint8_t debugger_get_flags(uint16_t address, uint8_t bank); std::string debugger_get_condition(uint16_t address, uint8_t bank); void debugger_set_condition(uint16_t address, uint8_t bank, const std::string &condition); bool debugger_evaluate_condition(uint16_t address, uint8_t bank); +bool debugger_has_valid_expression(uint16_t address, uint8_t bank); // Bank parameter is only meaninful for addresses >= $A000. // Addresses < $A000 will force bank to 0. diff --git a/src/display.cpp b/src/display.cpp index da9e5ab..40be8d7 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -80,12 +80,12 @@ static float Max_anisotropy = 1.0f; static bool vsync_is_enabled() { - return static_cast(Options.vsync_mode) > static_cast(vsync_mode_t::VSYNC_MODE_DISABLED); + return static_cast(Options.vsync_mode) > static_cast(vsync_mode_t::VSYNC_MODE_NONE); } static bool vsync_is_disabled() { - return static_cast(Options.vsync_mode) < static_cast(vsync_mode_t::VSYNC_MODE_DISABLED); + return static_cast(Options.vsync_mode) < static_cast(vsync_mode_t::VSYNC_MODE_NONE); } bool icon_set::load_file(const char *filename, int icon_width, int icon_height) diff --git a/src/glue.h b/src/glue.h index aea545f..a1edecc 100644 --- a/src/glue.h +++ b/src/glue.h @@ -27,14 +27,16 @@ #define ROM_SIZE (TOTAL_ROM_BANKS * 16384) /* banks at $C000-$FFFF */ extern _state6502 state6502; +extern _state6502 debug_state6502; extern uint8_t waiting; -extern ring_buffer<_smart_stack, 256> stack6502; -extern ring_buffer<_cpuhistory, 256, true> history6502; +extern _smart_stack stack6502[256]; +extern uint8_t stack6502_underflow; +extern ring_buffer<_cpuhistory, 256> history6502; + extern uint8_t *RAM; extern uint8_t ROM[ROM_SIZE]; extern uint32_t instructions; extern uint8_t debug6502; - extern bool save_on_exit; extern void machine_dump(const char *reason); diff --git a/src/hypercalls.cpp b/src/hypercalls.cpp index 027263d..d2d302a 100644 --- a/src/hypercalls.cpp +++ b/src/hypercalls.cpp @@ -16,6 +16,7 @@ #include "vera/sdcard.h" #include "zlib.h" +#define KERNAL_MCIOUT (0xfeb1) #define KERNAL_MACPTR (0xff44) #define KERNAL_SECOND (0xff93) #define KERNAL_TKSA (0xff96) @@ -35,7 +36,7 @@ static uint16_t Kernal_status = 0; static bool Has_boot_tasks = false; static bool prg_finished_loading = false; -static bool (*Hypercall_table[0x100])(void); +static bool (*Hypercall_table[0x200])(void); static bool is_kernal() { @@ -159,7 +160,22 @@ void hypercalls_update() memset(Hypercall_table, 0, sizeof(Hypercall_table)); if (ieee_hypercalls_allowed()) { - Hypercall_table[KERNAL_MACPTR & 0xff] = []() -> bool { + Hypercall_table[KERNAL_MCIOUT & 0x1ff] = []() -> bool { + uint16_t count = state6502.a; + const int s = MCIOUT(state6502.y << 8 | state6502.x, &count, state6502.status & 0x01); + state6502.x = count & 0xff; + state6502.y = count >> 8; + if (s == -2) { + state6502.status |= 1; // SEC (unsupported, or in this case, no open context) + } else { + state6502.status &= 0xfe; // clear C -> supported + } + + set_kernal_status(s); + return true; + }; + + Hypercall_table[KERNAL_MACPTR & 0x1ff] = []() -> bool { uint16_t count = state6502.a; const int s = MACPTR(state6502.y << 8 | state6502.x, &count, state6502.status & 0x01); @@ -172,19 +188,19 @@ void hypercalls_update() return true; }; - Hypercall_table[KERNAL_SECOND & 0xff] = []() -> bool { + Hypercall_table[KERNAL_SECOND & 0x1ff] = []() -> bool { const int s = SECOND(state6502.a); set_kernal_status(s); return true; }; - Hypercall_table[KERNAL_TKSA & 0xff] = []() -> bool { + Hypercall_table[KERNAL_TKSA & 0x1ff] = []() -> bool { TKSA(state6502.a); return true; }; - Hypercall_table[KERNAL_ACPTR & 0xff] = []() -> bool { + Hypercall_table[KERNAL_ACPTR & 0x1ff] = []() -> bool { const int s = ACPTR(&state6502.a); state6502.status = (state6502.status & ~3) | (!state6502.a << 1); @@ -193,19 +209,19 @@ void hypercalls_update() return true; }; - Hypercall_table[KERNAL_CIOUT & 0xff] = []() -> bool { + Hypercall_table[KERNAL_CIOUT & 0x1ff] = []() -> bool { const int s = CIOUT(state6502.a); set_kernal_status(s); return true; }; - Hypercall_table[KERNAL_UNTLK & 0xff] = []() -> bool { + Hypercall_table[KERNAL_UNTLK & 0x1ff] = []() -> bool { UNTLK(); return true; }; - Hypercall_table[KERNAL_UNLSN & 0xff] = []() -> bool { + Hypercall_table[KERNAL_UNLSN & 0x1ff] = []() -> bool { const int s = UNLSN(); if (s == -2) { // special error behavior state6502.status = (state6502.status | 1); // SEC @@ -226,19 +242,19 @@ void hypercalls_update() return true; }; - Hypercall_table[KERNAL_LISTEN & 0xff] = []() -> bool { + Hypercall_table[KERNAL_LISTEN & 0x1ff] = []() -> bool { LISTEN(state6502.a); return true; }; - Hypercall_table[KERNAL_TALK & 0xff] = []() -> bool { + Hypercall_table[KERNAL_TALK & 0x1ff] = []() -> bool { TALK(state6502.a); return true; }; } // if (!sdcard_is_attached()) { - // Hypercall_table[KERNAL_LOAD & 0xff] = []() -> bool { + // Hypercall_table[KERNAL_LOAD & 0x1ff] = []() -> bool { // if (RAM[FA] == 8) { // LOAD(); // return true; @@ -246,7 +262,7 @@ void hypercalls_update() // return false; // }; - // Hypercall_table[KERNAL_SAVE & 0xff] = []() -> bool { + // Hypercall_table[KERNAL_SAVE & 0x1ff] = []() -> bool { // if (RAM[FA] == 8) { // SAVE(); // return true; @@ -256,7 +272,7 @@ void hypercalls_update() // } if (Has_boot_tasks) { - Hypercall_table[KERNAL_CHRIN & 0xff] = []() -> bool { + Hypercall_table[KERNAL_CHRIN & 0x1ff] = []() -> bool { // as soon as BASIC starts reading a line... if (!Options.prg_path.empty()) { std::filesystem::path prg_path = options_get_hyper_path() / Options.prg_path; @@ -327,7 +343,7 @@ void hypercalls_update() } if (Options.echo_mode != echo_mode_t::ECHO_MODE_NONE) { - Hypercall_table[KERNAL_CHROUT & 0xff] = []() -> bool { + Hypercall_table[KERNAL_CHROUT & 0x1ff] = []() -> bool { uint8_t c = state6502.a; if (Options.echo_mode == echo_mode_t::ECHO_MODE_COOKED) { if (c == 0x0d) { @@ -360,11 +376,11 @@ void hypercalls_update() void hypercalls_process() { - if (!is_kernal() || state6502.pc < 0xFF44) { + if (!is_kernal() || state6502.pc < 0xFEB1) { return; } - const auto hypercall = Hypercall_table[state6502.pc & 0xff]; + const auto hypercall = Hypercall_table[state6502.pc & 0x1ff]; if (hypercall != nullptr) { if (hypercall()) { state6502.pc = (RAM[0x100 + state6502.sp + 1] | (RAM[0x100 + state6502.sp + 2] << 8)) + 1; diff --git a/src/ieee.cpp b/src/ieee.cpp index 7b34944..670b248 100644 --- a/src/ieee.cpp +++ b/src/ieee.cpp @@ -678,8 +678,7 @@ static void set_activity(bool active) static void set_error(int e, int t, int s) { - snprintf(error, sizeof(error), "%02x,%s,%02d,%02d\r", e, error_string(e), t, s); - error_len = static_cast(strlen(error)); + error_len = snprintf(error, sizeof(error), "%02x,%s,%02d,%02d\r", e, error_string(e), t, s); error_pos = 0; uint8_t cbdos_flags = get_kernal_cbdos_flags(); if (e < 0x10 || e == 0x73) { @@ -1277,7 +1276,7 @@ int ACPTR(uint8_t *a) int CIOUT(uint8_t a) { - int ret = -1; + int ret = 0; if (log_ieee) { printf("%s $%02x\n", __func__, a); } @@ -1386,7 +1385,7 @@ int MACPTR(uint16_t addr, uint16_t *c, uint8_t stream_mode) write6502(0, ram_bank); } } - if (ret >= 0) { + if (ret > 0) { break; } } while (i < count); @@ -1396,3 +1395,38 @@ int MACPTR(uint16_t addr, uint16_t *c, uint8_t stream_mode) *c = i; return ret; } + +int MCIOUT(uint16_t addr, uint16_t *c, uint8_t stream_mode) +{ + if (log_ieee) { + printf("%s $%04x $%04x $%02x\n", __func__, addr, *c, stream_mode); + } + + int ret = 0; + int count = (*c != 0) ? (*c) : 256; + uint8_t ram_bank = read6502(0); + int i = 0; + if (channels[channel].f && channels[channel].write) { + do { + uint8_t byte; + byte = read6502(addr); + i++; + if (!stream_mode) { + addr++; + if (addr == 0xc000) { + addr = 0xa000; + ram_bank++; + write6502(0, ram_bank); + } + } + ret = CIOUT(byte); + if (ret > 0) { + break; + } + } while(i < count); + } else { + ret = -2; + } + *c = i; + return ret; +} diff --git a/src/ieee.h b/src/ieee.h index b5e2297..65ba070 100644 --- a/src/ieee.h +++ b/src/ieee.h @@ -16,5 +16,6 @@ int UNLSN(); void LISTEN(uint8_t a); void TALK(uint8_t a); int MACPTR(uint16_t addr, uint16_t *count, uint8_t stream_mode); +int MCIOUT(uint16_t addr, uint16_t *count, uint8_t stream_mode); #endif diff --git a/src/keyboard.cpp b/src/keyboard.cpp index 6259b18..ac0fe40 100644 --- a/src/keyboard.cpp +++ b/src/keyboard.cpp @@ -47,38 +47,38 @@ static std::list Keyboard_event_list; static ring_buffer Keyboard_buffer; static const uint16_t SDL_to_keynum_table[] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x001f, 0x0032, 0x0030, 0x0021, 0x0013, 0x0022, 0x0023, 0x0024, 0x0018, 0x0025, 0x0026, 0x0027, - 0x0034, 0x0033, 0x0019, 0x001a, 0x0011, 0x0014, 0x0020, 0x0015, 0x0017, 0x0031, 0x0012, 0x002f, 0x0016, 0x002e, 0x0002, 0x0003, - 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x002b, 0x007e, 0x000f, 0x0010, 0x003d, 0x000c, 0x000d, 0x001b, - 0x001c, 0x001d, 0x0000, 0x0028, 0x0029, 0x0001, 0x0035, 0x0036, 0x0037, 0x001e, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, - 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x0000, 0x007d, 0x007e, 0x004b, 0x0050, 0x0055, 0x004c, 0x0051, 0x0056, 0x0059, - 0x004f, 0x0054, 0x0053, 0x0000, 0x005f, 0x0064, 0x0069, 0x006a, 0x006c, 0x005d, 0x0062, 0x0067, 0x005c, 0x0061, 0x0066, 0x005b, - 0x0060, 0x0065, 0x0063, 0x0068, 0x002d, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x003a, 0x002c, 0x003c, 0x003b, 0x0040, 0x0039, 0x003e, 0x003f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x001f, 0x0032, 0x0030, 0x0021, 0x0013, 0x0022, 0x0023, 0x0024, 0x0018, 0x0025, 0x0026, 0x0027, + 0x0034, 0x0033, 0x0019, 0x001a, 0x0011, 0x0014, 0x0020, 0x0015, 0x0017, 0x0031, 0x0012, 0x002f, 0x0016, 0x002e, 0x0002, 0x0003, + 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x002b, 0x006e, 0x000f, 0x0010, 0x003d, 0x000c, 0x000d, 0x001b, + 0x001c, 0x001d, 0x0000, 0x0028, 0x0029, 0x0001, 0x0035, 0x0036, 0x0037, 0x001e, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, + 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x0000, 0x007d, 0x007e, 0x004b, 0x0050, 0x0055, 0x004c, 0x0051, 0x0056, 0x0059, + 0x004f, 0x0054, 0x0053, 0x0000, 0x005f, 0x0064, 0x0069, 0x006a, 0x006c, 0x005d, 0x0062, 0x0067, 0x005c, 0x0061, 0x0066, 0x005b, + 0x0060, 0x0065, 0x0063, 0x0068, 0x002d, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x003a, 0x002c, 0x003c, 0x003b, 0x0040, 0x0039, 0x003e, 0x003f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; static bool process_key_event(const key_event_data &data) diff --git a/src/main.cpp b/src/main.cpp index 74aa730..a2cdcb2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -464,7 +464,6 @@ void emulator_loop() cpu_visualization_step(); uint8_t clocks = (uint8_t)(clockticks6502 - old_clockticks6502); bool new_frame = vera_video_step(MHZ, clocks); - bool via1_irq_old = via1_irq(); via1_step(clocks); via2_step(clocks); rtc_step(clocks); @@ -493,12 +492,7 @@ void emulator_loop() #endif } - if (!via1_irq_old && via1_irq()) { - nmi6502(); - debugger_interrupt(); - } - - if (vera_video_get_irq_out() || YM_irq() || via2_irq()) { + if (vera_video_get_irq_out() || YM_irq() || via1_irq() || via2_irq()) { irq6502(); debugger_interrupt(); } diff --git a/src/memory.cpp b/src/memory.cpp index ae0ec25..f9386f9 100644 --- a/src/memory.cpp +++ b/src/memory.cpp @@ -16,6 +16,7 @@ #include "gif_recorder.h" #include "glue.h" #include "hypercalls.h" +#include "unicode.h" #include "vera/vera_video.h" #include "via.h" #include "wav_recorder.h" @@ -35,6 +36,8 @@ uint8_t rom_bank_register; static uint64_t *RAM_written; static uint8_t addr_ym = 0; +static uint64_t clock_snap = 0UL; +static uint64_t clock_base = 0UL; #define DEVICE_EMULATOR (0x9fb0) @@ -238,10 +241,10 @@ uint8_t debug_emu_read(uint8_t reg) case 5: return gif_recorder_get_state(); case 6: return wav_recorder_get_state(); case 7: return Options.no_keybinds ? 1 : 0; - case 8: return (clockticks6502 >> 0) & 0xff; - case 9: return (clockticks6502 >> 8) & 0xff; - case 10: return (clockticks6502 >> 16) & 0xff; - case 11: return (clockticks6502 >> 24) & 0xff; + case 8: return (clock_snap >> 0) & 0xff; // don't do snapshotting here because no state should be changed + case 9: return (clock_snap >> 8) & 0xff; + case 10: return (clock_snap >> 16) & 0xff; + case 11: return (clock_snap >> 24) & 0xff; // case 12: return -1; case 13: return Options.keymap; case 14: return '1'; // emulator detection @@ -261,10 +264,12 @@ uint8_t real_emu_read(uint8_t reg) case 5: return gif_recorder_get_state(); case 6: return wav_recorder_get_state(); case 7: return Options.no_keybinds ? 1 : 0; - case 8: return (clockticks6502 >> 0) & 0xff; - case 9: return (clockticks6502 >> 8) & 0xff; - case 10: return (clockticks6502 >> 16) & 0xff; - case 11: return (clockticks6502 >> 24) & 0xff; + case 8: + clock_snap = clockticks6502 - clock_base; + return (clock_snap >> 0) & 0xff; + case 9: return (clock_snap >> 8) & 0xff; + case 10: return (clock_snap >> 16) & 0xff; + case 11: return (clock_snap >> 24) & 0xff; // case 12: return -1; case 13: return Options.keymap; case 14: return '1'; // emulator detection @@ -290,6 +295,20 @@ void emu_write(uint8_t reg, uint8_t value) case 5: gif_recorder_set((gif_recorder_command_t)value); break; case 6: wav_recorder_set((wav_recorder_command_t)value); break; case 7: Options.no_keybinds = v; break; + case 8: clock_base = clockticks6502; break; + case 9: printf("User debug 1: $%02x\n", value); break; + case 10: printf("User debug 2: $%02x\n", value); break; + case 11: { + if (value == 0x09 || value == 0x0a || value == 0x0d || (value >= 0x20 && value < 0x7f)) { + putchar(value); + } else if (value >= 0xa1) { + print_iso8859_15_char((char) value); + } else { + printf("\xef\xbf\xbd"); // � + } + fflush(stdout); + break; + } default: break; // printf("WARN: Invalid register %x\n", DEVICE_EMULATOR + reg); } } diff --git a/src/overlay/overlay.cpp b/src/overlay/overlay.cpp index 2676a0f..63c4b29 100644 --- a/src/overlay/overlay.cpp +++ b/src/overlay/overlay.cpp @@ -440,9 +440,32 @@ static void draw_debugger_cpu_status() ImGui::TableNextColumn(); - if (ImGui::BeginTable("smart stack", 1, ImGuiTableFlags_ScrollY)) { - for (uint16_t i = 0; i < stack6502.count(); ++i) { - ImGui::TableNextColumn(); + if (ImGui::BeginTable("smart stack", 2, ImGuiTableFlags_ScrollY)) { + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 10); + ImGui::TableSetupColumn("Address"); + + ImGui::TableHeadersRow(); + + if (stack6502_underflow) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(1); + ImGui::TextDisabled("%s", "(Underflow)"); + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::Text("%s", "There appears to have been a smartstack underflow.\nThis usually means there was a mismatched jsr / rts pair,\nor an rti executed outside of an interrupt.\n\nBox16's SmartStack cannot currently track manual stack manipulation very well."); + ImGui::EndTooltip(); + } + ImGui::TableNextRow(); + } + for (uint16_t i = state6502.sp_unwind_depth - 1; i < state6502.sp_unwind_depth; --i) { + const auto &ss = stack6502[i]; + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + if (i == state6502.sp_depth - 1) { + ImGui::Text("%s", ">"); + } + ImGui::TableSetColumnIndex(1); auto do_label = [](uint16_t pc, uint8_t bank, bool allow_disabled) { char const *label = disasm_get_label(pc); bool pushed = false; @@ -452,11 +475,13 @@ static void draw_debugger_cpu_status() ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled)); char stack_line[256]; snprintf(stack_line, sizeof(stack_line), "$%02X:$%04X", bank, pc); + stack_line[255] = '\0'; pushed = ImGui::Selectable(stack_line, false, 0, ImGui::CalcTextSize(stack_line)); ImGui::PopStyleColor(); } else { char stack_line[256]; snprintf(stack_line, sizeof(stack_line), "$%02X:$%04X: %s", bank, pc, label); + stack_line[255] = '\0'; pushed = ImGui::Selectable(stack_line, false, 0, ImGui::CalcTextSize(stack_line)); } } else { @@ -464,11 +489,13 @@ static void draw_debugger_cpu_status() ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled)); char stack_line[256]; snprintf(stack_line, sizeof(stack_line), "$%04X", pc); + stack_line[255] = '\0'; pushed = ImGui::Selectable(stack_line, false, 0, ImGui::CalcTextSize(stack_line)); ImGui::PopStyleColor(); } else { char stack_line[256]; snprintf(stack_line, sizeof(stack_line), "$%04X: %s", pc, label); + stack_line[255] = '\0'; pushed = ImGui::Selectable(stack_line, false, 0, ImGui::CalcTextSize(stack_line)); } } @@ -482,23 +509,30 @@ static void draw_debugger_cpu_status() } } }; - - const auto &ss = stack6502[i]; - switch (ss.op_type) { - case _stack_op_type::nmi: - ImGui::PushStyleColor(ImGuiCol_TextDisabled, 0xFF003388); - ImGui::PushStyleColor(ImGuiCol_Text, 0xFF0077FF); - break; - case _stack_op_type::irq: - ImGui::PushStyleColor(ImGuiCol_TextDisabled, 0xFF007788); - ImGui::PushStyleColor(ImGuiCol_Text, 0xFF00FFFF); - break; - case _stack_op_type::op: - ImGui::PushStyleColor(ImGuiCol_TextDisabled, ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled)); - ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text)); - break; - default: - break; + if (i >= state6502.sp_depth) { + ImGui::PushStyleColor(ImGuiCol_TextDisabled, ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled)); + ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled)); + } else { + switch (ss.op_type) { + case _stack_op_type::nmi: + ImGui::PushStyleColor(ImGuiCol_TextDisabled, 0xFF003388); + ImGui::PushStyleColor(ImGuiCol_Text, 0xFF0077FF); + break; + case _stack_op_type::irq: + ImGui::PushStyleColor(ImGuiCol_TextDisabled, 0xFF007788); + ImGui::PushStyleColor(ImGuiCol_Text, 0xFF00FFFF); + break; + case _stack_op_type::smart: + ImGui::PushStyleColor(ImGuiCol_TextDisabled, 0xFF883300); + ImGui::PushStyleColor(ImGuiCol_Text, 0xFFFFFF00); + break; + case _stack_op_type::op: + ImGui::PushStyleColor(ImGuiCol_TextDisabled, ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled)); + ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text)); + break; + default: + break; + } } ImGui::PushID(i); do_label(ss.dest_pc, ss.dest_bank, true); @@ -536,13 +570,113 @@ static void draw_debugger_cpu_status() case _stack_op_type::op: ImGui::Text("%s", mnemonics[ss.opcode]); break; + case _stack_op_type::smart: + ImGui::Text("%s", "smart"); + break; default: break; } + if (i >= state6502.sp_depth) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextDisabled("%s", "Pop Address:"); + ImGui::TableSetColumnIndex(1); + do_label(ss.pop_pc, ss.pop_bank, false); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextDisabled("%s", "Pop Cause:"); + ImGui::TableSetColumnIndex(1); + switch (ss.pop_type) { + case _stack_pop_type::rti: + ImGui::Text("%s", "rti"); + break; + case _stack_pop_type::rts: + ImGui::Text("%s", "rts"); + break; + case _stack_pop_type::unknown: + ImGui::Text("%s", "(unknown)"); + break; + } + } + ImGui::EndTable(); } + if (ss.push_unwind_depth > 0) { + ImGui::TextDisabled("%s", "Additional byte pushes in this frame:"); + if (ImGui::BeginTable("additional pushes table", 5, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX)) { + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 10); + ImGui::TableSetupColumn("Address"); + ImGui::TableSetupColumn("Push Op"); + ImGui::TableSetupColumn("Value"); + ImGui::TableSetupColumn("Pull Op"); + ImGui::TableHeadersRow(); + + for (uint16_t j = ss.push_unwind_depth - 1; j < ss.push_unwind_depth; --j) { + const auto &ssx = ss.pushed_bytes[j]; + ImGui::TableNextRow(); + + ImGui::TableSetColumnIndex(0); + ImGui::Text("%s", (j == ss.push_depth - 1) ? ">" : " "); + + ImGui::TableSetColumnIndex(1); + do_label(ssx.pc, ssx.bank, false); + + ImGui::TableSetColumnIndex(2); + switch (ssx.push_type) { + case _push_op_type::a: + ImGui::Text("%s", "pha"); + break; + case _push_op_type::x: + ImGui::Text("%s", "phx"); + break; + case _push_op_type::y: + ImGui::Text("%s", "phy"); + break; + case _push_op_type::status: + ImGui::Text("%s", "php"); + break; + case _push_op_type::unknown: + ImGui::Text("%s", "(?)"); + break; + case _push_op_type::smart: + ImGui::Text("%s", "smart"); + break; + } + + ImGui::TableSetColumnIndex(3); + ImGui::Text("$%02x", ssx.value); + + if (j >= ss.push_depth) { + ImGui::TableSetColumnIndex(4); + switch (ssx.pull_type) { + case _push_op_type::a: + ImGui::Text("%s", "pla"); + break; + case _push_op_type::x: + ImGui::Text("%s", "plx"); + break; + case _push_op_type::y: + ImGui::Text("%s", "ply"); + break; + case _push_op_type::status: + ImGui::Text("%s", "plp"); + break; + case _push_op_type::unknown: + ImGui::Text("%s", "(?)"); + break; + case _push_op_type::smart: + ImGui::Text("%s", "smart"); + break; + } + } + } + ImGui::EndTable(); + } + } + ImGui::EndTooltip(); } } @@ -1670,8 +1804,12 @@ class tmap_visualizer for (uint32_t i = 0; i < num_dots; i++) { uint8_t tdat = tile_data[i]; - if (tdat > 0 && tdat < 16) // 8bpp quirk handling + if (tdat > 0 && tdat < 16) { // 8bpp quirk handling tdat += palette_offset; + if (t256c) { + tdat |= 0x80; + } + } pixels[i] = palette[tdat]; } } else { @@ -1721,8 +1859,13 @@ class tmap_visualizer for (int tj = 0; tj < tile_width; tj++) { uint8_t tdat = tile_data[src2]; src2 += src2_add; - if (tdat > 0 && tdat < 16) + if (tdat > 0 && tdat < 16) { tdat += pal; + if (t256c) { + tdat |= 0x80; + } + } + pixels[dst2++] = palette[tdat]; } } @@ -2140,7 +2283,7 @@ static void draw_breakpoints() ImGui::PopItemWidth(); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); ImGui::SameLine(); - ImGui::Tile(debugger_evaluate_condition(address, bank) ? display_icons::ICON_YES : display_icons::ICON_NO); + ImGui::Tile(debugger_has_valid_expression(address, bank) ? display_icons::ICON_YES : display_icons::ICON_NO); ImGui::PopStyleVar(); ImGui::PopID(); diff --git a/src/overlay/psg_overlay.cpp b/src/overlay/psg_overlay.cpp index a07364b..b0369eb 100644 --- a/src/overlay/psg_overlay.cpp +++ b/src/overlay/psg_overlay.cpp @@ -314,6 +314,7 @@ void draw_debugger_vera_psg() char rate_txt[15]; float rate_hz = rate <= 128 ? (float)SAMPLERATE * rate / 128 : 0; snprintf(rate_txt, 15, "%d (%.0f Hz)", rate, rate_hz); + rate_txt[14] = '\0'; ImGui::SetNextItemWidth(avail / 2 - 48); if (ImGui::SliderInt("Rate", &rate_i, 0, 128, rate_txt, ImGuiSliderFlags_AlwaysClamp)) { pcm_write_rate(rate_i); diff --git a/src/ring_buffer.h b/src/ring_buffer.h index a7b5e43..5e33a80 100644 --- a/src/ring_buffer.h +++ b/src/ring_buffer.h @@ -5,7 +5,7 @@ #include #include -template +template class ring_buffer { public: @@ -24,17 +24,10 @@ class ring_buffer T &allocate() { const size_t index = (m_oldest + m_count) % SIZE; - if constexpr (ALLOW_OVERWRITE) { - if (m_count < SIZE) { - ++m_count; - } else { - m_oldest = (m_oldest + 1) % SIZE; - } - } else { - if (m_count >= SIZE) { - return; - } + if (m_count < SIZE) { ++m_count; + } else { + m_oldest = (m_oldest + 1) % SIZE; } return m_elems[index]; } @@ -137,7 +130,7 @@ class ring_buffer T m_elems[SIZE]; }; -template +template class dynamic_ring_buffer { public: @@ -150,17 +143,10 @@ class dynamic_ring_buffer T &allocate() { const size_t index = (m_oldest + m_count) % m_size; - if constexpr (ALLOW_OVERWRITE) { - if (m_count < m_size) { - ++m_count; - } else { - m_oldest = (m_oldest + 1) % m_size; - } - } else { - if (m_count >= m_size) { - return; - } + if (m_count < m_size) { ++m_count; + } else { + m_oldest = (m_oldest + 1) % m_size; } return m_elems[index]; } diff --git a/src/vera/vera_pcm.cpp b/src/vera/vera_pcm.cpp index 9b6e56b..8bd1efc 100644 --- a/src/vera/vera_pcm.cpp +++ b/src/vera/vera_pcm.cpp @@ -114,29 +114,49 @@ void pcm_render(int16_t *buf, unsigned num_samples) uint8_t old_phase = phase; phase += rate; if ((old_phase & 0x80) != (phase & 0x80)) { - switch ((ctrl >> 4) & 3) { - case 0: { // mono 8-bit - cur_l = (int16_t)read_fifo() << 8; - cur_r = cur_l; - break; - } - case 1: { // stereo 8-bit - cur_l = read_fifo() << 8; - cur_r = read_fifo() << 8; - break; - } - case 2: { // mono 16-bit - cur_l = read_fifo(); - cur_l |= read_fifo() << 8; - cur_r = cur_l; - break; - } - case 3: { // stereo 16-bit - cur_l = read_fifo(); - cur_l |= read_fifo() << 8; - cur_r = read_fifo(); - cur_r |= read_fifo() << 8; - break; + if (fifo_cnt == 0) { + cur_l = 0; + cur_r = 0; + } else { + switch ((ctrl >> 4) & 3) { + case 0: { // mono 8-bit + cur_l = (int16_t)read_fifo() << 8; + cur_r = cur_l; + break; + } + case 1: { // stereo 8-bit + if (fifo_cnt < 2) { + fifo_cnt = 0; + fifo_rdidx = fifo_wridx; + } else { + cur_l = read_fifo() << 8; + cur_r = read_fifo() << 8; + } + break; + } + case 2: { // mono 16-bit + if (fifo_cnt < 2) { + fifo_cnt = 0; + fifo_rdidx = fifo_wridx; + } else { + cur_l = read_fifo(); + cur_l |= read_fifo() << 8; + cur_r = cur_l; + } + break; + } + case 3: { // stereo 16-bit + if (fifo_cnt < 4) { + fifo_cnt = 0; + fifo_rdidx = fifo_wridx; + } else { + cur_l = read_fifo(); + cur_l |= read_fifo() << 8; + cur_r = read_fifo(); + cur_r |= read_fifo() << 8; + } + break; + } } } } diff --git a/src/vera/vera_video.cpp b/src/vera/vera_video.cpp index 0e17a9e..db92192 100644 --- a/src/vera/vera_video.cpp +++ b/src/vera/vera_video.cpp @@ -53,6 +53,11 @@ // When rendering a layer line, we can amortize some of the cost by calculating multiple pixels at a time. #define LAYER_PIXELS_PER_ITERATION 8 +// Version +#define VERA_VERSION_MAJOR 0x00 +#define VERA_VERSION_MINOR 0x03 +#define VERA_VERSION_PATCH 0x02 + static bool is_fullscreen = false; static uint8_t video_ram[0x20000]; @@ -72,7 +77,9 @@ static uint8_t isr; static uint16_t irq_line; static uint8_t reg_layer[2][7]; -static uint8_t reg_composer[8]; + +#define COMPOSER_SLOTS 4*64 +static uint8_t reg_composer[COMPOSER_SLOTS]; static uint8_t layer_line[2][SCREEN_WIDTH]; static uint8_t sprite_line_col[SCREEN_WIDTH]; @@ -93,6 +100,63 @@ static int cheat_mask = 0; static bool log_video = false; static bool shadow_safety_frame[4] = { false, false, true, true }; +//////////////////////////////////////////////////////////// +// FX registers +//////////////////////////////////////////////////////////// +static uint8_t fx_addr1_mode; + +// These are all 16.16 fixed point in the emulator +// even though the VERA uses smaller bit widths +// for the whole and fractional parts. +// +// Sign extension is done manually when assigning negative numbers +// +// Native VERA bit widths are shown below. +static uint32_t fx_x_pixel_increment; // 11.9 fixed point (6.9 without 32x multiplier, 11.4 with 32x multiplier on) +static uint32_t fx_y_pixel_increment; // 11.9 fixed point (6.9 without 32x multiplier, 11.4 with 32x multiplier on) +static uint32_t fx_x_pixel_position; // 11.9 fixed point +static uint32_t fx_y_pixel_position; // 11.9 fixed point + +static uint16_t fx_poly_fill_length; // 10 bits + +static uint32_t fx_affine_tile_base; +static uint32_t fx_affine_map_base; + +static uint8_t fx_affine_map_size; + +static bool fx_4bit_mode; +static bool fx_16bit_hop; +static bool fx_cache_byte_cycling; +static bool fx_cache_fill; +static bool fx_cache_write; +static bool fx_trans_writes; + +static bool fx_2bit_poly; +static bool fx_2bit_poking; + +static bool fx_cache_increment_mode; +static bool fx_cache_nibble_index; +static uint8_t fx_cache_byte_index; +static bool fx_multiplier; +static bool fx_subtract; + +static bool fx_affine_clip; + +static uint8_t fx_16bit_hop_align; + +static bool fx_nibble_bit[2]; +static bool fx_nibble_incr[2]; + +static uint8_t fx_cache[4]; + +static int32_t fx_mult_accumulator; + +static const uint8_t vera_version_string[] = {'V', + VERA_VERSION_MAJOR, + VERA_VERSION_MINOR, + VERA_VERSION_PATCH +}; + static uint8_t framebuffer[SCREEN_WIDTH * SCREEN_HEIGHT * 4]; static const uint16_t default_palette[] = { @@ -125,6 +189,49 @@ void vera_video_reset() reg_composer[5] = 640 >> 2; reg_composer[7] = 480 >> 1; + // Initialize FX registers + fx_addr1_mode = 0; + fx_x_pixel_position = 0x8000; + fx_y_pixel_position = 0x8000; + fx_x_pixel_increment = 0; + fx_y_pixel_increment = 0; + + fx_cache_write = false; + fx_cache_fill = false; + fx_4bit_mode = false; + fx_16bit_hop = false; + fx_subtract = false; + fx_cache_byte_cycling = false; + fx_trans_writes = false; + fx_multiplier = false; + + fx_mult_accumulator = 0; + + fx_2bit_poly = false; + fx_2bit_poking = false; + + fx_cache_nibble_index = 0; + fx_cache_byte_index = 0; + fx_cache_increment_mode = 0; + + fx_cache[0] = 0; + fx_cache[1] = 0; + fx_cache[2] = 0; + fx_cache[3] = 0; + + fx_16bit_hop_align = 0; + + fx_nibble_bit[0] = false; + fx_nibble_bit[1] = false; + fx_nibble_incr[0] = false; + fx_nibble_incr[1] = false; + + fx_poly_fill_length = 0; + fx_affine_tile_base = 0; + fx_affine_map_base = 0; + fx_affine_map_size = 2; + fx_affine_clip = false; + // init sprite data memset(sprite_data, 0, sizeof(sprite_data)); @@ -654,8 +761,11 @@ static void render_layer_line_tile(uint16_t y) uint8_t col_index = (s >> color_shift) & props->color_mask; // Apply Palette Offset - if (palette_offset && col_index > 0 && col_index < 16) { + if (col_index > 0 && col_index < 16) { col_index += palette_offset; + if (props->text_mode_256c) { + col_index |= 0x80; + } } layer_line[layer][i] = col_index; @@ -703,8 +813,11 @@ static void render_layer_line_bitmap(uint16_t y) uint8_t col_index = (s >> (props->first_color_pos - ((xx & props->color_fields_max) << props->color_depth))) & props->color_mask; // Apply Palette Offset - if (palette_offset && col_index > 0 && col_index < 16) { + if (col_index > 0 && col_index < 16) { col_index += palette_offset << 4; + if (props->text_mode_256c) { + col_index |= 0x80; + } } layer_line[layer][i] = col_index; @@ -1003,10 +1116,71 @@ static const int increments[32] = { -640, }; -static uint32_t get_and_inc_address(uint8_t sel) +static uint32_t get_and_inc_address(uint8_t sel, bool write) { uint32_t address = io_addr[sel]; - io_addr[sel] += increments[io_inc[sel]]; + int16_t incr = increments[io_inc[sel]]; + + if (fx_4bit_mode && fx_nibble_incr[sel] && !incr) { + if (fx_nibble_bit[sel]) { + if ((io_inc[sel] & 1) == 0) io_addr[sel] += 1; + fx_nibble_bit[sel] = 0; + } else { + if (io_inc[sel] & 1) io_addr[sel] -= 1; + fx_nibble_bit[sel] = 1; + } + } + + if (sel == 1 && fx_16bit_hop) { + if (incr == 4) { + if (fx_16bit_hop_align == (address & 0x3)) + incr = 1; + else + incr = 3; + } else if (incr == 320) { + if (fx_16bit_hop_align == (address & 0x3)) + incr = 1; + else + incr = 319; + } + } + + io_addr[sel] += incr; + + if (sel == 1 && fx_addr1_mode == 1) { // FX line draw mode + fx_x_pixel_position += fx_x_pixel_increment; + if (fx_x_pixel_position & 0x10000) { + fx_x_pixel_position &= ~0x10000; + if (fx_4bit_mode && fx_nibble_incr[0]) { + if (fx_nibble_bit[1]) { + if ((io_inc[0] & 1) == 0) io_addr[1] += 1; + fx_nibble_bit[1] = 0; + } else { + if (io_inc[0] & 1) io_addr[1] -= 1; + fx_nibble_bit[1] = 1; + } + } + io_addr[1] += increments[io_inc[0]]; + } + } else if (fx_addr1_mode == 2 && write == false) { // FX polygon fill mode + fx_x_pixel_position += fx_x_pixel_increment; + fx_y_pixel_position += fx_y_pixel_increment; + fx_poly_fill_length = ((int32_t) fx_y_pixel_position >> 16) - ((int32_t) fx_x_pixel_position >> 16); + if (sel == 0 && fx_cache_byte_cycling && !fx_cache_fill) { + fx_cache_byte_index = (fx_cache_byte_index + 1) & 3; + } + if (sel == 1) { + if (fx_4bit_mode) { + io_addr[1] = io_addr[0] + (fx_x_pixel_position >> 17); + fx_nibble_bit[1] = (fx_x_pixel_position >> 16) & 1; + } else { + io_addr[1] = io_addr[0] + (fx_x_pixel_position >> 16); + } + } + } else if (sel == 1 && fx_addr1_mode == 3 && write == false) { // FX affine mode + fx_x_pixel_position += fx_x_pixel_increment; + fx_y_pixel_position += fx_y_pixel_increment; + } return address; } @@ -1019,6 +1193,59 @@ uint8_t vera_video_space_read(uint32_t address) return video_ram[address & 0x1FFFF]; } +void fx_affine_prefetch(void) +{ + if (fx_addr1_mode != 3) return; // only if affine mode is selected + + uint32_t address; + uint8_t affine_x_tile = (fx_x_pixel_position >> 19) & 0xff; + uint8_t affine_y_tile = (fx_y_pixel_position >> 19) & 0xff; + uint8_t affine_x_sub_tile = (fx_x_pixel_position >> 16) & 0x07; + uint8_t affine_y_sub_tile = (fx_y_pixel_position >> 16) & 0x07; + + if (!fx_affine_clip) { // wrap + affine_x_tile &= fx_affine_map_size - 1; + affine_y_tile &= fx_affine_map_size - 1; + } + + if (affine_x_tile >= fx_affine_map_size || affine_y_tile >= fx_affine_map_size) { + // We clipped, return value for tile 0 + address = fx_affine_tile_base + (affine_y_sub_tile << (3 - fx_4bit_mode)) + (affine_x_sub_tile >> (uint8_t)fx_4bit_mode); + if (fx_4bit_mode) fx_nibble_bit[1] = 0; + } else { + // Get the address within the tile map + address = fx_affine_map_base + (affine_y_tile * fx_affine_map_size) + affine_x_tile; + // Now translate that to the tile base address + uint8_t affine_tile_idx = vera_video_space_read(address); + address = fx_affine_tile_base + (affine_tile_idx << (6 - fx_4bit_mode)); + // Now add the sub-tile address + address += (affine_y_sub_tile << (3 - fx_4bit_mode)) + (affine_x_sub_tile >> (uint8_t)fx_4bit_mode); + if (fx_4bit_mode) fx_nibble_bit[1] = affine_x_sub_tile & 1; + } + io_addr[1] = address; + io_rddata[1] = vera_video_space_read(address); +} + +void fx_vram_cache_write(uint32_t address, uint8_t value, uint8_t mask) +{ + if (!fx_trans_writes || value > 0) { + switch (mask) { + case 0: + video_ram[address & 0x1FFFF] = value; + break; + case 1: + video_ram[address & 0x1FFFF] = (video_ram[address & 0x1FFFF] & 0x0f) | (value & 0xf0); + break; + case 2: + video_ram[address & 0x1FFFF] = (video_ram[address & 0x1FFFF] & 0xf0) | (value & 0x0f); + break; + case 3: + // Do nothing + break; + } + } +} + void vera_video_space_read_range(uint8_t *dest, uint32_t address, uint32_t size) { address &= 0x1FFFF; @@ -1032,6 +1259,33 @@ void vera_video_space_read_range(uint8_t *dest, uint32_t address, uint32_t size) } } +void fx_vera_video_space_write(uint32_t address, bool nibble, uint8_t value) +{ + if (fx_4bit_mode) { + if (nibble) { + if (!fx_trans_writes || (value & 0x0f) > 0) { + video_ram[address & 0x1FFFF] = (video_ram[address & 0x1FFFF] & 0xf0) | (value & 0x0f); + } + } else { + if (!fx_trans_writes || (value & 0xf0) > 0) { + video_ram[address & 0x1FFFF] = (video_ram[address & 0x1FFFF] & 0x0f) | (value & 0xf0); + } + } + } else { + if (!fx_trans_writes || value > 0) video_ram[address & 0x1FFFF] = value; + } + + if (address >= ADDR_PSG_START && address < ADDR_PSG_END) { + psg_writereg(address & 0x3f, value); + } else if (address >= ADDR_PALETTE_START && address < ADDR_PALETTE_END) { + palette[address & 0x1ff] = value; + video_palette.dirty = true; + } else if (address >= ADDR_SPRDATA_START && address < ADDR_SPRDATA_END) { + sprite_data[(address >> 3) & 0x7f][address & 0x7] = value; + refresh_sprite_properties((address >> 3) & 0x7f); + } +} + void vera_video_space_write(uint32_t address, uint8_t value) { video_ram[address & 0x1FFFF] = value; @@ -1060,7 +1314,7 @@ uint8_t vera_debug_video_read(uint8_t reg) switch (reg & 0x1F) { case 0x00: return io_addr[io_addrsel] & 0xff; case 0x01: return (io_addr[io_addrsel] >> 8) & 0xff; - case 0x02: return (io_addr[io_addrsel] >> 16) | (io_inc[io_addrsel] << 3); + case 0x02: return (io_addr[io_addrsel] >> 16) | (fx_nibble_bit[io_addrsel] << 1) | (fx_nibble_incr[io_addrsel] << 2) | (io_inc[io_addrsel] << 3); case 0x03: case 0x04: return io_rddata[reg - 3]; @@ -1073,8 +1327,58 @@ uint8_t vera_debug_video_read(uint8_t reg) case 0x09: case 0x0A: case 0x0B: - case 0x0C: return reg_composer[reg - 0x09 + (io_dcsel ? 4 : 0)]; - + case 0x0C: { + int i = reg - 0x09 + (io_dcsel << 2); + switch (i) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + // DCSEL = [0,1] with any composer register, or [2] at $9f29 + return reg_composer[i]; + break; + case 0x16: // DCSEL=5, 0x9F2B + if (fx_poly_fill_length >= 768) { + return ((fx_2bit_poly && fx_addr1_mode == 2) ? 0x00 : 0x80); + } + if (fx_4bit_mode) { + if (fx_2bit_poly && fx_addr1_mode == 2) { + return ((fx_y_pixel_position & 0x00008000) >> 8) | + ((fx_x_pixel_position >> 11) & 0x60) | + ((fx_x_pixel_position >> 14) & 0x10) | + ((fx_poly_fill_length & 0x0007) << 1) | + ((fx_x_pixel_position & 0x00008000) >> 15); + } else { + return ((!!(fx_poly_fill_length & 0xfff8)) << 7) | + ((fx_x_pixel_position >> 11) & 0x60) | + ((fx_x_pixel_position >> 14) & 0x10) | + ((fx_poly_fill_length & 0x0007) << 1); + } + } else { + return ((!!(fx_poly_fill_length & 0xfff0)) << 7) | + ((fx_x_pixel_position >> 11) & 0x60) | + ((fx_poly_fill_length & 0x000f) << 1); + } + break; + case 0x17: // DCSEL=5, 0x9F2C + return ((fx_poly_fill_length & 0x03f8) >> 2); + break; + case 0x18: // DCSEL=6, 0x9F29, would affect multiplier + case 0x19: // DCSEL=6, 0x9F2A, would affect multiplier + default: + // The rest of the space is write-only, + // so reading the values out instead returns the version string. + // fall out of the switch + break; + } + return vera_version_string[i % 4]; + break; + } case 0x0D: case 0x0E: case 0x0F: @@ -1109,14 +1413,38 @@ uint8_t vera_video_read(uint8_t reg) switch (reg & 0x1F) { case 0x00: return io_addr[io_addrsel] & 0xff; case 0x01: return (io_addr[io_addrsel] >> 8) & 0xff; - case 0x02: return (io_addr[io_addrsel] >> 16) | (io_inc[io_addrsel] << 3); + case 0x02: return (io_addr[io_addrsel] >> 16) | (fx_nibble_bit[io_addrsel] << 1) | (fx_nibble_incr[io_addrsel] << 2) | (io_inc[io_addrsel] << 3); case 0x03: case 0x04: { - uint32_t address = get_and_inc_address(reg - 3); + uint32_t address = get_and_inc_address(reg - 3, false); uint8_t value = io_rddata[reg - 3]; - io_rddata[reg - 3] = vera_video_space_read(io_addr[reg - 3]); + + if (reg == 4 && fx_addr1_mode == 3) + fx_affine_prefetch(); + else + io_rddata[reg - 3] = vera_video_space_read(io_addr[reg - 3]); + + if (fx_cache_fill) { + if (fx_4bit_mode) { + if (fx_cache_nibble_index) { + fx_cache[fx_cache_byte_index] = (fx_cache[fx_cache_byte_index] & 0xf0) | (value & 0x0f); + fx_cache_nibble_index = 0; + fx_cache_byte_index = ((fx_cache_byte_index + 1) & 0x3); + } else { + fx_cache[fx_cache_byte_index] = (fx_cache[fx_cache_byte_index] & 0x0f) | (value & 0xf0); + fx_cache_nibble_index = 1; + } + } else { + fx_cache[fx_cache_byte_index] = value; + if (fx_cache_increment_mode) + fx_cache_byte_index = (fx_cache_byte_index & 0x2) | ((fx_cache_byte_index + 1) & 0x1); + else + fx_cache_byte_index = ((fx_cache_byte_index + 1) & 0x3); + } + } + if (log_video) { printf("READ video_space[$%X] = $%02X\n", address, value); @@ -1131,8 +1459,71 @@ uint8_t vera_video_read(uint8_t reg) case 0x09: case 0x0A: case 0x0B: - case 0x0C: return reg_composer[reg - 0x09 + (io_dcsel ? 4 : 0)]; - + case 0x0C: { + int i = reg - 0x09 + (io_dcsel << 2); + switch (i) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + // DCSEL = [0,1] with any composer register, or [2] at $9f29 + return reg_composer[i]; + break; + case 0x16: // DCSEL=5, 0x9F2B + if (fx_poly_fill_length >= 768) { + return ((fx_2bit_poly && fx_addr1_mode == 2) ? 0x00 : 0x80); + } + if (fx_4bit_mode) { + if (fx_2bit_poly && fx_addr1_mode == 2) { + return ((fx_y_pixel_position & 0x00008000) >> 8) | + ((fx_x_pixel_position >> 11) & 0x60) | + ((fx_x_pixel_position >> 14) & 0x10) | + ((fx_poly_fill_length & 0x0007) << 1) | + ((fx_x_pixel_position & 0x00008000) >> 15); + } else { + return ((!!(fx_poly_fill_length & 0xfff8)) << 7) | + ((fx_x_pixel_position >> 11) & 0x60) | + ((fx_x_pixel_position >> 14) & 0x10) | + ((fx_poly_fill_length & 0x0007) << 1); + } + } else { + return ((!!(fx_poly_fill_length & 0xfff0)) << 7) | + ((fx_x_pixel_position >> 11) & 0x60) | + ((fx_poly_fill_length & 0x000f) << 1); + } + break; + case 0x17: // DCSEL=5, 0x9F2C + return ((fx_poly_fill_length & 0x03f8) >> 2); + break; + case 0x18: // DCSEL=6, 0x9F29 + fx_mult_accumulator = 0; + // fall out of the switch + break; + case 0x19: { + // DCSEL=6, 0x9F2A + ; // <- avoids the error in some compilers about a declaration after a label + int32_t m_result = (int16_t)((fx_cache[1] << 8) | fx_cache[0]) * (int16_t)((fx_cache[3] << 8) | fx_cache[2]); + if (fx_subtract) + fx_mult_accumulator -= m_result; + else + fx_mult_accumulator += m_result; + // fall out of the switch + break; + } + default: + // The rest of the space is write-only, + // so reading the values out instead returns the version string. + // fall out of the switch + break; + } + return vera_version_string[i % 4]; + break; + } case 0x0D: case 0x0E: case 0x0F: @@ -1167,7 +1558,14 @@ void vera_video_write(uint8_t reg, uint8_t value) // printf("ioregisters[%d] = $%02X\n", reg, value); switch (reg & 0x1F) { case 0x00: - io_addr[io_addrsel] = (io_addr[io_addrsel] & 0x1ff00) | value; + if (fx_2bit_poly && fx_4bit_mode && fx_addr1_mode == 2 && io_addrsel == 1) { + fx_2bit_poking = true; + io_addr[1] = (io_addr[1] & 0x1fffc) | (value & 0x3); + } else { + io_addr[io_addrsel] = (io_addr[io_addrsel] & 0x1ff00) | value; + if (fx_16bit_hop && io_addrsel == 1) + fx_16bit_hop_align = value & 3; + } io_rddata[io_addrsel] = vera_video_space_read(io_addr[io_addrsel]); break; case 0x01: @@ -1176,16 +1574,74 @@ void vera_video_write(uint8_t reg, uint8_t value) break; case 0x02: io_addr[io_addrsel] = (io_addr[io_addrsel] & 0x0ffff) | ((value & 0x1) << 16); + fx_nibble_bit[io_addrsel] = (value >> 1) & 0x1; + fx_nibble_incr[io_addrsel] = (value >> 2) & 0x1; io_inc[io_addrsel] = value >> 3; io_rddata[io_addrsel] = vera_video_space_read(io_addr[io_addrsel]); break; case 0x03: case 0x04: { - uint32_t address = get_and_inc_address(reg - 3); + if (fx_2bit_poking && fx_addr1_mode) { + fx_2bit_poking = false; + uint8_t mask = value >> 6; + switch (mask) { + case 0x00: + video_ram[io_addr[1] & 0x1FFFF] = (fx_cache[fx_cache_byte_index] & 0xc0) | (io_rddata[1] & 0x3f); + break; + case 0x01: + video_ram[io_addr[1] & 0x1FFFF] = (fx_cache[fx_cache_byte_index] & 0x30) | (io_rddata[1] & 0xcf); + break; + case 0x02: + video_ram[io_addr[1] & 0x1FFFF] = (fx_cache[fx_cache_byte_index] & 0x0c) | (io_rddata[1] & 0xf3); + break; + case 0x03: + video_ram[io_addr[1] & 0x1FFFF] = (fx_cache[fx_cache_byte_index] & 0x03) | (io_rddata[1] & 0xfc); + break; + } + break; // break out of the enclosing switch statement early, too + } + bool nibble = fx_nibble_bit[reg - 3]; + uint32_t address = get_and_inc_address(reg - 3, true); if (log_video) { printf("WRITE video_space[$%X] = $%02X\n", address, value); } - vera_video_space_write(address, value); + + if (fx_cache_write) { + address &= 0x1fffc; + if (fx_cache_byte_cycling) { + fx_vram_cache_write(address+0, fx_cache[fx_cache_byte_index], value & 0x03); + fx_vram_cache_write(address+1, fx_cache[fx_cache_byte_index], (value >> 2) & 0x03); + fx_vram_cache_write(address+2, fx_cache[fx_cache_byte_index], (value >> 4) & 0x03); + fx_vram_cache_write(address+3, fx_cache[fx_cache_byte_index], value >> 6); + } else { + if (fx_multiplier) { + int32_t m_result = (int16_t)((fx_cache[1] << 8) | fx_cache[0]) * (int16_t)((fx_cache[3] << 8) | fx_cache[2]); + if (fx_subtract) + m_result = fx_mult_accumulator - m_result; + else + m_result = fx_mult_accumulator + m_result; + fx_vram_cache_write(address+0, (m_result) & 0xff, value & 0x03); + fx_vram_cache_write(address+1, (m_result >> 8) & 0xff, (value >> 2) & 0x03); + fx_vram_cache_write(address+2, (m_result >> 16) & 0xff, (value >> 4) & 0x03); + fx_vram_cache_write(address+3, (m_result >> 24) & 0xff, value >> 6); + } else { + fx_vram_cache_write(address+0, fx_cache[0], value & 0x03); + fx_vram_cache_write(address+1, fx_cache[1], (value >> 2) & 0x03); + fx_vram_cache_write(address+2, fx_cache[2], (value >> 4) & 0x03); + fx_vram_cache_write(address+3, fx_cache[3], value >> 6); + } + } + } else { + if (fx_cache_byte_cycling) { + if (fx_4bit_mode) { + fx_vram_cache_write(address, fx_cache[fx_cache_byte_index], nibble+1); + } else { + fx_vram_cache_write(address, fx_cache[fx_cache_byte_index], 0); + } + } else { + fx_vera_video_space_write(address, nibble, value); // Normal write + } + } io_rddata[reg - 3] = vera_video_space_read(io_addr[reg - 3]); break; @@ -1194,7 +1650,7 @@ void vera_video_write(uint8_t reg, uint8_t value) if (value & 0x80) { vera_video_reset(); } - io_dcsel = (value >> 1) & 1; + io_dcsel = (value >> 1) & 0x3f; io_addrsel = value & 1; break; case 0x06: @@ -1212,7 +1668,7 @@ void vera_video_write(uint8_t reg, uint8_t value) case 0x0A: case 0x0B: case 0x0C: { - int i = reg - 0x09 + (io_dcsel ? 4 : 0); + int i = reg - 0x09 + (io_dcsel << 2); if (i == 0) { // interlace field bit is read-only reg_composer[0] = (reg_composer[0] & ~0x7f) | (value & 0x7f); @@ -1220,9 +1676,103 @@ void vera_video_write(uint8_t reg, uint8_t value) } else { reg_composer[i] = value; } + switch (i) { + case 0x08: // DCSEL=2, $9F29 + fx_addr1_mode = value & 0x03; + fx_4bit_mode = (value & 0x04) >> 2; + fx_16bit_hop = (value & 0x08) >> 3; + fx_cache_byte_cycling = (value & 0x10) >> 4; + fx_cache_fill = (value & 0x20) >> 5; + fx_cache_write = (value & 0x40) >> 6; + fx_trans_writes = (value & 0x80) >> 7; + break; + case 0x09: // DCSEL=2, $9F2A + fx_affine_tile_base = (value & 0xfc) << 9; + fx_affine_clip = (value & 0x02) >> 1; + fx_2bit_poly = (value & 0x01); + break; + case 0x0a: // DCSEL=2, $9F2B + fx_affine_map_base = (value & 0xfc) << 9; + fx_affine_map_size = 2 << ((value & 0x03) << 1); + break; + case 0x0b: // DCSEL=2, $9F2C + fx_cache_increment_mode = value & 0x01; + fx_cache_nibble_index = (value & 0x02) >> 1; + fx_cache_byte_index = (value & 0x0c) >> 2; + fx_multiplier = (value & 0x10) >> 4; + fx_subtract = (value & 0x20) >> 5; + if (value & 0x40) { // accumulate + int32_t m_result = (int16_t)((fx_cache[1] << 8) | fx_cache[0]) * (int16_t)((fx_cache[3] << 8) | fx_cache[2]); + if (fx_subtract) + fx_mult_accumulator -= m_result; + else + fx_mult_accumulator += m_result; + } + if (value & 0x80) { // reset accumulator + fx_mult_accumulator = 0; + } + break; + case 0x0c: // DCSEL=3, $9F29 + fx_x_pixel_increment = ((((reg_composer[0x0d] & 0x7f) << 15) + (reg_composer[0x0c] << 7)) // base value + | ((reg_composer[0x0d] & 0x40) ? 0xffc00000 : 0)) // sign extend if negative + << 5*(!!(reg_composer[0x0d] & 0x80)); // multiply by 32 if flag set + break; + case 0x0d: // DCSEL=3, $9F2A + fx_x_pixel_increment = ((((reg_composer[0x0d] & 0x7f) << 15) + (reg_composer[0x0c] << 7)) // base value + | ((reg_composer[0x0d] & 0x40) ? 0xffc00000 : 0)) // sign extend if negative + << 5*(!!(reg_composer[0x0d] & 0x80)); // multiply by 32 if flag set + // Reset subpixel to 0.5 + fx_x_pixel_position = (fx_x_pixel_position & 0x07ff0000) | 0x00008000; + break; + case 0x0e: // DCSEL=3, $9F2B + fx_y_pixel_increment = ((((reg_composer[0x0f] & 0x7f) << 15) + (reg_composer[0x0e] << 7)) // base value + | ((reg_composer[0x0f] & 0x40) ? 0xffc00000 : 0)) // sign extend if negative + << 5*(!!(reg_composer[0x0f] & 0x80)); // multiply by 32 if flag set + break; + case 0x0f: // DCSEL=3, $9F2C + fx_y_pixel_increment = ((((reg_composer[0x0f] & 0x7f) << 15) + (reg_composer[0x0e] << 7)) // base value + | ((reg_composer[0x0f] & 0x40) ? 0xffc00000 : 0)) // sign extend if negative + << 5*(!!(reg_composer[0x0f] & 0x80)); // multiply by 32 if flag set + // Reset subpixel to 0.5 + fx_y_pixel_position = (fx_y_pixel_position & 0x07ff0000) | 0x00008000; + break; + case 0x10: // DCSEL=4, $9F29 + fx_x_pixel_position = (fx_x_pixel_position & 0x0700ff80) | (value << 16); + fx_affine_prefetch(); + break; + case 0x11: // DCSEL=4, $9F2A + fx_x_pixel_position = (fx_x_pixel_position & 0x00ffff00) | ((value & 0x7) << 24) | (value & 0x80); + fx_affine_prefetch(); + break; + case 0x12: // DCSEL=4, $9F2B + fx_y_pixel_position = (fx_y_pixel_position & 0x0700ff80) | (value << 16); + fx_affine_prefetch(); + break; + case 0x13: // DCSEL=4, $9F2C + fx_y_pixel_position = (fx_y_pixel_position & 0x00ffff00) | ((value & 0x7) << 24) | (value & 0x80); + fx_affine_prefetch(); + break; + case 0x14: // DCSEL=5, $9F29 + fx_x_pixel_position = (fx_x_pixel_position & 0x07ff0080) | (value << 8); + break; + case 0x15: // DCSEL=5, $9F2A + fx_y_pixel_position = (fx_y_pixel_position & 0x07ff0080) | (value << 8); + break; + case 0x18: // DCSEL=6, $9F29 + fx_cache[0] = value; + break; + case 0x19: // DCSEL=6, $9F2A + fx_cache[1] = value; + break; + case 0x1a: // DCSEL=6, $9F2B + fx_cache[2] = value; + break; + case 0x1b: // DCSEL=6, $9F2C + fx_cache[3] = value; + break; + } break; } - case 0x0D: case 0x0E: case 0x0F: @@ -1565,4 +2115,4 @@ vera_video_rect vera_video_get_scan_visible() VGA_Y_OFFSET, VGA_Y_OFFSET + SCREEN_HEIGHT }; } -} \ No newline at end of file +} diff --git a/tools/generate_sdl_to_keynum_table.cpp b/tools/generate_sdl_to_keynum_table.cpp index 58a96f3..f1cadff 100644 --- a/tools/generate_sdl_to_keynum_table.cpp +++ b/tools/generate_sdl_to_keynum_table.cpp @@ -10,7 +10,7 @@ using namespace std; #define EXTENDED_FLAG 0x100 -#define ESC_IS_BREAK /* if enabled, Esc sends Break/Pause key instead of Esc */ +//#define ESC_IS_BREAK /* if enabled, Esc sends Break/Pause key instead of Esc */ uint16_t keynum_from_SDL_Scancode(SDL_Scancode scancode) {