nes/c65.c

436 lines
6.3 KiB
C

#include <stddef.h>
#include "c65.h"
#define PN 0x80
#define PV 0x40
#define PB 16
#define PD 8
#define PI 4
#define PZ 2
#define PC 1
#define setnz(x) c->p = (c->p & 0x7D) | ((!x) << 1) | (x & 0x7F)
#define pull_noinc() (c->r(c, 0x100|(c->s)))
#define pull() (c->r(c, 0x100|(++c->s)))
#define push(x) c->w(c, 0x100|(c->s--), x)
#define op(x) static void x(c65* c)
// yes these end with two braces. dont worry about it
#define swop(x) static void x(c65* c) { switch(c->cycle)
swop(i_brk) {
case 0:
c->r(c, c->ip);
break;
case 1:
push(c->ip >> 8);
break;
case 2:
push(c->ip);
break;
case 3:
push(c->p | PB); // TODO is B set in-register too?
break;
case 4:
c->ip = c->r(c, 0xFFFE);
break;
case 5:
c->ip |= c->r(c, 0xFFFF) << 8;
c->end++;
break;
}}
swop(s_plpa) {
case 0:
c->r(c, c->ip);
break;
case 1:
pull();
break;
case 2:
uint8_t s = pull_noinc();
if(c->op == 0x20) c->p = s | 0x20; // bit 5 always set
else {
setnz(s);
c->a = s;
}
c->end++;
break;
}}
op(s_phpa) {
// https://www.nesdev.org/wiki/Status_flags#The_B_flag
switch(c->cycle) {
case 0:
c->r(c, c->ip);
break;
case 1:
push(((c->op & 0x40) ? c->a : c->p) | 0x10); // php always pushes brk flag as set
c->end++;
break;
}
}
op(i_sef) {
static uint8_t t[4] = {0, 2, 6, 3};
uint8_t s = t[c->op >> 6];
c->p &= ~(1 << s) | (((c->op >> 5) & 1) << s); // set or clear flag depending on value of bit 5
c->r(c, c->ip); // TODO best guess
c->end++;
}
swop(a_jmp) {
case 0:
c->b = c->r(c, c->ip++);
break;
case 1:
c->ip = (c->r(c, c->ip) << 8) | c->b;
c->end++;
break;
}}
swop(r_bcc) { // TODO verify implementation
case 0:
c->b = c->r(c, c->ip++);
static char m[4] = {7, 6, 0, 9};
char d = m[c->op >> 6];
if(
(
(
c->p >> (d & 7)
) & 1
) ^ (d >> 3)
) c->end++; // branch not taken
break;
case 1:
c->ip++; // TODO correct?
c->r(c, ((c->ip + c->b) & 0xFF) | (c->ip & 0xFF00));
if(!((((c->ip & 0xFF) + ((int8_t)c->b)) >> 8) & 1)) c->end++; // no page crossing
c->ip += ((int8_t)c->b);
break;
case 2:
c->r(c, c->ip);
break;
}}
op(i_trans) {
switch(c->op) {
case 0x8A:
c->a = c->x;
setnz(c->a);
break;
case 0x98:
c->a = c->y;
setnz(c->a);
break;
case 0x9A:
c->s = c->x;
break;
case 0xA8:
c->y = c->a;
setnz(c->y);
break;
case 0xAA:
c->x = c->a;
setnz(c->x);
break;
case 0xBA:
c->x = c->s;
setnz(c->x);
break;
}
c->r(c, c->ip); // TODO best guess
c->end++;
}
op(i_dec) {
switch(c->op) {
case 0x88:
c->y--;
setnz(c->y);
break;
case 0xC8:
c->y++;
setnz(c->y);
break;
case 0xCA:
c->x--;
setnz(c->x);
break;
case 0xE8:
c->x++;
setnz(c->x);
break;
}
}
static void(*t[256])(c65*) = {
i_brk,
NULL, // 01
NULL, // 02
NULL, // 03
NULL, // 04
NULL, // 05
NULL, // 06
NULL, // 07
s_phpa, // 08
NULL, // 09
NULL, // 0A
NULL, // 0B
NULL, // 0C
NULL, // 0D
NULL, // 0E
NULL, // 0F
r_bcc, // 10
NULL, // 11
NULL, // 12
NULL, // 13
NULL, // 14
NULL, // 15
NULL, // 16
NULL, // 17
i_sef, // 18
NULL, // 19
NULL, // 1A
NULL, // 1B
NULL, // 1C
NULL, // 1D
NULL, // 1E
NULL, // 1F
NULL, // 20
NULL, // 21
NULL, // 22
NULL, // 23
NULL, // 24
NULL, // 25
NULL, // 26
NULL, // 27
s_plpa, // 28
NULL, // 29
NULL, // 2A
NULL, // 2B
NULL, // 2C
NULL, // 2D
NULL, // 2E
NULL, // 2F
r_bcc, // 30
NULL, // 31
NULL, // 32
NULL, // 33
NULL, // 34
NULL, // 35
NULL, // 36
NULL, // 37
i_sef, // 38
NULL, // 39
NULL, // 3A
NULL, // 3B
NULL, // 3C
NULL, // 3D
NULL, // 3E
NULL, // 3F
NULL, // 40
NULL, // 41
NULL, // 42
NULL, // 43
NULL, // 44
NULL, // 45
NULL, // 46
NULL, // 47
s_phpa, // 48
NULL, // 49
NULL, // 4A
NULL, // 4B
a_jmp, // 4C
NULL, // 4D
NULL, // 4E
NULL, // 4F
r_bcc, // 50
NULL, // 51
NULL, // 52
NULL, // 53
NULL, // 54
NULL, // 55
NULL, // 56
NULL, // 57
i_sef, // 58
NULL, // 59
NULL, // 5A
NULL, // 5B
NULL, // 5C
NULL, // 5D
NULL, // 5E
NULL, // 5F
NULL, // 60
NULL, // 61
NULL, // 62
NULL, // 63
NULL, // 64
NULL, // 65
NULL, // 66
NULL, // 67
s_plpa, // 68
NULL, // 69
NULL, // 6A
NULL, // 6B
NULL, // 6C
NULL, // 6D
NULL, // 6E
NULL, // 6F
r_bcc, // 70
NULL, // 71
NULL, // 72
NULL, // 73
NULL, // 74
NULL, // 75
NULL, // 76
NULL, // 77
i_sef, // 78
NULL, // 79
NULL, // 7A
NULL, // 7B
NULL, // 7C
NULL, // 7D
NULL, // 7E
NULL, // 7F
NULL, // 80
NULL, // 81
NULL, // 82
NULL, // 83
NULL, // 84
NULL, // 85
NULL, // 86
NULL, // 87
i_dec, // 88
NULL, // 89
i_trans, // 8A
NULL, // 8B
NULL, // 8C
NULL, // 8D
NULL, // 8E
NULL, // 8F
r_bcc, // 90
NULL, // 91
NULL, // 92
NULL, // 93
NULL, // 94
NULL, // 95
NULL, // 96
NULL, // 97
i_trans, // 98
NULL, // 99
i_trans, // 9A
NULL, // 9B
NULL, // 9C
NULL, // 9D
NULL, // 9E
NULL, // 9F
NULL, // A0
NULL, // A1
NULL, // A2
NULL, // A3
NULL, // A4
NULL, // A5
NULL, // A6
NULL, // A7
i_trans, // A8
NULL, // A9
i_trans, // AA
NULL, // AB
NULL, // AC
NULL, // AD
NULL, // AE
NULL, // AF
r_bcc, // B0
NULL, // B1
NULL, // B2
NULL, // B3
NULL, // B4
NULL, // B5
NULL, // B6
NULL, // B7
i_sef, // B8
NULL, // B9
i_trans, // BA
NULL, // BB
NULL, // BC
NULL, // BD
NULL, // BE
NULL, // BF
NULL, // C0
NULL, // C1
NULL, // C2
NULL, // C3
NULL, // C4
NULL, // C5
NULL, // C6
NULL, // C7
i_dec, // C8
NULL, // C9
i_dec, // CA
NULL, // CB
NULL, // CC
NULL, // CD
NULL, // CE
NULL, // CF
r_bcc, // D0
NULL, // D1
NULL, // D2
NULL, // D3
NULL, // D4
NULL, // D5
NULL, // D6
NULL, // D7
i_sef, // D8
NULL, // D9
NULL, // DA
NULL, // DB
NULL, // DC
NULL, // DD
NULL, // DE
NULL, // DF
NULL, // E0
NULL, // E1
NULL, // E2
NULL, // E3
NULL, // E4
NULL, // E5
NULL, // E6
NULL, // E7
i_dec, // E8
NULL, // E9
NULL, // EA
NULL, // EB
NULL, // EC
NULL, // ED
NULL, // EE
NULL, // EF
r_bcc, // F0
NULL, // F1
NULL, // F2
NULL, // F3
NULL, // F4
NULL, // F5
NULL, // F6
NULL, // F7
i_sef, // F8
NULL, // F9
NULL, // FA
NULL, // FB
NULL, // FC
NULL, // FD
NULL, // FE
NULL, // FF
};
void c65_s(c65* c) {
if(c->end) {
c->op = c->r(c, c->ip); // fetch should not be part of the opcode cycle
c->cycle = c->end = 0; // op. impl. expected to end with ip set to next
// instruction's address - meant for jmp mostly
} else {
if(t[c->op]) t[c->op](c);
c->cycle++;
}
}