OXYGENHUB
# 8799a5e4565e x16-emulator / src / cpu / fake6502.c
10.5 KiB · C · last changed chore(): initial commit 15 days ago source blame history raw
1
/* Fake6502 CPU emulator core v1.1 *******************
2
 * (c)2011 Mike Chambers ([email protected])       *
3
 *****************************************************
4
 * v1.1 - Small bugfix in BIT opcode, but it was the *
5
 *        difference between a few games in my NES   *
6
 *        emulator working and being broken!         *
7
 *        I went through the rest carefully again    *
8
 *        after fixing it just to make sure I didn't *
9
 *        have any other typos! (Dec. 17, 2011)      *
10
 *                                                   *
11
 * v1.0 - First release (Nov. 24, 2011)              *
12
 *****************************************************
13
 * LICENSE: This source code is released into the    *
14
 * public domain, but if you use it please do give   *
15
 * credit. I put a lot of effort into writing this!  *
16
 *                                                   *
17
 *****************************************************
18
 * Fake6502 is a MOS Technology 6502 CPU emulation   *
19
 * engine in C. It was written as part of a Nintendo *
20
 * Entertainment System emulator I've been writing.  *
21
 *                                                   *
22
 * If you do discover an error in timing accuracy,   *
23
 * or operation in general please e-mail me at the   *
24
 * address above so that I can fix it. Thank you!    *
25
 *                                                   *
26
 *****************************************************
27
 * Usage:                                            *
28
 *                                                   *
29
 * Fake6502 requires you to provide two external     *
30
 * functions:                                        *
31
 *                                                   *
32
 * uint8_t read6502(uint16_t address, uint8_t bank)  *
33
 * void write6502(uint16_t address, uint8_t bank,    *
34
 * uint8_t value)                                    *
35
 *                                                   *
36
 * You may optionally pass Fake6502 the pointer to a *
37
 * function which you want to be called after every  *
38
 * emulated instruction. This function should be a   *
39
 * void with no parameters expected to be passed to  *
40
 * it.                                               *
41
 *                                                   *
42
 * This can be very useful. For example, in a NES    *
43
 * emulator, you check the number of clock ticks     *
44
 * that have passed so you can know when to handle   *
45
 * APU events.                                       *
46
 *                                                   *
47
 * To pass Fake6502 this pointer, use the            *
48
 * hookexternal(void *funcptr) function provided.    *
49
 *                                                   *
50
 * To disable the hook later, pass NULL to it.       *
51
 *****************************************************
52
 * Useful functions in this emulator:                *
53
 *                                                   *
54
 * void reset6502(bool c816)                         *
55
 *   - Call this once before you begin execution.    *
56
 *                                                   *
57
 * void exec6502(uint32_t tickcount)                 *
58
 *   - Execute 6502 code up to the next specified    *
59
 *     count of clock ticks.                         *
60
 *                                                   *
61
 * void step6502()                                   *
62
 *   - Execute a single instrution.                  *
63
 *                                                   *
64
 * void irq6502()                                    *
65
 *   - Trigger a hardware IRQ in the 6502 core.      *
66
 *                                                   *
67
 * void nmi6502()                                    *
68
 *   - Trigger an NMI in the 6502 core.              *
69
 *                                                   *
70
 * void hookexternal(void *funcptr)                  *
71
 *   - Pass a pointer to a void function taking no   *
72
 *     parameters. This will cause Fake6502 to call  *
73
 *     that function once after each emulated        *
74
 *     instruction.                                  *
75
 *                                                   *
76
 *****************************************************
77
 * Useful variables in this emulator:                *
78
 *                                                   *
79
 * uint32_t clockticks6502                           *
80
 *   - A running total of the emulated cycle count.  *
81
 *                                                   *
82
 * uint32_t instructions                             *
83
 *   - A running total of the total emulated         *
84
 *     instruction count. This is not related to     *
85
 *     clock cycle timing.                           *
86
 *                                                   *
87
 *****************************************************/
