blob: fbe72b27c9020ff2eba87f9ffbc96443b79efe1d [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
swissChilidbbd5402020-08-07 15:07:39 -070016void reset(cpu_t *cpu)
17{
18 cpu->regs[SP] = 0xFD; // stack at is 0x100 + SP
swissChilibb478f12020-08-07 20:45:07 -070019 cpu->pc = 0x600; // arbitrary program counter start
swissChilidbbd5402020-08-07 15:07:39 -070020 cpu->running = true;
21}
22
swissChili6c61a792020-07-28 16:29:20 -070023cpu_t new_cpu()
24{
25 cpu_t cpu = { 0 };
swissChili6c61a792020-07-28 16:29:20 -070026 cpu.regs[SP] = 0xFD; // stack at is 0x100 + SP
swissChilibb478f12020-08-07 20:45:07 -070027 cpu.pc = 0x600; // arbitrary program counter start
swissChili6264a3b2020-07-30 19:02:07 -070028 cpu.running = true;
swissChili6c61a792020-07-28 16:29:20 -070029 cpu.mem = malloc(0xFFFF);
30 memset(cpu.mem, 0, 0xFFFF);
31
32 if (!cpu.mem)
33 {
34 die("Could not allocate memory for CPU");
35 }
36
37 return cpu;
38}
39
swissChilida4803e2020-08-06 20:06:04 -070040uint16_t le_to_native(uint8_t a, uint8_t b)
41{
42#ifdef LITTLE_ENDIAN
43 return b << 8 | a;
44#else
45 return a << 8 | b;
46#endif
47}
48
49void native_to_le(uint16_t n, uint8_t *a, uint8_t *b)
50{
51#ifdef LITTLE_ENDIAN
52 *a = n >> 8;
53 *b = n & 0xFF;
54#else
55 *a = n & 0xFF;
56 *b = n >> 8;
57#endif
58}
59
swissChili6264a3b2020-07-30 19:02:07 -070060void stack_push(cpu_t *cpu, uint8_t v)
61{
62 cpu->mem[cpu->regs[SP]-- + 0x100] = v;
63}
64
swissChilida4803e2020-08-06 20:06:04 -070065void stack_pushle(cpu_t *cpu, uint16_t v)
66{
67 uint8_t a, b;
68 native_to_le(v, &a, &b);
69 // push in "reverse" order so that the address is stored as LE
70 stack_push(cpu, b);
71 stack_push(cpu, a);
72}
73
swissChili6264a3b2020-07-30 19:02:07 -070074uint8_t stack_pop(cpu_t *cpu)
75{
76 return cpu->mem[cpu->regs[SP]++ + 0x100];
77}
78
swissChilida4803e2020-08-06 20:06:04 -070079uint16_t stack_pople(cpu_t *cpu)
80{
81 uint8_t a = stack_pop(cpu);
82 uint8_t b = stack_pop(cpu);
83 return le_to_native(a, b);
84}
85
swissChili6c61a792020-07-28 16:29:20 -070086void free_cpu(cpu_t *cpu)
87{
88 free(cpu->mem);
89}
90
swissChilida4803e2020-08-06 20:06:04 -070091// rotate right
92uint8_t ror(uint8_t a, uint8_t n)
93{
94 return (a >> n) | (a << (8 - n));
95}
96
97// rotate left
98uint8_t rol(uint8_t a, uint8_t n)
99{
100 return (a << n) | (a >> (8 - n));
101}
102
swissChili6264a3b2020-07-30 19:02:07 -0700103void stat_nz(cpu_t *cpu, int8_t v)
swissChili6c61a792020-07-28 16:29:20 -0700104{
swissChili6264a3b2020-07-30 19:02:07 -0700105 cpu->status.negative = v < 0;
106 cpu->status.zero = v == 0;
107}
108
109// Used to check for overflow, is c unique?
110bool last_unique(bool a, bool b, bool c)
111{
112 return a == b && a != c;
113}
114
115void stat_cv(cpu_t *cpu, uint8_t a, uint8_t b, uint8_t c)
116{
117 cpu->status.overflow = last_unique(a >> 7, b >> 7, c >> 7);
118 cpu->status.carry = c < a || c < b;
119}
120
swissChilida4803e2020-08-06 20:06:04 -0700121void cmp(cpu_t *cpu, uint8_t reg, uint8_t mem)
122{
123 cpu->status.negative = 0;
124 cpu->status.zero = 0;
125 cpu->status.carry = 0;
126 if (cpu->regs[reg] < mem)
127 {
128 cpu->status.negative = 1;
129 }
130 else if (cpu->regs[reg] == mem)
131 {
132 cpu->status.zero = 1;
133 cpu->status.carry = 1;
134 }
135 else
136 {
137 cpu->status.carry = 1;
138 }
139}
140
141void execute(cpu_t *cpu, const char *mnemonic, uint8_t op, arg_t a, uint8_t am)
swissChili6264a3b2020-07-30 19:02:07 -0700142{
143 // used to save space
144 #define REGS \
145 R(X) R(A) R(Y)
146
swissChili710d18d2020-07-29 19:43:20 -0700147 switch (op) {
swissChili6264a3b2020-07-30 19:02:07 -0700148 // Load and store instructions:
149 #define R(reg) \
150 case LD##reg: \
151 cpu->regs[reg] = a.val; \
152 stat_nz(cpu, a.val); \
153 break;
154
155 REGS
156
157 #undef R
158
159 #define R(reg) \
160 case ST##reg: \
161 cpu->mem[a.ptr] = cpu->regs[reg]; \
162 break; \
163
164 REGS
165
166 #undef R
167
168 // Arithmetic instructions:
169 // NOTE: binary coded decimals are NOT SUPPORTED because I don't want
170 // to implement them.
171 case ADC:
172 {
173 uint8_t sum = cpu->regs[A] + a.val + cpu->status.carry;
174 // signed overflow
175 stat_cv(cpu, cpu->regs[A], a.val + cpu->status.carry, sum);
176 stat_nz(cpu, sum);
177 cpu->regs[A] = sum;
178 break;
179 }
180
181 case SBC:
182 {
183 uint8_t diff = cpu->regs[A] - a.val - !cpu->status.carry;
184 stat_cv(cpu, cpu->regs[A], a.val - !cpu->status.carry, diff);
185 stat_nz(cpu, diff);
186 cpu->regs[A] = diff;
187 break;
188 }
189
190 case INC:
191 cpu->mem[a.ptr]++;
192 stat_nz(cpu, cpu->mem[a.ptr]);
193 break;
194
195 case INX:
196 cpu->regs[X]++;
197 stat_nz(cpu, cpu->regs[X]);
198 break;
199
200 case INY:
201 cpu->regs[Y]++;
202 stat_nz(cpu, cpu->regs[Y]);
203 break;
204
205 case DEC:
206 cpu->mem[a.ptr]--;
207 stat_nz(cpu, cpu->mem[a.ptr]);
208 break;
swissChilida4803e2020-08-06 20:06:04 -0700209
210 case DEX:
211 cpu->regs[X]--;
212 stat_nz(cpu, cpu->regs[X]);
213 break;
214
215 case DEY:
216 cpu->regs[Y]--;
217 stat_nz(cpu, cpu->regs[Y]);
218 break;
219
220 case ASL:
221 // This check must be done here unfortunately, it would be nice
222 // to do this while decoding operands but it would require
223 // a substantial change to the architecture of the emulator
224 if (am == AM_ACC)
225 {
226 cpu->status.carry = cpu->regs[A] >> 7;
227 cpu->regs[A] <<= 1;
228 stat_nz(cpu, cpu->regs[A]);
229 }
230 else
231 {
232 cpu->status.carry = cpu->mem[a.val] >> 7;
233 cpu->mem[a.ptr] <<= 1;
234 stat_nz(cpu, cpu->mem[a.ptr]);
235 }
236 break;
237
238 case LSR:
239 if (am == AM_ACC)
240 {
241 cpu->status.carry = cpu->regs[A] & 1;
242 cpu->regs[A] >>= 1;
243 stat_nz(cpu, cpu->regs[A]);
244 }
245 else
246 {
247 cpu->status.carry = cpu->mem[a.val] & 7;
248 cpu->mem[a.ptr] >>= 1;
249 stat_nz(cpu, cpu->mem[a.ptr]);
250 }
251 break;
252
253 case ROL:
254 if (am == AM_ACC)
255 {
256 cpu->status.carry = cpu->regs[A] >> 7;
257 cpu->regs[A] = rol(cpu->regs[A], 1);
258 stat_nz(cpu, cpu->regs[A]);
259 }
260 else
261 {
262 cpu->status.carry = cpu->mem[a.val] >> 7;
263 cpu->mem[a.ptr] = rol(a.val, 1);
264 stat_nz(cpu, cpu->mem[a.ptr]);
265 }
266 break;
267
268 case ROR:
269 if (am == AM_ACC)
270 {
271 cpu->status.carry = cpu->regs[A] & 1;
272 cpu->regs[A] = ror(cpu->regs[A], 1);
273 stat_nz(cpu, cpu->regs[A]);
274 }
275 else
276 {
277 cpu->status.carry = cpu->mem[a.val] & 1;
278 cpu->mem[a.ptr] = ror(a.val, 1);
279 stat_nz(cpu, cpu->mem[a.ptr]);
280 }
281 break;
282
283 case AND:
284 cpu->regs[A] &= a.val;
285 stat_nz(cpu, cpu->regs[A]);
286 break;
287
288 case ORA:
289 cpu->regs[A] |= a.val;
290 stat_nz(cpu, cpu->regs[A]);
291 break;
292
293 case EOR:
294 cpu->regs[A] ^= a.val;
295 stat_nz(cpu, cpu->regs[A]);
296 break;
297
298 case CMP:
299 cmp(cpu, A, a.val);
300 break;
301
302 case CPX:
303 cmp(cpu, X, a.val);
304 break;
305
306 case CPY:
307 cmp(cpu, Y, a.val);
308 break;
309
310 // TODO: implement BIT here
311
312 #define BRANCHES \
313 B(BCC, carry == 0) \
314 B(BCS, carry == 1) \
315 B(BNE, zero == 0) \
316 B(BEQ, zero == 1) \
317 B(BPL, negative == 0) \
318 B(BMI, negative == 1) \
319 B(BVC, overflow == 0) \
320 B(BVS, overflow == 1)
321
322 #define B(i, c) \
323 case i: \
324 if (cpu->status . c) \
325 cpu->pc = a.ptr;\
326 break;
327
328 BRANCHES
329
330 #undef B
331 #undef BRANCHES
332
333 #define TRANSFERS \
334 T(A, X) \
335 T(X, A) \
336 T(A, Y) \
337 T(Y, A)
338
339 #define T(a, b) \
340 case T ## a ## b: \
341 cpu->regs[b] = cpu->regs[a]; \
342 stat_nz(cpu, cpu->regs[b]); \
343 break;
344
345 TRANSFERS
346
347 #undef T
348 #undef TRANSFERS
349
350 case TSX:
351 cpu->regs[X] = cpu->regs[SP];
352 stat_nz(cpu, cpu->regs[X]);
353 break;
354
355 case TXS:
356 cpu->regs[SP] = cpu->regs[X];
357 stat_nz(cpu, cpu->regs[X]);
358 break;
359
360 case PHA:
361 stack_push(cpu, cpu->regs[A]);
362 break;
363
364 case PLA:
365 cpu->regs[A] = stack_pop(cpu);
366 stat_nz(cpu, cpu->regs[A]);
367 break;
368
369 case PHP:
370 stack_push(cpu, *(uint8_t *)(&cpu->status));
371 break;
372
373 case PLP:
374 {
375 uint8_t s = stack_pop(cpu);
376 *(uint8_t *)(&cpu->status) = s;
377 }
378
379 case JMP:
380 cpu->pc = a.ptr;
381 break;
382
383 case JSR:
384 stack_pushle(cpu, cpu->pc);
385 break;
386
387 case RTS:
388 cpu->pc = stack_pople(cpu);
389 break;
390
391 // TODO: implement RTI
392 // TODO: implement flag instructions
393
394 case BRK:
395 // TODO: trigger an interrupt
396 cpu->running = false;
397 break;
398
399 case NOP:
400 break;
401
402 default:
403 die("Unsupported opcode: %x\n", op);
swissChili710d18d2020-07-29 19:43:20 -0700404 }
swissChili6264a3b2020-07-30 19:02:07 -0700405 #undef REGS
swissChili6c61a792020-07-28 16:29:20 -0700406}
407
swissChili6c61a792020-07-28 16:29:20 -0700408uint16_t fetch_le(cpu_t *cpu)
409{
410 uint8_t a = cpu->mem[cpu->pc++];
411 uint8_t b = cpu->mem[cpu->pc++];
412 return le_to_native(a, b);
413}
414
swissChili710d18d2020-07-29 19:43:20 -0700415arg_t arg_imm(uint16_t a)
416{
417 return (arg_t){ a, a };
418}
419
420arg_t arg_ptr(cpu_t *c, uint flags, uint16_t p)
421{
422 if (flags & FETCH_NO_INDIRECTION)
423 return arg_imm(p);
424
425 return (arg_t){ c->mem[p], p };
426}
427
428arg_t arg(uint16_t v, uint16_t a)
429{
430 return (arg_t){ v, a };
431}
432
433arg_t fetch_addr(cpu_t *cpu, uint8_t am, uint f)
swissChili6c61a792020-07-28 16:29:20 -0700434{
435 switch (am)
436 {
437 case AM_ACC:
438 case AM_IMP:
swissChili710d18d2020-07-29 19:43:20 -0700439 return arg_imm(0);
swissChili6c61a792020-07-28 16:29:20 -0700440
441 // In both cases return immediate 8 bit value
442 case AM_IMM:
443 case AM_ZP:
swissChili710d18d2020-07-29 19:43:20 -0700444 return arg_imm(cpu->mem[cpu->pc++]);
swissChili6c61a792020-07-28 16:29:20 -0700445
446 case AM_ABS:
swissChili710d18d2020-07-29 19:43:20 -0700447 return arg_ptr(cpu, f, fetch_le(cpu));
swissChili6c61a792020-07-28 16:29:20 -0700448
449 case AM_REL:
450 {
swissChili6264a3b2020-07-30 19:02:07 -0700451 // Aparently, PC should will point to the NEXT opcode
452 // I can't find any documentation on this unfortunately, but
453 // I have discovered this through testing the output of other
454 // assemblers.
455 uint16_t pc = cpu->pc + 1;
456 return arg_ptr(cpu, f, (int8_t)cpu->mem[cpu->pc++] + pc);
swissChili6c61a792020-07-28 16:29:20 -0700457 }
458
459 case AM_IND:
460 {
461 uint16_t addr = fetch_le(cpu);
swissChili6264a3b2020-07-30 19:02:07 -0700462
463 if (f & FETCH_NO_INDIRECTION)
464 return arg_imm(addr);
465
swissChili6c61a792020-07-28 16:29:20 -0700466 uint8_t low = cpu->mem[addr],
467 high = cpu->mem[addr + 1];
468
swissChili710d18d2020-07-29 19:43:20 -0700469 return arg_ptr(cpu, f, le_to_native(low, high));
swissChili6c61a792020-07-28 16:29:20 -0700470 }
471
472 case AM_AX:
swissChili710d18d2020-07-29 19:43:20 -0700473 return arg_ptr(cpu, f, fetch_le(cpu) + cpu->regs[X]);
swissChili6c61a792020-07-28 16:29:20 -0700474
475 case AM_AY:
swissChili710d18d2020-07-29 19:43:20 -0700476 return arg_ptr(cpu, f, fetch_le(cpu) + cpu->regs[Y]);
swissChili6c61a792020-07-28 16:29:20 -0700477
478 case AM_ZPX:
swissChili710d18d2020-07-29 19:43:20 -0700479 return arg_ptr(cpu, f, cpu->mem[cpu->pc++] + cpu->regs[X]);
swissChili6c61a792020-07-28 16:29:20 -0700480
481 case AM_ZPY:
swissChili710d18d2020-07-29 19:43:20 -0700482 return arg_ptr(cpu, f, cpu->mem[cpu->pc++] + cpu->regs[Y]);
swissChili6c61a792020-07-28 16:29:20 -0700483
484 case AM_ZIX:
485 {
486 uint8_t zp = cpu->mem[cpu->pc++];
swissChili6264a3b2020-07-30 19:02:07 -0700487
488 if (f & FETCH_NO_INDIRECTION)
489 return arg_imm(zp);
490
swissChili710d18d2020-07-29 19:43:20 -0700491 uint16_t addr = zp + cpu->regs[X];
492 uint16_t indirect = le_to_native(cpu->mem[addr], cpu->mem[addr + 1]);
493 return arg_ptr(cpu, f, indirect);
swissChili6c61a792020-07-28 16:29:20 -0700494 }
495
496 case AM_ZIY:
497 {
498 uint8_t zp = cpu->mem[cpu->pc++];
swissChili6264a3b2020-07-30 19:02:07 -0700499
500 if (f & FETCH_NO_INDIRECTION)
501 return arg_imm(zp);
502
swissChili6c61a792020-07-28 16:29:20 -0700503 uint16_t base = le_to_native(cpu->mem[zp], cpu->mem[zp + 1]);
swissChili710d18d2020-07-29 19:43:20 -0700504 return arg_ptr(cpu, f, base + cpu->regs[Y]);
swissChili6c61a792020-07-28 16:29:20 -0700505 }
506
507 default:
508 die("Unknown address mode %x", am);
swissChili710d18d2020-07-29 19:43:20 -0700509 __builtin_unreachable();
swissChili6c61a792020-07-28 16:29:20 -0700510 }
511}
512
513void step(cpu_t *cpu)
514{
515 switch (cpu->mem[cpu->pc++])
516 {
517#define INST(mn, am, op) \
518 case op: \
swissChilida4803e2020-08-06 20:06:04 -0700519 execute(cpu, #mn, mn, fetch_addr(cpu, am, 0), am); \
swissChili6c61a792020-07-28 16:29:20 -0700520 break;
521
522 INSTRUCTIONS
523
524#undef INST
525
526 default:
527 die("Undefined opcode");
528 }
529}
530
swissChilic51e9222020-08-07 16:09:14 -0700531int dump_inst(cpu_t *cpu, char *buf, const char *mn, uint16_t addr, uint8_t am)
swissChili6c61a792020-07-28 16:29:20 -0700532{
swissChilic51e9222020-08-07 16:09:14 -0700533 char *end = buf;
534 end += sprintf(end, "%s ", mn);
swissChili6c61a792020-07-28 16:29:20 -0700535
536 switch (am)
537 {
538 case AM_IMM:
swissChilic51e9222020-08-07 16:09:14 -0700539 end += sprintf(end, "#");
swissChili6c61a792020-07-28 16:29:20 -0700540 case AM_REL:
541 case AM_ABS:
542 case AM_ZP:
swissChilic51e9222020-08-07 16:09:14 -0700543 end += sprintf(end, "$%x", addr);
swissChili6c61a792020-07-28 16:29:20 -0700544 break;
545
546 case AM_IND:
swissChilic51e9222020-08-07 16:09:14 -0700547 end += sprintf(end, "($%x)", addr);
swissChili6c61a792020-07-28 16:29:20 -0700548 break;
549
550 case AM_AX:
551 case AM_ZPX:
swissChilic51e9222020-08-07 16:09:14 -0700552 end += sprintf(end, "$%x, X", addr);
swissChili6c61a792020-07-28 16:29:20 -0700553 break;
554
555 case AM_AY:
556 case AM_ZPY:
swissChilic51e9222020-08-07 16:09:14 -0700557 end += sprintf(end, "$%x, Y", addr);
swissChili6c61a792020-07-28 16:29:20 -0700558 break;
559
560 case AM_ZIX:
swissChilic51e9222020-08-07 16:09:14 -0700561 end += sprintf(end, "($%x, X)", addr);
swissChili6c61a792020-07-28 16:29:20 -0700562 break;
563
564 case AM_ZIY:
swissChilic51e9222020-08-07 16:09:14 -0700565 end += sprintf(end, "($%x), Y", addr);
swissChili6c61a792020-07-28 16:29:20 -0700566 break;
567 }
568
swissChilic51e9222020-08-07 16:09:14 -0700569 return end - buf;
swissChili6c61a792020-07-28 16:29:20 -0700570}
571
swissChilic51e9222020-08-07 16:09:14 -0700572char *disas_step(cpu_t *cpu)
swissChili6c61a792020-07-28 16:29:20 -0700573{
swissChilic51e9222020-08-07 16:09:14 -0700574 char *buffer = malloc(80);
575 char *end = buffer;
576
577 // end += sprintf(buffer, "$%x", cpu->pc);
swissChili6c61a792020-07-28 16:29:20 -0700578 uint8_t op = cpu->mem[cpu->pc++];
579 switch (op)
580 {
581#define INST(mn, am, op) \
582 case op: \
swissChilic51e9222020-08-07 16:09:14 -0700583 end += dump_inst(cpu, end, #mn, \
swissChili710d18d2020-07-29 19:43:20 -0700584 fetch_addr(cpu, am, FETCH_NO_INDIRECTION).ptr, am); \
swissChili6c61a792020-07-28 16:29:20 -0700585 break;
586
587 INSTRUCTIONS
588
589#undef INST
590
591 default:
swissChilic51e9222020-08-07 16:09:14 -0700592 end += sprintf(end, "Undefined opcode %x", op);
swissChili6c61a792020-07-28 16:29:20 -0700593 }
swissChilic51e9222020-08-07 16:09:14 -0700594
595 *end = 0;
596
597 return buffer;
swissChili6c61a792020-07-28 16:29:20 -0700598}
599
swissChilida4803e2020-08-06 20:06:04 -0700600void disas_num(cpu_t *cpu, uint16_t num)
601{
swissChilic51e9222020-08-07 16:09:14 -0700602 uint16_t pc = cpu->pc;
swissChilida4803e2020-08-06 20:06:04 -0700603 for (int i = 0; i < num; i++)
604 {
swissChilic51e9222020-08-07 16:09:14 -0700605 uint16_t last_pc = cpu->pc;
606 char *line = disas_step(cpu);
607 printf("$%x\t%s\n", last_pc, line);
608 free(line);
swissChilida4803e2020-08-06 20:06:04 -0700609 }
swissChilic51e9222020-08-07 16:09:14 -0700610 cpu->pc = pc;
swissChilida4803e2020-08-06 20:06:04 -0700611}
612
swissChili6c61a792020-07-28 16:29:20 -0700613void disas(cpu_t *cpu)
614{
swissChilic51e9222020-08-07 16:09:14 -0700615 uint16_t pc = cpu->pc;
swissChili6264a3b2020-07-30 19:02:07 -0700616 // Raw binary, no way to know what's code what isn't
swissChili6c61a792020-07-28 16:29:20 -0700617 while (cpu->pc < 0xFFFF)
618 {
swissChilic51e9222020-08-07 16:09:14 -0700619 uint16_t last_pc = cpu->pc;
620 char *line = disas_step(cpu);
621 printf("$%x\t%s\n", last_pc, line);
622 free(line);
swissChili6c61a792020-07-28 16:29:20 -0700623 }
swissChilic51e9222020-08-07 16:09:14 -0700624 cpu->pc = pc;
swissChili6c61a792020-07-28 16:29:20 -0700625}
swissChilic51e9222020-08-07 16:09:14 -0700626
swissChilida4803e2020-08-06 20:06:04 -0700627void run(cpu_t *cpu)
628{
629 while (cpu->running)
630 {
631 step(cpu);
632 }
633
634 printf("CPU Halted\n");
635}