@tmustier/pi-nes 0.2.4 → 0.2.5

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.
Files changed (32) hide show
  1. package/AGENTS.md +89 -1
  2. package/README.md +78 -49
  3. package/extensions/nes/native/nes-core/Cargo.lock +0 -2
  4. package/extensions/nes/native/nes-core/Cargo.toml +1 -1
  5. package/extensions/nes/native/nes-core/index.d.ts +5 -0
  6. package/extensions/nes/native/nes-core/index.node +0 -0
  7. package/extensions/nes/native/nes-core/native.d.ts +5 -0
  8. package/extensions/nes/native/nes-core/src/lib.rs +25 -0
  9. package/extensions/nes/native/nes-core/vendor/nes_rust/.cargo-ok +1 -0
  10. package/extensions/nes/native/nes-core/vendor/nes_rust/.cargo_vcs_info.json +5 -0
  11. package/extensions/nes/native/nes-core/vendor/nes_rust/.travis.yml +3 -0
  12. package/extensions/nes/native/nes-core/vendor/nes_rust/Cargo.toml +23 -0
  13. package/extensions/nes/native/nes-core/vendor/nes_rust/LICENSE +21 -0
  14. package/extensions/nes/native/nes-core/vendor/nes_rust/README.md +59 -0
  15. package/extensions/nes/native/nes-core/vendor/nes_rust/src/apu.rs +1114 -0
  16. package/extensions/nes/native/nes-core/vendor/nes_rust/src/audio.rs +6 -0
  17. package/extensions/nes/native/nes-core/vendor/nes_rust/src/button.rs +23 -0
  18. package/extensions/nes/native/nes-core/vendor/nes_rust/src/cpu.rs +2364 -0
  19. package/extensions/nes/native/nes-core/vendor/nes_rust/src/default_audio.rs +49 -0
  20. package/extensions/nes/native/nes-core/vendor/nes_rust/src/default_display.rs +47 -0
  21. package/extensions/nes/native/nes-core/vendor/nes_rust/src/default_input.rs +33 -0
  22. package/extensions/nes/native/nes-core/vendor/nes_rust/src/display.rs +10 -0
  23. package/extensions/nes/native/nes-core/vendor/nes_rust/src/input.rs +7 -0
  24. package/extensions/nes/native/nes-core/vendor/nes_rust/src/joypad.rs +86 -0
  25. package/extensions/nes/native/nes-core/vendor/nes_rust/src/lib.rs +168 -0
  26. package/extensions/nes/native/nes-core/vendor/nes_rust/src/mapper.rs +502 -0
  27. package/extensions/nes/native/nes-core/vendor/nes_rust/src/memory.rs +73 -0
  28. package/extensions/nes/native/nes-core/vendor/nes_rust/src/ppu.rs +1378 -0
  29. package/extensions/nes/native/nes-core/vendor/nes_rust/src/register.rs +560 -0
  30. package/extensions/nes/native/nes-core/vendor/nes_rust/src/rom.rs +231 -0
  31. package/extensions/nes/nes-core.ts +27 -4
  32. package/package.json +1 -1