88

89
#include "registers.h"
90

91
#include <stdio.h>
92
#include <stdint.h>
93
#include <stdbool.h>
94

95
// 6502 / 65816 registers
96

97
struct regs regs;
98

99
//helper variables
100
uint32_t instructions = 0; //keep track of total instructions executed
101
uint32_t clockticks6502 = 0, clockgoal6502 = 0;
102
uint16_t opcode_addr, oldpc, reladdr, value;
103
uint32_t ea;
104
uint32_t result;
105
uint8_t opcode, oldstatus;
106

107
bool warn_rockwell = true;
108

109
uint8_t penaltyop, penaltyaddr;
110
uint8_t penaltym = 0;
111
uint8_t penaltye = 0;
112
uint8_t penaltyn = 0;
113
uint8_t penaltyx = 0;
114
uint8_t penaltyd = 0;
115
uint8_t waiting = 0;
116

117
//externally supplied functions
118
extern uint8_t read6502(uint16_t address, uint8_t bank);
119
extern void write6502(uint16_t address, uint8_t bank, uint8_t value);
120
extern void stop6502(uint16_t address, uint8_t bank);
121
extern void vp6502();
122
extern uint8_t memory_get_ram_bank();
123
extern uint8_t memory_get_rom_bank();
124

125
static void (*addrtable_c02[256])();
126
static void (*addrtable_c816[256])();
127
static void (*optable_c02[256])();
128
static void (*optable_c816[256])();
129

130
static const uint32_t ticktable_c02[256];
131
static const uint32_t ticktable_c816[256];
132

133
static void (**addrtable)();
134
static void (**optable)();
135
static const uint32_t *ticktable;
136

137
#include "support.h"
138
#include "modes.h"
139

140
void rockwell_warning(const char *instruction) {
141
    uint8_t pc_bank;
142

143
    if (opcode_addr < 0xa000) {
144
        pc_bank = 0;
145
    } else if (opcode_addr < 0xc000) {
146
        pc_bank = memory_get_ram_bank();
147
    } else {
148
        pc_bank = memory_get_rom_bank();
149
    }
150

151
    printf("Warning: encountered Rockwell instruction %s at $%02x:%04x.\n", instruction, pc_bank, opcode_addr);
152
    printf("\tFuture Commander X16 hardware may ship with a 65C816 CPU,\n");
153
    printf("\twhich does not support these instructions.\n");
154
    printf("\tThis will be the only warning given for Rockwell\n");
155
    printf("\tinstructions until the emulator is relaunched.\n");
156
    printf("\tPass -rockwell to the command line to suppress this warning.\n\n");
157

158
    warn_rockwell = false;
159
}
160

161
static uint8_t wrappedBankByte(uint32_t addr) {
162
    if (addrtable[opcode] == zp || addrtable[opcode] == zpx || addrtable[opcode] == zpy) {
163
        return 0;
164
    }
165
    else {
166
        return bank_byte(addr+1);
167
    }
168
}
169

170
static uint16_t getvalue(bool use16Bit) {
171
    if (addrtable[opcode] == acc) {
172
        return use16Bit ? regs.c : (uint16_t)regs.a;
173
    } else if (use16Bit) {
174
        return ((uint16_t)read6502(ea, bank_byte(ea)) | ((uint16_t)read6502(ea+1, wrappedBankByte(ea)) << 8));
175
    }
176
    return read6502(ea, bank_byte(ea));
177
}
178

179
__attribute__((unused)) static uint16_t getvalue16() {
180
    return((uint16_t)read6502(ea, bank_byte(ea)) | ((uint16_t)read6502(ea+1, wrappedBankByte(ea)) << 8));
181
}
182

