Извлечение определенного байта на основе значения индекса - PullRequest
0 голосов
/ 11 мая 2019

В настоящее время я могу извлечь младший или старший байт из 16-битного типа int и сохранить его в 8-битном типе int.Посмотрите на следующий пример кода:

#include <bitset>
#include <cassert>
#include <iostream>
#include <vector> // using for std::uint8_t, etc.

// valid values for idx[0,1]
void getByteFrom(std::uint16_t val, std::uint8_t idx, std::uint8_t& res) {  
    assert(idx == 0 || idx == 1);
    res = ((val >> (idx << 3)) & 0xff);
}

int main() {    
    std::uint16_t value = 13579;
    std::bitset<16> bits{ value };

    std::cout << "Reference Bits:\n"
              << bits.to_ulong()
              << '\n' << bits << "\n\n";

    std::uint8_t lowByte = 0, highByte = 0;
    getByteFrom(value, 0, lowByte);
    getByteFrom(value, 1, highByte);

    std::bitset<8> lowBits{ lowByte };
    std::bitset<8> highBits{ highByte };

    std::cout << "lowByte = " << lowByte << '\n';
    std::cout << "lowBits (value): " << lowBits.to_ulong() << '\n';
    std::cout << "lowBits (bits):  " << lowBits << "\n\n";

    std::cout << "highByte = " << highByte << '\n';
    std::cout << "highBits (value): " << highBits.to_ulong() << '\n';
    std::cout << "highBits (bits):  " << highBits << "\n\n";

    return EXIT_SUCCESS;
}

И он генерирует этот вывод, который ожидаем и желателен.

Вывод

Reference Bits:
13579
0011010100001011

lowByte = ♂
lowBits (value): 11
lowBits (bits):  00001011

highByte = 5
highBits (value): 53
highBits (bits):  00110101

Теперь я хотел бы сделать то же самое, но для больших типов ...

// u8 = std::uint8_t, u16 = uint16_t, etc.

// valid idx [0,1,2,3]
getByteFrom( u32 val, u8 idx, u8& res );

// valid idx [0,1,2,3,4,5,6,7]
getByteFrom( u64 val, u8 idx, u8& res );

// Also: getting words from dwords and qwords and getting qwords from words

// valid idx[0,1]
getWordFrom( u32 val, u8 idx, u16& res );

// valid idx [0,1,2,3]
getWordFrom( u64 val, u8 idx, u16& res );

// valid idx[0,1]
getDWordFrom( u64 value, u8 idx, u32& res );

Зная, что я могу использовать двоичную логику, чтобы получить один байт из слова:

res = ((val >> (idx << 3)) & 0xff);

Что я хотел бы знать, так это то, что будет заполненной таблицей двоичных логических выражений со сдвигом битов и маскированием битов, чтобы я мог завершить написание своих функций?


ПРИМЕЧАНИЕ: - Это относится к первым первым двум ответам ниже: -

Для первого ответа пользователя dhanushka: Это может быть интересно, но функции выше неавтономные функции они будут реализованы как набор унаследованных конструкторов классов.Я пытаюсь создать базовый класс Register и создать классы Reg8, Reg16, Reg32 и Reg64.Базовым типом каждого класса является соответствующий std::uintx_t, где x равен 8, 16, 32 и 64 соответственно.Эти структуры будут содержать член этого типа в качестве своих данных, а также std::bitset<x>, где x - размер битов для этого типа.

Конструкторы будут устанавливать значение члена типа uint на основе того, какой конструктор был использован, и его передачи в параметрах.Некоторые конструкторы будут инициализироваться по умолчанию 0, другие будут передаваться по значению или ссылочному (явному) типу.Усечение битов - это нормально, если конструирование из большего размера приводит к меньшему размеру, если усечение не влияет на желаемый результат.Затем конструкторы инициализируют элемент bitset<size> на основе значения элемента data.

Я буду использовать эти классы Register в качестве объектов регистров в программе виртуальной машины.Эти классы предназначены для того, чтобы быть простыми, быстрыми, но надежными, с множеством функций и очень низкой стоимостью.В связи с этим я хотел бы также попробовать шаблонизировать эти классы, чтобы большая часть накладных расходов выполнялась во время компиляции.

Каждый из типов будет представлять регистры размера byte, word, dword & qword виртуального процессора.Некоторые функции, которые я хочу включить, включают в себя тот факт, что вы можете легко и быстро изменить порядок битов.Допустим, у нас есть u8 тип структуры Reg8.Допустим, он был создан по значению его базового типа, и допустим, что значение равно 222 в десятичном виде.Так как это std::uint8_t, то под капотом это будет выглядеть так: dec binary hex 222 = 1101 1110 0xde

