Implement a few registers and move engines to NV2A state class

This commit is contained in:
StrikerX3 2019-12-08 20:27:35 -03:00
parent cf0deb7d47
commit 5e623da2e9
43 changed files with 455 additions and 234 deletions

View file

@ -11,14 +11,14 @@
#include <cstdint>
#include <string>
#include "state.h"
namespace strikebox::nv2a {
class NV2A;
// Abstract base class of all NV2A engines.
class NV2AEngine {
public:
NV2AEngine(const std::string name, const uint32_t offset, const uint32_t length, const NV2A& nv2a)
NV2AEngine(const std::string name, const uint32_t offset, const uint32_t length, NV2A& nv2a)
: m_name(name)
, m_offset(offset)
, m_length(length)
@ -27,8 +27,11 @@ public:
{}
virtual void Reset() = 0;
virtual uint32_t Read(const uint32_t addr, const uint8_t size) = 0;
virtual void Write(const uint32_t addr, const uint32_t value, const uint8_t size) = 0;
virtual uint32_t Read(const uint32_t addr) = 0;
virtual void Write(const uint32_t addr, const uint32_t value) = 0;
virtual uint32_t ReadUnaligned(const uint32_t addr, const uint8_t size);
virtual void WriteUnaligned(const uint32_t addr, const uint32_t value, const uint8_t size);
const std::string GetName() const noexcept { return m_name; }
const uint32_t GetOffset() const noexcept { return m_offset; }
@ -36,12 +39,14 @@ public:
const bool Contains(uint32_t address) const noexcept { return address >= m_offset && address < m_offsetEnd; }
protected:
NV2A& m_nv2a;
private:
const std::string m_name;
const uint32_t m_offset;
const uint32_t m_length;
const uint32_t m_offsetEnd; // m_offset + m_length, precomputed for speed
const NV2A& m_nv2a;
};
}

View file

@ -36,22 +36,3 @@
#include "state.h"
#include "engine.h"
#include "pmc.h"
#include "pbus.h"
#include "pfifo.h"
#include "prma.h"
#include "pvideo.h"
#include "ptimer.h"
#include "pcounter.h"
#include "pnvio.h"
#include "pfb.h"
#include "pstraps.h"
#include "prom.h"
#include "pgraph.h"
#include "pcrtc.h"
#include "prmcio.h"
#include "pramdac.h"
#include "prmdio.h"
#include "pramin.h"
#include "user.h"

View file

@ -22,11 +22,13 @@ namespace strikebox::nv2a {
// NV2A bus control engine (PBUS)
class PBUS : public NV2AEngine {
public:
PBUS(const NV2A& nv2a) : NV2AEngine("PBUS", 0x001000, 0x1000, nv2a) {}
PBUS(NV2A& nv2a) : NV2AEngine("PBUS", 0x001000, 0x1000, nv2a) {
Reset();
}
void Reset() override;
uint32_t Read(const uint32_t addr, const uint8_t size) override;
void Write(const uint32_t addr, const uint32_t value, const uint8_t size) override;
uint32_t Read(const uint32_t addr) override;
void Write(const uint32_t addr, const uint32_t value) override;
};
}

View file

@ -24,11 +24,13 @@ namespace strikebox::nv2a {
// NV2A performance monitoring counters engine (PCOUNTER)
class PCOUNTER : public NV2AEngine {
public:
PCOUNTER(const NV2A& nv2a) : NV2AEngine("PCOUNTER", 0x00A000, 0x1000, nv2a) {}
PCOUNTER(NV2A& nv2a) : NV2AEngine("PCOUNTER", 0x00A000, 0x1000, nv2a) {
Reset();
}
void Reset() override;
uint32_t Read(const uint32_t addr, const uint8_t size) override;
void Write(const uint32_t addr, const uint32_t value, const uint8_t size) override;
uint32_t Read(const uint32_t addr) override;
void Write(const uint32_t addr, const uint32_t value) override;
};
}

View file

@ -17,11 +17,13 @@ namespace strikebox::nv2a {
// NV2A CRTC controls engine (PCRTC)
class PCRTC : public NV2AEngine {
public:
PCRTC(const NV2A& nv2a) : NV2AEngine("PCRTC", 0x600000, 0x1000, nv2a) {}
PCRTC(NV2A& nv2a) : NV2AEngine("PCRTC", 0x600000, 0x1000, nv2a) {
Reset();
}
void Reset() override;
uint32_t Read(const uint32_t addr, const uint8_t size) override;
void Write(const uint32_t addr, const uint32_t value, const uint8_t size) override;
uint32_t Read(const uint32_t addr) override;
void Write(const uint32_t addr, const uint32_t value) override;
};
}

View file

@ -17,11 +17,13 @@ namespace strikebox::nv2a {
// NV2A memory interface engine (PFB)
class PFB : public NV2AEngine {
public:
PFB(const NV2A& nv2a) : NV2AEngine("PFB", 0x100000, 0x1000, nv2a) {}
PFB(NV2A& nv2a) : NV2AEngine("PFB", 0x100000, 0x1000, nv2a) {
Reset();
}
void Reset() override;
uint32_t Read(const uint32_t addr, const uint8_t size) override;
void Write(const uint32_t addr, const uint32_t value, const uint8_t size) override;
uint32_t Read(const uint32_t addr) override;
void Write(const uint32_t addr, const uint32_t value) override;
};
}

View file

@ -23,11 +23,13 @@ namespace strikebox::nv2a {
// NV2A MMIO and DMA FIFO submission to PGRAPH engine (PFIFO)
class PFIFO : public NV2AEngine {
public:
PFIFO(const NV2A& nv2a) : NV2AEngine("PFIFO", 0x002000, 0x2000, nv2a) {}
PFIFO(NV2A& nv2a) : NV2AEngine("PFIFO", 0x002000, 0x2000, nv2a) {
Reset();
}
void Reset() override;
uint32_t Read(const uint32_t addr, const uint8_t size) override;
void Write(const uint32_t addr, const uint32_t value, const uint8_t size) override;
uint32_t Read(const uint32_t addr) override;
void Write(const uint32_t addr, const uint32_t value) override;
};
}

View file

