; Abbrevations used:
;
; RGB = 3 bytes per pixel (R,G,B) 24bit.
; FTC = 1 byte per pixel.  Fake True Color, the palette is set in such a way
;       that it acts as a 8bit (or less) true color mode.

	Opt	l+,case

MC68000	EQU	0

	XDef	_RANDOM
	XDef	_RGBTOFTC
	XDef	_DECODEGIF
	XDef	_SCALERGB
	XDef	_SCALEGRAY

	Incdir	"INCLUDE:"

	Include	"exec/types.i"
	Include	"exec/nodes.i"

	Include	"FV.hi"

ToggleLed	MACRO
	Move.l	d0,-(sp)
	Moveq	#-1,d0
	Bset	#1,$bfe001
.Loop\@	Move.l	$0.l,$0.l
	Dbra	d0,.Loop\@
	Bclr	#1,$bfe001
	Move.l	(sp)+,d0
	ENDM


; seed = ((seed + 7) * 257) % (2^31-1)

;$0ffff - $ffff   = $0000   =  $ffff + $0    = $ffff -> $0000
;$10000 - $ffff   = $0001   =  $0000 + $1    = $0001
;$1fffd - $ffff   = $fffe   =  $fffd + $1    = $fffe
;$1fffe - $ffff*2 = $0000   =  $fffe + $1    = $ffff -> $0000
;$1ffff - $ffff*2 = $0001   =  $ffff + $1    = $0000 + X = $0001
;$20000 - $ffff*2 = $0002   =  $0000 + $2    = $0002
;$2fffc - $ffff*2 = $fffe   =  $fffc + $2    = $fffe
;$2fffd - $ffff*3 = $0000   =  $fffd + $2    = $ffff -> $0000
;$2fffe - $ffff*3 = $0001   =  $fffe + $2    = $0000 + X = $0001
;$2ffff - $ffff*3 = $0002   =  $ffff + $2    = $0001 + X = $0002
;$30000 - $ffff*3 = $0003   =  $0000 + $3    = $0003

;$07fff - $7fff   = $0000   =  $7fff + $0    = $7fff - $7fff = $0000
;$08000 - $7fff   = $0001   =  $0000 + $1    = $0001
;$0fffd - $7fff   = $7ffe   =  $7ffd + $1    = $7ffe
;$0fffe - $7fff*2 = $0000   =  $7ffe + $1    = $7fff - $7fff = $0000
;$0ffff - $7fff*2 = $0001   =  $7fff + $1    = $8000 - $7fff = $0001
;$10000 - $7fff*2 = $0002   =  $0000 + $2    = $0002
;$17ffc - $7fff*2 = $7ffe   =  $
;$17ffd - $7fff*3 = $0000   =  $7ffd + $2    = $7fff - $7fff = $0000
;$17ffe - $7fff*3 = $0001
;$17fff - $7fff*3 = $0002   =  $7fff + $2    = $8001 - $7fff = $0002
;$18000 - $7fff*3 = $0003   =  $0000 + $3    = $0003

;$808505                    =  $0505 + $101  = $0606

	CNOP	0,4
_RANDOM	; IN: d0.l = Seed
	;OUT: d0.l = New seed

	Movem.l	d1-d3,-(sp)
	Moveq	#0,d2
	Move.l	d0,d3

	Moveq	#0,d1	d1:d0 = seed
	Addq.l	#7,d0	d1:d0 = seed+7

	Rol.l	#8,d0
	Move.b	d0,d1
	Clr.b	d0	d1:d0 = (seed+7) * 256

	Add.l	d3,d0
	Addx.w	d2,d1	d1:d0 = (seed+7) * 257

	Lsl.w	#1,d1	Make room for a bit.
	Lsl.l	#1,d0	Shift highest bit into X.
	Addx.w	d2,d1	Highest bit of d0 shifted into lowest bit of d1
	Lsr.l	#1,d0	d0 is now automatically ANDed with $7fffffff

	Add.l	d1,d0
	Move.l	#$7fffffff,d1
	Cmp.l	d1,d0
	Blo.s	.Done
	Sub.l	d1,d0

.Done	Movem.l	(sp)+,d1-d3
	Rts



;	CNOP	0,4
;_RANDOM	; IN: d0.l = Seed
;	;OUT: d0.l = New seed
;	Movem.l	d1-d2,-(sp)
;
;	Moveq	#0,d1
;	Move.w	d0,d1
;	Clr.w	d0
;	Swap	d0
;
;	Move.l	d0,d2
;	Lsl.l	#8,d0
;	Add.l	d2,d0
;
;	Move.l	d1,d2
;	Lsl.l	#8,d1
;	Add.l	d2,d1
;
;	Move.l	d0,d2
;	Swap	d2
;	And.l	#$7fff0000,d2
;	Add.l	d2,d1
;	Bvc.s	.Skip
;	Addq.l	#1,d1
;
;.Skip	Moveq	#15,d2
;	Lsr.l	d2,d0
;	Add.l	d1,d0
;	Bclr	#31,d0
;
;	Movem.l	(sp)+,d1-d2
;	Rts




	CNOP	0,4
_SCALERGB	; IN: a0.l = Input (RGB buffer)
	; IN: a1.l = Output (RGB buffer)
	; IN: d0.w = Width in pixels
	; IN: d1.w = Scalefactor (0-4095 for shrinking, 4096 for no scaling, 4097-32767 for enlarging)

	Movem.l	d0-d2/a0-a1,-(sp)
	Move.w	#-2048,d2
	Subq.w	#1,d0

	Cmp.w	#4096,d1
	Bgt.s	.Enlarging

.Loop	Add.w	d1,d2
	Blt.s	.GoLoop
	Sub.w	#4096,d2
	Move.b	(a0)+,(a1)+
	Move.b	(a0)+,(a1)+
	Move.b	(a0)+,(a1)+
	Dbra	d0,.Loop
	Bra.s	.Exit
.GoLoop	Addq.l	#3,a0
	Dbra	d0,.Loop
	Bra.s	.Exit

.Enlarging	Add.w	d1,d2
.Loop2	Move.b	(a0),(a1)+
	Move.b	1(a0),(a1)+
	Move.b	2(a0),(a1)+
	Sub.w	#4096,d2
	Bge.s	.Loop2
	Addq.l	#3,a0
	Dbra	d0,.Enlarging

.Exit	Movem.l	(sp)+,d0-d2/a0-a1
	Rts



	CNOP	0,4
_SCALEGRAY	; IN: a0.l = Input (RGB buffer)
	; IN: a1.l = Output (RGB buffer)
	; IN: d0.w = Width in pixels
	; IN: d1.w = Scalefactor (0-4095 for shrinking, 4096 for no scaling, 4097-32767 for enlarging)

	Movem.l	d0-d2/a0-a1,-(sp)
	Move.w	#-2048,d2
	Subq.w	#1,d0

	Cmp.w	#4096,d1
	Bgt.s	.Enlarging

.Loop	Add.w	d1,d2
	Blt.s	.GoLoop
	Sub.w	#4096,d2
	Move.b	(a0)+,(a1)+
	Dbra	d0,.Loop
	Bra.s	.Exit
.GoLoop	Addq.l	#1,a0
	Dbra	d0,.Loop
	Bra.s	.Exit

.Enlarging	Add.w	d1,d2
.Loop2	Move.b	(a0),(a1)+
	Sub.w	#4096,d2
	Bge.s	.Loop2
	Addq.l	#1,a0
	Dbra	d0,.Enlarging

.Exit	Movem.l	(sp)+,d0-d2/a0-a1
	Rts



	CNOP	0,4
_RGBTOFTC	; IN: a0.l = Input (RGB buffer)
	; IN: a1.l = Output (FTC buffer)
	; IN: d0.w = Width in pixels
	; IN: d1.w = Height in pixels
	; IN: d2.w = Renderdepth

	Movem.l	a0-a2/d1-d2/d4-d7,-(sp)

	Move.w	d2,d4
	Add.w	d4,d4
	Add.w	d2,d4
	Lea	FTCtable-3(pc,d4.w),a2
	Move.b	(a2)+,d5
	Move.b	(a2)+,d6
	Move.b	(a2)+,d7

	Subq.w	#1,d1

.YLoop	Move.w	d0,d4
	Subq.w	#1,d4

