yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
memory.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <bit>
5#include <cstdint>
6#include <vector>
7
8#include "util/log.h"
9
10namespace yaze {
11namespace emu {
12
13namespace {
14
15// Largest cartridge address space any mapper reaches (ExHiROM, 8 MB). A larger
16// mapping window adds no reachable offsets.
17constexpr uint32_t kMaxMappedRomSize = 0x800000;
18
19} // namespace
20
21void MemoryImpl::Initialize(const std::vector<uint8_t>& rom_data,
22 bool verbose) {
23 verbose_ = verbose;
24 type_ = 1; // LoROM
25
26 // Validate ROM data size before accessing header
27 // LoROM header is at 0x7FC0, and we need to access bytes at 0x7FD7 and 0x7FD8
28 constexpr uint32_t kLoRomHeaderLocation = 0x7FC0;
29 constexpr uint32_t kMinRomSizeForHeader = 0x7FD9; // 0x7FC0 + 0x18 + 1
30
31 uint32_t header_rom_size = 0;
32 if (rom_data.size() < kMinRomSizeForHeader) {
34 "Memory",
35 "ROM too small for header access: %zu bytes (need at least %u bytes)",
36 rom_data.size(), kMinRomSizeForHeader);
37 // Fallback: size ROM from the file alone if header is not accessible
38 sram_size_ = 0x2000; // Default 8KB SRAM
39 } else {
40 auto location = kLoRomHeaderLocation; // LoROM header location
41 uint8_t rom_size_shift = rom_data[location + 0x17];
42 uint8_t sram_size_shift = rom_data[location + 0x18];
43
44 // Validate shift values to prevent excessive memory allocation
45 if (rom_size_shift > 15) { // Max reasonable shift (0x400 << 15 = 32MB)
46 LOG_DEBUG("Memory", "Invalid ROM size shift: %u, using file size",
47 rom_size_shift);
48 } else {
49 header_rom_size = 0x400u << rom_size_shift;
50 }
51
52 if (sram_size_shift > 7) { // Max reasonable shift (0x400 << 7 = 512KB)
53 LOG_DEBUG("Memory", "Invalid SRAM size shift: %u, using default",
54 sram_size_shift);
55 sram_size_ = 0x2000; // Default 8KB SRAM
56 } else {
57 sram_size_ = 0x400 << sram_size_shift;
58 }
59 }
60
61 // Size the mapping window from the larger of the header size and the file
62 // size, rounded up to a power of two so the address mask is well formed.
63 // Expanded hacks keep the vanilla header byte (0x0A = 1 MB); trusting it
64 // alone would fold every bank past the first 1 MB back onto it. Reads past
65 // the loaded bytes return open bus (see ReadRom).
66 const uint32_t file_rom_size = static_cast<uint32_t>(
67 std::min<size_t>(rom_data.size(), kMaxMappedRomSize));
68 rom_size_ =
69 std::min(std::bit_ceil(std::max({header_rom_size, file_rom_size, 1u})),
70 kMaxMappedRomSize);
71
72 // Keep every file byte; never truncate to the header size.
73 rom_.assign(rom_data.begin(), rom_data.end());
74
75 ram_.resize(sram_size_);
76 std::fill(ram_.begin(), ram_.end(), 0);
77
78 LOG_DEBUG("Memory",
79 "LoROM initialized: ROM map size=$%06X (%uKB) file=$%06zX "
80 "header=$%06X SRAM size=$%04X",
81 rom_size_, rom_size_ / 1024, rom_data.size(), header_rom_size,
83
84 // Log reset vector if ROM is large enough
85 if (rom_data.size() >= 0x7FFE) {
86 LOG_DEBUG("Memory", "Reset vector at ROM offset $7FFC-$7FFD = $%02X%02X",
87 rom_data[0x7FFD], rom_data[0x7FFC]);
88 } else {
89 LOG_DEBUG("Memory", "ROM too small to read reset vector (size: %zu bytes)",
90 rom_data.size());
91 }
92}
93
94uint8_t MemoryImpl::cart_read(uint8_t bank, uint16_t adr) {
95 // Emulator uses this path for all ROM/cart reads
96 switch (type_) {
97 case 0:
98 return open_bus_;
99 case 1:
100 return cart_readLorom(bank, adr);
101 case 2:
102 return cart_readHirom(bank, adr);
103 case 3:
104 return cart_readExHirom(bank, adr);
105 }
106 return open_bus_;
107}
108
109void MemoryImpl::cart_write(uint8_t bank, uint16_t adr, uint8_t val) {
110 switch (type_) {
111 case 0:
112 break;
113 case 1:
114 cart_writeLorom(bank, adr, val);
115 break;
116 case 2:
117 cart_writeHirom(bank, adr, val);
118 break;
119 case 3:
120 cart_writeHirom(bank, adr, val);
121 break;
122 }
123}
124
125uint8_t MemoryImpl::cart_readLorom(uint8_t bank, uint16_t adr) {
126 // SRAM access: banks 70-7d and f0-ff, addresses 0000-7fff
127 if (((bank >= 0x70 && bank < 0x7e) || bank >= 0xf0) && adr < 0x8000 &&
128 sram_size_ > 0) {
129 return ram_[(((bank & 0xf) << 15) | adr) & (sram_size_ - 1)];
130 }
131
132 // ROM access: banks 00-7f (mirrored to 80-ff), addresses 8000-ffff
133 // OR banks 40-7f, all addresses
134 bank &= 0x7f;
135 if (adr >= 0x8000 || bank >= 0x40) {
136 return ReadRom((bank << 15) | (adr & 0x7fff));
137 }
138
139 return open_bus_;
140}
141
142void MemoryImpl::cart_writeLorom(uint8_t bank, uint16_t adr, uint8_t val) {
143 // Must match the SRAM window in cart_readLorom.
144 if (((bank >= 0x70 && bank < 0x7e) || bank >= 0xf0) && adr < 0x8000 &&
145 sram_size_ > 0) {
146 // banks 70-7d and f0-ff, adr 0000-7fff
147 ram_[(((bank & 0xf) << 15) | adr) & (sram_size_ - 1)] = val;
148 }
149}
150
151uint8_t MemoryImpl::cart_readHirom(uint8_t bank, uint16_t adr) {
152 bank &= 0x7f;
153 if (bank < 0x40 && adr >= 0x6000 && adr < 0x8000 && sram_size_ > 0) {
154 // banks 00-3f and 80-bf, adr 6000-7fff
155 return ram_[(((bank & 0x3f) << 13) | (adr & 0x1fff)) & (sram_size_ - 1)];
156 }
157 if (adr >= 0x8000 || bank >= 0x40) {
158 // adr 8000-ffff in all banks or all addresses in banks 40-7f and c0-ff
159 return ReadRom(((bank & 0x3f) << 16) | adr);
160 }
161 return open_bus_;
162}
163
164uint8_t MemoryImpl::cart_readExHirom(uint8_t bank, uint16_t adr) {
165 if ((bank & 0x7f) < 0x40 && adr >= 0x6000 && adr < 0x8000 && sram_size_ > 0) {
166 // banks 00-3f and 80-bf, adr 6000-7fff
167 return ram_[(((bank & 0x3f) << 13) | (adr & 0x1fff)) & (sram_size_ - 1)];
168 }
169 bool secondHalf = bank < 0x80;
170 bank &= 0x7f;
171 if (adr >= 0x8000 || bank >= 0x40) {
172 // adr 8000-ffff in all banks or all addresses in banks 40-7f and c0-ff
173 return ReadRom(((bank & 0x3f) << 16) | (secondHalf ? 0x400000 : 0) | adr);
174 }
175 return open_bus_;
176}
177
178void MemoryImpl::cart_writeHirom(uint8_t bank, uint16_t adr, uint8_t val) {
179 bank &= 0x7f;
180 if (bank < 0x40 && adr >= 0x6000 && adr < 0x8000 && sram_size_ > 0) {
181 // banks 00-3f and 80-bf, adr 6000-7fff
182 ram_[(((bank & 0x3f) << 13) | (adr & 0x1fff)) & (sram_size_ - 1)] = val;
183 }
184}
185
186uint32_t MemoryImpl::GetMappedAddress(uint32_t address) const {
187 // NOTE: This function is only used by ROM editor via Memory interface.
188 // The emulator core uses cart_read/cart_write instead.
189 // Returns identity mapping for now - full implementation not needed for
190 // emulator.
191 return address;
192}
193
194} // namespace emu
195} // namespace yaze
uint8_t ReadRom(uint32_t offset) const
Definition memory.h:293
std::vector< uint8_t > rom_
Definition memory.h:285
std::vector< uint8_t > ram_
Definition memory.h:286
uint8_t cart_readHirom(uint8_t bank, uint16_t adr)
Definition memory.cc:151
uint32_t GetMappedAddress(uint32_t address) const
Definition memory.cc:186
uint8_t cart_read(uint8_t bank, uint16_t adr)
Definition memory.cc:94
void cart_writeLorom(uint8_t bank, uint16_t adr, uint8_t val)
Definition memory.cc:142
uint8_t cart_readLorom(uint8_t bank, uint16_t adr)
Definition memory.cc:125
uint8_t cart_readExHirom(uint8_t bank, uint16_t adr)
Definition memory.cc:164
void cart_write(uint8_t bank, uint16_t adr, uint8_t val)
Definition memory.cc:109
void cart_writeHirom(uint8_t bank, uint16_t adr, uint8_t val)
Definition memory.cc:178
void Initialize(const std::vector< uint8_t > &romData, bool verbose=false)
Definition memory.cc:21
uint32_t sram_size_
Definition memory.h:309
#define LOG_DEBUG(category, format,...)
Definition log.h:133