@ -19,11 +19,13 @@ namespace strikebox::nv2a {
// NV2A 2D/3D graphics engine (PGRAPH)
class PGRAPH : public NV2AEngine {
public:
PGRAPH(const NV2A& nv2a) : NV2AEngine("PGRAPH", 0x400000, 0x2000, nv2a) {}
PGRAPH(NV2A& nv2a) : NV2AEngine("PGRAPH", 0x400000, 0x2000, nv2a) {
Reset();
}
void Reset() override;
uint32_t Read(const uint32_t addr, const uint8_t size) override;
void Write(const uint32_t addr, const uint32_t value, const uint8_t size) override;
uint32_t Read(const uint32_t addr) override;
void Write(const uint32_t addr, const uint32_t value) override;
};
}

View file

@ -21,11 +21,13 @@ namespace strikebox::nv2a {
// NV2A master control engine (PMC)
class PMC : public NV2AEngine {
public:
PMC(const NV2A& nv2a) : NV2AEngine("PMC", 0x000000, 0x1000, nv2a) {}
PMC(NV2A& nv2a) : NV2AEngine("PMC", 0x000000, 0x1000, nv2a) {
Reset();
}
void Reset() override;
uint32_t Read(const uint32_t addr, const uint8_t size) override;
void Write(const uint32_t addr, const uint32_t value, const uint8_t size) override;
uint32_t Read(const uint32_t addr) override;
void Write(const uint32_t addr, const uint32_t value) override;
};
}

View file

@ -17,11 +17,13 @@ namespace strikebox::nv2a {
// NV2A VGA sequencer and graph controller registers (PNVIO)
class PNVIO : public NV2AEngine {
public:
PNVIO(const NV2A& nv2a) : NV2AEngine("PNVIO", 0x0C0000, 0x1000, nv2a) {}
PNVIO(NV2A& nv2a) : NV2AEngine("PNVIO", 0x0C0000, 0x1000, nv2a) {
Reset();
}
void Reset() override;
uint32_t Read(const uint32_t addr, const uint8_t size) override;
void Write(const uint32_t addr, const uint32_t value, const uint8_t size) override;
uint32_t Read(const uint32_t addr) override;
void Write(const uint32_t addr, const uint32_t value) override;
};
}

View file

@ -17,11 +17,20 @@ namespace strikebox::nv2a {
// NV2A RAMDAC, video overlay, cursor, and PLL control engine (PRAMDAC)
class PRAMDAC : public NV2AEngine {
public:
PRAMDAC(const NV2A& nv2a) : NV2AEngine("PRAMDAC", 0x680000, 0x1000, nv2a) {}
PRAMDAC(NV2A& nv2a) : NV2AEngine("PRAMDAC", 0x680000, 0x1000, nv2a) {
Reset();
}
void Reset() override;
uint32_t Read(const uint32_t addr, const uint8_t size) override;
void Write(const uint32_t addr, const uint32_t value, const uint8_t size) override;
uint32_t Read(const uint32_t addr) override;
void Write(const uint32_t addr, const uint32_t value) override;
private:
uint32_t m_coreClockCoeff;
uint32_t m_memoryClockCoeff;
uint32_t m_videoClockCoeff;
uint32_t m_mem[0x1000 / 4]; // for all other reads/writes
};
}

View file

@ -19,11 +19,13 @@ namespace strikebox::nv2a {
// NV2A RAMIN access engine (PRAMIN)
class PRAMIN : public NV2AEngine {
public:
PRAMIN(const NV2A& nv2a) : NV2AEngine("PRAMIN", 0x700000, 0x100000, nv2a) {}
PRAMIN(NV2A& nv2a) : NV2AEngine("PRAMIN", 0x700000, 0x100000, nv2a) {
Reset();
}
void Reset() override;
uint32_t Read(const uint32_t addr, const uint8_t size) override;
void Write(const uint32_t addr, const uint32_t value, const uint8_t size) override;
uint32_t Read(const uint32_t addr) override;
void Write(const uint32_t addr, const uint32_t value) override;
};
}

View file

@ -19,11 +19,13 @@ namespace strikebox::nv2a {
// NV2A real mode BAR access (PRMA)
class PRMA : public NV2AEngine {
public:
PRMA(const NV2A& nv2a) : NV2AEngine("PRMA", 0x007000, 0x1000, nv2a) {}
PRMA(NV2A& nv2a) : NV2AEngine("PRMA", 0x007000, 0x1000, nv2a) {
Reset();
}
void Reset() override;
uint32_t Read(const uint32_t addr, const uint8_t size) override;
void Write(const uint32_t addr, const uint32_t value, const uint8_t size) override;
uint32_t Read(const uint32_t addr) override;
void Write(const uint32_t addr, const uint32_t value) override;
};
}

View file

@ -17,11 +17,13 @@ namespace strikebox::nv2a {
// NV2A VGA CRTC and attribute controller registers engine (PRMCIO)
class PRMCIO : public NV2AEngine {
public:
PRMCIO(const NV2A& nv2a) : NV2AEngine("PRMCIO", 0x601000, 0x1000, nv2a) {}
PRMCIO(NV2A& nv2a) : NV2AEngine("PRMCIO", 0x601000, 0x1000, nv2a) {
Reset();
}
void Reset() override;
uint32_t Read(const uint32_t addr, const uint8_t size) override;
void Write(const uint32_t addr, const uint32_t value, const uint8_t size) override;
uint32_t Read(const uint32_t addr) override;
void Write(const uint32_t addr, const uint32_t value) override;
};
}

View file

@ -17,11 +17,13 @@ namespace strikebox::nv2a {
// NV2A VGA DAC registers engine (PRMDIO)
class PRMDIO : public NV2AEngine {
public:
PRMDIO(const NV2A& nv2a) : NV2AEngine("PRMDIO", 0x681000, 0x1000, nv2a) {}
PRMDIO(NV2A& nv2a) : NV2AEngine("PRMDIO", 0x681000, 0x1000, nv2a) {
Reset();
}
void Reset() override;
uint32_t Read(const uint32_t addr, const uint8_t size) override;
void Write(const uint32_t addr, const uint32_t value, const uint8_t size) override;
uint32_t Read(const uint32_t addr) override;
void Write(const uint32_t addr, const uint32_t value) override;
};
}

View file