.XLoop	Moveq	#0,d2
	Move.b	(a0)+,d2
	Lsl.w	d5,d2
	Move.b	(a0)+,d2
	Lsl.w	d6,d2
	Move.b	(a0)+,d2
	Lsl.w	d7,d2
	Lsr.w	#8,d2
	Move.b	d2,(a1)+
	Dbra	d4,.XLoop

	Dbra	d1,.YLoop

	Movem.l	(sp)+,a0-a2/d1-d2/d4-d7
	Rts

FTCtable	Dc.b	0,1,0
	Dc.b	1,1,0
	Dc.b	1,1,1
	Dc.b	1,2,1
	Dc.b	2,2,1
	Dc.b	2,2,2
	Dc.b	2,3,2
	Dc.b	3,3,2

	RsReset
Size	Rs.w	1	0
Cursize	Rs.w	1	2
Clearcode	Rs.w	1	4
Endcode	Rs.w	1	6
Newcodes	Rs.w	1	8	not worth a register
Topslot	Rs.w	1	10
Slot	Rs.w	1	12
Navailbytes	Rs.w	1	14
Nbitsleft	Rs.w	1	16
Shiftreg	Rs.w	1	18
Suffix	Rs.l	1	20
Prefix	Rs.l	1	24
Stack	Rs.l	1	28
Sp	Rs.l	1	32	stack current location
Oc	Rs.w	1	36
Fc	Rs.w	1	38
Row	Rs.w	1	40
Interlaced	Rs.b	1	42


	CNOP	0,4
_DECODEGIF	; IN: a0.l = struct ScreenNode *
	; IN: a1.l = UBYTE *buffer
	; IN: d0.w = rows
	;OUT: d0.l = Error code or zero if everything went okay.

	Movem.l	d1-d7/a0-a6,-(sp)

	Move.l	sn_BytesPerRow(a0),d7
	Mulu	d0,d7

	Moveq	#-lh_SIZEOF*8,d1
	Move.w	sn_Data+Row(a0),d2

	Move.b	sn_Data+Interlaced(a0),d3
	Beq.s	.LLoop

	; 4 = NextStart = 2*4, step 8
	; 3 = NextStart = 2*2, step 8
	; 2 = NextStart = 2*1, step 4
	; 1 = NextStart = 2*0.5, step 2

	Cmp.b	#2,d3
	Blt.s	.Type1
	Beq.s	.Type2
	Cmp.b	#3,d3
	Beq.s	.Type3
	Moveq	#8,d4
	Moveq	#8,d5
	Bra.s	.IntLoop
.Type3	Moveq	#8,d4
	Moveq	#4,d5
	Bra.s	.IntLoop
.Type2	Moveq	#4,d4
	Moveq	#2,d5
	Bra.s	.IntLoop
.Type1	Moveq	#2,d4
	Moveq	#1,d5

.IntLoop	Move.w	d2,lh_Row(a1,d1.w)
	Add.w	d4,d2
	Move.b	d5,lh_Repeatrow(a1,d1.w)
.IntRetry	Cmp.w	sn_Height(a0),d2
	Blt.s	.IntCont

	Subq.b	#1,sn_Data+Interlaced(a0)
	Lsr.w	#1,d5	NextStart
	Move.w	d5,d2
	Cmp.w	#4,d5
	Beq.s	.IntSkip
	Lsr.w	#1,d4	Step
.IntSkip	Bra.s	.IntRetry

.IntCont	Addq.w	#lh_SIZEOF,d1
	Bne.s	.IntLoop

	Bra.s	.Continue


.LLoop	Move.w	d2,lh_Row(a1,d1.w)
	Addq.w	#1,d2
	Move.b	#1,lh_Repeatrow(a1,d1.w)

	Addq.w	#lh_SIZEOF,d1
	Bne.s	.LLoop



.Continue	Move.w	d2,sn_Data+Row(a0)

	Move.w	sn_Data+Nbitsleft(a0),d2
	Move.w	sn_Data+Navailbytes(a0),d3
	Move.w	sn_Data+Shiftreg(a0),d4
	Move.w	sn_Data+Cursize(a0),d5
	Move.w	sn_Data+Slot(a0),d6

	Move.l	sn_Data+Sp(a0),a2
	Move.w	sn_Data+Fc(a0),a3
	Move.w	sn_Data+Oc(a0),a4
	Move.l	sn_Data+Suffix(a0),a5
	Move.l	sn_Data+Prefix(a0),a6


	;Register layout:
	;d0.w = c
	;d1.w = code, or scratch for getnextcode!
	;d2.w = nbits_left
	;d3.w = navail_bytes
	;d4.w = b1 (shift-bit register) bits 8-15 must be clear always
	;d5.w = curr_size
	;d6.w = slot
	;d7.l = bufcnt

	;a0.l = ScreenNode *
	;a1.l = UBYTE *buffer
	;a2.l = sp (stack pointer)
	;a3.w = fc
	;a4.w = oc
	;a5.l = suffix
	;a6.l = prefix

.DoWhile	Cmp.l	sn_Data+Stack(a0),a2
	Ble.s	.Loop

	Move.b	-(a2),(a1)+
	Subq.l	#1,d7
	Bne.s	.DoWhile

	; Save registers, loopinfo and stuff, then return
.SaveRegs	Move.w	d2,sn_Data+Nbitsleft(a0)
	Move.w	d3,sn_Data+Navailbytes(a0)
	Move.w	d4,sn_Data+Shiftreg(a0)
	Move.w	d5,sn_Data+Cursize(a0)
	Move.w	d6,sn_Data+Slot(a0)

	Move.l	a2,sn_Data+Sp(a0)
	Move.w	a3,sn_Data+Fc(a0)
	Move.w	a4,sn_Data+Oc(a0)
	Moveq	#0,d0
.Exit	Movem.l	(sp)+,d1-d7/a0-a6
	Rts


.Loop	Bsr	GetNextCode
	;OUT: d0 = the next code
	Cmp.w	sn_Data+Endcode(a0),d0
	Beq.s	.EndOfGif

	Cmp.w	sn_Data+Clearcode(a0),d0
	Bne.s	.NotClearCode

	Move.w	sn_Data+Size(a0),d5	cursize
	Addq.w	#1,d5

	Moveq	#1,d6
	Lsl.w	d5,d6
	Move.w	d6,sn_Data+Topslot(a0)

	Move.w	sn_Data+Newcodes(a0),d6	slot

.AnotherClearCode	Bsr	GetNextCode
	;OUT: d0 = the next code
	Cmp.w	sn_Data+Clearcode(a0),d0
	Beq.s	.AnotherClearCode

	Cmp.w	sn_Data+Endcode(a0),d0
	Bne.s	.NotEnding
.EndOfGif	Moveq	#ERROR_END_OF_STREAM,d0
	Bra.s	.Exit

.NotEnding	Cmp.w	d0,d6	Code,Slot
	Bgt.s	.NotBeyondRange
	Moveq	#0,d0

.NotBeyondRange	Move.w	d0,a3
	Move.w	d0,a4

	Move.b	d0,(a1)+
	Subq.l	#1,d7
	Bne.s	.Loop

	Bra	.SaveRegs



.NotClearCode	Move.w	d0,d1
	Cmp.w	d1,d6	code,slot
	Bgt.s	.CodeIsInRange
	;Beq.s	.NotABadCode
	;Encountered a bad code... what should we do with it?
	;The example code simply increased a counter and ignored it.
.NotABadCode	Move.w	a4,d1
	Exg	a3,d0
	Move.b	d0,(a2)+
	Exg	a3,d0

.CodeIsInRange	Cmp.w	sn_Data+Newcodes(a0),d1
	Blt.s	.EndWhile

	Move.b	(a5,d1.w),(a2)+	suffix,stack
	Move.w	(a6,d1.w*2),d1	prefix,code
	Bra.s	.CodeIsInRange

.EndWhile	Move.b	d1,(a2)+
	Cmp.w	sn_Data+Topslot(a0),d6
	Bge.s	.Endif1
	Move.b	d1,(a5,d6.w)
	Move.w	d1,a3
	Move.w	a4,(a6,d6.w*2)
	Addq.w	#1,d6
	Move.w	d0,a4

.Endif1	Cmp.w	sn_Data+Topslot(a0),d6
	Blt.s	.Endif2
	Cmp.w	#12,d5
	Bge.s	.Endif2
	Lsl.w	sn_Data+Topslot(a0)
	Addq.w	#1,d5

.Endif2	Bra	.DoWhile





	CNOP	0,4