Я могу использовать функцию bitset's to_string() для преобразования ее в строку, используйте std::reverseчтобы изменить порядок битов и использовать std::stoi, чтобы преобразовать его обратно в тип int и перезаписать исходные элементы, получая это:

dec    binary     hex
123    0111 1011  0x7b

Таким образом, любой, кто будет использовать эту библиотеку виртуальной машины, может быстро настроить хранилище битов любым способом, который им необходим. Теперь в классах регистров большего размера возьмем, например, Reg16, где его базовый тип - std::uint16_t и имеет сопровождающий член std::bitset<16> с тем же значением; с помощью битовых полей можно легко получить доступ к каждому отдельному байту из слова. Я также хотел бы включить встроенную функцию и режим для переключения между порядками байтов, и это можно сделать на лету. По умолчанию я думаю, что буду придерживаться порядка байтов, так как это то, что моя машина. Поэтому нет нужды говорить, что в течение последних 4-5 дней я пробовал различные шаблоны проектирования, пытаясь собрать все соединения. Всего будет 4 основных способа построения этих регистров; конструкция по умолчанию 0 инициализирована, построена из базового типа и инициализирована переданным в параметре (явном), построена из переданного в параметре, но зависит от значения индекса в более крупном базовом типе, и, наконец, эти регистры также должны иметь возможность быть построенными из других типов регистров. Я могу передать Reg64 в конструктор Reg8 и создать Reg8 из одного из 8 байтов или Reg64. Я также могу создать Reg64 из одного Reg8, который может быть вставлен в любой из его 8 байтов, или из нескольких Reg8. Да, есть много сложностей в настройке этих классов; но это универсальность, что я после.

В моем виртуальном ПК; эти регистры будут использоваться для эмуляции реальных регистров, за исключением того, что это разновидность динамических полиморфных регистров, которые имеют двустороннюю связь, двунаправленный ввод / вывод. Конечно, мне понадобятся некоторые флаги для этого позже; и я планирую использовать процесс потоковой передачи битов с использованием перегруженных operator<< и operator>>, чтобы в конечном итоге поместить эти регистры в строковые потоки. Возможно, я имею в виду систему регистровых сетей на основе векторной матрицы, которая является основной частью виртуального процессора.

Позже, когда я начну раскладывать свои коды операций - байтовые коды и мнемонику, я не думаю, что буду жестко их кодировать. Я имею в виду наличие файла свойств, который читается и анализируется, и информация будет сохранена в статической хэш-карте.

Поэтому, когда я собираюсь создавать операции ЦП вместо обычной системы стекового типа, в которой все функции кода операции жестко запрограммированы; эти хеш-карты будут использоваться для запроса соответствующей операции. Все это может измениться со временем тоже; Я имею в виду систему очередей, управляемую событиями. Теперь в общей концепции все регистры в ЦП будут 64-битными, где регистры меньшего размера будут обобщены. Так что, если я создаю, скажем, два типа Reg16 и делаю сложение через оп или байт-коды; Процессор буквально возьмет один Reg64 и сохранит оба Reg16 в разных частях слова этого 64-битного регистра. Затем он выполнит предоставленное сложение двух (так сказать, на месте) и сохранит результаты в одном из оставшихся пространств слов, оставшихся в этом регистре. Затем он сместит биты, чтобы вы получили правильное значение. Если вы просматривали результаты от члена данных Reg64, он может представлять или не представлять точное значение сложения, поскольку это будет зависеть от кодов команд, если результирующее слово было смещено, чтобы дать это значение или нет. Вы также можете легко запросить или вернуть тип Reg16 этого значения, так как оно будет сохранено.

Вот небольшой пример, но с Reg32 в качестве базового типа для простоты. Это может быть не совсем точно, но показано только для иллюстрации концепции.

CPU fetches op codes and gets a set of instructions for 2 Reg8s and to be added and stored into a Reg32. 

// 0x01 - load immediate into reg A
// 0x02 - load immediate into reg B
// 0x10 - add
// 0x0c - store res into 4th byte of Reg32.
// 0xe0 - shift bits in Reg32 to reflect correct value of addition

0000 0001 - load immediate (33) into first byte of Reg32
0000 0010 - load immediate (36) into 2nd byte of Reg32
0001 0000 - add reg & and reg b
0000 1100 - store result of addition into 4th byte of Reg32
1110 0000 - shift bits in Reg32 to reflect actual value of the addition.