@ -17,11 +17,13 @@ namespace strikebox::nv2a {
// NV2A ROM access window engine (PROM)
class PROM : public NV2AEngine {
public:
PROM(const NV2A& nv2a) : NV2AEngine("PROM", 0x300000, 0x20000, nv2a) {}
PROM(NV2A& nv2a) : NV2AEngine("PROM", 0x300000, 0x20000, nv2a) {
Reset();
}
void Reset() override;
uint32_t Read(const uint32_t addr, const uint8_t size) override;
void Write(const uint32_t addr, const uint32_t value, const uint8_t size) override;
uint32_t Read(const uint32_t addr) override;
void Write(const uint32_t addr, const uint32_t value) override;
};
}

View file

@ -27,11 +27,13 @@ namespace strikebox::nv2a {
// NV2A straps readout engine (PSTRAPS)
class PSTRAPS : public NV2AEngine {
public:
PSTRAPS(const NV2A& nv2a) : NV2AEngine("PSTRAPS", 0x101000, 0x1000, nv2a) {}
PSTRAPS(NV2A& nv2a) : NV2AEngine("PSTRAPS", 0x101000, 0x1000, nv2a) {
Reset();
}
void Reset() override;
uint32_t Read(const uint32_t addr, const uint8_t size) override;
void Write(const uint32_t addr, const uint32_t value, const uint8_t size) override;
uint32_t Read(const uint32_t addr) override;
void Write(const uint32_t addr, const uint32_t value) override;
};
}

View file

@ -24,11 +24,13 @@ namespace strikebox::nv2a {
// NV2A time measurement and time-based alarms (PTIMER)
class PTIMER : public NV2AEngine {
public:
PTIMER(const NV2A& nv2a) : NV2AEngine("PTIMER", 0x009000, 0x1000, nv2a) {}
PTIMER(NV2A& nv2a) : NV2AEngine("PTIMER", 0x009000, 0x1000, nv2a) {
Reset();
}
void Reset() override;
uint32_t Read(const uint32_t addr, const uint8_t size) override;
void Write(const uint32_t addr, const uint32_t value, const uint8_t size) override;
uint32_t Read(const uint32_t addr) override;
void Write(const uint32_t addr, const uint32_t value) override;
};
}

View file

@ -17,11 +17,13 @@ namespace strikebox::nv2a {
// NV2A video overlay engine (PVIDEO)
class PVIDEO : public NV2AEngine {
public:
PVIDEO(const NV2A& nv2a) : NV2AEngine("PVIDEO", 0x008000, 0x1000, nv2a) {}
PVIDEO(NV2A& nv2a) : NV2AEngine("PVIDEO", 0x008000, 0x1000, nv2a) {
Reset();
}
void Reset() override;
uint32_t Read(const uint32_t addr, const uint8_t size) override;
void Write(const uint32_t addr, const uint32_t value, const uint8_t size) override;
uint32_t Read(const uint32_t addr) override;
void Write(const uint32_t addr, const uint32_t value) override;
};
}

View file

@ -8,11 +8,77 @@
// brackets optionally followed by a quote from the documentation.
#pragma once
#include <string>
#include <map>
#include <optional>
#include <functional>
#include "engine.h"
#include "pmc.h"
#include "pbus.h"
#include "pfifo.h"
#include "prma.h"
#include "pvideo.h"
#include "ptimer.h"
#include "pcounter.h"
#include "pnvio.h"
#include "pfb.h"
#include "pstraps.h"
#include "prom.h"
#include "pgraph.h"
#include "pcrtc.h"
#include "prmcio.h"
#include "pramdac.h"
#include "prmdio.h"
#include "pramin.h"
#include "user.h"
namespace strikebox::nv2a {
using PCIConfigReader = std::function<uint32_t(uint8_t address)>;
using PCIConfigWriter = std::function<void(uint8_t address, uint32_t value)>;
// Represents the state of the NV2A GPU.
class NV2A {
public:
NV2A(uint8_t* systemRAM, uint32_t systemRAMSize, PCIConfigReader pciCfgReader, PCIConfigWriter pciCfgWriter);
std::unique_ptr<PMC> pmc = std::make_unique<PMC>(*this);
std::unique_ptr<PBUS> pbus = std::make_unique<PBUS>(*this);
std::unique_ptr<PFIFO> pfifo = std::make_unique<PFIFO>(*this);
std::unique_ptr<PRMA> prma = std::make_unique<PRMA>(*this);
std::unique_ptr<PVIDEO> pvideo = std::make_unique<PVIDEO>(*this);
std::unique_ptr<PTIMER> ptimer = std::make_unique<PTIMER>(*this);
std::unique_ptr<PCOUNTER> pcounter = std::make_unique<PCOUNTER>(*this);
std::unique_ptr<PNVIO> pnvio = std::make_unique<PNVIO>(*this);
std::unique_ptr<PFB> pfb = std::make_unique<PFB>(*this);
std::unique_ptr<PSTRAPS> pstraps = std::make_unique<PSTRAPS>(*this);
std::unique_ptr<PROM> prom = std::make_unique<PROM>(*this);
std::unique_ptr<PGRAPH> pgraph = std::make_unique<PGRAPH>(*this);
std::unique_ptr<PCRTC> pcrtc = std::make_unique<PCRTC>(*this);
std::unique_ptr<PRMCIO> prmcio = std::make_unique<PRMCIO>(*this);
std::unique_ptr<PRAMDAC> pramdac = std::make_unique<PRAMDAC>(*this);
std::unique_ptr<PRMDIO> prmdio = std::make_unique<PRMDIO>(*this);
std::unique_ptr<PRAMIN> pramin = std::make_unique<PRAMIN>(*this);
std::unique_ptr<USER> user = std::make_unique<USER>(*this);
uint8_t* systemRAM;
const uint32_t systemRAMSize;
void Reset();
uint32_t Read(const uint32_t addr, const uint8_t size);
void Write(const uint32_t addr, const uint32_t value, const uint8_t size);
// PCI config space read/write access
const PCIConfigReader pciCfgReader;
const PCIConfigWriter pciCfgWriter;
private:
// Fast engine lookup
std::map<uint32_t, nv2a::NV2AEngine&> engines;
void RegisterEngine(nv2a::NV2AEngine& engine);
std::optional<std::reference_wrapper<nv2a::NV2AEngine>> FindEngine(const uint32_t address);
};
}

View file