GetNextCode	; IN: d1.l = SCRATCH
	; IN: d2.w = nbits_left
	; IN: d3.w = navail_bytes
	; IN: d4.w = shift-bit register
	; IN: d5.w = curr_size
	; IN: a2.l = UBYTE *fileptr
	;OUT: d0.w = the next code
	; This routine gets next code which is d5 (curr_size) bits in size and
	; returns this x bit code in the lower bits of d0.  This routine can
	; never fail since the buffer is pre-read, and so I/O errors are not
	; of our concern.

	Move.l	a2,-(sp)
	Move.l	sn_FilePtr(a0),a2

	Tst.w	d2
	Bne.s	.NBitsLeftNotZero

	Tst.w	d3
	Bgt.s	.NAvailBytesPos

	; No more bytes in current block, so read the next block-counter
	Moveq	#0,d3
	Move.b	(a2)+,d3

.NAvailBytesPos	; There were still bytes left in the current GIF block
	Move.b	(a2)+,d4
	Moveq	#8,d2
	Subq.w	#1,d3

.NBitsLeftNotZero	Moveq	#8,d1
	Sub.w	d2,d1
	Move.w	d4,d0
	Lsr.w	d1,d0

.While	Cmp.w	d5,d2
	Bge.s	.EndWhile

	Tst.w	d3
	Bgt.s	.NAvailBytesPos2

	; No more bytes in current block, so read the next block-counter
	Moveq	#0,d3
	Move.b	(a2)+,d3

.NAvailBytesPos2	Move.b	(a2)+,d4

	Move.w	d4,d1
	Lsl.w	d2,d1
	Or.w	d1,d0

	Addq.w	#8,d2
	Subq.w	#1,d3

	Bra.s	.While

.EndWhile	Sub.w	d5,d2
	And.w	CodeMask(pc,d5.w*2),d0

	Move.l	a2,sn_FilePtr(a0)
	Move.l	(sp)+,a2
	Rts

CodeMask	Dc.w	0,1,3,7,15,31,63,127,255,511,1023,2047,4095


	END


/* DECODE.C - An LZW decoder for GIF
 * Copyright (C) 1987, by Steven A. Bennett
 *
 * Permission is given by the author to freely redistribute and include
 * this code in any program as long as this credit is given where due.
 *
 * In accordance with the above, I want to credit Steve Wilhite who wrote
 * the code which this is heavily inspired by...
 *
 * GIF and 'Graphics Interchange Format' are trademarks (tm) of
 * Compuserve, Incorporated, an H&R Block Company.
 *
 * Release Notes: This file contains a decoder routine for GIF images
 * which is similar, structurally, to the original routine by Steve Wilhite.
 * It is, however, somewhat noticably faster in most cases.
 *
 */

#include "std.h"
#include "errs.h"

IMPORT TEXT *malloc();                 /* Standard C library allocation */

/* IMPORT INT get_byte()
 *
 *   - This external (machine specific) function is expected to return
 * either the next byte from the GIF file, or a negative number, as
 * defined in ERRS.H.
 */
IMPORT INT get_byte();

/* IMPORT INT out_line(pixels, linelen)
 *     UBYTE pixels[];
 *     INT linelen;
 *
 *   - This function takes a full line of pixels (one byte per pixel) and
 * displays them (or does whatever your program wants with them...).  It
 * should return zero, or negative if an error or some other event occurs
 * which would require aborting the decode process...  Note that the length
 * passed will almost always be equal to the line length passed to the
 * decoder function, with the sole exception occurring when an ending code
 * occurs in an odd place in the GIF file...  In any case, linelen will be
 * equal to the number of pixels passed...
 */
IMPORT INT out_line();

/* IMPORT INT bad_code_count;
 *
 * This value is the only other global required by the using program, and
 * is incremented each time an out of range code is read by the decoder.
 * When this value is non-zero after a decode, your GIF file is probably
 * corrupt in some way...
 */
IMPORT INT bad_code_count;

#define NULL   0L
#define MAX_CODES   4095

/* Static variables */
LOCAL WORD curr_size;                     /* The current code size */
LOCAL WORD clear;                         /* Value for a clear code */
LOCAL WORD ending;                        /* Value for a ending code */
LOCAL WORD newcodes;                      /* First available code */
LOCAL WORD top_slot;                      /* Highest code for current size */
LOCAL WORD slot;                          /* Last read code */

/* The following static variables are used
 * for seperating out codes
 */
LOCAL WORD navail_bytes = 0;              /* # bytes left in block */
LOCAL WORD nbits_left = 0;                /* # bits left in current byte */
LOCAL UTINY b1;                           /* Current byte */
LOCAL UTINY byte_buff[257];               /* Current block */
LOCAL UTINY *pbytes;                      /* Pointer to next byte in block */

LOCAL LONG code_mask[13] = {
     0,
     0x0001, 0x0003,
     0x0007, 0x000F,
     0x001F, 0x003F,
     0x007F, 0x00FF,
     0x01FF, 0x03FF,
     0x07FF, 0x0FFF
     };


/* This function initializes the decoder for reading a new image.
 */
LOCAL WORD init_exp(WORD size) {
   curr_size = size + 1;
   top_slot = 1 << curr_size;
   clear = 1 << size;
   ending = clear + 1;
   slot = newcodes = ending + 1;
   navail_bytes = nbits_left = 0;
   return(0);
}

/* get_next_code()
 * - gets the next code from the GIF file.  Returns the code, or else
 * a negative number in case of file errors...
 */
LOCAL WORD get_next_code() {
   WORD i, x;
   ULONG ret;

   if (nbits_left == 0) {
      if (navail_bytes <= 0) {

         /* Out of bytes in current block, so read next block
          */
         pbytes = byte_buff;
         if ((navail_bytes = get_byte()) < 0)
            return(navail_bytes);
         else if (navail_bytes) {
            for (i = 0; i < navail_bytes; ++i) {
               if ((x = get_byte()) < 0)
                  return(x);
               byte_buff[i] = x;
            }
         }
      }

      b1 = *pbytes++;
      nbits_left = 8;
      --navail_bytes;
   }

   ret = b1 >> (8 - nbits_left);
   while (curr_size > nbits_left) {
      if (navail_bytes <= 0) {

         /* Out of bytes in current block, so read next block
          */
         pbytes = byte_buff;
         if ((navail_bytes = get_byte()) < 0)
            return(navail_bytes);
         else if (navail_bytes) {
            for (i = 0; i < navail_bytes; ++i) {
               if ((x = get_byte()) < 0)
                  return(x);
               byte_buff[i] = x;
            }
         }
      }
      b1 = *pbytes++;
      ret |= b1 << nbits_left;
      nbits_left += 8;
      --navail_bytes;
   }
   nbits_left -= curr_size;
   ret &= code_mask[curr_size];
   return((WORD)(ret));
}


/* The reason we have these seperated like this instead of using
 * a structure like the original Wilhite code did, is because this
 * stuff generally produces significantly faster code when compiled...
 * This code is full of similar speedups...  (For a good book on writing
 * C for speed or for space optomisation, see Efficient C by Tom Plum,
 * published by Plum-Hall Associates...)
 */
LOCAL UTINY stack[MAX_CODES + 1];            /* Stack for storing pixels */
LOCAL UTINY suffix[MAX_CODES + 1];           /* Suffix table */
LOCAL UWORD prefix[MAX_CODES + 1];           /* Prefix linked list */

/* WORD decoder(linewidth)
 *    WORD linewidth;               * Pixels per line of image *
 *
 * - This function decodes an LZW image, according to the method used
 * in the GIF spec.  Every *linewidth* "characters" (ie. pixels) decoded
 * will generate a call to out_line(), which is a user specific function
 * to display a line of pixels.  The function gets it's codes from
 * get_next_code() which is responsible for reading blocks of data and
 * seperating them into the proper size codes.  Finally, get_byte() is
 * the global routine to read the next byte from the GIF file.
 *
 * It is generally a good idea to have linewidth correspond to the actual
 * width of a line (as specified in the Image header) to make your own
 * code a bit simpler, but it isn't absolutely necessary.
 *
 * Returns: 0 if successful, else negative.  (See ERRS.H)
 *
 */

