Hi All
The Coldfire has a few interesting instructions which are not seen when programming exclusively in C. To make use of them it is sometimes necessary to write some assembler code.
The
BYTEREV is such a command. It reverses the bytes in a register:
Dx[31:24] -> D[7:0]
Dx[23:16] -> D[15:8]
Dx[15:8] -> D[23:16]
Dx[7:0] -> D[31:24]where x is one of the data registers (eg. D0).
This is effectively the same as the following C-code does:
x = ((x >> 24) | ((x >> 8 ) & 0x0000ff00) | ((x << 8 ) & 0x00ff0000) | ((x << 24) & 0xff000000));This code can be used to swap addresses between little and big-endian format and looks like this when compiled on the Coldfire, where the original address (32 bit word) is in d5 and the final converted address is in d0:
00001F38: 2605 move.l d5,d3
00001F3A: E08B lsr.l #8,d3
00001F3C: 7E18 moveq #24,d7
00001F3E: 2405 move.l d5,d2
00001F40: EEAA lsr.l d7,d2
00001F42: 02830000FF00 andi.l #0xff00,d3
00001F48: 8483 or.l d3,d2
00001F4A: 2605 move.l d5,d3
00001F4C: E18B lsl.l #8,d3
00001F4E: 028300FF0000 andi.l #0xff0000,d3
00001F54: EFAD lsl.l d7,d5
00001F56: 8483 or.l d3,d2
00001F58: 0285FF000000 andi.l #0xff000000,d5
00001F5E: 8485 or.l d5,d2Compared to the single instruction
byterev.l d0 it is obviously rather less efficient. But compilers generally will not detect that this can be simplified and the longer form results in real code.
Now it is interesting to note that the USB controller in the Coldfire operates in little-endian mode, whereas the Coldfire CPU itself operates in big-endian mode [more details about this can be found in the USB user's guide in appendix A at
http://www.utasker.com/docs/uTasker/USB_User_Guide.PDF]. This requires the driver code to convert between the two when collecting USB messages from the input buffers (OUT tokens) or preparing new transmit messages (IN tokens) and so the BYTEREV instruction was an opportunity to make this interface a bit more efficient.
Here's how it was done (this will be included in the next service pack or can be tried yourselves by making the quite small changes below).
1) In
Startup.s add a new routine called
byte_rev()byte_rev: // sub routine for reversing bytes in a long word
_byte_rev:
byterev.l d0
rts 2) In
M5223x.c change the routine
fnLE_add() to call this assembler sub-routine instead of doing the 'long-winded' C-version:
ulLE_long_word = ((long_word >> 24) | ((long_word >> 8 ) & 0x0000ff00) | ((long_word << 8 ) & 0x00ff0000) | ((long_word << 24) & 0xff000000));
return (void *)ulLE_long_word;becomes
return (void *)byte_rev(long_word);where the prototype
extern unsigned long byte_rev(unsigned long); will also be required.
Even with the sub-routine call, this reduces each conversion from originally about 14 instructions down to just 4 instructions and makes the USB driver operation that bit more efficient!
Regards
Mark