spiky-nes 0.1.0__tar.gz

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 (41) hide show
  1. spiky_nes-0.1.0/CMakeLists.txt +254 -0
  2. spiky_nes-0.1.0/PKG-INFO +53 -0
  3. spiky_nes-0.1.0/include/core/apu.h +183 -0
  4. spiky_nes-0.1.0/include/core/bus.h +59 -0
  5. spiky_nes-0.1.0/include/core/cartridge.h +76 -0
  6. spiky_nes-0.1.0/include/core/constants.h +58 -0
  7. spiky_nes-0.1.0/include/core/controller.h +28 -0
  8. spiky_nes-0.1.0/include/core/cpu.h +358 -0
  9. spiky_nes-0.1.0/include/core/emulator.h +59 -0
  10. spiky_nes-0.1.0/include/core/mapper/mapper_000.h +20 -0
  11. spiky_nes-0.1.0/include/core/mapper/mapper_001.h +41 -0
  12. spiky_nes-0.1.0/include/core/mapper/mapper_002.h +22 -0
  13. spiky_nes-0.1.0/include/core/mapper/mapper_003.h +22 -0
  14. spiky_nes-0.1.0/include/core/mapper/mapper_004.h +55 -0
  15. spiky_nes-0.1.0/include/core/mapper.h +65 -0
  16. spiky_nes-0.1.0/include/core/ppu.h +199 -0
  17. spiky_nes-0.1.0/include/core/utils.h +39 -0
  18. spiky_nes-0.1.0/include/ui/sdl_emulator.h +51 -0
  19. spiky_nes-0.1.0/pyproject.toml +54 -0
  20. spiky_nes-0.1.0/python/README.md +35 -0
  21. spiky_nes-0.1.0/python/binding.cpp +103 -0
  22. spiky_nes-0.1.0/python/wrapper/__init__.py +9 -0
  23. spiky_nes-0.1.0/src/core/apu.cpp +686 -0
  24. spiky_nes-0.1.0/src/core/bus.cpp +121 -0
  25. spiky_nes-0.1.0/src/core/cartridge.cpp +210 -0
  26. spiky_nes-0.1.0/src/core/controller.cpp +32 -0
  27. spiky_nes-0.1.0/src/core/cpu/addressing.cpp +147 -0
  28. spiky_nes-0.1.0/src/core/cpu/operation.cpp +746 -0
  29. spiky_nes-0.1.0/src/core/cpu/unofficial.cpp +230 -0
  30. spiky_nes-0.1.0/src/core/cpu.cpp +602 -0
  31. spiky_nes-0.1.0/src/core/emulator.cpp +75 -0
  32. spiky_nes-0.1.0/src/core/mapper/mapper_000.cpp +53 -0
  33. spiky_nes-0.1.0/src/core/mapper/mapper_001.cpp +156 -0
  34. spiky_nes-0.1.0/src/core/mapper/mapper_002.cpp +55 -0
  35. spiky_nes-0.1.0/src/core/mapper/mapper_003.cpp +58 -0
  36. spiky_nes-0.1.0/src/core/mapper/mapper_004.cpp +198 -0
  37. spiky_nes-0.1.0/src/core/mapper.cpp +38 -0
  38. spiky_nes-0.1.0/src/core/ppu.cpp +746 -0
  39. spiky_nes-0.1.0/src/core/utils.cpp +61 -0
  40. spiky_nes-0.1.0/src/ui/main.cpp +14 -0
  41. spiky_nes-0.1.0/src/ui/sdl_emulator.cpp +334 -0
