Browse Source

Fix interupt handling

- Passes Klaus Dormann's interrupt tests
v2
Nathaniel Walizer 9 months ago
parent
commit
d4c74c9fe4
2 changed files with 92 additions and 59 deletions
  1. +84
    -54
      src/f6502.c
  2. +8
    -5
      src/f6502.h

+ 84
- 54
src/f6502.c View File

@@ -1,3 +1,5 @@
#include <stdbool.h>

#include "f6502.h"
#include "f6502_consts.h"

@@ -67,8 +69,9 @@ static inline uint16_t f6502_read16(f6502_Memory* mem,
((uint16_t)f6502_read(mem, addr + 1) << 8));
}

static inline void f6502_write(f6502_Memory* mem,
uint16_t addr, uint8_t val) {
static inline int f6502_write(f6502_Memory* mem,
uint16_t addr, uint8_t val) {
int ret = 0;
#ifdef F6502_TRACE
printf("W $%04X <- %02X\n", addr, val);
#endif
@@ -80,10 +83,19 @@ static inline void f6502_write(f6502_Memory* mem,
int irq = val & 0b01;
int nmi = val & 0b10;

f6502_wire_interrupts(core,
(irq ? f6502_Int_IRQ : 0) |
(nmi ? f6502_Int_NMI : 0)
);
#ifdef F6502_TRACE
if (!irq ^ !(core->interrupts & f6502_Int_IRQ)) {
fprintf(stdout, "%s IRQ\n", irq ? "Set" : "Clear");
}
if (!nmi ^ !(core->interrupts & f6502_Int_NMI)) {
fprintf(stdout, "%s NMI\n", nmi ? "Set" : "Clear");
}
#endif

f6502_set_NMI(core, nmi);
f6502_set_IRQ(core, irq);

ret = 1; // Interrupt [may have been] triggered
}
#endif
mem->ram[addr] = val;
@@ -91,12 +103,12 @@ static inline void f6502_write(f6502_Memory* mem,
// TODO
memval = val;
#endif
return ret;
}


void f6502_init(f6502_Core* core) {
core->int_state = f6502_Int_NMI | f6502_Int_IRQ;
core->int_wiring = f6502_Int_NMI | f6502_Int_IRQ;
// TODO: ???
}

void f6502_reset(f6502_Core* core) {
@@ -106,9 +118,14 @@ void f6502_reset(f6502_Core* core) {
core->registers.P |= (f6502_Status_B | f6502_Status_1);
}

void f6502_wire_interrupts(f6502_Core* core,
f6502_Interrupt ints) {
core->int_wiring = ints;
void f6502_set_NMI(f6502_Core* core, bool active) {
if (active) core->interrupts |= f6502_Int_NMI;
else core->interrupts &= ~(f6502_Int_NMI | f6502_Int_NMI_Serviced);
}

void f6502_set_IRQ(f6502_Core* core, bool active) {
if (active) core->interrupts |= f6502_Int_IRQ;
else core->interrupts &= ~f6502_Int_IRQ;
}

#define PUSH(core, S, val) \
@@ -128,32 +145,31 @@ void f6502_wire_interrupts(f6502_Core* core,
#define SET(R, flags) R |= (flags)

static inline int f6502_interrupt(f6502_Core* core,
f6502_Interrupt interrupt,
uint16_t addr) {
core->int_state &= ~interrupt;
core->int_state |= (core->int_wiring & interrupt);
PUSH16(core, core->registers.S, core->registers.PC);
PUSH(core, core->registers.S,
core->registers.P & ~f6502_Status_B);
CLR(core->registers.P, f6502_Status_D);
SET(core->registers.P, f6502_Status_I);
core->registers.PC = f6502_read16(&core->memory, addr);
return 7;
}

static inline int f6502_int_ready(const f6502_Core* core,
f6502_Interrupt interrupt) {
return ((core->int_state ^ core->int_wiring) & interrupt);
static inline bool f6502_nmi_ready(const f6502_Core* core) {
return ( (core->interrupts & f6502_Int_NMI) &&
!(core->interrupts & f6502_Int_NMI_Serviced));
}

static inline bool f6502_irq_ready(const f6502_Core* core) {
return ( (core->interrupts & f6502_Int_IRQ) &&
!(core->registers.P & f6502_Status_I));
}

static inline int f6502_check_interrupts(f6502_Core* core) {
if (f6502_int_ready(core, f6502_Int_NMI)) {
return f6502_interrupt(core, f6502_Int_NMI,
f6502_Vector_NMI);
} else if ( f6502_int_ready(core, f6502_Int_IRQ) &&
!(core->registers.P & f6502_Status_I)) {
return f6502_interrupt(core, f6502_Int_IRQ,
f6502_Vector_IRQ);
if (f6502_nmi_ready(core)) {
core->interrupts |= f6502_Int_NMI_Serviced;
return f6502_interrupt(core, f6502_Vector_NMI);
} else if (f6502_irq_ready(core)) {
return f6502_interrupt(core, f6502_Vector_IRQ);
}
return 0;
}
@@ -223,19 +239,18 @@ static inline int f6502_check_interrupts(f6502_Core* core) {
A = res; \
}
#define AND(v) A &= (v); TEST(A)
#define ASL(a) { \
#define ASL(a) ({ \
CLR(P, f6502_Status_N | f6502_Status_Z | f6502_Status_C); \
register uint16_t addr = a; \
register uint8_t val = f6502_read(&core->memory, addr); \
SET(P, table_asl[val]); \
val <<= 1; \
f6502_write(&core->memory, addr, val); \
}
int_trig = f6502_write(&core->memory, addr, val); \
})
#define ASL_A() \
CLR(P, f6502_Status_N | f6502_Status_Z | f6502_Status_C); \
SET(P, table_asl[A]); \
A <<= 1

#ifdef F6502_HCF
#define BIF_HCF() \
if (pc_prev == PC + 2) { \
@@ -268,22 +283,24 @@ static inline int f6502_check_interrupts(f6502_Core* core) {
SET(P, table_test[diff & 0xFFU]); \
if (diff < 0x100) SET(P, f6502_Status_C); \
}
#define DEC(a) { \
#define DEC(a) ({ \
register uint16_t addr = a; \
register uint8_t val = f6502_read(&core->memory, addr); \
f6502_write(&core->memory, addr, --val); \
--val; \
TEST(val); \
}
int_trig = f6502_write(&core->memory, addr, val); \
})
#define EOR(v) A ^= (v); TEST(A)
#define INC(a) { \
#define INC(a) ({ \
register uint16_t addr = a; \
register uint8_t val = f6502_read(&core->memory, addr); \
f6502_write(&core->memory, addr, ++val); \
++val; \
TEST(val); \
}
int_trig = f6502_write(&core->memory, addr, val); \
})
#define JMP(a) PC = (a)
#define LD(reg, v) reg = (v); TEST(reg)
#define LSR(a) { \
#define LSR(a) ({ \
register uint16_t addr = a; \
register uint8_t val = f6502_read(&core->memory, addr); \
CLR(P, f6502_Status_N | f6502_Status_Z | f6502_Status_C); \
@@ -291,8 +308,8 @@ static inline int f6502_check_interrupts(f6502_Core* core) {
val >>= 1; \
SET(P, val & f6502_Status_N); \
if (!val) SET(P, f6502_Status_Z); \
f6502_write(&core->memory, addr, val); \
}
int_trig = f6502_write(&core->memory, addr, val); \
})
#define LSR_A() \
CLR(P, f6502_Status_N | f6502_Status_Z | f6502_Status_C); \
SET(P, A & f6502_Status_C); \
@@ -300,7 +317,7 @@ static inline int f6502_check_interrupts(f6502_Core* core) {
SET(P, A & f6502_Status_N); \
if (!A) SET(P, f6502_Status_Z)
#define ORA(v) A |= (v); TEST(A)
#define ROL(a) { \
#define ROL(a) ({ \
register uint16_t addr = a; \
register uint8_t val = f6502_read(&core->memory, addr); \
register uint8_t new_c = (A & 0x80) ? f6502_Status_C : 0; \
@@ -309,8 +326,8 @@ static inline int f6502_check_interrupts(f6502_Core* core) {
P |= new_c; \
if (!val) SET(P, f6502_Status_Z); \
SET(P, val & f6502_Status_N); \
f6502_write(&core->memory, addr, val); \
}
int_trig = f6502_write(&core->memory, addr, val); \
})
#define ROL_A() { \
register uint8_t new_c = (A & 0x80) ? f6502_Status_C : 0; \
A = (A << 1) | (P & f6502_Status_C); \
@@ -319,7 +336,7 @@ static inline int f6502_check_interrupts(f6502_Core* core) {
if (!A) SET(P, f6502_Status_Z); \
SET(P, A & f6502_Status_N); \
}
#define ROR(a) { \
#define ROR(a) ({ \
register uint16_t addr = a; \
register uint8_t val = f6502_read(&core->memory, addr); \
register uint8_t new_c = (val & f6502_Status_C); \
@@ -329,10 +346,10 @@ static inline int f6502_check_interrupts(f6502_Core* core) {
P |= new_c; \
if (!val) SET(P, f6502_Status_Z); \
SET(P, (val & f6502_Status_N)); \
f6502_write(&core->memory, addr, val); \
}
int_trig = f6502_write(&core->memory, addr, val); \
})
#define ROR_A() { \
register uint8_t new_c = (A & f6502_Status_C); \
register uint8_t new_c = (A & f6502_Status_C); \
A >>= 1; \
if (P & f6502_Status_C) A |= f6502_Status_N; \
CLR(P, f6502_Status_C | f6502_Status_Z | f6502_Status_N); \
@@ -352,7 +369,7 @@ static inline int f6502_check_interrupts(f6502_Core* core) {
} \
A = res; \
}
#define ST(reg, a) f6502_write(&core->memory, (a), reg)
#define ST(reg, a) int_trig = f6502_write(&core->memory, (a), reg)