WORD decoder(WORD linewidth) {
   FAST UTINY *sp, *bufptr;
   UTINY *buf;
   FAST WORD code, fc, oc, bufcnt;
   WORD c, size, ret;

   /* Initialize for decoding a new image...
    */
   if ((size = get_byte()) < 0)
      return(size);
   if (size < 2 || 9 < size)
      return(BAD_CODE_SIZE);
   init_exp(size);

   /* Initialize in case they forgot to put in a clear code.
    * (This shouldn't happen, but we'll try and decode it anyway...)
    */
   oc = fc = 0;

   /* Allocate space for the decode buffer
    */
   if ((buf = (UTINY *)malloc(linewidth + 1)) == NULL)
      return(OUT_OF_MEMORY);

   /* Set up the stack pointer and decode buffer pointer
    */
   sp = stack;
   bufptr = buf;
   bufcnt = linewidth;

   /* This is the main loop.  For each code we get we pass through the
    * linked list of prefix codes, pushing the corresponding "character" for
    * each code onto the stack.  When the list reaches a single "character"
    * we push that on the stack too, and then start unstacking each
    * character for output in the correct order.  Special handling is
    * included for the clear code, and the whole thing ends when we get
    * an ending code.
    */
   while ((c = get_next_code()) != ending) {

      /* If we had a file error, return without completing the decode
       */
      if (c < 0)
         {
         free(buf);
         return(0);
         }

      /* If the code is a clear code, reinitialize all necessary items.
       */
      if (c == clear) {
         curr_size = size + 1;
         slot = newcodes;
         top_slot = 1 << curr_size;

         /* Continue reading codes until we get a non-clear code
          * (Another unlikely, but possible case...)
          */
         while ((c = get_next_code()) == clear)
            ;

         /* If we get an ending code immediately after a clear code
          * (Yet another unlikely case), then break out of the loop.
          */
         if (c == ending)
            break;

         /* Finally, if the code is beyond the range of already set codes,
          * (This one had better NOT happen...  I have no idea what will
          * result from this, but I doubt it will look good...) then set it
          * to color zero.
          */
         if (c >= slot)
            c = 0;

         oc = fc = c;

         /* And let us not forget to put the char into the buffer... And
          * if, on the off chance, we were exactly one pixel from the end
          * of the line, we have to send the buffer to the out_line()
          * routine...
          */
         *bufptr++ = c;
         if (--bufcnt == 0) {
            if ((ret = out_line(buf, linewidth)) < 0) {
               free(buf);
               return(ret);
            }
            bufptr = buf;
            bufcnt = linewidth;
         }
      }
      else {

         /* In this case, it's not a clear code or an ending code, so
          * it must be a code code...  So we can now decode the code into
          * a stack of character codes. (Clear as mud, right?)
          */
         code = c;

         /* Here we go again with one of those off chances...  If, on the
          * off chance, the code we got is beyond the range of those already
          * set up (Another thing which had better NOT happen...) we trick
          * the decoder into thinking it actually got the last code read.
          * (Hmmn... I'm not sure why this works...  But it does...)
          */
         if (code >= slot) {
            if (code > slot)
               ++bad_code_count;
            code = oc;
            *sp++ = fc;
         }

         /* Here we scan back along the linked list of prefixes, pushing
          * helpless characters (ie. suffixes) onto the stack as we do so.
          */
         while (code >= newcodes) {
            *sp++ = suffix[code];   // bytes
            code = prefix[code];    // words
         }

         /* Push the last character on the stack, and set up the new
          * prefix and suffix, and if the required slot number is greater
          * than that allowed by the current bit size, increase the bit
          * size.  (NOTE - If we are all full, we *don't* save the new
          * suffix and prefix...  I'm not certain if this is correct...
          * it might be more proper to overwrite the last code...
          */
         *sp++ = code;
         if (slot < top_slot) {
            suffix[slot] = fc = code;
            prefix[slot++] = oc;
            oc = c;
         }
         if (slot >= top_slot) {
            if (curr_size < 12) {
               top_slot <<= 1;
               ++curr_size;
            }
         }

         /* Now that we've pushed the decoded string (in reverse order)
          * onto the stack, lets pop it off and put it into our decode
          * buffer...  And when the decode buffer is full, write another
          * line...
          */
         while (sp > stack) {
            *bufptr++ = *(--sp);
            if (--bufcnt == 0) {
               if ((ret = out_line(buf, linewidth)) < 0) {
                  free(buf);
                  return(ret);
               }
               bufptr = buf;
               bufcnt = linewidth;
            }
         }
      }
   }
   ret = 0;
   if (bufcnt != linewidth)
      ret = out_line(buf, (linewidth - bufcnt));
   free(buf);
   return(ret);
   }




	END

ERROR_WHILE_DECODING = 25

MC68000	EQU	0

	Opt	l+

	XDef	_DECODEJPEG

	Incdir	"INCLUDE:"

	Include	"exec/nodes.i"
	Include	"exec/types.i"

	Include	"WORK:GifRtnFV.s"

 STRUCTURE ScreenNode,0
  STRUCT sn_node,LN_SIZE

  STRUCT sn_data,16 ; private to the XXX functions
  UWORD sn_width    ; these describe the picture on disk
  UWORD sn_height
  UWORD sn_depth    ; 2^depth is not necessarily the number of colors (HAM)
  UWORD sn_colors   ; not initid if truecolor = TRUE
  UWORD sn_aspectx
  UWORD sn_aspecty

  ; following UBYTE's are TRUE or FALSE
  UBYTE sn_truecolor  ; No palette-info depth 1-8 = gray   depth 15,16,24 = color
  UBYTE sn_ham        ; depth 6 = HAM6   depth 8 = HAM8
  UBYTE sn_ehb
  UBYTE sn_planar

  ; now follows space for a 256-color palette (4 bytes/entry)
  STRUCT sn_palette,1024

  ; following UBYTE's are TRUE or FALSE and are set by the BestModeID function
  ; to help the rendering routines
  UBYTE sn_rerender
  UBYTE sn_gray
  UBYTE sn_deep       ; not set means HAM6/GRAY4, set means HAM8/GRAY8
  UBYTE sn_halvewidth
  UBYTE sn_direct     ; direct HAM or EHB, if TRUE then rerender must be FALSE

  UBYTE sn_pad
  WORD  sn_renderaspect
  ULONG sn_displayid
  UWORD sn_nominalw
  UWORD sn_nominalh

  ; following fields are not to be modified by the initXXX or exitXXX routines
  ULONG sn_lock       ; lock on the dir this file is located in
  APTR  sn_screen
  APTR  sn_window
  ULONG sn_buffer

  UWORD sn_renderw
  UWORD sn_renderh
  UWORD sn_renderd
  UWORD sn_pad2

  ULONG sn_buffersmemory

  ULONG sn_linebuffer
  ULONG sn_rgbbuffer
  ULONG sn_bytebuffer

  ULONG sn_floydbuf1
  ULONG sn_floydbuf2

  UWORD sn_outputline

	CNOP	0,4
_DECODEJPEG	; IN: a0.l = struct ScreenNode *
	;OUT: d0.l = Error code, zero if okay
	Movem.l	d1-d7/a0-a5,-(sp)

	Move.l	a0,a5
.YLoop	;SingleLine
	Moveq	#0,d2
	Move.w	sn_width(a5),d2
;	Cmp.w	#1,d7
;	Beq.s	.Gray
	Add.w	d2,d2
	Add.w	sn_width(a5),d2
;.Gray
	Move.l	d2,-(sp)	Bufsize
	Move.l	sn_rgbbuffer(a5),-(sp)	Buffer
	Move.l	#$73202,-(sp)	CDM_PROCESS
	Move.l	sn_data(a5),-(sp)	Object
	Bsr	DoMethod
	Lea	16(sp),sp

	Cmp.l	d0,d2
	Bne.s	.Error

	Bsr	ProcessLine
	;Bsr	CheckMouse
	;Bne.s	.UserExited

	Dbra	d6,.YLoop

	Moveq	#0,d0
.Exit	Movem.l	(sp)+,d1-d7/a0-a5
	Rts
.Error	Moveq	#ERROR_WHILE_DECODING,d0
	Bra.s	.Exit



	CNOP	0,4
DoMethod	Move.l	A2,-(SP)
	Movea.l	$8(SP),A2
	Move.l	A2,D0
	Beq.s	LabA9A
	Lea	$C(SP),A1
	Movea.l	-$4(A2),A0
	Bra.s	LabA90

	Move.l	A2,-(SP)
	Movem.l	$8(SP),A0/A2
	Move.l	A2,D0
	Beq.s	LabA9A
	Move.l	A0,D0
	Beq.s	LabA9A
	Lea	$10(SP),A1
	Movea.l	$18(A0),A0
	Bra.s	LabA90

	Move.l	A2,-(SP)
	Movem.l	$8(SP),A0/A2
	Move.l	A2,D0
	Beq.s	LabA9A
	Move.l	A0,D0
	Beq.s	LabA9A
	Lea	$10(SP),A1
	Bra.s	LabA90

	Move.l	A2,-(SP)
	Movem.l	$8(SP),A0/A2
	Move.l	A2,D0
	Beq.s	LabA9A
	Move.l	A0,D0
	Beq.s	LabA9A
	Movea.l	$10(SP),A1