@@ -0,0 +1,231 @@
1
+ use memory::Memory;
2
+ use mapper::{Mapper, MapperFactory};
3
+
4
+ pub struct Rom {
5
+ header: RomHeader,
6
+ memory: Memory,
7
+ mapper: Box<dyn Mapper>
8
+ }
9
+
10
+ pub static HEADER_SIZE: usize = 16;
11
+
12
+ pub enum Mirrorings {
13
+ SingleScreen,
14
+ Horizontal,
15
+ Vertical,
16
+ FourScreen
17
+ }
18
+
19
+ impl Rom {
20
+ pub fn new(data: Vec<u8>) -> Self {
21
+ let header = RomHeader::new(data[0..HEADER_SIZE].to_vec());
22
+ let mapper = MapperFactory::create(&header);
23
+ Rom {
24
+ header: header,
25
+ memory: Memory::new(data[HEADER_SIZE..].to_vec()),
26
+ mapper: mapper
27
+ }
28
+ }
29
+
30
+ /**
31
+ * CPU memory address:
32
+ * 0x0000 - 0x1FFF: Character ROM access
33
+ * 0x8000 - 0xFFFF: Program ROM access
34
+ *
35
+ * To access wide range ROM data with limited CPU memory address space
36
+ * Mapper maps CPU memory address to ROM's.
37
+ * In general writing control registers in Mapper via .store() switches bank.
38
+ */
39
+ pub fn load(&self, address: u32) -> u8 {
40
+ let mut address_in_rom = 0 as u32;
41
+ if address < 0x2000 {
42
+ // load from character rom
43
+ address_in_rom += self.header.prg_rom_bank_num() as u32 * 0x4000;
44
+ address_in_rom += self.mapper.map_for_chr_rom(address);
45
+ } else {
46
+ address_in_rom += self.mapper.map(address);
47
+ }
48
+ self.memory.load(address_in_rom)
49
+ }
50
+
51
+ pub fn load_without_mapping(&self, address: u32) -> u8 {
52
+ self.memory.load(address)
53
+ }
54
+
55
+ /**
56
+ * In general writing with ROM address space updates control registers in Mapper.
57
+ */
58
+ pub fn store(&mut self, address: u32, value: u8) {
59
+ self.mapper.store(address, value);
60
+ }
61
+
62
+ pub fn valid(&self) -> bool {
63
+ self.header.is_nes()
64
+ }
65
+
66
+ pub fn has_chr_rom(&self) -> bool {
67
+ self.header.has_chr_rom()
68
+ }
69
+
70
+ pub fn has_battery_backed_ram(&self) -> bool {
71
+ self.header.has_battery_backed_ram()
72
+ }
73
+
74
+ pub fn mirroring_type(&self) -> Mirrorings {
75
+ match self.mapper.has_mirroring_type() {
76
+ true => self.mapper.mirroring_type(),
77
+ false => self.header.mirroring_type()
78
+ }
79
+ }
80
+
81
+ // @TODO: MMC3Mapper specific. Should this method be here?
82
+ pub fn irq_interrupted(&mut self) -> bool {
83
+ self.mapper.drive_irq_counter()
84
+ }
85
+ }
86
+
87
+ // @TODO: Cache
88
+ pub struct RomHeader {
89
+ data: Vec<u8>
90
+ }
91
+
92
+ impl RomHeader {
93
+ fn new(vec: Vec<u8>) -> Self {
94
+ let mut header = RomHeader {
95
+ data: Vec::new()
96
+ };
97
+ for i in 0..HEADER_SIZE {
98
+ header.data.push(vec[i]);
99
+ }
100
+ header
101
+ }
102
+
103
+ fn load(&self, address: u32) -> u8 {
104
+ self.data[address as usize]
105
+ }
106
+
107
+ fn is_nes(&self) -> bool {
108
+ if self.signature() == "NES" && self.magic_number() == 0x1a {
109
+ return true;
110
+ }
111
+ false
112
+ }
113
+
114
+ fn signature(&self) -> String {
115
+ let mut vec = Vec::new();
116
+ for i in 0..3 as u32 {
117
+ vec.push(self.load(i));
118
+ }
119
+ String::from_utf8(vec).unwrap()
120
+ }
121
+
122
+ fn magic_number(&self) -> u8 {
123
+ self.load(3)
124
+ }
125
+
126
+ pub fn prg_rom_bank_num(&self) -> u8 {
127
+ self.load(4)
128
+ }
129
+
130
+ pub fn chr_rom_bank_num(&self) -> u8 {
131
+ self.load(5)
132
+ }
133
+
134
+ fn has_chr_rom(&self) -> bool {
135
+ self.chr_rom_bank_num() > 0
136
+ }
137
+
138
+ fn control_byte1(&self) -> u8 {
139
+ self.load(6)
140
+ }
141
+
142
+ fn control_byte2(&self) -> u8 {
143
+ self.load(7)
144
+ }
145
+
146
+ fn _ram_bank_num(&self) -> u8 {
147
+ self.load(8)
148
+ }
149
+
150
+ fn _unused_field(&self) -> u64 {
151
+ let mut value = 0 as u64;
152
+ for i in 0..7 as u32 {
153
+ value = (value << 8) | self.load(9 + i) as u64;
154
+ }
155
+ value
156
+ }
157
+
158
+ fn extract_bits(&self, value: u8, offset: u8, size: u8) -> u8 {
159
+ (value >> offset) & ((1 << size) - 1)
160
+ }
161
+
162
+ fn mirroring_type(&self) -> Mirrorings {
163
+ match self.four_screen_mirroring() {
164
+ true => Mirrorings::FourScreen,
165
+ false => match self.extract_bits(self.control_byte1(), 0, 1) {
166
+ 0 => Mirrorings::Horizontal,
167
+ _ /* 1 */ => Mirrorings::Vertical
168
+ }
169
+ }
170
+ }
171
+
172
+ fn _is_horizontal_mirroring(&self) -> bool {
173
+ match self.mirroring_type() {
174
+ Mirrorings::Horizontal => true,
175
+ _ => false
176
+ }
177
+ }
178
+
179
+ pub fn has_battery_backed_ram(&self) -> bool {
180
+ self.extract_bits(self.control_byte1(), 1, 1) == 1
181
+ }
182
+
183
+ fn _trainer_512_bytes(&self) -> u8 {
184
+ self.extract_bits(self.control_byte1(), 2, 1)
185
+ }
186
+
187
+ fn four_screen_mirroring(&self) -> bool {
188
+ self.extract_bits(self.control_byte1(), 3, 1) == 1
189
+ }
190
+
191
+ pub fn mapper_num(&self) -> u8 {
192
+ let lower_bits = self.extract_bits(self.control_byte1(), 4, 4);
193
+ let higher_bits = self.extract_bits(self.control_byte2(), 4, 4);
194
+ (higher_bits << 4) | lower_bits
195
+ }
196
+ }
197
+
198
+ #[cfg(test)]
199
+ mod tests_rom {
200
+ use super::*;
201
+
202
+ #[test]
203
+ fn initialize() {
204
+ let r = Rom::new(vec![0; 17]);
205
+ }
206
+
207
+ #[test]
208
+ fn load() {
209
+ let r = Rom::new(vec![0; 17]);
210
+ assert_eq!(0, r.load(0));
211
+ }
212
+
213
+ #[test]
214
+ fn store() {
215
+ let mut r = Rom::new(vec![0; 17]);
216
+ r.store(0, 0);
217
+ }
218
+
219
+ #[test]
220
+ fn valid() {
221
+ let r = Rom::new(vec![0; 64]);
222
+ assert_eq!(false, r.valid());
223
+ let mut v = vec![0; 64];
224
+ v[0] = 0x4e; // N
225
+ v[1] = 0x45; // E
226
+ v[2] = 0x53; // S
227
+ v[3] = 0x1a; // magic number
228
+ let r2 = Rom::new(v);
229
+ assert_eq!(true, r2.valid());
230
+ }
231
+ }
@@ -34,6 +34,11 @@ interface NativeNesInstance {
34
34
  reset(): void;
35
35
  pressButton(button: number): void;
36
36
  releaseButton(button: number): void;
37
+ hasBatteryBackedRam(): boolean;
38
+ getSram(): Uint8Array;
39
+ setSram(data: Uint8Array): void;
40
+ isSramDirty(): boolean;
41
+ markSramSaved(): void;
37
42
  getFramebuffer(): Uint8Array;
38
43
  }
