/* convert.c */ #include #include "convert.h" extern int debug; /* 1: print debugging messages */ extern int printing; /* 1: print conversion statistics */ Hinfo *ahinfo(); /* hunk information */ Hinfo **hunk; /* -> vector of ptrs to Hinfo structs */ Hinfo *firsthunk; /* -> first hunk */ int nhunks; /* #hunks in input file */ int curhunk; /* current hunk# */ int hvalid; /* state variable (to bump curhunk) */ long filpos; /* position in input file */ long siz[3]; /* segment sizes */ /* relocation (fixup) information */ long rsize; /* total size of all relocation information */ long *rbuf; /* -> buffer for relocation information */ long *rpos; /* current position in relocation info buffer */ char buf[BUFFERSIZE]; /* buffer for copying stuff */ /* * Convert AMIG* binary loadfile to GEMDOS .PRG executable. */ convert(ifd, ofd) int ifd, ofd; { /* init globals */ hunk = (Hinfo **)NULL; firsthunk = (Hinfo *)NULL; hvalid = nhunks = curhunk = 0; filpos = 0L; rsize = 0L; pass1(ifd); if (curhunk < nhunks) panic("Bugcheck: not enough hunks in input body."); bind(); if ((rbuf = (long *)malloc((int)rsize)) == NULL) allerr((int)rsize); rpos = rbuf; output(ifd, ofd); reloc(ifd, ofd); release(); } /* * Parse hunks in AMIG* binary loadfile, * gather information about it. */ pass1(ifd) int ifd; { long lw, junk; Hinfo *h; DEBUG("~ pass1()\n"); for(;;) { if (readlong(ifd, &lw) == EOF) break; switch ((int)lw) { case HUNKUNIT: case HUNKNAME: DEBUG("~\tHUNKUNIT or HUNKNAME\n"); getlong(ifd, &lw); skip(ifd, lw * 4); break; case HUNKCODE: chkhunk(); h = hunk[curhunk]; getlong(ifd, &lw); /* get size (in longs) */ lw *= 4; h->hsize = lw; h->hpos = filpos; h->htype = TEXT; skip(ifd, lw); if (debug) dumphinfo(h); break; case HUNKDATA: chkhunk(); h = hunk[curhunk]; getlong(ifd, &lw); /* get size (in longs) */ lw *= 4; h->hsize = lw; h->hpos = filpos; h->htype = DATA; skip(ifd, lw); if (debug) dumphinfo(h); break; case HUNKBSS: chkhunk(); h = hunk[curhunk]; getlong(ifd, &lw); /* get size (in longs) */ h->hsize = lw * 4; h->htype = BSS; if (debug) dumphinfo(h); break; case HUNKR32: h = hunk[curhunk]; for (;;) { getlong(ifd, &lw); if (!lw) break; lw = (lw + 1) * 4; h->hrsize += lw + 4; skip(ifd, lw); } h->hrsize += 4; rsize += h->hrsize + 4; if(debug)printf("~\thrsize = %ld\n", h->hrsize); break; case HUNKR16: panic("16-bit relocation not supported."); break; case HUNKR8: panic("8-bit relocation not supported."); break; case HUNKEXT: panic("External symbols not supported."); break; case HUNKSYMBOL: panic("Symbols not supported."); break; case HUNKDEBUG: panic("Debug blocks not supported."); break; case HUNKEND: if (hvalid) { hvalid = 0; ++curhunk; } if(debug)printf("~HUNKEND, curhunk = %d\n", curhunk); break; case HUNKHEADER: header(ifd); break; case HUNKOVERLAY: panic("Overlays not supported."); break; case HUNKBREAK: panic("Breaks (overlays) not supported."); break; } } } /* * Sanity check about hunk adjacency (which may not be a * problem) and hunk number range. */ chkhunk() { if (hvalid) panic("Bugcheck: two adjacent hunks w/o HUNKEND!"); if (curhunk >= nhunks) panic("Bugcheck: too many hunks!"); hvalid = 1; } /* * Read hunk header, * get info for global vars. * */ header(fd) int fd; { long lw, *p; long htabsize, firsthunk, lasthunk; unsigned int i, j; /* * Skip library names. */ for (;;) { getlong(fd, &lw); if (!lw) break; skip(fd, lw*4); } /* more random header info */ getlong(fd, &htabsize); getlong(fd, &firsthunk); getlong(fd, &lasthunk); if (debug) printf("~\thtabsize = %ld\n\tfirsthunk = %ld\n\tlasthunk = %ld\n", htabsize, firsthunk, lasthunk); /* alloc space for hunk database */ nhunks = (unsigned)(lasthunk - firsthunk + 1); hunk = (Hinfo **)malloc(nhunks * sizeof(Hinfo)); if (hunk == NULL) allerr(nhunks * sizeof(Hinfo)); for (j = 0; j < nhunks; ++j) { hunk[j] = ahinfo(); hunk[j]->hunkno = j; getlong(fd, &hunk[j]->hsize); if(debug) printf("~\t%d hsize = %ld ($%lx)\n", j, hunk[j]->hsize, hunk[j]->hsize); } } /* * Compute hunk order and starting addresses. */ bind() { int typ, hnk; long addr; Hinfo *hptr; DEBUG("~ bind()\n"); addr = 0L; hptr = firsthunk; for (typ = 0; typ < 3; ++typ) { siz[typ] = 0L; for (hnk = 0; hnk < nhunks; ++hnk) if (hunk[hnk]->htype == typ) { if (firsthunk == (Hinfo *)NULL) hptr = firsthunk = hunk[hnk]; else { hptr->hnext = hunk[hnk]; hptr = hunk[hnk]; } siz[typ] += hptr->hsize; hptr->haddr = addr; addr += hptr->hsize; if(debug) printf("~\thunk[%d]->haddr = $%lx\n", hnk, hptr->haddr); } } if(printing) printf("text %ld ($%lx), data %ld ($%lx), bss %ld ($%lx)\n", siz[0], siz[0], siz[1], siz[1], siz[2], siz[2]); } /* * Generate the GEMDOS output file. */ output(ifd, ofd) int ifd, ofd; { int i; Hinfo *hu; DEBUG("~ output()\n"); /* * Write .PRG header. */ writeword(ofd, 0x601a); for (i = 0; i < 3; i++) writelong(ofd, siz[i]); for (i = 0; i < 7; i++) writeword(ofd, 0x0000); for (hu = firsthunk; hu != NULL; hu = hu->hnext) if (hu->htype == TEXT || hu->htype == DATA) { lseek(ifd, hu->hpos, 0); randw(ifd, hu->hsize, ofd); if (hu->hrsize) { lseek(ifd, 4L, 1); *rpos++ = hu->hunkno; if (read(ifd, rpos, (int)hu->hrsize) != (int)hu->hrsize) panic("Reloc info read error."); if(debug)printf("~ relocsiz = %ld\n", hu->hrsize); rpos += hu->hrsize >> 2; } } } /* * Generate relocation information * (at the end of the output file). */ reloc(ifd, ofd) int ifd, ofd; { int cmplong(); long cvt2long(); long *rp, *drp; char *crp, c; int i, hno, nel; long count, base, lw, addr; DEBUG("~ reloc()\n"); if (debug) { i = 0; for (crp = (char *)rbuf; crp < (char *)rpos; crp += 4) { if (!i) printf("~ "); printf("%02x%02x%02x%02x", crp[0]&0xff, crp[1]&0xff, crp[2]&0xff, crp[3]&0xff); if (++i >= 8) { printf("\n"); i = 0; } else putchar(' '); } putchar('\n'); } /* * Go back into the .PRG file and add * hunk starting-offsets to the longwords * that must be fixed up. * 28L is the size of the GEMDOS .PRG header. */ for (rp = rbuf; rp < rpos;) { hno = *rp++; if(debug)printf("~ hno = %d\n", hno); for (;;) { *rp = cvt2long(*rp); if (!(count = *rp++)) break; *rp = cvt2long(*rp); base = hunk[(int)*rp++]->haddr; if(debug)printf("~ count = %ld, base = $%0lx\n", count, base); while (count--) { *rp = cvt2long(*rp); if(debug)printf("~ seek(ifd) = %ld, seek(ofd) = %ld ", hunk[hno]->hpos + *rp, hunk[hno]->haddr + *rp + 28L); lseek(ifd, hunk[hno]->hpos + *rp, 0); lseek(ofd, hunk[hno]->haddr + *rp++ + 28L, 0); readlong(ifd, &lw); lw += base; writelong(ofd, lw); if(debug)printf("lw = $%0lx\n", lw); } } } /* * Compute and sort fixup offsets. */ nel = 0; drp = rbuf; for (rp = rbuf; rp < rpos;) { hno = *rp++; for (;;) { if (!(count = *rp++)) break; ++rp; while (count--) { *drp++ = hunk[hno]->haddr + *rp++; ++nel; } } } rpos = drp; qsort(rbuf, nel, 4, cmplong); /* print sorted info */ if(debug) { i = 0; for (rp = rbuf; rp < rpos; ++rp) { if (!i) printf("~ "); printf("%08lx", *rp); if (++i >= 8) { printf("\n"); i = 0; } else putchar(' '); } putchar('\n'); } /* * Write GEMDOS relocation information */ lseek(ofd, 0L, 2); /* to end of file */ rp = rbuf; while (rp < rpos) { /* write offset to first fixup */ if (rp == rbuf) { writelong(ofd, *rp); addr = *rp++; if(debug)printf("$%lx\n", addr); continue; } if (addr >= *rp) panic("Bad relocation information."); c = 254; lw = *rp - addr; addr = *rp++; if(debug)printf("$%lx:", lw); while (lw > 254) { write(ofd, &c, 1); lw -= 254L; if(debug)putchar('+'); } c = (char)lw; write(ofd, &c, 1); if(debug)printf("$%x\n", (int)lw); } /* * Terminate relocation list */ c = 0; write(ofd, &c, 1); } /* * Compare two longs * for qsort(). */ cmplong(l1, l2) long *l1, *l2; { return *l1 - *l2; } /* * Allocate (and initialize) a Hinfo node. */ Hinfo *ahinfo() { Hinfo *h; if (curhunk >= nhunks) panic("curhunk >= nhunks, too many hunks!"); if ((h = (Hinfo *)malloc(sizeof(Hinfo))) == NULL) allerr(sizeof(Hinfo)); h->hsize = 0L; h->hpos = 0L; h->haddr = 0L; h->hrsize = 0L; h->htype = -1; h->hrel = (long *)NULL; h->hnext = (Hinfo *)NULL; return h; } /* * Release memory used by hunk database. */ release() { int i; for (i = 0; i < nhunks; ++i) { if (hunk[i]->hrel != NULL) free(hunk[i]->hrel); free(hunk[i]); } free(hunk); } /* * Skip some of the input file */ skip(fd, count) int fd; long count; { filpos += count; lseek(fd, count, 1); } /* * Out of memory (malloc() didn't work); * complain and die. */ allerr(size) int size; { fprintf (stderr, "malloc(%d) failed..\n", size); panic("Allocation failure, heap exhausted, I give up!\n"); } /* * Print information about an Hinfo node. */ dumphinfo(h) Hinfo *h; { printf("~\thsize = %ld ($%lx), hpos = %ld, haddr = %ld ($%lx)\n", h->hsize, h->hsize, h->hpos, h->haddr, h->haddr); printf("~\thtype = %d ", h->htype); printf("hrel = $%lx, hnext = $%lx\n", (long)h->hrel, (long)h->hnext); } /* * Convert 68000 long to local long */ long cvt2long(lw) long lw; { #ifdef AMIGA return (lw); #else char *out, *in; long olw; /* * 8086/VAX conversion */ in = (char *)&lw; out = (char *)&olw; out[0] = in[3]; out[1] = in[2]; out[2] = in[1]; out[3] = in[0]; return olw; #endif /* AMIGA */ }