Безопасно преобразуйте 2 байта в короткие

Я делаю эмулятор для Intel 8080. Один из кодов операций требует 16-битного адреса путем объединения регистров b и c (оба 1 байт). У меня есть структура с регистрами, расположенными рядом друг с другом. Я объединяю два регистра следующим образом:

using byte = char;

struct {

    ... code
    byte b;
    byte c;
    ... code

} state;

...somewhere in code    

// memory is an array of byte with a size of 65535
memory[*reinterpret_cast<short*>(&state.b)]

Я думал, что могу просто OR их вместе, но это не работает.

short address = state.b | state.c

Еще один способ, которым я пытался это сделать, - создать шорт и установить 2 байта по отдельности.

short address;
*reinterpret_cast<byte*>(&address) = state.b;
*(reinterpret_cast<byte*>(&address) + 1) = state.c;

Есть ли лучший/безопасный способ добиться того, что я пытаюсь сделать?


person Greg M    schedule 12.05.2016    source источник


Ответы (4)


short j;
j = state.b;
j <<= 8;
j |= state.c;

Поменяйте местами state.b и state.c, если вам нужен противоположный порядок байтов.

person David Schwartz    schedule 12.05.2016
comment
Это может вызвать неопределенное поведение - person M.M; 12.05.2016
comment
@ М.М Как так? Вы имеете в виду, потому что short подписано? - person David Schwartz; 12.05.2016

Как уже упоминалось, есть проблемы с порядком байтов, но вы также можете использовать союз для управления памятью без необходимости какого-либо смещения.

Пример кода

#include <cstdint>
#include <iostream>

using byte = std::uint8_t;

struct Regs
{
    union
    {
        std::uint16_t bc;

        struct
        {
            // The order of these bytes matters
            byte c;
            byte b;
        };
    };
};

int main()
{
    Regs regs;

    regs.b = 1; // 0000 0001
    regs.c = 7; // 0000 0111

    // Read these vertically to know the value associated with each bit
    //
    //                             2 1
    //                             5 2631
    //                             6 8426 8421
    //
    // The overall binary: 0000 0001 0000 0111
    //
    // 256 + 4 + 2 + 1 = 263

    std::cout << regs.bc << "\n";

    return 0;
}

Пример вывода

263

Живой пример

person James Adkison    schedule 12.05.2016

Вы можете использовать:

unsigned short address = state.b * 0x100u + state.c;

Использование умножения вместо сдвига позволяет избежать всех проблем, связанных со сдвигом знакового бита и т. д.

address должно быть unsigned, иначе вы вызовете назначение вне диапазона, и, вероятно, вы все равно захотите использовать диапазон адресов от 0 до 65535 вместо -32768 до 32767.

person M.M    schedule 12.05.2016

short address = ((unsigned short)state.b << 8) | (unsigned char)state.c;

Это портативный способ. По-вашему, с reinterpret_cast не так уж и страшно, если вы понимаете, что он будет работать только на архитектуре с правильным порядком байтов.

person Sam Varshavchik    schedule 12.05.2016
comment
Проблемы с выравниванием более серьезны, чем проблемы с порядком байтов. - person David Schwartz; 12.05.2016
comment
Что ж, short будет правильно выровнено, поэтому я не вижу в этом проблемы. - person Sam Varshavchik; 12.05.2016
comment
Я отвечаю на ваше последнее предложение, которое все равно что сказать, что можно переходить улицу, не глядя по сторонам, потому что вас вряд ли собьет самолет. Вы указали ему на незначительную проблему, игнорируя большую. - person David Schwartz; 12.05.2016
comment
Метод reinterpret_cast‹короткий *› является UB из-за нарушения строгого правила алиасинга - person M.M; 12.05.2016