Как печатать каламбур в Си - PullRequest
0 голосов
/ 29 марта 2019

Продолжение расширенного обсуждения в Кастинг в C

Я пытаюсь эмулировать Z80 в C, где несколько 8-битных регистров могут быть объединены для создания 16-битных регистров.

Вот та логика, которую я пытаюсь использовать:

struct {
    uint8_t b;
    uint8_t c;
    uint16_t *bc;
} regs[1];
...
regs->bc = (uint16_t *)&(regs->b);

Почему это неверно, и как я могу сделать это правильно (используя при необходимости ввод типов)?

Мне нужно сделать это несколько раз, предпочтительно внутри одной структуры.

Для тех из вас, кому я не упомянул это: Я понимаю, что это предполагает архитектуру с прямым порядком байтов. Я полностью справился с этим.

Ответы [ 3 ]

7 голосов
/ 29 марта 2019

Это неверно, потому что b имеет тип uint8_t и указатель на uint16_t не может использоваться для доступа к такой переменной. Это может быть неправильно выровнено, и это строгое нарушение псевдонимов .

Вы можете делать (uint8_t *)&regs или (struct reg_t*)&regs->b, поскольку (6.7.2.1/15)

Указатель на объект структуры, соответствующим образом преобразованный, указывает на его начальный элемент и наоборот.


При выполнении аппаратного программирования никогда не используйте подписанные типы. Это означает изменение intn_t на uintn_t.

Что касается правильного ввода слов, используйте объединение:

typedef union
{
  struct                 /* standard C anonymous struct */
  {
    uint8_t b;
    uint8_t c;
  };
  uint16_t bc;
} reg_t;

Затем вы можете назначить это для указания на 16-битный аппаратный регистр, например так:

volatile reg_t* reg = (volatile reg_t*)0x1234;

где 0x1234 - адрес аппаратного регистра.

ПРИМЕЧАНИЕ: это объединение зависит от порядка байтов. b будет иметь доступ к байту MS bc в системах с прямым порядком байтов, но байт LS bc в системах с прямым порядком байтов.

3 голосов
/ 29 марта 2019

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

struct {
    uint16_t bc;
    uint8_t *b;
    uint8_t *c;
} regs[1];

regs->b = (uint8_t *) &(regs->bc);
regs->c = regs->b + 1

Интересно, что он все еще действителен для компилятора C ++ ...

3 голосов
/ 29 марта 2019

Чтобы эмулировать аппаратный регистр, к которому можно получить доступ как два восьмибитных регистра или один 16-битный регистр, вы можете использовать:

union
{
    struct { int8_t b, c; };
    int16_t bc;
} regs[1];

Тогда regs->bc будет 16-разрядным регистром, а regs->b и regs->c будут 8-разрядными регистрами.

Примечание. При этом используется анонимный struct, поэтому b и c выглядят так, как если бы они были членами союза. Если у struct было имя, например:

union
{
    struct { int8_t b, c; } s;
    int16_t bc;
} regs[1];

тогда вам нужно будет включить его имя при доступе к b или c, как с regs->s.b. Однако в C есть функция, которая позволяет вам использовать для этой цели объявление без имени.

Также обратите внимание, что для этого требуется компилятор Си. C позволяет использовать объединения для переинтерпретации данных. C ++ имеет другие правила.

...