Home > Software engineering >  How do I deal with negative numbers in C?
How do I deal with negative numbers in C?

Time:11-27

I'm trying to code a very basic Assembler language in C. Each instruction has 32 bits, the first 8 being the Opcode and the following 24 containing an immediate value (if the instruction comes with one. Else it's just zeros). The Opcodes are defined as simple numbers. For example PUSHC is defined as 1.

Now I want to test if the Opcode and the immediate value are properly separable.

I wrote the following defintions:

#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)

In a method void runProg(uint32_t prog[]) {...}, I pass an Array that contains the instructions for an Assembler program, in order, separated by commas, like such:

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

Here's what runProg does:

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

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

}

So it prints out the full command as a hexidecimal, followed by just the Opcode, followed by just the immediate value. This works for all instructions I have defined. The test program above gives this output:

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]

Now here's the problem:

The command PUSHC only works with positive values. Changing the immediate value of the first PUSHC call to -3 produces this result:

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]

So as you can see, the immediate value is displayed correctly, however the command is missing the Opcode.

Changing the definition of IMMEDIATE(x) from

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

to

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

produces the exact opposite result, where the Opcode is correctly separated, but the immediate value is wrong:

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]

Therefore, I am relatively sure that my definition of SIGN_EXTEND(i) is flawed. However, I can't seem to put my finger on it.

Any ideas on how to fix this are greatly appreciated!

CodePudding user response:

0x01fffffd is correct; the 8-bit operation code field contains 0x01, and the 24-bit immediate field contains 0xfffffd, which is the 24-bit two’s complement encoding of −3. When encoding, there is no need for sign extension; the IMMEDIATE macro is passed a (presumably) 32-bit two’s complement value, and its job is merely to reduce it to 24 bits, not to extend it. So it merely needs to be #define IMMEDIATE(x) ((x) & 0xffffff). When you want to interpret the immediate field, as for printing, then you need to convert from 24 bits to 32. For that, you need a different macro, the way you have different macros to encode an operation code (OPI) and to decode/extract/interpret the operation code (OPCODE).

You can interpret the immediate field with this function:

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);
}

CodePudding user response:

You're right in thinking that SIGN_EXTEND is flawed. I suggest you check on the precedence of the ? ternary and & binary and operators, and add a couple more parentheses to be more explicit of what you want to put into the conditional of the ?.

  •  Tags:  
  • c
  • Related