Как мне работать с отрицательными числами в C?

Я пытаюсь закодировать очень простой язык ассемблера на C. Каждая инструкция имеет 32 бита, первые 8 из которых являются кодом операции, а следующие 24 содержат непосредственное значение (если инструкция содержит единицу. В противном случае это просто нули). Коды операций определяются как простые числа. Например, PUSHC определяется как 1.

Теперь я хочу проверить, правильно ли разделены код операции и непосредственное значение.

Я написал следующие определения:

#define SIGN_EXTEND(i) ((i) & 0x00800000 ? (i) | 0xFF000000 : (i))      // Handles negative Immediate Values (i)
#define IMMEDIATE(x) SIGN_EXTEND(x & 0x00FFFFFF)                        // Returns the Immediate Value of an instruction (x)
#define OPCODE(x) (x >> 24)                                             // Returns the Opcode of an instruction (x)
#define OP(o) ((o) << 24)                                               // An instruction with an Opcode (o)
#define OPI(o, i) (OP(o) | IMMEDIATE(i))                                // An instruction with an Opcode (o) and an Immediate Value (i)

В методе void runProg(uint32_t prog[]) {...} я передаю массив, содержащий инструкции для программы на ассемблере, по порядку, разделенные запятыми, например:

uint32_t progTest[] = {OPI(PUSHC, 3), OPI(PUSHC, 4}, OP(ADD), OP(HALT)};
runProg(progTest);

Вот что делает runProg:

void runProg(uint32_t prog[]) {
    int pc = -1;            // the program counter
    uint32_t instruction;

    do {
        pc++;
        instruction = prog[pc];
        printf("Command %d : 0x%08x -> Opcode [%d] Immediate [%d]\n",
               pc, instruction, OPCODE(instruction), IMMEDIATE(instruction));
    } while (prog[pc] != OP(HALT));

}

Таким образом, он выводит полную команду в виде шестнадцатеричного числа, за которым следует только код операции, за которым следует только непосредственное значение. Это работает для всех инструкций, которые я определил. Вышеприведенная тестовая программа дает следующий результат:

Command 0 : 0x01000003 -> Opcode [1] Immediate [3]
Command 1 : 0x01000004 -> Opcode [1] Immediate [4]
Command 2 : 0x02000000 -> Opcode [2] Immediate [0]
Command 3 : 0x00000000 -> Opcode [0] Immediate [0]

Теперь вот проблема:

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

Command 0 : 0xfffffffd -> Opcode [255] Immediate [-3]
Command 1 : 0x01000004 -> Opcode [1] Immediate [4]
Command 2 : 0x02000000 -> Opcode [2] Immediate [0]
Command 3 : 0x00000000 -> Opcode [0] Immediate [0]

Как видите, непосредственное значение отображается правильно, однако в команде отсутствует код операции.

Изменение определения IMMEDIATE(x) от

#define IMMEDIATE(x) SIGN_EXTEND(x & 0x00FFFFFF)

к

#define IMMEDIATE(x) (SIGN_EXTEND(x) & 0x00FFFFFF)

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

Command 0 : 0x01fffffd -> Opcode [1] Immediate [16777213]
Command 1 : 0x01000004 -> Opcode [1] Immediate [4]
Command 2 : 0x02000000 -> Opcode [2] Immediate [0]
Command 3 : 0x00000000 -> Opcode [0] Immediate [0]

Поэтому я относительно уверен, что мое определение SIGN_EXTEND(i) ошибочно. Тем не менее, я не могу указать на это пальцем.

Любые идеи о том, как это исправить, приветствуются!

OPI затирает код операции отрицательными немедленными значениями

stark 26.11.2022 14:18
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

0x01fffffd правильно; 8-битное поле кода операции содержит 0x01, а 24-битное непосредственное поле содержит 0xffffffd, что является 24-битным дополнительным кодом до двух для -3. При кодировании нет необходимости в расширении знака; макросу IMMEDIATE передается (предположительно) 32-битное значение дополнения до двух, и его задача — просто уменьшить его до 24 бит, а не расширить. Так что это просто должно быть #define IMMEDIATE(x) ((x) & 0xffffff). Когда вы хотите интерпретировать непосредственное поле, как для печати, вам нужно преобразовать из 24 бит в 32. Для этого вам нужен другой макрос, как у вас есть разные макросы для кодирования кода операции (OPI) и для декодирования /извлечь/интерпретировать код операции (OPCODE).

Вы можете интерпретировать непосредственное поле с помощью этой функции:

static int InterpretImmediate(unsigned x)
{
    //  Extract the low 24 bits.
    x &= (1u<<24) - 1;

    /*  Flip the sign bit.  If the sign bit is 0, this
        adds 2**23.  If the sign bit is 1, this subtracts 2**23.
    */
    x ^= 1u<<23;

    /*  Convert to int and subtract 2**23.  If the sign bit started as 0, this
        negates the 2**23 we added above.  If it started as 1, this results in
        a total subtraction of 2**24, which produces the two’s complement of a
        24-bit encoding.
    */
    return (int) x - (1u<<23);
}

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