OXYGENHUB
# 2980b7b6d7cd enhanced-x16-emulator / src / cpu / support.h
9.2 KiB · Objective-C · last changed chore(): initial commit 25 days ago source blame history raw
1
/*
2
3
						Extracted from original single fake6502.c file
4
5
*/
6
7
8
//flag modifier macros
9
#define setcarry() regs.status |= FLAG_CARRY
10
#define clearcarry() regs.status &= (~FLAG_CARRY)
11
#define setzero() regs.status |= FLAG_ZERO
12
#define clearzero() regs.status &= (~FLAG_ZERO)
13
#define setinterrupt() regs.status |= FLAG_INTERRUPT
14
#define clearinterrupt() regs.status &= (~FLAG_INTERRUPT)
15
#define setdecimal() regs.status |= FLAG_DECIMAL
16
#define cleardecimal() regs.status &= (~FLAG_DECIMAL)
17
#define setoverflow() regs.status |= FLAG_OVERFLOW
18
#define clearoverflow() regs.status &= (~FLAG_OVERFLOW)
19
#define setsign() regs.status |= FLAG_SIGN
20
#define clearsign() regs.status &= (~FLAG_SIGN)
21
22
23
//flag calculation macros
24
#define zerocalc(n, use16Bit) {\
25
    if (use16Bit) \
26
        if ((n) & 0xFFFF) clearzero();\
27
            else setzero();\
28
    else if ((n) & 0x00FF) clearzero();\
29
        else setzero();\
30
}
31
32
#define signcalc(n, use16Bit) {\
33
    if (use16Bit) \
34
        if ((n) & 0x8000) setsign();\
35
            else clearsign();\
36
    else if ((n) & 0x0080) setsign();\
37
        else clearsign();\
38
}
39
40
#define carrycalc(n, use16Bit) {\
41
    if (use16Bit) \
42
        if ((n) & 0x10000) setcarry();\
43
            else clearcarry();\
44
    else if ((n) & 0x0100) setcarry();\
45
        else clearcarry();\
46
}
47
48
#define overflowcalc8(n, m, o) { /* n = result, m = accumulator, o = memory */ \
49
    if (((n) ^ (m)) & ((n) ^ (o)) & 0x80) setoverflow();\
50
        else clearoverflow();\
51
}
52
53
#define overflowcalc16(n, m, o) { /* n = result, m = accumulator, o = memory */ \
54
    if (((n) ^ (m)) & ((n) ^ (o)) & 0x8000) setoverflow();\
55
        else clearoverflow();\
56
}
57
58
#define index_16bit() (regs.is65c816 && !(regs.status & FLAG_INDEX_WIDTH))
59
#define memory_16bit() (regs.is65c816 && !(regs.status & FLAG_MEMORY_WIDTH))
60
#define acc_for_mode() (memory_16bit() ? regs.c : ((uint16_t) regs.a))
61
62
// Bits 4 and 5 of P mean different things on the two CPUs, so pin them before
63
// each instruction according to which one is running.
64
//
65
// On the 65C816 they are the index and memory width flags, forced to 1 in
66
// emulation mode.
67
//
68
// On the 65C02 bit 5 is unused and reads as 1, while bit 4 is the break flag,
69
// which has no register existence -- it only appears in a copy of P pushed to
70
// the stack. No instruction alters it, so it must be preserved. Forcing it
71
// here corrupts it: ProcessorTests has 22,900 of 25,400 cases expecting it
72
// clear.
73
static inline void
74
cpu_pin_status_flags(void)
75
{
76
    if (regs.is65c816) {
77
        if (regs.e) {
78
            regs.status |= FLAG_INDEX_WIDTH | FLAG_MEMORY_WIDTH;
79
        }
80
    } else {
81
        regs.status |= FLAG_CONSTANT;
82
    }
83
}
84
85
// PLP and RTI load P from the stack. On the 65C02 bits 4 and 5 are not
86
// loadable -- there is no register bit behind either -- so they keep their
87
// previous values. On the 65C816 both are real width flags and load normally.
88
static inline uint8_t
89
cpu_merge_pulled_status(uint8_t pulled)
90
{
91
    const uint8_t keep = FLAG_INDEX_WIDTH | FLAG_CONSTANT;
92
    if (regs.is65c816) {
93
        return pulled;
94
    }
95
    return (uint8_t) ((pulled & (uint8_t) ~keep) | (regs.status & keep));
96
}
97
98
// A 16-bit operand is one more byte through the bus, and each byte costs a
99
// cycle: the datasheet's "M = 0 or X = 0, 16 bit operation, add 1 cycle". A
100
// read-modify-write pays twice, once in each direction.
101
//
102
// Accumulator addressing touches no memory, so ASL A costs the same at either
103
// width and must not be charged.
104
#define pay_for_wide_memory(n) \
105
    (penaltym = addressing_is_acc ? 0 : (uint8_t)(n))
