gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

C/C++ > Strange cast issue

#22505 - sgeos - Wed Jun 23, 2004 4:21 am

Code:
#define VRAM      ((u16*)0x6000000)   // BG Tile VRAM, Tilemaps, Bitmaps
#define TILE16_SIZE   0x20   // 16 color tile
#define SBB_SIZE   0x800   // Screen Base Block Size
#define CBB_SIZE   0x4000   // Character Base Block Size

/***   This Function does not work without a cast
 */
void load_tiles(const void *source, u32 length, int block, int start_tile)
{
   u16 dma_count = length / 4;
   void *destination = (void *)
      ((int)VRAM + block * CBB_SIZE + start_tile * TILE16_SIZE);  // Works
//      (VRAM + block * CBB_SIZE + start_tile * TILE16_SIZE);  // Fails

   if (dma_count < 1)
      dma_count = 1;
   dma_copy(3, source, destination, dma_count, 32 /* bit */);
}

/***   This Function *does work* without a cast
 */
void load_tilemap(const void *source, u32 length, int block, int start_char)
{
   // FIXME: start_char * map_cell_size
   u16 dma_count = length / 4;
   void *destination = (void *)
      (VRAM + block * SBB_SIZE + start_char * TILE16_SIZE);

   if (dma_count < 1)
      dma_count = 1;
   dma_copy(3, source, destination, dma_count, 32 /* bit */);
}
It strikes me as odd that the first function doesn't work without a cast, while the second one does.

-Brendan

#22506 - tepples - Wed Jun 23, 2004 4:42 am

In C, adding an int to a pointer will add the int times the size of what the pointer points to to the pointer. You can get rid of the cast if you express CBB_SIZE and TILE16_SIZE in u16s rather than in bytes, that is, as 0x2000 and 0x10.

Or you can use one of my macros to compute tile addresses:
Code:
/* compute the address of a pattern table aka CHR base block */
#define PATRAM(x) ((u32 *)(0x06000000 | ((x) << 14)))
/* compute the address of a 4-bit tile in VRAM */
#define PATRAM4(x, c) ((u32 *)(0x06000000 | (((x) << 14) + ((c) << 5)) ))
/* compute the address of an 8-bit tile in VRAM */
#define PATRAM8(x, c) ((u32 *)(0x06000000 | (((x) << 14) + ((c) << 6)) ))
/* compute the address of a tile in sprite cel VRAM */
#define SPR_VRAM(x) ((u32 *)(0x06010000 | ((x) << 5)))

typedef u16 NAMETABLE[32][32];
/* compute the address of a nametable aka screen base block; example: MAP[31] */
#define MAP ((NAMETABLE *)0x06000000)


_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#22507 - sgeos - Wed Jun 23, 2004 5:32 am

tepples wrote:
In C, adding an int to a pointer will add the int times the size of what the pointer points...
Right. I forgot about that. The second function was actually broken, but looked fine because I passed zeros to it. Thanks!

-Brendan

#22508 - jma - Wed Jun 23, 2004 5:54 am

You can get around it, of course, by just casting your pointer as a (char*).

Jeff
_________________
massung@gmail.com
http://www.retrobyte.org

#22511 - sgeos - Wed Jun 23, 2004 6:49 am

jma wrote:
You can get around it, of course, by just casting your pointer as a (char*).
I could set the void pointer to VRAM and then add to it. That will probably be a little clearer.

-Brendan

#22528 - sajiimori - Wed Jun 23, 2004 6:21 pm

No, you can't add to a void*.

#22534 - Miked0801 - Wed Jun 23, 2004 9:21 pm

Right, only non-void types have a size associated with them. How big is a void pointer? You should avoid void pointers as much as possible anways as they by pass the weak type cast checking C already uses. The stuff that's still there is there for a reason!

#22540 - sgeos - Thu Jun 24, 2004 12:37 am

sajiimori wrote:
No, you can't add to a void*.
I did. It works. I am fairly sure that a void pointer and a pointer to char are functionally the same, or at least very close. I'd have to post to comp.lang.c or read the standard to be sure.

Miked0801 wrote:
Right, only non-void types have a size associated with them. How big is a void pointer?
The pointer, or the value it pointes to? One is guarnateed to be able to cast any non-void pointer to a void-pointer and back again without loss of information. There no is such guarnatee when dealing when casting between other types of pointers. All pointers on the GBA are going to be 32-bits. A void pointer points to an arbitrary memory address. On the GBA that is going to be an 8-bit unit of data.

Miked0801 wrote:
You should avoid void pointers as much as possible anways as they by pass the weak type cast checking C already uses. The stuff that's still there is there for a reason!
I agree. Uning void pointers recklessly and for no reason is not a good idea. Only when one needs a pointer to an arbitrary memory address, should a void pointer be used.

My DMA function accepts void pointers, as I believe it should. You are right in that the functions that use DMA should not use void pointers unless they have a reason to do so. In that case, which of these is easer to read and understand?
Code:
void *destination = (void *)VRAM;
destination += block * CBB_SIZE + start_tile * TILE16_SIZE;

u32 *destination = PATRAM4(block, start_tile);


