summaryrefslogtreecommitdiff
path: root/ld/typeconv.c
diff options
context:
space:
mode:
Diffstat (limited to 'ld/typeconv.c')
-rw-r--r--ld/typeconv.c536
1 files changed, 536 insertions, 0 deletions
diff --git a/ld/typeconv.c b/ld/typeconv.c
new file mode 100644
index 0000000..82dafdd
--- /dev/null
+++ b/ld/typeconv.c
@@ -0,0 +1,536 @@
+/* typeconv.c - convert between char arrays and unsigneds */
+
+/* Copyright (C) 1994 Bruce Evans */
+
+/*
+ c2u2(): 2 byte array to 2 byte unsigned
+ c4u4(): 4 byte array to 4 byte unsigned
+ cnu2(): n byte array to 2 byte unsigned
+ cnu4(): n byte array to 4 byte unsigned
+ u2c2(): 2 byte unsigned to 2 byte array
+ u2cn(): 2 byte unsigned to n byte array
+ u4c4(): 4 byte unsigned to 4 byte array
+ u4cn(): 4 byte unsigned to n byte array
+ typeconv_init: (re)initialise for given byte order.
+ Default is no swapping, but the initialisation should be done
+ anyway to provide some validity checks (returns FALSE if error).
+
+ Not provided:
+ c2u4(), c4u2(), u2c4(), u4c2().
+ Each of these is best done by truncating or extending a return value
+ or argument to the appropiate fixed-count function.
+ c4u2() has too many cases to do in-line conveniently, and the others
+ are hardly more efficient when done in-line.
+
+ 4 byte orderings for both char arrays and unsigneds are supported:
+ 0123 - little-endian
+ 3210 - big-endian
+ 2301 - little-endian with long words big-endian (pdp11)
+ 1032 - big-endian with long words little_endian (who knows?)
+
+ The unsigned's byte order is that of the machine on which these
+ routines are running.
+ It is determined at run time initialisation since the compiler/
+ preprocessor is too dumb to tell us at compile time.
+*/
+
+#include "const.h"
+#include "type.h"
+#include "globvar.h"
+
+FORWARD u2_pt c2u2_00 P((char *buf));
+FORWARD u4_pt c4u4_00 P((char *buf));
+FORWARD u2_pt c2u2_ss P((char *buf));
+FORWARD u4_pt c4u4_ss P((char *buf));
+FORWARD u4_pt c4u4_s0 P((char *buf));
+FORWARD u4_pt c4u4_0s P((char *buf));
+FORWARD void u2c2_00 P((char *buf, u2_pt offset));
+FORWARD void u4c4_00 P((char *buf, u4_t offset));
+FORWARD void u2c2_ss P((char *buf, u2_pt offset));
+FORWARD void u4c4_ss P((char *buf, u4_t offset));
+FORWARD void u4c4_s0 P((char *buf, u4_t offset));
+FORWARD void u4c4_0s P((char *buf, u4_t offset));
+
+PRIVATE u2_pt (*pc2u2) P((char *buf)) = c2u2_00;
+PRIVATE u4_pt (*pc4u4) P((char *buf)) = c4u4_00;
+PRIVATE void (*pu2c2) P((char *buf, u2_pt offset)) = u2c2_00;
+PRIVATE void (*pu4c4) P((char *buf, u4_t offset)) = u4c4_00;
+
+/* === char arrays to unsigneds === */
+
+/* no bytes swapped, longwinded to avoid alignment problems */
+
+PRIVATE u2_pt c2u2_00(buf)
+register char *buf;
+{
+ u2_t offset;
+
+ ((char *) &offset)[0] = buf[0];
+ ((char *) &offset)[1] = buf[1];
+ return offset;
+}
+
+PRIVATE u4_pt c4u4_00(buf)
+register char *buf;
+{
+ u4_t offset;
+
+ ((char *) &offset)[0] = buf[0];
+ ((char *) &offset)[1] = buf[1];
+ ((char *) &offset)[2] = buf[2];
+ ((char *) &offset)[3] = buf[3];
+ return offset;
+}
+
+/* straight swapping for little-endian to big-endian and vice versa */
+
+PRIVATE u2_pt c2u2_ss(buf)
+register char *buf;
+{
+ u2_t offset;
+
+ ((char *) &offset)[0] = buf[1];
+ ((char *) &offset)[1] = buf[0];
+ return offset;
+}
+
+PRIVATE u4_pt c4u4_ss(buf)
+register char *buf;
+{
+ u4_t offset;
+
+ ((char *) &offset)[0] = buf[3];
+ ((char *) &offset)[1] = buf[2];
+ ((char *) &offset)[2] = buf[1];
+ ((char *) &offset)[3] = buf[0];
+ return offset;
+}
+
+/* wierd swapping for different-endian u2's, same-endian u4's */
+
+PRIVATE u4_pt c4u4_s0(buf)
+register char *buf;
+{
+ u4_t offset;
+
+ ((char *) &offset)[0] = buf[1];
+ ((char *) &offset)[1] = buf[0];
+ ((char *) &offset)[2] = buf[3];
+ ((char *) &offset)[3] = buf[2];
+ return offset;
+}
+
+/* very wierd swapping for same-endian u2's, different-endian u4's */
+
+PRIVATE u4_pt c4u4_0s(buf)
+register char *buf;
+{
+ u4_t offset;
+
+ ((char *) &offset)[0] = buf[2];
+ ((char *) &offset)[1] = buf[3];
+ ((char *) &offset)[2] = buf[0];
+ ((char *) &offset)[3] = buf[1];
+ return offset;
+}
+
+/* === entry points === */
+
+PUBLIC u2_pt c2u2(buf)
+char *buf;
+{
+ return (*pc2u2) (buf);
+}
+
+PUBLIC u4_t c4u4(buf)
+char *buf;
+{
+ return (*pc4u4) (buf);
+}
+
+PUBLIC u2_pt cnu2(buf, count)
+char *buf;
+unsigned count;
+{
+ switch (count)
+ {
+ case 1:
+ return buf[0] & 0xFF;
+ case 2:
+ return (*pc2u2) (buf);
+ case 4:
+ return (u2_pt) (*pc4u4) (buf);
+ default:
+ return 0;
+ }
+}
+
+PUBLIC u4_t cnu4(buf, count)
+char *buf;
+unsigned count;
+{
+ switch (count)
+ {
+ case 1:
+ return buf[0] & 0xFF;
+ case 2:
+ return (*pc2u2) (buf);
+ case 4:
+ return (*pc4u4) (buf);
+ default:
+ return 0;
+ }
+}
+
+/* === unsigneds to char arrays === */
+
+/* no bytes swapped, longwinded to avoid alignment problems */
+
+PRIVATE void u2c2_00(buf, offset)
+register char *buf;
+u2_pt offset;
+{
+
+ buf[0] = ((char *) &offset)[0];
+ buf[1] = ((char *) &offset)[1];
+}
+
+PRIVATE void u4c4_00(buf, offset)
+register char *buf;
+u4_t offset;
+{
+ buf[0] = ((char *) &offset)[0];
+ buf[1] = ((char *) &offset)[1];
+ buf[2] = ((char *) &offset)[2];
+ buf[3] = ((char *) &offset)[3];
+}
+
+/* straight swapping for little-endian to big-endian and vice versa */
+
+PRIVATE void u2c2_ss(buf, offset)
+register char *buf;
+u2_pt offset;
+{
+ u2_t offset2;
+
+ offset2 = offset;
+ buf[0] = ((char *) &offset2)[1];
+ buf[1] = ((char *) &offset2)[0];
+}
+
+PRIVATE void u4c4_ss(buf, offset)
+register char *buf;
+u4_t offset;
+{
+ buf[0] = ((char *) &offset)[3];
+ buf[1] = ((char *) &offset)[2];
+ buf[2] = ((char *) &offset)[1];
+ buf[3] = ((char *) &offset)[0];
+}
+
+/* wierd swapping for different-endian u2's, same-endian u4's */
+
+PRIVATE void u4c4_s0(buf, offset)
+register char *buf;
+u4_t offset;
+{
+ buf[0] = ((char *) &offset)[1];
+ buf[1] = ((char *) &offset)[0];
+ buf[2] = ((char *) &offset)[3];
+ buf[3] = ((char *) &offset)[2];
+}
+
+/* very wierd swapping for same-endian u2's, different-endian u4's */
+
+PRIVATE void u4c4_0s(buf, offset)
+register char *buf;
+u4_t offset;
+{
+ buf[0] = ((char *) &offset)[2];
+ buf[1] = ((char *) &offset)[3];
+ buf[2] = ((char *) &offset)[0];
+ buf[3] = ((char *) &offset)[1];
+}
+
+/* === entry points === */
+
+PUBLIC void u2c2(buf, offset)
+register char *buf;
+u2_pt offset;
+{
+ (*pu2c2) (buf, offset);
+}
+
+PUBLIC void u4c4(buf, offset)
+register char *buf;
+u4_t offset;
+{
+ (*pu4c4) (buf, offset);
+}
+
+PUBLIC void u2cn(buf, offset, count)
+register char *buf;
+u2_pt offset;
+unsigned count;
+{
+ switch (count)
+ {
+ case 1:
+ buf[0] = (char) offset;
+ return;
+ case 2:
+ (*pu2c2) (buf, offset);
+ return;
+ case 4:
+ (*pu4c4) (buf, (u4_t) offset);
+ return;
+ }
+}
+
+PUBLIC void u4cn(buf, offset, count)
+register char *buf;
+u4_t offset;
+unsigned count;
+{
+ switch (count)
+ {
+ case 1:
+ buf[0] = (char) offset;
+ return;
+ case 2:
+ (*pu2c2) (buf, (u2_pt) (u2_t) offset);
+ return;
+ case 4:
+ (*pu4c4) (buf, offset);
+ return;
+ }
+}
+
+/* initialise type conversion, return FALSE if it cannot be handled */
+
+PUBLIC bool_pt typeconv_init(big_endian, long_big_endian)
+bool_pt big_endian;
+bool_pt long_big_endian;
+{
+ u2_pt conv2;
+ u4_pt conv4;
+ char *conv2ptr;
+ char *conv4ptr;
+
+ if (sizeof(u2_t) != 2 || sizeof(u4_t) != 4)
+ /* dumb preprocessor's don't accept sizeof in #if expressions */
+ return FALSE;
+
+ if (big_endian)
+ {
+ conv2ptr = (conv4ptr = "\1\2\3\4") + 2;
+ if (!long_big_endian)
+ conv4ptr = "\3\4\1\2";
+ }
+ else
+ {
+ conv2ptr = conv4ptr = "\4\3\2\1";
+ if (long_big_endian)
+ conv4ptr = "\2\1\4\3";
+ }
+ conv2 = c2u2_00(conv2ptr);
+ conv4 = c4u4_00(conv4ptr);
+ if (conv2 == 0x0304)
+ {
+ pc2u2 = c2u2_00;
+ pc4u4 = c4u4_00;
+ pu2c2 = u2c2_00;
+ pu4c4 = u4c4_00;
+ if (conv4 == 0x03040102L)
+ {
+ pc4u4 = c4u4_0s;
+ pu4c4 = u4c4_0s;
+ }
+ else if (conv4 != 0x01020304L)
+ return FALSE;
+ }
+ else if (conv2 == 0x0403)
+ {
+ pc2u2 = c2u2_ss;
+ pc4u4 = c4u4_ss;
+ pu2c2 = u2c2_ss;
+ pu4c4 = u4c4_ss;
+ if (conv4 == 0x02010403L)
+ {
+ pc4u4 = c4u4_s0;
+ pu4c4 = u4c4_s0;
+ }
+ else if (conv4 != 0x04030201L)
+ return FALSE;
+ }
+ else
+ return FALSE;
+ return TRUE;
+}
+
+#ifdef DEBUG_TYPECONV
+
+main()
+{
+ char *source;
+ char target[4];
+ u2_t u2;
+ u2_t u2a;
+ u4_t u4;
+ u4_t u4a;
+
+ printf("%u\n", typeconv_init(FALSE, FALSE));
+ printf("%u\n", typeconv_init(FALSE, TRUE));
+ printf("%u\n", typeconv_init(TRUE, FALSE));
+ printf("%u\n", typeconv_init(TRUE, TRUE));
+
+ typeconv_init(FALSE, FALSE);
+ source = "\4\3\2\1";
+
+ target[0] = 0;
+ target[1] = 0;
+ u2 = cnu2(source, 2);
+ u2cn(target, u2, 2);
+ if (strncmp(source, target, 2))
+ printf("oops9\n");
+
+ target[0] = 0;
+ target[1] = 0;
+ u4a = cnu4(source, 2);
+ u4cn(target, u4a, 2);
+ if (strncmp(source, target, 2))
+ printf("oops10\n");
+
+ target[0] = 0;
+ target[1] = 0;
+ target[2] = 0;
+ target[3] = 0;
+ u2a = cnu2(source, 4);
+ u2cn(target, u2a, 4);
+ if (strncmp(target, "\4\3\0\0", 4))
+ printf("oops11\n");
+
+ target[0] = 0;
+ target[1] = 0;
+ target[2] = 0;
+ target[3] = 0;
+ u4 = cnu4(source, 4);
+ u4cn(target, u4, 4);
+ if (strncmp(source, target, 4))
+ printf("oops12\n");
+
+ printf("%04x %04x %08lx %08lx\n", u2, u2a, u4, u4a);
+
+ typeconv_init(FALSE, TRUE);
+ source = "\2\1\4\3";
+
+ target[0] = 0;
+ target[1] = 0;
+ u2 = cnu2(source + 2, 2);
+ u2cn(target, u2, 2);
+ if (strncmp(source + 2, target, 2))
+ printf("oops13\n");
+
+ target[0] = 0;
+ target[1] = 0;
+ u4a = cnu4(source + 2, 2);
+ u4cn(target, u4a, 2);
+ if (strncmp(source + 2, target, 2))
+ printf("oops14\n");
+
+ target[0] = 0;
+ target[1] = 0;
+ target[2] = 0;
+ target[3] = 0;
+ u2a = cnu2(source, 4);
+ u2cn(target, u2a, 4);
+ if (strncmp(target, "\0\0\4\3", 4))
+ printf("oops15\n");
+
+ target[0] = 0;
+ target[1] = 0;
+ target[2] = 0;
+ target[3] = 0;
+ u4 = cnu4(source, 4);
+ u4cn(target, u4, 4);
+ if (strncmp(source, target, 4))
+ printf("oops16\n");
+
+ printf("%04x %04x %08lx %08lx\n", u2, u2a, u4, u4a);
+
+ typeconv_init(TRUE, FALSE);
+ source = "\3\4\1\2";
+
+ target[0] = 0;
+ target[1] = 0;
+ u2 = cnu2(source, 2);
+ u2cn(target, u2, 2);
+ if (strncmp(source, target, 2))
+ printf("oops5\n");
+
+ target[0] = 0;
+ target[1] = 0;
+ u4a = cnu4(source, 2);
+ u4cn(target, u4a, 2);
+ if (strncmp(source, target, 2))
+ printf("oops6\n");
+
+ target[0] = 0;
+ target[1] = 0;
+ target[2] = 0;
+ target[3] = 0;
+ u2a = cnu2(source, 4);
+ u2cn(target, u2a, 4);
+ if (strncmp(target, "\3\4\0\0", 4))
+ printf("oops7\n");
+
+ target[0] = 0;
+ target[1] = 0;
+ target[2] = 0;
+ target[3] = 0;
+ u4 = cnu4(source, 4);
+ u4cn(target, u4, 4);
+ if (strncmp(source, target, 4))
+ printf("oops8\n");
+
+ printf("%04x %04x %08lx %08lx\n", u2, u2a, u4, u4a);
+
+ typeconv_init(TRUE, TRUE);
+ source = "\1\2\3\4";
+
+ target[0] = 0;
+ target[1] = 0;
+ u2 = cnu2(source + 2, 2);
+ u2cn(target, u2, 2);
+ if (strncmp(source + 2, target, 2))
+ printf("oops1\n");
+
+ target[0] = 0;
+ target[1] = 0;
+ u4a = cnu4(source + 2, 2);
+ u4cn(target, u4a, 2);
+ if (strncmp(source + 2, target, 2))
+ printf("oops2\n");
+
+ target[0] = 0;
+ target[1] = 0;
+ target[2] = 0;
+ target[3] = 0;
+ u2a = cnu2(source, 4);
+ u2cn(target, u2a, 4);
+ if (strncmp(target, "\0\0\3\4", 4))
+ printf("oops3\n");
+
+ target[0] = 0;
+ target[1] = 0;
+ target[2] = 0;
+ target[3] = 0;
+ u4 = cnu4(source, 4);
+ u4cn(target, u4, 4);
+ if (strncmp(source, target, 4))
+ printf("oops4\n");
+
+ printf("%04x %04x %08lx %08lx\n", u2, u2a, u4, u4a);
+}
+
+#endif /* DEBUG_TYPECONV */