Skip to content

Commit

Permalink
various fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
ziplantil committed Oct 19, 2022
1 parent 4687285 commit d368df6
Show file tree
Hide file tree
Showing 18 changed files with 176 additions and 96 deletions.
1 change: 1 addition & 0 deletions NOTES
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Notes about w65c02s and its development:
* For some reason, -O2 and -O3 are consistently *slower* on gcc than -O1. This
seems to occur due to -ftree-tail-merge, which causes extra range checks to
be added to switch statements, despite __builtin_unreachable() being used.
Is this a gcc bug?
* The case of W65C02SCE_COARSE=0 could be optimized greatly by eliminating
the "cont = 0" branch entirely, but that does not seem possible in C89, even
with compiler extensions.
23 changes: 22 additions & 1 deletion docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,23 @@ write, STP, etc.) will result in undefined behavior.
* **Parameter** `cycles`: The number of cycles to run
* **Return value**: The number of cycles that were actually run

## w65c02s_step_instruction
Runs the CPU for one instruction, or if an instruction is already running,
finishes that instruction.

```c
unsigned long w65c02s_step_instruction(struct w65c02s_cpu *cpu);
```
Prefer using w65c02s_run_instructions or w65c02s_run_cycles instead if you can
to run many instructions at once.
This function is not reentrant. Calling it from a callback (for a memory read,
write, STP, etc.) will result in undefined behavior.
* **Parameter** `cpu`: The CPU instance to run
* **Return value**: The number of cycles that were actually run
## w65c02s_run_instructions
Runs the CPU for the given number of instructions.
Expand Down Expand Up @@ -194,7 +211,11 @@ Triggers a CPU reset.
void w65c02s_reset(struct w65c02s_cpu *cpu);
```
The RESET will begin executing before the next instruction.
The RESET will begin executing before the next instruction. The RESET does not
behave exactly like in the original chip, and will not carry out any extra
cycles in between the end of the instruction and beginning of the reset (like
some chips do). There is also no requirement that RESET be asserted before the
last cycle, like with NMI or IRQ.
* **Parameter** `cpu`: The CPU instance
Expand Down
1 change: 1 addition & 0 deletions docs/generate.py
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#!/usr/bin/python3
import re
import textwrap

Expand Down
2 changes: 1 addition & 1 deletion src/decode.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*******************************************************************************
w65c02sce -- cycle-accurate C emulator of the WDC 65C02S
by ziplantil 2022 -- under the CC0 license
version: 2022-10-18
version: 2022-10-19
decode.c - instruction decoder
*******************************************************************************/
Expand Down
2 changes: 1 addition & 1 deletion src/decode.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*******************************************************************************
w65c02sce -- cycle-accurate C emulator of the WDC 65C02S
by ziplantil 2022 -- under the CC0 license
version: 2022-10-18
version: 2022-10-19
decode.h - instruction decoder
*******************************************************************************/
Expand Down
136 changes: 79 additions & 57 deletions src/execute.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*******************************************************************************
w65c02sce -- cycle-accurate C emulator of the WDC 65C02S
by ziplantil 2022 -- under the CC0 license
version: 2022-10-18
version: 2022-10-19
execute.c - instruction execution unit
*******************************************************************************/
Expand All @@ -20,7 +20,9 @@ INLINE void handle_reset(struct w65c02s_cpu *cpu) {
/* do RESET */
cpu->in_rst = 1;
cpu->in_nmi = cpu->in_irq = 0;
cpu->cpu_state = CPU_STATE_RUN;
CPU_STATE_INSERT(cpu, CPU_STATE_RUN);
CPU_STATE_CLEAR_NMI(cpu);
CPU_STATE_CLEAR_IRQ(cpu);
SET_P(P_A1, 1);
SET_P(P_B, 1);
}
Expand Down Expand Up @@ -58,9 +60,33 @@ INLINE void handle_end_of_instruction(struct w65c02s_cpu *cpu) {
#endif
}

#if !W65C02SCE_COARSE_CYCLE_COUNTER
#define SPENT_CYCLE ++cpu->total_cycles
#else
#define SPENT_CYCLE
#endif

#define STARTING_INSTRUCTION 0
#define CONTINUE_INSTRUCTION 1

static int handle_stp_wai_i(struct w65c02s_cpu *cpu) {
switch (CPU_STATE_EXTRACT(cpu)) {
case CPU_STATE_WAIT:
/* if there is an IRQ or NMI, latch it immediately and continue */
if (cpu->int_trig) {
w65c02si_irq_latch(cpu);
CPU_STATE_INSERT(cpu, CPU_STATE_RUN);
return 0;
}
case CPU_STATE_STOP:
/* spurious read to waste a cycle */
w65c02si_stall(cpu);
SPENT_CYCLE;
return 1;
}
return 0;
}

#if !W65C02SCE_COARSE

static int handle_stp_wai_c(struct w65c02s_cpu *cpu) {
Expand All @@ -79,9 +105,8 @@ static int handle_stp_wai_c(struct w65c02s_cpu *cpu) {
}
case CPU_STATE_STOP:
for (;;) {
if (CPU_STATE_EXTRACT(cpu) == CPU_STATE_RESET) {
if (CPU_STATE_EXTRACT(cpu) == CPU_STATE_RESET)
return 0;
}
w65c02si_stall(cpu);
if (CYCLE_CONDITION) return 1;
}
Expand All @@ -104,47 +129,29 @@ INTERNAL unsigned long w65c02si_execute_c(struct w65c02s_cpu *cpu,
if (cpu->cycl) cpu->cycl += maximum_cycles;
return maximum_cycles;
}
handle_end_of_instruction(cpu);
} else if (UNLIKELY(cpu->cpu_state != CPU_STATE_RUN)) {
/* we are starting a new instruction and state isn't RUN.
we don't want STEP to trigget yet - we didn't run an instruction! */
goto bypass_step;
} else {
/* we are starting a new instruction and state is RUN. */
goto next_instruction;
goto end_of_instruction;
}

/* new instruction, handle special states now */
if (UNLIKELY(cpu->cpu_state != CPU_STATE_RUN))
goto check_special_state;

for (;;) {
unsigned long cyclecount;
#if W65C02SCE_COARSE_CYCLE_COUNTER
#define CYCLES_NOW (maximum_cycles - cpu->left_cycles)
#else
#define CYCLES_NOW (cpu->total_cycles - (cpu->target_cycles - maximum_cycles))
#endif
if (UNLIKELY(cpu->cpu_state != CPU_STATE_RUN)) {
if (cpu->cpu_state & CPU_STATE_STEP) {
cpu->cycl = 0;
return CYCLES_NOW;
}
bypass_step:
if (handle_stp_wai_c(cpu)) return CYCLES_NOW;
if (handle_interrupt(cpu)) goto decoded;
}

next_instruction:
w65c02si_decode(cpu, READ(cpu->pc++));
w65c02si_prerun_mode(cpu);
cpu->cycl = 1; /* interrupts don't need this -- no cycle skip in BRK */

decoded:
#if W65C02SCE_COARSE_CYCLE_COUNTER
cyclecount = --cpu->left_cycles;
if (UNLIKELY(!cyclecount)) break;
cyclecount = cpu->left_cycles;
#else
cyclecount = ++cpu->total_cycles;
if (UNLIKELY(cyclecount == cpu->target_cycles)) break;
cyclecount = cpu->total_cycles;
#endif
if (UNLIKELY(CYCLE_CONDITION))
return maximum_cycles;

cpu->cycl = 1;
if (UNLIKELY(w65c02si_run_mode(cpu, STARTING_INSTRUCTION))) {
if (cpu->cycl) {
#if W65C02SCE_COARSE_CYCLE_COUNTER
Expand All @@ -157,33 +164,46 @@ INTERNAL unsigned long w65c02si_execute_c(struct w65c02s_cpu *cpu,
}
return maximum_cycles;
}

#if W65C02SCE_COARSE_CYCLE_COUNTER
#define CYCLES_NOW (maximum_cycles - cpu->left_cycles)
#else
#define CYCLES_NOW (maximum_cycles - (cpu->target_cycles - cpu->total_cycles))
#endif
end_of_instruction:
handle_end_of_instruction(cpu);
if (UNLIKELY(cpu->cpu_state != CPU_STATE_RUN)) {
check_special_state:
if (handle_stp_wai_c(cpu)) return CYCLES_NOW;
if (handle_interrupt(cpu)) goto decoded;
}
}
return maximum_cycles;
}

#else /* W65C02SCE_COARSE */

static int handle_stp_wai_i(struct w65c02s_cpu *cpu) {
switch (CPU_STATE_EXTRACT(cpu)) {
case CPU_STATE_WAIT:
/* if there is an IRQ or NMI, latch it immediately and continue */
if (cpu->int_trig) {
w65c02si_irq_latch(cpu);
CPU_STATE_INSERT(cpu, CPU_STATE_RUN);
return 0;
}
case CPU_STATE_STOP:
/* spurious read to waste a cycle */
w65c02si_stall(cpu);
#if !W65C02SCE_COARSE_CYCLE_COUNTER
++cpu->total_cycles;
INLINE unsigned run_mode_and_return_cycles(struct w65c02s_cpu *cpu,
unsigned cont) {
#if W65C02SCE_COARSE_CYCLE_COUNTER
cpu->left_cycles = cont ? 0 : -1;
#else
cpu->target_cycles = cpu->total_cycles - (cont ? 0 : 1);
#endif
return 1;
}
return 0;
w65c02si_run_mode(cpu, cont);
#if W65C02SCE_COARSE_CYCLE_COUNTER
return -cpu->left_cycles;
#else
return cpu->total_cycles - cpu->target_cycles;
#endif
}

INTERNAL unsigned long w65c02si_execute_ic(struct w65c02s_cpu *cpu) {
unsigned cycles;
cycles = run_mode_and_return_cycles(cpu, CONTINUE_INSTRUCTION);
handle_end_of_instruction(cpu);
return cycles;
}

#endif /* W65C02SCE_COARSE */

INTERNAL unsigned long w65c02si_execute_i(struct w65c02s_cpu *cpu) {
unsigned cycles;
if (UNLIKELY(cpu->cpu_state != CPU_STATE_RUN)) {
Expand All @@ -194,14 +214,16 @@ INTERNAL unsigned long w65c02si_execute_i(struct w65c02s_cpu *cpu) {
w65c02si_decode(cpu, READ(cpu->pc++));
decoded:
w65c02si_prerun_mode(cpu);
#if !W65C02SCE_COARSE_CYCLE_COUNTER
++cpu->total_cycles;
#endif
SPENT_CYCLE;

#if !W65C02SCE_COARSE
cpu->cycl = 1; /* make sure we start at the first cycle */
cycles = run_mode_and_return_cycles(cpu, STARTING_INSTRUCTION);
#else
cycles = w65c02si_run_mode(cpu);
#endif
handle_end_of_instruction(cpu);
return cycles;
}

#endif /* W65C02SCE_COARSE */

#endif /* W65C02SCE_SEPARATE */
6 changes: 3 additions & 3 deletions src/execute.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*******************************************************************************
w65c02sce -- cycle-accurate C emulator of the WDC 65C02S
by ziplantil 2022 -- under the CC0 license
version: 2022-10-18
version: 2022-10-19
execute.h - instruction execution unit
*******************************************************************************/
Expand All @@ -16,8 +16,8 @@ INTERNAL_INLINE void w65c02si_irq_update_mask(struct w65c02s_cpu *cpu);
#if !W65C02SCE_COARSE
INTERNAL unsigned long w65c02si_execute_c(struct w65c02s_cpu *cpu,
unsigned long maximum_cycles);
#else /* W65C02SCE_COARSE */
INTERNAL unsigned long w65c02si_execute_i(struct w65c02s_cpu *cpu);
INTERNAL unsigned long w65c02si_execute_ic(struct w65c02s_cpu *cpu);
#endif /* W65C02SCE_COARSE */
INTERNAL unsigned long w65c02si_execute_i(struct w65c02s_cpu *cpu);

#endif /* W65C02SCE_EXECUTE_H */
2 changes: 1 addition & 1 deletion src/mode.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*******************************************************************************
w65c02sce -- cycle-accurate C emulator of the WDC 65C02S
by ziplantil 2022 -- under the CC0 license
version: 2022-10-18
version: 2022-10-19
mode.c - addressing modes
*******************************************************************************/
Expand Down
7 changes: 5 additions & 2 deletions src/mode.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*******************************************************************************
w65c02sce -- cycle-accurate C emulator of the WDC 65C02S
by ziplantil 2022 -- under the CC0 license
version: 2022-10-18
version: 2022-10-19
mode.h - addressing modes
*******************************************************************************/
Expand All @@ -24,8 +24,11 @@
INTERNAL_INLINE void w65c02si_irq_latch(struct w65c02s_cpu *cpu);
INTERNAL_INLINE void w65c02si_stall(struct w65c02s_cpu *cpu);
INTERNAL_INLINE void w65c02si_prerun_mode(struct w65c02s_cpu *cpu);
#if W65C02SCE_COARSE
INTERNAL unsigned w65c02si_run_mode(struct w65c02s_cpu *cpu);
#else
INTERNAL unsigned w65c02si_run_mode(struct w65c02s_cpu *cpu, unsigned cont);

#endif /* W65C02SCE_COARSE */
#endif /* W65C02SCE_SEPARATE */

#endif /* W65C02SCE_MODE_H */
2 changes: 1 addition & 1 deletion src/modejump.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*******************************************************************************
w65c02sce -- cycle-accurate C emulator of the WDC 65C02S
by ziplantil 2022 -- under the CC0 license
version: 2022-10-18
version: 2022-10-19
mjump.h - addressing mode jump table implementation
*******************************************************************************/
Expand Down
2 changes: 1 addition & 1 deletion src/oper.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*******************************************************************************
w65c02sce -- cycle-accurate C emulator of the WDC 65C02S
by ziplantil 2022 -- under the CC0 license
version: 2022-10-18
version: 2022-10-19
oper.c - opcodes and internal operations
*******************************************************************************/
Expand Down
2 changes: 1 addition & 1 deletion src/oper.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*******************************************************************************
w65c02sce -- cycle-accurate C emulator of the WDC 65C02S
by ziplantil 2022 -- under the CC0 license
version: 2022-10-18
version: 2022-10-19
oper.h - opcodes and internal operations
*******************************************************************************/
Expand Down
Loading

0 comments on commit d368df6

Please sign in to comment.