static int f6502_do_step(f6502_Core* core, int clocks) {
@@ -365,11 +382,21 @@ static int f6502_do_step(f6502_Core* core, int clocks) {
register uint8_t Y = core->registers.Y;
register uint8_t P = core->registers.P;

while (clocks_elapsed < clocks) {
register bool int_trig = 0;

while (clocks_elapsed < clocks && !int_trig) {
uint8_t opcode = f6502_read(&core->memory, PC++);
#ifdef F6502_TRACE
printf("S:%02x A:%02x X:%02x Y:%02x P:%02x\n",
S, A, X, Y, P);

printf("$%04x:", (int)S + f6502_Base_Stack);
for (int i = S; i < 256; ++i)
printf(" %02x", (int)f6502_read(&core->memory, i + f6502_Base_Stack));
putc('\n', stdout);

printf("INT %02x\n", core->interrupts);

printf("$%04x: ", PC - 1);
f6502_dump_instr(core, PC - 1);
printf(" -> ");
@@ -605,9 +632,10 @@ static int f6502_do_step(f6502_Core* core, int clocks) {

case 0x40: // RTI
POP(core, S, P);
SET(P, f6502_Status_1);
SET(P, f6502_Status_1 | f6502_Status_B);
POP16(core, S, PC);
CLK(6);
int_trig = 1;
break;

case 0x41: // EOR (zp, X)
@@ -705,14 +733,10 @@ static int f6502_do_step(f6502_Core* core, int clocks) {
CLR(P, f6502_Status_I);
CLK(2);
if ( (p_old & f6502_Status_I) &&
f6502_int_ready(core, f6502_Int_IRQ)) {
core->int_state &= ~f6502_Int_IRQ;
core->int_state |= ( core->int_wiring &
f6502_Int_IRQ);
(core->interrupts & f6502_Int_IRQ)) {
CLK(7);
PUSH16(core, S, PC);
PUSH(core, S, P & ~f6502_Status_B);
CLR(P, f6502_Status_D);
SET(P, f6502_Status_I);
PC = f6502_read16(&core->memory,
f6502_Vector_IRQ);
@@ -1380,6 +1404,10 @@ static int f6502_do_step(f6502_Core* core, int clocks) {
}
}

#ifdef F6502_TRACE
if (int_trig) printf("Possible interrupt trigger\n");
#endif

step_done:
core->registers = (f6502_Registers){
.PC = PC,
@@ -1396,10 +1424,12 @@ step_done:
int f6502_step(f6502_Core* core, int clocks) {
int clocks_elapsed = 0;
// TODO: Why are we processing seven clocks prior to NMI?
if (f6502_int_ready(core, f6502_Int_NMI)) {
/*
if (f6502_nmi_ready(core)) {
clocks_elapsed += f6502_do_step(core, 7);
clocks -= clocks_elapsed;
}
*/
clocks_elapsed += f6502_check_interrupts(core);
return (clocks_elapsed + f6502_do_step(core, clocks));
}

+ 8
- 5
src/f6502.h View File

@@ -1,8 +1,10 @@
#ifndef F6502_H_
#define F6502_H_

#include <stdbool.h>
#include <stdint.h>


#define F6502_RAM_SIZE (0x2000U)


@@ -34,8 +36,9 @@ typedef struct __attribute__ ((__packed__)) {
} f6502_Registers;

typedef enum {
f6502_Int_NMI = 0b01,
f6502_Int_IRQ = 0b10,
f6502_Int_NMI = 0b00000001,
f6502_Int_IRQ = 0b00000010,
f6502_Int_NMI_Serviced = 0b10000000,
} f6502_Interrupt;

typedef struct __attribute__ ((__packed__)) {
@@ -51,15 +54,15 @@ typedef struct __attribute__ ((__packed__)) {

typedef struct __attribute__ ((__packed__)) {
f6502_Registers registers;
f6502_Interrupt int_state:2;
f6502_Interrupt int_wiring:2;
f6502_Interrupt interrupts;
f6502_Memory memory;
} f6502_Core;

void f6502_init(f6502_Core*);
void f6502_reset(f6502_Core*);
int f6502_step(f6502_Core*, int clocks);
void f6502_wire_interrupts(f6502_Core*, f6502_Interrupt ints);
void f6502_set_NMI(f6502_Core*, bool active);
void f6502_set_IRQ(f6502_Core*, bool active);


#endif // F6502_H_

Loading…
Cancel
Save