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 --- CastleLicence.txt | 159 +++++++++ VersionNum | 23 ++ asmcall.s | 63 ++++ squeeze.c | 972 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ squeeze.h | 77 +++++ unsqrm.s | 94 ++++++ unsqueeze.s | 468 ++++++++++++++++++++++++++ xpand.c | 348 +++++++++++++++++++ 8 files changed, 2204 insertions(+) create mode 100644 CastleLicence.txt create mode 100644 VersionNum create mode 100644 asmcall.s create mode 100644 squeeze.c create mode 100644 squeeze.h create mode 100644 unsqrm.s create mode 100644 unsqueeze.s create mode 100644 xpand.c 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 +# 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)); +} 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, # + 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 diff --git a/xpand.c b/xpand.c new file mode 100644 index 0000000..2b29acb --- /dev/null +++ b/xpand.c @@ -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 +#include +#include +#include + +#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 +#include +#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, #, 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 []\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 ", 0); + if (b == NULL) b = a; /* xpand it to itself */ + return(xpand(a, b)); +} -- cgit v1.2.3