Skip to content
Snippets Groups Projects
memory.cpp 11 KiB
Newer Older
  • Learn to ignore specific revisions
  • Vicki Pfau's avatar
    Vicki Pfau committed
    #include "memory.h"
    
    #include <cstdlib>
    #include <unordered_map>
    
    using namespace Retro;
    using namespace std;
    
    Endian Retro::reduce(Endian e) {
    	switch (e) {
    	case Endian::BIG:
    	case Endian::LITTLE:
    	case Endian::UNDEF:
    	case Endian::MIXED_BL:
    	case Endian::MIXED_LB:
    		return e;
    	case Endian::NATIVE:
    		return Endian::REAL_NATIVE;
    	case Endian::MIXED_BN:
    		return Endian::REAL_MIXED_BN;
    	case Endian::MIXED_LN:
    		return Endian::REAL_MIXED_LN;
    	}
    	return e;
    }
    
    bool Retro::reduceCompare(Endian a, Endian b) {
    	return reduce(a) == reduce(b);
    }
    
    DataType::DataType(const char* type)
    	: width(type[strlen(type) - 1] - '0')
    	, endian(
    		  type[0] == '=' ? Endian::NATIVE : type[0] == '>' ? (type[1] == '<' ? Endian::MIXED_BL : type[1] == '=' ? Endian::MIXED_BN : Endian::BIG) : type[0] == '<' ? (type[1] == '>' ? Endian::MIXED_LB : type[1] == '=' ? Endian::MIXED_LN : Endian::LITTLE) : Endian::UNDEF)
    	, repr(static_cast<Repr>(type[strlen(type) - 2]))
    	, type{ type[0], type[1], type[2], type[3] }
    	, maskLo(repr == Repr::LN_BCD || repr == Repr::BCD ? 0xF : 0xFF)
    	, maskHi(repr == Repr::BCD ? 0xF0 : 0x0)
    	, cvt(repr == Repr::BCD || repr == Repr::LN_BCD ? 10 : 256) {
    	uint64_t shiftInc =
    		repr == Repr::BCD ? 100 : repr == Repr::LN_BCD ? 10 : 256;
    
    	int baseLoc;
    	int baseEnd;
    	int halfLoc = -1;
    	int diff;
    
    	switch (reduce(endian)) {
    	case Endian::LITTLE:
    	default:
    		baseLoc = 0;
    		baseEnd = width;
    		diff = 1;
    		break;
    	case Endian::BIG:
    		baseLoc = width - 1;
    		baseEnd = -1;
    		diff = -1;
    		break;
    	case Endian::MIXED_LB:
    		baseLoc = width / 2 - 1;
    		baseEnd = -1;
    		halfLoc = width - 1;
    		diff = -1;
    		break;
    	case Endian::MIXED_BL:
    		baseLoc = width / 2;
    		baseEnd = width;
    		halfLoc = 0;
    		diff = 1;
    		break;
    	}
    
    	uint64_t baseShift = 1;
    	for (int i = baseLoc; i != baseEnd; i += diff, baseShift *= shiftInc) {
    		shift[i] = baseShift;
    	}
    	if (halfLoc >= 0) {
    		for (int i = halfLoc; i != baseLoc; i += diff, baseShift *= shiftInc) {
    			shift[i] = baseShift;
    		}
    	}
    }
    
    DataType::DataType(const string& type)
    	: DataType(type.c_str()) {
    }
    
    Datum DataType::operator()(void* base) const {
    	return Datum(base, *this);
    }
    
    Datum DataType::operator()(void* base, size_t offset, const MemoryOverlay& overlay) const {
    	return Datum(base, offset, *this, overlay);
    }
    
    bool DataType::operator==(const DataType& other) const {
    	return width == other.width && endian == other.endian && repr == other.repr;
    }
    
    bool DataType::operator!=(const DataType& other) const {
    	return !(*this == other);
    }
    
    void DataType::encode(void* buffer, int64_t value) const {
    	for (size_t i = 0; i < width; ++i) {
    		uint64_t b = (uint64_t) value / shift[i];
    		b = b % cvt + b / cvt % cvt * (~maskHi + 1);
    		static_cast<uint8_t*>(buffer)[i] = b;
    	}
    }
    
    int64_t DataType::decode(const void* buffer) const {
    	int64_t datum = 0;
    	for (size_t i = 0; i < width; ++i) {
    		uint8_t b = static_cast<const uint8_t*>(buffer)[i];
    		datum += ((b & maskLo) % cvt + ((b & maskHi) >> 4) % cvt * 10) * shift[i];
    	}
    	if (repr == Repr::SIGNED) {
    		datum <<= 8 * (8 - width);
    		datum >>= 8 * (8 - width);
    	}
    	return datum;
    }
    
    size_t hash<DataType>::operator()(const DataType& type) const {
    	return hash<uint32_t>()(*reinterpret_cast<const uint32_t*>(type.type));
    }
    
    static constexpr char endianTag(Endian e) {
    	switch (e) {
    	case Endian::BIG:
    		return '>';
    	case Endian::LITTLE:
    		return '<';
    	default:
    	case Endian::UNDEF:
    		return '|';
    	case Endian::NATIVE:
    		return '=';
    	}
    }
    
    MemoryOverlay::MemoryOverlay(Endian backing, Endian real, size_t width)
    	: width(width)
    	, m_backing({ endianTag(backing), 'u', static_cast<char>('0' + width) })
    	, m_real({ endianTag(real), 'u', static_cast<char>('0' + width) }) {
    }
    
    MemoryOverlay::MemoryOverlay(char backing, char real, size_t width)
    	: width(width)
    	, m_backing({ backing, 'u', static_cast<char>('0' + width) })
    	, m_real({ real, 'u', static_cast<char>('0' + width) }) {
    }
    
    void* MemoryOverlay::parse(const void* in, size_t offset, void* out, size_t size) const {
    	size_t offsetEdge = offset & (width - 1);
    	uintptr_t base = reinterpret_cast<uintptr_t>(in);
    	base += offset & ~(width - 1);
    	size += offsetEdge;
    	uintptr_t outBase = reinterpret_cast<uintptr_t>(out);
    	for (size_t i = 0; i < size; i += width) {
    		int64_t val = m_backing.decode(reinterpret_cast<const void*>(base + i));
    		m_real.encode(reinterpret_cast<void*>(outBase + i), val);
    	}
    	return reinterpret_cast<void*>(outBase + offsetEdge);
    }
    
    void MemoryOverlay::unparse(void* out, size_t offset, const void* in, size_t size) const {
    	size_t offsetEdge = offset & (width - 1);
    	uintptr_t base = reinterpret_cast<uintptr_t>(out);
    	base += offset & ~(width - 1);
    	size += offsetEdge;
    	uintptr_t inBase = reinterpret_cast<uintptr_t>(in);
    	for (size_t i = 0; i < size; i += width) {
    		int64_t val = m_real.decode(reinterpret_cast<void*>(inBase + i));
    		m_backing.encode(reinterpret_cast<void*>(base + i), val);
    	}
    }
    
    Datum::Datum(void* base, const DataType& type)
    	: m_base(base)
    	, m_type(type) {
    }
    
    Datum::Datum(void* base, size_t offset, const DataType& type, const MemoryOverlay& overlay)
    	: m_base(base)
    	, m_offset(offset)
    	, m_type(type)
    	, m_overlay(overlay) {
    }
    
    Datum::Datum(void* base, const Variable& var, const MemoryOverlay& overlay)
    	: m_base(base)
    	, m_offset(var.address)
    	, m_type(var.type)
    	, m_mask(var.mask)
    	, m_overlay(overlay) {
    }
    
    Datum& Datum::operator=(int64_t value) {
    	if (m_base) {
    		if (m_overlay.width > 1 || m_offset) {
    			uint8_t fakeBase[16]{};
    			m_type.encode(m_overlay.parse(m_base, m_offset, reinterpret_cast<void*>(fakeBase), m_type.width), value);
    			m_overlay.unparse(m_base, m_offset, reinterpret_cast<void*>(fakeBase), m_type.width);
    		} else {
    			m_type.encode(m_base, value);
    		}
    	}
    	return *this;
    }
    
    Datum::operator int64_t() const {
    	if (!m_base) {
    		return 0;
    	}
    	int64_t value;
    	if (m_overlay.width > 1 || m_offset) {
    		uint8_t fakeBase[16]{};
    		value = m_type.decode(m_overlay.parse(m_base, m_offset, reinterpret_cast<void*>(fakeBase), m_type.width));
    	} else {
    		value = m_type.decode(m_base);
    	}
    	return value & m_mask;
    }
    
    DynamicMemoryView::DynamicMemoryView(void* buffer, size_t bytes, const DataType& dtype, const MemoryOverlay& overlay)
    	: dtype(dtype)
    	, overlay(overlay) {
    	m_mem.open(buffer, bytes);
    }
    
    Datum DynamicMemoryView::operator[](size_t offset) {
    	return dtype(m_mem.offset(0), offset, overlay);
    }
    
    int64_t DynamicMemoryView::operator[](size_t offset) const {
    	if (overlay.width > 1) {
    		uint8_t fakeBase[16]{};
    		return dtype.decode(overlay.parse(m_mem.offset(0), offset, reinterpret_cast<void*>(fakeBase), dtype.width));
    	}
    	return dtype.decode(m_mem.offset(offset));
    }
    
    
    Vicki Pfau's avatar
    Vicki Pfau committed
    const DataType AddressSpace::s_type{ "|u1" };
    
    Vicki Pfau's avatar
    Vicki Pfau committed
    
    void AddressSpace::addBlock(size_t offset, size_t size, void* data) {
    	if (data) {
    		m_blocks[offset].open(data, size);
    	} else {
    		m_blocks[offset].open(size);
    	}
    }
    
    void AddressSpace::addBlock(size_t offset, size_t size, const void* data) {
    	if (data) {
    		m_blocks[offset].clone(data, size);
    	} else {
    		m_blocks[offset].open(size);
    	}
    }
    
    void AddressSpace::addBlock(size_t offset, const MemoryView<>& base) {
    	m_blocks[offset].clone(base);
    }
    
    void AddressSpace::updateBlock(size_t offset, void* data) {
    	m_blocks[offset].open(data, m_blocks[offset].size());
    }
    
    void AddressSpace::updateBlock(size_t offset, const void* data) {
    	m_blocks[offset].clone(data, m_blocks[offset].size());
    }
    
    void AddressSpace::updateBlock(size_t offset, const MemoryView<>& base) {
    	m_blocks[offset].clone(base);
    }
    
    bool AddressSpace::hasBlock(size_t offset) const {
    	for (const auto& block : m_blocks) {
    		if (offset < block.first) {
    			continue;
    		}
    		if (offset < block.first + block.second.size()) {
    			return true;
    		}
    	}
    	return false;
    }
    
    const MemoryView<>& AddressSpace::block(size_t offset) const {
    	for (const auto& block : m_blocks) {
    		if (offset < block.first) {
    			continue;
    		}
    		if (offset < block.first + block.second.size()) {
    			return block.second;
    		}
    	}
    	throw std::out_of_range("No known mapping");
    }
    
    MemoryView<>& AddressSpace::block(size_t offset) {
    	for (auto& block : m_blocks) {
    		if (offset < block.first) {
    			continue;
    		}
    		if (offset < block.first + block.second.size()) {
    			return block.second;
    		}
    	}
    	throw std::out_of_range("No known mapping");
    }
    
    bool AddressSpace::ok() const {
    	return m_blocks.size() > 0;
    }
    
    void AddressSpace::reset() {
    	m_blocks.clear();
    }
    
    void AddressSpace::clone(const AddressSpace& as) {
    	m_blocks.clear();
    	m_overlay = make_unique<MemoryOverlay>(*as.m_overlay);
    	for (auto& kv : as.m_blocks) {
    		m_blocks[kv.first].clone(kv.second);
    	}
    }
    
    void AddressSpace::clone() {
    	for (auto& kv : m_blocks) {
    		kv.second.clone();
    	}
    }
    
    void AddressSpace::setOverlay(const MemoryOverlay& overlay) {
    	m_overlay = make_unique<MemoryOverlay>(overlay);
    }
    
    Datum AddressSpace::operator[](size_t offset) {
    	for (auto& kv : m_blocks) {
    		if (offset < kv.first) {
    			throw std::out_of_range("No known mapping");
    		}
    		if (offset - kv.first >= kv.second.size()) {
    			continue;
    		}
    		return Datum(kv.second.offset(0), offset - kv.first, s_type, *m_overlay);
    	}
    	throw std::out_of_range("No known mapping");
    }
    
    Datum AddressSpace::operator[](const Variable& var) {
    	for (auto& kv : m_blocks) {
    		if (var.address < kv.first) {
    			throw std::out_of_range("No known mapping");
    		}
    		if (var.address - kv.first >= kv.second.size()) {
    			continue;
    		}
    
    Vicki Pfau's avatar
    Vicki Pfau committed
    		return Datum(kv.second.offset(0), Variable{ var.type, var.address - kv.first, var.mask }, *m_overlay);
    
    Vicki Pfau's avatar
    Vicki Pfau committed
    	}
    	throw std::out_of_range("No known mapping");
    }
    
    uint8_t AddressSpace::operator[](size_t offset) const {
    	for (const auto& kv : m_blocks) {
    		if (offset < kv.first) {
    			throw std::out_of_range("No known mapping");
    		}
    		if (offset - kv.first >= kv.second.size()) {
    			continue;
    		}
    		uint8_t fakeBase[16]{};
    		return s_type.decode(m_overlay->parse(kv.second.offset(0), offset - kv.first, reinterpret_cast<void*>(fakeBase), s_type.width));
    	}
    	throw std::out_of_range("No known mapping");
    }
    
    int64_t AddressSpace::operator[](const Variable& var) const {
    	for (const auto& kv : m_blocks) {
    		if (var.address < kv.first) {
    			throw std::out_of_range("No known mapping");
    		}
    		if (var.address - kv.first >= kv.second.size()) {
    			continue;
    		}
    		int64_t value;
    		if (m_overlay->width > 1) {
    			uint8_t fakeBase[16];
    			value = var.type.decode(m_overlay->parse(kv.second.offset(0), var.address - kv.first, reinterpret_cast<void*>(fakeBase), var.type.width));
    		} else {
    			value = var.type.decode(kv.second.offset(var.address - kv.first));
    		}
    		value &= var.mask;
    		return value;
    	}
    	throw std::out_of_range("No known mapping");
    }
    
    AddressSpace& AddressSpace::operator=(AddressSpace&& as) {
    	m_blocks.clear();
    	m_overlay = move(as.m_overlay);
    	for (auto& kv : as.m_blocks) {
    		m_blocks[kv.first] = move(as.m_blocks[kv.first]);
    	}
    	as.m_blocks.clear();
    	return *this;
    }
    
    int64_t Retro::toBcd(int64_t value) {
    	int64_t out = 0;
    	int shift = 0;
    	while (value) {
    		out |= (value % 10) << (shift * 4);
    		++shift;
    		value /= 10;
    	}
    	return out;
    }
    
    int64_t Retro::toLNBcd(int64_t value) {
    	int64_t out = 0;
    	int shift = 0;
    	while (value) {
    		out |= (value % 10) << (shift * 8);
    		++shift;
    		value /= 10;
    	}
    	return out;
    }
    
    bool Retro::isBcd(uint64_t value) {
    	uint64_t halfdigits = (value >> 1) & 0x7777777777777777;
    	return !((halfdigits + 0x3333333333333333) & 0x8888888888888888);
    }