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 байтов.)

Другие вопросы по тегам