blob: 5a7c39196076c96f5937c59c678dadbe4700fa79 [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
swissChilida4803e2020-08-06 20:06:04 -070033uint16_t le_to_native(uint8_t a, uint8_t b)
34{
35#ifdef LITTLE_ENDIAN
36 return b << 8 | a;
37#else
38 return a << 8 | b;
39#endif
40}
41
42void native_to_le(uint16_t n, uint8_t *a, uint8_t *b)
43{
44#ifdef LITTLE_ENDIAN
45 *a = n >> 8;
46 *b = n & 0xFF;
47#else
48 *a = n & 0xFF;
49 *b = n >> 8;
50#endif
51}
52
swissChili6264a3b2020-07-30 19:02:07 -070053void stack_push(cpu_t *cpu, uint8_t v)
54{
55 cpu->mem[cpu->regs[SP]-- + 0x100] = v;
56}
57
swissChilida4803e2020-08-06 20:06:04 -070058void stack_pushle(cpu_t *cpu, uint16_t v)
59{
60 uint8_t a, b;
61 native_to_le(v, &a, &b);
62 // push in "reverse" order so that the address is stored as LE
63 stack_push(cpu, b);
64 stack_push(cpu, a);
65}
66
swissChili6264a3b2020-07-30 19:02:07 -070067uint8_t stack_pop(cpu_t *cpu)
68{
69 return cpu->mem[cpu->regs[SP]++ + 0x100];
70}
71
swissChilida4803e2020-08-06 20:06:04 -070072uint16_t stack_pople(cpu_t *cpu)
73{
74 uint8_t a = stack_pop(cpu);
75 uint8_t b = stack_pop(cpu);
76 return le_to_native(a, b);
77}
78
swissChili6c61a792020-07-28 16:29:20 -070079void free_cpu(cpu_t *cpu)
80{
81 free(cpu->mem);
82}
83
swissChilida4803e2020-08-06 20:06:04 -070084// rotate right
85uint8_t ror(uint8_t a, uint8_t n)
86{
87 return (a >> n) | (a << (8 - n));
88}
89
90// rotate left
91uint8_t rol(uint8_t a, uint8_t n)
92{
93 return (a << n) | (a >> (8 - n));
94}
95
swissChili6264a3b2020-07-30 19:02:07 -070096void stat_nz(cpu_t *cpu, int8_t v)
swissChili6c61a792020-07-28 16:29:20 -070097{
swissChili6264a3b2020-07-30 19:02:07 -070098 cpu->status.negative = v < 0;
99 cpu->status.zero = v == 0;
100}
101
102// Used to check for overflow, is c unique?
103bool last_unique(bool a, bool b, bool c)
104{
105 return a == b && a != c;
106}
107
108void stat_cv(cpu_t *cpu, uint8_t a, uint8_t b, uint8_t c)
109{
110 cpu->status.overflow = last_unique(a >> 7, b >> 7, c >> 7);
111 cpu->status.carry = c < a || c < b;
112}
113
swissChilida4803e2020-08-06 20:06:04 -0700114void cmp(cpu_t *cpu, uint8_t reg, uint8_t mem)
115{
116 cpu->status.negative = 0;
117 cpu->status.zero = 0;
118 cpu->status.carry = 0;
119 if (cpu->regs[reg] < mem)
120 {
121 cpu->status.negative = 1;
122 }
123 else if (cpu->regs[reg] == mem)
124 {
125 cpu->status.zero = 1;
126 cpu->status.carry = 1;
127 }
128 else
129 {
130 cpu->status.carry = 1;
131 }
132}
133
134void execute(cpu_t *cpu, const char *mnemonic, uint8_t op, arg_t a, uint8_t am)
swissChili6264a3b2020-07-30 19:02:07 -0700135{
136 // used to save space
137 #define REGS \
138 R(X) R(A) R(Y)
139
swissChili710d18d2020-07-29 19:43:20 -0700140 switch (op) {
swissChili6264a3b2020-07-30 19:02:07 -0700141 // Load and store instructions:
142 #define R(reg) \
143 case LD##reg: \
144 cpu->regs[reg] = a.val; \
145 stat_nz(cpu, a.val); \
146 break;
147
148 REGS
149
150 #undef R
151
152 #define R(reg) \
153 case ST##reg: \
154 cpu->mem[a.ptr] = cpu->regs[reg]; \
155 break; \
156
157 REGS
158
159 #undef R
160
161 // Arithmetic instructions:
162 // NOTE: binary coded decimals are NOT SUPPORTED because I don't want
163 // to implement them.
164 case ADC:
165 {
166 uint8_t sum = cpu->regs[A] + a.val + cpu->status.carry;
167 // signed overflow
168 stat_cv(cpu, cpu->regs[A], a.val + cpu->status.carry, sum);
169 stat_nz(cpu, sum);
170 cpu->regs[A] = sum;
171 break;
172 }
173
174 case SBC:
175 {
176 uint8_t diff = cpu->regs[A] - a.val - !cpu->status.carry;
177 stat_cv(cpu, cpu->regs[A], a.val - !cpu->status.carry, diff);
178 stat_nz(cpu, diff);
179 cpu->regs[A] = diff;
180 break;
181 }
182
183 case INC:
184 cpu->mem[a.ptr]++;
185 stat_nz(cpu, cpu->mem[a.ptr]);
186 break;
187
188 case INX:
189 cpu->regs[X]++;
190 stat_nz(cpu, cpu->regs[X]);
191 break;
192
193 case INY:
194 cpu->regs[Y]++;
195 stat_nz(cpu, cpu->regs[Y]);
196 break;
197
198 case DEC:
199 cpu->mem[a.ptr]--;
200 stat_nz(cpu, cpu->mem[a.ptr]);
201 break;
swissChilida4803e2020-08-06 20:06:04 -0700202
203 case DEX:
204 cpu->regs[X]--;
205 stat_nz(cpu, cpu->regs[X]);
206 break;
207
208 case DEY:
209 cpu->regs[Y]--;
210 stat_nz(cpu, cpu->regs[Y]);
211 break;
212
213 case ASL:
214 // This check must be done here unfortunately, it would be nice
215 // to do this while decoding operands but it would require
216 // a substantial change to the architecture of the emulator
217 if (am == AM_ACC)
218 {
219 cpu->status.carry = cpu->regs[A] >> 7;
220 cpu->regs[A] <<= 1;
221 stat_nz(cpu, cpu->regs[A]);
222 }
223 else
224 {
225 cpu->status.carry = cpu->mem[a.val] >> 7;
226 cpu->mem[a.ptr] <<= 1;
227 stat_nz(cpu, cpu->mem[a.ptr]);
228 }
229 break;
230
231 case LSR:
232 if (am == AM_ACC)
233 {
234 cpu->status.carry = cpu->regs[A] & 1;
235 cpu->regs[A] >>= 1;
236 stat_nz(cpu, cpu->regs[A]);
237 }
238 else
239 {
240 cpu->status.carry = cpu->mem[a.val] & 7;
241 cpu->mem[a.ptr] >>= 1;
242 stat_nz(cpu, cpu->mem[a.ptr]);
243 }
244 break;
245
246 case ROL:
247 if (am == AM_ACC)
248 {
249 cpu->status.carry = cpu->regs[A] >> 7;
250 cpu->regs[A] = rol(cpu->regs[A], 1);
251 stat_nz(cpu, cpu->regs[A]);
252 }
253 else
254 {
255 cpu->status.carry = cpu->mem[a.val] >> 7;
256 cpu->mem[a.ptr] = rol(a.val, 1);
257 stat_nz(cpu, cpu->mem[a.ptr]);
258 }
259 break;
260
261 case ROR:
262 if (am == AM_ACC)
263 {
264 cpu->status.carry = cpu->regs[A] & 1;
265 cpu->regs[A] = ror(cpu->regs[A], 1);
266 stat_nz(cpu, cpu->regs[A]);
267 }
268 else
269 {
270 cpu->status.carry = cpu->mem[a.val] & 1;
271 cpu->mem[a.ptr] = ror(a.val, 1);
272 stat_nz(cpu, cpu->mem[a.ptr]);
273 }
274 break;
275
276 case AND:
277 cpu->regs[A] &= a.val;
278 stat_nz(cpu, cpu->regs[A]);
279 break;
280
281 case ORA:
282 cpu->regs[A] |= a.val;
283 stat_nz(cpu, cpu->regs[A]);
284 break;
285
286 case EOR:
287 cpu->regs[A] ^= a.val;
288 stat_nz(cpu, cpu->regs[A]);
289 break;
290
291 case CMP:
292 cmp(cpu, A, a.val);
293 break;
294
295 case CPX:
296 cmp(cpu, X, a.val);
297 break;
298
299 case CPY:
300 cmp(cpu, Y, a.val);
301 break;
302
303 // TODO: implement BIT here
304
305 #define BRANCHES \
306 B(BCC, carry == 0) \
307 B(BCS, carry == 1) \
308 B(BNE, zero == 0) \
309 B(BEQ, zero == 1) \
310 B(BPL, negative == 0) \
311 B(BMI, negative == 1) \
312 B(BVC, overflow == 0) \
313 B(BVS, overflow == 1)
314
315 #define B(i, c) \
316 case i: \
317 if (cpu->status . c) \
318 cpu->pc = a.ptr;\
319 break;
320
321 BRANCHES
322
323 #undef B
324 #undef BRANCHES
325
326 #define TRANSFERS \
327 T(A, X) \
328 T(X, A) \
329 T(A, Y) \
330 T(Y, A)
331
332 #define T(a, b) \
333 case T ## a ## b: \
334 cpu->regs[b] = cpu->regs[a]; \
335 stat_nz(cpu, cpu->regs[b]); \
336 break;
337
338 TRANSFERS
339
340 #undef T
341 #undef TRANSFERS
342
343 case TSX:
344 cpu->regs[X] = cpu->regs[SP];
345 stat_nz(cpu, cpu->regs[X]);
346 break;
347
348 case TXS:
349 cpu->regs[SP] = cpu->regs[X];
350 stat_nz(cpu, cpu->regs[X]);
351 break;
352
353 case PHA:
354 stack_push(cpu, cpu->regs[A]);
355 break;
356
357 case PLA:
358 cpu->regs[A] = stack_pop(cpu);
359 stat_nz(cpu, cpu->regs[A]);
360 break;
361
362 case PHP:
363 stack_push(cpu, *(uint8_t *)(&cpu->status));
364 break;
365
366 case PLP:
367 {
368 uint8_t s = stack_pop(cpu);
369 *(uint8_t *)(&cpu->status) = s;
370 }
371
372 case JMP:
373 cpu->pc = a.ptr;
374 break;
375
376 case JSR:
377 stack_pushle(cpu, cpu->pc);
378 break;
379
380 case RTS:
381 cpu->pc = stack_pople(cpu);
382 break;
383
384 // TODO: implement RTI
385 // TODO: implement flag instructions
386
387 case BRK:
388 // TODO: trigger an interrupt
389 cpu->running = false;
390 break;
391
392 case NOP:
393 break;
394
395 default:
396 die("Unsupported opcode: %x\n", op);
swissChili710d18d2020-07-29 19:43:20 -0700397 }
swissChili6264a3b2020-07-30 19:02:07 -0700398 #undef REGS
swissChili6c61a792020-07-28 16:29:20 -0700399}
400
swissChili6c61a792020-07-28 16:29:20 -0700401uint16_t fetch_le(cpu_t *cpu)
402{
403 uint8_t a = cpu->mem[cpu->pc++];
404 uint8_t b = cpu->mem[cpu->pc++];
405 return le_to_native(a, b);
406}
407
swissChili710d18d2020-07-29 19:43:20 -0700408arg_t arg_imm(uint16_t a)
409{
410 return (arg_t){ a, a };
411}
412
413arg_t arg_ptr(cpu_t *c, uint flags, uint16_t p)
414{
415 if (flags & FETCH_NO_INDIRECTION)
416 return arg_imm(p);
417
418 return (arg_t){ c->mem[p], p };
419}
420
421arg_t arg(uint16_t v, uint16_t a)
422{
423 return (arg_t){ v, a };
424}
425
426arg_t fetch_addr(cpu_t *cpu, uint8_t am, uint f)
swissChili6c61a792020-07-28 16:29:20 -0700427{
428 switch (am)
429 {
430 case AM_ACC:
431 case AM_IMP:
swissChili710d18d2020-07-29 19:43:20 -0700432 return arg_imm(0);
swissChili6c61a792020-07-28 16:29:20 -0700433
434 // In both cases return immediate 8 bit value
435 case AM_IMM:
436 case AM_ZP:
swissChili710d18d2020-07-29 19:43:20 -0700437 return arg_imm(cpu->mem[cpu->pc++]);
swissChili6c61a792020-07-28 16:29:20 -0700438
439 case AM_ABS:
swissChili710d18d2020-07-29 19:43:20 -0700440 return arg_ptr(cpu, f, fetch_le(cpu));
swissChili6c61a792020-07-28 16:29:20 -0700441
442 case AM_REL:
443 {
swissChili6264a3b2020-07-30 19:02:07 -0700444 // Aparently, PC should will point to the NEXT opcode
445 // I can't find any documentation on this unfortunately, but
446 // I have discovered this through testing the output of other
447 // assemblers.
448 uint16_t pc = cpu->pc + 1;
449 return arg_ptr(cpu, f, (int8_t)cpu->mem[cpu->pc++] + pc);
swissChili6c61a792020-07-28 16:29:20 -0700450 }
451
452 case AM_IND:
453 {
454 uint16_t addr = fetch_le(cpu);
swissChili6264a3b2020-07-30 19:02:07 -0700455
456 if (f & FETCH_NO_INDIRECTION)
457 return arg_imm(addr);
458
swissChili6c61a792020-07-28 16:29:20 -0700459 uint8_t low = cpu->mem[addr],
460 high = cpu->mem[addr + 1];
461
swissChili710d18d2020-07-29 19:43:20 -0700462 return arg_ptr(cpu, f, le_to_native(low, high));
swissChili6c61a792020-07-28 16:29:20 -0700463 }
464
465 case AM_AX:
swissChili710d18d2020-07-29 19:43:20 -0700466 return arg_ptr(cpu, f, fetch_le(cpu) + cpu->regs[X]);
swissChili6c61a792020-07-28 16:29:20 -0700467
468 case AM_AY:
swissChili710d18d2020-07-29 19:43:20 -0700469 return arg_ptr(cpu, f, fetch_le(cpu) + cpu->regs[Y]);
swissChili6c61a792020-07-28 16:29:20 -0700470
471 case AM_ZPX:
swissChili710d18d2020-07-29 19:43:20 -0700472 return arg_ptr(cpu, f, cpu->mem[cpu->pc++] + cpu->regs[X]);
swissChili6c61a792020-07-28 16:29:20 -0700473
474 case AM_ZPY:
swissChili710d18d2020-07-29 19:43:20 -0700475 return arg_ptr(cpu, f, cpu->mem[cpu->pc++] + cpu->regs[Y]);
swissChili6c61a792020-07-28 16:29:20 -0700476
477 case AM_ZIX:
478 {
479 uint8_t zp = cpu->mem[cpu->pc++];
swissChili6264a3b2020-07-30 19:02:07 -0700480
481 if (f & FETCH_NO_INDIRECTION)
482 return arg_imm(zp);
483
swissChili710d18d2020-07-29 19:43:20 -0700484 uint16_t addr = zp + cpu->regs[X];
485 uint16_t indirect = le_to_native(cpu->mem[addr], cpu->mem[addr + 1]);
486 return arg_ptr(cpu, f, indirect);
swissChili6c61a792020-07-28 16:29:20 -0700487 }
488
489 case AM_ZIY:
490 {
491 uint8_t zp = cpu->mem[cpu->pc++];
swissChili6264a3b2020-07-30 19:02:07 -0700492
493 if (f & FETCH_NO_INDIRECTION)
494 return arg_imm(zp);
495
swissChili6c61a792020-07-28 16:29:20 -0700496 uint16_t base = le_to_native(cpu->mem[zp], cpu->mem[zp + 1]);
swissChili710d18d2020-07-29 19:43:20 -0700497 return arg_ptr(cpu, f, base + cpu->regs[Y]);
swissChili6c61a792020-07-28 16:29:20 -0700498 }
499
500 default:
501 die("Unknown address mode %x", am);
swissChili710d18d2020-07-29 19:43:20 -0700502 __builtin_unreachable();
swissChili6c61a792020-07-28 16:29:20 -0700503 }
504}
505
506void step(cpu_t *cpu)
507{
508 switch (cpu->mem[cpu->pc++])
509 {
510#define INST(mn, am, op) \
511 case op: \
swissChilida4803e2020-08-06 20:06:04 -0700512 execute(cpu, #mn, mn, fetch_addr(cpu, am, 0), am); \
swissChili6c61a792020-07-28 16:29:20 -0700513 break;
514
515 INSTRUCTIONS
516
517#undef INST
518
519 default:
520 die("Undefined opcode");
521 }
522}
523
524void dump_inst(cpu_t *cpu, const char *mn, uint16_t addr, uint8_t am)
525{
526 printf("\t%s\t", mn);
527
528 switch (am)
529 {
530 case AM_IMM:
531 printf("#");
532 case AM_REL:
533 case AM_ABS:
534 case AM_ZP:
535 printf("$%x", addr);
536 break;
537
538 case AM_IND:
539 printf("($%x)", addr);
540 break;
541
542 case AM_AX:
543 case AM_ZPX:
544 printf("$%x, X", addr);
545 break;
546
547 case AM_AY:
548 case AM_ZPY:
549 printf("$%x, Y", addr);
550 break;
551
552 case AM_ZIX:
553 printf("($%x, X)", addr);
554 break;
555
556 case AM_ZIY:
557 printf("($%x), Y", addr);
558 break;
559 }
560
561 printf("\n");
562}
563
564void disas_step(cpu_t *cpu)
565{
swissChili6264a3b2020-07-30 19:02:07 -0700566 printf("$%x", cpu->pc);
swissChili6c61a792020-07-28 16:29:20 -0700567 uint8_t op = cpu->mem[cpu->pc++];
568 switch (op)
569 {
570#define INST(mn, am, op) \
571 case op: \
swissChili710d18d2020-07-29 19:43:20 -0700572 dump_inst(cpu, #mn, \
573 fetch_addr(cpu, am, FETCH_NO_INDIRECTION).ptr, am); \
swissChili6c61a792020-07-28 16:29:20 -0700574 break;
575
576 INSTRUCTIONS
577
578#undef INST
579
580 default:
swissChili6264a3b2020-07-30 19:02:07 -0700581 warn("\tUndefined opcode %x", op);
swissChili6c61a792020-07-28 16:29:20 -0700582 }
583}
584
swissChilida4803e2020-08-06 20:06:04 -0700585void disas_num(cpu_t *cpu, uint16_t num)
586{
587 for (int i = 0; i < num; i++)
588 {
589 disas_step(cpu);
590 }
591}
592
swissChili6c61a792020-07-28 16:29:20 -0700593void disas(cpu_t *cpu)
594{
swissChili6264a3b2020-07-30 19:02:07 -0700595 // Raw binary, no way to know what's code what isn't
swissChili6c61a792020-07-28 16:29:20 -0700596 while (cpu->pc < 0xFFFF)
597 {
598 disas_step(cpu);
599 }
600}
swissChilida4803e2020-08-06 20:06:04 -0700601void run(cpu_t *cpu)
602{
603 while (cpu->running)
604 {
605 step(cpu);
606 }
607
608 printf("CPU Halted\n");
609}