C - побитовая конкатенация, приводящая к отсутствию информации
У меня есть пять переменных короткого типа, которые я хочу объединить в 32-битный тип unsigned int. Мне нужно объединить пять коротких переменных. Имена этих переменных называются кодом операции (5 бит), reg1(4 бита), reg2(4 бита), reg3(4 бита), расширением (3 бита) и addr_constant(12 бит). Теперь мой код не работает для одного случая, который я не знаю почему. Я перечислил мой код ниже.
Цель этого кода - преобразовать определенные значения в 32-битную машинную инструкцию, что означает, что, несмотря на то, что я получил эквивалентное значение, мне все равно нужно иметь 32-битную инструкцию.
...
unsigned int *const word;
unsigned short opcode = 1;
unsigned short reg1 = 3;
unsigned short reg2 = 4;
unsigned short reg3 = 5;
unsigned short extension = 0;
unsigned int addr_constant = 0;
unsigned int machine_word = 0;
machine_word = machine_word | (opcode << 27);
machine_word = machine_word | (reg1 << 23);
machine_word = machine_word | (reg2 << 19);
machine_word = machine_word | (reg3 << 15);
machine_word = machine_word | (extension << 12);
machine_word = machine_word | addr_constant;
*word = machine_word
return 0;
...
Вывод в двоичном виде должен быть:
0000 1001 1010 0010 1000 0000 0000 0000.
Но сейчас это:
1001 1010 0010 1000 0000 0000 0000.
Как видите, он пропускает первые 4 нуля.
В следующем тесте "слово": unsigned int *const word. И в конце кода выше я написал "*word = machine_word". В тесте он сравнивает: "word == 0x09a28000" Я не прошел следующий тест.
assert(word == 0x09a28000);
2 ответа
Возможно, проблема заключается только в интерпретации результатов. Я запустил следующий код, который основан на алгоритме, который вы предоставили с некоторыми изменениями в логических операциях и печати результатов. Основная логика операции остается неизменной по сравнению с тем, что вы опубликовали, поэтому, возможно, результат неверен.
Код следующий:
#include <stdio.h>
int main() {
int i, j, mask;
unsigned short opcode = 1;
unsigned short reg1 = 3;
unsigned short reg2 = 4;
unsigned short reg3 = 5;
unsigned short extension = 0;
unsigned int addr_constant = 0;
unsigned int machine_word = 0;
machine_word |= opcode << 27;
machine_word |= reg1 << 23;
machine_word |= reg2 << 19;
machine_word |= reg3 << 15;
machine_word |= extension << 12;
machine_word |= addr_constant;
for (i = 7; i >= 0; i--) {
for (j = 3; j >= 0; j--){
printf("%d", (machine_word & 0x00000001 << (4 * i + j)) >> (4 * i + j));
}
printf(" ");
}
printf("\n");
return 0;
}
Код дает следующий вывод:
0000 1001 1010 0010 1000 0000 0000 0000
Это должен быть результат, который вы ищете, он соответствует целочисленному значению без знака 161644544
(0x9A28000
).
Просто используйте битовые поля - они предназначены для этого.
struct all_the_things {
unsigned opcode : 5;
unsigned reg1 : 4;
unsigned reg2 : 4;
unsigned reg3 : 4;
unsigned extension : 3;
unsigned addr_constant : 12;
};
Заполнив такую структуру, вы можете преобразовать ее в 32-разрядное целое число, например так:
uint32_t num;
memcpy(&num, &things, 4);
(Не беспокойтесь, оптимизирующий компилятор не будет вызывать функцию для копирования 4 байтов.)