// Remember the CPU here is getting a 32bit instruction so all of these 
// byte codes would appear as this in a single 32bit sequence from an 
// instruction register    
// 0x0c100201 this single register contains 4 simultaneous instructions
// This could all possibly be done in one cpu clock cycle, 
// (the relative or conceptual idea, 
// but not necessarily done in practice due to hardware limitations, 
// but can be virtualized) 
// then the next byte code would appear in the 2nd 32 bit register.

// Now imagine this behavior with 64 bit registers. A single 64 bit Register 
// would contain up to 8 byte codes. some byte codes might contain multiple op codes....

Если вы зашли так далеко, читая это, я знаю, что это довольно долго; но я просто пытаюсь дать вам как можно более подробную информацию, охватывающую все основные аспекты дизайна моего класса, чтобы вы могли лучше понять, что я пытаюсь сделать и достичь и почему я ' Я хочу попробовать и сделать что-то определенным образом.

Я ценю то время, которое вы уделили мне, чтобы дать ответ с некоторыми подробными объяснениями. Мне нужно будет потратить некоторое время, поработать над обоими предложениями и проверить некоторые значения, чтобы понять, поможет ли это мне получить правильное поведение, которое я ищу, при создании моих классов.

Ответы [ 3 ]

1 голос
/ 11 мая 2019

Вы можете сделать это с помощью шаблонов. Вот код:

#include <cstdint>
#include <cassert>
#include <type_traits>

template <typename T, typename U>
void getValAtIdx(T val, uint8_t idx, U& res) {
    assert(std::is_integral<T>::value && std::is_integral<U>::value);
    assert((sizeof(val) > sizeof(res)) && (sizeof(val)/sizeof(res) > idx));
    res = (val >> ((sizeof(res) << 3)*idx)) & ((T)-1 >> ((sizeof(val)-sizeof(res)) << 3));
}

Я не делал тщательного тестирования, но я думаю, что логика в порядке.

Следование должно привести к ошибке подтверждения

uint16_t res;
uint64_t val = 0x12345678;
getValAtIdx<uint64_t, uint16_t>(val, 4, res);

тогда

uint16_t res;
uint64_t val = 0x12345678;
getValAtIdx<uint64_t, uint16_t>(val, 1, res);

должен дать вам 0x1234.

1 голос
/ 12 мая 2019

Посидев там и выполняя некоторые математические расчеты вручную, чтобы распознать шаблоны, я смог действительно упростить свой код с помощью нескольких шаблонов функций. Вот то, что я имею до сих пор, и значения, кажется, соответствуют тому, что я ожидаю.


Редактировать:

Я добавил некоторые typedefs в common.h и переместил туда свои шаблоны функций, чтобы упростить читабельность кода. Я удалил магические числа и заменил их константами, и, возможно, я также исправил некоторые условные проверки. Я даже завернул свой код в пространство имен. Я также включу мои предполагаемые классы Register, поскольку они почти завершены, но не буду использовать их в этом файле main.cpp - *


Редактировать

Я нашел еще несколько мест, где смог заменить свои typedef. Что еще более важно, я обнаружил ошибку в своих классах регистрации, когда Я их тестировал. Ошибка относится к порядку, в котором были объявлены type value и bitset<T>. Сначала я объявил bitset<T> первым, так что это было первым, что было инициализировано. Мне пришлось поменять порядок деклараций, и теперь все кажется хорошим. Все основные конструкторы завершены. Теперь нужно написать конструкторы, которые создали бы тип Register из нескольких меньших типов регистра: пример ... Reg32 (Reg8, Reg8, Reg16); Последний набор Конструкторов будет принимать либо меньший тип uint, либо меньший тип Reg вместе со значением индекса, например: Reg64 (Reg32, 0); Это назначит биты в Reg32 в младшие DWord из Reg64 и Reg32 (Reg8 3, Reg8 0); Это назначит битовую последовательность первого Reg8 в старший байт, а вторую - в младший байт Reg32, все биты в середине останутся неизменными по сравнению с их предыдущим значением. *


-Обновленный код-

main.cpp

#include "common.h"
//#include "Register.h" // if you include this you don't need to include common.h

int main() {
    using namespace nesx;

    std::uint16_t v16 = 23990;
    std::cout << "Byte Testing v16 = 23990\n";
    testBytes(v16);

    std::uint32_t v32 = 1801285115;
    std::cout << "Byte Testing v32 = 1801285115\n";
    testBytes(v32);
    std::cout << "Word Testing v32 = 1801285115\n";
    testWords(v32);

    std::uint64_t v64 = 7486836904524374950;
    std::cout << "Byte Testing v64 = 7486836904524374950\n";
    testBytes(v64);
    std::cout << "Word Testing v64 = 7486836904524374950\n";
    testWords(v64); 
    std::cout << "DWord Testing v64 = 7486836904524374950\n";
    testDWords(v64);

    return EXIT_SUCCESS;
}