Activate Super Parse Mode here. If I need to have a function return a function pointer to a function with the same parameter types and return types, I return a void pointer. /Super Parse Mode. I believe that this problem can also be solved by typedef'ing the function pointer, although I have never tried that.

C is a language that lets the programmer do whatever he wants to do if he knows what he is doing. C also allows a programmer to do whatever he thinks he wants to do if he does not know what he is doing. If you know what you are doing, you do what it takes to shut off the warnings.

A void pointer could be used as a single data spot for ISRs to get "parameters". The interrupt setup routine sets the pointer, loads the ISR(s) and then enables interrupts. The ISR takes the void pointer and casts it to a useful type.

-Brendan

#22542 - sgeos - Thu Jun 24, 2004 12:48 am

tepples wrote:
Code:
/* compute the address of a 4-bit tile in VRAM */
#define PATRAM4(x, c) ((u32 *)(0x06000000 | (((x) << 14) + ((c) << 5)) ))
/* compute the address of an 8-bit tile in VRAM */
#define PATRAM8(x, c) ((u32 *)(0x06000000 | (((x) << 14) + ((c) << 6)) ))
It took me a while to figure out exactly what is 4 and 8 bits. (The number of bits used to represent a pixel.) I suggest noting that those are 16 color and 256 color tile entries.

-Brendan

#22562 - sajiimori - Thu Jun 24, 2004 6:18 am

I just discovered that arithmetic on void pointers is a GCC extension. ANSI C disallows it, as does GCC when compiling as C++.
Quote:

One is guarnateed to be able to cast any non-void pointer to a void-pointer and back again without loss of information. There no is such guarnatee when dealing when casting between other types of pointers.

Yes there is, unless you're referring to casting overloads in C++.
Quote:

All pointers on the GBA are going to be 32-bits. A void pointer points to an arbitrary memory address. On the GBA that is going to be an 8-bit unit of data.

Even GCC (with all its extensions) disagrees with you here. Void pointers point to data of no particular size. If you don't believe me, try dereferencing one -- GCC refuses to return data of no particular size. Unfortunately, this is inconsistent with the arithmetic extension which treats void pointers like char pointers.
Quote:

Only when one needs a pointer to an arbitrary memory address, should a void pointer be used.

All pointers can point to arbitrary memory addresses.
Quote:

Activate Super Parse Mode here. If I need to have a function return a function pointer to a function with the same parameter types and return types, I return a void pointer. /Super Parse Mode. I believe that this problem can also be solved by typedef'ing the function pointer, although I have never tried that.

You neither need to return a void pointer or typedef the function type. You can return a pointer of any function type at will, by casting if necessary. However, the syntax is hairy.
Quote:

If you know what you are doing, you do what it takes to shut off the warnings.

Do you mean fixing the things that caused warnings, or disabling warnings in the compiler? I can agree with the former, but not the latter. Warnings are very helpful, especially when you're trying to write something portable (because other compilers might not be so generous).

#22564 - sgeos - Thu Jun 24, 2004 10:08 am

sajiimori wrote:
I just discovered that arithmetic on void pointers is a GCC extension. ANSI C disallows it, as does GCC when compiling as C++.
Fair enough. gcc gives me a "warning: wrong type argument to increment".

Quote:
Yes there is, unless you're referring to casting overloads in C++.
I was specifically talking about C.

sajiimori wrote:
Quote:
A void pointer points to an arbitrary memory address. On the GBA that is going to be an 8-bit unit of data.
Even GCC (with all its extensions) disagrees with you here. Void pointers point to data of no particular size. If you don't believe me, try dereferencing one -- GCC refuses to return data of no particular size. Unfortunately, this is inconsistent with the arithmetic extension which treats void pointers like char pointers.
You are right. A void pointer contains an arbitrary memory address. Without a way of interperting that address, the data is of no particular size. You may find this interesting:
Code:
#include <stdio.h>

int main(void)
{
        char *hello = "Hello";
        void *world = "World";

        printf("%s %s!\n", hello, world);
        return 0;
}

$ gcc -Wall -ansi -pedantic -o vptest vptest.c
vptest.c: In function `main':
vptest.c:8: warning: char format, void arg (arg 3)

$ ./vptest.exe
Hello World!

$
I'm assuming that world is implicitly cast to a pointer to char. The initialization does not issue a warning.

Quote:
Quote:
Only when one needs a pointer to an arbitrary memory address, should a void pointer be used.
All pointers can point to arbitrary memory addresses.
Fair enough. A void pointer should be used only when one does not know or care what the pointer points to? (At least in "this" body of code.)

Quote:
You neither need to return a void pointer or typedef the function type. You can return a pointer of any function type at will, by casting if necessary. However, the syntax is hairy.
Please provide an example.

Quote:
Quote:
If you know what you are doing, you do what it takes to shut off the warnings.

Do you mean fixing the things that caused warnings, or disabling warnings in the compiler? I can agree with the former, but not the latter. Warnings are very helpful, especially when you're trying to write something portable (because other compilers might not be so generous).
The former. The latter only requires knowing a little about your compiler.

-Brendan