LabA90	Pea	LabA9C
	Move.l	$8(A0),-(SP)
	Rts

LabA9A	Moveq	#$0,D0
LabA9C	Movea.l	(SP)+,A2

	Rts



	CNOP	0,4
ProcessLine	; IN: a5.l = struct ScreenNode *
	Movem.l	d0-d7/a0-a6,-(sp)

	Tst.b	sn_halvewidth(a5)
	Beq.s	.HAM.NoHalve
	Move.l	sn_rgbbuffer(a5),a0
	Move.w	sn_width(a5),d0
	Bsr	HalveRGB

.HAM.NoHalve	Move.l	sn_rgbbuffer(a5),a0
	Move.l	sn_bytebuffer(a5),a1
	Lea	HamPalette(pc),a2
	Lea	CacheMem(pc),a3
	Move.w	sn_width(a5),d0
	; IN: a0.l = Ptr to RGB line (3 words/color)
	; IN: a1.l = Ptr to HAM output buffer
	; IN: a2.l = Ptr to HAM-palette (4 words/color)
	; IN: a3.l = Ptr to 32768 bytes of cache memory
	; IN: d0.w = Length of RGB in pixels
	Bsr	RGBtoHAM

	Move.l	sn_bytebuffer(a5),a0
	Move.w	sn_outputline(a5),d1
	Addq.w	#1,sn_outputline(a5)
	Bsr	ChunkyToPlanar	destroys -all- registers

	Movem.l	(sp)+,d0-d7/a0-a6
	Rts


HamPalette	Dc.w	0,$00,$00,$00
	Dc.w	0,$11,$11,$11
	Dc.w	0,$22,$22,$22
	Dc.w	0,$33,$33,$33
	Dc.w	0,$44,$44,$44
	Dc.w	0,$55,$55,$55
	Dc.w	0,$66,$66,$66
	Dc.w	0,$77,$77,$77
	Dc.w	0,$88,$88,$88
	Dc.w	0,$99,$99,$99
	Dc.w	0,$aa,$aa,$aa
	Dc.w	0,$bb,$bb,$bb
	Dc.w	0,$cc,$cc,$cc
	Dc.w	0,$dd,$dd,$dd
	Dc.w	0,$ee,$ee,$ee
	Dc.w	0,$ff,$ff,$ff

CacheMem	Ds.b	4096

	END




	output the line
	Tst.l	Cat(a5)
	Beq.s	.Cont
	Bra	OutputLineRtnCat

.Cont	Movem.l	d0-d7/a0-a6,-(sp)

	Tst.l	Scale(a5)
	Beq.s	.NoScale

	Move.w	ScaleLevel(a5),d0
	Add.w	d0,VerScale(a5)
	Blt	.Exit
	Sub.w	#64,VerScale(a5)

.NoScale	Tst.b	sn_planar(a4)	Planar or Chunky?
	Beq	.Chunky

	Move.w	sn_width(a4),d0
	Move.l	sn_linebuffer(a4),a2
	Cmp.w	#24,sn_depth(a4)
	Bne.s	.Normal
	Moveq	#1,d3
	Move.l	sn_rgbbuffer(a4),a0
	Bsr	PlanarToChunky
	Move.w	sn_width(a4),d0
	Add.w	#15,d0
	And.w	#-16,d0
	Add.w	d0,a2
	Move.w	sn_width(a4),d0
	Addq.l	#1,a0
	Bsr	PlanarToChunky
	Move.w	sn_width(a4),d0
	Add.w	#15,d0
	And.w	#-16,d0
	Add.w	d0,a2
	Move.w	sn_width(a4),d0
	Addq.l	#1,a0
	Bsr	PlanarToChunky
	Bra.s	.Chunky

.Normal	Move.l	sn_bytebuffer(a4),a0
	Moveq	#0,d3
	Bsr	PlanarToChunky

	Tst.b	sn_ham(a4)
	Beq.s	.Chunky
	Tst.b	sn_rerender(a4)
	Beq.s	.Chunky
	Tst.b	sn_gray(a4)
	Bne.s	.Chunky
	Cmp.w	#8,sn_depth(a4)
	Bne.s	.Chunky

	Move.w	sn_width(a4),d0
	Move.l	a0,a2
	Move.l	sn_rgbbuffer(a4),a0
	Lea	sn_palette(a4),a1
	Bsr	HAM8ToRGB

.Chunky	Tst.b	sn_rerender(a4)
	Beq.s	.Direct
	Tst.b	sn_gray(a4)
	Bne.s	.Gray
	Bra	.HAM

.Direct	Move.l	sn_bytebuffer(a4),a0
	Tst.b	sn_truecolor(a4)
	Beq.s	.NotGray
	Cmp.w	#8,sn_depth(a4)
	Bgt.s	.NotGray
	Move.l	sn_rgbbuffer(a4),a0
.NotGray
	Tst.l	Scale(a5)
	Beq.s	.Direct.NoScale
	Move.w	sn_RenderW(a4),d1
	Bsr	PaletteScalerSimple	preserves A0
.Direct.NoScale	Move.l	a0,a1
	Bra	.Render8bit

.Gray	Btst	#0,DataInput(a5)	24 bit or 8 bit?
	Beq.s	.Gray.8bit
	Move.l	RGBBuffer(a5),a0
	Move.w	sn_W(a4),d0
	Bsr	RGBtoGray
	Bra.s	.Gray.Render

.Gray.8bit	Btst	#1,DataInput(a5)
	Bne.s	.Gray.Render
	Move.l	RGBBuffer(a5),a0
	Lea	sn_Palette(a4),a1
	Move.l	ByteBuffer(a5),a2
	Move.w	sn_W(a4),d0
	Bsr	PaletteToGray

.Gray.Render	Tst.l	Scale(a5)
	Beq.s	.Gray.NoScale

	Move.l	RGBBuffer(a5),a0
	Move.w	sn_RenderW(a4),d1
	Bsr	GrayScalerSimple

.Gray.NoScale	Move.l	RGBBuffer(a5),a0
	Move.l	ByteBuffer(a5),a1
	Move.w	sn_W(a4),d0
	Moveq	#8,d1
	Sub.w	sn_RenderDepth(a4),d1
	Tst.l	Dither(a5)
	Bne.s	.Gray.Dither
	Bsr	RGBtoBW	d0-d1 destroyed
	Bra	.Render8bit
.Gray.Dither	Bsr	RGBtoBWdither	d0-d2 destroyed
	Bra	.Render8bit


.HAM	Btst	#0,DataInput(a5)	24 bit or 8 bit?
	Bne.s	.HAM.24bit

	Move.l	RGBBuffer(a5),a0
	Lea	sn_Palette(a4),a1
	Move.l	ByteBuffer(a5),a2
	Move.w	sn_W(a4),d0
	Bsr	PaletteToRGB	d0-d4 destroyed
	Bra.s	.HAM.NoHalve

.HAM.24bit	Tst.b	HalveWidth(a5)
	Beq.s	.HAM.NoHalve
	Move.l	RGBBuffer(a5),a0
	Move.w	sn_W(a4),d0
	Bsr	HalveRGB

.HAM.NoHalve	Tst.l	MakeComment(a5)
	Beq.s	.HAM.NoHistogram
	Tst.b	HistogramAvailable(a5)
	Bne.s	.HAM.NoHistogram

	Move.l	RGBBuffer(a5),a2
	Move.l	a5,a1
	Add.l	#Histogram,a1
	Move.w	sn_W(a4),d0
	Tst.b	HalveWidth(a5)
	Beq.s	.HAM.NoHalve2
	Addq.w	#1,d0
	Lsr.w	#1,d0