common.h

#pragma once

#include <algorithm>
#include <bitset>
#include <cassert>
#include <cstdint>
#include <iostream>
#include <memory>
#include <map>
#include <string>
#include <sstream>
#include <vector>

namespace nesx {

    typedef std::int8_t i8;
    typedef std::int16_t i16;
    typedef std::int32_t i32;
    typedef std::int64_t i64;

    typedef std::uint8_t u8;
    typedef std::uint16_t u16;
    typedef std::uint32_t u32;
    typedef std::uint64_t u64;

    const u16 BYTE = 0x08, WORD = 0x10, DWORD = 0x20, QWORD = 0x40;

    typedef std::bitset<BYTE>  Byte;
    typedef std::bitset<WORD>  Word;
    typedef std::bitset<DWORD> DWord;
    typedef std::bitset<QWORD> QWord;

    template<typename T>
    void getByteFrom(T val, u8 idx, u8& res) {
        res = ((val >> (idx * 8) & 0xff));
    }

    template<typename T>
    void getWordFrom(T val, u8 idx, u16& res) {
        res = ((val >> (idx * 16) & 0xffff));
    }

    template<typename T>
    void getDWordFrom(T val, u8 idx, u32& res) {
        res = ((val >> (idx * 32) & 0xffffffff));
    }

    // Direct Byte Alignment No Offsets
    template<typename T>
    void testBytes(T& value) {
        const u16 size = sizeof(T);
        const u16 numBits = size * BYTE;

        // Make sure that T is either a word, dword or qword
        if (numBits < WORD) {
            return;
        }
        if (numBits == WORD) {
            Word wordBits{ value };
            std::cout << "Reference Bits:\n"
                      << "value = " << wordBits.to_ullong() << '\n'
                      << "bits  = " << wordBits << "\n\n";
        }
        if (numBits == DWORD) {
            DWord dwordBits{ value };
            std::cout << "Reference Bits:\n"
                      << "value = " << dwordBits.to_ullong() << '\n'
                      << "bits  = " << dwordBits << "\n\n";
        }
        if (numBits == QWORD) {
            QWord qwordBits{ value };
            std::cout << "Reference Bits:\n"
                      << "value = " << qwordBits.to_ullong() << '\n'
                      << "bits  = " << qwordBits << "\n\n";
        }

        std::vector<u8> bytes;
        std::vector<Byte> byteBits;
        bytes.resize(size, 0);
        byteBits.resize(size, 0);

        // Populate Our Vectors with Data
        for (u8 idx = 0; idx < size; idx++) {
            8 byte = 0;
            getByteFrom(value, idx, byte);
            bytes[idx] = byte;Byte bits{ byte };
            byteBits[idx] = bits;
        }

        // Now loop through and print out the information
        // from the vectors
        for (std::size_t i = 0; i < size; i++) {
            std::cout << "byte[" << i << "] = " << +bytes[i] << '\n';
            std::cout << "bitset (value): " << byteBits[i].to_ullong() << '\n';
            std::cout << "bitset  (bits): " << byteBits[i] << "\n\n";
        }
    }

