blob: fe5cf6a9b4294000386380b0ee291d5c5ca99940 [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, ...) \
swissChili6264a3b2020-07-30 19:02:07 -070010 printf("\033[31m" m "\033[0m\n", ##__VA_ARGS__); \
swissChili6c61a792020-07-28 16:29:20 -070011 exit(1);
12
swissChili6264a3b2020-07-30 19:02:07 -070013#define warn(m, ...) \
14 printf("\033[33m" m "\033[0m\n", ##__VA_ARGS__);
15
swissChili6c61a792020-07-28 16:29:20 -070016cpu_t new_cpu()
17{
18 cpu_t cpu = { 0 };
swissChili6c61a792020-07-28 16:29:20 -070019 cpu.regs[SP] = 0xFD; // stack at is 0x100 + SP
20 cpu.pc = 0; // arbitrary program counter start
swissChili6264a3b2020-07-30 19:02:07 -070021 cpu.running = true;
swissChili6c61a792020-07-28 16:29:20 -070022 cpu.mem = malloc(0xFFFF);
23 memset(cpu.mem, 0, 0xFFFF);
24
25 if (!cpu.mem)
26 {
27 die("Could not allocate memory for CPU");
28 }
29
30 return cpu;
31}
32
swissChili6264a3b2020-07-30 19:02:07 -070033void stack_push(cpu_t *cpu, uint8_t v)
34{
35 cpu->mem[cpu->regs[SP]-- + 0x100] = v;
36}
37
38uint8_t stack_pop(cpu_t *cpu)
39{
40 return cpu->mem[cpu->regs[SP]++ + 0x100];
41}
42
swissChili6c61a792020-07-28 16:29:20 -070043void free_cpu(cpu_t *cpu)
44{
45 free(cpu->mem);
46}
47
swissChili6264a3b2020-07-30 19:02:07 -070048void stat_nz(cpu_t *cpu, int8_t v)
swissChili6c61a792020-07-28 16:29:20 -070049{
swissChili6264a3b2020-07-30 19:02:07 -070050 cpu->status.negative = v < 0;
51 cpu->status.zero = v == 0;
52}
53
54// Used to check for overflow, is c unique?
55bool last_unique(bool a, bool b, bool c)
56{
57 return a == b && a != c;
58}
59
60void stat_cv(cpu_t *cpu, uint8_t a, uint8_t b, uint8_t c)
61{
62 cpu->status.overflow = last_unique(a >> 7, b >> 7, c >> 7);
63 cpu->status.carry = c < a || c < b;
64}
65
66void execute(cpu_t *cpu, const char *mnemonic, uint8_t op, arg_t a)
67{
68 // used to save space
69 #define REGS \
70 R(X) R(A) R(Y)
71
swissChili710d18d2020-07-29 19:43:20 -070072 switch (op) {
swissChili6264a3b2020-07-30 19:02:07 -070073 // Load and store instructions:
74 #define R(reg) \
75 case LD##reg: \
76 cpu->regs[reg] = a.val; \
77 stat_nz(cpu, a.val); \
78 break;
79
80 REGS
81
82 #undef R
83
84 #define R(reg) \
85 case ST##reg: \
86 cpu->mem[a.ptr] = cpu->regs[reg]; \
87 break; \
88
89 REGS
90
91 #undef R
92
93 // Arithmetic instructions:
94 // NOTE: binary coded decimals are NOT SUPPORTED because I don't want
95 // to implement them.
96 case ADC:
97 {
98 uint8_t sum = cpu->regs[A] + a.val + cpu->status.carry;
99 // signed overflow
100 stat_cv(cpu, cpu->regs[A], a.val + cpu->status.carry, sum);
101 stat_nz(cpu, sum);
102 cpu->regs[A] = sum;
103 break;
104 }
105
106 case SBC:
107 {
108 uint8_t diff = cpu->regs[A] - a.val - !cpu->status.carry;
109 stat_cv(cpu, cpu->regs[A], a.val - !cpu->status.carry, diff);
110 stat_nz(cpu, diff);
111 cpu->regs[A] = diff;
112 break;
113 }
114
115 case INC:
116 cpu->mem[a.ptr]++;
117 stat_nz(cpu, cpu->mem[a.ptr]);
118 break;
119
120 case INX:
121 cpu->regs[X]++;
122 stat_nz(cpu, cpu->regs[X]);
123 break;
124
125 case INY:
126 cpu->regs[Y]++;
127 stat_nz(cpu, cpu->regs[Y]);
128 break;
129
130 case DEC:
131 cpu->mem[a.ptr]--;
132 stat_nz(cpu, cpu->mem[a.ptr]);
133 break;
swissChili710d18d2020-07-29 19:43:20 -0700134 }
swissChili6264a3b2020-07-30 19:02:07 -0700135 #undef REGS
swissChili6c61a792020-07-28 16:29:20 -0700136}
137
138uint16_t le_to_native(uint8_t a, uint8_t b)
139{
140#ifdef LITTLE_ENDIAN
swissChili6c61a792020-07-28 16:29:20 -0700141 return b << 8 | a;
swissChili6264a3b2020-07-30 19:02:07 -0700142#else
143 return a << 8 | b;
swissChili6c61a792020-07-28 16:29:20 -0700144#endif
145}
146
147uint16_t fetch_le(cpu_t *cpu)
148{
149 uint8_t a = cpu->mem[cpu->pc++];
150 uint8_t b = cpu->mem[cpu->pc++];
151 return le_to_native(a, b);
152}
153
swissChili710d18d2020-07-29 19:43:20 -0700154arg_t arg_imm(uint16_t a)
155{
156 return (arg_t){ a, a };
157}
158
159arg_t arg_ptr(cpu_t *c, uint flags, uint16_t p)
160{
161 if (flags & FETCH_NO_INDIRECTION)
162 return arg_imm(p);
163
164 return (arg_t){ c->mem[p], p };
165}
166
167arg_t arg(uint16_t v, uint16_t a)
168{
169 return (arg_t){ v, a };
170}
171
172arg_t fetch_addr(cpu_t *cpu, uint8_t am, uint f)
swissChili6c61a792020-07-28 16:29:20 -0700173{
174 switch (am)
175 {
176 case AM_ACC:
177 case AM_IMP:
swissChili710d18d2020-07-29 19:43:20 -0700178 return arg_imm(0);
swissChili6c61a792020-07-28 16:29:20 -0700179
180 // In both cases return immediate 8 bit value
181 case AM_IMM:
182 case AM_ZP:
swissChili710d18d2020-07-29 19:43:20 -0700183 return arg_imm(cpu->mem[cpu->pc++]);
swissChili6c61a792020-07-28 16:29:20 -0700184
185 case AM_ABS:
swissChili710d18d2020-07-29 19:43:20 -0700186 return arg_ptr(cpu, f, fetch_le(cpu));
swissChili6c61a792020-07-28 16:29:20 -0700187
188 case AM_REL:
189 {
swissChili6264a3b2020-07-30 19:02:07 -0700190 // Aparently, PC should will point to the NEXT opcode
191 // I can't find any documentation on this unfortunately, but
192 // I have discovered this through testing the output of other
193 // assemblers.
194 uint16_t pc = cpu->pc + 1;
195 return arg_ptr(cpu, f, (int8_t)cpu->mem[cpu->pc++] + pc);
swissChili6c61a792020-07-28 16:29:20 -0700196 }
197
198 case AM_IND:
199 {
200 uint16_t addr = fetch_le(cpu);
swissChili6264a3b2020-07-30 19:02:07 -0700201
202 if (f & FETCH_NO_INDIRECTION)
203 return arg_imm(addr);
204
swissChili6c61a792020-07-28 16:29:20 -0700205 uint8_t low = cpu->mem[addr],
206 high = cpu->mem[addr + 1];
207
swissChili710d18d2020-07-29 19:43:20 -0700208 return arg_ptr(cpu, f, le_to_native(low, high));
swissChili6c61a792020-07-28 16:29:20 -0700209 }
210
211 case AM_AX:
swissChili710d18d2020-07-29 19:43:20 -0700212 return arg_ptr(cpu, f, fetch_le(cpu) + cpu->regs[X]);
swissChili6c61a792020-07-28 16:29:20 -0700213
214 case AM_AY:
swissChili710d18d2020-07-29 19:43:20 -0700215 return arg_ptr(cpu, f, fetch_le(cpu) + cpu->regs[Y]);
swissChili6c61a792020-07-28 16:29:20 -0700216
217 case AM_ZPX:
swissChili710d18d2020-07-29 19:43:20 -0700218 return arg_ptr(cpu, f, cpu->mem[cpu->pc++] + cpu->regs[X]);
swissChili6c61a792020-07-28 16:29:20 -0700219
220 case AM_ZPY:
swissChili710d18d2020-07-29 19:43:20 -0700221 return arg_ptr(cpu, f, cpu->mem[cpu->pc++] + cpu->regs[Y]);
swissChili6c61a792020-07-28 16:29:20 -0700222
223 case AM_ZIX:
224 {
225 uint8_t zp = cpu->mem[cpu->pc++];
swissChili6264a3b2020-07-30 19:02:07 -0700226
227 if (f & FETCH_NO_INDIRECTION)
228 return arg_imm(zp);
229
swissChili710d18d2020-07-29 19:43:20 -0700230 uint16_t addr = zp + cpu->regs[X];
231 uint16_t indirect = le_to_native(cpu->mem[addr], cpu->mem[addr + 1]);
232 return arg_ptr(cpu, f, indirect);
swissChili6c61a792020-07-28 16:29:20 -0700233 }
234
235 case AM_ZIY:
236 {
237 uint8_t zp = cpu->mem[cpu->pc++];
swissChili6264a3b2020-07-30 19:02:07 -0700238
239 if (f & FETCH_NO_INDIRECTION)
240 return arg_imm(zp);
241
swissChili6c61a792020-07-28 16:29:20 -0700242 uint16_t base = le_to_native(cpu->mem[zp], cpu->mem[zp + 1]);
swissChili710d18d2020-07-29 19:43:20 -0700243 return arg_ptr(cpu, f, base + cpu->regs[Y]);
swissChili6c61a792020-07-28 16:29:20 -0700244 }
245
246 default:
247 die("Unknown address mode %x", am);
swissChili710d18d2020-07-29 19:43:20 -0700248 __builtin_unreachable();
swissChili6c61a792020-07-28 16:29:20 -0700249 }
250}
251
252void step(cpu_t *cpu)
253{
254 switch (cpu->mem[cpu->pc++])
255 {
256#define INST(mn, am, op) \
257 case op: \
swissChili710d18d2020-07-29 19:43:20 -0700258 execute(cpu, #mn, mn, fetch_addr(cpu, am, 0)); \
swissChili6c61a792020-07-28 16:29:20 -0700259 break;
260
261 INSTRUCTIONS
262
263#undef INST
264
265 default:
266 die("Undefined opcode");
267 }
268}
269
270void dump_inst(cpu_t *cpu, const char *mn, uint16_t addr, uint8_t am)
271{
272 printf("\t%s\t", mn);
273
274 switch (am)
275 {
276 case AM_IMM:
277 printf("#");
278 case AM_REL:
279 case AM_ABS:
280 case AM_ZP:
281 printf("$%x", addr);
282 break;
283
284 case AM_IND:
285 printf("($%x)", addr);
286 break;
287
288 case AM_AX:
289 case AM_ZPX:
290 printf("$%x, X", addr);
291 break;
292
293 case AM_AY:
294 case AM_ZPY:
295 printf("$%x, Y", addr);
296 break;
297
298 case AM_ZIX:
299 printf("($%x, X)", addr);
300 break;
301
302 case AM_ZIY:
303 printf("($%x), Y", addr);
304 break;
305 }
306
307 printf("\n");
308}
309
310void disas_step(cpu_t *cpu)
311{
swissChili6264a3b2020-07-30 19:02:07 -0700312 printf("$%x", cpu->pc);
swissChili6c61a792020-07-28 16:29:20 -0700313 uint8_t op = cpu->mem[cpu->pc++];
314 switch (op)
315 {
316#define INST(mn, am, op) \
317 case op: \
swissChili710d18d2020-07-29 19:43:20 -0700318 dump_inst(cpu, #mn, \
319 fetch_addr(cpu, am, FETCH_NO_INDIRECTION).ptr, am); \
swissChili6c61a792020-07-28 16:29:20 -0700320 break;
321
322 INSTRUCTIONS
323
324#undef INST
325
326 default:
swissChili6264a3b2020-07-30 19:02:07 -0700327 warn("\tUndefined opcode %x", op);
swissChili6c61a792020-07-28 16:29:20 -0700328 }
329}
330
331void disas(cpu_t *cpu)
332{
swissChili6264a3b2020-07-30 19:02:07 -0700333 // Raw binary, no way to know what's code what isn't
swissChili6c61a792020-07-28 16:29:20 -0700334 while (cpu->pc < 0xFFFF)
335 {
336 disas_step(cpu);
337 }
338}