@ -20,11 +20,13 @@ namespace strikebox::nv2a {
// NV2A PFIFO MMIO/DMA submission area (USER)
class USER : public NV2AEngine {
public:
USER(const NV2A& nv2a) : NV2AEngine("USER", 0x800000, 0x200000, nv2a) {}
USER(NV2A& nv2a) : NV2AEngine("USER", 0x800000, 0x200000, nv2a) {
Reset();
}
void Reset() override;
uint32_t Read(const uint32_t addr, const uint8_t size) override;
void Write(const uint32_t addr, const uint32_t value, const uint8_t size) override;
uint32_t Read(const uint32_t addr) override;
void Write(const uint32_t addr, const uint32_t value) override;
};
}

View file

@ -1,8 +1,6 @@
#pragma once
#include <string>
#include <map>
#include <optional>
#include <memory>
#include "pci.h"
#include "../basic/irq.h"
@ -25,35 +23,10 @@ public:
void PCIMMIOWrite(int barIndex, uint32_t addr, uint32_t value, uint8_t size) override;
private:
uint8_t *m_pSystemRAM;
uint32_t m_systemRAMSize;
IRQHandler& m_irqHandler;
// NV2A state and engines
nv2a::NV2A m_nv2a;
nv2a::PMC m_pmc { m_nv2a };
nv2a::PBUS m_pbus { m_nv2a };
nv2a::PFIFO m_pfifo { m_nv2a };
nv2a::PRMA m_prma { m_nv2a };
nv2a::PVIDEO m_pvideo { m_nv2a };
nv2a::PTIMER m_ptimer { m_nv2a };
nv2a::PCOUNTER m_pcounter{ m_nv2a };
nv2a::PNVIO m_pnvio { m_nv2a };
nv2a::PFB m_pfb { m_nv2a };
nv2a::PSTRAPS m_pstraps { m_nv2a };
nv2a::PROM m_prom { m_nv2a };
nv2a::PGRAPH m_pgraph { m_nv2a };
nv2a::PCRTC m_pcrtc { m_nv2a };
nv2a::PRMCIO m_prmcio { m_nv2a };
nv2a::PRAMDAC m_pramdac { m_nv2a };
nv2a::PRMDIO m_prmdio { m_nv2a };
nv2a::PRAMIN m_pramin { m_nv2a };
nv2a::USER m_user { m_nv2a };
// Fast engine lookup
std::map<uint32_t, nv2a::NV2AEngine&> engines;
void RegisterEngine(nv2a::NV2AEngine& engine);
std::optional<std::reference_wrapper<nv2a::NV2AEngine>> FindEngine(const uint32_t address);
// NV2A state
std::unique_ptr<nv2a::NV2A> m_nv2a;
};
}

View file

@ -0,0 +1,45 @@
// StrikeBox NV2A PBUS (Bus control) engine emulation
// (C) Ivan "StrikerX3" Oliveira
//
// Based on envytools:
// https://envytools.readthedocs.io/en/latest/index.html
//
// References to particular items in the documentation are denoted between
// brackets optionally followed by a quote from the documentation.
#include "strikebox/hw/gpu/state.h"
#include "strikebox/log.h"
namespace strikebox::nv2a {
uint32_t NV2AEngine::ReadUnaligned(const uint32_t addr, const uint8_t size) {
// Unaligned read fits in one register
if ((addr & ~3) == ((addr + size - 1) & ~3)) {
log_warning("NV2AEngine::Read: Unaligned read! address = 0x%x, size = %u\n", addr, size);
uint8_t offset = (addr & 3) * 8;
uint32_t mask = 0xFFFFFFFF >> (32 - size * 8);
return (Read(addr & ~3) >> offset) & mask;
}
// Unaligned read spans two registers, corresponding to one of these cases:
// - 16-bit read from offset 3
// - 32-bit read from offsets 1, 2 or 3
// FIXME: how does the real hardware behave in this case?
log_warning("NV2AEngine::Read: Severely unaligned read! address = 0x%x, size = %u\n", addr, size);
uint32_t value1 = Read(addr & ~3);
uint32_t value2 = Read((addr & ~3) + 4);
uint8_t offset = (addr & 3) * 8;
uint32_t mask = 0xFFFFFFFF >> (32 - size * 8);
return ((value1 >> offset) | (value2 << (32 - offset))) & mask;
}
void NV2AEngine::WriteUnaligned(const uint32_t addr, const uint32_t value, const uint8_t size) {
log_warning("NV2AEngine::Write: Unaligned write! address = 0x%x, size = %u\n", addr, size);
// FIXME: how does the real hardware behave in this case?
// This is almost certainly wrong.
Write(addr & ~3, value);
}
}

View file