    // Direct Word Alignment No Offsets
    template<typename T>
    void testWords(T& value) {
        const u16 size = sizeof(T);
        const u16 numBits = size * BYTE;

        // Make sure T is either a dword or a qword
        if (numBits < DWORD) {
            return;
        }

        if (numBits == DWORD) {
            DWord dwordBits{ value };
            std::cout << "Reference Bits:\n"
                      << "value = " << dwordBits.to_ullong() << '\n'
                      << "bits  = " << dwordBits << "\n\n";}

        if (numBits == QWORD) {
            QWord qwordBits{ value };
            std::cout << "Reference Bits:\n"
                      << "value = " << qwordBits.to_ullong() << '\n'
                      << "bits  = " << qwordBits << "\n\n";
        }

        const u16 numWords = size / 2;
        std::vector<u16> words;
        std::vector<Word> wordBits;
        words.resize(numWords, 0);
        wordBits.resize(numWords, 0);

        // Populate Our Vectors with Data
        for (u8 idx = 0; idx < numWords; idx++) {
            u16 word = 0;
            getWordFrom(value, idx, word);
            words[idx] = word;
            Word bits{ word };
            wordBits[idx] = bits;
        }

        // Now loop through and print out the information
        // from the vectors
        for (std::size_t i = 0; i < numWords; i++) {
            std::cout << "word[" << i << "] = " << words[i] << '\n';
                      << "bitset (value): " << wordBits[i].to_ullong(
        << '\n';
            std::cout << "bitset  (bits): " << wordBits[i] << "\n\n";
        }
    }

    // Direct DWord Alignment No Offsets
    template<typename T>
    void testDWords(T& value) {
        const u16 size = sizeof(T);
        const u16 numBits = size * BYTE;

        // Make sure T is a qword
        if (numBits < QWORD) {
            return;
        }

        if (numBits == QWORD) {
            QWord qwordBits{ value };
            std::cout << "Reference Bits:\n"
                      << "value = " << qwordBits.to_ullong() << '\n'
                      << "bits  = " << qwordBits << "\n\n";
        }

        const u16 numDWords = size / 4;
        std::vector<u32> dwords;
        std::vector<DWord> dwordBits;
        dwords.resize(numDWords, 0);
        dwordBits.resize(numDWords, 0);

        // Populate Our Vectors with Data
        for (u8 idx = 0; idx < numDWords; idx++) {
            u32 dword = 0;
            getDWordFrom(value, idx, dword);
            dwords[idx] = dword;
            DWord bits{ dword };
            dwordBits[idx] = bits;
        }

        // Now loop through and print out the information from the vectors
       for (std::size_t i = 0; i < numDWords; i++) {
           std::cout << "dword[" << i << "] = " << dwords[i] << '\n';
           std::cout << "bitset (value): " << dwordBits[i].to_ullong() << '\n';
           std::cout << "bitset  (bits): " << dwordBits[i] << "\n\n";
        }
    }
} // namespace nesx

Register.h

#pragma once

#include "common.h"

namespace nesx {

    template<typename T>
    struct Register {
        T data;
        Register() = default;
    };

    struct Reg8 : public Register<u8> {
        u8 value;  // must be declared before std::bitset<T>
        Byte bits; // otherwise you will not get the proper bit sequence

        // Default 0 Initialized Constructor
        Reg8() : value{ 0 }, bits{ value } { this->data = 0; }

        // Constructors by Register Sized Values
        // Constructor of smaller types that takes larger types,
        // has to be casted by a narrowing convention
        explicit Reg8(u8& val)  : value{ val }, bits{ value } {
            this->data = value;
        }
        explicit Reg8(u16& val) : value{ static_cast<u8>(val) }, bits{ value } {
            this->data = value;
        }
        explicit Reg8(u32& val) : value{ static_cast<u8>(val) }, bits{ value } {
            this->data = value;
        }
        explicit Reg8(u64& val) : value{ static_cast<u8>(val) }, bits{ value } {
            this->data = value;
        }

        Reg8(u16 val, u8 idx ) {
            assert( idx == 0 || idx == 1 );
            getByteFrom(val, idx, this->value);
            bits = value;
            this->data = value;
        }

        Reg8(u32 val, u8 idx) {
            assert(idx <= 0 && idx >= 3);
            getByteFrom(val, idx, this->value);
            bits = value;
            this->data = value;
        }

        Reg8(u64 val, u8 idx) {
            assert(idx <= 0 && idx >= 7);
            getByteFrom(val, idx, this->value);
            bits = value;
            this->data = value;
        }

        // Constructors by Register Types
        template<typename T>
        explicit Reg8(Register<T>* reg) {
            this->value = static_cast<u8>( reg->data );
            this->bits = value;
        }

        template<typename T>
        Reg8(Register<T>* reg, u8 idx) {
            // first we need to know what type T is to determine 
            // how many bytes are in T so that we can assert our
            // index properly for each different type
            u16 size = sizeof(T); // in bytes

            if (size == BYTE)  { /* TODO: */ }
            if (size == WORD)  { /* TODO: */ }
            if (size == DWORD) { /* TODO: */ }
            if (size == QWORD) { /* TODO: */ }
        }
    };

    struct Reg16 : public Register<u16> {
        u16 value;  // Must be declared before std::bitset<t>
        Word bits;  // otherwise you will not get the proper bit sequence

        // Default 0 Initialized Constructor
        Reg16() : value{ 0 }, bits{ value } { this->data = 0; }

        // Constructors by Register Sized Values
        // Constructor of smaller types that takes larger types,
        // has to be casted by a narrowing convention
        explicit Reg16(u16& val) : value{ val }, bits{ value } {
            this->data = value;
        }

        explicit Reg16( u8& val) : value{ val }, bits{ value } {
            this->data = value;
        }

