GP32 Reading A Long


pea

developer
Joined
Oct 3, 2004
Messages
1,089
Age
45
Location
New Zealand
Website
www.projectitis.com
Hi all,

I am stepping through arbitrary byte data with a char* pointer (as you do :) ) and at some stage I have to read a 'long' or 'unsigned long' data type.

Usually, you could just go:
Code:
unsigned char *fpt;

// Read some bytes etc
byte1 = *fpt; fpt++;
byte2 = *fpt; fpt++;

// Read an unsigned long
ulong1 = *((unsigned long*)fpt);

Which works perfectly, UNLESS the pointer fpt does NOT happen to be on a 4-byte boundary. It just crashes the GP. So I tried this:

Code:
ulong1 = *(fpt+0)<<24 + *(fpt+1)<<16 + *(fpt+2)<<8 + *(fpt+3);
But it just comes up with some crazy number. Maybe I am not taking the sign into account? Or maybe the byte order is wrong?

How can I read a long or unsigned long from a byte pointer which is not on a four-byte boundary?
 
Hey pea,

You have got the byte order wrong - the GP32 is little-endian (although I think it can also do big-endian?). Also, the CPU can do unaligned 32-bit reads quite happily, but there's a flag in one of its registers that can be set to disable such accesses. You could look it up in the data sheet.

Your bit of code is a bit suspect also because when you go *(fpt+0) << 24, you're relying on the compiler to cast *(fpt+0), which has type unsigned char, to ulong before it does the 24-bit shift. The compiler is under no obligation to do that, so the result of *(fpt+0) << 24 could quite possibly be zero (although I don't know what gcc does).

So anyway, to be really correct, you should do something like
Code:
ulong1 = ((unsigned long)*fpt) + (((unsigned long)*(fpt+1)) << 8) + (((unsigned long)*(fpt+2)) << 16) + (((unsigned long)*(fpt+3)) << 8);
fpt += 4;
 
Thanks Rob, I will look for that register value, because doing an unaligned 32-bit lookup would be ideal.

My intermediate solution (which works ok) is:
Code:
ulong1 = ((*(fpt+0)<<0)) | ((*(fpt+1))<<8) | ((*(fpt+2))<<16) | ((*(fpt+3))<<24);

There are a few more brackets than is neccessary, but better to understand what needs to happen :). I didn't even think about the type casting for the shift!
 
I've done a search of the data sheet for 'boundary' and 'aligned' and of all the instances, none mentions a register setting. I also read the seconds on reading byte and word data, and little/big endian modes, but no luck.

Any clues on what to look for?
 
OK, I've figured it out. You can prevent an unaligned access from causing an exception and crashing the GP32, but it still won't do the access the way you want it to.

The flag you want is in the control register of co-processor 15. Look in appendix 2 of um_s3c2400x_rev1.1.pdf, which you can get from Mr Spiv's site. That register also contains the big endian/little endian bit.

Consider this piece of code:
Code:
	unsigned char *pc;
	unsigned long *pl;
	long x;

	pc=(unsigned char *)0xc100000;
	pl=(unsigned long *)pc;
	*pc++ = 0x12;
	*pc++ = 0x34;
	*pc++ = 0x56;
	*pc = 0x78;
	x=*pl;
	gm_sprintf (string,"unsigned long is %08X",x);
	GpTextOut(NULL, &gpDraw, 30, 20, string, 0xFF);

It runs fine and x ends up being 0x78563412, which you would expect on a little-endian machine. Now change pc to 0xc100001, and run it: the GP32 crashes.

I added some assembly code to the project:
Code:
	mrc p15, 0, r0, c1, c0, 0
	bic r0, r0, #2
	mcr p15, 0, r0, c1, c0, 0
to disable the memory alignment exception. Re-running the program with pc set to 0xc100001 no longer crashes the GP32, but x ends up being 0x00563412 ... so the highest-addressed byte (the 0x78) wasn't fetched.

All up, I think you're stuck with your slow byte-wise fetches :rolleyes:
 
Thanks heaps Rob.

It doesn't happen a lot in my code, and its mostly my fault (i.e. I developed the file specification for the file I am reading/parsing), so I should have thought of that.

Basically I have a sort of package file structure that holds other files inside it, and I should have zero padded these other files...
 
You know, it never even entered my brain to even consider the endian-ness register; given the GP32 is wide open, we can futz with it, but I never have on any ARM platform. If you switch it to big-endian mode, and avoid talking to little endian code like the BIOS, can it be done?

/me considers speed options from running in big-endian mode for emulating big-endian platforms.

In the end, not worth it, since all of us just pre-swap the bytes in big-endian-memory anyway, so no real gain. A brief thought though, right into the trashbin :)

jeff
 
Back
Top