@ -7,6 +7,7 @@
// References to particular items in the documentation are denoted between
// brackets optionally followed by a quote from the documentation.
#include "strikebox/hw/gpu/pbus.h"
#include "strikebox/hw/gpu/state.h"
#include "strikebox/log.h"
@ -15,13 +16,24 @@ namespace strikebox::nv2a {
void PBUS::Reset() {
}
uint32_t PBUS::Read(const uint32_t addr, const uint8_t size) {
log_spew("[NV2A] PBUS::Read: Unimplemented read! address = 0x%x, size = %u\n", addr, size);
uint32_t PBUS::Read(const uint32_t addr) {
if (addr >= 0x800 && addr <= 0x8FF) {
// PCI Configuration Space access
return m_nv2a.pciCfgReader(addr - 0x800);
}
log_spew("[NV2A] PBUS::Read: Unimplemented read! address = 0x%x\n", addr);
return 0;
}
void PBUS::Write(const uint32_t addr, const uint32_t value, const uint8_t size) {
log_spew("[NV2A] PBUS::Write: Unimplemented write! address = 0x%x, value = 0x%x, size = %u\n", addr, value, size);
void PBUS::Write(const uint32_t addr, const uint32_t value) {
if (addr >= 0x800 && addr <= 0x8FF) {
// PCI Configuration Space access
m_nv2a.pciCfgWriter(addr - 0x800, value);
}
else {
log_spew("[NV2A] PBUS::Write: Unimplemented write! address = 0x%x, value = 0x%x\n", addr, value);
}
}
}

View file

@ -15,13 +15,13 @@ namespace strikebox::nv2a {
void PCOUNTER::Reset() {
}
uint32_t PCOUNTER::Read(const uint32_t addr, const uint8_t size) {
log_spew("[NV2A] PCOUNTER::Read: Unimplemented read! address = 0x%x, size = %u\n", addr, size);
uint32_t PCOUNTER::Read(const uint32_t addr) {
log_spew("[NV2A] PCOUNTER::Read: Unimplemented read! address = 0x%x\n", addr);
return 0;
}
void PCOUNTER::Write(const uint32_t addr, const uint32_t value, const uint8_t size) {
log_spew("[NV2A] PCOUNTER::Write: Unimplemented write! address = 0x%x, value = 0x%x, size = %u\n", addr, value, size);
void PCOUNTER::Write(const uint32_t addr, const uint32_t value) {
log_spew("[NV2A] PCOUNTER::Write: Unimplemented write! address = 0x%x, value = 0x%x\n", addr, value);
}
}

View file

@ -15,13 +15,13 @@ namespace strikebox::nv2a {
void PCRTC::Reset() {
}
uint32_t PCRTC::Read(const uint32_t addr, const uint8_t size) {
log_spew("[NV2A] PCRTC::Read: Unimplemented read! address = 0x%x, size = %u\n", addr, size);
uint32_t PCRTC::Read(const uint32_t addr) {
log_spew("[NV2A] PCRTC::Read: Unimplemented read! address = 0x%x\n", addr);
return 0;
}
void PCRTC::Write(const uint32_t addr, const uint32_t value, const uint8_t size) {
log_spew("[NV2A] PCRTC::Write: Unimplemented write! address = 0x%x, value = 0x%x, size = %u\n", addr, value, size);
void PCRTC::Write(const uint32_t addr, const uint32_t value) {
log_spew("[NV2A] PCRTC::Write: Unimplemented write! address = 0x%x, value = 0x%x\n", addr, value);
}
}

View file

@ -7,21 +7,32 @@
// References to particular items in the documentation are denoted between
// brackets optionally followed by a quote from the documentation.
#include "strikebox/hw/gpu/pfb.h"
#include "strikebox/hw/gpu/state.h"
#include "strikebox/log.h"
namespace strikebox::nv2a {
static const uint32_t Reg_CFG0 = 0x200;
static const uint32_t Reg_CSTATUS = 0x20C; // Framebuffer size
static const uint32_t Reg_WBC = 0x410; // Write-back cache?
void PFB::Reset() {
}
uint32_t PFB::Read(const uint32_t addr, const uint8_t size) {
log_spew("[NV2A] PFB::Read: Unimplemented read! address = 0x%x, size = %u\n", addr, size);
return 0;
uint32_t PFB::Read(const uint32_t addr) {
switch (addr) {
case Reg_CFG0: return 3; // The kernel asserts this value to be 3 early during initialization
case Reg_CSTATUS: return m_nv2a.systemRAMSize;
case Reg_WBC: return 0;
default:
log_spew("[NV2A] PFB::Read: Unimplemented read! address = 0x%x\n", addr);
return 0;
}
}
void PFB::Write(const uint32_t addr, const uint32_t value, const uint8_t size) {
log_spew("[NV2A] PFB::Write: Unimplemented write! address = 0x%x, value = 0x%x, size = %u\n", addr, value, size);
void PFB::Write(const uint32_t addr, const uint32_t value) {
log_spew("[NV2A] PFB::Write: Unimplemented write! address = 0x%x, value = 0x%x\n", addr, value);
}
}

View file

@ -15,13 +15,13 @@ namespace strikebox::nv2a {
void PFIFO::Reset() {
}
uint32_t PFIFO::Read(const uint32_t addr, const uint8_t size) {
log_spew("[NV2A] PFIFO::Read: Unimplemented read! address = 0x%x, size = %u\n", addr, size);
uint32_t PFIFO::Read(const uint32_t addr) {
log_spew("[NV2A] PFIFO::Read: Unimplemented read! address = 0x%x\n", addr);
return 0;
}
void PFIFO::Write(const uint32_t addr, const uint32_t value, const uint8_t size) {
log_spew("[NV2A] PFIFO::Write: Unimplemented write! address = 0x%x, value = 0x%x, size = %u\n", addr, value, size);
void PFIFO::Write(const uint32_t addr, const uint32_t value) {
log_spew("[NV2A] PFIFO::Write: Unimplemented write! address = 0x%x, value = 0x%x\n", addr, value);
}
}

View file

@ -15,13 +15,13 @@ namespace strikebox::nv2a {
void PGRAPH::Reset() {
}
uint32_t PGRAPH::Read(const uint32_t addr, const uint8_t size) {
log_spew("[NV2A] PGRAPH::Read: Unimplemented read! address = 0x%x, size = %u\n", addr, size);
uint32_t PGRAPH::Read(const uint32_t addr) {
log_spew("[NV2A] PGRAPH::Read: Unimplemented read! address = 0x%x\n", addr);
return 0;
}
void PGRAPH::Write(const uint32_t addr, const uint32_t value, const uint8_t size) {
log_spew("[NV2A] PGRAPH::Write: Unimplemented write! address = 0x%x, value = 0x%x, size = %u\n", addr, value, size);
void PGRAPH::Write(const uint32_t addr, const uint32_t value) {
log_spew("[NV2A] PGRAPH::Write: Unimplemented write! address = 0x%x, value = 0x%x\n", addr, value);
}
}

View file

@ -15,13 +15,13 @@ namespace strikebox::nv2a {
void PMC::Reset() {
}
uint32_t PMC::Read(const uint32_t addr, const uint8_t size) {
log_spew("[NV2A] PMC::Read: Unimplemented read! address = 0x%x, size = %u\n", addr, size);
uint32_t PMC::Read(const uint32_t addr) {
log_spew("[NV2A] PMC::Read: Unimplemented read! address = 0x%x\n", addr);
return 0;
}
void PMC::Write(const uint32_t addr, const uint32_t value, const uint8_t size) {
log_spew("[NV2A] PMC::Write: Unimplemented write! address = 0x%x, value = 0x%x, size = %u\n", addr, value, size);
void PMC::Write(const uint32_t addr, const uint32_t value) {
log_spew("[NV2A] PMC::Write: Unimplemented write! address = 0x%x, value = 0x%x\n", addr, value);
}
}

View file

@ -15,13 +15,13 @@ namespace strikebox::nv2a {
void PNVIO::Reset() {
}
uint32_t PNVIO::Read(const uint32_t addr, const uint8_t size) {
log_spew("[NV2A] PNVIO::Read: Unimplemented read! address = 0x%x, size = %u\n", addr, size);
uint32_t PNVIO::Read(const uint32_t addr) {
log_spew("[NV2A] PNVIO::Read: Unimplemented read! address = 0x%x\n", addr);
return 0;
}
void PNVIO::Write(const uint32_t addr, const uint32_t value, const uint8_t size) {
log_spew("[NV2A] PNVIO::Write: Unimplemented write! address = 0x%x, value = 0x%x, size = %u\n", addr, value, size);
void PNVIO::Write(const uint32_t addr, const uint32_t value) {
log_spew("[NV2A] PNVIO::Write: Unimplemented write! address = 0x%x, value = 0x%x\n", addr, value);
}
}

View file

@ -12,16 +12,57 @@
namespace strikebox::nv2a {
static const uint32_t Reg_NVPLL = 0x500; // Core PLL clock
static const uint32_t Reg_MPLL = 0x504; // Memory PLL clock
static const uint32_t Reg_VPLL = 0x508; // Video PLL clocks
// [https://envytools.readthedocs.io/en/latest/hw/display/nv3/pramdac.html]
// "The bit layout for all NV4 PLLs is that bits 18-16 are P, bits 15-8 are N, and bits 7-0 are M."
union ClockCoefficients {
struct {
uint8_t M;
uint8_t N;
uint8_t P : 3;
};
uint32_t u32;
};
void PRAMDAC::Reset() {
// NV2A clocks:
// crystal = 16.6 MHz
// core = 233 MHz
// memory = 200 MHz
// video = 25.160 MHz
//
// The clocks are calculated as such: (Crystal frequency * N) / (1 << P) / M.
m_coreClockCoeff = ClockCoefficients{ 1, 28, 1 }.u32;
m_memoryClockCoeff = ClockCoefficients{ 1, 24, 1 }.u32;
m_videoClockCoeff = ClockCoefficients{ 3, 157, 13 }.u32;
std::fill(std::begin(m_mem), std::end(m_mem), 0);
}
uint32_t PRAMDAC::Read(const uint32_t addr, const uint8_t size) {
log_spew("[NV2A] PRAMDAC::Read: Unimplemented read! address = 0x%x, size = %u\n", addr, size);
return 0;
uint32_t PRAMDAC::Read(const uint32_t addr) {
switch (addr) {
case Reg_NVPLL: return m_coreClockCoeff;
case Reg_MPLL: return m_memoryClockCoeff;
case Reg_VPLL: return m_videoClockCoeff;
default:
//log_spew("[NV2A] PRAMDAC::Read: Unimplemented read! address = 0x%x\n", addr);
return m_mem[addr >> 2];
}
}
void PRAMDAC::Write(const uint32_t addr, const uint32_t value, const uint8_t size) {
log_spew("[NV2A] PRAMDAC::Write: Unimplemented write! address = 0x%x, value = 0x%x, size = %u\n", addr, value, size);
void PRAMDAC::Write(const uint32_t addr, const uint32_t value) {
switch (addr) {
case Reg_NVPLL: m_coreClockCoeff = value; break;
case Reg_MPLL: m_memoryClockCoeff = value; break;
case Reg_VPLL: m_videoClockCoeff = value; break;
default:
//log_spew("[NV2A] PRAMDAC::Write: Unimplemented write! address = 0x%x, value = 0x%x\n", addr, value);
m_mem[addr >> 2] = value;
break;
}
}
}

View file

@ -15,13 +15,13 @@ namespace strikebox::nv2a {
void PRAMIN::Reset() {
}
uint32_t PRAMIN::Read(const uint32_t addr, const uint8_t size) {
log_spew("[NV2A] PRAMIN::Read: Unimplemented read! address = 0x%x, size = %u\n", addr, size);
uint32_t PRAMIN::Read(const uint32_t addr) {
log_spew("[NV2A] PRAMIN::Read: Unimplemented read! address = 0x%x\n", addr);
return 0;
}
void PRAMIN::Write(const uint32_t addr, const uint32_t value, const uint8_t size) {
log_spew("[NV2A] PRAMIN::Write: Unimplemented write! address = 0x%x, value = 0x%x, size = %u\n", addr, value, size);
void PRAMIN::Write(const uint32_t addr, const uint32_t value) {
log_spew("[NV2A] PRAMIN::Write: Unimplemented write! address = 0x%x, value = 0x%x\n", addr, value);
}
}

View file

@ -15,13 +15,13 @@ namespace strikebox::nv2a {
void PRMA::Reset() {
}
uint32_t PRMA::Read(const uint32_t addr, const uint8_t size) {
log_spew("[NV2A] PRMA::Read: Unimplemented read! address = 0x%x, size = %u\n", addr, size);
uint32_t PRMA::Read(const uint32_t addr) {
log_spew("[NV2A] PRMA::Read: Unimplemented read! address = 0x%x\n", addr);
return 0;
}
void PRMA::Write(const uint32_t addr, const uint32_t value, const uint8_t size) {
log_spew("[NV2A] PRMA::Write: Unimplemented write! address = 0x%x, value = 0x%x, size = %u\n", addr, value, size);
void PRMA::Write(const uint32_t addr, const uint32_t value) {
log_spew("[NV2A] PRMA::Write: Unimplemented write! address = 0x%x, value = 0x%x\n", addr, value);
}
}

View file

@ -15,13 +15,13 @@ namespace strikebox::nv2a {
void PRMCIO::Reset() {
}
uint32_t PRMCIO::Read(const uint32_t addr, const uint8_t size) {
log_spew("[NV2A] PRMCIO::Read: Unimplemented read! address = 0x%x, size = %u\n", addr, size);
uint32_t PRMCIO::Read(const uint32_t addr) {
log_spew("[NV2A] PRMCIO::Read: Unimplemented read! address = 0x%x\n", addr);
return 0;
}
void PRMCIO::Write(const uint32_t addr, const uint32_t value, const uint8_t size) {
log_spew("[NV2A] PRMCIO::Write: Unimplemented write! address = 0x%x, value = 0x%x, size = %u\n", addr, value, size);
void PRMCIO::Write(const uint32_t addr, const uint32_t value) {
log_spew("[NV2A] PRMCIO::Write: Unimplemented write! address = 0x%x, value = 0x%x\n", addr, value);
}
}

View file

@ -15,13 +15,13 @@ namespace strikebox::nv2a {
void PRMDIO::Reset() {
}
uint32_t PRMDIO::Read(const uint32_t addr, const uint8_t size) {
log_spew("[NV2A] PRMDIO::Read: Unimplemented read! address = 0x%x, size = %u\n", addr, size);
uint32_t PRMDIO::Read(const uint32_t addr) {
log_spew("[NV2A] PRMDIO::Read: Unimplemented read! address = 0x%x\n", addr);
return 0;
}
void PRMDIO::Write(const uint32_t addr, const uint32_t value, const uint8_t size) {
log_spew("[NV2A] PRMDIO::Write: Unimplemented write! address = 0x%x, value = 0x%x, size = %u\n", addr, value, size);
void PRMDIO::Write(const uint32_t addr, const uint32_t value) {
log_spew("[NV2A] PRMDIO::Write: Unimplemented write! address = 0x%x, value = 0x%x\n", addr, value);
}
}

View file

@ -15,13 +15,13 @@ namespace strikebox::nv2a {
void PROM::Reset() {
}
uint32_t PROM::Read(const uint32_t addr, const uint8_t size) {
log_spew("[NV2A] PROM::Read: Unimplemented read! address = 0x%x, size = %u\n", addr, size);
uint32_t PROM::Read(const uint32_t addr) {
log_spew("[NV2A] PROM::Read: Unimplemented read! address = 0x%x\n", addr);
return 0;
}
void PROM::Write(const uint32_t addr, const uint32_t value, const uint8_t size) {
log_spew("[NV2A] PROM::Write: Unimplemented write! address = 0x%x, value = 0x%x, size = %u\n", addr, value, size);
void PROM::Write(const uint32_t addr, const uint32_t value) {
log_spew("[NV2A] PROM::Write: Unimplemented write! address = 0x%x, value = 0x%x\n", addr, value);
}
}

View file

@ -15,13 +15,13 @@ namespace strikebox::nv2a {
void PSTRAPS::Reset() {
}
uint32_t PSTRAPS::Read(const uint32_t addr, const uint8_t size) {
log_spew("[NV2A] PSTRAPS::Read: Unimplemented read! address = 0x%x, size = %u\n", addr, size);
uint32_t PSTRAPS::Read(const uint32_t addr) {
log_spew("[NV2A] PSTRAPS::Read: Unimplemented read! address = 0x%x\n", addr);
return 0;
}
void PSTRAPS::Write(const uint32_t addr, const uint32_t value, const uint8_t size) {
log_spew("[NV2A] PSTRAPS::Write: Unimplemented write! address = 0x%x, value = 0x%x, size = %u\n", addr, value, size);
void PSTRAPS::Write(const uint32_t addr, const uint32_t value) {
log_spew("[NV2A] PSTRAPS::Write: Unimplemented write! address = 0x%x, value = 0x%x\n", addr, value);
}
}

View file

@ -15,13 +15,13 @@ namespace strikebox::nv2a {
void PTIMER::Reset() {
}
uint32_t PTIMER::Read(const uint32_t addr, const uint8_t size) {
log_spew("[NV2A] PTIMER::Read: Unimplemented read! address = 0x%x, size = %u\n", addr, size);
uint32_t PTIMER::Read(const uint32_t addr) {
log_spew("[NV2A] PTIMER::Read: Unimplemented read! address = 0x%x\n", addr);
return 0;
}
void PTIMER::Write(const uint32_t addr, const uint32_t value, const uint8_t size) {
log_spew("[NV2A] PTIMER::Write: Unimplemented write! address = 0x%x, value = 0x%x, size = %u\n", addr, value, size);
void PTIMER::Write(const uint32_t addr, const uint32_t value) {
log_spew("[NV2A] PTIMER::Write: Unimplemented write! address = 0x%x, value = 0x%x\n", addr, value);
}
}

View file

@ -15,13 +15,13 @@ namespace strikebox::nv2a {
void PVIDEO::Reset() {
}
uint32_t PVIDEO::Read(const uint32_t addr, const uint8_t size) {
log_spew("[NV2A] PVIDEO::Read: Unimplemented read! address = 0x%x, size = %u\n", addr, size);
uint32_t PVIDEO::Read(const uint32_t addr) {
log_spew("[NV2A] PVIDEO::Read: Unimplemented read! address = 0x%x\n", addr);
return 0;
}
void PVIDEO::Write(const uint32_t addr, const uint32_t value, const uint8_t size) {
log_spew("[NV2A] PVIDEO::Write: Unimplemented write! address = 0x%x, value = 0x%x, size = %u\n", addr, value, size);
void PVIDEO::Write(const uint32_t addr, const uint32_t value) {
log_spew("[NV2A] PVIDEO::Write: Unimplemented write! address = 0x%x, value = 0x%x\n", addr, value);
}
}

View file

@ -0,0 +1,90 @@
#include "strikebox/hw/gpu/state.h"
#include "strikebox/log.h"
namespace strikebox::nv2a {
NV2A::NV2A(uint8_t* systemRAM, uint32_t systemRAMSize, PCIConfigReader pciCfgReader, PCIConfigWriter pciCfgWriter)
: systemRAM(systemRAM)
, systemRAMSize(systemRAMSize)
, pciCfgReader(pciCfgReader)
, pciCfgWriter(pciCfgWriter)
{
RegisterEngine(*pmc);
RegisterEngine(*pbus);
RegisterEngine(*pfifo);
RegisterEngine(*prma);
RegisterEngine(*pvideo);
RegisterEngine(*ptimer);
RegisterEngine(*pcounter);
RegisterEngine(*pnvio);
RegisterEngine(*pfb);
RegisterEngine(*pstraps);
RegisterEngine(*prom);
RegisterEngine(*pgraph);
RegisterEngine(*pcrtc);
RegisterEngine(*prmcio);
RegisterEngine(*pramdac);
RegisterEngine(*prmdio);
RegisterEngine(*pramin);
RegisterEngine(*user);
}
void NV2A::Reset() {
for (auto& eng : engines) {
eng.second.Reset();
}
}
uint32_t NV2A::Read(const uint32_t addr, const uint8_t size) {
auto opt_eng = FindEngine(addr);
if (opt_eng) {
auto& eng = opt_eng->get();
// Aligned 32-bit read as expected
if ((addr & 3) == 0 && size == 4) {
return eng.Read(addr - eng.GetOffset());
}
// Unaligned or non 32-bit read
log_warning("NV2A::Read: Unaligned read! address = 0x%x, size = %u\n", addr, size);
return eng.ReadUnaligned(addr - eng.GetOffset(), size);
}
log_spew("NV2A::Read: Unmapped read! address = 0x%x, size = %u\n", addr, size);
return 0;
}
void NV2A::Write(const uint32_t addr, const uint32_t value, const uint8_t size) {
auto opt_eng = FindEngine(addr);
if (opt_eng) {
auto& eng = opt_eng->get();
// Aligned 32-bit wrute as expected
if ((addr & 3) == 0 && size == 4) {
eng.Write(addr - eng.GetOffset(), value);
}
else {
// Unaligned or non 32-bit write
eng.WriteUnaligned(addr - eng.GetOffset(), value, size);
}
}
else {
log_spew("NV2A::Write: Unmapped write! address = 0x%x, value = 0x%x, size = %u\n", addr, value, size);
}
}
void NV2A::RegisterEngine(nv2a::NV2AEngine& engine) {
engines.insert({ engine.GetOffset() + engine.GetLength() - 1, engine });
}
std::optional<std::reference_wrapper<nv2a::NV2AEngine>> NV2A::FindEngine(const uint32_t address) {
auto entry = engines.lower_bound(address);
if (entry != engines.end()) {
auto& engine = entry->second;
if (engine.Contains(address)) {
return engine;
}
}
return std::nullopt;
}
}

View file

@ -15,13 +15,13 @@ namespace strikebox::nv2a {
void USER::Reset() {
}
uint32_t USER::Read(const uint32_t addr, const uint8_t size) {
log_spew("[NV2A] USER::Read: Unimplemented read! address = 0x%x, size = %u\n", addr, size);
uint32_t USER::Read(const uint32_t addr) {
log_spew("[NV2A] USER::Read: Unimplemented read! address = 0x%x\n", addr);
return 0;
}
void USER::Write(const uint32_t addr, const uint32_t value, const uint8_t size) {
log_spew("[NV2A] USER::Write: Unimplemented write! address = 0x%x, value = 0x%x, size = %u\n", addr, value, size);
void USER::Write(const uint32_t addr, const uint32_t value) {
log_spew("[NV2A] USER::Write: Unimplemented write! address = 0x%x, value = 0x%x\n", addr, value);
}
}

View file

@ -7,28 +7,11 @@ namespace strikebox {
NV2ADevice::NV2ADevice(uint8_t *pSystemRAM, uint32_t systemRAMSize, IRQHandler& irqHandler)
: PCIDevice(PCI_HEADER_TYPE_NORMAL, PCI_VENDOR_ID_NVIDIA, 0x02A0, 0xA1,
0x03, 0x00, 0x00) // VGA-compatible controller
, m_pSystemRAM(pSystemRAM)
, m_systemRAMSize(systemRAMSize)
, m_irqHandler(irqHandler)
{
RegisterEngine(m_pmc);
RegisterEngine(m_pbus);
RegisterEngine(m_pfifo);
RegisterEngine(m_prma);
RegisterEngine(m_pvideo);
RegisterEngine(m_ptimer);
RegisterEngine(m_pcounter);
RegisterEngine(m_pnvio);
RegisterEngine(m_pfb);
RegisterEngine(m_pstraps);
RegisterEngine(m_prom);
RegisterEngine(m_pgraph);
RegisterEngine(m_pcrtc);
RegisterEngine(m_prmcio);
RegisterEngine(m_pramdac);
RegisterEngine(m_prmdio);
RegisterEngine(m_pramin);
RegisterEngine(m_user);
nv2a::PCIConfigReader pciCfgReader = [&](uint8_t addr) -> uint32_t { return Read32(m_configSpace, addr); };
nv2a::PCIConfigWriter pciCfgWriter = [&](uint8_t addr, uint32_t value) { Write32(m_configSpace, addr, value); };
m_nv2a = std::make_unique<nv2a::NV2A>(pSystemRAM, systemRAMSize, pciCfgReader, pciCfgWriter);
}
NV2ADevice::~NV2ADevice() {
@ -83,14 +66,7 @@ void NV2ADevice::PCIIOWrite(int barIndex, uint32_t port, uint32_t value, uint8_t
void NV2ADevice::PCIMMIORead(int barIndex, uint32_t addr, uint32_t *value, uint8_t size) {
if (barIndex == 0) {
auto opt_eng = FindEngine(addr);
if (opt_eng) {
auto& eng = opt_eng->get();
*value = eng.Read(addr - eng.GetOffset(), size);
}
else {
log_spew("NV2ADevice::PCIMMIORead: Unmapped read! bar = %d, address = 0x%x, size = %u\n", barIndex, addr, size);
}
*value = m_nv2a->Read(addr, size);
}
else {
log_spew("NV2ADevice::PCIMMIORead: Unimplemented read! bar = %d, address = 0x%x, size = %u\n", barIndex, addr, size);
@ -100,33 +76,11 @@ void NV2ADevice::PCIMMIORead(int barIndex, uint32_t addr, uint32_t *value, uint8
void NV2ADevice::PCIMMIOWrite(int barIndex, uint32_t addr, uint32_t value, uint8_t size) {
if (barIndex == 0) {
auto opt_eng = FindEngine(addr);
if (opt_eng) {
auto& eng = opt_eng->get();
eng.Write(addr - eng.GetOffset(), value, size);
}
else {
log_spew("NV2ADevice::PCIMMIOWrite: Unmapped write! bar = %d, address = 0x%x, value = 0x%x, size = %u\n", barIndex, addr, value, size);
}
m_nv2a->Write(addr, value, size);
}
else {
log_spew("NV2ADevice::PCIMMIOWrite: Unimplemented write! bar = %d, address = 0x%x, value = 0x%x, size = %u\n", barIndex, addr, value, size);
}
}
void NV2ADevice::RegisterEngine(nv2a::NV2AEngine& engine) {
engines.insert({ engine.GetOffset() + engine.GetLength() - 1, engine });
}
std::optional<std::reference_wrapper<nv2a::NV2AEngine>> NV2ADevice::FindEngine(const uint32_t address) {
auto entry = engines.lower_bound(address);
if (entry != engines.end()) {
auto& engine = entry->second;
if (engine.Contains(address)) {
return engine;
}
}
return std::nullopt;
}
}