106
107
108
#define saveaccum(n) (memory_16bit() ? (regs.c = (n)) : (regs.a = (uint8_t)((n) & 0x00FF)))
109
110
#define mask_long_addr(addr) ((addr) & 0xFFFFFF)
111
#define as_bank_byte(b) ((uint32_t) (((uint8_t) (b)) << 16))
112
#define bank_byte(b) ((uint8_t) ((b) >> 16))
113
114
//a few general functions used by various other functions
115
116
uint16_t add_wrap_at_page_boundary(uint16_t value, uint8_t add) {
117
    if (regs.e) {
118
        return (value & 0xFF00) | ((uint16_t) (((uint8_t) (value & 0x00FF)) + add) & 0x00FF);
119
    } else {
120
        return value + add;
121
    }
122
}
123
124
uint16_t subtract_wrap_at_page_boundary(uint16_t value, uint8_t subtract) {
125
    if (regs.e) {
126
        return (value & 0xFF00) | ((uint16_t) (((uint8_t) (value & 0x00FF)) - subtract) & 0x00FF);
127
    } else {
128
        return value - subtract;
129
    }
130
}
131
132
void increment_wrap_at_page_boundary(uint16_t *value) {
133
    if (regs.e) {
134
        *value = (*value & 0xFF00) | ((uint16_t) (((uint8_t) (*value & 0x00FF)) + 1) & 0x00FF);
135
    } else {
136
        (*value)++;
137
    }
138
}
139
140
void decrement_wrap_at_page_boundary(uint16_t *value) {
141
    if (regs.e) {
142
        *value = (*value & 0xFF00) | ((uint16_t) (((uint8_t) (*value & 0x00FF)) - 1) & 0x00FF);
143
    } else {
144
        (*value)--;
145
    }
146
}
147
148
uint16_t direct_page_add(uint16_t offset) {
149
    if (regs.e && (regs.dp & 0x00FF) == 0) {
150
        return (regs.dp & 0xFF00) | ((uint16_t) ((uint8_t) (regs.dp & 0x00FF)) + (offset & 0xFF));
151
    } else {
152
        return regs.dp + offset;
153
    }
154
}
155
156
#define incsp() increment_wrap_at_page_boundary(&regs.sp)
157
#define decsp() decrement_wrap_at_page_boundary(&regs.sp)
158
159
void push16(uint16_t pushval) {
160
    write6502(regs.sp, 0, (pushval >> 8) & 0xFF);
161
    decsp();
162
    write6502(regs.sp, 0, pushval & 0xFF);
163
    decsp();
164
}
165
166
void push8(uint8_t pushval) {
167
    write6502(regs.sp, 0, pushval);
168
    decsp();
169
}
170
171
uint16_t pull16() {
172
    incsp();
173
    uint16_t temp16 = read6502(regs.sp, 0);
174
    incsp();
175
    temp16 |= (uint16_t) read6502(regs.sp, 0) << 8;
176
    return temp16;
177
}
178
179
uint8_t pull8() {
180
    incsp();
181
    uint8_t value = read6502(regs.sp, 0);
182
    return value;
183
}
184
185
// The instructions the 65C816 added use the whole stack pointer even in
186
// emulation mode, where the ones inherited from the 6502 wrap inside page one.
187
// The high byte is forced back to $01 once the instruction finishes, so only
188
// the addresses touched during it tell the two apart.
189
void push16_long(uint16_t pushval) {
190
    write6502(regs.sp, 0, (pushval >> 8) & 0xFF);
191
    regs.sp--;
192
    write6502(regs.sp, 0, pushval & 0xFF);
193
    regs.sp--;
194
}
195
196
void push8_long(uint8_t pushval) {
197
    write6502(regs.sp, 0, pushval);
198
    regs.sp--;
199
}
200
201
uint16_t pull16_long() {
202
    regs.sp++;
203
    uint16_t temp16 = read6502(regs.sp, 0);
204
    regs.sp++;
205
    temp16 |= (uint16_t) read6502(regs.sp, 0) << 8;
206
    return temp16;
207
}
208
209
uint8_t pull8_long() {
210
    regs.sp++;
211
    return read6502(regs.sp, 0);
212
}
213
214
// Emulation mode keeps the stack pointer in page one. Applied after each
215
// instruction rather than inside the pushes, so a 16-bit access can cross the
216
// boundary and come back.
217
static inline void
218
cpu_pin_stack_page(void)
219
{
220
    if (regs.e) {
221
        regs.sp = 0x0100 | (regs.sp & 0x00FF);
222
    }
223
}
224
225
void reset6502(bool c816) {
226
    cpu_irq_ctx_reset();   // any handler in flight is abandoned by a reset
227
    regs.pc = (uint16_t)read6502(0xFFFC, 0) | ((uint16_t)read6502(0xFFFD, 0) << 8);
228
    regs.c = 0;
229
    regs.x = 0;
230
    regs.y = 0;
231
    regs.dp = 0;
232
    regs.sp = 0x1FD;
233
    regs.e = 1;
234
    regs.k = 0;
235
    regs.db = 0;
236
    if (c816) {
237
        regs.status |= FLAG_INDEX_WIDTH | FLAG_MEMORY_WIDTH;
238
        regs.is65c816 = true;
239
        ticktable = ticktable_c816;
240
        optable = optable_c816;
241
        addrtable = addrtable_c816;
242
    } else {
243
        regs.status |= FLAG_CONSTANT;
244
        regs.is65c816 = false;
245
        ticktable = ticktable_c02;
246
        optable = optable_c02;
247
        addrtable = addrtable_c02;
248
    }
249
    setinterrupt();
250
    cleardecimal();
251
    waiting = 0;
252
}
253
254
enum InterruptType {
255
    INT_COP = 0x4,
256
    INT_BRK = 0x6,
257
    INT_NMI = 0xA,
258
    INT_IRQ = 0xE
259
};
260
261
void interrupt6502(enum InterruptType vector) {
262
    // BRK and COP are opcodes, so their cycles come from the tick table. A
263
    // hardware interrupt is not dispatched through the table and is charged
264
    // here instead. Recorded before the BRK vector is rewritten to IRQ below.
265
    const bool from_opcode = (vector == INT_BRK || vector == INT_COP);
266
267
    // Tell the debugger an interrupt is being taken, before anything is pushed:
268
    // regs.pc is still the interrupted address and regs.sp is what the matching
269
    // RTI will restore. See cpu/irq_ctx.h.
270
    cpu_irq_ctx_enter((int)vector, regs.pc | ((uint32_t)regs.k << 16), regs.sp);
271
272
    if (!regs.e) {
273
        push8(regs.k);
274
    }
275
276
    regs.k = 0; // also in emulated mode
277
278
    push16(regs.pc);
279
280
    if (regs.e) {
281
        // BRK and COP are software interrupts and push P with the break flag
282
        // set; IRQ and NMI push it clear. The flag only exists in the pushed
283
        // copy, which is what distinguishes the two on return.
284
        if (vector == INT_BRK || vector == INT_COP) {
285
            push8(regs.status | FLAG_BREAK);
286
            vector = (vector == INT_BRK) ? INT_IRQ : vector;
287
        } else {
288
            push8(regs.status & ~FLAG_BREAK);
289
        }
290
    } else {
291
        push8(regs.status);
292
    }
293
294
    setinterrupt();
295
    cleardecimal();
296
    vp6502();
297
298
    uint16_t vector_address = (regs.e ? 0xFFF0 : 0xFFE0) + (uint8_t) vector;
299
    regs.pc = (uint16_t) read6502(vector_address, 0) | ((uint16_t) read6502(vector_address + 1, 0) << 8);
300
301
    if (!from_opcode) {
302
        clockticks6502 += 7; // consumed by CPU to process interrupt
303
    }
304
}
305
306
static uint32_t addr_with_db(uint16_t addr) {
307
    return mask_long_addr(as_bank_byte(regs.db) | addr);
308
}
309
310
static uint32_t addr_with_k(uint16_t addr) {
311
    return mask_long_addr(as_bank_byte(regs.k) | addr);
312
}