        explicit Reg16(u32& val) : value{ static_cast<u16>(val) }, bits{ value } {
            this->data = value;
        }

        explicit Reg16(u64& val) : value{ static_cast<u16>(val) }, bits{ value } {
            this->data = value;
        }

        // TODO:
        // low is right side, high is left side of the bitset...
        // Reg16( u8& byte0, u8& byte1 ) { ... } // byte0 = low && byte1 = high

        Reg16( u32 val, u8  idx) {
            assert(idx == 0 || idx == 1);
            getWordFrom(val, idx, this->value);
            bits = value;
            this->data = value;
        }

        Reg16(u64 val, u8 idx) {
            assert(idx <= 0 || idx <= 3);
            getWordFrom(val, idx, this->value);
            bits = value;
            this->data = value;
        }

        // Constructors by Register Types
        template<typename T>
        explicit Reg16(Register<T>* reg) {
            this->value = static_cast<u16>(reg->data);
            this->bits = value;
        }
    };

    struct Reg32 : public Register<u32> {
        u32 value;  // must be declared before std::bitset<T>
        DWord bits; // otherwise you will not get the proper bit sequence

        // Default 0 Initialized Constructor
        Reg32() : value{ 0 }, bits{ value } { this->data = 0; }

        // Constructors by Register Sized Values
        // Constructor of smaller types that takes larger types,
        // has to be casted by a narrowing convention
        explicit Reg32(u32& val) : value{ val }, bits{ value } {
            this->data = value;
        }

        explicit Reg32( u8& val) : value{val}, bits{value} {
            this->data = value;
        }

        explicit Reg32(u16& val) : value{val}, bits{value} {
            this->data = value;
        }

        explicit Reg32(u64& val) : value{ static_cast<u32>(val) }, bits{ value } {
            this->data = value;
        }

        // TODO: 
        // low is right side, high is left side of bitset
        // Reg32( u8 byte0, u8 byte1, u8 byte2, u8 byte3 ) { ... } // byte0 = low ... byte3 = high
        // Reg32( u16 word0, word1 ) { ... } // word0 = low  word1 = high

        Reg32(u64 val, u8 idx) {
            assert(idx == 0 || idx == 1);
            getDWordFrom(val, idx, this->value);
            bits = value;
            this->data = value;
        }

        // Constructors by Register Types
        template<typename T>
        explicit Reg32(Register<T>* reg) {
            this->value = static_cast<u32>(reg->data);
            this->bits = value;
        }
    };

    struct Reg64 : public Register<u64> {
        u64 value;  // Must be declared before std::bitset<T>
        QWord bits; // Otherwise you will not get the proper bit sequence

        // Default 0 Initialized Constructor
        Reg64() : value{ 0 }, bits{ value } { this->data = 0; }

        // Constructors by Register Sized Values
        // Constructor of smaller types that takes larger types,
        // has to be casted by a narrowing convention
        explicit Reg64(u64& val) : value{ val }, bits{ value }{
            this->data = value;
        }

        explicit Reg64( u8& val) : value{ static_cast<u64>(val) }, bits{ value } {
            this->data = value;
        }

        explicit Reg64(u16& val) : value{ static_cast<u64>(val) }, bits{ value } {
             this->data = value;
        }

        explicit Reg64(u32& val) : value{ static_cast<u64>(val) }, bits{ value } {
             this->data = value;
        }

        // TODO:
        // low is right side, high is left side of bitset
        // Reg64( u8 b0, u8 b1, u8 b2, u8 b3, u8 b4, u8 b5, u8 b6, u8 b7 ) {...} b0 = low ... b7 = high
        // Reg64( u16 w0, u16 w1, u16 w2, u16, w3 );
        // Reg64( u32 dw0, u32 dw1 );

        // Constructors by Register Types
        template<typename T>
        explicit Reg64(Register<T>* reg) {
             this->value = static_cast<u64>(reg->data);
             this->bits = value;
        }
    };
};

Единственное отличие состоит в том, что я не являюсь asserting в этих шаблонных функциях, но когда я портирую этот код в конструкторы моих классов или структур, я буду тогда утверждать соответствующие значения там.


Вот вывод:

Byte Testing v16 = 23990
Reference Bits:
value = 23990
bits  = 0101110110110110

byte[0] = ╢     // with promoted uchar 182
bitset (value): 182
bitset  (bits): 10110110

byte[1] = ]     // with promoted uchar 93
bitset (value): 93
bitset  (bits): 01011101

Byte Testing v32 = 1801285115
Reference Bits:
value = 1801285115
bits  = 01101011010111010110110111111011

byte[0] = √     // with promoted uchar 251
bitset (value): 251
bitset  (bits): 11111011