.HAM.NoHalve2	Subq.w	#1,d0
.HAM.Loop	Move.b	(a2)+,d3
	Lsl.w	#4,d3
	Move.b	(a2)+,d3
	Lsl.w	#4,d3
	Move.b	(a2)+,d3
	Lsr.w	#4,d3

	IFNE	MC68000
	Add.w	d3,d3
	Move.w	(a1,d3.w),d2
	ELSE
	Move.w	(a1,d3.w*2),d2
	ENDC
	Cmp.w	#65530,d2
	Bhi.s	.HAM.NoInc
	Addq.w	#1,d2
	IFNE	MC68000
	Move.w	d2,(a1,d3.w)
	ELSE
	Move.w	d2,(a1,d3.w*2)
	ENDC

	IFNE	MC68000
	Add.w	d3,d3
	Addq.w	#1,(a1,d3.w)
	ELSE
	Addq.w	#1,(a1,d3.w*2)
	ENDC
	Bcc.s	.HAM.NoInc
	IFNE	MC68000
	Move.w	#65530,(a1,d3.w)
	ELSE
	Move.w	#65530,(a1,d3.w*2)
	ENDC
.HAM.NoInc	Dbra	d0,.HAM.Loop

.HAM.NoHistogram	Tst.l	Scale(a5)
	Beq.s	.HAM.NoScale

	Move.l	RGBBuffer(a5),a0
	Move.w	sn_RenderW(a4),d1
	Bsr	RGBScalerSimple

.HAM.NoScale	Move.w	sn_RenderW(a4),d0
	Move.l	RGBBuffer(a5),a0
	Move.l	ByteBuffer(a5),a1
	Lea	sn_RenderPalette(a4),a2
	Move.l	a5,a3
	Add.l	#Cache,a3

	Tst.l	Dither(a5)
	Bne.s	.HAM.Dither
	Cmp.b	#3,Render+1(a5)
	Beq.s	.HAM8.NoDither
	Bsr	RGBtoHAM	d0-d7 destroyed
	Bra.s	.Render8bit
.HAM8.NoDither	Bsr	RGBtoHAM8	d0-d7 destroyed
	Bra.s	.Render8bit
.HAM.Dither	Cmp.b	#3,Render+1(a5)
	Beq.s	.HAM8.Dither
	Bsr	RGBtoHAMdither	d0-d7 destroyed
	Bra.s	.Render8bit
.HAM8.Dither	Bsr	RGBtoHAM8dither	d0-d7 destroyed
	Bra	.Render8bit

.Render8bit	; IN: a1.l = Ptr to 8-bit buffer
	Move.l	a1,a0
.NotByteBuffer	Move.l	sn_Screen(a4),d0
	Bne.s	.RenderToScreen
	Move.l	sn_BufferCurrent(a4),a1
	Move.w	sn_RoundW(a4),d0
	Lsr.w	#4,d0
	Subq.w	#1,d0
.CopyToBuffer	Move.l	(a0)+,(a1)+
	Move.l	(a0)+,(a1)+
	Move.l	(a0)+,(a1)+
	Move.l	(a0)+,(a1)+
	Dbra	d0,.CopyToBuffer
	Move.l	a1,sn_BufferCurrent(a4)
	Bra.s	.Exit

.RenderToScreen	Move.l	d0,a1
	Bsr	RenderLineToScreen

.Exit	Movem.l	(sp)+,d0-d7/a0-a6
	Rts




RenderLineToScreen	; IN: a0.l = ByteBuffer (source)
	; IN: a1.l = Ptr to screen
	; IN: a4.l = ScreenNode
	Movem.l	d0-d7/a0-a6,-(sp)
	Move.w	sn_OutputLine(a4),d1
	Move.w	sn_PicType(a4),d0          !!!!
	Cmp.w	#"IF",d0
	Bne.s	.CheckRest
	Tst.b	Render+1(a5)
	Bne.s	.NotInterlaced
	Addq.w	#1,sn_OutputLine(a4)

	Move.w	sn_RenderW(a4),d2
	Bsr	PlanarToPlanar
	Bra.s	.Exit
	Beq.s	.NotInterlaced

.CheckRest	Cmp.w	#"JP",d0
	Beq.s	.NotInterlaced
	Cmp.w	#"BM",d0
	Beq.s	.Backwards
	Cmp.w	#"PC",d0
	Beq.s	.NotInterlaced
	Must be GIF
	Btst	#6,sn_SpecialInfo+1(a4)
	Beq.s	.NotInterlaced
	Move.w	d1,d2
	Sub.w	FontH(a5),d2
	Move.b	sn_SpecialInfo(a4),d3
	Beq.s	.Step8
	Subq.b	#2,d3
	Beq.s	.Step4
	Blt.s	.Step8b
.Step2	Addq.w	#2,d2
	Bra.s	.Done
.Step4	Addq.w	#4,d2
	Cmp.w	sn_H(a4),d2
	Blt.s	.Done
	Addq.b	#1,sn_SpecialInfo(a4)
	Moveq	#1,d2
	Bra.s	.Done
.Step8b	Addq.w	#8,d2
	Cmp.w	sn_H(a4),d2
	Blt.s	.Done
	Addq.b	#1,sn_SpecialInfo(a4)
	Moveq	#2,d2
	Bra.s	.Done
.Step8	Addq.w	#8,d2
	Cmp.w	sn_H(a4),d2
	Blt.s	.Done
	Addq.b	#1,sn_SpecialInfo(a4)
	Moveq	#4,d2
.Done	Add.w	FontH(a5),d2
	Move.w	d2,sn_OutputLine(a4)
	Bra.s	.DoC2P
.Backwards	Subq.w	#1,sn_OutputLine(a4)
	Bra.s	.DoC2P
.NotInterlaced	Addq.w	#1,sn_OutputLine(a4)
.DoC2P	Move.w	sn_RenderW(a4),d2
	Bsr	ChunkyToPlanar

.Exit	Movem.l	(sp)+,d0-d7/a0-a6
	Rts




PlanarToChunky	; IN: a0.l = Ptr to output buffer
	; IN: a2.l = Ptr to planar data
	; IN: d0.w = Width of line in pixels
	; IN: d3.w = 0: Palette data  1: 24-bit data
	;COM: d0-d2/d4-d7 destroyed

	Movem.l	a0-a5,-(sp)
	Add.w	#15,d0
	Lsr.w	#4,d0
	Add.w	d0,d0

	Move.w	d0,a1	1x
	Lea	(a1,d0.w),a5	2x
	;		a2 points to BP 0
	Lea	(a2,a5.l),a3	a3 points to BP 2
	Lea	(a3,a5.l),a4	a4 points to BP 4
	Lea	(a4,a5.l),a5	a5 points to BP 6

	Move.l	#$f0f0f0f0,d6
	Move.l	#$cccc3333,d7
	Subq.w	#1,d0

.Loop	Move.b	(a4,a1.l),d4	BP 5
	Swap	d4
	Move.b	(a5,a1.l),d4	BP 7
	Lsl.l	#8,d4
	Move.b	(a5)+,d4	BP 6
	Swap	d4
	Move.b	(a4)+,d4	BP 4

	Move.b	(a2,a1.l),d5	BP 1
	Swap	d5
	Move.b	(a3,a1.l),d5	BP 3
	Lsl.l	#8,d5
	Move.b	(a3)+,d5	BP 2
	Swap	d5
	Move.b	(a2)+,d5	BP 0

	Move.l	d4,d1
	Move.l	d5,d2
	And.l	d6,d4
	And.l	d6,d2
	Eor.l	d4,d1
	Eor.l	d2,d5
	Lsr.l	#4,d2
	Lsl.l	#4,d1
	Or.l	d2,d4
	Or.l	d1,d5

	Move.l	d4,d1
	And.l	d7,d4
	Eor.l	d4,d1
	Lsr.w	#2,d1
	Swap	d1
	Lsl.w	#2,d1
	Or.l	d1,d4

	Move.l	d5,d1
	And.l	d7,d5
	Eor.l	d5,d1
	Lsr.w	#2,d1
	Swap	d1
	Lsl.w	#2,d1
	Or.l	d1,d5

	Move.l	d4,d1
	And.l	#$aa55aa55,d4
	Eor.l	d4,d1
	Lsr.b	#1,d1
	Rol.w	#8,d1
	Add.b	d1,d1
	Swap	d1
	Lsr.b	#1,d1
	Rol.w	#8,d1
	Add.b	d1,d1
	Swap	d1
	Or.l	d1,d4

	Move.l	d5,d1
	And.l	#$aa55aa55,d5
	Eor.l	d5,d1
	Lsr.b	#1,d1
	Rol.w	#8,d1
	Add.b	d1,d1
	Swap	d1
	Lsr.b	#1,d1
	Rol.w	#8,d1
	Add.b	d1,d1
	Swap	d1
	Or.l	d1,d5

	Tst.b	d3
	Bne.s	.24bit
	Move.l	d4,(a0)+
	Move.l	d5,(a0)+
	Dbra	d0,.Loop
	Bra.s	.Exit