39
44
 
@@ -73,6 +78,7 @@ class NativeNesCore implements NesCore {
73
78
  private readonly nes: NativeNesInstance;
74
79
  private readonly audioWarning: string | null;
75
80
  private readonly frameBuffer: Uint8Array;
81
+ private hasSram = false;
76
82
 
77
83
  constructor(enableAudio: boolean) {
78
84
  this.audioWarning = enableAudio
@@ -88,6 +94,7 @@ class NativeNesCore implements NesCore {
88
94
 
89
95
  loadRom(rom: Uint8Array): void {
90
96
  this.nes.setRom(rom);
97
+ this.hasSram = this.nes.hasBatteryBackedRam();
91
98
  this.nes.bootup();
92
99
  }
93
100
 
@@ -110,16 +117,32 @@ class NativeNesCore implements NesCore {
110
117
  }
111
118
 
112
119
  getSram(): Uint8Array | null {
113
- return null;
120
+ if (!this.hasSram) {
121
+ return null;
122
+ }
123
+ return this.nes.getSram();
114
124
  }
115
125
 
116
- setSram(_sram: Uint8Array): void {}
126
+ setSram(sram: Uint8Array): void {
127
+ if (!this.hasSram) {
128
+ return;
129
+ }
130
+ this.nes.setSram(sram);
131
+ }
117
132
 
118
133
  isSramDirty(): boolean {
119
- return false;
134
+ if (!this.hasSram) {
135
+ return false;
136
+ }
137
+ return this.nes.isSramDirty();
120
138
  }
121
139
 
122
- markSramSaved(): void {}
140
+ markSramSaved(): void {
141
+ if (!this.hasSram) {
142
+ return;
143
+ }
144
+ this.nes.markSramSaved();
145
+ }
123
146
 
124
147
  getAudioWarning(): string | null {
125
148
  return this.audioWarning;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tmustier/pi-nes",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "NES emulator extension for pi",
5
5
  "keywords": [
6
6
  "pi-package",