Adding controller.
Adding logging. Fixing lots of bugs.
This commit is contained in:
parent
40c7ff5882
commit
796828fa01
@ -22,7 +22,7 @@ add_executable(nes
|
|||||||
src/Window.cpp
|
src/Window.cpp
|
||||||
src/Window.h
|
src/Window.h
|
||||||
src/Shifter.cpp
|
src/Shifter.cpp
|
||||||
src/Shifter.h)
|
src/Shifter.h src/Logger.cpp src/Logger.h src/Controller.cpp src/Controller.h src/SdlKeyboardController.cpp src/SdlKeyboardController.h)
|
||||||
|
|
||||||
find_package(SDL2 CONFIG REQUIRED)
|
find_package(SDL2 CONFIG REQUIRED)
|
||||||
find_package(fmt REQUIRED)
|
find_package(fmt REQUIRED)
|
||||||
|
|||||||
6
main.cpp
6
main.cpp
@ -1,5 +1,6 @@
|
|||||||
#include "src/Nes.h"
|
#include "src/Nes.h"
|
||||||
#include "src/Window.h"
|
#include "src/Window.h"
|
||||||
|
#include "src/SdlKeyboardController.h"
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
@ -14,9 +15,10 @@ int main() {
|
|||||||
window.setSize(nes::Ppu::SCREEN_WIDTH * 4, nes::Ppu::SCREEN_HEIGHT * 4);
|
window.setSize(nes::Ppu::SCREEN_WIDTH * 4, nes::Ppu::SCREEN_HEIGHT * 4);
|
||||||
|
|
||||||
device.setNewFrameCallback(std::bind(&nes::SdlWindow::drawFrame, &window, _1));
|
device.setNewFrameCallback(std::bind(&nes::SdlWindow::drawFrame, &window, _1));
|
||||||
//device.insertCartridge("/home/selim/Downloads/nestest.nes");
|
device.connect(std::make_shared<SdlKeyboardController>());
|
||||||
|
device.insertCartridge("/home/selim/Downloads/smb.nes");
|
||||||
//device.insertCartridge("/Users/selim/Documents/nestest.nes");
|
//device.insertCartridge("/Users/selim/Documents/nestest.nes");
|
||||||
device.insertCartridge("C:\\Users\\selim\\Documents\\nestest.nes");
|
//device.insertCartridge("C:\\Users\\selim\\Documents\\nestest.nes");
|
||||||
|
|
||||||
SDL_Event e;
|
SDL_Event e;
|
||||||
uint64_t cycles = 0;
|
uint64_t cycles = 0;
|
||||||
|
|||||||
21
src/Bus.cpp
21
src/Bus.cpp
@ -23,6 +23,9 @@ namespace nes {
|
|||||||
else if(address >= 0x8000) {
|
else if(address >= 0x8000) {
|
||||||
return _cartridge->readPrg(address);
|
return _cartridge->readPrg(address);
|
||||||
}
|
}
|
||||||
|
else if(address == 0x4016) {
|
||||||
|
return _controller1->read();
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -37,6 +40,9 @@ namespace nes {
|
|||||||
else if(address >= 0x8000) {
|
else if(address >= 0x8000) {
|
||||||
std::cout << "Cartridge write at address: " << address << std::endl;
|
std::cout << "Cartridge write at address: " << address << std::endl;
|
||||||
}
|
}
|
||||||
|
else if(address == 0x4016) {
|
||||||
|
_controller1->poll();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bus::connect(std::shared_ptr<Cartridge> cartridge) {
|
void Bus::connect(std::shared_ptr<Cartridge> cartridge) {
|
||||||
@ -46,4 +52,19 @@ namespace nes {
|
|||||||
void Bus::connect(std::shared_ptr<Ppu> ppu) {
|
void Bus::connect(std::shared_ptr<Ppu> ppu) {
|
||||||
_ppu = std::move(ppu);
|
_ppu = std::move(ppu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Bus::connect(std::shared_ptr<Controller> controller) {
|
||||||
|
_controller1 = std::move(controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For debug
|
||||||
|
// Calc "hash" - just sum of the bytes of first page
|
||||||
|
// Can be useful for detecting change in ram
|
||||||
|
uint32_t Bus::zpHash() const {
|
||||||
|
uint32_t sum = 0;
|
||||||
|
for(size_t i = 0; i < 2048; ++i) {
|
||||||
|
sum += _ram[i];
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "Cartridge.h"
|
#include "Cartridge.h"
|
||||||
#include "Ppu.h"
|
#include "Ppu.h"
|
||||||
|
#include "Controller.h"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -20,11 +21,14 @@ namespace nes {
|
|||||||
void write(uint16_t address, uint8_t value);
|
void write(uint16_t address, uint8_t value);
|
||||||
void connect(std::shared_ptr<Cartridge> cartridge);
|
void connect(std::shared_ptr<Cartridge> cartridge);
|
||||||
void connect(std::shared_ptr<Ppu> ppu);
|
void connect(std::shared_ptr<Ppu> ppu);
|
||||||
|
void connect(std::shared_ptr<Controller> controller);
|
||||||
|
[[nodiscard]] uint32_t zpHash() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<uint8_t[]> _ram;
|
std::unique_ptr<uint8_t[]> _ram;
|
||||||
std::shared_ptr<Cartridge> _cartridge;
|
std::shared_ptr<Cartridge> _cartridge;
|
||||||
std::shared_ptr<Ppu> _ppu;
|
std::shared_ptr<Ppu> _ppu;
|
||||||
|
std::shared_ptr<Controller> _controller1;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
18
src/Controller.cpp
Normal file
18
src/Controller.cpp
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
//
|
||||||
|
// Created by selim on 9/17/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "Controller.h"
|
||||||
|
|
||||||
|
namespace nes {
|
||||||
|
|
||||||
|
Controller::Controller(): _data{} {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t Controller::read() {
|
||||||
|
uint8_t result = (_data & 0x80) > 0;
|
||||||
|
_data <<= 1;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
36
src/Controller.h
Normal file
36
src/Controller.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
//
|
||||||
|
// Created by selim on 9/17/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef NES_CONTROLLER_H
|
||||||
|
#define NES_CONTROLLER_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace nes {
|
||||||
|
|
||||||
|
class Controller {
|
||||||
|
public:
|
||||||
|
enum Key: uint8_t {
|
||||||
|
A = 7,
|
||||||
|
B = 6,
|
||||||
|
Select = 5,
|
||||||
|
Start = 4,
|
||||||
|
Up = 3,
|
||||||
|
Down = 2,
|
||||||
|
Left = 1,
|
||||||
|
Right = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
Controller();
|
||||||
|
uint8_t read();
|
||||||
|
virtual void poll() = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint8_t _data;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //NES_CONTROLLER_H
|
||||||
37
src/Cpu.cpp
37
src/Cpu.cpp
@ -75,7 +75,7 @@ namespace nes {
|
|||||||
_instructions[0xD0] = {"BNE", &Cpu::BNE, &Cpu::REL, 2, false};
|
_instructions[0xD0] = {"BNE", &Cpu::BNE, &Cpu::REL, 2, false};
|
||||||
_instructions[0x81] = {"STA", &Cpu::STA, &Cpu::IZX, 6, false};
|
_instructions[0x81] = {"STA", &Cpu::STA, &Cpu::IZX, 6, false};
|
||||||
_instructions[0x8D] = {"STA", &Cpu::STA, &Cpu::ABS, 4, false};
|
_instructions[0x8D] = {"STA", &Cpu::STA, &Cpu::ABS, 4, false};
|
||||||
_instructions[0x91] = {"STA", &Cpu::STA, &Cpu::IZY, 5, true};
|
_instructions[0x91] = {"STA", &Cpu::STA, &Cpu::IZY, 6, false};
|
||||||
_instructions[0x95] = {"STA", &Cpu::STA, &Cpu::ZPX, 4, false};
|
_instructions[0x95] = {"STA", &Cpu::STA, &Cpu::ZPX, 4, false};
|
||||||
_instructions[0x99] = {"STA", &Cpu::STA, &Cpu::ABY, 5, false};
|
_instructions[0x99] = {"STA", &Cpu::STA, &Cpu::ABY, 5, false};
|
||||||
_instructions[0x9D] = {"STA", &Cpu::STA, &Cpu::ABX, 5, false};
|
_instructions[0x9D] = {"STA", &Cpu::STA, &Cpu::ABX, 5, false};
|
||||||
@ -174,34 +174,36 @@ namespace nes {
|
|||||||
uint16_t hi = _bus->read(0xFFFD);
|
uint16_t hi = _bus->read(0xFFFD);
|
||||||
|
|
||||||
PC = (hi << 8) | lo;
|
PC = (hi << 8) | lo;
|
||||||
|
|
||||||
|
_ticks = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cpu::tick() {
|
bool Cpu::tick() {
|
||||||
|
bool executed = false;
|
||||||
|
|
||||||
if(_ticks == 0) {
|
if(_ticks == 0) {
|
||||||
uint8_t opcode = _bus->read(PC++);
|
uint8_t opcode = _bus->read(PC++);
|
||||||
auto instruction = _instructions[opcode];
|
auto instruction = _instructions[opcode];
|
||||||
|
_currentOpcode = opcode;
|
||||||
|
|
||||||
if(instruction.getAddress == nullptr) {
|
if(instruction.getAddress == nullptr) {
|
||||||
std::cout << "Unknown instruction: " << (int)opcode << std::endl;
|
std::cout << "Unknown instruction: " << (int)opcode << std::endl;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto args = (this->*instruction.getAddress)();
|
auto args = (this->*instruction.getAddress)();
|
||||||
(this->*instruction.process)(args);
|
(this->*instruction.process)(args);
|
||||||
|
|
||||||
// auto str = std::format("{} ({:02X}), PC: {:X}, SP: {:X}, A: {:02X}, X: {:02X}, Y: {:02X}, [N:{}, V:{}, B{}, D{}, I{}, Z:{}, C:{}]", instruction.name, opcode, PC, SP, A, X, Y,
|
_ticks += instruction.cycles;
|
||||||
// (int)getFlag(Negative), (int)getFlag(Overflow), (int)getFlag(Break), (int)getFlag(DecimalMode), (int)getFlag(InterruptDisable), (int)getFlag(Zero), (int)getFlag(Carry));
|
|
||||||
// std::cout << str << std::endl;
|
|
||||||
|
|
||||||
//std::cout << instruction.name << std::hex << ", OpCode: " << (int)opcode << ", PC: " << (int)PC << ", SP: " << (int)SP << ", A: " << (int)A << ", X: " << (int)X << ", Y: " << (int)Y << std::endl;
|
|
||||||
|
|
||||||
_ticks = instruction.cycles;
|
|
||||||
if(instruction.variableCycles) {
|
if(instruction.variableCycles) {
|
||||||
_ticks += args.cycles;
|
_ticks += args.cycles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
executed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
_ticks--;
|
_ticks--;
|
||||||
|
return executed;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cpu::nmi() {
|
void Cpu::nmi() {
|
||||||
@ -220,6 +222,21 @@ namespace nes {
|
|||||||
_ticks = 8;
|
_ticks = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Cpu::state() const {
|
||||||
|
return std::format("{} ({:02X}), PC: {:X}, SP: {:X}, A: {:02X}, X: {:02X}, Y: {:02X}, [N:{}, V:{}, B{}, D{}, I{}, Z:{}, C:{}], H: {:08X}",
|
||||||
|
_instructions[_currentOpcode].name,
|
||||||
|
_currentOpcode,
|
||||||
|
PC, SP, A, X, Y,
|
||||||
|
(int)getFlag(Negative),
|
||||||
|
(int)getFlag(Overflow),
|
||||||
|
(int)getFlag(Break),
|
||||||
|
(int)getFlag(DecimalMode),
|
||||||
|
(int)getFlag(InterruptDisable),
|
||||||
|
(int)getFlag(Zero),
|
||||||
|
(int)getFlag(Carry),
|
||||||
|
_bus->zpHash());
|
||||||
|
}
|
||||||
|
|
||||||
void Cpu::setFlag(CpuFlags flag, bool value) {
|
void Cpu::setFlag(CpuFlags flag, bool value) {
|
||||||
if(value) {
|
if(value) {
|
||||||
flags |= flag;
|
flags |= flag;
|
||||||
|
|||||||
@ -42,12 +42,13 @@ namespace nes {
|
|||||||
public:
|
public:
|
||||||
Cpu();
|
Cpu();
|
||||||
void reset();
|
void reset();
|
||||||
void tick();
|
bool tick();
|
||||||
void setFlag(CpuFlags flag, bool value);
|
void setFlag(CpuFlags flag, bool value);
|
||||||
bool getFlag(CpuFlags flag) const;
|
bool getFlag(CpuFlags flag) const;
|
||||||
void setStartAddress(uint16_t address);
|
void setStartAddress(uint16_t address);
|
||||||
Bus* bus() const;
|
Bus* bus() const;
|
||||||
void nmi();
|
void nmi();
|
||||||
|
std::string state() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t _ticks;
|
size_t _ticks;
|
||||||
@ -64,6 +65,10 @@ namespace nes {
|
|||||||
uint8_t SP; // Stack Pointer
|
uint8_t SP; // Stack Pointer
|
||||||
uint8_t flags;
|
uint8_t flags;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Debug info
|
||||||
|
uint8_t _currentOpcode;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void branch(InstructionArgs args);
|
void branch(InstructionArgs args);
|
||||||
|
|
||||||
|
|||||||
35
src/Logger.cpp
Normal file
35
src/Logger.cpp
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
//
|
||||||
|
// Created by selim on 9/16/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "Logger.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <fstream>
|
||||||
|
#include <format>
|
||||||
|
|
||||||
|
namespace nes {
|
||||||
|
|
||||||
|
Logger::Logger(size_t size): _size{size}, _offset{0} {
|
||||||
|
_data = std::make_unique<uint8_t[]>(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Logger::addLine(uint64_t cycle, std::string_view line) {
|
||||||
|
if((_offset + line.size() + 10) >= _size) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string cycleString = std::format("[{:06d}] ", cycle);
|
||||||
|
std::memcpy(_data.get() + _offset, cycleString.data(), cycleString.size());
|
||||||
|
_offset += cycleString.size();
|
||||||
|
|
||||||
|
std::memcpy(_data.get() + _offset, line.data(), line.size());
|
||||||
|
_offset += line.size();
|
||||||
|
_data[_offset++] = '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
void Logger::dump(const fs::path &path) {
|
||||||
|
std::ofstream file(path, std::ios::binary);
|
||||||
|
file.write(reinterpret_cast<char*>(_data.get()), static_cast<std::streamsize>(_offset));
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/Logger.h
Normal file
30
src/Logger.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
//
|
||||||
|
// Created by selim on 9/16/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef NES_LOGGER_H
|
||||||
|
#define NES_LOGGER_H
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <memory>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
namespace nes {
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
class Logger {
|
||||||
|
public:
|
||||||
|
explicit Logger(size_t size);
|
||||||
|
void addLine(uint64_t cycle, std::string_view line);
|
||||||
|
void dump(const fs::path& path);
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t _size;
|
||||||
|
size_t _offset;
|
||||||
|
std::unique_ptr<uint8_t[]> _data;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //NES_LOGGER_H
|
||||||
20
src/Nes.cpp
20
src/Nes.cpp
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
namespace nes {
|
namespace nes {
|
||||||
|
|
||||||
Nes::Nes(): _cycles{} {
|
Nes::Nes(): _cycles{}, _logger(100*1024*1024) {
|
||||||
_cpu = std::make_unique<Cpu>();
|
_cpu = std::make_unique<Cpu>();
|
||||||
_ppu = std::make_shared<Ppu>();
|
_ppu = std::make_shared<Ppu>();
|
||||||
}
|
}
|
||||||
@ -23,6 +23,10 @@ namespace nes {
|
|||||||
reset(address);
|
reset(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Nes::connect(std::shared_ptr<Controller> controller) {
|
||||||
|
_cpu->bus()->connect(std::move(controller));
|
||||||
|
}
|
||||||
|
|
||||||
void Nes::reset(std::optional<uint16_t> address) {
|
void Nes::reset(std::optional<uint16_t> address) {
|
||||||
_cpu->reset();
|
_cpu->reset();
|
||||||
_ppu->reset();
|
_ppu->reset();
|
||||||
@ -36,15 +40,27 @@ namespace nes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Nes::tick() {
|
void Nes::tick() {
|
||||||
|
|
||||||
bool needInterrupt = _ppu->tick();
|
bool needInterrupt = _ppu->tick();
|
||||||
|
// _logger.addLine(_cycles, _ppu->state());
|
||||||
|
|
||||||
if(_cycles % 3 == 0) {
|
if(_cycles % 3 == 0) {
|
||||||
_cpu->tick();
|
bool instructionExecuted = _cpu->tick();
|
||||||
|
// if(instructionExecuted) {
|
||||||
|
// _logger.addLine(_cycles, _cpu->state());
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
if(needInterrupt) {
|
if(needInterrupt) {
|
||||||
_cpu->nmi();
|
_cpu->nmi();
|
||||||
|
// _logger.addLine(_cycles, "NMI");
|
||||||
|
// _logger.addLine(_cycles, _cpu->state());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if(_cycles == 500000) {
|
||||||
|
// _logger.dump("/home/selim/Documents/log.txt");
|
||||||
|
// }
|
||||||
|
|
||||||
_cycles++;
|
_cycles++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
#include "Cpu.h"
|
#include "Cpu.h"
|
||||||
#include "Cartridge.h"
|
#include "Cartridge.h"
|
||||||
#include "Ppu.h"
|
#include "Ppu.h"
|
||||||
|
#include "Logger.h"
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
@ -20,6 +21,7 @@ namespace nes {
|
|||||||
public:
|
public:
|
||||||
Nes();
|
Nes();
|
||||||
void insertCartridge(const fs::path& path, std::optional<uint16_t> address = std::nullopt);
|
void insertCartridge(const fs::path& path, std::optional<uint16_t> address = std::nullopt);
|
||||||
|
void connect(std::shared_ptr<Controller> controller);
|
||||||
void reset(std::optional<uint16_t> address = std::nullopt);
|
void reset(std::optional<uint16_t> address = std::nullopt);
|
||||||
void setNewFrameCallback(std::function<void(const Pixel*)> onNewFrame);
|
void setNewFrameCallback(std::function<void(const Pixel*)> onNewFrame);
|
||||||
void tick();
|
void tick();
|
||||||
@ -29,6 +31,7 @@ namespace nes {
|
|||||||
std::unique_ptr<Cpu> _cpu;
|
std::unique_ptr<Cpu> _cpu;
|
||||||
std::shared_ptr<Ppu> _ppu;
|
std::shared_ptr<Ppu> _ppu;
|
||||||
std::shared_ptr<Cartridge> _cartridge;
|
std::shared_ptr<Cartridge> _cartridge;
|
||||||
|
Logger _logger;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
59
src/Ppu.cpp
59
src/Ppu.cpp
@ -3,6 +3,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#include "Ppu.h"
|
#include "Ppu.h"
|
||||||
|
#include <format>
|
||||||
|
|
||||||
namespace nes {
|
namespace nes {
|
||||||
|
|
||||||
@ -80,8 +81,13 @@ namespace nes {
|
|||||||
|
|
||||||
bool Ppu::tick() {
|
bool Ppu::tick() {
|
||||||
|
|
||||||
|
_needEmitNmi = false;
|
||||||
|
|
||||||
if(_scanline == 241 && _column == 1) {
|
if(_scanline == 241 && _column == 1) {
|
||||||
_status.verticalBlank = 1;
|
_status.verticalBlank = 1;
|
||||||
|
if(_control.enableNmi) {
|
||||||
|
_needEmitNmi = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// All visible scanlines
|
// All visible scanlines
|
||||||
@ -97,8 +103,10 @@ namespace nes {
|
|||||||
|
|
||||||
// Preloading some data ahead of time
|
// Preloading some data ahead of time
|
||||||
if ((_column >= 2 && _column < 258) || (_column >= 321 && _column < 338)) {
|
if ((_column >= 2 && _column < 258) || (_column >= 321 && _column < 338)) {
|
||||||
|
if(_mask.renderBackground) {
|
||||||
_bgPatternShifter.shift();
|
_bgPatternShifter.shift();
|
||||||
_bgAttribShifter.shift();
|
_bgAttribShifter.shift();
|
||||||
|
}
|
||||||
prepareNextBgTile(_column);
|
prepareNextBgTile(_column);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,21 +133,15 @@ namespace nes {
|
|||||||
uint8_t colorIndex = 0;
|
uint8_t colorIndex = 0;
|
||||||
uint8_t palette = 0;
|
uint8_t palette = 0;
|
||||||
|
|
||||||
if(_column < SCREEN_WIDTH && _scanline < SCREEN_HEIGHT && _scanline >= 0) {
|
|
||||||
|
|
||||||
if(_mask.renderBackground) {
|
if(_mask.renderBackground) {
|
||||||
colorIndex = _bgPatternShifter.getValue(_fineX);
|
colorIndex = _bgPatternShifter.getValue(_fineX);
|
||||||
palette = _bgAttribShifter.getValue(_fineX);
|
palette = _bgAttribShifter.getValue(_fineX);
|
||||||
Pixel pixel = getColor(palette, colorIndex);
|
Pixel pixel = getColor(palette, colorIndex);
|
||||||
|
|
||||||
// For debugging
|
_curPalette = palette;
|
||||||
if (colorIndex != 0) {
|
_curColorIndex = colorIndex;
|
||||||
pixel = Pixel(0xFF, 0xFF, 0xFF);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
pixel = Pixel();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if(_column < SCREEN_WIDTH && _scanline < SCREEN_HEIGHT && _scanline >= 0) {
|
||||||
setPixel(_scanline, _column, pixel);
|
setPixel(_scanline, _column, pixel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -155,7 +157,7 @@ namespace nes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return _status.verticalBlank && _control.enableNmi;
|
return _needEmitNmi;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t Ppu::read(uint16_t address) {
|
uint8_t Ppu::read(uint16_t address) {
|
||||||
@ -204,7 +206,7 @@ namespace nes {
|
|||||||
_tRamAddress.value = (_tRamAddress.value & 0xFF00) | value;
|
_tRamAddress.value = (_tRamAddress.value & 0xFF00) | value;
|
||||||
_vRamAddress.value = _tRamAddress.value;
|
_vRamAddress.value = _tRamAddress.value;
|
||||||
} else {
|
} else {
|
||||||
_tRamAddress.value = ((value & 0x3F) << 8) | (_tRamAddress.value & 0x00FF);
|
_tRamAddress.value = (((value & 0x3F) << 8) | (_tRamAddress.value & 0x00FF));
|
||||||
}
|
}
|
||||||
_addressWriteInProgress = !_addressWriteInProgress;
|
_addressWriteInProgress = !_addressWriteInProgress;
|
||||||
break;
|
break;
|
||||||
@ -273,9 +275,6 @@ namespace nes {
|
|||||||
else if(address >= 0x2000 && address < 0x3F00) {
|
else if(address >= 0x2000 && address < 0x3F00) {
|
||||||
address &= 0x0FFF;
|
address &= 0x0FFF;
|
||||||
_nameTable[address & 0x03FF] = value;
|
_nameTable[address & 0x03FF] = value;
|
||||||
if (value == 32) {
|
|
||||||
int x = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if(address >= 0x3F00 && address < 0x4000) {
|
else if(address >= 0x3F00 && address < 0x4000) {
|
||||||
address &= 0x1F;
|
address &= 0x1F;
|
||||||
@ -325,7 +324,7 @@ namespace nes {
|
|||||||
uint16_t address = (_control.patternBackground << 12)
|
uint16_t address = (_control.patternBackground << 12)
|
||||||
| ((uint16_t)_nextBgTile.id << 4)
|
| ((uint16_t)_nextBgTile.id << 4)
|
||||||
| (_vRamAddress.fineY + 8);
|
| (_vRamAddress.fineY + 8);
|
||||||
_nextBgTile.lsb = internalRead(address);
|
_nextBgTile.msb = internalRead(address);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 7:
|
case 7:
|
||||||
@ -367,17 +366,43 @@ namespace nes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Ppu::updateScrollX() {
|
void Ppu::updateScrollX() {
|
||||||
if(_mask.renderBackground && !_mask.renderSprites) {
|
if(_mask.renderBackground || _mask.renderSprites) {
|
||||||
_vRamAddress.nameTableX = _tRamAddress.nameTableX;
|
_vRamAddress.nameTableX = _tRamAddress.nameTableX;
|
||||||
_vRamAddress.coarseX = _tRamAddress.coarseX;
|
_vRamAddress.coarseX = _tRamAddress.coarseX;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ppu::updateScrollY() {
|
void Ppu::updateScrollY() {
|
||||||
if(_mask.renderBackground && !_mask.renderSprites) {
|
if(_mask.renderBackground || _mask.renderSprites) {
|
||||||
_vRamAddress.nameTableY =_tRamAddress.nameTableY;
|
_vRamAddress.nameTableY =_tRamAddress.nameTableY;
|
||||||
_vRamAddress.coarseY = _tRamAddress.coarseY;
|
_vRamAddress.coarseY = _tRamAddress.coarseY;
|
||||||
_vRamAddress.fineY = _tRamAddress.fineY;
|
_vRamAddress.fineY = _tRamAddress.fineY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Ppu::state() const {
|
||||||
|
|
||||||
|
auto palettes = (uint32_t*)_paletteTable.get();
|
||||||
|
|
||||||
|
return std::format("X:{:03d}, Y:{:03d}, V:{:d}, vR: [CX:{:02X}, CY:{:02X}, NX:{:d}, NY:{:d}, FY:{:d}], tR: [CX:{:02X}, CY:{:02X}, NX:{:d}, NY:{:d}, FY:{:d}], FX:{:d}, AL:{:d}, PI:{:02X}, CI:{:02X}, ASL:{:04X}, ASH:{:04X}",
|
||||||
|
_column,
|
||||||
|
_scanline,
|
||||||
|
_status.verticalBlank,
|
||||||
|
_vRamAddress.coarseX,
|
||||||
|
_vRamAddress.coarseY,
|
||||||
|
_vRamAddress.nameTableX,
|
||||||
|
_vRamAddress.nameTableY,
|
||||||
|
_vRamAddress.fineY,
|
||||||
|
_tRamAddress.coarseX,
|
||||||
|
_tRamAddress.coarseY,
|
||||||
|
_tRamAddress.nameTableX,
|
||||||
|
_tRamAddress.nameTableY,
|
||||||
|
_tRamAddress.fineY,
|
||||||
|
_fineX,
|
||||||
|
_addressWriteInProgress,
|
||||||
|
_curPalette,
|
||||||
|
_curColorIndex,
|
||||||
|
_bgAttribShifter._lo,
|
||||||
|
_bgAttribShifter._hi);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
18
src/Ppu.h
18
src/Ppu.h
@ -80,12 +80,12 @@ namespace nes {
|
|||||||
|
|
||||||
union LoopyRegister {
|
union LoopyRegister {
|
||||||
struct {
|
struct {
|
||||||
uint8_t coarseX : 5;
|
uint16_t coarseX : 5;
|
||||||
uint8_t coarseY : 5;
|
uint16_t coarseY : 5;
|
||||||
uint8_t nameTableX : 1;
|
uint16_t nameTableX : 1;
|
||||||
uint8_t nameTableY : 1;
|
uint16_t nameTableY : 1;
|
||||||
uint8_t fineY : 3;
|
uint16_t fineY : 3;
|
||||||
uint8_t unused : 1;
|
uint16_t unused : 1;
|
||||||
};
|
};
|
||||||
uint16_t value;
|
uint16_t value;
|
||||||
};
|
};
|
||||||
@ -105,6 +105,7 @@ namespace nes {
|
|||||||
void setPixel(uint16_t row, uint16_t column, Pixel pixel);
|
void setPixel(uint16_t row, uint16_t column, Pixel pixel);
|
||||||
void connect(std::shared_ptr<Cartridge> cartridge);
|
void connect(std::shared_ptr<Cartridge> cartridge);
|
||||||
void reset();
|
void reset();
|
||||||
|
std::string state() const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::function<void(const Pixel*)> onNewFrame;
|
std::function<void(const Pixel*)> onNewFrame;
|
||||||
@ -134,6 +135,7 @@ namespace nes {
|
|||||||
TileInfo _nextBgTile;
|
TileInfo _nextBgTile;
|
||||||
Shifter _bgPatternShifter;
|
Shifter _bgPatternShifter;
|
||||||
Shifter _bgAttribShifter;
|
Shifter _bgAttribShifter;
|
||||||
|
bool _needEmitNmi;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<uint8_t[]> _nameTable;
|
std::unique_ptr<uint8_t[]> _nameTable;
|
||||||
@ -143,6 +145,10 @@ namespace nes {
|
|||||||
private:
|
private:
|
||||||
std::unique_ptr<Pixel[]> _frameBuffer;
|
std::unique_ptr<Pixel[]> _frameBuffer;
|
||||||
std::shared_ptr<Cartridge> _cartridge;
|
std::shared_ptr<Cartridge> _cartridge;
|
||||||
|
|
||||||
|
private: // For debug
|
||||||
|
uint8_t _curPalette;
|
||||||
|
uint8_t _curColorIndex;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
19
src/SdlKeyboardController.cpp
Normal file
19
src/SdlKeyboardController.cpp
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
//
|
||||||
|
// Created by selim on 9/17/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "SdlKeyboardController.h"
|
||||||
|
#include <SDL.h>
|
||||||
|
|
||||||
|
void SdlKeyboardController::poll() {
|
||||||
|
auto state = SDL_GetKeyboardState(nullptr);
|
||||||
|
_data = 0;
|
||||||
|
_data |= state[SDL_SCANCODE_Z] << Key::A;
|
||||||
|
_data |= state[SDL_SCANCODE_X] << Key::B;
|
||||||
|
_data |= state[SDL_SCANCODE_TAB] << Key::Select;
|
||||||
|
_data |= state[SDL_SCANCODE_RETURN] << Key::Start;
|
||||||
|
_data |= state[SDL_SCANCODE_UP] << Key::Up;
|
||||||
|
_data |= state[SDL_SCANCODE_DOWN] << Key::Down;
|
||||||
|
_data |= state[SDL_SCANCODE_LEFT] << Key::Left;
|
||||||
|
_data |= state[SDL_SCANCODE_RIGHT] << Key::Right;
|
||||||
|
}
|
||||||
16
src/SdlKeyboardController.h
Normal file
16
src/SdlKeyboardController.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//
|
||||||
|
// Created by selim on 9/17/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef NES_SDLKEYBOARDCONTROLLER_H
|
||||||
|
#define NES_SDLKEYBOARDCONTROLLER_H
|
||||||
|
|
||||||
|
#include "Controller.h"
|
||||||
|
|
||||||
|
class SdlKeyboardController: public nes::Controller {
|
||||||
|
public:
|
||||||
|
void poll() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //NES_SDLKEYBOARDCONTROLLER_H
|
||||||
@ -16,7 +16,7 @@ namespace nes {
|
|||||||
void shift();
|
void shift();
|
||||||
uint16_t getValue(uint8_t offset);
|
uint16_t getValue(uint8_t offset);
|
||||||
|
|
||||||
private:
|
public:
|
||||||
uint16_t _lo;
|
uint16_t _lo;
|
||||||
uint16_t _hi;
|
uint16_t _hi;
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user