diff options
-rw-r--r-- | CastleLicence.txt | 159 | ||||
-rw-r--r-- | VersionNum | 23 | ||||
-rw-r--r-- | asmcall.s | 63 | ||||
-rw-r--r-- | squeeze.c | 972 | ||||
-rw-r--r-- | squeeze.h | 77 | ||||
-rw-r--r-- | unsqrm.s | 94 | ||||
-rw-r--r-- | unsqueeze.s | 468 | ||||
-rw-r--r-- | xpand.c | 348 |
8 files changed, 2204 insertions, 0 deletions
diff --git a/CastleLicence.txt b/CastleLicence.txt new file mode 100644 index 0000000..721bb3e --- /dev/null +++ b/CastleLicence.txt @@ -0,0 +1,159 @@ + * [topbar] [topba] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ * * [topbar] * +[l] Welcome to www.castle-technology.co.uk [r] + Castle + Technology ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ * * [bottombar] * + Ltd. ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ * * [topbar] * + [diag] Castle Technology Ltd [r] [l] [r] + + * [bottomb] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ * * [bottombar] * +* [topbar] * +[l] [r] + +* [bottombar] * Download as a pdf file here +* [topbar] * +[l] [r] LICENCE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + OF RISC OS AND ITS DERIVATIVES +* [bottombar] * + This licence agreement ("Licence") is between Castle Technology Limited + ("Castle") and any person ("You", "Your") who exercises any of the legal + rights over any components of the computer operating system known as + RISC OS granted by Castle in this Licence. The phrase "RISC OS" used in + this Licence refers to those components of version 5.0 of RISC OS that + Castle chooses to release from time to time and any subsequent release of + RISC OS which is made generally available by Castle pursuant to this + Licence. The phrase "Derivative Work" means any derivative work which is + based on, or derived from, RISC OS or any part of RISC OS (whether by + modification or translation) and any work subsequently derived from such + Derivative Work or any part of it PROVIDED THAT such work is only intended + to be used in conjunction with an embodiment (whether physical or emulated) + of one or more versions of the ARM processor architecture. By exercising + any of the rights granted to You by Castle pursuant to this Licence You + indicate Your acceptance of all of the terms and conditions of this Licence + which shall therefore be legally binding on You. + + 1 LICENCE GRANT + 1.1 Subject to the restrictions set out in the remainder of this Licence + Castle hereby grants to You a non-exclusive royalty free worldwide licence + to use, copy and distribute the RISC OS source code and object code in any + medium PROVIDED THAT you ensure that each copy You distribute incorporates + the text of, or an Internet link to, this Licence. + 1.2 You may modify your copy of RISC OS or any part of it (and any Derivative + Work or any part of it) to create a Derivative Work and You may copy and + distribute the source code and object code of such Derivative Work in any + medium PROVIDED THAT you meet all of the following conditions: + 1.2.1 You shall ensure that each Derivative Work You distribute carries a + notice stating that it is ultimately derived from RISC OS and the date of + Your modifications; + 1.2.2 You shall ensure that each Derivative Work You distribute as source + code prominently carries the following header text in each individual source + code file: + 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 + 1.2.3 You shall ensure that each Derivative Work distributed by You is + distributed and licensed in its entirety at no charge on the terms of this + Licence to any third party who is prepared to accept the terms of this Licence. + If any part of any Derivative Work distributed by You can reasonably be + considered an independent and separate work then this Licence shall not apply + to any such part where You distribute such part as an independent and separate + work; + 1.2.4 You undertake, in respect of each Derivative Work that You distribute, + to make the whole source code of every individual component of the Derivative + Work available for unrestricted access by third parties by including a copy of + the source code with Your distribution and/or by uploading the source code to + the official RISC OS source code repository at + www.riscosopen.org/content/sources + 1.2.5 You hereby grant to Castle a royalty free worldwide licence (with the + right to grant sub-licences) to incorporate into RISC OS or any Derivative + Work produced by Castle, the whole or any part of any Derivative Work which + You create or distribute pursuant to this Licence. You acknowledge that + Castle may make the whole or any part of RISC OS or any Derivative Work + produced by Castle available to OEM organisations on commercial terms for + incorporation into hardware products intended for sale. Your hereby waive any + moral rights You may have in any part of any Derivative Work which Castle + so incorporates into RISC OS or any Derivative Work produced by Castle. + Nothing in this Clause 1.2.5 shall oblige Castle to incorporate into + RISC OS or any Derivative Work produced by Castle any part of any Derivative + Work which You create or distribute pursuant to this Licence. + + 2 LICENCE RESTRICTIONS + 2.1 You may not copy, modify, sublicense or distribute RISC OS or any + Derivative Work except as expressly provided under this Licence. Any attempt + by You otherwise to copy, modify, sublicense or distribute RISC OS or any + Derivative Work shall be void and shall lead to the automatic termination + of Your rights under this Licence but such termination shall not affect the + rights of any person to whom You have distributed RISC OS or any Derivative + Work provided they continue to comply with the terms and conditions of this + Licence. + 2.2 You may not impose on any third party to whom You have distributed copies + of RISC OS or any Derivative Work any licence restrictions other than those + set out in this Licence. + 2.3 If, as a consequence of any court judgment, allegation of infringement of + any proprietary right or any other reason You are unable to comply fully with + the terms and conditions of this Licence then You shall refrain from + distributing RISC OS or such Derivative Works (as appropriate) for the + duration of such non-compliance. + 2.4 This Licence is not an OEM licence. Consequently, You may not incorporate + or embed RISC OS or any Derivative Work or any part of them in any hardware + product which is intended for commercial sale, nor may You distribute, sell, + supply or otherwise dispose of any such hardware product unless You have + obtained the prior written consent of Castle. Castle is generally prepared + to grant OEM licences on commercial terms to any person who wishes to + incorporate or embed RISC OS or any Derivative Work or any part of them into + a hardware product and/or to sell, supply or otherwise dispose of such + hardware products. Details of Castle's standard OEM licensing terms are + available from Castle's website at + www.castle-techology.co.uk/riscosoemlicence.htm or + by email from oem.enquiries@castle-technology.co.uk + + 3 NO WARRANTY + 3.1 RISC OS AND ALL DERIVATIVE WORKS ARE SUPPLIED "AS IS" WITHOUT ANY + WARRANTIES OF ANY KIND WHETHER EXPRESS OR IMPLIED INCLUDING BUT NOT LIMITED + TO ANY IMPLIED WARRANTIES OR CONDITIONS THAT RISC OS AND/OR ANY DERIVATIVE + WORK IS ERROR FREE, FIT FOR ANY PARTICULAR PURPOSE OR THAT ITS USE, + MODIFICATION OR DISTRIBUTION SHALL NOT LEAD TO INFRINGEMENT OF ANY THIRD + PARTY PROPRIETARY RIGHTS. ALL IMPLIED WARRANTIES AND CONDITIONS ARE + EXCLUDED TO THE EXTENT PERMITTED BY LAW. + 4 DISCLAIMER OF LIABILITY + 4.1 IN NO EVENT SHALL CASTLE OR ANY PERSON WHO HAS MODIFIEID AND/OR + DISTRIBUTED THE WHOLE OR ANY PART OF RISC OS OR ANY DERIVATIVE WORK IN + ACCORDANCE WITH THIS LICENCE BE LIABLE TO YOU FOR ANY LOSS OR DAMAGE + WHATSOEVER ARISING OUT OF YOUR USE, MODIFICATION OR DISTRIBUTION OR YOUR + INABILITY TO USE, MODIFY OR DISTRIBUTE RISC OS OR ANY DERIVATIVE WORK OR + YOUR EXERCISE OF ANY OF THE RIGHTS GRANTED UNDER THIS LICENCE (INCLUDING + BUT NOT LIMITED TO LOSS OF OR DAMAGE TO YOUR OR ANY THIRD PARTY'S DATA + CAUSED BY ANY DEFECT IN RISC OS OR ANY DERIVATIVE WORK OR ANY + INCOMPATIBILITY BETWEEN RISC OS OR ANY DERIVATIVE WORK AND ANY OTHER + SOFTWARE). ALL SUCH LIABILITIES ARE EXCLUDED TO THE MAXIMUM EXTENT + PERMITTED BY APPLICABLE LAW. + + + Document No.1852007 version 1 dated 18th May 2007 + + + + + + + + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + © Castle Technology Ltd 2001-2011. Castle is a trading name of Castle + Technology Ltd. IYONIX and the IYONIX logo are trademarks of Castle + Technology Ltd. +RISC OS is © Castle Technology Ltd. XScale is a registered trademark of +Intel Corporation. ARM is a registered trademark of ARM Ltd. All other + trademarks acknowledged. E&OE + diff --git a/VersionNum b/VersionNum new file mode 100644 index 0000000..5f76dd3 --- /dev/null +++ b/VersionNum @@ -0,0 +1,23 @@ +/* (5.12) + * + * This file is automatically maintained by srccommit, do not edit manually. + * Last processed by srccommit version: 1.1. + * + */ +#define Module_MajorVersion_CMHG 5.12 +#define Module_MinorVersion_CMHG +#define Module_Date_CMHG 05 Feb 2012 + +#define Module_MajorVersion "5.12" +#define Module_Version 512 +#define Module_MinorVersion "" +#define Module_Date "05 Feb 2012" + +#define Module_ApplicationDate "05-Feb-12" + +#define Module_ComponentName "squeeze" +#define Module_ComponentPath "castle/RiscOS/Tools/Sources/squeeze" + +#define Module_FullVersion "5.12" +#define Module_HelpVersion "5.12 (05 Feb 2012)" +#define Module_LibraryVersionInfo "5:12" diff --git a/asmcall.s b/asmcall.s new file mode 100644 index 0000000..1523187 --- /dev/null +++ b/asmcall.s @@ -0,0 +1,63 @@ +; 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 +; +; > s.asmcall RCC 24-Mar-88 +; +; This makes it possible to call code which scribbles on all the +; registers with a standard ARM procedure call, e.g. from C. +; +; If the code called also scribbles on lk, you must regain control +; by forcing it to branch to asmcall_exit. +; +; NB it relies on scribbling on memory addressed pc-relative, so it +; won't work if the code area is not writable. +; + +r0 RN 0 +r1 RN 1 +r2 RN 2 +r3 RN 3 +r4 RN 4 +ip RN 12 +lk RN 14 +pc RN 15 + + AREA |C$$code|, CODE, READONLY + EXPORT asmcall_call + EXPORT asmcall_exit + +asmcall_savedregs + DCD 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 11 words for registers +asmcall_call + ; Enter with r0: thing to call + ; r1, r2, r3: parameters for it + + ADR ip, asmcall_savedregs + STMIA ip, {r4-lk} + MOV ip, r0 + MOV r0, r1 + MOV r1, r2 + MOV r2, r3 + MOV lk, pc + MOV pc, ip +asmcall_exit + NOP ; 2 spurious instructions here in case the caller + NOP ; forgets to allow for prefetch ... + ADR ip, asmcall_savedregs + LDMIA ip, {r4-lk} + MOV pc, lk + END 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 <string.h> +# include <stdlib.h> +#else +# include <strings.h> +/* extern char *malloc(); */ +/*# define DATE "Mar 07 1991" */ +#endif + +#include "VersionNum" + +#include <stdio.h> +#include <time.h> +#include <signal.h> +#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 <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#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<<COUNTBITS)-1) + +#define unpack(p,n,c) { word t = p->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<<VECBITS) +{ Info **vec = xalloc(VECSIZE * sizeof(Info *), "mergevec"); + Info **pp; + Info *p, *next; + Info *q, *qnext, *qprev; + int freq, qfreq, qprevfreq, maxfreq; + word val, qval; + + for (pp = vec + VECSIZE-1; pp >= 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)); +} diff --git a/squeeze.h b/squeeze.h new file mode 100644 index 0000000..920bfb7 --- /dev/null +++ b/squeeze.h @@ -0,0 +1,77 @@ +/* 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 + */ +typedef unsigned int word; +typedef enum { NO, YES } bool; + +typedef struct aifhdr { + word bl_decompress; + word bl_selfreloc; + word bl_zeroinit; + word bl_imageentry; + word swi_exit; + int codesize; + int datasize; + int debugsize; + int zerosize; + int debugtype; + int imagebase; + int reserved[5]; + word zeroinit[16]; +} aifhdr; + +#define AIFBYTES sizeof(aifhdr) /* size in bytes of an AIF header */ +#define AIFWORDS (AIFBYTES / sizeof(int)) +#define AIFPRELUDE 5 /* no of extra instructions in AIF decompression */ +#define PREFETCH 2 /* words of ARM prefetch */ + +typedef struct datahdr { + word bl_decompress; + word datamagic; + word load; + word exec; + word size; +} datahdr; + +#define DATABYTES sizeof(datahdr) +#define DATAWORDS (DATABYTES / sizeof(int)) +#define DATAMAGIC 0x213542 +#define SQUEEZED 0xffffea00 + +typedef struct SqueezeHeader { + int decodedsize; + int encodedsize; + int encodedtabs; + int nshorts; + int nlongs; + int bytestomove; +} SqueezeHeader; + +#define SQUEEZEBYTES sizeof(SqueezeHeader) +#define SQUEEZEWORDS (SQUEEZEBYTES / sizeof(int)) + +#define B 0xea000000 +#define BL 0xeb000000 +#define B_OFFSET 0x00ffffff +#define MOV_PC_R8 0xe1a0f008 +#define ADD_R8_R8_0 0xe2888000 +#define SUB_R8_R8_0 0xe2488000 +#define MOV_R0_R0 0xe1a00000 +#define LDR_PC_R8_MINUS4 0xe518f004 +#define NV (0xf0000000 - 0xe0000000) +#define SUB_R1_R8_IMM4 (0xE2481004) +#define SWI_XOS_SynchroniseCodeAreas (0xEF02006E) diff --git a/unsqrm.s b/unsqrm.s new file mode 100644 index 0000000..8a2797c --- /dev/null +++ b/unsqrm.s @@ -0,0 +1,94 @@ +; 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 +; +r0 RN 0 +r1 RN 1 +r2 RN 2 +r3 RN 3 +r4 RN 4 +r5 RN 5 +r6 RN 6 +r12 RN 12 +lr RN 14 +pc RN 15 + +OS_GetEnv EQU &10 +OS_Exit EQU &11 +XOS_Module EQU &2001e +OS_Module EQU &1e +OS_GenerateError EQU &2b +OS_ChangeEnvironment EQU &40 +OS_ReadDefaultHandler EQU &55 + +XWimp_SlotSize EQU &600ec + +n_module_run EQU 2 +n_module_load EQU 11 + +n_upcall_h EQU 16 + +o_run_entry EQU &00 +o_title_entry EQU &10 + + EXPORT unsqueeze_base + EXPORT unsqueeze_end + EXPORT unsqueeze_limit + + AREA unsqueeze, CODE, READONLY + +unsqueeze_base MOV r0, r0 + MOV r0, #-1 + MOV r1, #-1 + SWI XWimp_SlotSize + MOV r6, #0 + MOVVC r6, r0 + ADR r0, unsqueeze_end + LDR r1, [r0], #4 + ADD r0, r0, r1 + SUB r0, r0, #&8000 + MOV r1, #-1 + SWI XWimp_SlotSize + MOV r0, #n_upcall_h + ADR r1, upcall_handler + SWI OS_ChangeEnvironment + ADR r1, unsqueeze_end + LDR r2, [r1], #4 + MOV r0, #n_module_load + SWI XOS_Module + MOV r5, r0 + MOV r0, #n_upcall_h + SWI OS_ReadDefaultHandler + SWI OS_ChangeEnvironment + MOVS r0, r6 + MOVNE r1, #-1 + SWINE XWimp_SlotSize + MOV r0, r5 + CMP r0, #n_module_load + SWINE OS_GenerateError + SWI OS_Exit + +upcall_handler SUB r12, r0, #256 + CMP r12, #1 + MOVEQ r0, #0 + MOV pc, lr + +unsqueeze_end DCD 0 + +unsqueeze_limit +module_start + + END diff --git a/unsqueeze.s b/unsqueeze.s new file mode 100644 index 0000000..85e0dc2 --- /dev/null +++ b/unsqueeze.s @@ -0,0 +1,468 @@ +; 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 +; +; +; s.UnSqueeze by RCC 25-Aug-87 +; This is a bit of code to be included in self-decompressing images to +; expand the image in place. See elsewhere for details of the compression +; algorithm. +; +; *********************************** +; *** C h a n g e L i s t *** +; *********************************** + +; Date Name Description +; ---- ---- ----------- +; 13-Feb-90 TDobson Minor optimisation which saves 1 instruction for +; every output word that isn't a "short" or a "long". + + AREA |M2$$Data|, DATA +; EXPORT |UnSqueeze_C$| +; EXPORT |UnSqueeze_CS$| + EXPORT |UnSqueeze_UnSqueezeBase| + EXPORT |UnSqueeze_UnSqueezeLimit| +; EXPORT |UnSqueeze_D$| +; EXPORT |UnSqueeze_FindUnSqueezeCode| +; IMPORT |SYSTEM.STKOVF| +; IMPORT |SYSTEM.RAISE| +|UnSqueeze_D$| + % 4 + AREA |M2$$Code|, CODE, READONLY + +R0 RN 0 +R1 RN 1 +R2 RN 2 +R3 RN 3 +R4 RN 4 +R5 RN 5 +R6 RN 6 +R7 RN 7 +R8 RN 8 +R9 RN 9 +R10 RN 10 +R11 RN 11 +R12 RN 12 +R13 RN 13 +LR RN 14 +PC RN 15 + +|UnSqueeze_CS$| EQU 40 +|UnSqueeze_C$| + +StackSize * 64 +decodedSize * 0 +encodedSize * 4 +tableSize * 8 +nShorts * 12 +nLongs * 16 +sizeToMove * 20 + + GBLL expand_memcheck +expand_memcheck SETL {TRUE} + + [ expand_memcheck +; GET hdr:ListOpts +; GET hdr:Macros +; GET hdr:System +; GET hdr:MsgTrans +OS_GetEnv EQU &10 +OS_GenerateError EQU &2b +XOS_SynchroniseCodeAreas EQU &2006e +XMessageTrans_ErrorLookup EQU &61506 + ] + +; Constants defining partition of nibble value space: these must match +; corresponding values in mod.squeeze. + +NibsLong * 7 +NibsShort * (14-NibsLong) +MinShort * (2+NibsLong) +MinLong * 2 + +; Code between UnSqueezeBase and UnSqueezeLimit will be copied into +; the start of a squeezed image, and when the image is loaded it will +; jump to the start. + +; Before start of unsqueeze code there will be 6 words to tell +; it where the data is, how big it is etc. + +|UnsqueezeDataBlock| + NOP + NOP + NOP + NOP + NOP + NOP + +|UnSqueeze_UnSqueezeBase| +|UnsqueezeAIFImage| + ; If it was an AIF image, we enter here and overwrite the BL decompress + ; xpand relies on the first instruction here being MOV r0, #<imm> + MOV R0, #&E1000000 ; hex for instruction MOV r0, r0 + ORR R0, R0, #&00A00000 + SUB R1, LR, PC ; mode independent status bit removal + ADD R1, PC, R1 ; R1 = LR (- any PSR bits if there) + 4 + STR R0, [R1, #-8]! ; overwrite the instruction we just BL'ed from + +|UnsqueezeADFSImage| + [ UnsqueezeADFSImage - UnsqueezeAIFImage <> 5*4 + ! 1, "Change AIFPRELUDE in squeeze.h" + ] + ; We arrive here knowing very little about anything. + ; First find out where we are, and where the tables start. + + ADR R0, |UnsqueezeDataBlock| ; R0 points to data (PC-relative) + LDMIA R0, {R8-R13} ; load all the data + ; R13 := sizeToMove + ; R12 := nLongs + ; R11 := nShorts + SUB R10, R0, R10 ; R10 := base of encoded tables + SUB R9, R10, R9 ; R9 := base of encoded data + ADD R8, R9, R8 ; R8 := top of decoded image + + ; We only need nLongs and nShorts while we are decoding the tables. + ; Afterwards we will re-use the registers for pointers to start + ; of tables. + + ; SWI &10 ; GetEnv - returns RAM limit in R1 + ; SUB R6, R1, #&4000 ; grab 16K workspace, remember table base + ADR R6, |UnSqueeze_UnSqueezeLimit|+24 ; top of squeezed image + CMP R6, R8 ; find highest of top of squeezed and unsqueezed image + MOVLO R6, R8 ; use SWI if you prefer... (UNIX ?) + + ; Allocate space for tables + ADD R1, R11, R12 ; nLongs + nShorts + ADD R7, R6, R1, LSL #2 ; curFree += (nLongs + nShorts) * 4; + + [ expand_memcheck + SWI OS_GetEnv ; returns RAM limit in R1 + ; R7 points to end of tables; add space required for copied-up + ; decode routine. + ADD R2,R7, #|UnSqueeze_UnSqueezeLimit| + 24 - decodeImage + ; if PC < the RAM limit, and R2 > the RAM limit, we're in trouble + ; (if PC > the RAM limit, we assume we're not in the application slot) + CMP PC,R1 + CMPLO R1,R2 + BLO expand_would_overwrite + ] + + MOV R5, R10 ; R5 is ptr into encoded tables + MOV R4, #0 ; this is the first table el +decodeTab + ; Require: R11 -- no of els left to decode + ; R6 -- ptr into decoded table + ; R5 -- ptr into encoding + ; R4 -- = 0 iff this is the shorts table (i.e. 4-byte vals) + +; I believe this loop could be made good deal smaller and possibly +; faster, but it's only a couple of hundred bytes and it works. + + + MOV R2, R6 ; stash away base of first table + MOV R3, #-1 ; start as if previous entry was -1 +decodeEntry + SUBS R11, R11, #1 ; while (--nEntries >= 0) { + BLT decodedTab ; assert: previous word is in R3 + LDRB R1, [R5], #1 ; byte = *p++ + SUBS R0, R1, #10 + BGE greaterThan9 +literalOrOnes + CMPS R1, #0 + BNE ones +literal + LDRB R0, [R5], #1 + LDRB R1, [R5], #1 + ORR R0, R0, R1, LSL #8 + LDRB R1, [R5], #1 + ORR R0, R0, R1, LSL #16 + CMPS R4, #0 ; in the 4-byte (short encodings) table? + LDREQB R1, [R5], #1 ; yes, so include the 4th byte + ORREQ R0, R0, R1, LSL #24 ; in the resultant word + ADD R3, R3, R0 + STR R3, [R6], #4 + B decodeEntry +ones + SUB R11, R11, R1 + ADD R11, R11, #1 +anotherOne ; Have number of increment-by-ones in R1 + ADD R3, R3, #1 + STR R3, [R6], #4 + SUBS R1, R1, #1 + BGT anotherOne + B decodeEntry +greaterThan9 + CMPS R1, #92 + ADDLT R3, R3, R0 + STRLT R3, [R6], #4 + BLT decodeEntry +greaterThan91 + SUBS R0, R1, #174 + BLT oneMore +twoMore + LDRB R1, [R5], #1 + ORR R0, R1, R0, LSL #16 + LDRB R1, [R5], #1 + ORR R0, R0, R1, LSL #8 + ADD R3, R3, R0 + STR R3, [R6], #4 + B decodeEntry +oneMore + SUBS R0, R1, #92 + LDRB R1, [R5], #1 + ORR R0, R1, R0, LSL #8 + ADD R3, R3, R0 + STR R3, [R6], #4 + B decodeEntry ; } /* end while (--nEntries >= 0) { */ + +decodedTab + CMPS R4, #0 ; if isShorts then + BNE finishLongs ; else finishLongs +finishShorts + MOV R11, R12 ; no of els to decode = nLongs + MOV R12, R2 ; R12 = &shorts[0] + MOV R2, R6 ; stash away start of longs table + MOV R4, #1 ; next table is longs + B decodeTab + [ {TRUE} + ; ROL has adopted a policy in their fork of the OS of not allowing + ; compressed binaries to run unless their version of UnsqueezeAIF + ; recognises characteristic instruction sequences within the + ; application's unsqueeze code, which it knows how to patch up and + ; run during Service_UKCompression 0. The justification for this is + ; apparently that they didn't like the fact that the binary is + ; still compressed during Service_UKCompression 1 if UnsqueezeAIF + ; drops back to letting the application unsqueeze code run itself + ; when normal execution at &8000 starts. So, basically they're + ; saying: if this is an application which UnsqueezeAIF doesn't + ; currently know for a fact to handle its own cache coherency, then + ; on the off-chance that one day in the future it might be + ; desirable to be able to patch the application using AppPatcher - + ; and yet it would for some reason be impractical to update + ; UnsqueezeAIF at *that* point to recognise these cases (!?!) - + ; then the OS should already refuse to run the application! + ; + ; On the other hand, there is a real downside, in that this policy + ; hinders the development of alternative compression schemes, or as + ; happened in squeeze 5.09, the fixing of certain bugs that have + ; a demonstrable effect on real hardware. + ; + ; Since ROL has failed for nearly 7 years now to adapt their OS + ; to cope with squeeze 5.09, we don't have much option but to try + ; to work around it. By inserting a pre-StrongARM code sequence + ; here (where it will never be executed, but after the start of + ; the unsqueeze code where UnsqueezeAIF starts looking for it), we + ; can trick UnsqueezeAIF into thinking it recognises us and trusts + ; us to be run. +FakeUnsqSignature + LDMIA R5!,{R0-R3} + STMIA R7!,{R0-R3} + CMP R5,R6 + BLT FakeUnsqSignature + MOV PC,R4 + ] +finishLongs + MOV R11, R2 ; R11 = &longs[0] +decodedBothTabs + ; Now have: R13 = sizeToMove + ; R12 = &shorts[0] + ; R11 = &longs[0] + ; R10 = top of encoded data + ; R9 = base of encoded data + ; R8 = top of decoded data + ; R7 = curFree - base of unused memory + ; R0..R6 unused + +moveRestOfCode + ; Decompression is going to scribble on us here, so copy the + ; rest of the code up into free space. + ADR R5, decodeImage + ADR R6, |UnSqueeze_UnSqueezeLimit|+24 ; allow for branch to exec addr + MOV R4, R7 ; we will jump to R4 + + ; The following code is what is recognised by UnSqzAIF to interfere + ; in the decompression (to do OS_SynchroniseCodeAreas). Changing it + ; will stop it interfering. +moveCode + LDMIA R5!, {R0-R3} + STMIA R7!, {R0-R3} ; NB this updates free space pointer as we go + CMPS R5, R6 + BLT moveCode + MOV R1, R4 ; this instruction causes a non-match in UnSqzAIF + ADD R2, R1, #|UnSqueeze_UnSqueezeLimit|+28-decodeImage + MOV R0, #1 + SWI XOS_SynchroniseCodeAreas ; we've written some code. + MOV PC, R4 ; jump to the new copy of the rest of the code + + [ expand_memcheck + ; If we were to let the expansion occur, either a data abort would + ; occur, or we would overwrite our parent application. +expand_would_overwrite + ADR R0, error_block - 6 * 4 + LDMIB R0!, {R1,R2,R4-R7} + SWI XMessageTrans_ErrorLookup + LDR R1,[R0] + TEQ R1, #0 + ADRNE R0, error_block_failed + SWI OS_GenerateError + DCD 0, 0, 0, 0, 0 +error_block + DCD 0 + DCB "NoMem", 0 + ALIGN +error_block_failed + DCD 0 + DCB "Not enough memory", 0 + ALIGN + ] + +decodeImage + ; The code from here on gets executed only after it is copied + ; elsewhere. This is confusing, but necessary. + + ; Most of the data gets decoded in place, but we have to go round twice + ; just in case we have to copy some data elsewhere, so first + ; time round we use a higher R9 (bottom of encoded data). + + ADD R9, R9, R13 ; base = base + sizeToMove + + ; top of encoded data in R10 + ; base of encoded data in R9 + ; top of decoded data in R8 + ; ptr to shorts in R12 + ; ptr to longs in R11 + ; R0..R6 are free for workspace + +; For the moment, we want to overwrite the first word of the image, +; I think this is just a kludge... + SUB R8, R8, #4 + +decodePair + CMPS R10, R9 ; Have we reached the base ? + BLE doneDecode + LDRB R6, [R10, #-1]! ; byte value + ; The words will be put in R4 and R5, to be STMDB'd + AND R3, R6, #15 ; first nibble + SUBS R0, R3, #MinShort ; idx = (val - 8) + BLT notshort0 +short0 + LDRB R1, [R10, #-1]! + ORR R0, R1, R0, LSL #8 + LDR R4, [R12, R0, LSL #2] ; w = shorts[(nibble-8)<<8 | *p--] + B gotFirst +notshort0 + SUBS R0, R3, #MinLong ; idx = (val - 2) + BLT notlong0 +long0 + LDRB R1, [R10, #-1]! + ORR R0, R1, R0, LSL #8 + LDR R0, [R11, R0, LSL #2] ; w = longs[(nibble-2)<<8 | *p--] + LDRB R1, [R10, #-1]! + ORR R4, R1, R0, LSL #8 + B gotFirst +notlong0 + MOVS R4, R3 ; TMD 13-Feb-90: combine 2 instructions here + ; used to be CMPS R3,#0; MOVEQ R4,R3 + BEQ gotFirst +literal0 + LDRB R0, [R10, #-1]! + LDRB R1, [R10, #-1]! + ORR R0, R0, R1, LSL #8 + LDRB R1, [R10, #-1]! + ORR R0, R0, R1, LSL #16 + LDRB R1, [R10, #-1]! + ORR R4, R0, R1, LSL #24 + +gotFirst + ; Phew! We have the first word of the pair (in R4), now we have + ; to do (almost) the same again, result in R5, and STMDB. + + MOV R3, R6, LSR #4 ; second nibble + SUBS R0, R3, #MinShort ; idx = (val - 8) + BLT notshort1 +short1 + LDRB R1, [R10, #-1]! + ORR R0, R1, R0, LSL #8 + LDR R5, [R12, R0, LSL #2] ; w = shorts[(nibble-8)<<8 | *p--] + STMDB R8!, {R4,R5} + B decodePair +notshort1 + SUBS R0, R3, #MinLong ; idx = (val - 2) + BLT notlong1 +long1 + LDRB R1, [R10, #-1]! + ORR R0, R1, R0, LSL #8 + LDR R0, [R11, R0, LSL #2] ; w = longs[(nibble-2)<<8 | *p--] + LDRB R1, [R10, #-1]! + ORR R5, R1, R0, LSL #8 + STMDB R8!, {R4,R5} + B decodePair +notlong1 + MOVS R5, R3 ; TMD 13-Feb-90: combine 2 instructions here + ; used to be CMPS R3,#0; MOVEQ R5,R3 + + ; This doesn't pay off much + STMEQDB R8!, {R4,R5} ; might be better to swap round + BEQ decodePair ; literal and zero, to save 3S on +literal1 ; the longer path ? + LDRB R0, [R10, #-1]! + LDRB R1, [R10, #-1]! ; If I had the right byte-sex and + ORR R0, R0, R1, LSL #8 ; a couple of registers to spare, + LDRB R1, [R10, #-1]! ; could do this in 15S instead of 22S + ORR R0, R0, R1, LSL #16 ; using the load non-aligned word code + LDRB R1, [R10, #-1]! ; given in ARM CPU Manual. + ORR R5, R0, R1, LSL #24 + STMDB R8!, {R4,R5} + B decodePair + +doneDecode + CMPS R13, #0 ; Any data need to be copied elsewhere ? + BLE runImage ; No -- just run the image + SUB R6, R9, R13 ; R6 points to base of encoded data + MOV R9, R7 ; R9 points to base of copied data + ADD R10, R9, R13 ; R10 is current pointer into copied data +moveEncoded + LDMIA R6!, {R0-R3} + STMIA R7!, {R0-R3} + SUBS R13, R13, #16 + BGT moveEncoded + B decodePair ; Carry on decoding + +; Now R8 should be a pointer to the first word of the decoded image, +; so lets cross our fingers and jump to it... +runImage + ADR r2, decodeImage-4 + MOV R0, #1 +; [up to 3 SUB instructions here] R8 adjusted to point back to AIF header +; SUB R1, R8, #4 +; SWI XOS_SynchroniseCodeAreas +; MOV PC, R8 + +|UnSqueeze_UnSqueezeLimit| + +; Now the bit of code that actually runs in the image compression program: +; this just tells it where the decompression code lives. Are you confused ? + +; Entry point to PROCEDURE FindUnSqueezeCode +; Parameters: base: [FP,#-20]/R0 limit: [FP,#-16]/R1 +|UnSqueeze_FindUnSqueezeCode| + ADR R2, |UnSqueeze_UnSqueezeBase| + STR R2, [R0] + ADR R2, |UnSqueeze_UnSqueezeLimit| + STR R2, [R1] + MOV PC, LR + + END @@ -0,0 +1,348 @@ +/* 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: xpand - decompression of squeezed AIF executable images + * Author: RCC + * Copyright: (C) 1988, Acorn Computers Ltd, Cambridge, England. + * Date: 24-Mar-88 + * LastEdit: 24-Mar-88 + + * 22Feb99: SJM: Note started to convert this to be able to run on + * Solaris. However this isn't easily possible since currently it xpands + * by running the unsqueeze code embedded in the code. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +#include "CLX/wholefls.h" + +#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 + +#ifndef __riscos +#include <sys/types.h> +#include <sys/stat.h> +#endif + +#include "squeeze.h" +#include "VersionNum" + +#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 SELF "xpand" + +typedef int func(int, int, int); + +#ifndef __riscos +# include "asmcall1.h" +#else +extern int asmcall_call(func *, int, int, int); +extern int *asmcall_exit; +#endif + +static int debug; +static int verbose; + +#undef AIFPRELUDE /* musn't rely on this constant - it's changed over time */ + +/* + * Veneer on file-handling. + */ + +#define SAVE 0 +#define WRITEINFO 1 +#define READINFO 5 +#define LOAD 0xff + +#define FILFOUND 1 + +#ifdef __riscos +static void arthurise(_kernel_osfile_block *info) +{ 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 */ + int data[2]; + data[0] = 3; + if (_kernel_osword(14, data) != _kernel_ERROR) { + info->exec = data[0]; + info->load = 0xfffff800 + (data[1] & 0xff); + } + } + } +} +#endif + +static int fileinfo(_kernel_osfile_block *info, const char *name) +{ +#ifdef __riscos + if (_kernel_osfile(READINFO, name, info) != FILFOUND) + 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; +} + +static void fatalerror(const char *format, const char *name) { + fputs(SELF ": ", stderr); + fprintf(stderr, format, name); +#ifdef __riscos + { _kernel_oserror *e = _kernel_last_oserror(); + if (e != 0) fprintf(stderr, " (host error %#x: %s)", e->errnum, e->errmess); + } +#endif + fputc('\n', stderr); + exit(1); +} + +#define ROR(x, n) (((x)<<(32-(n))) | (((x)>>(n)) & ((1<<(32-(n)))-1))) + +static int immfield(word inst) { + int shift = (inst & 0xf00) >> 7; + int val = inst & 0xff; + return ROR(val, shift); +} + +static SqueezeHeader *find_squeeze_header(int isaif, int *decompress) +{ + /* + * The size of the decoded thing is stored 6 words before the + * start of the unsqueezing code (see squeeze.c). + */ + if (!isaif) { + /* + * Non-AIF images skip the first few instructions of the unsqueezing + * code; this has changed over time, so search backwards for + * MOV R0, #<imm>, which is always the first instruction + */ + while (((*decompress & 0xfffff000) != 0xe3a00000)) decompress--; + } + return (SqueezeHeader *) (decompress-SQUEEZEWORDS); +} + +static int xpand(char *in, char *out) +{ _kernel_osfile_block info; + bool isaif, isdata; + int squeezedby = 0; + int size; + int *ws; + aifhdr *hdr; + char *lastb; + + if (verbose) + fprintf(stderr, "-- xpanding '%s' to '%s'\n", in, out); + + if (fileinfo(&info, in) == -1) + fatalerror("no file '%s'", in); + size = info.start; + ws = (int *)malloc(size + sizeof(int) + (24*1024)); /* allow space for unsqueezing */ + hdr = (aifhdr *)(ws + 1); + if (wf_load(in, hdr, size) == -1) + fatalerror("can't load '%s'", in); + if ((info.load & 0xfc000000) != 0) { /* Not a valid address */ + if ((info.load & 0xffffff00) == 0xfffff800) { /* Arthur absolute */ + info.load = 0x8000; info.exec = 0x8000; + } else if ((info.load & 0xffffff00) != SQUEEZED) { + info.exec = info.load; + } + } + + if ((size & 15) == 15 && + ((hdr->bl_decompress & ~B_OFFSET) == BL || + (hdr->bl_decompress & ~B_OFFSET) == B) + ) { + int d1, d2, d3; + lastb = (char *)hdr + size; + while (*--lastb == ' ') /* nothing */; + if (isdigit(d3 = *--lastb) && + isdigit(d2 = *--lastb) && + *--lastb == '.' && + isdigit(d1 = *--lastb) && + *--lastb == ' ' && + *--lastb == 'c' && + *--lastb == 'c' && + *--lastb == 'r') + squeezedby = (d1-'0')*100+(d2-'0')*10+(d3-'0'); + } + + if (squeezedby == 0) + fatalerror("'%s' is not squeezed", in); + + if (hdr->swi_exit == 0xef000011) /* OK, it's AIF */ + isdata = NO, isaif = YES; + else if (((datahdr *)hdr)->datamagic == DATAMAGIC) /* OK, it's squeezed data */ + isdata = YES, isaif = NO; + else + isdata = NO, isaif = NO; + + { int *decompress = &(((int *)hdr)[(hdr->bl_decompress & B_OFFSET) + PREFETCH]); + SqueezeHeader *h = find_squeeze_header(isaif, decompress); + int realsize = h->decodedsize; + word *lastw = (word *)lastb - 1; + + if (debug) + fprintf(stderr, "decodedsize %x encodedsize %x encodedtabs %x\n" + "nshorts %x nlongs %x bytestomove %x\n" + "squeezer %d.%02d\n", + h->decodedsize, h->encodedsize, h->encodedtabs, + h->nshorts, h->nlongs, h->bytestomove, + squeezedby/100, squeezedby%100); + /* Arrange to get back control after decompression. We can't plant a + branch, since the decompression code gets shifted up store before + executing (and we'd rather not know the details). We rely on the + fact that at the end of decompression, r8 points to the base of the + decompressed image and this is followed by + SUB r8, r8, #&7c + MOV pc, r8 + (for aif images) or + + ADD r8, r8, #exec_address - load_address + MOV pc, r8 + (otherwise) + */ + + if (((int)lastb & 3) != 0 || + *lastw != MOV_PC_R8) + fatalerror("format error in '%s'", in); + if (isaif) { + int backwards; + for (backwards = 1; backwards < 8; ++backwards) { + if (*(lastw-backwards) == (SUB_R8_R8_0 | 0x7c)) break; + } + if (backwards == 8) { + fatalerror("aif format error in '%s' (end of expansion code not found)", in); + } + *(lastw-backwards) = (SUB_R8_R8_0 | 0x80); + } else { + int execoffset = 0; + for (;; lastw--) { + word inst = *(lastw-1); + if ((inst & ~0xfff) == ADD_R8_R8_0) + execoffset += immfield(inst); + else if ((inst & ~0xfff) == SUB_R8_R8_0) + execoffset -= immfield(inst); + else + break; + } + info.exec += execoffset; + } + *lastw = LDR_PC_R8_MINUS4; + *ws = (int)(&asmcall_exit); + asmcall_call((func *)hdr, 0, 0, 0); + /* + * Just have to save it again. + */ + if (isdata) { + datahdr *d = (datahdr *)hdr; + info.load = (int)d->load; + info.exec = d->exec; + info.start = (int)(d+1); + info.end = info.start + d->size; + } else { +#ifdef __riscos + arthurise(&info); +#endif + info.start = (int)hdr; + if (isaif) + info.end = info.start + sizeof(*hdr) + realsize; + else + info.end = info.start + realsize; + } + if (wf_save(out, (void *)info.start, info.end - info.start) == -1) + fatalerror("failed to write '%s'", out); +#ifdef __riscos + _swix(OS_File, _INR(0,2), 18, out, (info.load << 12) >> 20); +#endif + + return(0); + } +} + +static void help(void) +{ + fprintf(stderr, "\n%s vsn %s [%s] - \n", SELF, VSN, DATE); + fprintf(stderr, "takes an executable AIF image or data file compressed by\n"); + fprintf(stderr, "'squeeze' and decompresses it to reproduce the original image\n"); + fprintf(stderr, "(possibly with an extra 4 bytes of zeros on the end).\n\n"); + fprintf(stderr, "syntax: xpand <squeezed-file> [<unsqueezed-file>]\n"); +} + +int main(int argc, char *argv[]) +{ int j; + char *arg; + char *a = NULL; + char *b = NULL; + char c; + + debug = verbose = 0; + for (j = 1; j < argc; ++j) { + arg = argv[j]; + if (arg[0] == '-') { + c = arg[1]; + if (('A' <= c) && (c <= 'Z')) c += ('a' - 'A'); + switch (c) { + case 'h': help(); exit(0); + case 'q': ++debug; break; + case 'v': ++verbose; break; + default: + fprintf(stderr, SELF ": flag '%c' not recognised", c); + help(); + exit(1); + } + } else { /* a filename */ + if (a == NULL) a = arg; + else if (b == NULL) b = arg; + else { + fatalerror("too many args '%s'", arg); + } + } + } + if (a == NULL) fatalerror("need <file-to-xpand>", 0); + if (b == NULL) b = a; /* xpand it to itself */ + return(xpand(a, b)); +} |