-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy patholc6502.h
275 lines (232 loc) · 11 KB
/
olc6502.h
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
/*
olc6502 - An emulation of the 6502/2A03 processor
"Thanks Dad for believing computers were gonna be a big deal..." - javidx9
License (OLC-3)
~~~~~~~~~~~~~~~
Copyright 2018-2019 OneLoneCoder.com
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions or derivations of source code must retain the above
copyright notice, this list of conditions and the following disclaimer.
2. Redistributions or derivative works in binary form must reproduce
the above copyright notice. This list of conditions and the following
disclaimer must be reproduced in the documentation and/or other
materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Background
~~~~~~~~~~
I love this microprocessor. It was at the heart of two of my favourite
machines, the BBC Micro, and the Nintendo Entertainment System, as well
as countless others in that era. I learnt to program on the Model B, and
I learnt to love games on the NES, so in many ways, this processor is
why I am the way I am today.
In February 2019, I decided to undertake a selfish personal project and
build a NES emulator. Ive always wanted to, and as such I've avoided
looking at source code for such things. This made making this a real
personal challenge. I know its been done countless times, and very likely
in far more clever and accurate ways than mine, but I'm proud of this.
Datasheet: http://archive.6502.org/datasheets/rockwell_r650x_r651x.pdf
Files: olc6502.h, olc6502.cpp
Relevant Video: https://youtu.be/8XmxKPJDGU0
Links
~~~~~
YouTube: https://www.youtube.com/javidx9
https://www.youtube.com/javidx9extra
Discord: https://discord.gg/WhwHUMV
Twitter: https://www.twitter.com/javidx9
Twitch: https://www.twitch.tv/javidx9
GitHub: https://www.github.com/onelonecoder
Patreon: https://www.patreon.com/javidx9
Homepage: https://www.onelonecoder.com
Author
~~~~~~
David Barr, aka javidx9, ©OneLoneCoder 2019
*/
#pragma once
// With little modification, reliance upon the stdlib can
// be removed entirely if required.
// Ths is required for translation table and disassembler. The table
// could be implemented straight up as an array, but I used a vector.
#include <vector>
// These are required for disassembler. If you dont require disassembly
// then just remove the function.
#include <string>
#include <map>
// Emulation Behaviour Logging ======================================
// Uncomment this to create a logfile entry for each clock tick of
// the CPU. Beware: this slows down emulation considerably and
// generates extremely large files. I recommend "glogg" to view the
// data as it is designed to handle enormous files.
//
//#define LOGMODE // <- Uncomment me to enable logging!
#ifdef LOGMODE
#include <stdio.h>
#endif
// Forward declaration of generic communications bus class to
// prevent circular inclusions
class Bus;
// The 6502 Emulation Class. This is it!
class olc6502
{
public:
olc6502();
~olc6502();
public:
// CPU Core registers, exposed as public here for ease of access from external
// examinors. This is all the 6502 has.
uint8_t a = 0x00; // Accumulator Register
uint8_t x = 0x00; // X Register
uint8_t y = 0x00; // Y Register
uint8_t stkp = 0x00; // Stack Pointer (points to location on bus)
uint16_t pc = 0x0000; // Program Counter
uint8_t status = 0x00; // Status Register
// External event functions. In hardware these represent pins that are asserted
// to produce a change in state.
void reset(); // Reset Interrupt - Forces CPU into known state
void irq(); // Interrupt Request - Executes an instruction at a specific location
void nmi(); // Non-Maskable Interrupt Request - As above, but cannot be disabled
void clock(); // Perform one clock cycle's worth of update
// Indicates the current instruction has completed by returning true. This is
// a utility function to enable "step-by-step" execution, without manually
// clocking every cycle
bool complete();
// Link this CPU to a communications bus
void ConnectBus(Bus *n) { bus = n; }
// Produces a map of strings, with keys equivalent to instruction start locations
// in memory, for the specified address range
std::map<uint16_t, std::string> disassemble(uint16_t nStart, uint16_t nStop);
// The status register stores 8 flags. Ive enumerated these here for ease
// of access. You can access the status register directly since its public.
// The bits have different interpretations depending upon the context and
// instruction being executed.
enum FLAGS6502
{
C = (1 << 0), // Carry Bit
Z = (1 << 1), // Zero
I = (1 << 2), // Disable Interrupts
D = (1 << 3), // Decimal Mode (unused in this implementation)
B = (1 << 4), // Break
U = (1 << 5), // Unused
V = (1 << 6), // Overflow
N = (1 << 7), // Negative
};
private:
// Convenience functions to access status register
uint8_t GetFlag(FLAGS6502 f);
void SetFlag(FLAGS6502 f, bool v);
// Assisstive variables to facilitate emulation
uint8_t fetched = 0x00; // Represents the working input value to the ALU
uint16_t temp = 0x0000; // A convenience variable used everywhere
uint16_t addr_abs = 0x0000; // All used memory addresses end up in here
uint16_t addr_rel = 0x00; // Represents absolute address following a branch
uint8_t opcode = 0x00; // Is the instruction byte
uint8_t cycles = 0; // Counts how many cycles the instruction has remaining
uint32_t clock_count = 0; // A global accumulation of the number of clocks
// Linkage to the communications bus
Bus *bus = nullptr;
uint8_t read(uint16_t a);
void write(uint16_t a, uint8_t d);
// The read location of data can come from two sources, a memory address, or
// its immediately available as part of the instruction. This function decides
// depending on address mode of instruction byte
uint8_t fetch();
// This structure and the following vector are used to compile and store
// the opcode translation table. The 6502 can effectively have 256
// different instructions. Each of these are stored in a table in numerical
// order so they can be looked up easily, with no decoding required.
// Each table entry holds:
// Pneumonic : A textual representation of the instruction (used for disassembly)
// Opcode Function: A function pointer to the implementation of the opcode
// Opcode Address Mode : A function pointer to the implementation of the
// addressing mechanism used by the instruction
// Cycle Count : An integer that represents the base number of clock cycles the
// CPU requires to perform the instruction
struct INSTRUCTION
{
std::string name;
uint8_t (olc6502::*operate )(void) = nullptr;
uint8_t (olc6502::*addrmode)(void) = nullptr;
uint8_t cycles = 0;
};
std::vector<INSTRUCTION> lookup;
private:
// Addressing Modes =============================================
// The 6502 has a variety of addressing modes to access data in
// memory, some of which are direct and some are indirect (like
// pointers in C++). Each opcode contains information about which
// addressing mode should be employed to facilitate the
// instruction, in regards to where it reads/writes the data it
// uses. The address mode changes the number of bytes that
// makes up the full instruction, so we implement addressing
// before executing the instruction, to make sure the program
// counter is at the correct location, the instruction is
// primed with the addresses it needs, and the number of clock
// cycles the instruction requires is calculated. These functions
// may adjust the number of cycles required depending upon where
// and how the memory is accessed, so they return the required
// adjustment.
uint8_t IMP(); uint8_t IMM();
uint8_t ZP0(); uint8_t ZPX();
uint8_t ZPY(); uint8_t REL();
uint8_t ABS(); uint8_t ABX();
uint8_t ABY(); uint8_t IND();
uint8_t IZX(); uint8_t IZY();
private:
// Opcodes ======================================================
// There are 56 "legitimate" opcodes provided by the 6502 CPU. I
// have not modelled "unofficial" opcodes. As each opcode is
// defined by 1 byte, there are potentially 256 possible codes.
// Codes are not used in a "switch case" style on a processor,
// instead they are repsonisble for switching individual parts of
// CPU circuits on and off. The opcodes listed here are official,
// meaning that the functionality of the chip when provided with
// these codes is as the developers intended it to be. Unofficial
// codes will of course also influence the CPU circuitry in
// interesting ways, and can be exploited to gain additional
// functionality!
//
// These functions return 0 normally, but some are capable of
// requiring more clock cycles when executed under certain
// conditions combined with certain addressing modes. If that is
// the case, they return 1.
//
// I have included detailed explanations of each function in
// the class implementation file. Note they are listed in
// alphabetical order here for ease of finding.
uint8_t ADC(); uint8_t AND(); uint8_t ASL(); uint8_t BCC();
uint8_t BCS(); uint8_t BEQ(); uint8_t BIT(); uint8_t BMI();
uint8_t BNE(); uint8_t BPL(); uint8_t BRK(); uint8_t BVC();
uint8_t BVS(); uint8_t CLC(); uint8_t CLD(); uint8_t CLI();
uint8_t CLV(); uint8_t CMP(); uint8_t CPX(); uint8_t CPY();
uint8_t DEC(); uint8_t DEX(); uint8_t DEY(); uint8_t EOR();
uint8_t INC(); uint8_t INX(); uint8_t INY(); uint8_t JMP();
uint8_t JSR(); uint8_t LDA(); uint8_t LDX(); uint8_t LDY();
uint8_t LSR(); uint8_t NOP(); uint8_t ORA(); uint8_t PHA();
uint8_t PHP(); uint8_t PLA(); uint8_t PLP(); uint8_t ROL();
uint8_t ROR(); uint8_t RTI(); uint8_t RTS(); uint8_t SBC();
uint8_t SEC(); uint8_t SED(); uint8_t SEI(); uint8_t STA();
uint8_t STX(); uint8_t STY(); uint8_t TAX(); uint8_t TAY();
uint8_t TSX(); uint8_t TXA(); uint8_t TXS(); uint8_t TYA();
// I capture all "unofficial" opcodes with this function. It is
// functionally identical to a NOP
uint8_t XXX();
#ifdef LOGMODE
private:
FILE* logfile = nullptr;
#endif
};
// End of File - Jx9