3

I'm developing a gameboy game in GBDK but I've got a problem with right-shifting (8bit) unsigned variables. The code looks like this.

#include <gb/gb.h>
#include <stdio.h>

#define GAME_OBJ_MAX_WIDTH 10
#define GAME_OBJ_MAX_HEIGHT 8

struct game_obj
{
    UBYTE matrix[GAME_OBJ_MAX_WIDTH];
    UBYTE width, height;
};

void draw_game_obj(struct game_obj *object)
{
    for (unsigned char i = 0; i < object->width && i < GAME_OBJ_MAX_WIDTH; i++)
   {
       printf("BASE->%d\n", object->matrix[i]);
       for (unsigned char j = 0; j < object->height && j < GAME_OBJ_MAX_HEIGHT; j++)
           printf("Shift by %d->%u\n", j, object->matrix[i] >> j);
   }
}

void main() {
    struct game_obj racer;
    racer.height = 4;
    racer.width = 3;
    racer.matrix[0] = 10;
    racer.matrix[1] = 7;
    racer.matrix[2] = 10;
    draw_game_obj(&racer);
}

The output is:

BASE->10
Shift by 0->235
Shift by 1->117
Shift by 2->58
Shift by 3->29
BASE->7
Shift by 0->235
Shift by 1->117
Shift by 2->58
Shift by 3->29
BASE->10
Shift by 0->235
Shift by 1->117
Shift by 2->58
Shift by 3->29

Basically any unsigned value right shifted (even by 0) changes to 235, 117, 58 and so on... I'm trying to understand why is that.

Solution

Issue fixed by assigning object->matrix[i] to separate variable.

void draw_game_obj(struct game_obj *object)
{
    for (unsigned char i = 0; i < object->width && i < GAME_OBJ_MAX_WIDTH; i++)
   {
       printf("BASE->%d\n", object->matrix[i]);
       UBYTE u = object->matrix[i];
       for (unsigned char j = 0; j < object->height && j < GAME_OBJ_MAX_HEIGHT; j++)
           printf("Shift by %d->%u\n", j, u >> j);
   }
}
Timax
  • 31
  • 3
  • The problem is that initial value changes to exactly 235 but I do agree that giving further values was unnecessary. – Timax Apr 22 '22 at 20:44
  • Did you include a proper declaration for `printf()` in this module? – chqrlie Apr 22 '22 at 20:45
  • This is not `printf()` issue. I used `printf()` for demonstration purposes only. – Timax Apr 22 '22 at 20:54
  • Edit the question to provide a [mre]. Most likely, something else is wrong in your program; memory was corrupted or whatever, and it is spilling over into something that damages the code currently shown in the question. – Eric Postpischil Apr 22 '22 at 21:00
  • Added full code. – Timax Apr 22 '22 at 21:15
  • 1
    And does the problem reproduce with just that code shown and no other? With the code now in the question, replacing `#include ` with `typedef unsigned char UBYTE;` and `void main` with `int main` and running in an ordinary C implementation does not reproduce the output; the expected results are printed (10, 5, 2, 1 for the 10 values and 7, 3, 1, 0 for the 7 value). If that exact code is not working in your GBDK, then there is something wrong in your GBDK. – Eric Postpischil Apr 22 '22 at 21:17
  • Yes, this is the exact code that generates this "error". I also tried ordinary C implementation (problem did not occur) and looked up in docs but did not found anything. What's funny, the left shift does work properly. – Timax Apr 22 '22 at 21:22
  • 2
    @Timax Suggest changing print to `unsigned u = object->matrix[i]; u >> j; printf("Shift by %d->%u\n", j, u);`. I suspect `object->matrix[i] >> j` as an `...` argument is mis-compiling. – chux - Reinstate Monica Apr 22 '22 at 21:38
  • 2
    Note that technically `printf("Shift by %d->%u\n", j, object->matrix[i] >> j);` may be undefined behavior as `object->matrix[i] >> j` in an `int`. The print format for `int` is `"%d"`. Usually when the value is common as `unsigned/int`, there is no issue. – chux - Reinstate Monica Apr 22 '22 at 21:47
  • Yup, thats it! Thank you very much! – Timax Apr 22 '22 at 21:51
  • @Timax Which comment fixed it for you? – chux - Reinstate Monica Apr 23 '22 at 00:39
  • 1
    @Timax: you changed the conversion format from `%d` to `%u` between the initial post and the current code, did this cause the problem? Did reverting to `%d` fix the problem? This is such an **unlikely** explanation that we want to document a system with this behavior. What is the host system and `gbdk` tool chain version? – chqrlie Apr 23 '22 at 11:32
  • 1
    Assigning the `object->matrix[i]` to separate variable and performing right shift on it fixed the issue. The conversion format did not change anything in program's behaviour (retested with `%d` instead of `%u`). The host system is M1 MacBook Air 2020 with MacOs 12.3.1. GBDK is version 4.0.6. – Timax Apr 23 '22 at 16:13
  • Can you determine the exact version of the compiler (SDCC?) that's being used? Can you post the assembly emitted by the compiler? This starts to sound like a compiler bug, as assigning to a different variable shouldn't change anything. – Nate Eldredge Apr 23 '22 at 19:07
  • @chux-ReinstateMonica: The `%d` vs `%u` question came up recently in another comment thread, and someone pointed me to C17 6.2.5 (9) which appears to guarantee that a positive `int` has the same representation as the corresponding `unsigned int` and therefore may be passed wherever `unsigned` is expected. So I don't think it's actually UB. And in any case it does not appear to have been the issue. – Nate Eldredge Apr 23 '22 at 19:11
  • @NateEldredge Same encoding for positive values is not enough to avoid UB as the passing mechanism may differ - even though unlikely with `"%d"` vs. `"%u"`. IAC, 1) my comment already alluded to that with "_may_ be undefined behavior" and "Usually when the value is common as unsigned/int, there is no issue" 2) _something_ is amiss in OP's code. Useful to eliminate the [usual suspects](https://www.youtube.com/watch?v=vtSmfws0_To). – chux - Reinstate Monica Apr 23 '22 at 19:22
  • @Timax Good you have a fixed, yet I think your are coding around a compiler bug. – chux - Reinstate Monica Apr 23 '22 at 19:25
  • @chux-ReinstateMonica: Well, the footnote says "The same representation and alignment requirements are meant to imply interchangeability as arguments to functions [...]". I know it isn't normative, but an implementation that used different passing mechanisms for `signed int` and `unsigned int` would appear to violate at least the spirit of the standard. – Nate Eldredge Apr 23 '22 at 19:31
  • @NateEldredge What is needed is a good question/answer to address this - perhaps one exist? – chux - Reinstate Monica Apr 23 '22 at 20:44
  • @NateEldredge `sdcc -v` gives `SDCC : z80/gbz80 4.1.6 #12539 (Mac OS X x86_64)` (so it must be running under rosetta in my case). If you mean assembly source code, here it is https://pastebin.com/xx3Di4UJ (this is without the fix). – Timax Apr 23 '22 at 20:45

0 Answers0