-
Notifications
You must be signed in to change notification settings - Fork 1
/
vm.c
100 lines (87 loc) · 3.02 KB
/
vm.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#define SETNZ Z = (data == 0); N = (data > 127);
//#define SETNZ DeferZ = DeferN = data;
//#define SETNZ goto SetNZ;
#define RD(addr) Read(addr)
#define RDW(addr) ReadWord(addr)
#define LO RD(PC+1)
#define HI RD(PC+2)
#define HILO ReadWord(PC+1)
#define AGEN_IMM
#define AGEN_ZP addr = LO;
#define AGEN_ZPX addr = (ushort)((LO+X)&0xFF); // Zero page wraps around
#define AGEN_ZPY addr = (ushort)((LO+Y)&0xFF); // Zero page wraps around
#define AGEN_A
#define AGEN_ABS addr = HILO;
// Extra cycle if we cross a page on a 4-cycle opcode
#define AGEN_ABSX { \
ushort baseAddr = HILO; \
addr = (ushort)(baseAddr+X); \
if(CYCLES == 4 && (addr & 0xFF00) != (baseAddr & 0xFF00)) \
WaitCycles+=3; \
}
// Extra cycle if we cross a page on a 4-cycle opcode
#define AGEN_ABSY { \
ushort baseAddr = HILO; \
addr = (ushort)(baseAddr+Y); \
if(CYCLES == 4 && (addr & 0xFF00) != (baseAddr & 0xFF00)) \
WaitCycles+=3; \
}
// Only used for jump indirect -- simulate JMP INDIRECT page wraparound "bug"
#define AGEN_IND { \
ushort addr1 = HILO; \
byte addr2_lo = RD(addr1); \
addr1 = (ushort)((addr1 & 0xFF00) | ((addr1+1)&0xFF)); \
byte addr2_hi = RD(addr1); \
addr = (ushort)(addr2_lo | (addr2_hi<<8)); \
}
#define AGEN_INDX addr = ReadWordZP((ushort)(LO+X));
// Indirect Indexed -- extra cycle if we cross a page boundary on a 5 cycle opcode
#define AGEN_INDY { \
ushort baseAddr = ReadWordZP(LO); \
addr = (ushort)(baseAddr+Y); \
if(CYCLES == 5 && (addr & 0xFF00) != (baseAddr & 0xFF00)) \
WaitCycles+=3; \
}
#define AGEN_IMPL
#define AGEN_REL
// TODO: For indirect, the next byte is wraps around to beginning of page! :(
#define READ_IMM data = LO;
#define READ_ZP data = RD(addr);
#define READ_ZPX data = RD(addr);
#define READ_ZPY data = RD(addr);
#define READ_A data = A;
#define READ_ABS data = RD(addr);
#define READ_ABSX data = RD(addr);
#define READ_ABSY data = RD(addr);
#define READ_IND data = RD(addr);
#define READ_INDX data = RD(addr);
#define READ_INDY data = RD(addr);
#define READ_IMPL
#define READ_REL // TODO: Hmm
#define WRITE_IMM
#define WRITE_ZP Write(addr, data);
#define WRITE_ZPX Write(addr, data);
#define WRITE_ZPY Write(addr, data);
#define WRITE_A A = data;
#define WRITE_ABS Write(addr, data);
#define WRITE_ABSX Write(addr, data);
#define WRITE_ABSY Write(addr, data);
#define WRITE_IND
#define WRITE_INDX Write(addr, data);
#define WRITE_INDY Write(addr, data);
#define WRITE_IMPL
#define WRITE_REL // TODO: Hmm
// Untaken branches take 2 cycles.
// Taken branches take 3 cycles.
// Taken branches crossing page boundaries take 4 cycles.
// Page boundary cross is defined as when the high byte of the next instruction != the high byte of the taken target.
#define BRANCH(cond) if(cond) { \
int offset = (sbyte)LO; \
ushort takenTarget = (ushort)(PC+2+offset); \
if((takenTarget >> 8) != (NPC >> 8)) \
WaitCycles += 2*3; \
else \
WaitCycles += 3; \
NPC = takenTarget; \
}
#include "vm_out.c"