blob: f0d7d6883bfe7a33e52f5261f1b7dfcaa457b0dc [file] [log] [blame]
swissChili6c61a792020-07-28 16:29:20 -07001#include "cpu.h"
2#include "instructions.h"
3
4#include <endian.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8
9#define die(m, ...) \
10 printf("\033[31mError: " m "\033[0m\n", ##__VA_ARGS__); \
11 exit(1);
12
13cpu_t new_cpu()
14{
15 cpu_t cpu = { 0 };
16 cpu.regs[SR] = UNUSED; // unused flag always set
17 cpu.regs[SP] = 0xFD; // stack at is 0x100 + SP
18 cpu.pc = 0; // arbitrary program counter start
19 cpu.mem = malloc(0xFFFF);
20 memset(cpu.mem, 0, 0xFFFF);
21
22 if (!cpu.mem)
23 {
24 die("Could not allocate memory for CPU");
25 }
26
27 return cpu;
28}
29
30void free_cpu(cpu_t *cpu)
31{
32 free(cpu->mem);
33}
34
35void execute(cpu_t *cpu, const char *mnemonic, uint8_t op, uint16_t addr)
36{
37
38}
39
40uint16_t le_to_native(uint8_t a, uint8_t b)
41{
42#ifdef LITTLE_ENDIAN
43 return a << 8 | b;
44#else
45 return b << 8 | a;
46#endif
47}
48
49uint16_t fetch_le(cpu_t *cpu)
50{
51 uint8_t a = cpu->mem[cpu->pc++];
52 uint8_t b = cpu->mem[cpu->pc++];
53 return le_to_native(a, b);
54}
55
56uint16_t fetch_addr(cpu_t *cpu, uint8_t am)
57{
58 switch (am)
59 {
60 case AM_ACC:
61 case AM_IMP:
62 return 0;
63
64 // In both cases return immediate 8 bit value
65 case AM_IMM:
66 case AM_ZP:
67 return cpu->mem[cpu->pc++];
68
69 case AM_ABS:
70 return fetch_le(cpu);
71
72 case AM_REL:
73 {
74 // PC should point to the opcode
75 // braces needed to avoid c stupidity
76 uint16_t pc = cpu->pc - 1;
77 return cpu->mem[cpu->pc++] + pc;
78 }
79
80 case AM_IND:
81 {
82 uint16_t addr = fetch_le(cpu);
83 uint8_t low = cpu->mem[addr],
84 high = cpu->mem[addr + 1];
85
86 return le_to_native(low, high);
87 }
88
89 case AM_AX:
90 return fetch_le(cpu) + cpu->regs[X];
91
92 case AM_AY:
93 return fetch_le(cpu) + cpu->regs[Y];
94
95 case AM_ZPX:
96 return cpu->mem[cpu->pc++] + cpu->regs[X];
97
98 case AM_ZPY:
99 return cpu->mem[cpu->pc++] + cpu->regs[Y];
100
101 case AM_ZIX:
102 {
103 uint8_t zp = cpu->mem[cpu->pc++];
104 return le_to_native(cpu->mem[zp + cpu->regs[X]], cpu->mem[zp + cpu->regs[X] + 1]);
105 }
106
107 case AM_ZIY:
108 {
109 uint8_t zp = cpu->mem[cpu->pc++];
110 uint16_t base = le_to_native(cpu->mem[zp], cpu->mem[zp + 1]);
111 return base + cpu->regs[Y];
112 }
113
114 default:
115 die("Unknown address mode %x", am);
116 return -1; // unreachable
117 }
118}
119
120void step(cpu_t *cpu)
121{
122 switch (cpu->mem[cpu->pc++])
123 {
124#define INST(mn, am, op) \
125 case op: \
126 execute(cpu, #mn, mn, fetch_addr(cpu, am)); \
127 break;
128
129 INSTRUCTIONS
130
131#undef INST
132
133 default:
134 die("Undefined opcode");
135 }
136}
137
138void dump_inst(cpu_t *cpu, const char *mn, uint16_t addr, uint8_t am)
139{
140 printf("\t%s\t", mn);
141
142 switch (am)
143 {
144 case AM_IMM:
145 printf("#");
146 case AM_REL:
147 case AM_ABS:
148 case AM_ZP:
149 printf("$%x", addr);
150 break;
151
152 case AM_IND:
153 printf("($%x)", addr);
154 break;
155
156 case AM_AX:
157 case AM_ZPX:
158 printf("$%x, X", addr);
159 break;
160
161 case AM_AY:
162 case AM_ZPY:
163 printf("$%x, Y", addr);
164 break;
165
166 case AM_ZIX:
167 printf("($%x, X)", addr);
168 break;
169
170 case AM_ZIY:
171 printf("($%x), Y", addr);
172 break;
173 }
174
175 printf("\n");
176}
177
178void disas_step(cpu_t *cpu)
179{
180 printf("%x", cpu->pc);
181 uint8_t op = cpu->mem[cpu->pc++];
182 switch (op)
183 {
184#define INST(mn, am, op) \
185 case op: \
186 dump_inst(cpu, #mn, fetch_addr(cpu, am), am); \
187 break;
188
189 INSTRUCTIONS
190
191#undef INST
192
193 default:
194 die("Undefined opcode %x", op);
195 }
196}
197
198void disas(cpu_t *cpu)
199{
200 while (cpu->pc < 0xFFFF)
201 {
202 disas_step(cpu);
203 }
204}