@@ -0,0 +1,254 @@
1
+ cmake_minimum_required(VERSION 4.0)
2
+ project(SpikyNes)
3
+
4
+ set(CMAKE_CXX_STANDARD 20)
5
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
6
+ set(CMAKE_CXX_EXTENSIONS OFF)
7
+ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
8
+
9
+ option(SPIKY_NES_ENABLE_TESTS "Build tests" OFF)
10
+ option(SPIKY_NES_ENABLE_ROM_TESTS "Build ROM tests" OFF)
11
+ option(SPIKY_NES_ENABLE_STRICT "Use strict compile options" OFF)
12
+ option(SPIKY_NES_ENABLE_COVERAGE "Enable coverage reporting" OFF)
13
+ option(SPIKY_NES_ENABLE_UI "Build SDL UI" OFF)
14
+ option(SPIKY_NES_BIND_PYTHON "Enable Python binding" OFF)
15
+ option(SPIKY_NES_BIND_ES "Enable ECMAScript binding" OFF)
16
+
17
+ if(APPLE)
18
+ set(ENV{PKG_CONFIG_PATH} "/opt/homebrew/lib/pkgconfig:$ENV{PKG_CONFIG_PATH}")
19
+ endif()
20
+
21
+ if(SPIKY_NES_ENABLE_STRICT)
22
+ if (CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
23
+ add_compile_options(
24
+ -Wall
25
+ -Wextra
26
+ -Werror
27
+ -Wpedantic
28
+ -Wmissing-include-dirs
29
+ -Wundef
30
+ -Wredundant-decls
31
+ )
32
+ endif()
33
+ endif()
34
+
35
+ if(SPIKY_NES_ENABLE_COVERAGE)
36
+ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
37
+ add_compile_options(--coverage -O0 -g -fno-elide-constructors -fno-default-inline)
38
+ add_link_options(--coverage)
39
+ endif()
40
+ endif()
41
+
42
+ add_library(SpikyNesCore STATIC
43
+ include/core/emulator.h
44
+ src/core/emulator.cpp
45
+ include/core/cpu.h
46
+ src/core/cpu.cpp
47
+ include/core/ppu.h
48
+ src/core/ppu.cpp
49
+ include/core/apu.h
50
+ src/core/apu.cpp
51
+ include/core/bus.h
52
+ src/core/bus.cpp
53
+ include/core/cartridge.h
54
+ src/core/cartridge.cpp
55
+ include/core/constants.h
56
+ include/core/utils.h
57
+ src/core/utils.cpp
58
+ src/core/cpu/addressing.cpp
59
+ src/core/cpu/operation.cpp
60
+ src/core/cpu/unofficial.cpp
61
+ include/core/controller.h
62
+ src/core/controller.cpp
63
+ include/core/mapper.h
64
+ src/core/mapper.cpp
65
+ src/core/mapper.cpp
66
+ include/core/mapper/mapper_000.h
67
+ src/core/mapper/mapper_000.cpp
68
+ include/core/mapper/mapper_003.h
69
+ src/core/mapper/mapper_003.cpp
70
+ include/core/mapper/mapper_001.h
71
+ src/core/mapper/mapper_001.cpp
72
+ include/core/mapper/mapper_002.h
73
+ src/core/mapper/mapper_002.cpp
74
+ include/core/mapper/mapper_004.h
75
+ src/core/mapper/mapper_004.cpp
76
+ )
77
+
78
+ target_include_directories(SpikyNesCore
79
+ PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include
80
+ )
81
+
82
+ if(SPIKY_NES_ENABLE_TESTS)
83
+ enable_testing()
84
+
85
+ include(FetchContent)
86
+
87
+ FetchContent_Declare(
88
+ googletest
89
+ GIT_REPOSITORY https://github.com/google/googletest.git
90
+ GIT_TAG v1.17.0
91
+ )
92
+
93
+ FetchContent_MakeAvailable(googletest)
94
+
95
+ FetchContent_Declare(
96
+ tinypng
97
+ GIT_REPOSITORY https://github.com/CyberZHG/TinyPNG.git
98
+ GIT_TAG v1.0.0
99
+ )
100
+
101
+ FetchContent_MakeAvailable(tinypng)
102
+
103
+ add_executable(runTests
104
+ tests/test_main.cpp
105
+ tests/core/test_image_utils.h
106
+ tests/core/test_image_utils.cpp
107
+ tests/core/cpu/test_cpu_utils.cpp
108
+ tests/core/cpu/test_cpu_utils.h
109
+ tests/core/cpu/test_load.cpp
110
+ tests/core/cpu/test_store.cpp
111
+ tests/core/cpu/test_transfer.cpp
112
+ tests/core/cpu/test_stack.cpp
113
+ tests/core/cpu/test_shift.cpp
114
+ tests/core/cpu/test_logical.cpp
115
+ tests/core/cpu/test_arithmetic.cpp
116
+ tests/core/cpu/test_increment.cpp
117
+ tests/core/cpu/test_flags.cpp
118
+ tests/core/cpu/test_jump.cpp
119
+ tests/core/cpu/test_control.cpp
120
+ tests/core/cpu/test_branch.cpp
121
+ tests/core/cpu/test_nop.cpp
122
+ tests/core/test_cartridge.cpp
123
+ tests/core/test_controller.cpp
124
+ tests/core/ppu/test_registers.cpp
125
+ tests/core/test_utils.cpp
126
+ tests/core/ppu/test_frame_timing.cpp
127
+ tests/core/ppu/test_shift_registers.cpp
128
+ tests/core/ppu/test_scroll.cpp
129
+ tests/core/ppu/test_background_rendering.cpp
130
+ tests/core/ppu/test_sprite_rendering.cpp
131
+ tests/core/ppu/test_ppu_utils.h
132
+ tests/core/ppu/test_ppu_utils.cpp
133
+ tests/core/mapper/test_mapper_000.cpp
134
+ tests/core/mapper/test_mapper_001.cpp
135
+ tests/core/mapper/test_mapper_002.cpp
136
+ tests/core/mapper/test_mapper_003.cpp
137
+ tests/core/mapper/test_mapper_004.cpp
138
+ tests/core/apu/test_pulse.cpp
139
+ tests/core/apu/test_triangle.cpp
140
+ tests/core/apu/test_noise.cpp
141
+ tests/core/apu/test_dmc.cpp
142
+ tests/core/ppu/rom/test_ppu_read_buffer.cpp
143
+ tests/core/ppu/rom/test_ppu_open_bus.cpp
144
+ )
145
+
146
+ if(SPIKY_NES_ENABLE_ROM_TESTS)
147
+ target_sources(runTests PRIVATE
148
+ tests/core/cpu/rom/test_nestest.cpp
149
+ tests/core/cpu/rom/test_cpu_timing.cpp
150
+ tests/core/cpu/rom/test_all_instrs_v5.cpp
151
+ tests/core/ppu/rom/test_ppu_tests.cpp
152
+ tests/core/ppu/rom/test_sprite_hit_tests.cpp
153
+ tests/core/ppu/rom/test_sprite_overflow_tests.cpp
154
+ tests/core/ppu/rom/test_vbl_nmi_timing.cpp
155
+ tests/core/apu/rom/test_apu_test.cpp
156
+ tests/core/mapper/rom/test_mmc3.cpp
157
+ )
158
+ target_compile_definitions(runTests PRIVATE SPIKY_NES_ROM_TESTS_ENABLED)
159
+ endif()
160
+
161
+ target_link_libraries(runTests
162
+ PRIVATE SpikyNesCore
163
+ PRIVATE gtest gtest_main
164
+ PRIVATE TinyPNG
165
+ )
166
+
167
+ add_test(NAME SpikyNesCoreTests COMMAND runTests)
168
+ endif()
169
+
170
+ if(SPIKY_NES_ENABLE_UI)
171
+ include(FetchContent)
172
+
173
+ FetchContent_Declare(
174
+ SDL
175
+ GIT_REPOSITORY https://github.com/libsdl-org/SDL.git
176
+ GIT_TAG release-3.4.0
177
+ )
178
+
179
+ FetchContent_MakeAvailable(SDL)
180
+
181
+ FetchContent_Declare(
182
+ imgui
183
+ GIT_REPOSITORY https://github.com/ocornut/imgui.git
184
+ GIT_TAG docking
185
+ )
186
+ FetchContent_MakeAvailable(imgui)
187
+
188
+ add_executable(SpikyNesUI
189
+ src/ui/main.cpp
190
+ include/ui/sdl_emulator.h
191
+ src/ui/sdl_emulator.cpp
192
+ )
193
+
194
+ target_sources(SpikyNesUI PRIVATE
195
+ ${imgui_SOURCE_DIR}/imgui.cpp
196
+ ${imgui_SOURCE_DIR}/imgui_draw.cpp
197
+ ${imgui_SOURCE_DIR}/imgui_tables.cpp
198
+ ${imgui_SOURCE_DIR}/imgui_widgets.cpp
199
+ ${imgui_SOURCE_DIR}/backends/imgui_impl_sdl3.cpp
200
+ ${imgui_SOURCE_DIR}/backends/imgui_impl_sdlrenderer3.cpp
201
+ )
202
+
203
+ target_include_directories(SpikyNesUI
204
+ PRIVATE ${imgui_SOURCE_DIR}
205
+ PRIVATE ${imgui_SOURCE_DIR}/backends
206
+ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
207
+ )
208
+
209
+ target_link_libraries(SpikyNesUI
210
+ PRIVATE SpikyNesCore SDL3::SDL3
211
+ )
212
+ endif()
213
+
214
+ if(SPIKY_NES_BIND_PYTHON)
215
+ include(FetchContent)
216
+ FetchContent_Declare(
217
+ pybind11
218
+ GIT_REPOSITORY https://github.com/pybind/pybind11.git
219
+ GIT_TAG v3.0
220
+ )
221
+ FetchContent_MakeAvailable(pybind11)
222
+
223
+ pybind11_add_module(_core
224
+ python/binding.cpp
225
+ )
226
+ target_link_libraries(_core
227
+ PRIVATE SpikyNesCore
228
+ )
229
+ install(TARGETS _core
230
+ LIBRARY DESTINATION spiky_nes)
231
+ install(FILES python/wrapper/__init__.py DESTINATION spiky_nes)
232
+ endif()
233
+
234
+ if(SPIKY_NES_BIND_ES)
235
+ add_executable(SpikyNesWASM
236
+ wasm/binding.cpp
237
+ )
238
+
239
+ target_link_libraries(SpikyNesWASM
240
+ PRIVATE SpikyNesCore
241
+ )
242
+
243
+ target_compile_options(SpikyNesWASM PRIVATE "-O3")
244
+
245
+ set_target_properties(SpikyNesWASM PROPERTIES SUFFIX ".js")
246
+
247
+ target_link_options(SpikyNesWASM PRIVATE
248
+ "--bind"
249
+ "-sMODULARIZE=1"
250
+ "-sEXPORT_ES6=1"
251
+ "-sEXPORT_NAME=SpikyNesWASMModule"
252
+ "-sALLOW_MEMORY_GROWTH=1"
253
+ )
254
+ endif()
@@ -0,0 +1,53 @@
1
+ Metadata-Version: 2.2
2
+ Name: spiky-nes
3
+ Version: 0.1.0
4
+ Summary: A toy NES emulator.
5
+ Classifier: Programming Language :: Python :: 3
6
+ Classifier: Programming Language :: C++
7
+ Classifier: Operating System :: OS Independent
8
+ Project-URL: Homepage, https://github.com/CyberZHG/SpikyNES
9
+ Project-URL: Repository, https://github.com/CyberZHG/SpikyNES
10
+ Project-URL: Issues, https://github.com/CyberZHG/SpikyNES/issues
11
+ Requires-Python: >=3.8
12
+ Provides-Extra: test
13
+ Requires-Dist: pytest; extra == "test"
14
+ Requires-Dist: black; extra == "test"
15
+ Requires-Dist: isort; extra == "test"
16
+ Requires-Dist: flake8; extra == "test"
17
+ Description-Content-Type: text/markdown
18
+
19
+ # spiky-nes
20
+
21
+ Python bindings for [SpikyNES](https://github.com/CyberZHG/SpikyNES), a toy NES emulator.
22
+
23
+ Supported mappers: 0 (NROM), 1 (MMC1), 2 (UxROM), 3 (CNROM), 4 (MMC3).
24
+
25
+ ## Installation
26
+
27
+ ```bash
28
+ pip install spiky-nes
29
+ ```
30
+
31
+ ## Quick Start
32
+
33
+ ```python
34
+ import spiky_nes
35
+
36
+ emulator = spiky_nes.Emulator()
37
+ assert emulator.load_rom("game.nes")
38
+
39
+ emulator.press(spiky_nes.Button.START)
40
+ audio = emulator.run_frame(sample_rate=44100)
41
+ emulator.release(spiky_nes.Button.START)
42
+
43
+ rgba = emulator.frame_buffer() # 256 * 240 * 4 bytes
44
+ ```
45
+
46
+ Saving a frame with Pillow:
47
+
48
+ ```python
49
+ from PIL import Image
50
+
51
+ Image.frombytes("RGBA", (spiky_nes.SCREEN_WIDTH, spiky_nes.SCREEN_HEIGHT), emulator.frame_buffer()).save("frame.png")
52
+ ```
53
+
@@ -0,0 +1,183 @@
1
+ #ifndef SPIKYNES_APU_H
2
+ #define SPIKYNES_APU_H
3
+
4
+ #include <array>
5
+
6
+ #include "core/constants.h"
7
+ #include "core/utils.h"
8
+
9
+ namespace nes_core {
10
+
11
+ class Bus;
12
+
13
+ struct PulseRegisters {
14
+ void reset();
15
+ void write(std::uint16_t addr, std::uint16_t value);
16
+ [[nodiscard]] std::uint8_t output() const;
17
+
18
+ void envelopeStep();
19
+ void sweepStep(bool isPulse1);
20
+ void timerStep();
21
+ void lengthCounterStep();
22
+
23
+ std::uint8_t duty, dutyBit;
24
+ bool lengthCounterHalted;
25
+ bool constantVolume;
26
+ std::uint8_t volume;
27
+
28
+ bool sweepEnabled;
29
+ std::uint8_t sweepDividerPeriod;
30
+ bool sweepNegate;
31
+ std::uint8_t sweepShift;
32
+ bool sweepReload;
33
+ std::uint8_t sweepCounter;
34
+
35
+ std::uint16_t timerPeriod;
36
+ std::uint8_t lengthCounterLoad;
37
+ bool envelopeStart;
38
+ std::uint8_t envelopeDecayLevel;
39
+ std::uint8_t envelopeDivider;
40
+
41
+ std::uint16_t timer;
42
+ std::uint8_t lengthCounter;
43
+ };
44
+
45
+ struct TriangleRegisters {
46
+ void reset();
47
+ void write(std::uint16_t addr, std::uint16_t value);
48
+ [[nodiscard]] std::uint8_t output() const;
49
+
50
+ void linearCounterStep();
51
+ void timerStep();
52
+ void lengthCounterStep();
53
+
54
+ bool lengthCounterHalted;
55
+ std::uint8_t linearCounterReload;
56
+ std::uint8_t linearCounter;
57
+
58
+ std::uint16_t timerPeriod;
59
+ std::uint8_t lengthCounterLoad;
60
+ bool linearStart;
61
+
62
+ std::uint8_t dutyIndex;
63
+ std::uint16_t timer;
64
+ std::uint8_t lengthCounter;
65
+ };
66
+
67
+ struct NoiseRegisters {
68
+ void reset();
69
+ void write(std::uint16_t addr, std::uint16_t value);
70
+ [[nodiscard]] std::uint8_t output() const;
71
+
72
+ void envelopeStep();
73
+ void timerStep();
74
+ void lengthCounterStep();
75
+
76
+ bool lengthCounterHalted;
77
+ bool constantVolume;
78
+ std::uint8_t volume;
79
+
80
+ bool mode;
81
+ std::uint8_t periodIndex;
82
+ std::uint16_t shiftRegister;
83
+
84
+ bool envelopeStart;
85
+ std::uint8_t envelopeDecayLevel;
86
+ std::uint8_t envelopeDivider;
87
+
88
+ std::uint16_t timer;
89
+ std::uint8_t lengthCounter;
90
+ };
91
+
92
+ struct DMCRegisters {
93
+ void reset();
94
+ void write(std::uint16_t addr, std::uint16_t value);
95
+ [[nodiscard]] std::uint8_t output() const;
96
+
97
+ void timerStep();
98
+ void restart();
99
+
100
+ bool irqEnabled;
101
+ bool loopEnabled;
102
+ std::uint8_t rateIndex;
103
+
104
+ std::uint8_t outputLevel;
105
+
106
+ std::uint8_t sampleAddress;
107
+ std::uint8_t sampleLength;
108
+
109
+ std::uint16_t currentAddress;
110
+ std::uint16_t bytesRemaining;
111
+ std::uint8_t sampleBuffer;
112
+ bool sampleBufferEmpty;
113
+
114
+ std::uint8_t shiftRegister;
115
+ std::uint8_t bitsRemaining;
116
+ bool silence;
117
+
118
+ bool irqFlag;
119
+ std::uint16_t timer;
120
+ };
121
+
122
+ struct APURegisters {
123
+ void reset();
124
+
125
+ void write(std::uint16_t addr, std::uint16_t value);
126
+
127
+ PulseRegisters pulse1{};
128
+ PulseRegisters pulse2{};
129
+ TriangleRegisters triangle{};
130
+ NoiseRegisters noise{};
131
+ DMCRegisters dmc{};
132
+
133
+ bool pulse1Enabled = false;
134
+ bool pulse2Enabled = false;
135
+ bool triangleEnabled = false;
136
+ bool noiseEnabled = false;
137
+ bool dmcEnabled = false;
138
+
139
+ bool sequencerMode = false;
140
+ bool irqInhibited = false;
141
+ bool frameIrqFlag = false;
142
+ };
143
+
144
+ class APU {
145
+ public:
146
+ explicit APU(Bus* bus);
147
+
148
+ void reset();
149
+
150
+ [[nodiscard]] std::uint8_t read(std::uint16_t addr);
151
+ void write(std::uint16_t addr, std::uint8_t value);
152
+
153
+ void step();
154
+
155
+ APURegisters& registers() { return regs_; }
156
+ [[nodiscard]] const APURegisters& registers() const { return regs_; }
157
+
158
+ [[nodiscard]] float outputBuffer() const { return outputBuffer_; }
159
+
160
+ [[nodiscard]] bool irqAsserted() const { return regs_.frameIrqFlag || regs_.dmc.irqFlag; }
161
+
162
+ protected:
163
+ void quarterFrameStep();
164
+ void halfFrameStep();
165
+
166
+ [[nodiscard]] std::uint8_t pulse1Output() const;
167
+ [[nodiscard]] std::uint8_t pulse2Output() const;
168
+ [[nodiscard]] std::uint8_t triangleOutput() const;
169
+ [[nodiscard]] std::uint8_t noiseOutput() const;
170
+ [[nodiscard]] std::uint8_t dmcOutput() const;
171
+ [[nodiscard]] float mixOutputs() const;
172
+
173
+ private:
174
+ Bus* bus_ = nullptr;
175
+ APURegisters regs_{};
176
+ std::int64_t cycle_ = 0;
177
+ float outputBuffer_ = 0.0;
178
+ int frameResetCountdown_ = -1;
179
+ };
180
+
181
+ }
182
+
183
+ #endif //SPIKYNES_APU_H
@@ -0,0 +1,59 @@
1
+ #ifndef SPIKYNES_BUS_H
2
+ #define SPIKYNES_BUS_H
3
+
4
+ #include <cstdint>
5
+
6
+ #include "core/constants.h"
7
+
8
+ namespace nes_core {
9
+
10
+ class CPU;
11
+ class PPU;
12
+ class APU;
13
+ class Cartridge;
14
+ class Controller;
15
+
16
+ class Bus {
17
+ public:
18
+ Bus(CPU* cpu, PPU* ppu, APU* apu, Cartridge* cartridge, Controller* controller1, Controller* controller2);
19
+
20
+ void reset();
21
+
22
+ [[nodiscard]] std::uint8_t cpuRead(std::uint16_t addr);
23
+ void cpuWrite(std::uint16_t addr, std::uint8_t value);
24
+ [[nodiscard]] std::uint8_t lastBusValue() const { return lastBusValue_; }
25
+ void setLastBusValue(const std::uint8_t value) { lastBusValue_ = value; }
26
+
27
+ [[nodiscard]] std::uint8_t ppuRead(std::uint16_t addr) const;
28
+ void ppuWrite(std::uint16_t addr, std::uint8_t value) const;
29
+
30
+ void cpuNMI() const;
31
+ void cpuNMIClear() const;
32
+ void cpuStall(int cycles) const;
33
+ [[nodiscard]] std::int64_t cpuCycles() const;
34
+
35
+ [[nodiscard]] std::int32_t ppuScanline() const;
36
+ [[nodiscard]] std::int32_t ppuCycle() const;
37
+ [[nodiscard]] std::int64_t ppuFrame() const;
38
+ [[nodiscard]] std::int64_t ppuCycles() const;
39
+
40
+ void notifyA12(std::uint16_t addr) const;
41
+ void mapperPpuStep() const;
42
+
43
+ [[nodiscard]] bool irqAsserted() const;
44
+ [[nodiscard]] Mirroring mirroring() const;
45
+
46
+ private:
47
+ CPU* cpu_ = nullptr;
48
+ PPU* ppu_ = nullptr;
49
+ APU* apu_ = nullptr;
50
+ Cartridge* cartridge_ = nullptr;
51
+ Controller* controller1_ = nullptr;
52
+ Controller* controller2_ = nullptr;
53
+
54
+ std::uint8_t lastBusValue_ = 0;
55
+ };
56
+
57
+ }
58
+
59
+ #endif //SPIKYNES_BUS_H
@@ -0,0 +1,76 @@
1
+ #ifndef SPIKYNES_CARTRIDGE_H
2
+ #define SPIKYNES_CARTRIDGE_H
3
+
4
+ #include <cstdint>
5
+ #include <string>
6
+ #include <vector>
7
+ #include <memory>
8
+
9
+ #include "constants.h"
10
+ #include "core/mapper.h"
11
+
12
+ namespace nes_core {
13
+
14
+ class Bus;
15
+
16
+ struct INESHeader {
17
+ std::uint8_t prgRomBanks = 0; // PRG ROM size in 16KB units
18
+ std::uint8_t prgRamBanks = 0; // PRG RAM size in 8KB units
19
+ std::uint8_t chrRomBanks = 0; // CHR ROM size in 8KB units
20
+ std::uint16_t mapper = 0; // Mapper number
21
+ std::uint8_t subMapper = 0; // Sub-mapper number
22
+ bool isNES2 = false;
23
+ bool verticalMirroring = false;
24
+ bool batteryBacked = false;
25
+ bool trainer = false;
26
+ };
27
+
28
+ class Cartridge {
29
+ public:
30
+ explicit Cartridge(Bus* bus);
31
+
32
+ bool loadFromFile(const std::string& path);
33
+ bool loadFromMemory(const std::vector<std::uint8_t>& buffer);
34
+ void reset();
35
+
36
+ /** Initialize PRG ROM with given size (for testing). */
37
+ void initPrgRom(std::size_t size);
38
+ /** Initialize PRG RAM with given size (for testing). */
39
+ void initPrgRam(std::size_t size = PRG_RAM_BANK_SIZE);
40
+ /** Initialize CHR ROM with given size (for testing). */
41
+ void initChrRom(std::size_t size);
42
+ /** Initialize CHR RAM with given size (for testing). */
43
+ void initChrRam(std::size_t size = CHR_ROM_BANK_SIZE);
44
+
45
+ void initMapper(std::uint16_t mapperId, std::uint8_t prgRomBanks, std::uint8_t prgRamBanks, std::uint8_t chrRomBanks);
46
+
47
+ [[nodiscard]] std::uint8_t cpuRead(std::uint16_t addr) const;
48
+ void cpuWrite(std::uint16_t addr, std::uint8_t value);
49
+
50
+ [[nodiscard]] std::uint8_t ppuRead(std::uint16_t addr) const;
51
+ void ppuWrite(std::uint16_t addr, std::uint8_t value);
52
+
53
+ void notifyA12(std::uint16_t addr) const;
54
+ void mapperPpuStep() const;
55
+
56
+ [[nodiscard]] Mirroring mirroring() const;
57
+ [[nodiscard]] bool irqAsserted() const;
58
+ void setMirroring(const Mirroring m) { mirroring_ = m; }
59
+
60
+ [[nodiscard]] const INESHeader& header() const { return header_; }
61
+
62
+ private:
63
+ Bus* bus_ = nullptr;
64
+
65
+ INESHeader header_{};
66
+ std::vector<std::uint8_t> prgRom_{};
67
+ std::vector<std::uint8_t> prgRam_{};
68
+ std::vector<std::uint8_t> chrRom_{};
69
+ std::vector<std::uint8_t> chrRam_{};
70
+ std::unique_ptr<Mapper> mapper_ = nullptr;
71
+ Mirroring mirroring_ = Mirroring::HORIZONTAL;
72
+ };
73
+
74
+ }
75
+
76
+ #endif //SPIKYNES_CARTRIDGE_H
@@ -0,0 +1,58 @@
1
+ #ifndef SPIKYNES_CONSTANTS_H
2
+ #define SPIKYNES_CONSTANTS_H
3
+
4
+ #include <cstdint>
5
+ #include <cstdio>
6
+
7
+ namespace nes_core {
8
+
9
+ constexpr std::int32_t CPU_FREQUENCY_NTSC = 1789773; // 1.789773 MHz
10
+ constexpr std::int32_t CPU_FREQUENCY_PAL = 1662607; // 1.662607 MHz
11
+
12
+ constexpr std::size_t CPU_RAM_SIZE = 0x0800;
13
+ constexpr std::size_t CPU_STACK_OFFSET = 0x0100;
14
+ constexpr std::size_t CPU_PROGRAM_BEGIN = 0x0200;
15
+ constexpr std::uint16_t CPU_RAM_END = 0x2000;
16
+ constexpr std::uint16_t CPU_VECTOR_NMI = 0xFFFA;
17
+ constexpr std::uint16_t CPU_VECTOR_RESET = 0xFFFC;
18
+ constexpr std::uint16_t CPU_VECTOR_IRQ = 0xFFFE;
19
+
20
+ constexpr std::uint16_t PPU_REG_START = 0x2000;
21
+ constexpr std::size_t PPU_REG_COUNT = 8;
22
+ constexpr std::uint16_t PPU_REG_END = 0x4000;
23
+ constexpr std::uint16_t PPU_MEM_MASK = 0x3FFF;
24
+ constexpr std::uint16_t PPU_NAMETABLE_START = 0x2000;
25
+ constexpr std::uint16_t PPU_NAMETABLE_MIRRO_START = 0x3000;
26
+ constexpr std::size_t PPU_NAMETABLE_SIZE = 0x1000;
27
+ constexpr std::uint16_t PPU_PALETTE_START = 0x3F00;
28
+ constexpr std::size_t PPU_PALETTE_SIZE = 0x20;
29
+ constexpr std::uint16_t PPU_OAM_SIZE = 0x100;
30
+ constexpr std::uint16_t PPU_SECONDARY_OAM_SIZE = 32;
31
+ constexpr std::uint16_t PPU_FRAME_BUFFER_SIZE = 256 * 240;
32
+
33
+ constexpr std::uint16_t APU_REG_START = 0x4000;
34
+ constexpr std::size_t APU_REG_COUNT = 0x18;
35
+ constexpr std::uint16_t APU_REG_END = 0x4018;
36
+
37
+ constexpr std::uint16_t PRG_RAM_START = 0x6000;
38
+ constexpr std::size_t PRG_RAM_SIZE = 0x2000;
39
+ constexpr std::size_t PRG_RAM_BANK_SIZE = 0x2000;
40
+ constexpr std::size_t PRG_ROM_SIZE = 0x8000;
41
+ constexpr std::uint16_t PRG_ROM_START = 0x8000;
42
+ constexpr std::size_t PRG_ROM_BANK_SIZE = 0x4000;
43
+
44
+ constexpr std::size_t CHR_ROM_BANK_SIZE = 0x2000;
45
+
46
+ constexpr std::size_t INES_HEADER_SIZE = 16;
47
+
48
+ enum class Mirroring {
49
+ INVALID,
50
+ HORIZONTAL,
51
+ VERTICAL,
52
+ SINGLE_LOWER,
53
+ SINGLE_UPPER,
54
+ };
55
+
56
+ }
57
+
58
+ #endif //SPIKYNES_CONSTANTS_H