summaryrefslogtreecommitdiff
path: root/src/nes
diff options
context:
space:
mode:
Diffstat (limited to 'src/nes')
-rw-r--r--src/nes/CMakeLists.txt2
-rw-r--r--src/nes/cartridge.cc65
-rw-r--r--src/nes/cartridge.hh33
-rw-r--r--src/nes/nes.cc12
-rw-r--r--src/nes/nes.hh6
-rw-r--r--src/nes/util/CMakeLists.txt1
-rw-r--r--src/nes/util/address.hh40
7 files changed, 156 insertions, 3 deletions
diff --git a/src/nes/CMakeLists.txt b/src/nes/CMakeLists.txt
index 560dcdd..c217219 100644
--- a/src/nes/CMakeLists.txt
+++ b/src/nes/CMakeLists.txt
@@ -5,5 +5,7 @@ target_sources(
PRIVATE
nes.hh
nes.cc
+ cartridge.hh
+ cartridge.cc
display.hh
display.cc) \ No newline at end of file
diff --git a/src/nes/cartridge.cc b/src/nes/cartridge.cc
new file mode 100644
index 0000000..5970d2a
--- /dev/null
+++ b/src/nes/cartridge.cc
@@ -0,0 +1,65 @@
+#include "nes/cartridge.hh"
+#include <fstream>
+
+namespace
+{
+ constexpr auto prg_rom_bank_size = std::size_t{ 16 * 1024 };
+ constexpr auto chr_rom_bank_size = std::size_t{ 8 * 1024 };
+ constexpr auto ram_bank_size = std::size_t{ 8 * 1024 };
+} // namespace
+
+namespace nes
+{
+ auto cartridge::from_data(void const* data, std::size_t const length) -> cartridge
+ {
+ auto const bytes = static_cast<std::uint8_t const*>(data);
+ auto res = cartridge{};
+ res.is_valid_ = true;
+
+ // Parse header.
+ if (length < 16) { return cartridge{}; }
+ if (bytes[0] != 'N' || bytes[1] != 'E' || bytes[2] != 'S' || bytes[3] != 0x1A) { return cartridge{}; }
+ auto const prg_rom_banks = bytes[4];
+ auto const chr_rom_banks = bytes[5];
+ auto const control_1 = bytes[6];
+ auto const control_2 = bytes[7];
+ auto const ram_banks = (bytes[8] != 0) ? bytes[8] : 1;
+ auto const has_trainer = (control_1 & 0b00000100) != 0;
+
+ res.mapper_number_ =
+ (control_1 & 0b11110000) >> 4 |
+ (control_2 & 0b11110000) >> 0;
+
+ // Load program data.
+ auto offset = 16;
+ if (has_trainer) { offset += 512; }
+
+ auto const prg_rom_size = prg_rom_banks * prg_rom_bank_size;
+ if (length < offset + prg_rom_size) { return cartridge{}; }
+ res.prg_rom_ = std::vector(&bytes[offset], &bytes[offset + prg_rom_size]);
+
+ offset += prg_rom_size;
+
+ auto const chr_rom_size = chr_rom_banks * chr_rom_bank_size;
+ if (length < offset + chr_rom_size) { return cartridge{}; }
+ res.chr_rom_ = std::vector(&bytes[offset], &bytes[offset + chr_rom_size]);
+
+ res.ram_ = std::vector(ram_banks * ram_bank_size, std::uint8_t{ 0 });
+
+ return res;
+ }
+
+ auto cartridge::from_file(std::string_view const path) -> cartridge
+ {
+ auto stream = std::ifstream{ path, std::ios::binary };
+ if (!stream.good()) { return cartridge{}; }
+
+ stream.seekg(0, std::ios::end);
+ auto const length = stream.tellg();
+ stream.seekg(0, std::ios::beg);
+ auto data = std::vector<std::uint8_t>(length);
+ stream.read(reinterpret_cast<char*>(data.data()), data.size());
+
+ return from_data(data.data(), data.size());
+ }
+} // namespace nes \ No newline at end of file
diff --git a/src/nes/cartridge.hh b/src/nes/cartridge.hh
new file mode 100644
index 0000000..a334125
--- /dev/null
+++ b/src/nes/cartridge.hh
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <cstddef>
+#include <vector>
+#include <string_view>
+
+namespace nes
+{
+ class cartridge
+ {
+ bool is_valid_{ false };
+ std::vector<std::uint8_t> prg_rom_;
+ std::vector<std::uint8_t> chr_rom_;
+ std::vector<std::uint8_t> ram_;
+ std::uint8_t mapper_number_{ 0 };
+
+ public:
+ explicit cartridge() = default;
+
+ static auto from_data(void const* data, std::size_t length) -> cartridge;
+ static auto from_file(std::string_view path) -> cartridge;
+
+ auto is_valid() const -> bool { return is_valid_; }
+ auto get_prg_rom() const -> std::uint8_t const* { return prg_rom_.data(); }
+ auto get_prg_rom_length() const -> std::size_t { return prg_rom_.size(); }
+ auto get_chr_rom() const -> std::uint8_t const* { return chr_rom_.data(); }
+ auto get_chr_rom_length() const -> std::size_t { return chr_rom_.size(); }
+ auto get_ram() const -> std::uint8_t const* { return ram_.data(); }
+ auto get_ram_mut() -> std::uint8_t* { return ram_.data(); }
+ auto get_ram_length() const -> std::size_t { return ram_.size(); }
+ auto get_mapper_number() const -> std::uint8_t { return mapper_number_; }
+ };
+} // namespace nes \ No newline at end of file
diff --git a/src/nes/nes.cc b/src/nes/nes.cc
index c503d65..63175aa 100644
--- a/src/nes/nes.cc
+++ b/src/nes/nes.cc
@@ -1,12 +1,20 @@
#include "nes/nes.hh"
#include "nes/display.hh"
#include "nes/util/rgb.hh"
+#include <iostream>
namespace nes
{
- nes::nes(display& display)
- : display_{ display }
+ nes::nes(cartridge cartridge, display& display)
+ : cartridge_{ std::move(cartridge) }
+ , display_{ display }
{
+ if (!cartridge_.is_valid())
+ {
+ std::cerr << "Cartridge not valid!" << std::endl;
+ std::abort();
+ }
+
display.clear(rgb::from_hex(0xFF0000));
display.switch_buffers();
}
diff --git a/src/nes/nes.hh b/src/nes/nes.hh
index ab975f0..924ddd4 100644
--- a/src/nes/nes.hh
+++ b/src/nes/nes.hh
@@ -1,15 +1,19 @@
#pragma once
+#include "nes/cartridge.hh"
+
namespace nes
{
class display;
+ /// The main console abstraction.
class nes
{
+ cartridge cartridge_;
display& display_;
public:
- explicit nes(display&);
+ explicit nes(cartridge, display&);
nes(nes const&) = delete;
nes(nes&&) = delete;
diff --git a/src/nes/util/CMakeLists.txt b/src/nes/util/CMakeLists.txt
index c2255dd..1d30125 100644
--- a/src/nes/util/CMakeLists.txt
+++ b/src/nes/util/CMakeLists.txt
@@ -1,4 +1,5 @@
target_sources(
nes
PRIVATE
+ address.hh
rgb.hh) \ No newline at end of file
diff --git a/src/nes/util/address.hh b/src/nes/util/address.hh
new file mode 100644
index 0000000..f02b66b
--- /dev/null
+++ b/src/nes/util/address.hh
@@ -0,0 +1,40 @@
+#pragma once
+
+#include <cstdint>
+
+namespace nes
+{
+ /// An address on the NES.
+ class address
+ {
+ std::uint16_t absolute_{ 0 };
+
+ public:
+ /// Create a zero address.
+ explicit address() = default;
+
+ /// Create an address from its absolute value.
+ explicit address(std::uint16_t const absolute)
+ : absolute_{ absolute }
+ {
+ }
+
+ /// Get the absolute address value.
+ auto get_absolute() const -> std::uint16_t
+ {
+ return absolute_;
+ }
+
+ /// Get the address page (the first 8 bits).
+ auto get_page() const -> std::uint8_t
+ {
+ return static_cast<std::uint8_t>((get_absolute() & 0xFF00) >> 8);
+ }
+
+ /// Get the offset within the page (the last 8 bits).
+ auto get_offset() const -> std::uint8_t
+ {
+ return static_cast<std::uint8_t>((get_absolute() & 0x00FF) >> 0);
+ }
+ };
+} // namespace nes \ No newline at end of file