From 7af7ce540deefe10d17d0efa36415f63167c18c8 Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Mon, 24 Feb 2025 18:56:39 +0000 Subject: [PATCH] Remove index property, store it packed in the pointer instead To achieve this I force memory to a 16-bytes alignment, to achieve this code will allocate 15 extra bytes on the heap but each Block and Frame object will be 8 bytes smaller. Given they are created on the fly and passed around a lot, I think this is a good tradeoff. Not super urgent, but I got it done now, mostly to make sure index is used in a sane way and doesn't take too large values, which would prevent me from making this change later in the future. --- .../memcard/include/memcard/alignment.hpp | 37 +++ subprojects/memcard/include/memcard/block.hpp | 22 +- subprojects/memcard/include/memcard/frame.hpp | 10 +- .../memcard/include/memcard/memorycard.hpp | 4 +- .../include/duckhandy/bitfield_pack.hpp | 180 +++++++++++ .../include/duckhandy/packed_pointer.hpp | 306 ++++++++++++++++++ subprojects/memcard/meson.build | 6 +- subprojects/memcard/src/alignment.cpp | 54 ++++ subprojects/memcard/src/block.cpp | 5 +- subprojects/memcard/src/frame.cpp | 5 +- subprojects/memcard/src/memorycard.cpp | 7 +- 11 files changed, 616 insertions(+), 20 deletions(-) create mode 100644 subprojects/memcard/include/memcard/alignment.hpp create mode 100644 subprojects/memcard/lib/duckhandy/include/duckhandy/bitfield_pack.hpp create mode 100644 subprojects/memcard/lib/duckhandy/include/duckhandy/packed_pointer.hpp create mode 100644 subprojects/memcard/src/alignment.cpp diff --git a/subprojects/memcard/include/memcard/alignment.hpp b/subprojects/memcard/include/memcard/alignment.hpp new file mode 100644 index 0000000..08319da --- /dev/null +++ b/subprojects/memcard/include/memcard/alignment.hpp @@ -0,0 +1,37 @@ +/* Copyright 2020-2025, Michele Santullo + * This file is part of memoserv. + * + * Memoserv 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. + * + * Memoserv 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 Memoserv. If not, see . + */ + +#pragma once + +#include +#include + +namespace dhandy { +template +class PackedPointer; +} //namespace dhandy + +namespace mc::psx { +std::uint8_t* to_block_aligned (std::uint8_t* ptr, std::size_t index=0, std::ptrdiff_t base_offs=0); +const std::uint8_t* to_block_aligned (const std::uint8_t* ptr, std::size_t index=0, std::ptrdiff_t base_offs=0); +bool is_block_aligned (const std::uint8_t* ptr); + +template +bool is_block_aligned (const dhandy::PackedPointer& ptr); + +std::unique_ptr make_unique_for_block_aligned(std::size_t size); +} //namespace mc::psx diff --git a/subprojects/memcard/include/memcard/block.hpp b/subprojects/memcard/include/memcard/block.hpp index dc46be0..dc61719 100644 --- a/subprojects/memcard/include/memcard/block.hpp +++ b/subprojects/memcard/include/memcard/block.hpp @@ -20,6 +20,7 @@ #include "memcard/frame.hpp" #include "memcard/frame_iterator.hpp" #include "memcard/country_code.hpp" +#include "duckhandy/packed_pointer.hpp" #include #include #include @@ -44,17 +45,21 @@ public: using data_type = typename std::conditional::type; typedef FrameIterator iterator; typedef FrameIterator const_iterator; + typedef BasicFrame FrameType; static const constexpr std::size_t FrameCount = 64; static const constexpr std::size_t TOCBlockIndex = 0xFF; static const constexpr uint16_t LastLink = 0xFFFF; - static constexpr std::size_t BlockByteSize = BasicFrame::Size * FrameCount; + static constexpr std::size_t BlockByteSize = FrameType::Size * FrameCount; + static constexpr auto DataAlignment = FrameType::DataAlignment; + static constexpr bool IsConst = Const; + static_assert(BlockByteSize % DataAlignment == 0); BasicBlock() = default; constexpr BasicBlock (const BasicBlock& other); BasicBlock(BasicBlock&&) = default; BasicBlock (data_type* blk, std::size_t index); - BasicBlock (data_type* blk, BasicFrame toc_entry, std::size_t index); + BasicBlock (data_type* blk, FrameType toc_entry, std::size_t index); template BasicBlock(const BasicBlock& other); ~BasicBlock(); @@ -68,9 +73,9 @@ public: data_type* data() { return m_begin; } const data_type* data() const { return m_begin; } - BasicFrame frame(unsigned int idx); + FrameType frame(unsigned int idx); ConstFrame frame(unsigned int idx) const; - BasicFrame toc(); + FrameType toc(); ConstFrame toc() const; static constexpr std::size_t size() { return FrameCount; } @@ -79,7 +84,7 @@ public: bool has_magic() const; IconDisplayFlag icon_display_flag() const; std::size_t block_count() const; - std::size_t index() const { return m_index; } + std::size_t index() const { return dhandy::get<0>(m_begin); } std::string title() const; std::size_t available_blocks() const; uint32_t use_byte() const; @@ -95,9 +100,8 @@ public: BasicBlock& operator= (BasicBlock&&) = default; private: - BasicFrame m_toc_entry; - std::size_t m_index; - data_type* m_begin; + FrameType m_toc_entry; + dhandy::PackedPointer m_begin; }; [[gnu::const]] @@ -122,7 +126,6 @@ inline int32_t calc_icon_count (IconDisplayFlag idf) { template constexpr BasicBlock::BasicBlock (const BasicBlock& other) : m_toc_entry(other.m_toc_entry), - m_index(other.m_index), m_begin(other.m_begin) { } @@ -131,7 +134,6 @@ template template inline BasicBlock::BasicBlock(const BasicBlock& other) : m_toc_entry(other.m_toc_entry), - m_index(other.m_index), m_begin(other.m_begin) { } diff --git a/subprojects/memcard/include/memcard/frame.hpp b/subprojects/memcard/include/memcard/frame.hpp index 5717802..7191513 100644 --- a/subprojects/memcard/include/memcard/frame.hpp +++ b/subprojects/memcard/include/memcard/frame.hpp @@ -17,6 +17,7 @@ #pragma once +#include "duckhandy/packed_pointer.hpp" #include #include #include @@ -29,6 +30,11 @@ class BasicFrame { using data_type = typename std::conditional::type; public: static const constexpr unsigned int Size = 128; + //Standard memory cards can hold up to 15 blocks so we need 4 bits in the + //data pointer field to use for index storing. This means alignment of the + //supplied block should be at least 16 + static constexpr std::size_t DataAlignment = 16; + static_assert(Size % DataAlignment == 0); BasicFrame() = default; BasicFrame (data_type* data, std::size_t index); @@ -60,8 +66,7 @@ public: private: void extract (std::size_t offset, std::size_t sz, uint8_t* out); - std::size_t m_index; - data_type* m_data; + dhandy::PackedPointer m_data; }; template @@ -78,7 +83,6 @@ inline T read_as (const BasicFrame& frame, std::size_t offset) { template template inline BasicFrame::BasicFrame (const BasicFrame& other) { - m_index = other.m_index; m_data = other.m_data; } diff --git a/subprojects/memcard/include/memcard/memorycard.hpp b/subprojects/memcard/include/memcard/memorycard.hpp index af64be3..14e4a60 100644 --- a/subprojects/memcard/include/memcard/memorycard.hpp +++ b/subprojects/memcard/include/memcard/memorycard.hpp @@ -58,7 +58,9 @@ public: const_iterator end() const; private: - std::vector m_data; + const std::uint8_t* data_ptr() const; + + std::vector m_data; }; std::vector block_group (const MemoryCard& mc, std::size_t index); diff --git a/subprojects/memcard/lib/duckhandy/include/duckhandy/bitfield_pack.hpp b/subprojects/memcard/lib/duckhandy/include/duckhandy/bitfield_pack.hpp new file mode 100644 index 0000000..9af11ab --- /dev/null +++ b/subprojects/memcard/lib/duckhandy/include/duckhandy/bitfield_pack.hpp @@ -0,0 +1,180 @@ +/* Copyright 2016-2025 Michele Santullo + * This file is part of "duckhandy". + * + * "duckhandy" 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. + * + * "duckhandy" 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 "duckhandy". If not, see . + */ + +#ifndef id895441FC389F481EB824A809A0BF5004 +#define id895441FC389F481EB824A809A0BF5004 + +#include +#include +#include + +namespace dhandy { + namespace implem { + template + class BitfieldData; + + template + class BitfieldData : public BitfieldData { + protected: + BitfieldData (T init) : BitfieldData(init), m_value(init) {} + public: + T& value() { return m_value; } + T value() const { return m_value; } + private: + T m_value; + }; + + template + class BitfieldData { + protected: + BitfieldData (T init) : m_value(init) {} + public: + T& value() { return m_value; } + T value() const { return m_value; } + private: + T m_value; + }; + + template + class BitfieldData; + + struct SizeAndOffs { + std::size_t size, offset; + }; + struct IndexAndSize { + std::size_t index, size; + }; + + template + constexpr inline SizeAndOffs size_and_offset_impl (std::size_t) { + throw std::out_of_range("Requested pack index out of range"); + } + template + constexpr inline SizeAndOffs size_and_offset_impl (std::size_t index) { + return (index ? size_and_offset_impl(index - 1) : SizeAndOffs{Sz, Sum}); + } + template + constexpr inline SizeAndOffs size_and_offset (std::size_t index) { + return size_and_offset_impl<0, Sizes...>(index); + } + + template + constexpr inline IndexAndSize index_and_size_impl() { + throw std::runtime_error("Should not be reached"); + } + template + constexpr inline IndexAndSize index_and_size_impl() { + return (Idx ? index_and_size_impl< + (CurrMax > Sz ? CurrMax : Sz), + (CurrMax > Sz ? MaxIdx : Idx), + Idx - 1, + Sizes... + >() : IndexAndSize{(CurrMax > Sz ? MaxIdx : Idx) - 1, CurrMax > Sz ? CurrMax : Sz}); + } + template + constexpr inline IndexAndSize index_and_size() { + return index_and_size_impl(); + } + + template + struct BitfieldFacts { + static const constexpr std::size_t Sum = size_and_offset(sizeof...(Sizes) - 1).size + size_and_offset(sizeof...(Sizes) - 1).offset; + static const constexpr std::size_t ElementCount = (Sum + CHAR_BIT * sizeof(T) - 1) / (CHAR_BIT * sizeof(T)); + static const constexpr std::size_t LargestIndex = index_and_size().index; + static const constexpr std::size_t LargestSize = index_and_size().size; + }; + + template + [[gnu::const]] + constexpr inline T make_mask (std::size_t len) noexcept { + return (T{1} << len) - 1; + } + } //namespace implem + + template + class BitfieldPack; + + template + T get (const BitfieldPack&); + template + void set (BitfieldPack&, V); + + template + class BitfieldPack : private implem::BitfieldData::ElementCount> { + typedef implem::BitfieldFacts Facts; + template friend U get (const BitfieldPack&); + template friend void set (BitfieldPack&, V); + typedef implem::BitfieldData base_type; + static_assert(Facts::LargestSize <= sizeof(T) * CHAR_BIT, "Bitfield size can't be larger than the data type itself"); + public: + template + static constexpr T max_at_index = implem::make_mask(implem::size_and_offset(Idx).size); + static constexpr std::size_t element_count = sizeof...(Sizes); + BitfieldPack (T init=T{}) : base_type(init) {} + }; + + template + T get (const BitfieldPack& pack) { + using implem::size_and_offset; + using implem::BitfieldData; + using implem::make_mask; + + static_assert(Idx < sizeof...(Sizes), "Index out of bounds"); + const constexpr auto so = size_and_offset(Idx); + const constexpr std::size_t index = so.offset / (sizeof(T) * CHAR_BIT); + const constexpr std::size_t reloffs = so.offset % (sizeof(T) * CHAR_BIT); + const constexpr bool bleeds_into_next = reloffs + so.size > sizeof(T) * CHAR_BIT; + + const T bottom = static_cast&>(pack).value() >> reloffs; + if constexpr (bleeds_into_next) { + const std::size_t safe_index = index + 2; + const T val = static_cast&>(pack).value(); + const T top = val << (sizeof(T) * CHAR_BIT - reloffs); + return static_cast(top | bottom) & make_mask(so.size); + } + else { + return bottom & make_mask(so.size); + } + } + + template + void set (BitfieldPack& pack, V val) { + using implem::size_and_offset; + using implem::BitfieldData; + using implem::make_mask; + + static_assert(Idx < sizeof...(Sizes), "Index out of bounds"); + const constexpr auto so = size_and_offset(Idx); + const constexpr std::size_t index = so.offset / (sizeof(T) * CHAR_BIT); + const constexpr std::size_t reloffs = so.offset % (sizeof(T) * CHAR_BIT); + const constexpr bool bleeds_into_next = reloffs + so.size > sizeof(T) * CHAR_BIT; + + T& bottom = static_cast&>(pack).value(); + bottom &= ~(make_mask(so.size) << reloffs); + bottom |= static_cast((val & make_mask(so.size)) << reloffs); + + if (bleeds_into_next) { + const std::size_t safe_index = index + 2 - not bleeds_into_next; + T& top = static_cast&>(pack).value(); + const constexpr std::size_t shift = sizeof(T) * CHAR_BIT - reloffs; + top &= ~(make_mask(so.size) >> shift); + top |= val >> shift; + } + } +} //namespace dhandy + +#endif diff --git a/subprojects/memcard/lib/duckhandy/include/duckhandy/packed_pointer.hpp b/subprojects/memcard/lib/duckhandy/include/duckhandy/packed_pointer.hpp new file mode 100644 index 0000000..364f913 --- /dev/null +++ b/subprojects/memcard/lib/duckhandy/include/duckhandy/packed_pointer.hpp @@ -0,0 +1,306 @@ +/* Copyright 2016-2025 Michele Santullo + * This file is part of "duckhandy". + * + * "duckhandy" 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. + * + * "duckhandy" 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 "duckhandy". If not, see . + */ + +#ifndef id83CE9FAB90684250B98EA584C548E13F +#define id83CE9FAB90684250B98EA584C548E13F + +#include "bitfield_pack.hpp" +#include +#include +#include +#include +#include +#include + +namespace dhandy { +namespace detail { + +//This monster expression is just the one-liner version of this +//implementation of log2: +//unsigned int v; // 32-bit value to find the log2 of +//register unsigned int r; // result of log2(v) will go here +//register unsigned int shift; +// +//r = (v > 0xFFFF) << 4; v >>= r; +//shift = (v > 0xFF ) << 3; v >>= shift; r |= shift; +//shift = (v > 0xF ) << 2; v >>= shift; r |= shift; +//shift = (v > 0x3 ) << 1; v >>= shift; r |= shift; +// r |= (v >> 1); +//see https://graphics.stanford.edu/~seander/bithacks.html#IntegerLog for +//an explanation of that. +//This is a shorter version of it but it only works if the input is a power +//of 2: +//(((((NUM&0xAAAAAAAA)!=0)|((NUM&0xFFFF0000)!=0)<<4)|((NUM&0xFF00FF00)!=0)<< +//3)|((NUM&0xF0F0F0F0)!=0)<<2)|((NUM&0xCCCCCCCC)!=0)<<1 +//Source for that is also available at the page linked above. +template constexpr std::size_t pointer_unused_bit_count = + ((((((NUM > 0xFFFF) << 4) | (((NUM >> ((NUM > 0xFFFF) << 4)) > 0xFF) << + 3)) | ((((NUM >> ((NUM > 0xFFFF) << 4)) >> (((NUM >> ((NUM > 0xFFFF) << + 4)) > 0xFF) << 3)) > 0xF) << 2)) | (((((NUM >> ((NUM > 0xFFFF) << 4)) >> + (((NUM >> ((NUM > 0xFFFF) << 4)) > 0xFF) << 3)) >> ((((NUM >> ((NUM > + 0xFFFF) << 4)) >> (((NUM >> ((NUM > 0xFFFF) << 4)) > 0xFF) << 3)) > 0xF) + << 2)) > 0x3) << 1)) | (((((NUM >> ((NUM > 0xFFFF) << 4)) >> (((NUM >> ( + (NUM > 0xFFFF) << 4)) > 0xFF) << 3)) >> ((((NUM >> ((NUM > 0xFFFF) << 4) + ) >> (((NUM >> ((NUM > 0xFFFF) << 4)) > 0xFF) << 3)) > 0xF) << 2)) >> (( + (((NUM >> ((NUM > 0xFFFF) << 4)) >> (((NUM >> ((NUM > 0xFFFF) << 4)) > + 0xFF) << 3)) >> ((((NUM >> ((NUM > 0xFFFF) << 4)) >> (((NUM >> ((NUM > + 0xFFFF) << 4)) > 0xFF) << 3)) > 0xF) << 2)) > 0x3) << 1)) >> 1)) +; + +template +BitfieldPack< + std::uintptr_t, + sizeof(std::uintptr_t) * CHAR_BIT - sizeof...(S), (S+1)/(S+1)... +> +guess_bitfield_type (std::index_sequence); + +template +using BaseBitfieldPackForPackedPointer = decltype( + guess_bitfield_type( + std::make_index_sequence>() + ) +); + +template +using BaseIntPackForPackedPointer = BitfieldPack< + std::uintptr_t, + sizeof(std::uintptr_t) * CHAR_BIT - pointer_unused_bit_count, + pointer_unused_bit_count +>; + +template +using BaseBitfieldType = std::conditional_t, BaseIntPackForPackedPointer +>; + +template +struct PackedPointerSetterGetter { + template + static void set (dhandy::BitfieldPack& out, I val) { + static_assert(0 == Idx); + dhandy::set<1>(out, val); + } + template + static I get (const dhandy::BitfieldPack& in) { + static_assert(0 == Idx); + return dhandy::get<1>(in); + } +}; + +template +struct PackedPointerSetterGetter { + template + static void set (dhandy::BitfieldPack& out, bool flag) { + static_assert(Idx < sizeof...(S) - 1, "Index out of range"); + dhandy::set(out, (flag ? 1u : 0u)); + } + template + static bool get (const dhandy::BitfieldPack& in) { + static_assert(Idx < sizeof...(S) - 1, "Index out of range"); + return static_cast(dhandy::get(in)); + } +}; +} //namespace detail + +template +class PackedPointer : detail::BaseBitfieldType::value> { + static_assert(A >= 2, "Not enough space to pack anything"); + static_assert(sizeof(T*) == sizeof(std::uintptr_t), "Mismatching int/pointer size"); + typedef detail::BaseBitfieldType::value> parent_type; + static_assert(sizeof(parent_type) == sizeof(T*)); + + template friend J get (const PackedPointer&); + template friend void set (PackedPointer&, J); +public: + static constexpr std::size_t FlagsCount = parent_type::element_count - 1; + static constexpr auto max_payload = + static_cast, unsigned int, I>>( + parent_type::template max_at_index<1> + ); + typedef I int_type; + + PackedPointer(); + PackedPointer (T* ptr); + PackedPointer (const PackedPointer& other); + PackedPointer (PackedPointer&& other); + ~PackedPointer() = default; + + operator T*(); + operator const T*() const; + operator std::uintptr_t() const; + operator bool() const; + + PackedPointer& operator= (const PackedPointer& other); + PackedPointer& operator= (PackedPointer&& other); + PackedPointer& operator= (T* ptr); + T* operator+ (std::integral auto diff); + const T* operator+ (std::integral auto diff) const; + T* operator- (std::integral auto diff); + const T* operator- (std::integral auto diff) const; + T* operator->(); + const T* operator->() const; + T& operator*(); + const T& operator*() const; + bool operator== (const PackedPointer& other) const; + bool operator== (T* other) const; + bool operator!= (const PackedPointer& other) const; + bool operator!= (T* other) const; + +private: + //static const constexpr uintptr_t PtrMask = static_cast(-1) << FlagsCount; +}; + +template +inline void set (PackedPointer& obj, I value) { + assert(value < (1u << A)); + detail::PackedPointerSetterGetter::template set(obj, value); +} + +template +inline I get (const PackedPointer& obj) { + return detail::PackedPointerSetterGetter::template get(obj); +} + +template const constexpr std::size_t PackedPointer::FlagsCount; +//template const constexpr std::uintptr_t PackedPointer::PtrMask; + +template +PackedPointer::PackedPointer() : + PackedPointer(nullptr) +{ +} + +template +PackedPointer::PackedPointer (T* ptr) : + parent_type(0) +{ + (*this) = ptr; +} + +template +PackedPointer::PackedPointer (const PackedPointer& other) : + parent_type(other) +{ +} + +template +PackedPointer::PackedPointer (PackedPointer&& other) : + parent_type(std::move(other)) +{ +} + +template +PackedPointer::operator T*() { + return reinterpret_cast(get<0>(*static_cast(this)) << FlagsCount); +} + +template +PackedPointer::operator const T*() const { + return reinterpret_cast(get<0>(*static_cast(this)) << FlagsCount); +} + +template +PackedPointer::operator std::uintptr_t() const { + return get<0>(*static_cast(this)) << FlagsCount; +} + +template +PackedPointer::operator bool() const { + return static_cast(*this) != nullptr; +} + +template +PackedPointer& PackedPointer::operator= (const PackedPointer& other) { + *static_cast(this) = static_cast(other); + return *this; +} + +template +PackedPointer& PackedPointer::operator= (PackedPointer&& other) { + *static_cast(this) = std::move(static_cast(other)); + return *this; +} + +template +PackedPointer& PackedPointer::operator= (T* ptr) { + assert(reinterpret_cast(ptr) % A == 0); + set<0>(*static_cast(this), reinterpret_cast(ptr) >> FlagsCount); + return *this; +} + +template +T* PackedPointer::operator+ (std::integral auto diff) { + return static_cast(*this) + diff; +} + +template +const T* PackedPointer::operator+ (std::integral auto diff) const { + return static_cast(*this) + diff; +} + +template +T* PackedPointer::operator- (std::integral auto diff) { + return static_cast(*this) + diff; +} + +template +const T* PackedPointer::operator- (std::integral auto diff) const { + return static_cast(*this) + diff; +} + +template +T* PackedPointer::operator->() { + return static_cast(*this); +} + +template +const T* PackedPointer::operator->() const { + return static_cast(*this); +} + +template +T& PackedPointer::operator*() { + return *static_cast(*this); +} + +template +const T& PackedPointer::operator*() const { + return *static_cast(*this); +} + +template +bool PackedPointer::operator== (const PackedPointer& other) const { + return this == &other or static_cast(*this) == static_cast(other); +} + +template +bool PackedPointer::operator== (T* other) const { + return static_cast(*this) == other; +} + +template +bool PackedPointer::operator!= (const PackedPointer& other) const { + return not this->operator==(other); +} + +template +bool PackedPointer::operator!= (T* other) const { + return not this->operator==(other); +} +} //namepace dhandy + +#endif diff --git a/subprojects/memcard/meson.build b/subprojects/memcard/meson.build index 61fc4b6..1e630b7 100644 --- a/subprojects/memcard/meson.build +++ b/subprojects/memcard/meson.build @@ -3,6 +3,7 @@ add_project_link_arguments(['-lstdc++fs'], language: 'cpp') private_incl = include_directories('src') library_incl = include_directories('include') +duckhandy_incl = include_directories('lib/duckhandy/include') memcard = shared_library('memcard', 'src/memorycard.cpp', @@ -16,11 +17,12 @@ memcard = shared_library('memcard', 'src/part_iterator.cpp', 'src/frame.cpp', 'src/country_code.cpp', + 'src/alignment.cpp', install: true, - include_directories: [private_incl, library_incl], + include_directories: [private_incl, library_incl, duckhandy_incl], ) memcard_dep = declare_dependency( - include_directories: library_incl, + include_directories: [library_incl, duckhandy_incl], link_with: memcard ) diff --git a/subprojects/memcard/src/alignment.cpp b/subprojects/memcard/src/alignment.cpp new file mode 100644 index 0000000..4686010 --- /dev/null +++ b/subprojects/memcard/src/alignment.cpp @@ -0,0 +1,54 @@ +/* Copyright 2020-2025, Michele Santullo + * This file is part of memoserv. + * + * Memoserv 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. + * + * Memoserv 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 Memoserv. If not, see . + */ + +#include "memcard/alignment.hpp" +#include "memcard/block.hpp" +#include "duckhandy/packed_pointer.hpp" + +namespace mc::psx { +std::uint8_t* to_block_aligned (std::uint8_t* ptr, std::size_t index, std::ptrdiff_t base_offs) { + return reinterpret_cast( + (reinterpret_cast(ptr) + (Block::DataAlignment-1)) & ~(Block::DataAlignment-1) + ) + base_offs + index * Block::BlockByteSize; +} + +const std::uint8_t* to_block_aligned (const std::uint8_t* ptr, std::size_t index, std::ptrdiff_t base_offs) { + return const_cast( + to_block_aligned(const_cast(ptr), index, base_offs) + ); +} + +bool is_block_aligned (const std::uint8_t* ptr) { + return (0 == reinterpret_cast(ptr) % Block::DataAlignment); +} + +template +bool is_block_aligned (const dhandy::PackedPointer& ptr) { + return (0 == static_cast(ptr) % Block::DataAlignment); +} + +template bool is_block_aligned ( + const dhandy::PackedPointer& +); +template bool is_block_aligned ( + const dhandy::PackedPointer& +); + +std::unique_ptr make_unique_for_block_aligned(std::size_t size) { + return std::make_unique(size + Block::DataAlignment - 1); +} +} //namespace mc::psx diff --git a/subprojects/memcard/src/block.cpp b/subprojects/memcard/src/block.cpp index 27c55b4..7c43849 100644 --- a/subprojects/memcard/src/block.cpp +++ b/subprojects/memcard/src/block.cpp @@ -17,6 +17,7 @@ #include "memcard/block.hpp" #include "memcard/memorycard.hpp" +#include "memcard/alignment.hpp" #include "shiftjis_to_utf8.hpp" #include #include @@ -94,10 +95,12 @@ BasicBlock::BasicBlock (data_type* blk, std::size_t index) : template BasicBlock::BasicBlock (data_type* blk, BasicFrame toc_entry, std::size_t index) : m_toc_entry(toc_entry), - m_index(index), m_begin(blk) { assert(m_begin); + assert(is_block_aligned(m_begin)); + assert(index <= m_begin.max_payload); + dhandy::set<0>(m_begin, index); } template diff --git a/subprojects/memcard/src/frame.cpp b/subprojects/memcard/src/frame.cpp index 0d8bb3c..a707c85 100644 --- a/subprojects/memcard/src/frame.cpp +++ b/subprojects/memcard/src/frame.cpp @@ -16,14 +16,16 @@ */ #include "memcard/frame.hpp" +#include "memcard/alignment.hpp" #include namespace mc::psx { template BasicFrame::BasicFrame (data_type* data, std::size_t index) : - m_index(index), m_data(data) { + assert(is_block_aligned(m_data)); + dhandy::set<0>(m_data, index); } template @@ -41,7 +43,6 @@ auto BasicFrame::operator[] (std::size_t index) const -> const data_type& template template BasicFrame& BasicFrame::operator= (const BasicFrame& other) { - m_index = other.m_index; m_data = other.m_data; return *this; } diff --git a/subprojects/memcard/src/memorycard.cpp b/subprojects/memcard/src/memorycard.cpp index 9d13fb7..926edf5 100644 --- a/subprojects/memcard/src/memorycard.cpp +++ b/subprojects/memcard/src/memorycard.cpp @@ -18,6 +18,7 @@ #include "memcard/memorycard.hpp" #include "memcard/block.hpp" #include "memcard/frame.hpp" +#include "memcard/alignment.hpp" #include #include @@ -39,7 +40,7 @@ namespace { } //unnamed namespace MemoryCard::MemoryCard() : - m_data(MemoryCardSize) + m_data(MemoryCardSize + Block::DataAlignment - 1) { } @@ -130,4 +131,8 @@ std::vector block_group (const MemoryCard& mc, std::size_t index) { } return {}; } + +const std::uint8_t* MemoryCard::data_ptr() const { + return to_block_aligned(m_data.data()); +} } //namespace mc::psx