.24bit	Move.b	d4,9(a0)
	Lsr.w	#8,d4
	Move.b	d4,6(a0)
	Swap	d4
	Move.b	d4,3(a0)
	Lsr.w	#8,d4
	Move.b	d4,(a0)

	Move.b	d5,21(a0)
	Lsr.w	#8,d5
	Move.b	d5,18(a0)
	Swap	d5
	Move.b	d5,15(a0)
	Lsr.w	#8,d5
	Move.b	d5,12(a0)

	Lea	24(a0),a0
	Dbra	d0,.Loop

.Exit	Movem.l	(sp)+,a0-a5
	Rts



	CNOP	0,4
HAM8ToRGB	; IN: a0.l = Ptr to RGB output buffer
	; IN: a1.l = Ptr to palette (4 bytes/entry)
	; IN: a2.l = Ptr to Palette-line (1 byte/pixel)
	; IN: d0.w = Length of Palette-line in pixels

	Movem.l	a0/a2,-(sp)

	Moveq	#0,d5
	Move.b	1(a1),d5
	Moveq	#0,d6
	Move.b	2(a1),d6
	Moveq	#0,d7
	Move.b	3(a1),d7

	Subq.w	#1,d0

.Loop	Moveq	#0,d1
	Move.b	(a2)+,d1
	Move.w	d1,d2
	And.w	#$3f,d1
	Eor.w	d1,d2

	Sub.w	#64,d2
	Beq.s	.Blue
	Blt.s	.Color
	Tst.b	d2
	Blt.s	.Green
.Red	Lsl.b	#2,d1
	And.w	#3,d5
	Or.w	d1,d5
	Bra.s	.DoLoop
.Blue	Lsl.b	#2,d1
	And.w	#3,d7
	Or.w	d1,d7
	Bra.s	.DoLoop
.Green	Lsl.b	#2,d1
	And.w	#3,d6
	Or.w	d1,d6
	Bra.s	.DoLoop
.Color
	IFNE	MC68000
	Lsl.w	#2,d1
	Move.b	1(a1,d1.w),d5
	Move.b	2(a1,d1.w),d6
	Move.b	3(a1,d1.w),d7
	ELSE
	Move.b	1(a1,d1.w*4),d5
	Move.b	2(a1,d1.w*4),d6
	Move.b	3(a1,d1.w*4),d7
	ENDC
.DoLoop	Move.b	d5,(a0)+
	Move.b	d6,(a0)+
	Move.b	d7,(a0)+
	Dbra	d0,.Loop

	Movem.l	(sp)+,a0/a2
	Rts


	CNOP	0,4
RGBtoGray	; IN: a0.l = Ptr to RGB buffer (in- and output)
	; IN: d0.w = Length of Palette-line in pixels

	Movem.l	a0-a1,-(sp)
	Move.l	a0,a1
.Loop	Moveq	#0,d1
	Move.b	(a1)+,d1	Red component
	Move.w	d1,d2
	Lsl.w	#3,d1
	Add.w	d1,d2

	Moveq	#0,d1
	Move.b	(a1)+,d1	Green component
	Add.w	d1,d2
	Add.w	d1,d2
	Add.w	d1,d2
	Lsl.w	#4,d1
	Add.w	d1,d2

	Moveq	#0,d1
	Move.b	(a1)+,d1	Blue component
	Lsl.w	#2,d1
	Add.w	d1,d2

	Add.w	#16,d2
	Lsr.w	#5,d2
	Move.b	d2,(a0)+

	Dbra	d0,.Loop

	;R:G:B = 9:19:4 (32) => 281:594:125
	;R:G:B = 290:597:113 

	Movem.l	(sp)+,a0-a1
	Rts



	CNOP	0,4
PaletteToRGB	; IN: a0.l = Ptr to RGB output buffer
	; IN: a1.l = Ptr to palette (4 bytes/entry)
	; IN: a2.l = Ptr to Palette-line (1 byte/pixel)
	; IN: d0.w = Length of Palette-line in pixels
	;COM: d0-d4 destroyed

	Movem.l	a0/a2,-(sp)
	Moveq	#0,d1

	Tst.b	HalveWidth(a5)
	Beq.s	.Loop2
	Move.l	#$fefefe,d5
	Addq.w	#1,d0
	Lsr.w	#1,d0
	Subq.w	#1,d0

.LoopDownScaleBy2	Move.b	(a2)+,d1	Color Number
	IFNE	MC68000
	Lsl.w	#2,d1
	Move.l	(a1,d1.w),d3
	Moveq	#0,d1
	ELSE
	Move.l	(a1,d1.w*4),d3
	ENDC
	And.l	d5,d3
	Move.b	(a2)+,d1
	IFNE	MC68000
	Lsl.w	#2,d1
	Move.l	(a1,d1.w),d4
	Moveq	#0,d1
	ELSE
	Move.l	(a1,d1.w*4),d4
	ENDC
	And.l	d5,d4

	Add.l	d4,d3
	Lsr.l	#1,d3

	IFNE	MC68000
	Move.b	d3,2(a0)
	Lsr.w	#8,d3
	Move.b	d3,1(a0)
	Swap	d3
	Move.b	d3,(a0)
	ELSE
	Move.w	d3,1(a0)
	Swap	d3
	Move.b	d3,(a0)
	ENDC
	Addq.l	#3,a0
	Dbra	d0,.LoopDownScaleBy2
	Bra.s	.GoOn

.Loop2	Subq.w	#1,d0
.Loop	Move.b	(a2)+,d1
	IFNE	MC68000
	Lsl.w	#2,d1
	Move.b	1(a1,d1.w),(a0)+
	Move.b	2(a1,d1.w),(a0)+
	Move.b	3(a1,d1.w),(a0)+
	Moveq	#0,d1
	ELSE
	Move.b	1(a1,d1.w*4),(a0)+
	Move.w	2(a1,d1.w*4),(a0)+
	ENDC
	Dbra	d0,.Loop

.GoOn	Clr.b	(a0)+
	Clr.b	(a0)+
	Clr.b	(a0)+

	Movem.l	(sp)+,a0/a2
	Rts



	CNOP	0,4
PaletteToGray	; IN: a0.l = Ptr to Gray output buffer
	; IN: a1.l = Ptr to palette (4 bytes/entry)
	; IN: a2.l = Ptr to Palette-line (1 byte/pixel)
	; IN: d0.w = Length of Palette-line in pixels
	;COM: d0-d4 destroyed

	Movem.l	a0/a2,-(sp)
	Moveq	#0,d1

	Subq.w	#1,d0
.Loop	Move.b	(a2)+,d1
	Move.b	(a1,d1.w),(a0)+
	Dbra	d0,.Loop

.GoOn	Clr.b	(a0)+

	Movem.l	(sp)+,a0/a2
	Rts



	CNOP	0,4
HalveRGB	; IN: a0.l = RGB Buffer (3 bytes/pixel)
	; IN: d0.w = Number of pixels in RGB Buffer

	Movem.l	a0-a1,-(sp)
	Move.l	a0,a1
	Move.l	#$fefefe,d7
	Addq.w	#1,d0
	Lsr.w	#1,d0
	Subq.w	#1,d0

.Loop	Move.l	(a0),d1
	Lsr.l	#8,d1
	Addq.l	#2,a0
	And.l	d7,d1
	Move.l	(a0)+,d2
	And.l	d7,d2

	Add.l	d1,d2
	Lsr.l	#1,d2

	IFNE	MC68000
	Move.b	d2,2(a1)
	Lsr.w	#8,d2
	Move.b	d2,1(a1)
	Swap	d2
	Move.b	d2,(a1)
	ELSE
	Move.w	d2,1(a1)
	Swap	d2
	Move.b	d2,(a1)
	ENDC
	Addq.l	#3,a1
	Dbra	d0,.Loop

	Movem.l	(sp)+,a0-a1
	Rts




