@tmustier/pi-nes 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +89 -1
- package/README.md +77 -48
- package/extensions/nes/config.ts +32 -12
- package/extensions/nes/native/nes-core/Cargo.lock +0 -2
- package/extensions/nes/native/nes-core/Cargo.toml +1 -1
- package/extensions/nes/native/nes-core/index.d.ts +5 -0
- package/extensions/nes/native/nes-core/index.node +0 -0
- package/extensions/nes/native/nes-core/native.d.ts +5 -0
- package/extensions/nes/native/nes-core/src/lib.rs +25 -0
- package/extensions/nes/native/nes-core/vendor/nes_rust/.cargo-ok +1 -0
- package/extensions/nes/native/nes-core/vendor/nes_rust/.cargo_vcs_info.json +5 -0
- package/extensions/nes/native/nes-core/vendor/nes_rust/.travis.yml +3 -0
- package/extensions/nes/native/nes-core/vendor/nes_rust/Cargo.toml +23 -0
- package/extensions/nes/native/nes-core/vendor/nes_rust/LICENSE +21 -0
- package/extensions/nes/native/nes-core/vendor/nes_rust/README.md +59 -0
- package/extensions/nes/native/nes-core/vendor/nes_rust/src/apu.rs +1114 -0
- package/extensions/nes/native/nes-core/vendor/nes_rust/src/audio.rs +6 -0
- package/extensions/nes/native/nes-core/vendor/nes_rust/src/button.rs +23 -0
- package/extensions/nes/native/nes-core/vendor/nes_rust/src/cpu.rs +2364 -0
- package/extensions/nes/native/nes-core/vendor/nes_rust/src/default_audio.rs +49 -0
- package/extensions/nes/native/nes-core/vendor/nes_rust/src/default_display.rs +47 -0
- package/extensions/nes/native/nes-core/vendor/nes_rust/src/default_input.rs +33 -0
- package/extensions/nes/native/nes-core/vendor/nes_rust/src/display.rs +10 -0
- package/extensions/nes/native/nes-core/vendor/nes_rust/src/input.rs +7 -0
- package/extensions/nes/native/nes-core/vendor/nes_rust/src/joypad.rs +86 -0
- package/extensions/nes/native/nes-core/vendor/nes_rust/src/lib.rs +168 -0
- package/extensions/nes/native/nes-core/vendor/nes_rust/src/mapper.rs +502 -0
- package/extensions/nes/native/nes-core/vendor/nes_rust/src/memory.rs +73 -0
- package/extensions/nes/native/nes-core/vendor/nes_rust/src/ppu.rs +1378 -0
- package/extensions/nes/native/nes-core/vendor/nes_rust/src/register.rs +560 -0
- package/extensions/nes/native/nes-core/vendor/nes_rust/src/rom.rs +231 -0
- package/extensions/nes/nes-core.ts +27 -4
- package/package.json +1 -1
- package/spec.md +2 -4
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
pub struct MapperFactory;
|
|
2
|
+
use rom::Mirrorings;
|
|
3
|
+
use rom::RomHeader;
|
|
4
|
+
use register::Register;
|
|
5
|
+
|
|
6
|
+
impl MapperFactory {
|
|
7
|
+
pub fn create(header: &RomHeader) -> Box<dyn Mapper> {
|
|
8
|
+
match header.mapper_num() {
|
|
9
|
+
0 => Box::new(NRomMapper::new(header)),
|
|
10
|
+
1 => Box::new(MMC1Mapper::new(header)),
|
|
11
|
+
2 => Box::new(UNRomMapper::new(header)),
|
|
12
|
+
3 => Box::new(CNRomMapper::new()),
|
|
13
|
+
4 => Box::new(MMC3Mapper::new(header)),
|
|
14
|
+
_ => panic!("Unsupported mapper {}", header.mapper_num())
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
pub trait Mapper {
|
|
20
|
+
// Maps 0x8000 - 0xFFFF to the program rom address
|
|
21
|
+
fn map(&self, address: u32) -> u32;
|
|
22
|
+
|
|
23
|
+
// Maps 0x0000 - 0x1FFF to the character rom address
|
|
24
|
+
fn map_for_chr_rom(&self, address: u32) -> u32;
|
|
25
|
+
|
|
26
|
+
// Writes control register inside in general
|
|
27
|
+
fn store(&mut self, address: u32, value: u8);
|
|
28
|
+
|
|
29
|
+
fn has_mirroring_type(&self) -> bool;
|
|
30
|
+
|
|
31
|
+
fn mirroring_type(&self) -> Mirrorings;
|
|
32
|
+
|
|
33
|
+
// @TODO: MMC3Mapper specific. Should this method be here?
|
|
34
|
+
fn drive_irq_counter(&mut self) -> bool;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
pub struct NRomMapper {
|
|
38
|
+
program_bank_num: u8
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
impl NRomMapper {
|
|
42
|
+
fn new(header: &RomHeader) -> Self {
|
|
43
|
+
NRomMapper {
|
|
44
|
+
program_bank_num: header.prg_rom_bank_num()
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
impl Mapper for NRomMapper {
|
|
50
|
+
/**
|
|
51
|
+
* if program_bank_num == 1:
|
|
52
|
+
* 0x8000 - 0xBFFF: 0x0000 - 0x3FFF
|
|
53
|
+
* 0xC000 - 0xFFFF: 0x0000 - 0x3FFF
|
|
54
|
+
* else:
|
|
55
|
+
* 0x8000 - 0xFFFF: 0x0000 - 0x7FFF
|
|
56
|
+
*/
|
|
57
|
+
fn map(&self, mut address: u32) -> u32 {
|
|
58
|
+
if self.program_bank_num == 1 && address >= 0xC000 {
|
|
59
|
+
address -= 0x4000;
|
|
60
|
+
}
|
|
61
|
+
address - 0x8000
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 0x0000 - 0x1FFF: 0x0000 - 0x1FFF
|
|
66
|
+
*/
|
|
67
|
+
fn map_for_chr_rom(&self, address: u32) -> u32 {
|
|
68
|
+
address
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Nothing to do
|
|
73
|
+
*/
|
|
74
|
+
fn store(&mut self, _address: u32, _value: u8) {
|
|
75
|
+
// throw exception?
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
fn has_mirroring_type(&self) -> bool {
|
|
79
|
+
false
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
fn mirroring_type(&self) -> Mirrorings {
|
|
83
|
+
Mirrorings::SingleScreen // dummy
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
fn drive_irq_counter(&mut self) -> bool {
|
|
87
|
+
false
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
pub struct MMC1Mapper {
|
|
92
|
+
program_bank_num: u8,
|
|
93
|
+
control_register: Register<u8>,
|
|
94
|
+
chr_bank0_register: Register<u8>,
|
|
95
|
+
chr_bank1_register: Register<u8>,
|
|
96
|
+
prg_bank_register: Register<u8>,
|
|
97
|
+
latch: Register<u8>,
|
|
98
|
+
register_write_count: u32
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
impl MMC1Mapper {
|
|
102
|
+
fn new(header: &RomHeader) -> Self {
|
|
103
|
+
let mut control_register = Register::<u8>::new();
|
|
104
|
+
control_register.store(0x0C);
|
|
105
|
+
MMC1Mapper {
|
|
106
|
+
program_bank_num: header.prg_rom_bank_num(),
|
|
107
|
+
control_register: control_register,
|
|
108
|
+
chr_bank0_register: Register::<u8>::new(),
|
|
109
|
+
chr_bank1_register: Register::<u8>::new(),
|
|
110
|
+
prg_bank_register: Register::<u8>::new(),
|
|
111
|
+
latch: Register::<u8>::new(),
|
|
112
|
+
register_write_count: 0
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
impl Mapper for MMC1Mapper {
|
|
118
|
+
fn map(&self, address: u32) -> u32 {
|
|
119
|
+
let bank: u32;
|
|
120
|
+
let mut offset = address & 0x3FFF;
|
|
121
|
+
let bank_num = self.prg_bank_register.load() as u32 & 0x0F;
|
|
122
|
+
|
|
123
|
+
match self.control_register.load_bits(2, 2) {
|
|
124
|
+
0 | 1 => {
|
|
125
|
+
// switch 32KB at 0x8000, ignoring low bit of bank number
|
|
126
|
+
// TODO: Fix me
|
|
127
|
+
offset = offset | (address & 0x4000);
|
|
128
|
+
bank = bank_num & 0x0E;
|
|
129
|
+
},
|
|
130
|
+
2 => {
|
|
131
|
+
// fix first bank at 0x8000 and switch 16KB bank at 0xC000
|
|
132
|
+
bank = match address < 0xC000 {
|
|
133
|
+
true => 0,
|
|
134
|
+
false => bank_num
|
|
135
|
+
};
|
|
136
|
+
},
|
|
137
|
+
_ /*3*/ => {
|
|
138
|
+
// fix last bank at 0xC000 and switch 16KB bank at 0x8000
|
|
139
|
+
bank = match address >= 0xC000 {
|
|
140
|
+
true => self.program_bank_num as u32 - 1,
|
|
141
|
+
false => bank_num
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
bank * 0x4000 + offset
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
fn map_for_chr_rom(&self, address: u32) -> u32 {
|
|
149
|
+
let bank: u32;
|
|
150
|
+
let mut offset = address & 0x0FFF;
|
|
151
|
+
if self.control_register.load_bit(4) == 0 {
|
|
152
|
+
// switch 8KB at a time
|
|
153
|
+
bank = self.chr_bank0_register.load() as u32 & 0x1E;
|
|
154
|
+
offset = offset | (address & 0x1000);
|
|
155
|
+
} else {
|
|
156
|
+
// switch two separate 4KB banks
|
|
157
|
+
bank = match address < 0x1000 {
|
|
158
|
+
true => self.chr_bank0_register.load(),
|
|
159
|
+
false => self.chr_bank1_register.load()
|
|
160
|
+
} as u32 & 0x1f;
|
|
161
|
+
}
|
|
162
|
+
bank * 0x1000 + offset
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
fn store(&mut self, address: u32, value: u8) {
|
|
166
|
+
if (value & 0x80) != 0 {
|
|
167
|
+
self.register_write_count = 0;
|
|
168
|
+
self.latch.clear();
|
|
169
|
+
if (address & 0x6000) == 0 {
|
|
170
|
+
self.control_register.store_bits(2, 2, 3);
|
|
171
|
+
}
|
|
172
|
+
} else {
|
|
173
|
+
self.latch.store(((value & 1) << 4) | (self.latch.load() >> 1));
|
|
174
|
+
self.register_write_count += 1;
|
|
175
|
+
|
|
176
|
+
if self.register_write_count >= 5 {
|
|
177
|
+
let val = self.latch.load();
|
|
178
|
+
match address & 0x6000 {
|
|
179
|
+
0x0000 => self.control_register.store(val),
|
|
180
|
+
0x2000 => self.chr_bank0_register.store(val),
|
|
181
|
+
0x4000 => self.chr_bank1_register.store(val),
|
|
182
|
+
_ /*0x6000*/ => self.prg_bank_register.store(val)
|
|
183
|
+
};
|
|
184
|
+
self.register_write_count = 0;
|
|
185
|
+
self.latch.clear();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
fn has_mirroring_type(&self) -> bool {
|
|
191
|
+
true
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
fn mirroring_type(&self) -> Mirrorings {
|
|
195
|
+
match self.control_register.load_bits(0, 2) {
|
|
196
|
+
0 | 1 => Mirrorings::SingleScreen,
|
|
197
|
+
2 => Mirrorings::Vertical,
|
|
198
|
+
_ /*3*/ => Mirrorings::Horizontal
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
fn drive_irq_counter(&mut self) -> bool {
|
|
203
|
+
false
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
struct UNRomMapper {
|
|
208
|
+
program_bank_num: u8,
|
|
209
|
+
register: Register<u8>
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
impl UNRomMapper {
|
|
213
|
+
fn new(header: &RomHeader) -> Self {
|
|
214
|
+
UNRomMapper {
|
|
215
|
+
program_bank_num: header.prg_rom_bank_num(),
|
|
216
|
+
register: Register::<u8>::new()
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
impl Mapper for UNRomMapper {
|
|
222
|
+
fn map(&self, address: u32) -> u32 {
|
|
223
|
+
let bank = match address < 0xC000 {
|
|
224
|
+
true => self.register.load(),
|
|
225
|
+
false => self.program_bank_num - 1
|
|
226
|
+
} as u32;
|
|
227
|
+
let offset = address & 0x3FFF;
|
|
228
|
+
0x4000 * bank + offset
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
fn map_for_chr_rom(&self, address: u32) -> u32 {
|
|
232
|
+
address
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
fn store(&mut self, _address: u32, value: u8) {
|
|
236
|
+
self.register.store(value & 0xF);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
fn has_mirroring_type(&self) -> bool {
|
|
240
|
+
false
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
fn mirroring_type(&self) -> Mirrorings {
|
|
244
|
+
Mirrorings::SingleScreen // dummy
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
fn drive_irq_counter(&mut self) -> bool {
|
|
248
|
+
false
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
struct CNRomMapper {
|
|
253
|
+
register: Register<u8>
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
impl CNRomMapper {
|
|
257
|
+
fn new() -> Self {
|
|
258
|
+
CNRomMapper {
|
|
259
|
+
register: Register::<u8>::new()
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
impl Mapper for CNRomMapper {
|
|
265
|
+
fn map(&self, address: u32) -> u32 {
|
|
266
|
+
address - 0x8000
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
fn map_for_chr_rom(&self, address: u32) -> u32 {
|
|
270
|
+
self.register.load() as u32 * 0x2000 + (address & 0x1FFF)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
fn store(&mut self, _address: u32, value: u8) {
|
|
274
|
+
self.register.store(value & 0xF);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
fn has_mirroring_type(&self) -> bool {
|
|
278
|
+
false
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
fn mirroring_type(&self) -> Mirrorings {
|
|
282
|
+
Mirrorings::SingleScreen // dummy
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
fn drive_irq_counter(&mut self) -> bool {
|
|
286
|
+
false
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
struct MMC3Mapper {
|
|
291
|
+
program_bank_num: u8,
|
|
292
|
+
character_bank_num: u8,
|
|
293
|
+
register0: Register<u8>,
|
|
294
|
+
register1: Register<u8>,
|
|
295
|
+
register2: Register<u8>,
|
|
296
|
+
register3: Register<u8>,
|
|
297
|
+
register4: Register<u8>,
|
|
298
|
+
register5: Register<u8>,
|
|
299
|
+
register6: Register<u8>,
|
|
300
|
+
register7: Register<u8>,
|
|
301
|
+
program_register0: Register<u8>,
|
|
302
|
+
program_register1: Register<u8>,
|
|
303
|
+
character_register0: Register<u8>,
|
|
304
|
+
character_register1: Register<u8>,
|
|
305
|
+
character_register2: Register<u8>,
|
|
306
|
+
character_register3: Register<u8>,
|
|
307
|
+
character_register4: Register<u8>,
|
|
308
|
+
character_register5: Register<u8>,
|
|
309
|
+
irq_counter: u8,
|
|
310
|
+
irq_counter_reload: bool,
|
|
311
|
+
irq_enabled: bool
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
impl MMC3Mapper {
|
|
315
|
+
fn new(header: &RomHeader) -> Self {
|
|
316
|
+
MMC3Mapper {
|
|
317
|
+
program_bank_num: header.prg_rom_bank_num(),
|
|
318
|
+
character_bank_num: header.chr_rom_bank_num(),
|
|
319
|
+
register0: Register::<u8>::new(),
|
|
320
|
+
register1: Register::<u8>::new(),
|
|
321
|
+
register2: Register::<u8>::new(),
|
|
322
|
+
register3: Register::<u8>::new(),
|
|
323
|
+
register4: Register::<u8>::new(),
|
|
324
|
+
register5: Register::<u8>::new(),
|
|
325
|
+
register6: Register::<u8>::new(),
|
|
326
|
+
register7: Register::<u8>::new(),
|
|
327
|
+
program_register0: Register::<u8>::new(),
|
|
328
|
+
program_register1: Register::<u8>::new(),
|
|
329
|
+
character_register0: Register::<u8>::new(),
|
|
330
|
+
character_register1: Register::<u8>::new(),
|
|
331
|
+
character_register2: Register::<u8>::new(),
|
|
332
|
+
character_register3: Register::<u8>::new(),
|
|
333
|
+
character_register4: Register::<u8>::new(),
|
|
334
|
+
character_register5: Register::<u8>::new(),
|
|
335
|
+
irq_counter: 0,
|
|
336
|
+
irq_counter_reload: false,
|
|
337
|
+
irq_enabled: true
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
impl Mapper for MMC3Mapper {
|
|
343
|
+
fn map(&self, address: u32) -> u32 {
|
|
344
|
+
let bank = match address {
|
|
345
|
+
0x8000..=0x9FFF => match self.register0.is_bit_set(6) {
|
|
346
|
+
true => self.program_bank_num * 2 - 2,
|
|
347
|
+
false => self.program_register0.load()
|
|
348
|
+
},
|
|
349
|
+
0xA000..=0xBFFF => self.program_register1.load(),
|
|
350
|
+
0xC000..=0xDFFF => match self.register0.is_bit_set(6) {
|
|
351
|
+
true => self.program_register0.load(),
|
|
352
|
+
false => self.program_bank_num * 2 - 2
|
|
353
|
+
},
|
|
354
|
+
_ => self.program_bank_num * 2 - 1
|
|
355
|
+
};
|
|
356
|
+
// I couldn't in the spec but it seems that
|
|
357
|
+
// we need to wrap 2k bank with 4k program_bank_num
|
|
358
|
+
((bank as u32) % ((self.program_bank_num as u32) * 2)) * 0x2000 + (address & 0x1FFF)
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
fn map_for_chr_rom(&self, address: u32) -> u32 {
|
|
362
|
+
let bank = match self.register0.is_bit_set(7) {
|
|
363
|
+
true => match address & 0x1FFF {
|
|
364
|
+
0x0000..=0x03FF => self.character_register2.load(),
|
|
365
|
+
0x0400..=0x07FF => self.character_register3.load(),
|
|
366
|
+
0x0800..=0x0BFF => self.character_register4.load(),
|
|
367
|
+
0x0C00..=0x0FFF => self.character_register5.load(),
|
|
368
|
+
0x1000..=0x13FF => self.character_register0.load() & 0xFE,
|
|
369
|
+
0x1400..=0x17FF => self.character_register0.load() | 1,
|
|
370
|
+
0x1800..=0x1BFF => self.character_register1.load() & 0xFE,
|
|
371
|
+
_ => self.character_register1.load() | 1
|
|
372
|
+
},
|
|
373
|
+
false => match address & 0x1FFF {
|
|
374
|
+
0x0000..=0x03FF => self.character_register0.load() & 0xFE,
|
|
375
|
+
0x0400..=0x07FF => self.character_register0.load() | 1,
|
|
376
|
+
0x0800..=0x0BFF => self.character_register1.load() & 0xFE,
|
|
377
|
+
0x0C00..=0x0FFF => self.character_register1.load() | 1,
|
|
378
|
+
0x1000..=0x13FF => self.character_register2.load(),
|
|
379
|
+
0x1400..=0x17FF => self.character_register3.load(),
|
|
380
|
+
0x1800..=0x1BFF => self.character_register4.load(),
|
|
381
|
+
_ => self.character_register5.load()
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
// I couldn't in the spec but it seems that
|
|
385
|
+
// we need to wrap 0.4k bank with 4k character_bank_num
|
|
386
|
+
((bank as u32) % ((self.character_bank_num as u32) * 8)) * 0x400 + (address & 0x3FF)
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
fn store(&mut self, address: u32, value: u8) {
|
|
390
|
+
match address {
|
|
391
|
+
0x8000..=0x9FFF => match (address & 1) == 0 {
|
|
392
|
+
true => self.register0.store(value),
|
|
393
|
+
false => {
|
|
394
|
+
self.register1.store(value);
|
|
395
|
+
match self.register0.load_bits(0, 3) {
|
|
396
|
+
0 => self.character_register0.store(value & 0xFE),
|
|
397
|
+
1 => self.character_register1.store(value & 0xFE),
|
|
398
|
+
2 => self.character_register2.store(value),
|
|
399
|
+
3 => self.character_register3.store(value),
|
|
400
|
+
4 => self.character_register4.store(value),
|
|
401
|
+
5 => self.character_register5.store(value),
|
|
402
|
+
6 => self.program_register0.store(value & 0x3F),
|
|
403
|
+
_ => self.program_register1.store(value & 0x3F)
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
},
|
|
407
|
+
0xA000..=0xBFFF => match (address & 1) == 0 {
|
|
408
|
+
true => self.register2.store(value),
|
|
409
|
+
false => self.register3.store(value)
|
|
410
|
+
},
|
|
411
|
+
0xC000..=0xDFFF => {
|
|
412
|
+
match (address & 1) == 0 {
|
|
413
|
+
true => self.register4.store(value),
|
|
414
|
+
false => self.register5.store(value)
|
|
415
|
+
};
|
|
416
|
+
self.irq_counter_reload = true;
|
|
417
|
+
},
|
|
418
|
+
_ => match (address & 1) == 0 {
|
|
419
|
+
true => {
|
|
420
|
+
self.register6.store(value);
|
|
421
|
+
self.irq_enabled = false;
|
|
422
|
+
},
|
|
423
|
+
false => {
|
|
424
|
+
self.register7.store(value);
|
|
425
|
+
self.irq_enabled = true;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
fn has_mirroring_type(&self) -> bool {
|
|
432
|
+
true
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
fn mirroring_type(&self) -> Mirrorings {
|
|
436
|
+
match self.register2.is_bit_set(0) {
|
|
437
|
+
true => Mirrorings::Horizontal,
|
|
438
|
+
false => Mirrorings::Vertical
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
fn drive_irq_counter(&mut self) -> bool {
|
|
443
|
+
match self.irq_counter_reload {
|
|
444
|
+
true => {
|
|
445
|
+
self.irq_counter = self.register4.load();
|
|
446
|
+
self.irq_counter_reload = false;
|
|
447
|
+
false
|
|
448
|
+
},
|
|
449
|
+
false => match self.irq_enabled {
|
|
450
|
+
true => match self.irq_counter > 0 {
|
|
451
|
+
true => {
|
|
452
|
+
self.irq_counter -= 1;
|
|
453
|
+
match self.irq_counter == 0 {
|
|
454
|
+
true => {
|
|
455
|
+
self.irq_counter_reload = true;
|
|
456
|
+
true
|
|
457
|
+
}
|
|
458
|
+
false => false
|
|
459
|
+
}
|
|
460
|
+
},
|
|
461
|
+
false => false
|
|
462
|
+
},
|
|
463
|
+
false => false
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
#[cfg(test)]
|
|
470
|
+
mod tests_nrom_mapper {
|
|
471
|
+
use super::*;
|
|
472
|
+
|
|
473
|
+
#[test]
|
|
474
|
+
fn initialize() {
|
|
475
|
+
NRomMapper{program_bank_num: 1};
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
#[test]
|
|
479
|
+
fn map_with_program_bank_num_1() {
|
|
480
|
+
let m = NRomMapper{program_bank_num: 1};
|
|
481
|
+
assert_eq!(0x0000, m.map(0x8000));
|
|
482
|
+
assert_eq!(0x3FFF, m.map(0xBFFF));
|
|
483
|
+
assert_eq!(0x0000, m.map(0xC000));
|
|
484
|
+
assert_eq!(0x3FFF, m.map(0xFFFF));
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
#[test]
|
|
488
|
+
fn map_with_program_bank_num_2() {
|
|
489
|
+
let m = NRomMapper{program_bank_num: 2};
|
|
490
|
+
assert_eq!(0x0000, m.map(0x8000));
|
|
491
|
+
assert_eq!(0x3FFF, m.map(0xBFFF));
|
|
492
|
+
assert_eq!(0x4000, m.map(0xC000));
|
|
493
|
+
assert_eq!(0x7FFF, m.map(0xFFFF));
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
#[test]
|
|
497
|
+
fn map_for_chr_rom() {
|
|
498
|
+
let m = NRomMapper{program_bank_num: 1};
|
|
499
|
+
assert_eq!(0x0000, m.map_for_chr_rom(0x0000));
|
|
500
|
+
assert_eq!(0x1FFF, m.map_for_chr_rom(0x1FFF));
|
|
501
|
+
}
|
|
502
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
pub struct Memory {
|
|
2
|
+
data: Vec<u8>
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
impl Memory {
|
|
6
|
+
pub fn new(vec: Vec<u8>) -> Self {
|
|
7
|
+
Memory{ data: vec }
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
pub fn clear(&mut self) {
|
|
11
|
+
for i in 0..self.capacity() {
|
|
12
|
+
self.data[i as usize] = 0;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
pub fn capacity(&self) -> u32 {
|
|
17
|
+
self.data.len() as u32
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
pub fn load(&self, address: u32) -> u8 {
|
|
21
|
+
self.data[address as usize]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
pub fn store(&mut self, address: u32, value: u8) {
|
|
25
|
+
self.data[address as usize] = value;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
pub fn as_slice(&self) -> &[u8] {
|
|
29
|
+
&self.data
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
pub fn as_mut_slice(&mut self) -> &mut [u8] {
|
|
33
|
+
&mut self.data
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
#[cfg(test)]
|
|
38
|
+
mod tests_memory {
|
|
39
|
+
use super::*;
|
|
40
|
+
|
|
41
|
+
#[test]
|
|
42
|
+
fn initialize() {
|
|
43
|
+
let m = Memory::new(vec![0; 1]);
|
|
44
|
+
assert_eq!(0, m.load(0));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
#[test]
|
|
48
|
+
fn clear() {
|
|
49
|
+
let mut m = Memory::new(vec![0; 16]);
|
|
50
|
+
for i in 0..m.capacity() {
|
|
51
|
+
m.store(i, 1);
|
|
52
|
+
}
|
|
53
|
+
m.clear();
|
|
54
|
+
for i in 0..m.capacity() {
|
|
55
|
+
assert_eq!(0, m.load(i));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
#[test]
|
|
60
|
+
fn capacity() {
|
|
61
|
+
let m = Memory::new(vec![0; 16]);
|
|
62
|
+
assert_eq!(0x10, m.capacity());
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
#[test]
|
|
66
|
+
fn store_and_load() {
|
|
67
|
+
let mut m = Memory::new(vec![0; 16]);
|
|
68
|
+
assert_eq!(0, m.load(1));
|
|
69
|
+
m.store(1, 1);
|
|
70
|
+
assert_eq!(1, m.load(1));
|
|
71
|
+
assert_eq!(0, m.load(2));
|
|
72
|
+
}
|
|
73
|
+
}
|