-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcartridge.cpp
More file actions
160 lines (129 loc) · 4.37 KB
/
cartridge.cpp
File metadata and controls
160 lines (129 loc) · 4.37 KB
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
#include "cartridge.hpp"
#include "mapper.hpp"
#include "mapper000.hpp"
#include "mapper001.hpp"
#include "mapper002.hpp"
#include "mapper003.hpp"
#include "mapper004.hpp"
#include "mapper007.hpp"
#include <fstream>
#include <exception>
class BadFileException : public std::exception {};
class BadMapperException : public std::exception {};
Cartridge:: ~Cartridge()
{
delete mapper;
}
void Cartridge::LoadRom(const std::string &ROMFilePath)
{
std::ifstream ROMFile;
ROMFile.open(ROMFilePath, std::ios::binary);
if (!ROMFile)
throw BadFileException();
struct Header
{
uint8_t NES[3];
uint8_t eof;
uint8_t nBanksPRG;
uint8_t nBanksCHR;
uint8_t flags6;
uint8_t flags7;
uint8_t flags8;
uint8_t flags9;
uint8_t flags10;
uint8_t padding[5];
} header;
ROMFile.read((char*)&header, sizeof header);
// if trainer is present skip 512 bytes
if (header.flags6 & 0x04)
ROMFile.seekg(512, std::ios::cur);
nBanksPRG = header.nBanksPRG;
PRG_ROM.Resize(nBanksPRG * 0x4000); // a bank of program memory is 16 KiB
ROMFile.read((char*)PRG_ROM.Data(), PRG_ROM.Size());
nBanksCHR = header.nBanksCHR;
if (nBanksCHR == 0)
nBanksCHR = 1;
CHR_ROM.Resize(nBanksCHR * 0x2000); // a bank of character memory is 8 KiB
ROMFile.read((char*)CHR_ROM.Data(), CHR_ROM.Size());
PRG_RAM.Resize(0x2000); // 8 KiB of PRG RAM (optional)
mapperID = header.flags7 & 0xF0 | header.flags6 >> 4;
if (header.flags6 & 0x01)
hardwareMirroringMode = MirroringMode::VERTICAL;
else // (header.flags6 & 0x01) == 0
hardwareMirroringMode = MirroringMode::HORIZONTAL;
switch (mapperID)
{
case 0:
mapper = new Mapper000(nBanksPRG, nBanksCHR);
break;
case 1:
mapper = new Mapper001(nBanksPRG, nBanksCHR * 2); // 2 * number of 8 KiB CHR banks -> number of 4 KiB CHR banks
break;
case 2:
mapper = new Mapper002(nBanksPRG, nBanksCHR);
break;
case 3:
mapper = new Mapper003(nBanksPRG, nBanksCHR);
break;
case 4: // 2 * number of 16 KiB PRG banks -> number of 8 KiB PRG banks
mapper = new Mapper004(nBanksPRG * 2, nBanksCHR * 8); // 8 * number of 8 KiB CHR banks -> number of 1 KiB CHR banks
break;
case 7:
mapper = new Mapper007(nBanksPRG / 2, nBanksCHR); // number of 16 KiB PRG banks / 2 -> number of 32 KiB PRG banks
break;
default:
throw BadMapperException();
break;
}
ROMFile.close();
}
MirroringMode Cartridge::GetMirroringMode()
{
MirroringMode mirroringMode = mapper->GetMirroringMode();
if (mirroringMode == MirroringMode::HARDWARE) // set by hardware on the cartridge
return hardwareMirroringMode;
else // dynamically set by mapping circuit
return mirroringMode;
}
uint8_t Cartridge::CPURead(uint16_t address) // CPU read 0x4020 - 0xFFFF
{
bool fromRAM = false;
uint32_t mappedAddress = mapper->MapReadPRG(address, fromRAM);
if (fromRAM)
return PRG_RAM[mappedAddress];
else
return PRG_ROM[mappedAddress];
}
void Cartridge::CPUWrite(uint16_t address, uint8_t data) // CPU write 0x4020 - 0xFFFF
{
bool toRAM = false;
uint32_t mappedAddress = mapper->MapWritePRG(address, data, toRAM);
if (toRAM)
PRG_RAM[mappedAddress] = data;
}
uint8_t Cartridge::PPURead(uint16_t address) // PPU read 0x0000 - 0x1FFFF
{
uint32_t mappedAddress = mapper->MapReadCHR(address);
return CHR_ROM[mappedAddress];
}
void Cartridge::PPUWrite(uint16_t address, uint8_t data) // PPU write 0x0000 - 0x1FFFF
{
uint32_t mappedAddress = mapper->MapWriteCHR(address);
CHR_ROM[mappedAddress] = data;
}
bool Cartridge::InterruptAsserted()
{
if (mapperID == 4)
return static_cast<Mapper004*>(mapper)->InterruptAsserted();
return false;
}
void Cartridge::AcknowledgeInterrupt()
{
if (mapperID == 4)
static_cast<Mapper004*>(mapper)->AcknowledgeInterrupt();
}
void Cartridge::CountPPUScanline()
{
if (mapperID == 4)
static_cast<Mapper004*>(mapper)->CountPPUScanline();
}