;RGBScaler	; IN: a0.l = Ptr to RGB line (3 words/color)
;	Movem.l	d0-d7/a0-a3,-(sp)
;	Move.w	PicW(a5),d1
;	Move.w	ScaleLevel(a5),d0
;	Move.w	d0,d7
;	Move.l	a0,a1
;
;	Lea	ScaleTable(pc),a2
;	Move.w	VerScale(a5),d2
;	Add.w	d0,d2
;	Cmp.w	#16,d2
;	Bge.s	.ScaleIt
;	Move.w	d2,VerScale(a5)
;	Bra	.Exit
;
;.ScaleIt	Sub.w	#16,d2
;	Move.w	d2,VerScale(a5)
;
;.Scale	Moveq	#16,d2
;	Sub.w	d7,d2
;	Move.w	(a0)+,d3
;	Move.w	(a0)+,d4
;	Move.w	(a0)+,d5
;	Move.l	(a2,d7.w*4),a3
;	Move.w	(a3,d3.w*2),d3
;	Move.w	(a3,d4.w*2),d4
;	Move.w	(a3,d5.w*2),d5
;	Subq.w	#1,d1
;	Cmp.w	d0,d2
;	Bge.s	.Again
;	Bra.s	.Lower
;
;.Again	Sub.w	d0,d2
;	Move.l	(a2,d0.w*4),a3
;
;	Move.w	(a0)+,d6
;	Add.w	(a3,d6.w*2),d3
;	Move.w	(a0)+,d6
;	Add.w	(a3,d6.w*2),d4
;	Move.w	(a0)+,d6
;	Add.w	(a3,d6.w*2),d5
;	Subq.w	#1,d1
;	Cmp.w	d0,d2
;	Bge.s	.Again
;
;.Lower	Tst.w	d2
;	Beq.s	.Skip
;	Move.l	(a2,d2.w*4),a3
;	Move.w	(a0),d6
;	Add.w	(a3,d6.w*2),d3
;	Move.w	2(a0),d6
;	Add.w	(a3,d6.w*2),d4
;	Move.w	4(a0),d6
;	Add.w	(a3,d6.w*2),d5
;
;.Skip	Move.w	d0,d7
;	Sub.w	d2,d7
;
;	Lsr.w	#4,d3
;	Lsr.w	#4,d4
;	Lsr.w	#4,d5
;
;	Move.w	d3,(a1)+
;	Move.w	d4,(a1)+
;	Move.w	d5,(a1)+
;
;.Skip3	Tst.w	d1
;	Bgt	.Scale
;	Moveq	#0,d0
;	Bra.s	.Exit2
;
;.Exit	Moveq	#-1,d0
;.Exit2	Movem.l	(sp)+,d0-d7/a0-a3
;	Rts


	CNOP	0,4
RGBScalerSimple	; IN: a0.l = Ptr to RGB line (3 bytes/color)
	; IN: d1.w = Width of RGB line
	Movem.l	d0-d2/a0-a1,-(sp)
	Move.w	ScaleLevel(a5),d0
	Move.l	a0,a1

	;Add.w	d0,VerScale(a5)
	;Blt.s	.Exit
	;Sub.w	#64,VerScale(a5)

.ScaleIt	Subq.w	#1,d1
	Moveq	#-32,d2

.Scale	Add.w	d0,d2
	Blt.s	.SkipThisOne
	Sub.w	#64,d2
	IFNE	MC68000
	Move.b	(a0)+,(a1)+
	Move.b	(a0)+,(a1)+
	Move.b	(a0)+,(a1)+
	ELSE
	Move.w	(a0)+,(a1)+
	Move.b	(a0)+,(a1)+
	ENDC
	Dbra	d1,.Scale
	Bra.s	.End
.SkipThisOne	Addq.l	#3,a0
	Bra.s	.Scale

.End	Moveq	#0,d0
.Exit	Movem.l	(sp)+,d0-d2/a0-a1
	Rts



	CNOP	0,4
GrayScalerSimple	; IN: a0.l = Ptr to Grayline (1 byte/color)
	; IN: d1.w = Width of Gray line
	Movem.l	d0-d2/a0-a1,-(sp)
	Move.w	ScaleLevel(a5),d0
	Move.l	a0,a1

	;Add.w	d0,VerScale(a5)
	;Blt.s	.Exit
	;Sub.w	#64,VerScale(a5)

.ScaleIt	Subq.w	#1,d1
	Moveq	#-32,d2

.Scale	Add.w	d0,d2
	Blt.s	.SkipThisOne
	Sub.w	#64,d2
	Move.b	(a0)+,(a1)+
	Dbra	d1,.Scale
	Bra.s	.End
.SkipThisOne	Addq.l	#1,a0
	Bra.s	.Scale

.End	Moveq	#0,d0
.Exit	Movem.l	(sp)+,d0-d2/a0-a1
	Rts



	CNOP	0,4
PaletteScalerSimple	; IN: a0.l = Ptr to Palette line (1 byte/pixel)
	; IN: d1.w = Width of palette line
	Movem.l	d0-d2/a0-a1,-(sp)
	Move.w	ScaleLevel(a5),d0
	Move.l	a0,a1

	;Add.w	d0,VerScale(a5)
	;Blt.s	.Exit
	;Sub.w	#64,VerScale(a5)

.ScaleIt	Subq.w	#1,d1
	Moveq	#-32,d2

.Scale	Add.w	d0,d2
	Blt.s	.SkipThisOne
	Sub.w	#64,d2
	Move.b	(a0)+,(a1)+
	Dbra	d1,.Scale
	Bra.s	.End
.SkipThisOne	Addq.l	#1,a0
	Bra.s	.Scale

.End	Moveq	#0,d0
.Exit	Movem.l	(sp)+,d0-d2/a0-a1
	Rts



	CNOP	0,4
RGBtoBWdither	; IN: a0.l = Ptr to Gray line (1 byte/color) (unscaled)
	; IN: a1.l = Ptr to output buffer
	; IN: d0.w = Length of RGB in pixels
	; IN: d1.w = Shift final output data d1 bits to right

	;COM: d0-d2 destroyed
	Movem.l	a0-a3,-(sp)

	Subq.w	#1,d0
	Move.l	Floyd1(a5),a2
	Move.l	Floyd2(a5),a3
	Move.l	a3,Floyd1(a5)
	Move.l	a2,Floyd2(a5)

	Moveq	#0,d2
	Clr.w	(a3)
	Moveq	#8,d4
	Sub.w	d1,d4

.Loop	Moveq	#0,d3
	Move.b	(a0)+,d3

	Add.w	(a2)+,d3
	Bge.s	.OkA
	Moveq	#0,d3
.OkA	Cmp.w	#255,d3
	Ble.s	.OkA1
	Move.w	#255,d3

.OkA1	Move.w	d3,d2
	Lsr.w	d1,d2
	Move.b	d2,(a1)+
	Move.w	d3,d5
	Lsr.w	d4,d5
	Lsl.w	d1,d2
	Or.w	d5,d2

	;d3 = Color needed   d2 = Color chosen

	Sub.w	d2,d3
	Asr.w	#2,d3
	Add.w	d3,(a2)
	Add.w	d3,-2(a3)
	Add.w	d3,(a3)+
	Move.w	d3,(a3)

	Dbra	d0,.Loop

	Movem.l	(sp)+,a0-a3
	Rts



	CNOP	0,4
RGBtoBW	; IN: a0.l = Ptr to RGB line (1 byte/color) (unscaled)
	; IN: a1.l = Ptr to output buffer
	; IN: d0.w = Length of RGB in pixels
	; IN: d1.w = Shift final output data d1 bits to right
	;COM: d0/d2 destroyed
	Movem.l	a0-a1,-(sp)
	Subq.w	#1,d0
	Moveq	#0,d2

.Loop	Move.b	(a0)+,d2
	Lsr.w	d1,d2
	Move.b	d2,(a1)+
	Dbra	d0,.Loop

	Movem.l	(sp)+,a0-a1
	Rts





;	CNOP	0,4
;PlanarToPlanar	; IN: a0.l = Ptr to planar-data
;	; IN: a1.l = Ptr to Screen
;	; IN: a4.l = ScreenNode
;	; IN: d1.w = OutputLine
;	; IN: d2.w = RenderW
;
;	Add.w	#15,d2
;	Lsr.w	#4,d2
;	Subq.w	#1,d2
;
;	Move.l	88(a1),a2	a2 = bitmap
;	Moveq	#0,d3
;	Move.b	5(a2),d3
;	Subq.w	#1,d3
;	Mulu	(a2),d1	BytesPerRow * OutputLine
;	Addq.l	#8,a2
;
;.BPLoop	Move.l	(a2)+,a1
;	Add.l	d1,a1
;	Move.w	d2,d0
;.XLoop	Move.w	(a0)+,(a1)+
;	Dbra	d0,.XLoop
;	Dbra	d3,.BPLoop
;
;	Rts
