From e962168ecfe389000d07ed20203e8aa558827e9f Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Sat, 6 Oct 2012 15:55:53 +0100 Subject: Initial dump of Castle sources --- squeeze.c | 972 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 972 insertions(+) create mode 100644 squeeze.c (limited to 'squeeze.c') diff --git a/squeeze.c b/squeeze.c new file mode 100644 index 0000000..169f275 --- /dev/null +++ b/squeeze.c @@ -0,0 +1,972 @@ +/* This source code in this file is licensed to You by Castle Technology + * Limited ("Castle") and its licensors on contractual terms and conditions + * ("Licence") which entitle you freely to modify and/or to distribute this + * source code subject to Your compliance with the terms of the Licence. + * + * This source code has been made available to You without any warranties + * whatsoever. Consequently, Your use, modification and distribution of this + * source code is entirely at Your own risk and neither Castle, its licensors + * nor any other person who has contributed to this source code shall be + * liable to You for any loss or damage which You may suffer as a result of + * Your use, modification or distribution of this source code. + * + * Full details of Your rights and obligations are set out in the Licence. + * You should have received a copy of the Licence with this source code file. + * If You have not received a copy, the text of the Licence is available + * online at www.castle-technology.co.uk/riscosbaselicence.htm + */ +/* + * Title: squeeze - compression of ADFS executable images + * Author: RCC + * Copyright: (C) 1987, Acorn Computers Ltd, Cambridge, England. + * Date: 03-Nov-87 + * LastEdit: 28-Mar-88 + 19-Jul-88 just to change the version to 1.00, and date (JSutton) + 21-Jul-88 remove reference to xpand in help text (JRS) + 07-Mar-91 add recognition of MOV R0, R0 as well as BLNV $0 + */ + +#ifdef __STDC__ +# include +# include +#else +# include +/* extern char *malloc(); */ +/*# define DATE "Mar 07 1991" */ +#endif + +#include "VersionNum" + +#include +#include +#include +#ifdef __riscos +#include "CLib/kernel.h" +#include "CLib/swis.h" +#else +typedef struct { + int load, exec; /* load, exec addresses */ + int start, end; /* start address/length, end address/attributes */ +} _kernel_osfile_block; +#endif + +#include "CLX/err.h" +#include "CLX/host.h" +#include "CLX/hash.h" +#include "CLX/wholefls.h" + +#ifndef __riscos +#include +#include +#include +#endif + +/* + * squeeze takes an ARM executable image file and compresses it, usually + * to about half the size, adding decompression code so that the image + * will automatically expand itself when it is run. + * + * For details of the compression scheme, see doc.squeeze. Briefly, + * the image is treated as a sequence of 32-bit words, and each word + * is encoded in one of four ways, specified by a 4-bit nibble: + * zero -> nibble 0 + * the 7*256 most common word values are encoded with one byte extra as + * an index into a table + * the 7*256 most common upper-3-byte values are encoded with one byte + * extra as index into another table, with the low byte separate + * anything else is given in full as 4 bytes. + * + * The tables of common values are sorted into ascending order + * and encoded in a devious way. + */ + +#define DATE Module_Date +/* If macro value is empty, expression rewrites to "0 * + 1" which is zero. */ +#if 0 * Module_MinorVersion_CMHG + 1 == 0 +# define VSN Module_MajorVersion +#else +# define VSN Module_MajorVersion " (" Module_MinorVersion ")" +#endif +#define BRIEF "compress an executable ARM-code program" +#define SELF "squeeze" +#define HASHSIZE (8*1024) +#define CHUNKSIZE (16*1024) + +#include "squeeze.h" + +#if 1 +#include "unsqueeze1.h" +#include "unsqrm1.h" +#else +/*extern void UnSqueeze_FindUnSqueezeCode(int *base, int *limit); */ +extern int UnSqueeze_UnSqueezeBase, UnSqueeze_UnSqueezeLimit; +extern int unsqueeze_base, unsqueeze_limit, unsqueeze_end; +#endif + +static int verbose; +static int debug; +static int force; + +static clock_t lastticks; + +/* + * Veneer on file-handling. + */ + +#define SAVE 0 +#define WRITEINFO 1 +#define READINFO 5 +#define LOAD 0xff + +#define FILEFOUND 1 + +static int ticks(void) +{ int last; + last = lastticks; lastticks = clock(); return(lastticks-last); +} + +static int fileinfo(_kernel_osfile_block *info, const char *name) +{ +#ifdef __riscos + if (_kernel_osfile(READINFO, name, info) != FILEFOUND) + return -1; +#else + struct stat buf; + int len, ftype; + + if (stat(name, &buf) != 0) return -1; + + len = strlen(name); + if (len > 4 && name[len-4] == ',') + ftype = (int)strtoul(name + len - 4, NULL, 16); + else + ftype = 0xfff; + + info->load = 0xfff00000 | (ftype << 8); + info->exec = buf.st_mtime * 100 / 256; + info->start = buf.st_size; + info->end = 0; +#endif + return 0; +} + +/* + * Declarations of nibble values for the encoding. + */ + +#define NSHORTS 7 +#define NLONGS (14-NSHORTS) +#define MAXTAB (7*256) + +#define ZERO 0 +#define LITERAL 1 +#define LONG 2 +#define SHORT (LONG+NLONGS) + +/* + * Data structure declarations. + */ + +/* + * The Info structure is really a 3-word thing, but we are keen to save + * space, so pack together the pointer to the next element in the list + * and the count of number of occurrences of this value into a single + * word. To get as many bits as possible for the count, we constrain + * all Info structures to be 8-byte aligned (freeing 3 low bits) and + * take the top 8 bits of the address off. This will only work if we + * are in the low 16MBytes of address space, but it leaves us 11 bits + * for the count, which is nearly always enough. + * + * Later on, we use these same records to build hash tables mapping + * word values -> index in table of common values, for fast encoding. + * Fortunately, the tables are of size 7*256 < 2**11, so the same + * packing dodge still works. I'm afraid this is all a bit tweaky, + * but making it fast and small is worth the effort. + * + * The alternative is to look up each word by binary search, but that + * would be slower (I think), viz 10 iterations for each table. + */ + + +#ifdef __riscos + +typedef struct Info { + word nextandcount; + word value; +} Info; + +#define COUNTBITS 11 +#define MAXCOUNT ((1<nextandcount; \ + n = (Info *)((t>>COUNTBITS)<<3); \ + c = t & MAXCOUNT; } + +#define pack(p,n,c) { word t = (((word)n)<<(COUNTBITS-3)) | c; \ + p->nextandcount = t; } + +#define inccount(p,n,c) { if (c < MAXCOUNT) ++c; \ + p->nextandcount = (((word)n)<<(COUNTBITS-3)) | c; } +#else /* !__riscos */ + +typedef struct Info { + struct Info *next; + word count; + word value; +} Info; + +#define MAXCOUNT INT_MAX + +#define unpack(p,n,c) { n = p->next; \ + c = p->count; } + +#define pack(p,n,c) { p->next = n; \ + p->count = c; } + +#define inccount(p,n,c) { p->next = n; \ + p->count = ++c; } + +#endif /* !__riscos */ + +typedef Info *HashTab[HASHSIZE]; + +typedef struct VTable { /* sorted vector of common values */ + int nhits; /* sum of frequencies of words in this table */ + Info *misses; /* list of (freq, word) pairs not in this table */ + int size; /* number of els in the table */ + word els[MAXTAB]; /* table els: [0..size-1] are valid */ +} VTable; + +typedef struct Freqs { /* list of (value, frequency) pairs */ + int nzeros; /* no of zero words */ + int maxfreq; /* max frequency in list - useful for sorting */ + Info *freqs; /* list of (value, frequency) */ +} Freqs; + +/* + * Some ugly stuff to allocate 2-word Info structures efficiently with + * correct (8-byte) alignment. + */ + +typedef struct InfoChunk { + struct InfoChunk *next; + Info *free; + Info *limit; + Info chunks[(CHUNKSIZE-12)/sizeof(Info)]; +} InfoChunk; + +static Info *align(Info *p, int n) +{ int x = (int)p; + x += (n - 1); x -= (x % n); return (Info *)x; +} + +#if DEBUGGING +static char *heaplwm; +static char *heaphwm; +#endif + +static void *xalloc(int bytes, char *what) +{ void *p = malloc(bytes); + if (p == NULL) err_fail("no more room"); +/* if ((int)p < 0) err_fail("storage corruption (%d)", (int)p); */ +#if DEBUGGING + if (debug) fprintf(stderr, "-- alloc(%d, %s) -> &%x\n", bytes, what, (int)p); + if ((char *)p + bytes > heaphwm) heaphwm = (char *)p + bytes; + if ((char *)p < heaplwm) heaplwm = (char *)p; +#else + what = NULL; +#endif + return(p); +} + +static void xfree(void *p) +{ +#if DEBUGGING + if (debug) fprintf(stderr, "-- free(&%x)\n", (int)p); +#endif + free(p); +} + +static InfoChunk *curchunk; + +static void freechunks(void) +{ InfoChunk *p, *next; + for (p = curchunk; p != NULL; p = next) { next = p->next; xfree(p); } + curchunk = NULL; +} + +static Info *newinfo(Info *next, word v) +{ InfoChunk *chunk; + Info *p; + + chunk = curchunk; + if ((chunk == NULL) || ((p = chunk->free) >= chunk->limit)) { + chunk = (InfoChunk *)xalloc(CHUNKSIZE, "InfoChunk"); + chunk->next = curchunk; + chunk->free = p = align(chunk->chunks, 8); + chunk->limit = (Info *)(((int)chunk) + CHUNKSIZE - sizeof(Info)); + curchunk = chunk; + } + chunk->free = (p + 1); + pack(p, next, 1); p->value = v; + return(p); +} + +static void countwords(word *code, word *limit, Freqs *ans) +/* + * Counts number of occurrences of each word value in the specified block + * of code [code, limit), and returns list of (value, freqency) pairs. + */ +{ HashTab *hash; + Info **list; + Info *p, *next, *freqs; + int j, nzeros, maxfreq; + word w; + + hash = xalloc(sizeof(HashTab), "HashTab"); + for (j = 0; j < HASHSIZE; ++j) (*hash)[j] = NULL; + nzeros = 0; + while (code < limit) { +/* fprintf(stderr, "code %p limit %p\n", code, limit); */ + w = *code++; + if (w == 0) { ++nzeros; continue; } + j = (w + (w >> 11) + (w >> 22)) % HASHSIZE; /* simple hash function */ + list = &((*hash)[j]); + p = *list; + while (1) { + if (p == NULL) { /*fprintf(stderr, "newinfo\n");*/ *list = newinfo(*list, w); /*fprintf(stderr, "newinfo out\n");*/ break; } + unpack(p, next, j); + if (w == p->value) { inccount(p, next, j); break; } + p = next; + } + } /* while code < limit */ +/*fprintf(stderr, "done countwords\n");*/ + /* + * Now flatten the hash table into a single list, and free the vector. + */ + freqs = NULL; maxfreq = 0; + for (j = 0; j < HASHSIZE; ++j) { + for (p = (*hash)[j]; p != NULL; p = next) { + unpack(p, next, w); pack(p, freqs, w); freqs = p; + if (w > maxfreq) maxfreq = w; /* keep track of max frequency */ + } + } + ans->nzeros = nzeros; + ans->maxfreq = maxfreq; + ans->freqs = freqs; + xfree(hash); +} + +static int comparewords(const void *a, const void *b) +/* + * This proc is passed to the library qsort for sorting table elements. + * We know that all table elements are distinct, so can cheat a little. + */ +{ word x = *(word *)a; + word y = *(word *)b; + if (x > y) return(+1); + return(-1); +} + +static void maketable(Freqs *freqs, int maxsize, int wantmisses, VTable *tab) +/* + * Builds a VTable of the most common values in the list freqs, + * taking at most maxsize of them, destroying the freqs list + * in the process, and leaving the remnants hung off the VTable + * record. + */ +{ Info **withfreq = xalloc((freqs->maxfreq+1) * sizeof(Info *), "withfreq"); + Info **list; + Info *p, *next, *misses; + int freq, nhits, size; + + /* + * It is easy to sort things by frequency, as frequency range is + * limited to 1..freqs->maxfreq. So just build a vector of lists. + */ + for (list = withfreq + freqs->maxfreq; list >= withfreq; *list-- = NULL); + + for (p = freqs->freqs; p != NULL; p = next) { /* put p into bucket */ + unpack(p, next, freq); + pack(p, (withfreq[freq]), freq); + withfreq[freq] = p; + } + freqs->freqs = NULL; + + nhits = 0; + misses = NULL; + size = 0; + for (list = withfreq + freqs->maxfreq; list >= withfreq; --list) { + for (p = *list; p != NULL; p = next) { + unpack(p, next, freq); + if (size < maxsize) { /* add to table */ + tab->els[size++] = p->value; nhits += freq; + } else { /* add to misses list */ + if (!wantmisses) break; + pack(p, misses, freq); misses = p; + } + } + } + tab->nhits = nhits; + tab->misses = misses; + tab->size = size; + xfree(withfreq); + qsort((void *)(tab->els), size, sizeof(word), &comparewords); + if (verbose > 1) printf("-- built table in %d csec\n", ticks()); +} + +static void masklowbyte(Info *list, Freqs *ans) +/* + * Take a list of (value, frequency) of 4-byte values, merge values + * with the same low byte to produce list of 3-byte values. + */ +#define VECBITS 12 +#define VECSIZE (1<= vec; *pp-- = NULL); + for (p = list; p != NULL; p = next) { + unpack(p, next, freq); + val = (p->value >> 8); p->value = val; + pp = vec + ((val + (val >> 9) + (val >> 12)) % VECSIZE); + /* + * Now insert p in the ascending-value sorted list pp. + * This is tricky because of the packing of the nextandcount field, + * so have to handle start of list specially. + */ + q = *pp; + if (q == NULL) { pack(p, NULL, freq); *pp = p; continue; } + unpack(q, qnext, qfreq); qval = q->value; + if (val < qval) { pack(p, q, freq); *pp = p; continue; } + if (val == qval) { + qfreq += freq; if (qfreq > MAXCOUNT) qfreq = MAXCOUNT; + pack(q, qnext, qfreq); continue; + } + while (1) { + qprev = q; qprevfreq = qfreq; q = qnext; + if (q == NULL) { /* end of list: add it here */ + pack(p, NULL, freq); pack(qprev, p, qprevfreq); break; + } + unpack(q, qnext, qfreq); qval = q->value; + if (val < qval) { /* not in list: add it */ + pack(p, q, freq); pack(qprev, p, qprevfreq); break; + } + if (val == qval) { /* value matches: add frequency */ + qfreq += freq; if (qfreq > MAXCOUNT) qfreq = MAXCOUNT; + pack(q, qnext, qfreq); break; + } + } + } + /* + * Phew! That should keep the register allocator busy. + * Now we have a vector of sorted lists: just have to flatten it. + */ + q = NULL; maxfreq = 0; + for (pp = vec + VECSIZE-1; pp >= vec; --pp) { + for (p = *pp; p != NULL; p = next) { + unpack(p, next, freq); pack(p, q, freq); q = p; + if (freq > maxfreq) maxfreq = freq; + } + } + ans->maxfreq = maxfreq; + ans->freqs = q; + xfree(vec); +} + +#define FASTSIZE 4096 +#define FASTHASH(v) ((v + (v >> 7) + (v >> 15)) % FASTSIZE) + +static Info **fasttab(VTable *tab) +/* + * Builds a hash table for translating value -> index in table. + */ +{ Info **vec = xalloc(FASTSIZE * sizeof(Info *), "fasthash"); + Info **pp; + int idx; + word val; + Info *p; + + for (pp = vec + FASTSIZE; pp > vec; *--pp = NULL); + + for (idx = 0; idx < tab->size; ++idx) { + val = tab->els[idx]; + pp = vec + FASTHASH(val); + /* + * Values in table are unique, so just add it to chain. + */ + p = newinfo(NULL, val); pack(p, *pp, idx); *pp = p; + } + return(vec); +} + +static int lookup(Info **fast, word val) +{ Info *p, *next; + int idx; + + for (p = fast[FASTHASH(val)]; p != NULL; p = next) { + unpack(p, next, idx); + if (val == p->value) return(idx); + } + return(-1); +} + +#define TOPBYTE(n) ((n)>>24) +#define LOWBYTE(n) ((n)&0xff) +#define PUTLOWBYTE(p, n) (*p++ = (n)) /* relies on store masking low byte */ + +#define ENCODEVALUE(w, nibble, p) \ + if (w == 0) { \ + nibble = ZERO; \ + } else if ((idx = lookup(fshorts, w)) >= 0) { \ + PUTLOWBYTE(p, idx); \ + nibble = SHORT + (idx >> 8); \ + } else if ((idx = lookup(flongs, w>>8)) >= 0) { \ + PUTLOWBYTE(p, w); PUTLOWBYTE(p, idx); \ + nibble = LONG + (idx >> 8); \ + } else { \ + *p++ = TOPBYTE(w); w <<= 8; *p++ = TOPBYTE(w); w <<= 8; \ + *p++ = TOPBYTE(w); w <<= 8; *p++ = TOPBYTE(w); \ + nibble = LITERAL; \ + } + +#define ENCSIZE 8192 + +/* + * We encode a pair of words at a time. To avoid unnecessary copying of data, + * things are done in a rather curious order, not quite the opposite of the + * optimal decoding order. I can't quite convince myself that this is optimal, + * but I think it is quite good. + */ + +static char *encode(word *base, word *limit, Info **fshorts, Info **flongs, + SqueezeHeader *h) +/* + * Returns pointer to byte after the encoded data. + */ +{ word *code; + word w; + int idx, nib0, nib1; + char *buf, *p; + + buf = xalloc(ENCSIZE, "encodebuf"); p = buf; + + h->decodedsize = ((char *)limit - (char *)base); + for (code = base; code < limit; code += 2) { + w = code[1]; ENCODEVALUE(w, nib1, p); + w = code[0]; ENCODEVALUE(w, nib0, p); + *p++ = (nib0 | (nib1 << 4)); + if (buf != NULL) { + idx = ((int)code - (int)base - 12 - (p - buf)); + if (idx > 0) { h->bytestomove = (p - buf); } + if ((idx > 256) || (p - buf > ENCSIZE - 16)) { + /* + * Swap from encoding into buf to encoding on top of old stuff + * once we get 256 bytes clear, or run out of buf space. + */ + memcpy(base+1, buf, p-buf); p = (p-buf) + (char *)(base+1); + xfree(buf); buf = NULL; + } + } else { + if (p >= (char *)(code-2)) + err_fail("pathological file - can't cope"); + } + } + h->encodedsize = p - (char *)(base+1); + return(p); +} + +static char *encodetable(VTable *tab, int threebytes, char *out) +/* + * Encode the table of 3 or 4 byte values, starting at address p, + * return pointer to first free byte after table. + */ +{ word *p, *limit; + word prev, w; + int delta, ones; + + ones = 0; prev = (word)(-1); + p = tab->els; limit = p + tab->size; + while (p < limit) { + w = *p++; delta = (w - prev); prev = w; + if (delta == 1) ++ones; + if ((ones > 0) && ((delta != 1) || (ones >= 9))) { + *out++ = ones; ones = 0; + } + if (delta < 2) { /* dealt with above: no zeros, ones are peepholed */ } + else if (delta <= 91-10) { *out++ = delta+10; } + else if (delta < 82*256) { + *out++ = (delta>>8)+92; *out++ = LOWBYTE(delta); + } + else if (delta < 82*256*256) { + *out++ = (delta>>16)+174; + *out++ = LOWBYTE(delta); delta >>= 8; + *out++ = LOWBYTE(delta); + } + else { + *out++ = 0; + *out++ = LOWBYTE(delta); delta >>= 8; + *out++ = LOWBYTE(delta); delta >>= 8; + *out++ = LOWBYTE(delta); + if (!threebytes) { delta >>= 8; *out++ = delta; } + } + } /* end loop over values in table */ + if (ones > 0) *out++ = ones; + return(out); +} + +static char *writeunsqueeze(char *out, int execoffset) +{ int base, limit; + word *p; + int n, rotr, op; + + /* UnSqueeze_FindUnSqueezeCode(&base, &limit); */ + base = (int)&UnSqueeze_UnSqueezeBase; + limit = (int)&UnSqueeze_UnSqueezeLimit; + n = limit - base; + memcpy(out, (void *)base, n); out += n; + /* + * Now construct ARM code to jump to exec address, starting with + * load address in R8. + */ + op = ADD_R8_R8_0; + if (execoffset < 0) { op = SUB_R8_R8_0; execoffset = -execoffset; } + rotr = 32; p = (word *)out; + while (execoffset > 0) { + /* Generate ADD/SUB R8, R8, #thing */ + n = LOWBYTE(execoffset); execoffset >>= 8; + if (n != 0) { + *p++ = op + (0x100 * ((rotr % 32) / 2)) + n; + } + rotr -= 8; + } + *p++ = SUB_R1_R8_IMM4; + *p++ = SWI_XOS_SynchroniseCodeAreas; + *p++ = MOV_PC_R8; + return((char *)p); +} + +static char *compresscode(word *code, int size, int execoffset) +/* + * Returns NULL if no compression, else pointer to top of compressed thing. + */ +{ Freqs freqs; + word *limit; + VTable *shorts, *longs; + Info **fshorts, **flongs; + int nwords, guess, nliterals; + SqueezeHeader header; + char *pos, *tabstart; + + size += 7; size &= ~7; /* round up to an even number of words */ + limit = (word *)((char *)code + size); + countwords(code, limit, &freqs); + if (verbose > 1) printf("-- counted %d bytes in %d csec\n", size, ticks()); + /* + * Allocate the VTables here to avoid nasty storage interactions, which + * can lose us some chunks if we're not careful. + */ + shorts = xalloc(sizeof(VTable), "shorts"); + longs = xalloc(sizeof(VTable), "longs"); + maketable(&freqs, NSHORTS*256, 1, shorts); + masklowbyte(shorts->misses, &freqs); + if (verbose > 1) printf("-- masklowbyte took %d csec\n", ticks()); + maketable(&freqs, NLONGS*256, 0, longs); + freechunks(); + /* + * Now guess what the size of the compressed thing would be. + * The estimates of size of encoded data are exact, but the + * estimates of encoded table size are a guess, so we over-estimate + * the size of the decompression code to be on the safe side. + */ + nwords = (size / sizeof(word)); + nliterals = nwords - freqs.nzeros - shorts->nhits - longs->nhits; + + guess = (nwords / 2) /* 0.5 bytes per word of original */ + + (1 * shorts->nhits) /* 1 more byte for each short */ + + (2 * longs->nhits) /* 2 for each long */ + + (4 * nliterals) /* 4 for each literal */ + + (2 * shorts->size) /* rough size of shorts table */ + + (2 * longs->size) /* rough size of longs table */ + + 1024; /* decompression code + safety margin */ + + if (verbose) + fprintf(stderr, "-- encoding stats (0,1,2,4) %d%% %d%% %d%% %d%%\n", + (freqs.nzeros * 100) / nwords, + (shorts->nhits * 100) / nwords, + (longs->nhits * 100) / nwords, + (nliterals * 100) / nwords); + + if (guess > (9*size)/10) { /* not much squeeze to be had */ + if ((!force) || (guess > size)) return(NULL); + } + + /* + * Now can actually start encoding stuff. + */ + fshorts = fasttab(shorts); + flongs = fasttab(longs); + if (verbose > 1) fprintf(stderr, "-- built speedup tables in %d csec\n", ticks()); + pos = encode(code, limit, fshorts, flongs, &header); + xfree(flongs); + xfree(fshorts); + freechunks(); + if (verbose > 1) + fprintf(stderr, "-- encode gives %d in %d csec\n", header.encodedsize, ticks()); + tabstart = pos; + pos = encodetable(shorts, 0, pos); + pos = encodetable(longs, 1, pos); + header.nshorts = shorts->size; + xfree(shorts); + header.nlongs = longs->size; + xfree(longs); + /* now word-align before the header words */ + while (((int)pos & 3) != 0) *pos++ = 0; + header.encodedtabs = (pos - tabstart); + if (verbose > 1) + fprintf(stderr, "-- decode tables occupy %d bytes\n", header.encodedtabs); + memcpy(pos, &header, sizeof(SqueezeHeader)); pos += sizeof(SqueezeHeader); + /* + * Now the branch instruction to be put at the start: this has a word + * offset, with suitable allowance for ARM prefetch. + * In fact we want to skip the first 3 instructions of decompression + * code, as these are executed only for an AIF image. + */ + *code = B + ((word *)pos - code) + AIFPRELUDE - PREFETCH; + pos = writeunsqueeze(pos, execoffset); + pos += sprintf(pos, "rcc %s\n", Module_MajorVersion); + /* Pad size to be 15 mod 16 */ + while ((((int)pos - (int)code) & 0xf) != 0xf) *pos++ = ' '; + return(pos); +} + +static char *compress(word *code, int size, int execoffset) +/* + * This handles the AIF-specific stuff. + * Returns NULL if no compression, else pointer to top of compressed thing. + */ +{ char *top; + + if (code[0] != BL+NV && code [0] != MOV_R0_R0) + { /* not BLNV $+0, not NOOP => not an AIF image */ + return compresscode(code, size, execoffset); + } + top = compresscode(code+AIFWORDS, size-AIFBYTES, execoffset-AIFBYTES+4); + /* + * Now first word of the stuff we have just compressed is + * B UnsqueezeADFSImage. We want the first word of the header to + * be BL UnsqueezeAIFImage, i.e. destination AIFPRELUDE words earlier. + */ + code[0] = BL + (code[32] & 0x00ffffff) + AIFWORDS - AIFPRELUDE; + return(top); +} + +#ifdef __riscos +static void arthurise(_kernel_osfile_block *info, int type) +{ int data[2]; + if ((info->load == info->exec) && (info->load == 0x8000)) { + /* can we use Arthur 'FF8' filetype ? */ + if (_kernel_hostos() == _kernel_ARTHUR) { + /* This is Arthur - get time of day */ + if (verbose > 1) fprintf(stderr, "-- getting timestamp from Arthur\n"); + data[0] = 3; + (void) _kernel_osword(14, data); + info->exec = data[0]; + info->load = 0xfffff800 + (data[1] & 0xff); + if (type) + info->load = type + (data[1] & 0xff); + } + } +} +#endif + +static int squeeze(char *in, char *out) +{ _kernel_osfile_block info; + int rc, size, t, squeezed, isdata; + void *load; + datahdr *d; + word *code; + char *top, *p; + int type = 0; + + if (verbose) fprintf(stderr, "-- squeezing '%s' to '%s'\n", in, out); + squeezed = 0; isdata = 0; + if (fileinfo(&info, in) == -1) err_fail("'%s' does not exist", in); + size = info.start; + if ((!force) && (strcmp(in, out) == 0)) { + /* Check quickly to see if worth loading */ + if (size < 2048) { + err_report("'%s' is too small to squeeze", in); + return(0); + } + if ((size & 0xf) == 0xf) { + err_report("'%s' is already squeezed", in); + return(0); + } + } + if ((info.load & 0xffffff00) == 0xfffffa00) { /* Module */ + int header_size; + + header_size = (int)&unsqueeze_limit - (int)&unsqueeze_base; + unsqueeze_end = size; + size += header_size; + code = xalloc(size+DATABYTES+8, "code"); + d = (datahdr *)code; code += DATAWORDS; + top = ((char *)code) + size+8; + for (p = top-8; p < top; *p++ = 0); /* Clear the padding space */ + memcpy(code, &unsqueeze_base, header_size); + load = code + header_size; + } else { + code = xalloc(size+DATABYTES+8, "code"); /* Pad to even no of words */ + d = (datahdr *)code; code += DATAWORDS; /* Space for data header */ + top = ((char *)code) + size+8; + for (p = top-8; p < top; *p++ = 0); /* Clear the padding space */ + load = code; + } + if (wf_load(in, load, info.start) == -1) + err_fail("can't load '%s'", in); + if ((info.load & 0xfff00000) == 0xfff00000) { + type = (info.load & ~0xff); + if (type == 0xfffffa00) + type = 0xfffff800; + info.load = 0x8000; + info.exec = 0x8000; + } else { + if (info.load & 0xfc000000) { + isdata = 1; d->datamagic = DATAMAGIC; + d->load = info.load; d->exec = info.exec; d->size = size; + } + } + if (verbose > 1) fprintf(stderr, "-- loaded %d bytes in %d csec\n", size, ticks()); + t = clock(); + + if (isdata) top = compresscode(code, size, -DATABYTES + 4); + else top = compress(code, size, info.exec - info.load); + + if (top != NULL) { + t = clock() - t; + if (isdata) { + d->bl_decompress = code[0] + DATAWORDS; /* dirty... */ + code -= DATAWORDS; + info.load = SQUEEZED | (info.load & 0xff); + } else + info.exec = info.load; + rc = (top - (char *)code); + if (verbose) { + fprintf(stderr, "-- compressed size %d is %d%% of %d\n", rc, (rc*100)/size, size); + fprintf(stderr, "-- compression took %d csec, %d bytes/cpusec\n", t, (size*100)/(t?t:1)); + } + squeezed = 1; + } else { + top = (char *)code + size; + if (verbose) err_report("can't compress '%s', will copy", in); + } + if (squeezed || (strcmp(in, out) != 0)) { /* Write it out */ +#ifdef __riscos + arthurise(&info, type); +#endif + if (wf_save(out, code, top - (char *)code) == -1) + err_fail("failed to write '%s'", out); +#ifdef __riscos + _swix(OS_File, _INR(0,2), 18, out, (info.load << 12) >> 20); +#endif + } + xfree(d); +#if DEBUGGING + if (debug) { + printf("-- heaphwm = &%x = &8000+%d\n",(int)heaphwm,(int)heaphwm-0x8000); + printf("-- heaplwm = &%x, range = %d\n", (int)heaplwm, heaphwm - heaplwm); + } +#endif + return(0); +} + +static void help(void) +{ char ch = host_dir_sep_char(); + fprintf(stderr, "\n%s vsn %s [%s] - %s\n", SELF, VSN, DATE, BRIEF); + fprintf(stderr, "\n%s [options] unsqueezed-file [squeezed-file]\n", SELF); + fprintf(stderr, "\nOptions:-\n"); + fprintf(stderr, "-f try harder to squeeze unpleasant things\n"); + fprintf(stderr, "-v output messages and compression statistics\n"); + fprintf(stderr, "\nExamples:-\n"); + fprintf(stderr, " %s myprog %s -v myprog squozen%cmyprog\n", SELF, SELF, ch); + exit(EXIT_SUCCESS); +} + +static void handle_escape(int signo) +{ + signal(signo, handle_escape); + exit(EXIT_FAILURE); +} + +static void initialise(void) +{ + signal(SIGINT, handle_escape); + host_init(); + err_init(SELF); + debug = 0; force = 0; verbose = 0; + curchunk = NULL; + ticks(); +} + +int main(int argc, char *argv[]) +{ int j; + char *arg; + char *a = NULL; + char *b = NULL; + + initialise(); + + /* parse help or identify args */ + for (j = 1; j < argc; ++j) { + arg = argv[j]; + if (hash_cistrcmp("-help", arg) == 0 || hash_cistrcmp("-h", arg) == 0) { + help(); + } + } + + /* parse args */ + for (j = 1; j < argc; ++j) { + arg = argv[j]; + if (arg[0] == '-') { + int i = 1; + while (arg[i]) { + switch (arg[i]) { + case 'f': + case 'F': ++force; break; + case 'v': + case 'V': ++verbose; break; +#if DEBUGGING + case 'z': + case 'Z': ++debug; break; +#endif + case 'o': + ++j; + if (argv[j]) + b = argv[j]; + else + err_fail("no filename on -o flag\n"); + break; + default : err_fail("flag '%c' not recognised", arg[i]); + } + ++i; + } + } else { /* a filename */ + if (a == NULL) a = arg; + else if (b == NULL) b = arg; + else err_fail("too many filename arguments '%s'\n", arg); + } + } + if (a == NULL) err_fail("missing filename"); + if (b == NULL) b = a; /* squeeze it to itself */ + +#if DEBUGGING + if (debug) { heaplwm = (char *)0x03ffffff; heaphwm = NULL; } +#endif + + return(squeeze(a, b)); +} -- cgit v1.2.3