summaryrefslogtreecommitdiff
path: root/mknes_cpu.c
blob: 0eccf039a53b06732483d16d4be7af24ddbe3027 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// NOTE(peter): You can enable decimal mode by adding this to the compilerflags -dENABLE_DECIMAL_MODE DO NOT ENABLE FOR NES!!!

#define PAGE_CROSSED(base, addr) (((base ^ addr) > 0xff))

static inline uint8_t pack_flags(struct cpu_state *cpu) {
	return (cpu->n << 7) | (cpu->v << 6) | (1 << 5) | (cpu->d << 3) | (cpu->i << 2) | (cpu->z << 1) | cpu->c;
}

__attribute__((always_inline))
static inline void unpack_flags(struct cpu_state *cpu, uint8_t value) {
	cpu->n = (value >> 7) & 1;
	cpu->v = (value >> 6) & 1;
	cpu->d = (value >> 3) & 1;
	cpu->i = (value >> 2) & 1;
	cpu->z = (value >> 1) & 1;
	cpu->c = value & 1;
}


__attribute__((always_inline))
static inline void update_zn(struct cpu_state *cpu, uint8_t result) {
	cpu->z = (result == 0);
	cpu->n = (result & 0x80) != 0;
}

static void (*opcode_lut[256])(struct nes_state *) __attribute__((aligned(4096)));

#include "mknes_cpu_opcodes.c"
#include "mknes_cpu_opcodes_ud.c"

static inline void do_nmi(struct nes_state *state) {
	struct cpu_state * restrict cpu = &state->cpu;

	memory_read_dummy(state, cpu->pc);		// T1: dummy read (fetch suppressed)

	uint8_t pcl = cpu->pc & 0xff;
	uint8_t pch = cpu->pc >> 8;

	memory_write(state, 0x0100 + cpu->sp--, pch);	// T2
	memory_write(state, 0x0100 + cpu->sp--, pcl);	// T3
	memory_write(state, 0x0100 + cpu->sp--, pack_flags(cpu) & ~0x10);	// T4: push P (B flag clear)

	uint8_t lo = memory_read(state, 0xfffa);	// T5
	uint8_t hi = memory_read(state, 0xfffb);	// T6

	cpu->pc = lo | (hi << 8);			// T7
	cpu->i = 1;
}

static inline void do_irq(struct nes_state *state) {
	struct cpu_state * restrict cpu = &state->cpu;

	memory_read_dummy(state, cpu->pc);		// T1: dummy read (fetch suppressed)

	uint8_t pcl = cpu->pc & 0xff;
	uint8_t pch = cpu->pc >> 8;

	memory_write(state, 0x0100 + cpu->sp--, pch);	// T2
	memory_write(state, 0x0100 + cpu->sp--, pcl);	// T3
	memory_write(state, 0x0100 + cpu->sp--, pack_flags(cpu) & ~0x10);	// T4: push P (B flag clear)

	uint8_t lo = memory_read(state, 0xfffe);	// T5
	uint8_t hi = memory_read(state, 0xffff);	// T6

	cpu->pc = lo | (hi << 8);			// T7
	cpu->i = 1;
}

__attribute__((always_inline))
static inline void check_interrupts(struct nes_state *state) {
	struct cpu_state * restrict cpu = &state->cpu;

	if(state->cpu.nmi_pending) {
		state->cpu.nmi_pending = 0;
		do_nmi(state);
	} else if(state->cpu.irq_pending && cpu->i == 0) {
		state->cpu.irq_pending = 0;
		do_irq(state);
	}
}


static inline void cpu_reset(struct nes_state *state) {
	uint32_t lo = state->mapper_function.prg_rom_read(state, 0xfffc);
	uint32_t hi = state->mapper_function.prg_rom_read(state, 0xfffd);
	state->cpu.sp = 0xfd;
	state->cpu.i = 1;
	state->cpu.pc = (hi << 8) | lo;
}

static inline void cpu_tick(struct nes_state *state) {
	struct cpu_state * restrict cpu = &state->cpu;
	// check_interrupts(state);
	if(state->cpu.nmi_pending) {
		state->cpu.nmi_pending = 0;
		do_nmi(state);
	}
	if(state->cpu.irq_pending && cpu->i == 0) {
		state->cpu.irq_pending = 0;
		do_irq(state);
	}



	// printf("%4.4x: ", cpu->pc);
	uint8_t opcode = memory_read(state, cpu->pc++);
	// printf("%2.2x   a:%2.2x x:%2.2x y:%2.2x p:%2.2x sp:%2.2x  cycle: %ld\n", opcode, cpu->a, cpu->x, cpu->y, pack_flags(cpu), cpu->sp, state->cycles);
	opcode_lut[opcode](state);
}