byte[1] = m     // with promoted uchar 109
bitset (value): 109
bitset  (bits): 01101101

byte[2] = ]     // with promoted uchar 93
bitset (value): 93
bitset  (bits): 01011101

byte[3] = k     // with promoted uchar 107
bitset (value): 107
bitset  (bits): 01101011

Word Testing v32 = 1801285115
Reference Bits:
value = 1801285115
bits  = 01101011010111010110110111111011

word[0] = 28155
bitset (value): 28155
bitset  (bits): 0110110111111011

word[1] = 27485
bitset (value): 27485
bitset  (bits): 0110101101011101

Byte Testing v64 = 7486836904524374950
Reference Bits:
value = 7486836904524374950
bits  = 0110011111100110100101100111111101101001011101011110001110100110

byte[0] = ª     // with promoted uchar 166
bitset (value): 166
bitset  (bits): 10100110

byte[1] = π     // with promoted uchar 227
bitset (value): 227
bitset  (bits): 11100011

byte[2] = u     // with promoted uchar 117
bitset (value): 117
bitset  (bits): 01110101

byte[3] = I     // with promoted uchar 105
bitset (value): 105
bitset  (bits): 01101001

byte[4] = ⌂     // with promoted uchar 127
bitset (value): 127
bitset  (bits): 01111111

byte[5] = û     // with promoted uchar 150
bitset (value): 150
bitset  (bits): 10010110

byte[6] = µ     // with promoted uchar 230
bitset (value): 230
bitset  (bits): 11100110

byte[7] = g     // with promoted uchar 103
bitset (value): 103
bitset  (bits): 01100111

Word Testing v64 = 7486836904524374950
Reference Bits:
value = 7486836904524374950
bits  = 0110011111100110100101100111111101101001011101011110001110100110

word[0] = 58278
bitset (value): 58278
bitset  (bits): 1110001110100110

word[1] = 26997
bitset (value): 26997
bitset  (bits): 0110100101110101

word[2] = 38527
bitset (value): 38527
bitset  (bits): 1001011001111111

word[3] = 26598
bitset (value): 26598
bitset  (bits): 0110011111100110

DWord Testing v64 = 7486836904524374950
Reference Bits:
value = 7486836904524374950
bits  = 0110011111100110100101100111111101101001011101011110001110100110

dword[0] = 1769333670
bitset (value): 1769333670
bitset  (bits): 01101001011101011110001110100110

dword[1] = 1743165055
bitset (value): 1743165055
bitset  (bits): 01100111111001101001011001111111

Дайте мне знать, что вы думаете!


После замены моего кода на его обновленную версию, вот немного о классе моего Регистра. Вы можете создать любой из типов Регистра: Reg8, Reg16, Reg32 и Reg64 из любого типа uint: u8, u16, u32. & u64 по прямому значению. Вы также можете создать их по указателю или адресу другого типа регистра. Вы также можете выборочно построить их. Под этим я подразумеваю, что вы можете объявить Reg16 как переменную типа. Вы можете передать ему u64 и значение скажем 2 в качестве значения индекса. Этот вид конструктора извлечет 3 слова справа и использует его для создания типа Reg16. Такое поведение может быть сделано от любого из больших типов к меньшему типу. Дайте мне больше времени, и я буду иметь больше функций, включенных в эти типы регистра. Я хотел бы здесь ваши отзывы!

1 голос
/ 11 мая 2019

Я сомневаюсь, понял ли я вопрос. Но если бы я сделал, решение на самом деле легко.

Я начинаю с того, что OP уже имеет:

// valid values for idx[0,1]
void getByteFrom(std::uint16_t val, std::uint8_t idx, std::uint8_t& res) {  
    assert(idx == 0 || idx == 1);
    res = ((val >> (idx << 3)) & 0xff);
}

Мое первое впечатление таково: замена idx * 8 на idx << 3 - это ненужная запутанность кода. Я уверен, что любой серьезный современный компилятор может создать такой же эффективный код для:

// valid values for idx[0,1]
void getByteFrom(std::uint16_t val, std::uint8_t idx, std::uint8_t& res) {  
    assert(idx == 0 || idx == 1);
    res = ((val >> (idx * 8)) & 0xff);
}

Это как начало для getWordFrom():

// valid idx[0,1]
void getWordFrom(std::uint32_t val, std::uint8_t idx, std::uint16_t& res);

Необходимое ограничение idx для диапазона [0, 1] уже упоминалось:

