home *** CD-ROM | disk | FTP | other *** search
- /*
- The following are a couple of 'pseudo-assembler' functions written during development of the assembler
- mcomp & mdeco LZW compression functions, whose code can be found in !Squish.s.Core.
- For testing or further development, mccomp & mcdeco, below, may be used in place of mcomp & mdeco.
- Pseudo-assembler means that they have been written in C with a mind to being easy to translate directly
- into assembler, thus each line will typically mimic one or two ARM instructions, or simple code
- fragments (eg a loop used to zero a region of memory).
- This makes development including experimentation & debugging of the algorithm itself much simpler.
- The C compiler being efficient, this code is also reasonably fast - usually running at between 50% &
- 75% of the optimised assembler equivalent.
- When using such a technique, there are a few points to bear in mind:
- Variables mimicing registers should be unsigned int's;
- When copying an assembler comparison, be careful about the sign - eg GT being signed requires that the
- C analogue first has its arguments cast to signed int;
- Similarly be careful about sign in relation to shifts - ASL, ASR require a cast of C argument to signed
- int; LSR, LSL don't;
- Same applies to divisions by powers of 2 in C.
- On an assembler data store or load, if using LDM or STM be very careful about word alignment - if
- pointer not necessarily word aligned, ARM will round it down; in C analogue, ensure you explicitly
- round the argument down (eg use (pointer & ~3) instead of just pointer).
- */
-
- static char m[] = {0x00, 0x3c, 0x18, 0x54, 0x30, 0x0c, 0x48, 0x24};
-
- /*
- args: input, input size, output, workspace
- returns: number of output bytes written
- */
- int mccomp(char *in, int insz, char *out, char *ws)
- {
- /*
- vbles: bit index, chunk size, tag, tag upper bound (exclusive), output address (=out+bin/8, not
- necessarily word aligned in value, but assumes will be rounded down when accessed by a ldm/stm
- instruction in assembler code, or manually, in pseudo assembler {ie C below}), index into
- workspace, triplet, scratch 1, scratch 2, last chunk (may be byte or tag), workspace prime table
- length constant (nb can't alter this unless also alter workspace size used by clients)
- */
- unsigned int bin, chsz, tag, tagbd, oa, i, tri, t1, t2, lc, wspc;
- unsigned int oldout; /*Initial out, only needed once upon fn return, so in assembler, store in memory.*/
- unsigned int oldbin; /*old bit index, only needed on a tag reset, so in assembler, store in memory.*/
-
- oldout = (int)out;
- wspc = 5119;
-
- *out++ = 0x1f; /*Write 24-bit flag.*/
- *out++ = 0x9d;
- *out++ = 0x8c;
-
- lc = *in++; /*Get first byte of input,*/
- insz--; /*so one less byte to go.*/
- bin = ((int)out&3)*8; /*Initialise bin, from out ptr above 24-bit flag, rounded down to word boundary.*/
- oldbin = bin;
- while ((int)out&3) *out++ = 0;
- if (bin) out-=4; /*Reset out to point to address above 24-bit flag, then rounded down to
- word alignment, so that out+bin/8 points to first bit above 24-bit flag.*/
- mccomp_resettags:
-
- tag = 257;
- tagbd = 512;
- chsz = 9;
- t1 = (int)(ws+wspc*4-4);
- for (; t1>=(int)ws; t1-=4) *(int *)t1 = 0; /*Fill workspace with 0's, to reset all tags.*/
-
- mccomp_loop:
-
- insz--;
- if ((signed int)insz<0) {
- oa = (int)out + bin/8;
- t1 = *(int *)(oa&~3);
- t2 = bin&31;
- if (t2==0) t1 = lc;
- else {
- t1 = t1 | (lc<<t2);
- lc = lc >> (32-t2);
- }
- *(int *)((oa&~3)+0) = t1;
- *(int *)((oa&~3)+4) = lc;
- bin += chsz;
- out += (bin+7)/8;
- return (int)out - oldout;
- }
-
- tri = *in++;
- i = lc ^ 16*tri;
- tri = tri | 256*lc;
- t1 = *(int *)(ws + 4*i);
- if (t1 == 0) goto mccomp_unknownseq;
- if (tri == t1/4096) {
- lc = t1 & 0xfff;
- goto mccomp_loop;
- }
- if (i == 0) t1 = 1;
- else t1 = wspc-i;
- mccomp_scanws:
- i -= t1;
- if ((signed int)i < 0) i+=wspc;
- t2 = *(int *)(ws + 4*i);
- if (t2 == 0) goto mccomp_unknownseq;
- if (tri == t2/4096) {
- lc = t2 & 0xfff;
- goto mccomp_loop;
- }
- goto mccomp_scanws;
-
- mccomp_unknownseq:
- oa = (int)out + bin/8;
- t1 = *(int *)(oa&~3);
- t2 = bin&31;
- if (t2==0) t1 = lc;
- else {
- t1 = t1 | (lc<<t2);
- lc = lc >> (32-t2);
- }
- *(int *)((oa&~3)+0) = t1;
- *(int *)((oa&~3)+4) = lc;
- bin += chsz;
- lc = tri & 255;
- if (tag >= 4096) {
- oa = (int)out + bin/8;
- t1 = *(int *)(oa&~3);
- t2 = bin&31;
- if (t2==0) t1 = 256;
- else {
- tag = 256; /*tag no longer needed, so can temporarily use as a third scratch register.*/
- t1 = t1 | (tag<<t2);
- t2 = tag >> (32-t2);
- }
- *(int *)((oa&~3)+0) = t1;
- *(int *)((oa&~3)+4) = t2;
- bin += chsz;
- t1 = (bin - oldbin)/4 & 7;
- t1 = m[t1];
- t2 = (bin+7)&~7; /*round up to a byte - intervening bits will already be zero*/
- bin += t1;
- oldbin = bin;
- mccomp_appendzeros:
- *(out + t2/8) = 0;
- t2+=8;
- if (t2 < bin) goto mccomp_appendzeros;
- if ((t2&24) != 0) goto mccomp_appendzeros; /*initialise bits upto word boundary to zero,
- for subsequent writes, as at algo start*/
- goto mccomp_resettags;
- }
-
- tri = tag | 4096*tri;
- *(int *)(ws + 4*i) = tri;
- if (tag >= tagbd) {
- chsz++;
- tagbd *= 2;
- }
- tag++;
- goto mccomp_loop;
- }
-
- /*
- args: input, input size, output, workspace
- */
- BOOL mcdeco(char *in, int insz, char *out, char *ws)
- {
- /*
- vbles: bit index, old bit index, chunk, chunk size, tag, maximum tag, scratch 1, scratch 2
- */
- unsigned int bin, oldbin, ch, chsz, tag, tagmx, t1, t2;
-
- if (in[0]!=0x1f || in[1]!=0x9d || in[2]!=0x8c) return FALSE;
- ws = ws - 1024; /*as lowest int index will be 256*/
-
- in += 3; /*step over 24-bit flag*/
- insz -= 3; /*compensate insz*/
- bin = (int)in & 3;
- in -= bin; /*round in down to word boundary*/
- insz += bin; /*compensate insz*/
- bin *= 8; /*bit index, bit offset to data from in*/
- oldbin = bin;
- insz = insz*8 - 9;
- tag = 256;
- tagmx = 511;
- chsz = 9;
-
- mploop: /*start decompression*/
- if (bin>insz) return TRUE;
- if (tag>tagmx) {
- if (chsz<12) {
- chsz++;
- tagmx = 2*tagmx + 1;
- }
- }
- if (tag<4096) *(char **)(ws + 4*tag) = out;
- ch = *(int *)(in + bin/32*4);
- t1 = *(int *)(in + bin/32*4 + 4);
- if (bin&31) {
- ch >>= (bin&31);
- ch |= t1 << (32-(bin&31));
- }
- ch &= tagmx;
- bin += chsz;
- if (ch==256) {
- t1 = (bin-oldbin)/4 & 7;
- bin += m[t1];
- oldbin = bin;
- tag = 256;
- tagmx = 511;
- chsz = 9;
- goto mploop;
- }
- if (ch<256) {
- *out++ = ch;
- tag++;
- goto mploop;
- }
- t1 = *(int *)(ws + 4*ch - 4);
- t2 = *(int *)(ws + 4*ch);
- *out++ = *(char *)t1++;
- do *out++ = *(char *)t1++;
- while (t1<=t2);
- tag++;
- goto mploop;
- }
-