Testing backport of V2 components
This commit is contained in:
parent
6f0bed74d2
commit
4aa5e318f0
7 changed files with 661 additions and 94 deletions
|
|
@ -12,7 +12,7 @@ set(CMAKE_C_COMPILER "clang")
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
cmake_minimum_required(VERSION 3.18)
|
cmake_minimum_required(VERSION 3.18)
|
||||||
project(${PROJ_NAME} LANGUAGES C CXX)
|
project(${PROJ_NAME} LANGUAGES C CXX)
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
file(STRINGS "./other/build.nr" BUILD_NUMBER)
|
file(STRINGS "./other/build.nr" BUILD_NUMBER)
|
||||||
#-----------------------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
1646
|
1648
|
||||||
|
|
|
||||||
143
src/hardware/BUS.cpp
Normal file
143
src/hardware/BUS.cpp
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
/*
|
||||||
|
DragonV2 - A collection of useful functionality
|
||||||
|
Copyright (C) 2026 OmniaX-Dev
|
||||||
|
|
||||||
|
This file is part of DragonV2.
|
||||||
|
|
||||||
|
DragonV2 is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
DragonV2 is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with DragonV2. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "BUS.hpp"
|
||||||
|
|
||||||
|
namespace dragon
|
||||||
|
{
|
||||||
|
AtomicStorageMemoryDevice::AtomicStorageMemoryDevice(u64 size)
|
||||||
|
{
|
||||||
|
if (size == 0) // Special case, for classes like BUS, that are MemoryDevices but don't own any data
|
||||||
|
{
|
||||||
|
m_wordCount = 0;
|
||||||
|
m_size = 0;
|
||||||
|
m_words = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_wordCount = (size + 7) / 8; // Round up to multiple of 8 bytes for the word-granular backing
|
||||||
|
m_size = size;
|
||||||
|
m_words = new std::atomic<u64>[m_wordCount];
|
||||||
|
for (u64 i = 0; i < m_wordCount; ++i)
|
||||||
|
m_words[i].store(0, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
AtomicStorageMemoryDevice::~AtomicStorageMemoryDevice(void)
|
||||||
|
{
|
||||||
|
delete[] m_words;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MemoryDevice::guest_cas_u32(AddressType addr, u32 expected, u32& actual, u32 desired)
|
||||||
|
{
|
||||||
|
data::ErrorHandler::pushError(data::ErrorCodes::MM_AtomicNotSupported, "MemoryDevice::guest_cas_u32: atomic not supported: ");
|
||||||
|
fault(addr, data::ExceptionCause::AtomicNotSupported, "MemoryDevice::guest_cas_u32");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace hw
|
||||||
|
{
|
||||||
|
bool BUS::guest_cas_u32(AddressType addr, u32 expected, u32& actual, u32 desired)
|
||||||
|
{
|
||||||
|
auto region = findRegion(addr);
|
||||||
|
if (!region) { /* fault */ return false; }
|
||||||
|
AddressType finalAddr = (region->remap ? addr - region->startAddress : addr);
|
||||||
|
return region->device->guest_cas_u32(finalAddr, expected, actual, desired);
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 BUS::load_u8(AddressType addr) const
|
||||||
|
{
|
||||||
|
auto region = findRegion(addr);
|
||||||
|
if (region == nullptr) return 0x00; //TODO: Error
|
||||||
|
AddressType finalAddr = (region->remap ? addr - region->startAddress : addr);
|
||||||
|
return region->device->load_u8(finalAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
u16 BUS::load_u16(AddressType addr) const
|
||||||
|
{
|
||||||
|
auto region = findRegion(addr);
|
||||||
|
if (region == nullptr) return 0x0000; //TODO: Error
|
||||||
|
AddressType finalAddr = (region->remap ? addr - region->startAddress : addr);
|
||||||
|
return region->device->load_u16(finalAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 BUS::load_u32(AddressType addr) const
|
||||||
|
{
|
||||||
|
auto region = findRegion(addr);
|
||||||
|
if (region == nullptr) return 0x0000'0000; //TODO: Error
|
||||||
|
AddressType finalAddr = (region->remap ? addr - region->startAddress : addr);
|
||||||
|
return region->device->load_u32(finalAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 BUS::load_u64(AddressType addr) const
|
||||||
|
{
|
||||||
|
auto region = findRegion(addr);
|
||||||
|
if (region == nullptr) return 0x0000'0000'0000'0000; //TODO: Error
|
||||||
|
AddressType finalAddr = (region->remap ? addr - region->startAddress : addr);
|
||||||
|
return region->device->load_u64(finalAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BUS::store_u8(AddressType addr, u8 value)
|
||||||
|
{
|
||||||
|
auto region = findRegion(addr);
|
||||||
|
if (region == nullptr) return false;
|
||||||
|
AddressType finalAddr = (region->remap ? addr - region->startAddress : addr);
|
||||||
|
return region->device->store_u8(finalAddr, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BUS::store_u16(AddressType addr, u16 value)
|
||||||
|
{
|
||||||
|
auto region = findRegion(addr);
|
||||||
|
if (region == nullptr) return false;
|
||||||
|
AddressType finalAddr = (region->remap ? addr - region->startAddress : addr);
|
||||||
|
return region->device->store_u16(finalAddr, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BUS::store_u32(AddressType addr, u32 value)
|
||||||
|
{
|
||||||
|
auto region = findRegion(addr);
|
||||||
|
if (region == nullptr) return false;
|
||||||
|
AddressType finalAddr = (region->remap ? addr - region->startAddress : addr);
|
||||||
|
return region->device->store_u32(finalAddr, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BUS::store_u64(AddressType addr, u64 value)
|
||||||
|
{
|
||||||
|
auto region = findRegion(addr);
|
||||||
|
if (region == nullptr) return false;
|
||||||
|
AddressType finalAddr = (region->remap ? addr - region->startAddress : addr);
|
||||||
|
return region->device->store_u64(finalAddr, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BUS::mapDevice(MemoryDevice& device, AddressType startAddr, AddressType endAddr, bool remap, ostd::String name)
|
||||||
|
{
|
||||||
|
m_regions.push_back({ &device, startAddr, endAddr, remap, name });
|
||||||
|
}
|
||||||
|
|
||||||
|
ostd::String BUS::getMemoryRegionName(AddressType address)
|
||||||
|
{
|
||||||
|
tMemoryRegion* region = findRegion(address);
|
||||||
|
if (region == nullptr) return "Invalid";
|
||||||
|
return region->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
142
src/hardware/BUS.hpp
Normal file
142
src/hardware/BUS.hpp
Normal file
|
|
@ -0,0 +1,142 @@
|
||||||
|
/*
|
||||||
|
DragonV2 - A collection of useful functionality
|
||||||
|
Copyright (C) 2026 OmniaX-Dev
|
||||||
|
|
||||||
|
This file is part of DragonV2.
|
||||||
|
|
||||||
|
DragonV2 is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
DragonV2 is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with DragonV2. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ostd/data/BaseObject.hpp>
|
||||||
|
#include <atomic>
|
||||||
|
#include <span>
|
||||||
|
#include "../tools/GlobalData.hpp"
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace dragon
|
||||||
|
{
|
||||||
|
class MemoryDevice : public ostd::BaseObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MemoryDevice(void) = default;
|
||||||
|
MemoryDevice(const MemoryDevice&) = delete;
|
||||||
|
MemoryDevice& operator=(const MemoryDevice&) = delete;
|
||||||
|
MemoryDevice(MemoryDevice&&) = delete;
|
||||||
|
MemoryDevice& operator=(MemoryDevice&&) = delete;
|
||||||
|
virtual ~MemoryDevice() = default;
|
||||||
|
|
||||||
|
virtual bool guest_cas_u32(AddressType addr, u32 expected, u32& actual, u32 desired);
|
||||||
|
|
||||||
|
// ---- Aligned loads ----
|
||||||
|
virtual u64 load_u64(AddressType addr) const = 0;
|
||||||
|
virtual u32 load_u32(AddressType addr) const = 0;
|
||||||
|
virtual u16 load_u16(AddressType addr) const = 0;
|
||||||
|
virtual u8 load_u8 (AddressType addr) const = 0;
|
||||||
|
|
||||||
|
inline i64 load_i64(AddressType addr) const { return cast<i64>(load_u64(addr)); }
|
||||||
|
inline i32 load_i32(AddressType addr) const { return cast<i32>(load_u32(addr)); }
|
||||||
|
inline i16 load_i16(AddressType addr) const { return cast<i16>(load_u16(addr)); }
|
||||||
|
inline i8 load_i8 (AddressType addr) const { return cast<i8 >(load_u8 (addr)); }
|
||||||
|
|
||||||
|
// ---- Aligned stores ----
|
||||||
|
virtual bool store_u64(AddressType addr, u64 value) = 0;
|
||||||
|
virtual bool store_u32(AddressType addr, u32 value) = 0;
|
||||||
|
virtual bool store_u16(AddressType addr, u16 value) = 0;
|
||||||
|
virtual bool store_u8 (AddressType addr, u8 value) = 0;
|
||||||
|
|
||||||
|
inline bool store_i64(AddressType addr, u64 value) { return store_u64(addr, cast<i64>(value)); }
|
||||||
|
inline bool store_i32(AddressType addr, u32 value) { return store_u32(addr, cast<i32>(value)); }
|
||||||
|
inline bool store_i16(AddressType addr, u16 value) { return store_u16(addr, cast<i16>(value)); }
|
||||||
|
inline bool store_i8 (AddressType addr, u8 value) { return store_u8 (addr, cast<i8 >(value)); }
|
||||||
|
|
||||||
|
[[noreturn]] virtual void fault(AddressType addr, data::ExceptionCause cause, const String& msg = "") const { throw data::GuestException{ cause, addr, msg }; }; // Message can be useful for debugging
|
||||||
|
};
|
||||||
|
|
||||||
|
class AtomicStorageMemoryDevice : public MemoryDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit AtomicStorageMemoryDevice(u64 size);
|
||||||
|
virtual ~AtomicStorageMemoryDevice(void);
|
||||||
|
AtomicStorageMemoryDevice(const AtomicStorageMemoryDevice&) = delete;
|
||||||
|
AtomicStorageMemoryDevice& operator=(const AtomicStorageMemoryDevice&) = delete;
|
||||||
|
AtomicStorageMemoryDevice(AtomicStorageMemoryDevice&&) = delete;
|
||||||
|
AtomicStorageMemoryDevice& operator=(AtomicStorageMemoryDevice&&) = delete;
|
||||||
|
|
||||||
|
// ---- Helpers ----
|
||||||
|
inline u64 getWordCount(void) const { return m_wordCount; }
|
||||||
|
inline u64 getSize(void) const { return m_size; }
|
||||||
|
inline virtual std::span<std::atomic<u64>> getRawData(void) { return { m_words, m_size }; }
|
||||||
|
inline virtual std::span<const std::atomic<u64>> getRawData(void) const { return { m_words, m_size }; }
|
||||||
|
// Both getRawData() can be overridden to return empty spans for devices where internal memory shouldn't be exposed
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::atomic<u64>* m_words;
|
||||||
|
u64 m_wordCount;
|
||||||
|
u64 m_size;
|
||||||
|
};
|
||||||
|
namespace hw
|
||||||
|
{
|
||||||
|
class BUS : public MemoryDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
private: struct tMemoryRegion
|
||||||
|
{
|
||||||
|
MemoryDevice* device { nullptr };
|
||||||
|
AddressType startAddress { 0x0000'0000 };
|
||||||
|
AddressType endAddress { 0x0000'0000 };
|
||||||
|
bool remap { false };
|
||||||
|
ostd::String name { "" };
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
BUS(void) = default;
|
||||||
|
|
||||||
|
bool guest_cas_u32(AddressType addr, u32 expected, u32& actual, u32 desired) override;
|
||||||
|
|
||||||
|
// ---- Aligned loads ----
|
||||||
|
u64 load_u64(AddressType addr) const override;
|
||||||
|
u32 load_u32(AddressType addr) const override;
|
||||||
|
u16 load_u16(AddressType addr) const override;
|
||||||
|
u8 load_u8 (AddressType addr) const override;
|
||||||
|
|
||||||
|
// ---- Aligned stores ----
|
||||||
|
bool store_u64(AddressType addr, u64 value) override;
|
||||||
|
bool store_u32(AddressType addr, u32 value) override;
|
||||||
|
bool store_u16(AddressType addr, u16 value) override;
|
||||||
|
bool store_u8 (AddressType addr, u8 value) override;
|
||||||
|
|
||||||
|
void mapDevice(MemoryDevice& device, AddressType startAddr, AddressType endAddr, bool remap = false, ostd::String name = "");
|
||||||
|
ostd::String getMemoryRegionName(AddressType address);
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename Self>
|
||||||
|
auto findRegion(this Self&& self, AddressType address)
|
||||||
|
{
|
||||||
|
for (auto& region : self.m_regions)
|
||||||
|
{ // works whether self is const or not
|
||||||
|
if (address >= region.startAddress && address <= region.endAddress)
|
||||||
|
return ®ion;
|
||||||
|
}
|
||||||
|
data::ErrorHandler::pushError(data::ErrorCodes::MM_RegionNotFound, "Memory device not found");
|
||||||
|
return decltype(&self.m_regions[0]){nullptr};
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<tMemoryRegion> m_regions;
|
||||||
|
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
172
src/hardware/RAM.cpp
Normal file
172
src/hardware/RAM.cpp
Normal file
|
|
@ -0,0 +1,172 @@
|
||||||
|
/*
|
||||||
|
DragonV2 - A collection of useful functionality
|
||||||
|
Copyright (C) 2026 OmniaX-Dev
|
||||||
|
|
||||||
|
This file is part of DragonV2.
|
||||||
|
|
||||||
|
DragonV2 is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
DragonV2 is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with DragonV2. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "RAM.hpp"
|
||||||
|
#include <ostd/string/String.hpp>
|
||||||
|
|
||||||
|
#define DRAGON_TEST_FOR_MISALIGNED_OR_BOUNDS_FAULT_R(size, msg) \
|
||||||
|
if (addr + size > m_size) \
|
||||||
|
{ \
|
||||||
|
fault(addr, data::ExceptionCause::LoadBounds, msg); \
|
||||||
|
return 0; \
|
||||||
|
} \
|
||||||
|
if (addr & (size - 1)) \
|
||||||
|
{ \
|
||||||
|
fault(addr, data::ExceptionCause::MisalignedLoad, msg); \
|
||||||
|
return 0; \
|
||||||
|
} \
|
||||||
|
|
||||||
|
#define DRAGON_TEST_FOR_MISALIGNED_OR_BOUNDS_FAULT_W(size, msg) \
|
||||||
|
if (addr + size > m_size) \
|
||||||
|
{ \
|
||||||
|
fault(addr, data::ExceptionCause::StoreBounds, msg); \
|
||||||
|
return false; \
|
||||||
|
} \
|
||||||
|
if (addr & (size - 1)) \
|
||||||
|
{ \
|
||||||
|
fault(addr, data::ExceptionCause::MisalignedStore, msg); \
|
||||||
|
return false; \
|
||||||
|
} \
|
||||||
|
|
||||||
|
namespace dragon
|
||||||
|
{
|
||||||
|
namespace hw
|
||||||
|
{
|
||||||
|
bool RAM::guest_cas_u32(AddressType addr, u32 expected, u32& actual, u32 desired)
|
||||||
|
{
|
||||||
|
DRAGON_TEST_FOR_MISALIGNED_OR_BOUNDS_FAULT_R(4, "RAM::guest_cas_u32");
|
||||||
|
|
||||||
|
u32 shift = (addr & 4) * 8;
|
||||||
|
u64 mask = u64(0xFFFFFFFFu) << shift;
|
||||||
|
std::atomic<u64>& word = m_words[addr >> 3];
|
||||||
|
|
||||||
|
u64 word_expected = word.load(std::memory_order_seq_cst);
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
u32 current_in_slot = static_cast<u32>(word_expected >> shift);
|
||||||
|
if (current_in_slot != expected)
|
||||||
|
{
|
||||||
|
// CAS fails: report what we saw, don't modify
|
||||||
|
actual = current_in_slot;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// CAS could succeed: try to update
|
||||||
|
u64 word_desired = (word_expected & ~mask) | (u64(desired) << shift);
|
||||||
|
if (word.compare_exchange_weak(word_expected, word_desired, std::memory_order_seq_cst, std::memory_order_seq_cst))
|
||||||
|
{
|
||||||
|
actual = expected; // tell caller we succeeded
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// CAS spuriously failed or someone else changed the word; word_expected
|
||||||
|
// is updated. Loop and re-check.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 RAM::load_u64(AddressType addr) const
|
||||||
|
{
|
||||||
|
DRAGON_TEST_FOR_MISALIGNED_OR_BOUNDS_FAULT_R(8, "RAM::load_u64");
|
||||||
|
return m_words[addr >> 3].load(std::memory_order_seq_cst);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 RAM::load_u32(AddressType addr) const
|
||||||
|
{
|
||||||
|
DRAGON_TEST_FOR_MISALIGNED_OR_BOUNDS_FAULT_R(4, "RAM::load_u32");
|
||||||
|
u64 word = m_words[addr >> 3].load(std::memory_order_seq_cst);
|
||||||
|
u32 shift = (addr & 4) * 8; // 0 or 32
|
||||||
|
return static_cast<u32>(word >> shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
u16 RAM::load_u16(AddressType addr) const
|
||||||
|
{
|
||||||
|
DRAGON_TEST_FOR_MISALIGNED_OR_BOUNDS_FAULT_R(2, "RAM::load_u16");
|
||||||
|
u64 word = m_words[addr >> 3].load(std::memory_order_seq_cst);
|
||||||
|
u32 shift = (addr & 6) * 8; // 0, 16, 32, or 48
|
||||||
|
return static_cast<u16>(word >> shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 RAM::load_u8(AddressType addr) const
|
||||||
|
{
|
||||||
|
if (addr >= m_size)
|
||||||
|
{
|
||||||
|
fault(addr, data::ExceptionCause::LoadBounds, "RAM::load_u8");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
u64 word = m_words[addr >> 3].load(std::memory_order_seq_cst);
|
||||||
|
u32 shift = (addr & 7) * 8;
|
||||||
|
return static_cast<u8>(word >> shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RAM::store_u64(AddressType addr, u64 value)
|
||||||
|
{
|
||||||
|
DRAGON_TEST_FOR_MISALIGNED_OR_BOUNDS_FAULT_W(8, "RAM::store_u64");
|
||||||
|
m_words[addr >> 3].store(value, std::memory_order_seq_cst);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RAM::store_u32(AddressType addr, u32 value)
|
||||||
|
{
|
||||||
|
DRAGON_TEST_FOR_MISALIGNED_OR_BOUNDS_FAULT_W(4, "RAM::store_u32");
|
||||||
|
u32 shift = (addr & 4) * 8;
|
||||||
|
u64 mask = u64(0xFFFFFFFFu) << shift;
|
||||||
|
std::atomic<u64>& word = m_words[addr >> 3];
|
||||||
|
u64 expected = word.load(std::memory_order_relaxed);
|
||||||
|
u64 desired;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
desired = (expected & ~mask) | (u64(value) << shift);
|
||||||
|
} while (!word.compare_exchange_weak(expected, desired, std::memory_order_seq_cst, std::memory_order_relaxed));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RAM::store_u16(AddressType addr, u16 value)
|
||||||
|
{
|
||||||
|
DRAGON_TEST_FOR_MISALIGNED_OR_BOUNDS_FAULT_W(2, "RAM::store_u16");
|
||||||
|
u32 shift = (addr & 6) * 8;
|
||||||
|
u64 mask = u64(0xFFFFu) << shift;
|
||||||
|
std::atomic<u64>& word = m_words[addr >> 3];
|
||||||
|
u64 expected = word.load(std::memory_order_relaxed);
|
||||||
|
u64 desired;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
desired = (expected & ~mask) | (u64(value) << shift);
|
||||||
|
} while (!word.compare_exchange_weak(expected, desired, std::memory_order_seq_cst, std::memory_order_relaxed));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RAM::store_u8(AddressType addr, u8 value)
|
||||||
|
{
|
||||||
|
if (addr >= m_size)
|
||||||
|
{
|
||||||
|
fault(addr, data::ExceptionCause::StoreBounds, "RAM::store_u8");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
u32 shift = (addr & 7) * 8;
|
||||||
|
u64 mask = u64(0xFFu) << shift;
|
||||||
|
std::atomic<u64>& word = m_words[addr >> 3];
|
||||||
|
u64 expected = word.load(std::memory_order_relaxed);
|
||||||
|
u64 desired;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
desired = (expected & ~mask) | (u64(value) << shift);
|
||||||
|
} while (!word.compare_exchange_weak(expected, desired, std::memory_order_seq_cst, std::memory_order_relaxed));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
51
src/hardware/RAM.hpp
Normal file
51
src/hardware/RAM.hpp
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
DragonV2 - A collection of useful functionality
|
||||||
|
Copyright (C) 2026 OmniaX-Dev
|
||||||
|
|
||||||
|
This file is part of DragonV2.
|
||||||
|
|
||||||
|
DragonV2 is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
DragonV2 is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with DragonV2. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ostd/data/Types.hpp>
|
||||||
|
|
||||||
|
#include "BUS.hpp"
|
||||||
|
|
||||||
|
namespace dragon
|
||||||
|
{
|
||||||
|
namespace hw
|
||||||
|
{
|
||||||
|
class RAM : public AtomicStorageMemoryDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit RAM(u64 size) : AtomicStorageMemoryDevice(size) { }
|
||||||
|
|
||||||
|
bool guest_cas_u32(AddressType addr, u32 expected, u32& actual, u32 desired) override;
|
||||||
|
|
||||||
|
// ---- Aligned loads ----
|
||||||
|
u64 load_u64(AddressType addr) const override;
|
||||||
|
u32 load_u32(AddressType addr) const override;
|
||||||
|
u16 load_u16(AddressType addr) const override;
|
||||||
|
u8 load_u8 (AddressType addr) const override;
|
||||||
|
|
||||||
|
// ---- Aligned stores ----
|
||||||
|
bool store_u64(AddressType addr, u64 value) override;
|
||||||
|
bool store_u32(AddressType addr, u32 value) override;
|
||||||
|
bool store_u16(AddressType addr, u16 value) override;
|
||||||
|
bool store_u8 (AddressType addr, u8 value) override;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
namespace dragon
|
namespace dragon
|
||||||
{
|
{
|
||||||
|
using AddressType = u16;
|
||||||
namespace hw { class VirtualCPU; }
|
namespace hw { class VirtualCPU; }
|
||||||
namespace data
|
namespace data
|
||||||
{
|
{
|
||||||
|
|
@ -13,51 +14,109 @@ namespace dragon
|
||||||
class ErrorCodes
|
class ErrorCodes
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
inline static constexpr uint64_t NoError = 0x0000000000000000;
|
inline static constexpr uint64_t NoError = 0x0000000000000000;
|
||||||
inline static constexpr uint64_t AccessViolation_BiosModeRequired = 0x0000000000000001;
|
inline static constexpr uint64_t AccessViolation_BiosModeRequired = 0x0000000000000001;
|
||||||
|
|
||||||
inline static constexpr uint64_t MM_RegionNotFound = 0x1000000000000000;
|
inline static constexpr uint64_t MM_RegionNotFound = 0x1000000000000000;
|
||||||
|
inline static constexpr uint64_t MM_AtomicNotSupported = 0x1000000000000001;
|
||||||
|
|
||||||
inline static constexpr uint64_t CPU_UnknownInstruction = 0x2000000000000000;
|
inline static constexpr uint64_t CPU_UnknownInstruction = 0x2000000000000000;
|
||||||
inline static constexpr uint64_t CPU_UnsupportedExtension = 0x2000000000000001;
|
inline static constexpr uint64_t CPU_UnsupportedExtension = 0x2000000000000001;
|
||||||
inline static constexpr uint64_t CPU_StackOverflow = 0x2000000000000002;
|
inline static constexpr uint64_t CPU_StackOverflow = 0x2000000000000002;
|
||||||
|
|
||||||
inline static constexpr uint64_t BIOS_FailedToLoad = 0x3000000000000000;
|
inline static constexpr uint64_t BIOS_FailedToLoad = 0x3000000000000000;
|
||||||
inline static constexpr uint64_t BIOS_InvalidSize = 0x3000000000000001;
|
inline static constexpr uint64_t BIOS_InvalidSize = 0x3000000000000001;
|
||||||
inline static constexpr uint64_t BIOS_WriteAttempt = 0x3000000000000002;
|
inline static constexpr uint64_t BIOS_WriteAttempt = 0x3000000000000002;
|
||||||
inline static constexpr uint64_t BIOS_InvalidAddress = 0x3000000000000003;
|
inline static constexpr uint64_t BIOS_InvalidAddress = 0x3000000000000003;
|
||||||
|
|
||||||
inline static constexpr uint64_t HardDrive_UnableToMount = 0x4000000000000000;
|
inline static constexpr uint64_t HardDrive_UnableToMount = 0x4000000000000000;
|
||||||
inline static constexpr uint64_t HardDrive_Uninitialized = 0x4000000000000001;
|
inline static constexpr uint64_t HardDrive_Uninitialized = 0x4000000000000001;
|
||||||
inline static constexpr uint64_t HardDrive_ReadOverflow = 0x4000000000000002;
|
inline static constexpr uint64_t HardDrive_ReadOverflow = 0x4000000000000002;
|
||||||
inline static constexpr uint64_t HardDrive_WriteOverflow = 0x4000000000000003;
|
inline static constexpr uint64_t HardDrive_WriteOverflow = 0x4000000000000003;
|
||||||
inline static constexpr uint64_t HardDrive_BuffWriteOverflow = 0x4000000000000004;
|
inline static constexpr uint64_t HardDrive_BuffWriteOverflow = 0x4000000000000004;
|
||||||
inline static constexpr uint64_t HardDrive_EmptyBuffer = 0x4000000000000005;
|
inline static constexpr uint64_t HardDrive_EmptyBuffer = 0x4000000000000005;
|
||||||
inline static constexpr uint64_t HardDrive_InvalidConfiguration = 0x4000000000000006;
|
inline static constexpr uint64_t HardDrive_InvalidConfiguration = 0x4000000000000006;
|
||||||
inline static constexpr uint64_t HardDrive_ReadFailed = 0x4000000000000007;
|
inline static constexpr uint64_t HardDrive_ReadFailed = 0x4000000000000007;
|
||||||
inline static constexpr uint64_t HardDrive_WriteFailed = 0x4000000000000008;
|
inline static constexpr uint64_t HardDrive_WriteFailed = 0x4000000000000008;
|
||||||
inline static constexpr uint64_t HardDrive_MemoryOverflow = 0x4000000000000009;
|
inline static constexpr uint64_t HardDrive_MemoryOverflow = 0x4000000000000009;
|
||||||
inline static constexpr uint64_t HardDrive_InvalidDiskSelected = 0x400000000000000A;
|
inline static constexpr uint64_t HardDrive_InvalidDiskSelected = 0x400000000000000A;
|
||||||
inline static constexpr uint64_t HardDrive_EndOfDisk = 0x400000000000000B;
|
inline static constexpr uint64_t HardDrive_EndOfDisk = 0x400000000000000B;
|
||||||
inline static constexpr uint64_t HardDrive_ControllerReadFailed = 0x400000000000000C;
|
inline static constexpr uint64_t HardDrive_ControllerReadFailed = 0x400000000000000C;
|
||||||
inline static constexpr uint64_t HardDrive_ControllerWriteFailed = 0x400000000000000D;
|
inline static constexpr uint64_t HardDrive_ControllerWriteFailed = 0x400000000000000D;
|
||||||
inline static constexpr uint64_t HardDrive_DisconnectInvalid = 0x400000000000000E;
|
inline static constexpr uint64_t HardDrive_DisconnectInvalid = 0x400000000000000E;
|
||||||
inline static constexpr uint64_t HardDrive_DiskAlreadyConnected = 0x400000000000000F;
|
inline static constexpr uint64_t HardDrive_DiskAlreadyConnected = 0x400000000000000F;
|
||||||
|
|
||||||
inline static constexpr uint64_t CMOS_InvalidAddress = 0x5000000000000000;
|
inline static constexpr uint64_t CMOS_InvalidAddress = 0x5000000000000000;
|
||||||
inline static constexpr uint64_t CMOS_UnableToMount = 0x5000000000000001;
|
inline static constexpr uint64_t CMOS_UnableToMount = 0x5000000000000001;
|
||||||
inline static constexpr uint64_t CMOS_InvalidSize = 0x5000000000000002;
|
inline static constexpr uint64_t CMOS_InvalidSize = 0x5000000000000002;
|
||||||
inline static constexpr uint64_t CMOS_Uninitialized = 0x5000000000000003;
|
inline static constexpr uint64_t CMOS_Uninitialized = 0x5000000000000003;
|
||||||
|
|
||||||
inline static constexpr uint64_t BIOSVideo_InvalidAddress = 0x6000000000000000;
|
inline static constexpr uint64_t BIOSVideo_InvalidAddress = 0x6000000000000000;
|
||||||
|
|
||||||
inline static constexpr uint64_t IntVector_InvalidAddress = 0x7000000000000000;
|
inline static constexpr uint64_t IntVector_InvalidAddress = 0x7000000000000000;
|
||||||
|
|
||||||
inline static constexpr uint64_t Graphics_MemoryReadFailed = 0x8000000000000000;
|
inline static constexpr uint64_t Graphics_MemoryReadFailed = 0x8000000000000000;
|
||||||
inline static constexpr uint64_t Graphics_MemoryWriteFailed = 0x8000000000000001;
|
inline static constexpr uint64_t Graphics_MemoryWriteFailed = 0x8000000000000001;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class ExceptionCause : u32
|
||||||
|
{
|
||||||
|
// === Instruction fetch faults ===
|
||||||
|
MisalignedFetch = 0x0000'0000, // PC not aligned to instruction boundary
|
||||||
|
InstructionBounds = 0x0000'0001, // PC outside any mapped region
|
||||||
|
InstructionPageFault = 0x0000'0002, // virt page not mapped (MMU on) or no X permission
|
||||||
|
InstructionPrivPageFault = 0x0000'0003, // privileged virt page not mapped (MMU on) or no X permission
|
||||||
|
UnknownInstruction = 0x0000'0004, // decoded opcode is not valid
|
||||||
|
|
||||||
|
// === Privilege ===
|
||||||
|
PrivilegeViolation = 0x0000'1000, // user mode executed a [PRIV] instruction
|
||||||
|
|
||||||
|
// === Memory access faults (loads) ===
|
||||||
|
MisalignedLoad = 0x0000'2000, // load address not naturally aligned
|
||||||
|
LoadBounds = 0x0000'2001, // load address outside mapped region
|
||||||
|
LoadPageFault = 0x0000'2002, // virt page not mapped or no R permission
|
||||||
|
LoadPrivPageFault = 0x0000'2003, // virt page not mapped or no R permission
|
||||||
|
|
||||||
|
// === Memory access faults (stores) ===
|
||||||
|
MisalignedStore = 0x0000'3000,
|
||||||
|
StoreBounds = 0x0000'3001,
|
||||||
|
StorePageFault = 0x0000'3002, // virt page not mapped or no W permission
|
||||||
|
StorePrivPageFault = 0x0000'3003, // virt page not mapped or no W permission
|
||||||
|
|
||||||
|
// === Atomic op faults ===
|
||||||
|
AtomicNotSupported = 0x0000'4000, // CAS/XADD/XCHG on a device that can't (ROM, MMIO)
|
||||||
|
|
||||||
|
// === Arithmetic ===
|
||||||
|
DivideByZero = 0x0000'5000,
|
||||||
|
|
||||||
|
// === MMIO ===
|
||||||
|
BusUnmapped = 0x0000'6000,
|
||||||
|
ReadOnlyViolation = 0x0000'6001,
|
||||||
|
|
||||||
|
// === Deliberate guest-initiated traps ===
|
||||||
|
Syscall = 0x0000'7000, // syscall instruction
|
||||||
|
SoftwareInt = 0x0000'7001, // int N instruction
|
||||||
|
TrapInstruction = 0x0000'7002, // explicit trap instr
|
||||||
|
Breakpoint = 0x0000'7003, // brk for debugger
|
||||||
|
|
||||||
|
// === Asynchronous (external) ===
|
||||||
|
HardwareInterrupt = 0x0000'F000, // generic external IRQ; details in INT controller
|
||||||
|
|
||||||
|
// === Catastrophic ===
|
||||||
|
DoubleFault = 0x000F'FFFF, // exception while handling exception
|
||||||
|
|
||||||
|
// Just in case
|
||||||
|
NoFault = 0xFFFF'FFFF,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GuestException
|
||||||
|
{
|
||||||
|
ExceptionCause cause;
|
||||||
|
AddressType fault_addr; // VA for translation faults, PA for bus faults, 0 otherwise
|
||||||
|
String msg; // For debug
|
||||||
|
};
|
||||||
|
|
||||||
class ErrorHandler
|
class ErrorHandler
|
||||||
{
|
{
|
||||||
public: struct tError
|
public: struct tError
|
||||||
|
|
@ -127,22 +186,22 @@ namespace dragon
|
||||||
public:
|
public:
|
||||||
inline BiosVideoDefaultPalette(void)
|
inline BiosVideoDefaultPalette(void)
|
||||||
{
|
{
|
||||||
m_colors.push_back({ 0, 0, 0 }); // Black
|
m_colors.push_back({ 0, 0, 0 }); // Black
|
||||||
m_colors.push_back({ 157, 157, 157 }); // Gray
|
m_colors.push_back({ 157, 157, 157 }); // Gray
|
||||||
m_colors.push_back({ 255, 255, 255 }); // White
|
m_colors.push_back({ 255, 255, 255 }); // White
|
||||||
m_colors.push_back({ 190, 38, 51 }); // Red
|
m_colors.push_back({ 190, 38, 51 }); // Red
|
||||||
m_colors.push_back({ 224, 111, 139 }); // Pink
|
m_colors.push_back({ 224, 111, 139 }); // Pink
|
||||||
m_colors.push_back({ 73, 60, 43 }); // Brown
|
m_colors.push_back({ 73, 60, 43 }); // Brown
|
||||||
m_colors.push_back({ 164, 100, 34 }); // Dark Orange
|
m_colors.push_back({ 164, 100, 34 }); // Dark Orange
|
||||||
m_colors.push_back({ 235, 137, 49 }); // Orange
|
m_colors.push_back({ 235, 137, 49 }); // Orange
|
||||||
m_colors.push_back({ 247, 226, 107 }); // Yellow
|
m_colors.push_back({ 247, 226, 107 }); // Yellow
|
||||||
m_colors.push_back({ 47, 80, 42 }); // Dark Green
|
m_colors.push_back({ 47, 80, 42 }); // Dark Green
|
||||||
m_colors.push_back({ 68, 137, 26 }); // Green
|
m_colors.push_back({ 68, 137, 26 }); // Green
|
||||||
m_colors.push_back({ 163, 206, 39 }); // Lime
|
m_colors.push_back({ 163, 206, 39 }); // Lime
|
||||||
m_colors.push_back({ 27, 38, 50 }); // Dark Blue
|
m_colors.push_back({ 27, 38, 50 }); // Dark Blue
|
||||||
m_colors.push_back({ 0, 87, 132 }); // Blue
|
m_colors.push_back({ 0, 87, 132 }); // Blue
|
||||||
m_colors.push_back({ 49, 162, 242 }); // Light Blue
|
m_colors.push_back({ 49, 162, 242 }); // Light Blue
|
||||||
m_colors.push_back({ 178, 220, 239 }); // Sky
|
m_colors.push_back({ 178, 220, 239 }); // Sky
|
||||||
}
|
}
|
||||||
|
|
||||||
inline ostd::Color getColor(uint8_t col) override
|
inline ostd::Color getColor(uint8_t col) override
|
||||||
|
|
@ -203,12 +262,12 @@ namespace dragon
|
||||||
{
|
{
|
||||||
switch (code)
|
switch (code)
|
||||||
{
|
{
|
||||||
case DiskInterfaceFFinished: return "Disk_Interface_Finished";
|
case DiskInterfaceFFinished: return "Disk_Interface_Finished";
|
||||||
case KeyPressed: return "Key_Pressed";
|
case KeyPressed: return "Key_Pressed";
|
||||||
case KeyReleased: return "Key_Released";
|
case KeyReleased: return "Key_Released";
|
||||||
case TextEntered: return "Text_Entered";
|
case TextEntered: return "Text_Entered";
|
||||||
case Text16ModeScreenRefreshed: return "Text16_Mode_Screen_Refreshed";
|
case Text16ModeScreenRefreshed: return "Text16_Mode_Screen_Refreshed";
|
||||||
default: return "UNKNOWN";
|
default: return "UNKNOWN";
|
||||||
}
|
}
|
||||||
return "UNKNOWN";
|
return "UNKNOWN";
|
||||||
}
|
}
|
||||||
|
|
@ -217,59 +276,59 @@ namespace dragon
|
||||||
class CMOSRegisters
|
class CMOSRegisters
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
inline static constexpr uint8_t MemoryStart = 0x00;
|
inline static constexpr uint8_t MemoryStart = 0x00;
|
||||||
inline static constexpr uint8_t MemorySize = 0x02;
|
inline static constexpr uint8_t MemorySize = 0x02;
|
||||||
inline static constexpr uint8_t ClockSpeed = 0x04;
|
inline static constexpr uint8_t ClockSpeed = 0x04;
|
||||||
inline static constexpr uint8_t ScreenRedrawRate = 0x06;
|
inline static constexpr uint8_t ScreenRedrawRate = 0x06;
|
||||||
inline static constexpr uint8_t ScreenWidth = 0x07;
|
inline static constexpr uint8_t ScreenWidth = 0x07;
|
||||||
inline static constexpr uint8_t ScreenHeight = 0x09;
|
inline static constexpr uint8_t ScreenHeight = 0x09;
|
||||||
inline static constexpr uint8_t BootDisk = 0x10;
|
inline static constexpr uint8_t BootDisk = 0x10;
|
||||||
inline static constexpr uint8_t StackSize = 0x11;
|
inline static constexpr uint8_t StackSize = 0x11;
|
||||||
|
|
||||||
inline static constexpr uint8_t DiskList = 0x7E;
|
inline static constexpr uint8_t DiskList = 0x7E;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DPTStructure
|
class DPTStructure
|
||||||
{
|
{
|
||||||
public: struct tFlags {
|
public: struct tFlags {
|
||||||
inline static constexpr uint8_t Boot = 0;
|
inline static constexpr uint8_t Boot = 0;
|
||||||
};
|
};
|
||||||
public:
|
public:
|
||||||
|
|
||||||
inline static constexpr uint16_t DPTID = 0x000;
|
inline static constexpr uint16_t DPTID = 0x000;
|
||||||
inline static constexpr uint16_t DPTVersionMaj = 0x002;
|
inline static constexpr uint16_t DPTVersionMaj = 0x002;
|
||||||
inline static constexpr uint16_t DPTVersionMin = 0x003;
|
inline static constexpr uint16_t DPTVersionMin = 0x003;
|
||||||
inline static constexpr uint16_t PartitionCount = 0x004;
|
inline static constexpr uint16_t PartitionCount = 0x004;
|
||||||
|
|
||||||
inline static constexpr uint16_t EntriesStart = 0x00C;
|
inline static constexpr uint16_t EntriesStart = 0x00C;
|
||||||
|
|
||||||
inline static constexpr uint16_t EntryStartAddress = 0x000;
|
inline static constexpr uint16_t EntryStartAddress = 0x000;
|
||||||
inline static constexpr uint16_t EntryPartitionSize = 0x004;
|
inline static constexpr uint16_t EntryPartitionSize = 0x004;
|
||||||
inline static constexpr uint16_t EntryFlags = 0x008;
|
inline static constexpr uint16_t EntryFlags = 0x008;
|
||||||
inline static constexpr uint16_t EntryPartitionLabel = 0x024;
|
inline static constexpr uint16_t EntryPartitionLabel = 0x024;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
inline static constexpr uint16_t HeaderReservedSizeBytes = 7;
|
inline static constexpr uint16_t HeaderReservedSizeBytes = 7;
|
||||||
inline static constexpr uint16_t DiskAddress = 0x200;
|
inline static constexpr uint16_t DiskAddress = 0x200;
|
||||||
inline static constexpr uint16_t DPT_ID_CODE = 0xF1CA;
|
inline static constexpr uint16_t DPT_ID_CODE = 0xF1CA;
|
||||||
inline static constexpr uint16_t EntrySizeBytes = 100;
|
inline static constexpr uint16_t EntrySizeBytes = 100;
|
||||||
inline static constexpr uint16_t EntryLabelSizeBytes = 64;
|
inline static constexpr uint16_t EntryLabelSizeBytes = 64;
|
||||||
inline static constexpr uint16_t EntryReservedSizeBytes = 26;
|
inline static constexpr uint16_t EntryReservedSizeBytes = 26;
|
||||||
inline static constexpr uint16_t HeaderSizeBytes = 12;
|
inline static constexpr uint16_t HeaderSizeBytes = 12;
|
||||||
inline static constexpr uint16_t DPTBlockSizeBytes = 512;
|
inline static constexpr uint16_t DPTBlockSizeBytes = 512;
|
||||||
inline static constexpr uint8_t CurrentDPTVersionMaj = 0x00;
|
inline static constexpr uint8_t CurrentDPTVersionMaj = 0x00;
|
||||||
inline static constexpr uint8_t CurrentDPTVersionMin = 0x02;
|
inline static constexpr uint8_t CurrentDPTVersionMin = 0x02;
|
||||||
inline static constexpr uint32_t DiskStartAddr = 0x00000400;
|
inline static constexpr uint32_t DiskStartAddr = 0x00000400;
|
||||||
inline static constexpr uint8_t MaxPartCount = 5;
|
inline static constexpr uint8_t MaxPartCount = 5;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
inline static constexpr uint16_t BootPart_IDAddr = 0x0000;
|
inline static constexpr uint16_t BootPart_IDAddr = 0x0000;
|
||||||
inline static constexpr uint16_t BootPart_CodeStart = 0x0020;
|
inline static constexpr uint16_t BootPart_CodeStart = 0x0020;
|
||||||
inline static constexpr uint16_t BootPart_ID_CODE = 0xF1C4;
|
inline static constexpr uint16_t BootPart_ID_CODE = 0xF1C4;
|
||||||
inline static constexpr uint16_t BootPart_CodeSizeBytes = 1024;
|
inline static constexpr uint16_t BootPart_CodeSizeBytes = 1024;
|
||||||
inline static constexpr uint8_t BootPart_HeaderSizeBytes = 32;
|
inline static constexpr uint8_t BootPart_HeaderSizeBytes = 32;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CPUExtension
|
class CPUExtension
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue