В настоящее время я могу извлечь младший или старший байт из 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....
Если вы зашли так далеко, читая это, я знаю, что это довольно долго; но я просто пытаюсь дать вам как можно более подробную информацию, охватывающую все основные аспекты дизайна моего класса, чтобы вы могли лучше понять, что я пытаюсь сделать и достичь и почему я ' Я хочу попробовать и сделать что-то определенным образом.
Я ценю то время, которое вы уделили мне, чтобы дать ответ с некоторыми подробными объяснениями. Мне нужно будет потратить некоторое время, поработать над обоими предложениями и проверить некоторые значения, чтобы понять, поможет ли это мне получить правильное поведение, которое я ищу, при создании моих классов.