From 1922f54b88e4d2cb953f78cb66db6b254c8242f3 Mon Sep 17 00:00:00 2001 From: Jon Bright Date: Wed, 19 Jan 2022 09:56:22 +0100 Subject: [PATCH 1/6] Implement the zp,x version of BIT that's present on 65C02s such as the one in the eater.net kit. --- src/opcode_handlers/logical.h | 7 +++++++ src/opcodes.h | 1 + 2 files changed, 8 insertions(+) diff --git a/src/opcode_handlers/logical.h b/src/opcode_handlers/logical.h index 5747f38..c52007d 100644 --- a/src/opcode_handlers/logical.h +++ b/src/opcode_handlers/logical.h @@ -167,6 +167,13 @@ case BIT_ZP: set_flag(m, FLAG_NEGATIVE, t1 & 0x80); break; +case BIT_ZPX: + t1 = read_byte(m, ZP(NEXT_BYTE(m) + m->x)); //m->mem[ZP(NEXT_BYTE(m) + m->x)]; + set_flag(m, FLAG_ZERO, !(t1 & m->ac)); + set_flag(m, FLAG_OVERFLOW, t1 & 0x40); + set_flag(m, FLAG_NEGATIVE, t1 & 0x80); + break; + case RMB0: r1 = ZP(NEXT_BYTE(m)); //m->mem[r1] &= ~0x01; diff --git a/src/opcodes.h b/src/opcodes.h index 537abd8..fae72d1 100644 --- a/src/opcodes.h +++ b/src/opcodes.h @@ -65,6 +65,7 @@ #define BIT_AB 0x2C #define BIT_ZP 0x24 +#define BIT_ZPX 0x34 #define BMI_REL 0x30 #define BNE_REL 0xD0 From 454e2f20afd977d0ec10f8d01dff551728f1e61e Mon Sep 17 00:00:00 2001 From: Jon Bright Date: Wed, 19 Jan 2022 10:12:13 +0100 Subject: [PATCH 2/6] Allow reading the LCD busy flag before the LCD is initialized. This works on real hardware and is also used by Ben Eater's example programs (which always check the busy flag before sending an instruction). --- src/lcd.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lcd.c b/src/lcd.c index cb15637..a7f9089 100644 --- a/src/lcd.c +++ b/src/lcd.c @@ -33,6 +33,11 @@ void process_input(lcd* l, bool enable, bool rwb, bool data, uint8_t input) if (enable && !l->enable_latch) { // rising edge on enable l->enable_latch = true; trace_emu("LCD rising edge on enable\n"); + if (rwb && !data) { + // There's no command to check - this is read busy flag + trace_emu("LCD read busy\n"); + process_command(l, rwb, l->data); + } if (!l->initialized && !(input & 0xc0) && (input & CMD_FUNCTION_SET) && !rwb) { trace_emu("LCD initializing \n"); l->initialized = true; From bb9a086556e02b80cf7d4a458c160aab11aca563 Mon Sep 17 00:00:00 2001 From: Jon Bright Date: Tue, 25 Jan 2022 21:01:26 +0000 Subject: [PATCH 3/6] Add a "turbo" mode, running as fast as we can, for when you just want the program to execute. Switching to other modes (fast, slow, step) still works from this mode. --- src/cpu.h | 7 ++++--- src/gui.c | 13 +++++++++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/cpu.h b/src/cpu.h index 616e898..52b1eab 100644 --- a/src/cpu.h +++ b/src/cpu.h @@ -24,9 +24,10 @@ #define EMU_FLAG_WAIT_FOR_INTERRUPT 0x02 #define CLOCK_SPRINT 0x00 -#define CLOCK_FAST 0x01 -#define CLOCK_SLOW 0x02 -#define CLOCK_STEP 0x04 +#define CLOCK_TURBO 0x01 +#define CLOCK_FAST 0x02 +#define CLOCK_SLOW 0x04 +#define CLOCK_STEP 0x08 typedef struct { // clock mode diff --git a/src/gui.c b/src/gui.c index b20e91c..78c16d9 100644 --- a/src/gui.c +++ b/src/gui.c @@ -180,7 +180,7 @@ void update_gui(cpu *m) { m->sr & 0x02 ? 'Z' : '-', m->sr & 0x01 ? 'C' : '-', m->cycle); - mvwprintw(wnd_monitor_content, 3, 0, "Clock mode: %s", m->clock_mode == CLOCK_FAST ? "FAST" : m->clock_mode == CLOCK_SLOW ? "SLOW" : "STEP"); + mvwprintw(wnd_monitor_content, 3, 0, "Clock mode: %s", m->clock_mode == CLOCK_TURBO ? "TURBO" : m->clock_mode == CLOCK_FAST ? "FAST " : m->clock_mode == CLOCK_SLOW ? "SLOW " : "STEP "); wrefresh(wnd_monitor_content); // populate memory monitor @@ -224,6 +224,10 @@ void update_gui(cpu *m) { switch (m->clock_mode) { case CLOCK_SPRINT: + case CLOCK_TURBO: + read = getch(); + keep_going = true; + break; case CLOCK_FAST: halfdelay(1); read = getch(); @@ -260,6 +264,11 @@ void update_gui(cpu *m) { m->clock_mode = CLOCK_FAST; } break; + case KEY_F(9): // F9 + if (!(m->clock_mode == CLOCK_SPRINT)) { + m->clock_mode = CLOCK_TURBO; + } + break; case 10: m->k->key_enter = true; keep_going = true; @@ -307,4 +316,4 @@ void update_gui(cpu *m) { } } } while (!keep_going && m->clock_mode != CLOCK_SPRINT); -} \ No newline at end of file +} From bde179784e984bffcab0d01cafb317c0257951b6 Mon Sep 17 00:00:00 2001 From: Jon Bright Date: Wed, 26 Jan 2022 18:31:09 +0000 Subject: [PATCH 4/6] Make the turbo mode work even after one of the other continuous modes has started. --- src/gui.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui.c b/src/gui.c index 78c16d9..c922a49 100644 --- a/src/gui.c +++ b/src/gui.c @@ -225,6 +225,7 @@ void update_gui(cpu *m) { switch (m->clock_mode) { case CLOCK_SPRINT: case CLOCK_TURBO: + cbreak(); read = getch(); keep_going = true; break; From 71497c799cb2f3f6ccb451ad67e55886ff2bfa5c Mon Sep 17 00:00:00 2001 From: Jon Bright Date: Sun, 30 Jan 2022 18:20:15 +0000 Subject: [PATCH 5/6] Fix STX when used on the zero page - it was storing A, and now correctly stores X. --- src/opcode_handlers/store.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/opcode_handlers/store.h b/src/opcode_handlers/store.h index a6aa83e..31f5ea0 100644 --- a/src/opcode_handlers/store.h +++ b/src/opcode_handlers/store.h @@ -54,7 +54,7 @@ case STA_INZP: case STX_ZP: r1 = ZP(NEXT_BYTE(m)); - write_byte(m, r1, m->ac); //m->mem[r1] = m->x; + write_byte(m, r1, m->x); //m->mem[r1] = m->x; mark_dirty(m, r1); break; From 2ee997b332840e4b3d43481a0b5a440923738106 Mon Sep 17 00:00:00 2001 From: Jon Bright Date: Thu, 17 Feb 2022 09:36:12 +0000 Subject: [PATCH 6/6] Add "Run to NOP" and "Run to return" modes, triggered by keys N and R. The first switches to TURBO clock speed and runs at that until it encounters a NOP, at which point it switches to STEP. It can't be triggered when the current instruction is a NOP. The second does the same thing, but switches back to STEP when encountering an RTS. For each JSR it encounters in the interim, it'll wait for an additional RTS, so the RTS where it switches to STEP should be the one from the current subroutine. It acts weirdly when triggered while the current instruction is a JSR for reasons I should look into but haven't yet. Both of these should potentially be homed in emu.c rather than gui.c, but they work for my purposes. --- src/cpu.c | 4 +++- src/cpu.h | 6 ++++++ src/gui.c | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/cpu.c b/src/cpu.c index 1b9ebec..24ff8f1 100644 --- a/src/cpu.c +++ b/src/cpu.c @@ -18,6 +18,8 @@ cpu * new_cpu() { m->l = new_lcd(); m->k = new_keys(); m->cycle = 0; + m->run_to_nop = false; + m->run_to_ret = 0; return m; } @@ -26,4 +28,4 @@ void destroy_cpu(cpu* m) { destroy_lcd(m->l); destroy_keys(m->k); free(m); -} \ No newline at end of file +} diff --git a/src/cpu.h b/src/cpu.h index 52b1eab..797ea12 100644 --- a/src/cpu.h +++ b/src/cpu.h @@ -66,6 +66,12 @@ typedef struct { lcd* l; // keys keys* k; + // whether we should switch to clock mode STEP when the next NOP is encountered + bool run_to_nop; + // if >0, will be incremented/decremented when encountering JSR/RTS respectively and we'll switch to + // STEP when it's decremented to zero, allowing "run until return" + int run_to_ret; + } cpu; cpu * new_cpu(); diff --git a/src/gui.c b/src/gui.c index c922a49..2501f51 100644 --- a/src/gui.c +++ b/src/gui.c @@ -170,6 +170,21 @@ void update_gui(cpu *m) { // start by populating the monitor mvwprintw(wnd_monitor_content, 0, 0, "PC: %04x, OP: %02x (%s)", m->pc_actual, m->opcode, translate_opcode(m->opcode)); + if (m->run_to_nop && m->opcode == 0xea) { + // 0xea is NOP - we use this as a breakpoint + m->clock_mode = CLOCK_STEP; + m->run_to_nop = false; + } else if (m->run_to_ret && m->opcode == 0x60) { + // 0x60 is RTS + m->run_to_ret--; + if (!m->run_to_ret) { + // We're returning from the frame where run-to-ret was requested + m->clock_mode = CLOCK_STEP; + } + } else if (m->run_to_ret && m->opcode == 0x20) { + // 0x20 is JSR. We want to see an additional return after this, then. + m->run_to_ret++; + } mvwprintw(wnd_monitor_content, 1, 0, "ACC: %02x, X: %02x, Y: %02x, SP: %02x", m->ac, m->x, m->y, m->sp); mvwprintw(wnd_monitor_content, 2, 0, "SR: %c%c-%c%c%c%c%c, cycle: %08x", m->sr & 0x80 ? 'N' : '-', @@ -181,6 +196,16 @@ void update_gui(cpu *m) { m->sr & 0x01 ? 'C' : '-', m->cycle); mvwprintw(wnd_monitor_content, 3, 0, "Clock mode: %s", m->clock_mode == CLOCK_TURBO ? "TURBO" : m->clock_mode == CLOCK_FAST ? "FAST " : m->clock_mode == CLOCK_SLOW ? "SLOW " : "STEP "); + if (m->run_to_nop) { + mvwprintw(wnd_monitor_content, 3, 18, "RTN"); + } else { + mvwprintw(wnd_monitor_content, 3, 18, " "); + } + if (m->run_to_ret) { + mvwprintw(wnd_monitor_content, 3, 22, "RTR %d", m->run_to_ret); + } else { + mvwprintw(wnd_monitor_content, 3, 22, " "); + } wrefresh(wnd_monitor_content); // populate memory monitor @@ -314,6 +339,18 @@ void update_gui(cpu *m) { memory_start = 0xfe; } break; + case 'n': + if (!(m->clock_mode == CLOCK_SPRINT)) { + m->clock_mode = CLOCK_TURBO; + m->run_to_nop = true; + } + break; + case 'r': + if (!(m->clock_mode == CLOCK_SPRINT)) { + m->clock_mode = CLOCK_TURBO; + m->run_to_ret = 1; + } + break; } } } while (!keep_going && m->clock_mode != CLOCK_SPRINT);