summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CastleLicence.txt159
-rw-r--r--VersionNum23
-rw-r--r--asmcall.s63
-rw-r--r--squeeze.c972
-rw-r--r--squeeze.h77
-rw-r--r--unsqrm.s94
-rw-r--r--unsqueeze.s468
-rw-r--r--xpand.c348
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
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 <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));
+}