2

Problem

I'm working on a video game emulator using C++.

The memory is represented by an array of uint8_t values. Often, I also need to access the contents of this memory as 16-bit values (two consecutive 8-bit values).

I have something that looks like this:

struct Memory {

  uint8_t map[0xFFFF];

  // Let's say that [] is for accessing 8-bit values
  uint8_t& operator[](uint16_t address) {
      return map[address];
  }

 // () is for 16-bit values
  uint16_t& operator()(uint16_t address) {
      return ???; // Looking for a good implementation here
  }
};

For instance, if the memory contains [0xC0, 0xFF, 0x66, 0xAA...] then [] would return:

mem[0] -> 0xC0 mem[1] -> 0xFF mem[2] -> 0x66

and () would return (depending on the system endianness):

mem(0) -> 0xFFC0 mem(1) -> 0x66FF mem(2) -> 0xAA66

Those access methods are going to get called a lot. I would like to leverage pointers in () for fast access. I don't want to compute 16-bit values by shifting and |'ing 8-bit pairs (and I cannot as the method must return a reference).

My question: Is there a way in C++ to have different views on the same buffer? A 8-bit view and a 16-bit view pointing at the same data would be ideal.

(For example, the JS ArrayBuffer API provides DataView, which can do just that. Maybe there is a clever trick to achieve that cleanly with pointers in C++?)

Attempt #1

My first guess was to use some sort of union:

union mem_t {
    uint8_t bytes[0xFFFF];
    uint16_t words[0x8000]
}

But then only 16-bit values at even bytes can be accessed:

mem.words[0] -> 0xFFC0

mem.words[1] -> 0xAA66 // Should be 0x66FF :(

Attempt #2

A working solution is to have two additional 16-bit pointers for even and odd addresses, to handle overlapping:

uint16_t* even16 = (uint16_t*) map;
uint16_t* odd16 = (uint16_t*) (map + 1);

uint16_t& Memory::operator()(uint16_t address) {
    return address % 2 ? odd16[address / 2] : even16[address / 2];
}

It works fine but it seems very convoluted and I'm sure there are more elegant solutions.

Thanks.

merwaaan
  • 123
  • 3
  • Are you looking for `return *(uint16_t*)&map[address];` ? You are making it way too complicated. – Igor Tandetnik Aug 08 '15 at 18:57
  • Thank you for the suggestion Igor, this works fine. However, I'm still interested in a "lazier" approach without a cast at each access. – merwaaan Aug 08 '15 at 19:08
  • Then cast it one time and save it into a pointer... – deviantfan Aug 08 '15 at 19:12
  • 1
    You make it sound as if you've measured performance and determined that the cast is a performance bottleneck. Which would be highly unlikely as the cast is usually purely for the benefit of the compiler - it doesn't correspond to any actual machine code executed at run time. – Igor Tandetnik Aug 08 '15 at 19:13
  • Igor: Thanks for claryfiyng this. I'll go with your approach. deviantfan: such a cached pointer cannot access 16-bit data starting at all addresses (only those with the same parity as the address of the first access would be acessible, with an offset). – merwaaan Aug 08 '15 at 20:02

1 Answers1

0

something like this, but you may fall foul of memory alignment rules on your host system:

struct Memory {

    uint8_t map[0xFFFF];

    // Let's say that [] is for accessing 8-bit values
    uint8_t& operator[](uint16_t address) {
        return map[address];
    }

    // () is for 16-bit values
    uint16_t& operator()(uint16_t address) {
        return *reinterpret_cast<uint16_t*>(&map[address]);
    }
};
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
  • Thank you for the suggestion. How does this reinterpret_cast compares to Igor's "raw" cast? – merwaaan Aug 08 '15 at 19:11
  • @merwaaan Practically, it´s the same. It´s just that C has only this "parenthesis cast" `(type)` and C++ has three additional names for it to express what the reason for the cast is (and a really new, fourth type of cast too, but not important here) – deviantfan Aug 08 '15 at 19:17
  • it's almost exactly the same, but a little more explicit. It is conceptually "safer" because it does not cast away constness – Richard Hodges Aug 08 '15 at 19:18