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.
- spiky_nes-0.1.0/CMakeLists.txt +254 -0
- spiky_nes-0.1.0/PKG-INFO +53 -0
- spiky_nes-0.1.0/include/core/apu.h +183 -0
- spiky_nes-0.1.0/include/core/bus.h +59 -0
- spiky_nes-0.1.0/include/core/cartridge.h +76 -0
- spiky_nes-0.1.0/include/core/constants.h +58 -0
- spiky_nes-0.1.0/include/core/controller.h +28 -0
- spiky_nes-0.1.0/include/core/cpu.h +358 -0
- spiky_nes-0.1.0/include/core/emulator.h +59 -0
- spiky_nes-0.1.0/include/core/mapper/mapper_000.h +20 -0
- spiky_nes-0.1.0/include/core/mapper/mapper_001.h +41 -0
- spiky_nes-0.1.0/include/core/mapper/mapper_002.h +22 -0
- spiky_nes-0.1.0/include/core/mapper/mapper_003.h +22 -0
- spiky_nes-0.1.0/include/core/mapper/mapper_004.h +55 -0
- spiky_nes-0.1.0/include/core/mapper.h +65 -0
- spiky_nes-0.1.0/include/core/ppu.h +199 -0
- spiky_nes-0.1.0/include/core/utils.h +39 -0
- spiky_nes-0.1.0/include/ui/sdl_emulator.h +51 -0
- spiky_nes-0.1.0/pyproject.toml +54 -0
- spiky_nes-0.1.0/python/README.md +35 -0
- spiky_nes-0.1.0/python/binding.cpp +103 -0
- spiky_nes-0.1.0/python/wrapper/__init__.py +9 -0
- spiky_nes-0.1.0/src/core/apu.cpp +686 -0
- spiky_nes-0.1.0/src/core/bus.cpp +121 -0
- spiky_nes-0.1.0/src/core/cartridge.cpp +210 -0
- spiky_nes-0.1.0/src/core/controller.cpp +32 -0
- spiky_nes-0.1.0/src/core/cpu/addressing.cpp +147 -0
- spiky_nes-0.1.0/src/core/cpu/operation.cpp +746 -0
- spiky_nes-0.1.0/src/core/cpu/unofficial.cpp +230 -0
- spiky_nes-0.1.0/src/core/cpu.cpp +602 -0
- spiky_nes-0.1.0/src/core/emulator.cpp +75 -0
- spiky_nes-0.1.0/src/core/mapper/mapper_000.cpp +53 -0
- spiky_nes-0.1.0/src/core/mapper/mapper_001.cpp +156 -0
- spiky_nes-0.1.0/src/core/mapper/mapper_002.cpp +55 -0
- spiky_nes-0.1.0/src/core/mapper/mapper_003.cpp +58 -0
- spiky_nes-0.1.0/src/core/mapper/mapper_004.cpp +198 -0
- spiky_nes-0.1.0/src/core/mapper.cpp +38 -0
- spiky_nes-0.1.0/src/core/ppu.cpp +746 -0
- spiky_nes-0.1.0/src/core/utils.cpp +61 -0
- spiky_nes-0.1.0/src/ui/main.cpp +14 -0
- 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()
|
spiky_nes-0.1.0/PKG-INFO
ADDED
|
@@ -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
|