void getWordFrom(std::uint32_t val, std::uint8_t idx, std::uint16_t& res)
{ 
    assert(idx == 0 || idx == 1);

Битовый шаблон для маскирования 16-битного значения будет в двоичном 0b1111111111111111 (двоичное число с 16 1 цифрами). В C ++ нет поддержки двоичных литералов, о которых я знаю. Вместо этого предпочтительны литералы шестнадцатеричных чисел, поскольку одна шестнадцатеричная цифра всегда отражает ровно четыре двоичных числа, потому что 16 1 = 2 4 . (Я предполагаю, что это делает так популярными шестнадцатеричные числа в сообществе «битрейтеров».) Следовательно, битовый шаблон для маскировки 16-битного значения: 0xffff.

Чтобы сдвинуть старшие 16 бит в более низкую позицию, idx нужно умножить на 16.

    res = ((val >> (idx * 16)) & 0xffff);
}

Это было не так сложно ... (ИМХО).

Обратите внимание, что сдвиг вправо (>>) также выполняется для idx == 0, но сдвиг вправо на 0 не меняет значение.

Альтернативная реализация может быть:

    res = (idx != 0 ? val >> (idx * 16) : val) & 0xffff; // NOT BETTER

, который смещается вправо, только если idx != 0. Я действительно сомневаюсь, что это заработает. Я бы всегда предпочел первую форму.

(Такие микрооптимизации обычно не оказывают никакого влияния на общую производительность и на самом деле не заслуживают рассмотрения.)

Пример кода:

#include <cstdint>
#include <bitset>
#include <cassert>
#include <iomanip>
#include <iostream>

using U8 = std::uint8_t;
using U16 = std::uint16_t;
using U32 = std::uint32_t;

// valid values for idx[0,1]
void getByteFrom(U16 val, U8 idx, U8 &res)
{  
    assert(idx == 0 || idx == 1);
    res = ((val >> (idx * 8)) & 0xff);
}

// valid values for idx[0,1]
void getWordFrom(U32 val, U8 idx, U16 &res)
{  
    assert(idx == 0 || idx == 1);
    res = ((val >> (idx * 16)) & 0xffff);
}

// check this out
int main()
{
  {
    U16 value = 13579;
    std::bitset<16> bits{ value };
    std::cout << "Reference Bits:\n"
              << bits.to_ulong()
              << '\n' << bits << "\n\n";

    U8 lowByte = 0, highByte = 0;
    getByteFrom(value, 0, lowByte);
    getByteFrom(value, 1, highByte);

    std::bitset<8> lowBits{ lowByte };
    std::bitset<8> highBits{ highByte };

    std::cout << "lowByte = " << std::setw(2) << std::setfill('0') << std::hex << (unsigned)lowByte << std::dec << '\n';
    std::cout << "lowBits (value): " << lowBits.to_ulong() << '\n';
    std::cout << "lowBits (bits):  " << lowBits << "\n\n";

    std::cout << "highByte = " << std::setw(2) << std::setfill('0') << std::hex << (unsigned)highByte << std::dec << '\n';
    std::cout << "highBits (value): " << highBits.to_ulong() << '\n';
    std::cout << "highBits (bits):  " << highBits << "\n\n";
  }
  {
    U32 value = 135792468;
    std::bitset<32> bits{ value };
    std::cout << "Reference Bits:\n"
              << bits.to_ulong()
              << '\n' << bits << "\n\n";

    U16 lowWord = 0, highWord = 0;
    getWordFrom(value, 0, lowWord);
    getWordFrom(value, 1, highWord);

    std::bitset<16> lowBits{ lowWord };
    std::bitset<16> highBits{ highWord };

    std::cout << "lowWord = " << std::setw(4) << std::setfill('0') << std::hex << lowWord << std::dec << '\n';
    std::cout << "lowBits (value): " << lowBits.to_ulong() << '\n';
    std::cout << "lowBits (bits):  " << lowBits << "\n\n";

    std::cout << "highWord = " << std::setw(4) << std::setfill('0') << std::hex << highWord << std::dec << '\n';
    std::cout << "highBits (value): " << highBits.to_ulong() << '\n';
    std::cout << "highBits (bits):  " << highBits << "\n\n";
  }
}

Выход:

Reference Bits:
13579
0011010100001011

lowByte = 0b
lowBits (value): 11
lowBits (bits):  00001011

highByte = 35
highBits (value): 53
highBits (bits):  00110101

Reference Bits:
135792468
00001000000110000000011101010100

lowWord = 0754
lowBits (value): 1876
lowBits (bits):  0000011101010100

highWord = 0818
highBits (value): 2072
highBits (bits):  0000100000011000

Демонстрация в реальном времени на coliru

...