183
static void putvalue(uint16_t saveval, bool use16Bit) {
184
    if (addrtable[opcode] == acc) {
185
        if (use16Bit) {
186
            regs.c = saveval;
187
        } else {
188
            regs.a = (uint8_t)(saveval & 0x00FF);
189
        }
190
    } else if (use16Bit) {
191
        write6502(ea, bank_byte(ea), (saveval & 0x00FF));
192
        write6502(ea + 1, wrappedBankByte(ea), saveval >> 8);
193
    } else {
194
        write6502(ea, bank_byte(ea), (saveval & 0x00FF));
195
    }
196
}
197

198
#include "instructions.h"
199
#include "65c02.h"
200
#include "tables.h"
201

202
void nmi6502() {
203
    interrupt6502(INT_NMI);
204
    waiting = 0;
205
}
206

207
void irq6502() {
208
    if (!(regs.status & FLAG_INTERRUPT)) {
209
        interrupt6502(INT_IRQ);
210
    }
211
    waiting = 0;
212
}
213

214
uint8_t callexternal = 0;
215
void (*loopexternal)();
216

217
void exec6502(uint32_t tickcount) {
218
	if (waiting) {
219
		clockticks6502 += tickcount;
220
		clockgoal6502 = clockticks6502;
221
		return;
222
    }
223

224
    opcode_addr = regs.pc;
225

226
    clockgoal6502 += tickcount;
227

228
    while (clockticks6502 < clockgoal6502) {
229
        opcode = read6502(regs.pc++, regs.k);
230

231
        if (regs.e) {
232
            regs.status |= FLAG_INDEX_WIDTH | FLAG_MEMORY_WIDTH;
233
        }
234

235
        penaltyop = 0;
236
        penaltyaddr = 0;
237
        penaltym = 0;
238
        penaltye = 0;
239
        penaltyn = 0;
240
        penaltyx = 0;
241

242
        (*addrtable[opcode])();
243
        (*optable[opcode])();
244
        clockticks6502 += ticktable[opcode];
245

246
        if (!regs.e && penaltyop && penaltyaddr) clockticks6502++;
247
        if (memory_16bit()) clockticks6502 += penaltym;
248
        if (index_16bit()) clockticks6502 += penaltyx;
249
        if (penaltyn && !regs.e) clockticks6502++;
250
        if (penaltye && regs.e) clockticks6502++;
251

252
        instructions++;
253

254
        if (callexternal) (*loopexternal)();
255
    }
256
}
257

258
void step6502() {
259
	if (waiting) {
260
		++clockticks6502;
261
		clockgoal6502 = clockticks6502;
262
		return;
263
	}
264

265
    opcode_addr = regs.pc;
266

267
    opcode = read6502(regs.pc++, regs.k);
268

269
    if (regs.e) {
270
        regs.status |= FLAG_INDEX_WIDTH | FLAG_MEMORY_WIDTH;
271
    }
272

273
    penaltyop = 0;
274
    penaltyaddr = 0;
275
    penaltym = 0;
276
    penaltye = 0;
277
    penaltyn = 0;
278
    penaltyx = 0;
279

280
    (*addrtable[opcode])();
281
    (*optable[opcode])();
282
    clockticks6502 += ticktable[opcode];
283

284
    if (penaltyop && penaltyaddr) clockticks6502++;
285
    if (memory_16bit()) clockticks6502 += penaltym;
286
    if (index_16bit()) clockticks6502 += penaltyx;
287
    if (penaltyn && !regs.e) clockticks6502++;
288
    if (penaltye && regs.e) clockticks6502++;
289
    if (penaltyd) clockticks6502 ++;
290
    clockgoal6502 = clockticks6502;
291

292
    instructions++;
293

294
    if (callexternal) (*loopexternal)();
295
}
296

297
void hookexternal(void *funcptr) {
298
    if (funcptr != (void *)NULL) {
299
        loopexternal = funcptr;
300
        callexternal = 1;
301
    } else callexternal = 0;
302
}
303

304
//  Fixes from http://6502.org/tutorials/65c02opcodes.html
305
//
306
//  65C02 Cycle Count differences.
307
//        ADC/SBC work differently in decimal mode.
308
//        The wraparound fixes may not be required.