Motorola Integer conversion

4 posts / 0 new
Last post
jwanderson88
jwanderson88's picture
Offline
Last seen: 1 year 11 months ago
Joined: 2019-04-13 19:29
Motorola Integer conversion

I hope somebody can help me understand this. I'm "playing around" with a 68000 disassembler. It was originally written in C to run on an Amiga. I'm converting it to Microsoft Visual C++. My specific target is a very old Amiga game, namely "conquest". It's written in very basic 68000 code. My specific question involves the way motorola integer format differs from x86 format. The low order and high order bytes and words are reversed. I've run into this in the past when trying to read .BMP image files created on an Amiga. The integers need to be swapped to produce a recognizable .BMP file. I wrote a routine that swaps the parts around and it works. Here's the code here. It's very verbose, but it helps me undertand it. It starts with an unsigned long integer, sint.

struct wordparts {
uint8 lopart;
uint8 hipart;
};

struct longparts {
struct wordparts lolong;
struct wordparts hilong;
};

longparts olong, nlong, blong;
uint32 wlong;
uint16 loword, hiword;

loword = sint & 0xFFFF;
hiword = (uint16)(sint >> 16) & 0xFFFF;

olong.lolong.lopart = loword & 0xFF;
olong.lolong.hipart = loword >> 8;
olong.hilong.lopart = hiword & 0xFF;
olong.hilong.hipart = hiword >> 8;

nlong.lolong.lopart = olong.lolong.hipart;
nlong.lolong.hipart = olong.lolong.lopart;
nlong.hilong.lopart = olong.hilong.hipart;
nlong.hilong.hipart = olong.hilong.lopart;

blong.lolong = nlong.hilong;
blong.hilong = nlong.lolong;

wlong = blong.hilong.hipart << 24;
wlong |= blong.hilong.lopart << 16;
wlong |= blong.lolong.hipart << 8;
wlong |= blong.lolong.lopart;

return wlong;

What baffles me is that the original program reads 4 characters from the source file, which I assume is an unsigned long integer. It assembles it into a long integer. The original code follows.

long getlong ()

{
long res;

res = fgetc (in) << 24;
res |= fgetc (in) << 16;
res |= fgetc (in) << 8;
res |= fgetc (in);
f_offset += 4L;
return (res);
}

This results in hunk type 1011, which is 0x03F3. This is a recognizable hunk type, HUNK_HEADER. I assumed the integer was in Motorola format and needed high/low to be swapped. If it is swapped, it isn't a valid header type. My question is, why doesn't it need to be swapped?

msteed
msteed's picture
Offline
Last seen: 2 months 1 week ago
Joined: 2022-01-18 08:34
Re: Motorola Integer conversion

@jwanderson88

That's a problem that's all too familiar to Amiga programmers who are porting applications written for the little-endian PC platform to the big-endian Amiga platform. But the same problem exists when going the other way.

As you've noticed, writing the value 0x12345678 as a long on a big-endian system results in the byte stream 0x12 0x34 0x56 0x78, while writing the same value as a long on a little-endian system results in the byte stream 0x78 0x56 0x34 0x12.

Reading such a value back in correctly depends both on the endianness of the system that wrote it, as well as the system that is doing the reading.

The original code example you show is "endian agnostic" in that it runs properly on both big- and little-endian systems. It does this by reading one byte at a time and then assembling the bytes into a long, which works because reading bytes is the same regardless of endianness.

However, the code example is specific to data written by a big-endian system. Different code would be required to read in data written by a little-endian system.

Because the hunk file was written by a big-endian Amiga, it does not need to be swapped when read in by the example code- the example code takes care of reassembling the bytes correctly into the long. (If you tried to read in the value as a single long on a little-endian system, then you'd need to do the swapping afterward.)

Presumably there's an equivalent getshort() function that reads in 16-bit values.

thomas
thomas's picture
Offline
Last seen: 1 month 3 weeks ago
Joined: 2011-05-16 14:23
Re: Motorola Integer conversion

I find your code rather complicated. To be honest I can't even follow it to see if it is correct.

My way would rather be to mask each byte and move it to its destination one by one.

Like so:

  1. unsigned long swap_long (unsigned long in)
  2. {
  3. unsigned long out =
  4. ((in & 0xFF000000) >> 24) |
  5. ((in & 0x00FF0000) >> 8) |
  6. ((in & 0x0000FF00) << 8) |
  7. ((in & 0x000000FF) << 24);
  8. return (out);
  9. }
  10.  
  11.  
  12. unsigned short swap_short (unsigned short in)
  13. {
  14. unsigned short out =
  15. ((in & 0xFF00) >> 8) |
  16. ((in & 0x00FF) << 8);
  17. return (out);
  18. }
jwanderson88
jwanderson88's picture
Offline
Last seen: 1 year 11 months ago
Joined: 2019-04-13 19:29
Re: Motorola Integer conversion

Thanks msteed. That's very useful info. But I'm thinking of giving up on this particular code because of the obscure programming tricks used. It causes me a lot of confusion. One thing it does is use pointers to functions. It's kind of a slick way to switch code, but Visual C++ doesn't allow pointers to members. You have to do it outside of any class. I should look for a simpler example. I don't mind "borrowing" code, because I'm not going to sell it. Why reinvent the wheel when there are wheels all over the place? I will still run into the little / big endian problem. I didn't know what that means until now. I mean, I didn't know it had a name.

Log in or register to post comments