


;                             License/Disclaimer
;                             ------------------
;
;   This  source  may be freely distributed with the XPK compression package,
;as  long  as  it is kept in its original, complete, and unmodified form.  It
;may  not  be  distributed  by  itself or in a commercial package of any kind
;without my permission.
;
;   This  source  is  distributed  in  the  hope  that it will be useful, but
;WITHOUT  ANY  WARRANTY; without even the implied warranty of MERCHANTABILITY
;or FITNESS FOR A PARTICULAR PURPOSE.
;
; M.Zimmermann (zimmerma@helios.ibr.cs.tu-bs.de)






	opt	d-,noline,o+,ow2-,o11-,ow12-	;opt2  : offset removed
						;opt11 : lea -> addq.w #x,ay
						;opt12 : move.l #x,ay ->
						;        move.w #x,ay,
						;        -32K < x < +32K





xpkHUFFLIBVERSION		equ	0
xpkHUFFLIBREVISION		equ	62


xpkHUFFLIB_INFOTEXT		MACRO
				dc.b	"("
				dc.b	"$DATE: Mon Aug 10 10:31:44 1992"
				dc.b	" by M.Zimmermann "
				dc.b	"(zimerma@helios.ibr.cs.tu-bs.de)"
				dc.b	")"
				ENDM


xpkHUFFLIB_NAME			MACRO
				dc.b	"xpkHUFF.library"
				ENDM

xpkHUFFLIB_VERSION		MACRO
				dc.b	"V ","\<xpkHUFFLIBVERSION>"
				dc.b	".","\<xpkHUFFLIBREVISION>"
				ENDM



DecrunchCode	equ	1	;0: first code
				;1: byte oriented, chache optimized
				;   decrunching
				;2: word oriented decrunching (probably best
				;   on 68000)
				;3: 68030+ cache oriented decrunching
				;4: long oriented decrunching


;CrunchMethodIdentifier:

CMI_NORMAL	equ	0	;maybe there will be other crunch
				;methods/formats l8er

;__________________________________________________________




;History:
;
;	V 0.1  - 12-Jul-1992 :	first version
;	V x.yy - 18-Jul-1992 :	first OK version
;	V x.yy - 19-Jul-1992 :	sped up decrunching
;	V x.yy - 21-Jul-1992 :	bug fixed in word/long decrunching: min pack
;                               chunk size now 3/5
;	V x.yy - 21-Jul-1992 :	replaced many subq/bxx with dbf (ie sped up
;                               crunching a little bit), bug fixed: there was
;                               a dbf counter wrong (one of my favorite 0/1
;                               problem bugs)
;	V 0.50 - 29-Jul-1992 :	added 68030+ cache optimized decrunch code
;	V 0.51 - 01-Aug-1992 :	byte decrunch improved, first code added,
;				indicator byte for crunchmethod used added,
;				68030+ chache optimized code does not make
;				sense any more, since byte decrunch fits to
;				cache completely, now
;	V 0.52 - 01-Aug-1992 :	unsafe encryption supported
;	V 0.53 - 03-Aug-1992 :	slight improvements made to crunch code
;				(+ 6K/s)
;	V 0.54 - 03-Aug-1992 :	inconsistence in expansion handling fixed
;	V 0.55 - 03-Aug-1992 :	bug fixed: expansion handling now considers
;				table creation, too
;	V 0.56 - 03-Aug-1992 :	bug fixed: HUFF now can crunch files
;				consisting of always the same byte (shame
;				on me, termination criterium was wrong)
;	V 0.57 - 03-Aug-1992 :	Tree creation code partially rewritten
;	V 0.58 - 05-Aug-1992 :	bug fixed: wrong termination criterium for
;				expansion check (my favorite 0/1 problem)
;	V 0.59 - 06-Aug-1992 :	now decrypting in a special buffer, not using
;				InBuf (this is read only, I was told) any more
;	V 0.60 - 07-Aug-1992 :	added extra memory required during
;				packing/unpacking
;	V 0.61 - 08-Aug-1992 :	expansion check changed, renamed from
;				xpkDHUF.library to xpkHUFF.library thus
;				corresponding to the possibility of handling
;				adaptive huffman codes later without having
;				an inconsistence in the name
;	V 0.62 - 10-Aug-1992 :	Flag XPKIF_MODES removed (I don't have modes
;				yet (but I have a mapping code :-=))

;__________________________________________________________


	incdir	Include_i:

	include	XPK:include/xpk.i
	include	XPK:include/xpksub.i

	include	xpkHUFFbase.i
	include	xpkHUFFlibrary.inc
	include	xpkHUFF_lib.i


;__________________________________________________________

;*********************************
;*** xpk specific declarations ***
;*********************************

MaxPackChunkSize		equ	65534	;Max input chunk size for
						;processing

	IFEQ	DecrunchCode-0
MinPackChunkSize		equ	1	;Min input chunk size for
	ENDC					;processing

	IFEQ	DecrunchCode-1
MinPackChunkSize		equ	1	;Min input chunk size for
	ENDC					;processing


	IFEQ	DecrunchCode-2
MinPackChunkSize		equ	3	;Min input chunk size for
	ENDC					;processing

	IFEQ	DecrunchCode-3
MinPackChunkSize		equ	5	;Min input chunk size for
	ENDC					;processing

	IFEQ	DecrunchCode-4
MinPackChunkSize		equ	5	;Min input chunk size for
	ENDC					;processing


DefPackChunkSize		equ	MaxPackChunkSize
;Default processing input size (must be lower tahn or equal to MaxPackChunkSize)

DefMode				equ	50
;Default mode/efficiency, ranges from [0..100]

NumberOfAvailableModes		equ	(xpkHUFF_XpkModesArray_End-xpkHUFF_XpkModesArray_Start)/xm_SIZEOF
								;+1 for terminating 0



;__________________________________________________________

LibraryExecuteCode:


	moveq	#0,d0
	lea	dosname(pc),a1
	move.l	4.w,a6
	jsr	OpenLibrary(a6)
	tst.l	d0
	beq.s	LEC_Out

	move.l	d0,a6
	jsr	Output(a6)
	move.l	d0,d1
	move.l	#Text_YouCantExecuteALibrary,d2
	move.l	#LengthOfText_YouCantExecuteALibrary,d3
	jsr	Write(a6)

	move.l	a6,a1
	move.l	4.w,a6
	jsr	CloseLibrary(a6)

LEC_Out:

	moveq	#0,d0
	rts


dosname:

	dc.b	"dos.library",00
	cnop	0,4

Text_YouCantExecuteALibrary:

	dc.b	10,"You can't execute "
	xpkHUFFLIB_NAME
	dc.b	" "
	xpkHUFFLIB_VERSION
	dc.b	"!",10
	xpkHUFFLIB_INFOTEXT
	dc.b	10,10,00

LengthOfText_YouCantExecuteALibrary	equ	*-Text_YouCantExecuteALibrary
	
	cnop	0,4

;__________________________________________________________

;**************************
;*** resident structure ***
;**************************

ROMTag:

rt_MatchTag:	dc.w	$4afc
rt_ROMTag:	dc.l	ROMTag
rt_EndSkip:	dc.l	EndCode
rt_Flags:	dc.b	RTF_AUTOINIT
rt_Version:	dc.b	xpkHUFFLIBVERSION
rt_Type:	dc.b	NT_LIBRARY
rt_Pri:		dc.b	0
rt_Name:	dc.l	Text_xpkHUFF_library
rt_IDString:	dc.l	Text_xpkHUFF_library_IDString
rt_Init:	dc.l	InitTable_xpkHUFFLibrary


Text_xpkHUFF_library:

	xpkHUFFLIB_NAME
	dc.b	00


	dc.b	"$VER: "			;I like Kick2.x!
Text_xpkHUFF_library_IDString:

	xpkHUFFLIB_NAME
	dc.b	" "
	xpkHUFFLIB_VERSION
	dc.b	" "
	xpkHUFFLIB_INFOTEXT
	dc.b	00

	cnop	0,4

;__________________________________________________________

InitTable_xpkHUFFLibrary:

	dc.l	xpkHUFF_SIZEOF	;LIB_POSSIZE
	dc.l	FunctionTable
	dc.l	DataTable	;something like primary init
	dc.l	Init_xpkHUFFLibrary




ExtFunc				equ	0
EndOfLibraryFunctionTable	equ	-1

FunctionTable:

;OS functions:

	dc.l	xpkHUFF_Open
	dc.l	xpkHUFF_Close
	dc.l	xpkHUFF_ExpungeLib
	dc.l	xpkHUFF_ExtFunc


;public functions:

	dc.l	xpkHUFF_XpksPackerInfo
	dc.l	xpkHUFF_XpksPackChunk
	dc.l	xpkHUFF_XpksPackFree
	dc.l	xpkHUFF_XpksPackReset
	dc.l	xpkHUFF_XpksUnpackChunk
	dc.l	xpkHUFF_XpksUnpackFree

	dc.l	EndOfLibraryFunctionTable





EndOfDataTable			equ	0

DataTable:

	INITBYTE	xpkHUFF_LN_TYPE,NT_LIBRARY
	INITLONG	xpkHUFF_LN_NAME,Text_xpkHUFF_library

	INITBYTE	xpkHUFF_LIB_FLAGS,LIBF_CHANGED|LIBF_SUMUSED
			;something has changed the library since last sum
			;indicates if the library allows checksumming

	INITWORD	xpkHUFF_LIB_VERSION,xpkHUFFLIBVERSION
	INITWORD	xpkHUFF_LIB_REVISION,xpkHUFFLIBREVISION
	INITLONG	xpkHUFF_LIB_IDSTRING,Text_xpkHUFF_library_IDString
	dc.l		EndOfDataTable


;__________________________________________________________


Init_xpkHUFFLibrary:

;in:
;
;	d0 :	*library base
;	a0 :	*segment list
;	a6 :	*execbase
;
;out:
;
;	d0 :	*librarybase, if succesful,
;		0 otherwise


	movem.l	d0-d7/a0-a6,-(a7)

	move.l	d0,a5
	move.l	a0,xpkHUFF_SegList(a5)


xpkHUFF_InitXPKStructures:

;in:
;
;	a5 :	*xpkHUFF.library
;out:
;	-
;
;registers trashed: -
;

	movem.l	d0-d7/a0-a6,-(a7)


	;initialize XpkMode structures [0..NumberOfAvailableModes-1]

	;init XpkMode_0

	lea	xpkHUFF_XpkMode_0(a5),a1

	move.l	#0,xm_Next(a1)				;Chain to next
							;descriptor for
							;ModeDesc list
;	lea	xpkHUFF_XpkMode_1(a5),a0
;	move.l	a0,xm_Next(a1)				;Chain to next
							;descriptor for
							;ModeDesc list


	move.l	#100,xm_Upto(a1)			;Maximum efficiency
							;handled by this mode
	move.l	#XPKMF_A3000SPEED,xm_Flags(a1)		;Timings on A3000/25

	move.l	#xpkHUFF_CRS_SIZEOF,xm_PackMemory(a1)	;Extra memory required
							;during packing
	move.l	#xpkHUFF_DRS_SIZEOF,xm_UnpackMemory(a1)	;Extra memory during
							;unpacking

	move.l	#88,xm_PackSpeed(a1)			;Approx packing speed
							;in K per second

	IFEQ	DecrunchCode-0
	move.l	#100,xm_UnpackSpeed(a1)			;Approx unpacking
	ENDC						;speed in K per second

	IFEQ	DecrunchCode-1
	move.l	#138,xm_UnpackSpeed(a1)			;Approx unpacking
	ENDC						;speed in K per second

	IFEQ	DecrunchCode-2
	move.l	#89,xm_UnpackSpeed(a1)			;Approx unpacking
	ENDC						;speed in K per second

	IFEQ	DecrunchCode-3
	move.l	#93,xm_UnpackSpeed(a1)			;Approx unpacking
	ENDC						;speed in K per second

	IFEQ	DecrunchCode-4
	move.l	#91,xm_UnpackSpeed(a1)			;Approx unpacking
	ENDC						;speed in K per second

	move.w	#241,xm_Ratio(a1)			;CF in 0.1% for
							;AmigaVision
							;executable
	move.w	#MaxPackChunkSize/1024,xm_ChunkSize(a1)	;Desired chunk size in
							;K (!!) for this mode

	move.l	#"norm",xm_Description+0*Long_SIZEOF(a1) ;8 character mode
	move.w	#"al",xm_Description+1*Long_SIZEOF(a1)   ;description
	move.w	#0000,xm_Description+1*Long_SIZEOF+1*Word_SIZEOF(a1)



;	;init XpkMode_1
;
;	lea	xpkHUFF_XpkMode_1(a5),a1
;
;	move.l	#0,xm_Next(a1)				;Chain to next
;							;descriptor for
;							;ModeDesc list
;	move.l	#100,xm_Upto(a1)			;Maximum efficiency
;							;handled by this mode
;	move.l	#XPKMF_A3000SPEED,xm_Flags(a1)		;Timings on A3000/25
;	move.l	#0,xm_PackMemory(a1)			;Extra memory required
;							;during packing
;	move.l	#0,xm_UnpackMemory(a1)			;Extra memory during
;							;unpacking
;	move.l	#100,xm_PackSpeed(a1)			;Approx packing speed
;							;in K per second
;	move.l	#100,xm_UnpackSpeed(a1)			;Approx unpacking
;							;speed in K per second
;	move.w	#100,xm_Ratio(a1)			;CF in 0.1% for
;							;AmigaVision
							;executable
;	move.w	#MaxPackChunkSize/1024,xm_ChunkSize(a1)	;Desired chunk size
;							;in K (!!) for this
;							;mode
;	move.l	#"none",xm_Description+0*Long_SIZEOF(a1)
;	move.l	#00000000,xm_Description+1*Long_SIZEOF(a1)	;8 char mode
								;description


	;initialize XpkInfo structure

	lea	xpkHUFF_XpkInfo(a5),a1

	move.w	#1,xi_XpkInfoVersion(a1)		;Version number of
							;this structure
	move.w	#xpkHUFFLIBVERSION,xi_LibVersion(a1)	;The version of this
							;sublibrary
	move.w	#1,xi_MasterVersion(a1)			;The required master
							;lib version
	move.w	#0,xi_ModesVersion(a1)			;The version of the
							;mode descriptors

	lea	Text_xpkHUFFBriefName(pc),a0		;Brief name of the
	move.l	a0,xi_Name(a1)				; packer

	lea	Text_xpkHUFFFullName(pc),a0		;Full name of the
	move.l	a0,xi_LongName(a1)			;packer

	lea	Text_xpkHUFFDescription(pc),a0		;One line description
	move.l	a0,xi_Description(a1)			;of packer

	move.l	#"HUFF",xi_ID(a1)			;ID the packer goes by
							;(XPK format)
	move.l	#XPKIF_PK_CHUNK|XPKIF_UP_CHUNK|XPKIF_ENCRYPTION,xi_Flags(a1)
	move.l	#MaxPackChunkSize,xi_MaxPkInChunk(a1)	;Max input chunk size
							;for packing
	move.l	#MinPackChunkSize,xi_MinPkInChunk(a1)	;Min input chunk size
							;for packing
	move.l	#DefPackChunkSize,xi_DefPkInChunk(a1)	;Default packing chunk
							;size

	lea	Text_xpkHUFFPackMsg(pc),a0		;Packing message,
	move.l	a0,xi_PackMsg(a1)			;present tense

	lea	Text_xpkHUFFUnpackMsg(pc),a0		;Unpacking message,
	move.l	a0,xi_UnpackMsg(a1)			;present tense

	lea	Text_xpkHUFFPackedMsg(pc),a0		;Packing message,
	move.l	a0,xi_PackedMsg(a1)			;past tense

	lea	Text_xpkHUFFUnpackedMsg(pc),a0		;Unpacking message,
	move.l	a0,xi_UnpackedMsg(a1)			;past tense

	move.w	#DefMode,xi_DefMode(a1)			;Default mode number
	move.w	#0,xi_Pad(a1)				;for future use

	lea	xpkHUFF_XpkModesArray_Start(a5),a0	;*first mode structure
	move.l	a0,xi_Modes(a1)				;Array of compression
							;modes

	clr.l	xi_Reserved+0*Long_SIZEOF(a1)		;Future expansion -
	clr.l	xi_Reserved+1*Long_SIZEOF(a1)		;set to zero
	clr.l	xi_Reserved+2*Long_SIZEOF(a1)
	clr.l	xi_Reserved+3*Long_SIZEOF(a1)
	clr.l	xi_Reserved+4*Long_SIZEOF(a1)
	clr.l	xi_Reserved+5*Long_SIZEOF(a1)

	movem.l	(a7)+,d0-d7/a0-a6

	movem.l	(a7)+,d0-d7/a0-a6

	rts



Text_xpkHUFFBriefName:

	dc.b	"HUFF",00
	cnop	0,4


Text_xpkHUFFFullName:

	dc.b	"Huffman "
	xpkHUFFLIB_VERSION
	dc.b	00
	cnop	0,4


Text_xpkHUFFDescription:



	IFEQ	DecrunchCode-0
	dc.b	"Dynamic huffman crunch algorithm, "
	dc.b	"very simple decrunch algorithm",00
	ENDC

	IFEQ	DecrunchCode-1
	dc.b	"Dynamic huffman crunch algorithm, "
	dc.b	"cache optimized byte decrunch algorithm",00
	ENDC

	IFEQ	DecrunchCode-2
	dc.b	"Dynamic huffman crunch algorithm, "
	dc.b	"word oriented decrunch algorithm",00
	ENDC

	IFEQ	DecrunchCode-3
	dc.b	"Dynamic huffman crunch algorithm, "
	dc.b	"68030+ cache oriented decrunch algorithm",00
	ENDC

	IFEQ	DecrunchCode-4
	dc.b	"Dynamic huffman crunch algorithm, "
	dc.b	"long oriented decrunch algorithm",00
	ENDC

	cnop	0,4


Text_xpkHUFFPackMsg:

	dc.b	"Crunching",00
	cnop	0,4


Text_xpkHUFFUnpackMsg:

	dc.b	"Decrunching",00
	cnop	0,4


Text_xpkHUFFPackedMsg:

	dc.b	"Crunched",00
	cnop	0,4


Text_xpkHUFFUnpackedMsg:

	dc.b	"Decrunched",00

	cnop	0,4



;__________________________________________________________


xpkHUFF_Open:

;in:
;
;	d0 :	version
;	a6 :	*library base
;
;out:
;
;	d0 :	*library base, if successful,
;		0 otherwise
;
	addq.w	#1,LIB_OPENCNT(a6)
	bclr	#LIBB_DELEXP,LIB_FLAGS(a6)

	move.l	a6,d0
	rts

;__________________________________________________________

xpkHUFF_Close:

;in:
;
;	a6 :	*library base
;
;out:
;
;	d0 :	*SegList, if no longer open and delayed expunge,
;		0 otherwise


	moveq	#0,d0

	subq.w	#1,LIB_OPENCNT(a6)
	bne.s	xpkHUFF_C_OpenCountNot0

	btst	#LIBB_DELEXP,LIB_FLAGS(a6)
	beq.s	xpkHUFF_C_DontExpungeLibrary

	bsr.s	xpkHUFF_ExpungeLib

xpkHUFF_C_OpenCountNot0:
xpkHUFF_C_DontExpungeLibrary:

	rts



;__________________________________________________________

xpkHUFF_ExpungeLib:

;according  to Autodocs/RemLibrary a Forbid/Permit pair MUST be placed around
;RemLibrary  to  be  secure.  This should be granted, for ROM code calls this
;typically.  If not called from ROM, there MUST be Forbid/Permit.

;in:
;
;	a6 :	*library base
;
;out:
;
;	d0 :	*SegList, if no longer open and delayed expunge,
;		0 otherwise


	tst.w	LIB_OPENCNT(a6)
	bne.s	xpkHUFF_EL_OpenCountNot0

;free   all   resources  (fi  close  libraries)  here  that  were  opened  in
;Init_xpkHUFFLibrary


xpkHUFF_EL_RemoveLibraryNode:

	movem.l	d1-d7/a0-a6,-(a7)

	move.l	a6,a5
	bsr.s	SmartFreexpkHUFFLibraryResources


	move.l	a6,a5

	move.l	a6,a1
	move.l	4.w,a6
	jsr	Remove(a6)			;remove library node

	move.l	xpkHUFF_SegList(a5),d7		;keep SegList in mind
						;(what mind? :-)

	move.l	a5,a1				;*xpkHUFF library base
	moveq	#0,d0
	move.w	LIB_NEGSIZE(a5),d0
	sub.l	d0,a1				;equ *memory to free
	add.w	LIB_POSSIZE(a5),d0		;equ size of memory to free

	move.l	4.w,a6
	jsr	FreeMem(a6)

	move.l	d7,d0				;return SegList

	movem.l	(a7)+,d1-d7/a0-a6

	rts


xpkHUFF_EL_OpenCountNot0:

	bset	#LIBB_DELEXP,LIB_FLAGS(a6)	;next CloseLib will expunge
						;the library if OpenCount = 0

	moveq	#0,d0
	rts

;__________________________________________________________

xpkHUFF_ExtFunc:

	moveq	#0,d0
	rts


;__________________________________________________________

SmartFreexpkHUFFLibraryResources:

;in:
;
;	a5 :	*xpkHUFF.library
;out:
;	-
;
;registers trashed: -
;


	movem.l	d0-d7/a0-a6,-(a7)

	nop				;implemented for future use

	movem.l	(a7)+,d0-d7/a0-a6
	rts


;==========================================================
;==========================================================
                                                
;********************************
;*** xpk sublibrary functions ***
;********************************


xpkHUFF_XpksPackerInfo:

;in:
;
;	a6 :	*xpkHUFF.library
;out:
;	d0 :	*xh_XpkInfo
;
;registers trashed: -
;

	move.l	a6,d0
	add.l	#xpkHUFF_XpkInfo,d0

	rts

;__________________________________________________________


xpkHUFF_XpksPackChunk:

;in:
;
;	a0 :	*XpkSubParams
;	a6 :	*xpkHUFF.library
;out:
;	d0 :	err
;
;registers trashed: d0, d1, a0, a1
;




	movem.l	d1-d7/a1-a6,-(a7)



CurrentlyNotNeeded	MACRO
;map mode efficiency [0..100] to mode numbers [0..NumberOfAvailableModes-1]

	move.l	xsp_Mode(a0),d1				;The packing
							;efficiency to use
	lea	xpkHUFF_XpkModesArray_Start(a6),a1	;*modes array
	moveq	#0,d0					;mode number counter

XpksPackChunk_FindModeLoop:

	move.l	xm_Upto(a1),d2				;max. handled
							;efficiency of current
							;mode
	cmp.l	d2,d1					;do we need higher
							;efficiency ?
	bls.s	XpksPackChunk_ModeFound			;no, we'll take this
							;mode

	addq.l	#1,d0					;increase mode number
							;counter	
	move.l	xm_Next(a1),a1				;*next mode
	bne.s	XpksPackChunk_FindModeLoop		;there are more modes

XpksPackChunk_ModeNotAvailable:

	moveq	#XPKERR_WRONGMODE,d0
	movem.l	(a7)+,d2-d7/a2-a6
	rts

XpksPackChunk_ModeFound:

	cmp.l	#NumberOfAvailableModes-1,d0
	bhi.s	XpksPackChunk_ModeNotAvailable		;occurs only if we
							;have less entries in
							;the jump-table than
							;in the modes array

	;map mode number to appropriate routine

							;in d0: mode number
							;[0..NumberOfAvailableModes-1]
	add.l	d0,d0					;make long offset for
	add.l	d0,d0					;jump table

	lea	XpksPackChunkModeJumpTable(pc),a1
	add.l	0(a1,d0),a1				;calc adr of routine
	jsr	(a1)					;jump to specified
							;mode routine (sets d0
							;apropriate)

	ENDM						;see above

	bsr.s	XpksPackChunkMode_0			;if more modes become
							;available, this line
							;is to deleted and
							;mapping code above
							;is to be used


	movem.l	(a7)+,d1-d7/a1-a6
	rts


;__________________________________________________________


xpkHUFF_XpksPackFree:

;in:
;
;	a0 :	*XpkSubParams
;	a6 :	*xpkHUFF.library
;out:
;
;
;registers trashed: -
;

	rts

;__________________________________________________________


xpkHUFF_XpksPackReset:

;in:
;
;	a0 :	*XpkSubParams
;	a6 :	*xpkHUFF.library
;out:
;
;
;registers trashed: -
;

	rts

;__________________________________________________________


xpkHUFF_XpksUnpackChunk:

;in:
;
;	a0 :	*XpkSubParams
;	a6 :	*xpkHUFF.library
;out:
;	d0 :	err
;
;registers trashed: d0, d1, a0, a1
;

	movem.l	d1-d7/a1-a6,-(a7)

CurrentlyNotNeeded2	MACRO

;map mode efficiency [0..100] to mode numbers [0..NumberOfAvailableModes-1]

	move.l	xsp_Mode(a0),d1				;The unpacking
							;efficiency to use
	lea	xpkHUFF_XpkModesArray_Start(a6),a1	;*modes array
	moveq	#0,d0					;mode number counter

XpksUnpackChunk_FindModeLoop:

	move.l	xm_Upto(a1),d2				;max. handled
							;efficiency of current
							;mode
	cmp.l	d2,d1					;do we need higher
							;efficiency ?
	bls.s	XpksUnpackChunk_ModeFound		;nope, we'll take this
							;mode

	addq.l	#1,d0					;increase mode number
							;counter	
	move.l	xm_Next(a1),a1				;*next mode
	bne.s	XpksUnpackChunk_FindModeLoop		;there are more modes

XpksUnpackChunk_ModeNotAvailable:

	moveq	#XPKERR_WRONGMODE,d0
	movem.l	(a7)+,d2-d7/a2-a6
	rts

XpksUnpackChunk_ModeFound:

	cmp.l	#NumberOfAvailableModes-1,d0
	bhi.s	XpksUnpackChunk_ModeNotAvailable	;occurs only if we
							;have less entries in
							;the jump-table than
							;in the modes array

	;map mode number to appropriate routine

							;in d0: mode number
							;[0..NumberOfAvailableModes-1]
	add.l	d0,d0					;make long offset for
	add.l	d0,d0					;jump table

	lea	XpksUnpackChunkModeJumpTable(pc),a1
	add.l	0(a1,d0),a1				;calc adr of routine
	jsr	(a1)					;jump to specified
							;mode routine (sets d0
							;apropriate)

	ENDM						;see above

	bsr	XpksUnpackChunkMode_0			;if more modes become
							;available, this line
							;is to deleted and
							;mapping code above is
							;to be used

	movem.l	(a7)+,d1-d7/a1-a6
	rts

;__________________________________________________________


xpkHUFF_XpksUnpackFree:

;in:
;
;	a0 :	*XpkSubParams
;	a6 :	*xpkHUFF.library
;out:
;
;
;registers trashed: -
;

	rts



;__________________________________________________________


;************************************
;*** xpkHUFF sublib specific data ***
;************************************

	cnop	0,4


XpksPackChunkModeJumpTable:

	;format: .l offset distance between table and routine (is negative)

	dc.l	XpksPackChunkMode_0-XpksPackChunkModeJumpTable

XpksPackChunkModeJumpTableEnd:

	IFNE	NumberOfAvailableModes-(XpksPackChunkModeJumpTableEnd-XpksPackChunkModeJumpTable)/Long_SIZEOF
	FAIL	;different number of modes defined in xpkmodes array
		;please check the jump table above and update it

	ENDC

XpksUnpackChunkModeJumpTable:

	;format: .l offset distance between table and routine (is negative)

	dc.l	XpksUnpackChunkMode_0-XpksUnpackChunkModeJumpTable

XpksUnpackChunkModeJumpTableEnd:

	IFNE	NumberOfAvailableModes-(XpksUnpackChunkModeJumpTableEnd-XpksUnpackChunkModeJumpTable)/Long_SIZEOF
	FAIL	;different number of modes defined in xpkmodes array
		;please check the jump table above and update it
	ENDC



;__________________________________________________________



;**********************
;*** un-/pack modes ***
;**********************

;**************
;*** mode 0 ***
;**************


;mode 0 performs: dynamic huffman crunching
;			

XpksPackChunkMode_0:

;in:
;
;	a0 :	*XpkSubParams
;	a6 :	*xpkHUFF.library
;out:
;	d0 :	err
;	in XpkSubParams :	xsp_OutLen
;
;registers trashed:


;******************************
;*** huffman node structure ***
;******************************


xpkHUFF_HuffmanNodeStructure:
	rsreset

xpkHUFF_LinkToNextNodeOrLeaf:	rs.l	1	;* for fast tree creation

xpkHUFF_Sum:			rs.w	1	;sum of left and right
						;subtree sums
xpkHUFF_Char:			rs.b	1	;byte # (0..255) if leaf,
						;invalid else
xpkHUFF_Type:			rs.b	1	;UNUSED|NODEUSED|NODEUNUSED|
						;LEAFUSED|LEAFUNUSED
xpkHUFF_Parent:			rs.l	1	;*parent node or 0

xpkHUFF_Left:			rs.l	1	;*left subtree
xpkHUFF_Right:			rs.l	1	;*right subtree

xpkHUFF_Direction:		rs.w	1	;direction which to take from
						;parent to this node
xpkHUFF_pad1:			rs.w	1

xpkHUFF_pad2:			rs.l	1
xpkHUFF_pad3:			rs.l	1

xpkHUFF_HuffmanNodeStruct_SIZEOF:	rs.b	0	;size: 8 longs ==>
							;shifing for size
							;correction possible







;*************************
;*** reentry structure ***
;*************************


MAXHUFFMANCODELENGTH				equ	256/8

xpkHUFF_CrunchReentryStructure:
	rsreset

xpkHUFF_CRS_xpkSubParams:			rs.l	1		;*SubParamsStructure
xpkHUFF_CRS_xpkSubLibBase:			rs.l	1		;the pointer to my sublibrary
xpkHUFF_CRS_StatisticArray:			rs.b	256*Word_SIZEOF	;Word_SIZEOF: max InputBuffer allowed is 64K!
xpkHUFF_CRS_InBuf:				rs.l	1		;*input buffer
xpkHUFF_CRS_InLen:				rs.l	1		;size of input buffer in bytes
xpkHUFF_CRS_OutBuf:				rs.l	1		;*output buffer
xpkHUFF_CRS_OutLen:				rs.l	1		;will be set to output buffer size after crunching
xpkHUFF_CRS_OutBufLen:				rs.l	1		;for overflow check when crunching (recognize expansion)
xpkHUFF_CRS_CrunchedLength:			rs.l	1
xpkHUFF_CRS_DynamicHuffmanTreeLeafs:		rs.b	256*xpkHUFF_HuffmanNodeStruct_SIZEOF
xpkHUFF_CRS_DynamicHuffmanTerminatorLeaf:	rs.b	xpkHUFF_HuffmanNodeStruct_SIZEOF	;MUST stay uninitialized for a terminating condition below
xpkHUFF_CRS_DynamicHuffmanTreeNodes:		rs.b	255*xpkHUFF_HuffmanNodeStruct_SIZEOF
xpkHUFF_CRS_RootNode:				rs.l	1
xpkHUFF_CRS_PtrTableToSizesAndCodes:		rs.l	256*Long_SIZEOF
xpkHUFF_CRS_SpaceForLengthsAndCodes:		rs.b	256*MAXHUFFMANCODELENGTH+256*Byte_SIZEOF	;size will be stored in bytes here

xpkHUFF_CRS_SIZEOF:				rs.b	0


;************
;*** main ***
;************


xpkHUFF_Main:


	move.l	a0,a2					;keep subparams in
							;mind
	move.l	a6,a3					;keep xpkHUFF base
							;in mind

	move.l	#xpkHUFF_CRS_SIZEOF,d0
	move.l	#MEMF_CLEAR|MEMF_PUBLIC,d1
	move.l	4.w,a6
	jsr	AllocMem(a6)
	tst.l	d0
	beq	xpkHUFF_AllocReentryBufferFailed

	move.l	d0,a5					;reentry buffer
							;in a5, now

	move.l	a3,xpkHUFF_CRS_xpkSubLibBase(a5)	;store xpkHUFF base
							;in the reentry
							;structure
	move.l	a2,xpkHUFF_CRS_xpkSubParams(a5)		;store xpksubparams
							;in reentry structure

;______________


;********************************************
;*** get data from xpksubparams structure ***
;********************************************

	move.l	xsp_InBuf(a2),xpkHUFF_CRS_InBuf(a5)
	move.l	xsp_InLen(a2),xpkHUFF_CRS_InLen(a5)

	move.l	xsp_OutBuf(a2),xpkHUFF_CRS_OutBuf(a5)
	move.l	xsp_OutBufLen(a2),xpkHUFF_CRS_OutBufLen(a5)


;*******************************
;*** create statistics table ***
;*******************************



xpkHUFF_StatisticLoopInit:

	move.l	xpkHUFF_CRS_InBuf(a5),a0
	move.l	xpkHUFF_CRS_InLen(a5),d0		;InLen <= MaxPackChunkSize!
	subq.l	#1,d0					;dbf
	lea	xpkHUFF_CRS_StatisticArray(a5),a1

xpkHUFF_StatisticLoop:

	moveq	#0,d1
	move.b	(a0)+,d1
	add.w	d1,d1					;byte offset
							;-> word offset
	addq.w	#1,0(a1,d1.w)

	dbf	d0,xpkHUFF_StatisticLoop





;***************************************
;*** init leafs in reentry structure ***
;***************************************

xpkHUFF_InitLeafs:

	lea	xpkHUFF_CRS_StatisticArray(a5),a0
	lea	xpkHUFF_CRS_DynamicHuffmanTreeLeafs(a5),a1

	moveq	#-1,d0					;current byte #
	moveq	#LEAFUNLINKED,d1
	moveq	#-2,d2					;current byte's
							;word offset

	move.w	#255,d3					;256 runs (bytes range
							;from 0..255)

	moveq	#NOLISTLINKYET,d4

xpkHUFF_InitLeafsLoop:

	addq.l	#1,d0					;next byte number
	addq.l	#2,d2					;next word offset

	move.w	0(a0,d2.w),d5
	beq.s	xpkHUFF_ILL_DontConsiderEmptyLeaf

	move.b	d0,xpkHUFF_Char(a1)			;current byte #

	move.b	d1,xpkHUFF_Type(a1)			;type := LEAFUNLINKED

	move.w	d5,xpkHUFF_Sum(a1)

	move.l	d4,xpkHUFF_LinkToNextNodeOrLeaf(a1)

	lea	xpkHUFF_HuffmanNodeStruct_SIZEOF(a1),a1	;proceed to next leaf

xpkHUFF_ILL_DontConsiderEmptyLeaf:

	dbf	d3,xpkHUFF_InitLeafsLoop


;***********************************
;*** link leafs from low to high ***
;***********************************


FindHighestSum	MACRO

;finds  highest  sum of all currently unlinked nodes and returns a pointer to
;that node in a2

;
;registers trashed: a0, d1, a2


xpkHUFF_LL_FindHighestSum:

	lea	xpkHUFF_CRS_DynamicHuffmanTreeLeafs(a5),a0

	moveq	#0,d1					;initial sum
	sub.l	a2,a2					;currently no node
							;with highest sum


xpkHUFF_LL_FindHighestSumLoop:

	tst.b	xpkHUFF_Type(a0)			;= cmp.w #UNUSED,
							;pkHUFF_Type, ie did
	beq.s	xpkHUFF_LL_FoundHighestSum		; we reach the end of
							;the list?

	cmp.l	xpkHUFF_LinkToNextNodeOrLeaf(a0),d3	;already considered?
	bne.s	xpkHUFF_LL_AlreadyConsidered

	cmp.w	xpkHUFF_Sum(a0),d1
	bhi.s	xpkHUFF_LL_NoHigherSum

	move.w	xpkHUFF_Sum(a0),d1			;current sum
	move.l	a0,a2					;remember this adr


xpkHUFF_LL_AlreadyConsidered:
xpkHUFF_LL_NoHigherSum:

	lea	xpkHUFF_HuffmanNodeStruct_SIZEOF(a0),a0
	bra.s	xpkHUFF_LL_FindHighestSumLoop

xpkHUFF_LL_FoundHighestSum:


		ENDM





;register usage:
;
;	a1:	*last node

xpkHUFF_LinkLeafs:

	moveq	#0,d2					;for a fast cmp below
	sub.l	a1,a1					;keep 'last' node in
							;mind (in this case no
							;last node)
	moveq	#NOLISTLINKYET,d3			;for cmp below

xpkHUFF_LinkLeafsLoop:

	FindHighestSum

;here a2 contains the adr of the node with the highest sum or 0, if all nodes
;are linked already

	cmp.l	d2,a2					;no highest sum any
							;more?
	beq.s	xpkHUFF_LeavesLinked			;nope: ==> done

	move.l	a1,xpkHUFF_LinkToNextNodeOrLeaf(a2)
	move.l	a2,a1

	bra.s	xpkHUFF_LinkLeafsLoop

xpkHUFF_LeavesLinked:

	move.l	a1,a4


;here a4 contains the currently first node




;*********************
;*** CreateNewNode ***
;*********************

CreateNewNode	MACRO

;in:	a0: *A
;	a1: *N

	move.l	a1,a2
	move.l	xpkHUFF_LinkToNextNodeOrLeaf(a0),a1


;a0: *A
;a1: *B
;a2: *N

	move.w	#LEFTSUBTREE,xpkHUFF_Direction(a0)
	move.w	#RIGHTSUBTREE,xpkHUFF_Direction(a1)

	move.w	xpkHUFF_Sum(a0),d0
	add.w	xpkHUFF_Sum(a1),d0

	move.w	d0,xpkHUFF_Sum(a2)

	move.b	#NODEUSED,xpkHUFF_Type(a2)
;	move.b	#LEAFLINKED,xpkHUFF_Type(a0)		;not necessary, therefore not assembled
;	move.b	#LEAFLINKED,xpkHUFF_Type(a1)		;not necessary, therefore not assembled

	move.l	a0,xpkHUFF_Left(a2)
	move.l	a1,xpkHUFF_Right(a2)

	move.l	a2,xpkHUFF_Parent(a0)
	move.l	a2,xpkHUFF_Parent(a1)

	move.l	xpkHUFF_LinkToNextNodeOrLeaf(a1),a0
	lea	xpkHUFF_HuffmanNodeStruct_SIZEOF(a2),a1


;a0: *C
;a1: *O
;a2: *N

		ENDM







;***************************
;*** insert node to list ***
;***************************


InsertTreeNodeToList	MACRO

;in:	a0: *first node in list
;	a2: *node to insert in list

;a0: *first node in list (*C)
;a1: MUST BE LEFT UNTOUCHED
;a2: *node to insert (*N)


;have the last two nodes been linked to a lonely (ie in this case root) node?
;if so, we are ready


	cmp.l	a0,d7			;d7 = 0
	bne.s	HSH1L_a2IsNoLonelyNode

	move.l	a2,a0
	bra.s	xpkHUFF_InsertionComplete


HSH1L_a2IsNoLonelyNode:

	move.w	xpkHUFF_Sum(a2),d0

;_____________

	cmp.w	xpkHUFF_Sum(a0),d0
	bhi.s	xpkHUFF_DontInsertAsHeadOfList

xpkHUFF_InsertAsHeadOfList:

;case: 3 5 7 ... insert 2 before 3

	move.l	a0,xpkHUFF_LinkToNextNodeOrLeaf(a2)
	move.l	a2,a0
	bra.s	xpkHUFF_InsertionComplete

;_____________


xpkHUFF_DontInsertAsHeadOfList:


	move.l	a0,a3					;*first node in list


xpkHUFF_ProceedToNextNode:

	cmp.l	xpkHUFF_LinkToNextNodeOrLeaf(a3),d7
	beq.s	xpkHUFF_InsertAtEndOfList

	move.l	a3,a4
	move.l	xpkHUFF_LinkToNextNodeOrLeaf(a3),a3

	cmp.w	xpkHUFF_Sum(a3),d0
	bhi.s	xpkHUFF_ProceedToNextNode


;_____________


xpkHUFF_InsertInMiddleOfList:

;case: 3 5 ... insert 4 between 3 and 5

	move.l	a2,xpkHUFF_LinkToNextNodeOrLeaf(a4)

	move.l	a3,xpkHUFF_LinkToNextNodeOrLeaf(a2)
	bra.s	xpkHUFF_InsertionComplete

;_____________


xpkHUFF_InsertAtEndOfList:

;in: a3: *last node in list

;case: 3 5 7   insert 8 behind 7

	move.l	a2,xpkHUFF_LinkToNextNodeOrLeaf(a3)

	move.l	d7,xpkHUFF_LinkToNextNodeOrLeaf(a2)
;	bra.s	xpkHUFF_InsertionComplete

;_____________


xpkHUFF_InsertionComplete:

;a0: *first node in list

			ENDM






;*******************
;*** create tree ***
;*******************



;Here  we have all leafs lying next to each other, in order 0..255.  They are
;linked  via  a  single pointer (xpkHUFF_LinkToNextNodeOrLeaf).  If you start
;with  a4  here and proceed using the link pointers, you will find all leaves
;in ascending order (order: low to high by xpkHUFF_Sum).

;
;suggest we have the nodes
;
;  A  B  C  D  E  F   G  H . . . N O P
;  3  5  6  7  8  9  10 11       new (currently unused) nodes
;

;Now  we  have  to  create  a  new  node  N  with  A as xpkHUFF_Left and B as
;xpkHUFF_Right.   This new node N has to be sorted into he list (ie between D
;and  E).  The nodes A and B are excluded from the list when creating the new
;node N.  This will go on until there are only two nodes left.  These will be
;linked by the root node of the huffman tree.




;register usage:

;	a0: *current root of used nodes  (at the beginning *A)
;	a1: *currently first unused node (at the beginning *N)

	move.l	a4,a0						;a0: *A
	lea	xpkHUFF_CRS_DynamicHuffmanTreeNodes(a5),a1	;a1: *N
	moveq	#0,d7


	cmp.l	xpkHUFF_LinkToNextNodeOrLeaf(a0),d7		;is there only
								;one node?

	bne.s	xpkHUFF_CreateTreeLoop				;nope: construct
								;      tree

xpkHUFF_ThereIsOnlyOneNode:					;create tree
								;containing
								;only one leaf
;create new root node at (a1)

	move.w	#LEFTSUBTREE,xpkHUFF_Direction(a0)
;	move.w	xpkHUFF_Sum(a0),xpkHUFF_Sum(a1)

	move.l	a0,xpkHUFF_Left(a1)
	move.l	a1,xpkHUFF_Parent(a0)

	move.l	a1,a2						;new root
								;in a2
	bra.s	xpkHUFF_TreeCreationComplete



xpkHUFF_CreateTreeLoop:


	cmp.l	xpkHUFF_LinkToNextNodeOrLeaf(a0),d7
	beq.s	xpkHUFF_TreeCreationComplete

	CreateNewNode			;creates new node from a0 (A) and the
					;sequel node of A (B) in node a1 (N).
					;increments a0 (will point to C
					;afterwards) and a1 (will point to O
					;afterwards).
					;returns adr of N in a2




	InsertTreeNodeToList		;insert the node we just created
					;(a2: N) into the list (a0: C)
					;positioned according to it's
					;xpkHUFF_Sum field.


	bra.s	xpkHUFF_CreateTreeLoop



xpkHUFF_TreeCreationComplete:

;a2: *root node of huffman tree

	move.b	#ROOTNODE,xpkHUFF_Type(a2)
	move.w	#NODIRECTION,xpkHUFF_Direction(a2)

	move.l	a2,xpkHUFF_CRS_RootNode(a5)





xpkHUFF_CreateTranslationTableSizesAndCodes:

;**********************************************************
;*** create translation table, sizes of codes and codes ***
;**********************************************************


	lea	xpkHUFF_CRS_DynamicHuffmanTreeLeafs(a5),a0

	lea	xpkHUFF_CRS_PtrTableToSizesAndCodes(a5),a3
	lea	xpkHUFF_CRS_SpaceForLengthsAndCodes(a5),a4


xpkHUFF_CreateTranslationTableSizesAndCodesLoop:

;____________________________


xpkHUFF_GetOneLeafsCode:

;get code of leaf a0
;write length as byte to (a4)+
;write bitcode of leaf pointed to by a0 to (a4)+ behind size

	move.b	xpkHUFF_Char(a0),d5			;keep char in mind

	moveq	#0,d6
	move.b	d5,d6
	add.w	d6,d6					;offset:
	add.w	d6,d6					;.b -> .l

	move.l	a4,0(a3,d6.w)				;put pointer to table


	moveq	#-1,d1					;bit counter (dbf)
	move.l	a0,a1

xpkHUFF_GetOneLeafsCodeLoop:

	moveq	#0,d0
	move.w	xpkHUFF_Direction(a1),d0		;root reached?
	bmi.s	xpkHUFF_PutOneLeafsCompleteCodeOnStack	;yep

	addq.l	#1,d1					;one more bit for code
	move.w	d0,-(a7)
	move.l	xpkHUFF_Parent(a1),a1			;go up

	bra.s	xpkHUFF_GetOneLeafsCodeLoop


xpkHUFF_PutOneLeafsCompleteCodeOnStack:

;here all bits are on the stack, d1 is a dbf counter for composition

	move.b	d1,(a4)+

xpkHUFF_ComposeHuffmanCode:

	moveq	#0,d2					;will contain
							;composed code
	moveq	#7,d3					;# of bits not yet
							;used in dest
							;register (dbf)

xpkHUFF_ComposeHuffmanCodeLoop:

	move.w	(a7)+,d0


	roxr.b	#1,d0					;code bit to x & c
							;flag
	addx.b	d2,d2					;code bit to composed
							;code register
	dbf	d3,xpkHUFF_CHCL_DontWriteCodeByte

	moveq	#7,d3					;reset counter
	move.b	d2,(a4)+				;write to buffer

xpkHUFF_CHCL_DontWriteCodeByte:

	dbf	d1,xpkHUFF_ComposeHuffmanCodeLoop

;write the last byte (this could contain 1 to 7 bits) if not already written

	cmp.w	#7,d3					;last byte already
							;written?
	beq.s	xpkHUFF_CHCL_DontWriteLastByte		;yep

	addq.w	#1,d3					;was dbf counter
	lsl.b	d3,d2					;shift bits to left
							;edge of byte
	move.b	d2,(a4)+				;write last byte
							;to buffer

xpkHUFF_CHCL_DontWriteLastByte:


;____________________________


	lea	xpkHUFF_HuffmanNodeStruct_SIZEOF(a0),a0	;proceed to next leaf

	tst.b	xpkHUFF_Type(a0)			;equ cmp.b #UNUSED,...
	bne.s	xpkHUFF_CreateTranslationTableSizesAndCodesLoop







;**************************************
;*** identifier & password handling ***
;**************************************

	cmp.l	#6,xpkHUFF_CRS_OutBufLen(a5)		;2: indicator word,
							;4: password checksum
	bls	xpkHUFF_C_Expansion


;*****************************
;*** write identifier word ***
;*****************************



	move.l	xpkHUFF_CRS_OutBuf(a5),a1
	move.w	#CMI_NORMAL,(a1)+			;word indicating
							;crunchmethod used

;____________________________


;*************************
;*** password handling ***
;*************************


	move.l	xpkHUFF_DRS_xpkSubParams(a5),a0

xpkHUFF_C_CalcPasswordCheckSum:

	move.l	#$ABADCAFE,d0

	move.l	xsp_Password(a0),d2
	beq.s	xpkHUFF_C_CPCS_NoPassword
	
	move.w	#$C0DE,d1
	move.l	d2,a2

xpkHUFF_C_CalcPasswordChecksumLoop1:

	move.b	(a2)+,d1
	beq.s	xpkHUFF_C_CalcPasswordChecksumLoop1End

	add.w	d1,d0
	bra.s	xpkHUFF_C_CalcPasswordChecksumLoop1

xpkHUFF_C_CalcPasswordChecksumLoop1End:

	move.l	xsp_Password(a0),a2
	swap	d0

xpkHUFF_C_CalcPasswordChecksumLoop2:

	move.b	(a2)+,d1
	beq.s	xpkHUFF_C_CalcPasswordChecksumLoop2End

	eor.w	d1,d0
	rol.w	#8,d0
	bra.s	xpkHUFF_C_CalcPasswordChecksumLoop2

xpkHUFF_C_CalcPasswordChecksumLoop2End:

xpkHUFF_C_CPCS_NoPassword:

	move.l	d0,(a1)+


;____________________________


;*******************************************************
;*** write translation sizes & codes to outputbuffer ***
;*******************************************************


;Format: size.b
;If  size  <>  0 (or, in this representation -1 (because of size beeing a dbf
;counter))  there  is  the  full  code  following,  byte aligned, thus saving
;diskspace.   If  size  = 0 (ie -1 (dbf, see above)) no code will follow, coz
;size = 0 indicates that this code won't be used.

xpkHUFF_WriteTranslationSizesAndCodesToOuputBuffer:


	lea	xpkHUFF_CRS_PtrTableToSizesAndCodes(a5),a0

	move.l	xpkHUFF_CRS_OutBuf(a5),a4
	add.l	xpkHUFF_CRS_InLen(a5),a4		;a4: *to check for
							;expansion
;	add.l	#XPKMARGIN,a4				;a cruncher that
							;expands data makes no
							;sense

	move.w	#255,d0					;dbf counter over
							;all table entries
	moveq	#-1,d3

xpkHUFF_WriteTranslationSizesAndCodesToOuputBufferLoop:

	move.l	(a0)+,d2				;d2: *size
	beq.s	xpkHUFF_WTSACTOB_DontWriteCodeJustMinusOne

	move.l	d2,a2

	moveq	#0,d1
	move.b	(a2)+,d1				;a2: *code

	move.b	d1,(a1)+

	cmp.l	a4,a1					;check for expansion
	bhs	xpkHUFF_C_Expansion			;Uh, Oh, expansion



xpkHUFF_WTSACTOB_WriteCode:

	lsr.b	#3,d1					;8 bits = 1 byte
							;(worx! :-)

xpkHUFF_WTSACTOB_WriteCodeLoop:

	move.b	(a2)+,(a1)+

	cmp.l	a4,a1					;check for expansion
	bhs	xpkHUFF_C_Expansion			;Uh, Oh, expansion
	
	dbf	d1,xpkHUFF_WTSACTOB_WriteCodeLoop

	dbf	d0,xpkHUFF_WriteTranslationSizesAndCodesToOuputBufferLoop

	bra.s	xpkHUFF_WTSACTOB_Done

xpkHUFF_WTSACTOB_DontWriteCodeJustMinusOne:

	move.b	d3,(a1)+				;-1 (= dbf 0)

	cmp.l	a4,a1					;check for expansion
	bhs	xpkHUFF_C_Expansion			;Uh, Oh, expansion

	dbf	d0,xpkHUFF_WriteTranslationSizesAndCodesToOuputBufferLoop


xpkHUFF_WTSACTOB_Done:


;********************************************
;*** write crunched data to output buffer ***
;********************************************



xpkHUFF_WriteCrunchedDataToOutputBuffer:

xpkHUFF_WCDTOB_Init:

;here a1 contains *OutBuf, right behind the table

	move.l	xpkHUFF_CRS_InBuf(a5),a0
	move.l	xpkHUFF_CRS_InLen(a5),d0
	subq.l	#1,d0			;dbf

	lea	xpkHUFF_CRS_PtrTableToSizesAndCodes(a5),a2

	moveq	#7,d4					;# of bits not yet
							;used in dest
							;representation (dbf)

	move.l	xpkHUFF_CRS_OutBuf(a5),a4
	add.l	xpkHUFF_CRS_InLen(a5),a4
;	add.l	#XPKMARGIN,a4




xpkHUFF_WCDTOB_OuterLoop:


;============================

;*******************************************************************************
;*** This part will write one byte in it's crunched representation to OutBuf ***
;*******************************************************************************

	moveq	#0,d1
	move.b	(a0)+,d1				;byte to crunch
	add.w	d1,d1					;make long
	add.w	d1,d1					;offset

	move.l	0(a2,d1.w),a3				;*size of bytes'
							;huffman
							;representation
	moveq	#0,d2
	move.b	(a3)+,d2				;number of bits in
							;byte representation
							;(dbf counter)

;register usage here:

;	d0: MUST NOT BE TOUCHED
;	d1: (current byte to be coded)*4
;	d2: #of bits in huffamn representation for byte in d1
;	d4: #of bits not yet used in dest byte (dbf)
;	a0: MUST NOT BE TOUCHED
;	a1: MUST NOT BE TOUCHED
;	a3: *code of byte in d1:8



	moveq	#0,d3					;force read of first
							;byte below


xpkHUFF_WCDTOB_WriteHuffmanRepresentationOfOneByteLoop:

;register usage here:

;	d3:   #of not yet used bits in source representation (dbf)
;	d5:8: contains (partial) huffman code
;	d6:   compose register



	dbf	d3,xpkHUFF_WCDTOB_DontGetNewSourceRepresentationByte

	moveq	#7,d3
	move.b	(a3)+,d5

xpkHUFF_WCDTOB_DontGetNewSourceRepresentationByte:

	addx.b	d5,d5
	addx.b	d6,d6

	dbf	d4,xpkHUFF_WCDTOB_DontWriteDestByte

	moveq	#7,d4

	move.b	d6,(a1)+

	cmp.l	a4,a1					;check for expansion
	bhs.s	xpkHUFF_C_Expansion			;Uh, Oh, expansion

xpkHUFF_WCDTOB_DontWriteDestByte:


	dbf	d2,xpkHUFF_WCDTOB_WriteHuffmanRepresentationOfOneByteLoop

;============================

	dbf	d0,xpkHUFF_WCDTOB_OuterLoop

	cmp.b	#7,d4
	beq.s	xpkHUFF_DontWriteLastPartialByte

	cmp.l	a4,a1
	bhs.s	xpkHUFF_C_Expansion

	addq.l	#1,d4					;former dbf counter
	lsl.b	d4,d6
	move.b	d6,(a1)+

xpkHUFF_DontWriteLastPartialByte:

	move.l	a1,d1					;end of crunched data
	sub.l	xpkHUFF_CRS_OutBuf(a5),d1

	move.l	d1,xpkHUFF_CRS_OutLen(a5)

	move.l	d1,xpkHUFF_CRS_CrunchedLength(a5)
	move.l	xpkHUFF_CRS_xpkSubParams(a5),a0
	move.l	d1,xsp_OutLen(a0)

;__________________________________________________________

xpkHUFF_Encrypt:

	move.l	xpkHUFF_CRS_xpkSubParams(a5),a0
	move.l	xsp_Password(a0),d3
	beq.s	xpkHUFF_DontEncrypt
	move.l	d3,a3

	move.l	xsp_OutBuf(a0),a1
	addq.l	#6,a1			;skip indicator word
					;& password checksum
	move.l	xsp_OutLen(a0),d0
	move.b	(a3),d3			;get encryptor for first byte (for the
					;other bytes it's always the
					;predecessor)
	subq.w	#7,d0			;dbf, skip indicator word, password
					;checksum

;register usage:
;
;d0:	Length of buffer to work on (dbf)
;d1:	current byte to encrypt
;d2:	byte out of passkey for encryption
;d3:	last encrypted byte for more encrytion on next byte
;a1:	*Buffer
;a3:	*password

xpkHUFF_EncryptLoop:

	move.b	(a1),d1				;byte that is to be coded
	move.b	(a3)+,d2			;byte of password
	bne.s	xpkHUFF_EncryptLoopDontResetPasskeyPtr

	move.l	xsp_Password(a0),a3		;reset *password
	move.b	(a3)+,d2			;get first byte of passkey

xpkHUFF_EncryptLoopDontResetPasskeyPtr:
	
	eor.b	d2,d1				;password byte as encryptor
	add.b	d3,d1				;last encrypted byte as
						;encryptor
	move.b	d1,(a1)+			;write current encrypted byte
	move.b	d1,d3				;last byte is encryptor for
						;next byte

	dbf	d0,xpkHUFF_EncryptLoop

xpkHUFF_DontEncrypt:

;__________________________________________________________



xpkHUFF_FreeReentry:

	move.l	xpkHUFF_CRS_xpkSubParams(a5),-(a7)

	move.l	#xpkHUFF_CRS_SIZEOF,d0
	move.l	a5,a1
	move.l	4.w,a6
	jsr	FreeMem(a6)
	sub.l	a5,a5

	move.l	(a7)+,a0

	moveq	#XPKERR_OK,d0
	rts					;exit back to mapping code



xpkHUFF_AllocReentryBufferFailed:

	move.l	a2,a0				;subparams
	moveq	#XPKERR_NOMEM,d0
	rts					;don't crunch


xpkHUFF_C_Expansion:

	move.l	xpkHUFF_CRS_xpkSubParams(a5),-(a7)

	move.l	#xpkHUFF_CRS_SIZEOF,d0
	move.l	a5,a1
	move.l	4.w,a6
	jsr	FreeMem(a6)
	sub.l	a5,a5

	move.l	(a7)+,a0

	tst.l	xsp_Password(a0)
	beq.s	XPKHUFF_C_NormalExpansion

xpkHUFF_C_CantEncryptBecauseOfExpansion:

;if  file would expand I would have to copy data to OutBuf and encrypt there
;without  trying  to crunch.  But my buffer would be 6 bytes larger than the
;original  one,  for  I  add  an identifier and the password checksum at the
;beginning  of  buffer.  This means I would exceed my buffer, which is (with
;the current version of xpkmaster.library (V 2.1)) not allowed.  Therefore I
;return  this  error,  to  make  sure  user  notices  that  data WILL NOT BE
;ENCRYPTED.
;
;Should  only  occur  with short files (I have an indicator word, a password
;checksum and a table size of >256 bytes which I put to OutBuf, so indicator
;word,  password  checksum, tablesize and crunched data must be smaller than
;InLen) or files that have already been crunched.


	moveq	#XPKERR_SMALLBUF,d0		;indicates that outbuf was
						;too small
	rts

XPKHUFF_C_NormalExpansion:

	moveq	#XPKERR_EXPANSION,d0
	rts					;back to mapping code




;__________________________________________________________

XpksUnpackChunkMode_0:

;in:
;
;	a0 :	*XpkSubParams
;	a6 :	*xpkHUFF.library
;out:
;	d0 :	err
;	in XpkSubParams :	xsp_OutLen
;
;registers trashed:
;



ROOT	equ	 0
NODE	equ	 0
LEAF	equ	 $ff					;leftmost bit set in
							;word TypeAndChar
							;for bpl below


xpkHUFF_DecrunchTreeNodeStruct:
	rsreset

xpkHUFF_DTNS_TypeAndChar:		rs.b	0	;for fast decrunching
xpkHUFF_DTNS_Type:			rs.b	1	;ROOT|NODE|LEAF
xpkHUFF_DTNS_Char:			rs.b	1	;only for leafs

xpkHUFF_DTNS_LeftSubTree:		rs.l	1	;*left subtree
xpkHUFF_DTNS_RightSubTree:		rs.l	1	;*righ subtree

;xpkHUFF_DTNS_

xpkHUFF_DRS_TreeNode_SIZEOF:		rs.b	0


xpkHUFF_DecrunchReentryStructure:
		rsreset

xpkHUFF_DRS_xpkSubParams:		rs.l	1
xpkHUFF_DRS_xpkSubLibBase:		rs.l	1

xpkHUFF_DRS_CrunchMethodIdentifier:	rs.w	1	;to ensure I can distuinguish old crunches from new ones
xpkHUFF_DRS_PasswordChecksum:		rs.l	1	;space for checksum of password, if password was used, a constant (currently '$ABADCAFE') otherwise

xpkHUFF_DRS_CrunchedDataTreeBuffer:	rs.l	1	;*decrunch tree in crunched data buffer
xpkHUFF_DRS_CrunchedDataBuffer:		rs.l	1	;*crunched data
xpkHUFF_DRS_DecrunchedDataBuffer:	rs.l	1	;*buffer to decrunch to
xpkHUFF_DRS_CrunchedLength:		rs.l	1	;size of crunched data in bytes
xpkHUFF_DRS_DecrunchedLength:		rs.l	1	;size of decrunched data in bytes
xpkHUFF_DRS_RootNode:			rs.l	1	;*root node of decrunch tree
xpkHUFF_DRS_SpaceForDecrunchTree:	rs.b	(256+255)*xpkHUFF_DRS_TreeNode_SIZEOF
xpkHUFF_DRS_DecryptBuffer:		rs.b	MaxPackChunkSize

xpkHUFF_DRS_SIZEOF:			rs.b	0

	move.l	a0,a2					;keep xpksubparams
							;in mind
	move.l	a6,a3					;keep xpkHUFF base
							;in mind
	move.l	#xpkHUFF_DRS_SIZEOF,d0
	move.l	#MEMF_CLEAR|MEMF_PUBLIC,d1
	move.l	4.w,a6
	jsr	AllocMem(a6)
	tst.l	d0
	beq	xpkHUFF_AllocDecrunchReentryBufferFailed

	move.l	d0,a5					;reentry buffer
							;now in a5
	move.l	a3,xpkHUFF_DRS_xpkSubLibBase(a5)	;store xpkHUFF base in
							;the reentry structure
	move.l	a2,xpkHUFF_DRS_xpkSubParams(a5)		;store subparams in
							;the reentry structure





;==========================================================

;******************************
;*** fill reentry structure ***
;******************************

	move.l	xsp_InBuf(a2),a0

	move.l	xsp_InLen(a2),xpkHUFF_DRS_CrunchedLength(a5)
	subq.l	#6,xpkHUFF_DRS_CrunchedLength(a5)		;indicator
								;word,
								;password
								;checksum

	move.l	xsp_OutBuf(a2),xpkHUFF_DRS_DecrunchedDataBuffer(a5)
	move.l	xsp_OutLen(a2),xpkHUFF_DRS_DecrunchedLength(a5)



	move.w	(a0)+,xpkHUFF_DRS_CrunchMethodIdentifier(a5)
	move.l	(a0)+,xpkHUFF_DRS_PasswordChecksum(a5)

	cmp.w	#CMI_NORMAL,xpkHUFF_DRS_CrunchMethodIdentifier(a5)
	bne	xpkHUFF_UnknownCrunchMethod			;I cant't
								;handle this

	tst.l	xsp_Password(a2)
	beq.s	xpkHUFF_D_InBufNotEncrypted


;__________________________________________________________

;***************************
;*** decryption handling ***
;***************************

xpkHUFF_D_CalcPasswordCheckSum:

	move.l	xsp_Password(a2),a3

	move.l	#$ABADCAFE,d0
	move.w	#$C0DE,d1

xpkHUFF_D_CalcPasswordChecksumLoop1:

	move.b	(a3)+,d1
	beq.s	xpkHUFF_D_CalcPasswordChecksumLoop1End

	add.w	d1,d0
	bra.s	xpkHUFF_D_CalcPasswordChecksumLoop1

xpkHUFF_D_CalcPasswordChecksumLoop1End:

	move.l	xsp_Password(a2),a3
	swap	d0

xpkHUFF_D_CalcPasswordChecksumLoop2:

	move.b	(a3)+,d1
	beq.s	xpkHUFF_D_CalcPasswordChecksumLoop2End

	eor.w	d1,d0
	rol.w	#8,d0
	bra.s	xpkHUFF_D_CalcPasswordChecksumLoop2

xpkHUFF_D_CalcPasswordChecksumLoop2End:

	cmp.l	xpkHUFF_DRS_PasswordChecksum(a5),d0
	beq.s	xpkHUFF_D_PasswordProbablyOK

	bsr	xpkHUFF_FreeMem

	moveq	#XPKERR_WRONGPW,d0	;wrong password used to decrunch
	rts

xpkHUFF_D_PasswordProbablyOK:

;____________________________


;***************
;*** decrypt ***
;***************


	move.l	xpkHUFF_DRS_CrunchedLength(a5),d0
	subq.w	#1,d0			;dbf

	move.l	a0,a1			;*InBuf
	move.l	a2,a0			;*xpk sub params
	lea	xpkHUFF_DRS_DecryptBuffer(a5),a2

	move.l	xsp_Password(a0),a3
	move.b	(a3),d3			;get decryptor for first byte (for the
					;other bytes it's always the
					;predecessor)
	

;register usage:
;
;d0:	Length of buffer to work on
;d1:	current byte to encrypt
;d2:	byte out of passkey for encryption
;d3:	last encrypted byte for more encrytion on next byte
;a0:	*xpk sub params
;a1:	*InBuf
;a2:	*DecryptBuffer
;a3:	*password

xpkHUFF_DecryptLoop:

	move.b	(a1)+,d1		;byte that is to be decoded
	move.b	d1,d4
	move.b	(a3)+,d2		;byte of password
	bne.s	xpkHUFF_DontResetPassWordPointer

	move.l	xsp_Password(a0),a3	;reset *password
	move.b	(a3)+,d2		;get first byte of password

xpkHUFF_DontResetPassWordPointer:
	
	sub.b	d3,d1			;last encrypted byte as decryptor
	eor.b	d2,d1			;password byte as decryptor
	move.b	d1,(a2)+		;write current decrypted byte
	move.b	d4,d3			;last encrypted byte is decryptor for
					;next byte

	dbf	d0,xpkHUFF_DecryptLoop


	lea	xpkHUFF_DRS_DecryptBuffer(a5),a0
;	bra.s	xpkHUFF_D_OKToDecrunch



;__________________________________________________________



xpkHUFF_D_InBufNotEncrypted:
xpkHUFF_D_OKToDecrunch:

	move.l	a0,xpkHUFF_DRS_CrunchedDataTreeBuffer(a5)



;****************************
;*** create decrunch tree ***
;****************************

xpkHUFF_D_CreateDecrunchTree:

	move.l	xpkHUFF_DRS_CrunchedDataTreeBuffer(a5),a0
	lea	xpkHUFF_DRS_SpaceForDecrunchTree(a5),a1
	move.l	a1,xpkHUFF_DRS_RootNode(a5)
	move.l	a1,a3

	move.b	#ROOT,xpkHUFF_DTNS_Type(a1)
	lea	xpkHUFF_DRS_TreeNode_SIZEOF(a1),a1	;proceed to next node


	move.l	#255,d0					;256 runs
	moveq	#-1,d1					;for fast cmp.b below
	moveq	#-1,d2					;initial byte




xpkHUFF_D_CreateDecrunchTreeLeafLoop:

	addq.b	#1,d2

	moveq	#0,d3
	move.b	(a0)+,d3				;d3:Length
	cmp.b	d3,d1					;length = -1 (ie 0)?
							;(255 can't occur!)
	beq.s	xpkHUFF_D_CDTL_DoneWithChar


	moveq	#0,d5					;force dbf below
							;to read next byte
	move.l	a3,a2					;*root node

xpkHUFF_D_CreateDecrunchTreeNodeLoop:

	dbf	d5,xpkHUFF_D_CDTN_DontGetNextCodeByte

	move.b	(a0)+,d4
	moveq	#7,d5
	
xpkHUFF_D_CDTN_DontGetNextCodeByte:

	add.b	d4,d4					;leftmost bit to carry
	bcs.s	xpkHUFF_D_CDTN_RightSubTree
	

;____________________________


xpkHUFF_D_CDTN_LeftSubTree:

	move.l	xpkHUFF_DTNS_LeftSubTree(a2),d6
	beq.s	xpkHUFF_D_CDTN_LeftSubTree_CreateNewNode

	move.l	d6,a2

	dbf	d3,xpkHUFF_D_CreateDecrunchTreeNodeLoop
	bra.s	xpkHUFF_D_CDTN_WriteCharDataToLeaf


xpkHUFF_D_CDTN_LeftSubTree_CreateNewNode:

	move.l	a1,xpkHUFF_DTNS_LeftSubTree(a2)
	move.l	a1,a2
	lea	xpkHUFF_DRS_TreeNode_SIZEOF(a1),a1

	dbf	d3,xpkHUFF_D_CreateDecrunchTreeNodeLoop
	bra.s	xpkHUFF_D_CDTN_WriteCharDataToLeaf

;____________________________

xpkHUFF_D_CDTN_RightSubTree:

	move.l	xpkHUFF_DTNS_RightSubTree(a2),d6
	beq.s	xpkHUFF_D_CDTN_RightSubTree_CreateNewNode

	move.l	d6,a2

	dbf	d3,xpkHUFF_D_CreateDecrunchTreeNodeLoop
	bra.s	xpkHUFF_D_CDTN_WriteCharDataToLeaf


xpkHUFF_D_CDTN_RightSubTree_CreateNewNode:

	move.l	a1,xpkHUFF_DTNS_RightSubTree(a2)
	move.l	a1,a2
	lea	xpkHUFF_DRS_TreeNode_SIZEOF(a1),a1

	dbf	d3,xpkHUFF_D_CreateDecrunchTreeNodeLoop
;	bra.s	xpkHUFF_D_CDTN_WriteCharDataToLeaf
;____________________________



xpkHUFF_D_CDTN_WriteCharDataToLeaf:

	move.b	d2,xpkHUFF_DTNS_Char(a2)
	move.b	#LEAF,xpkHUFF_DTNS_Type(a2)


xpkHUFF_D_CDTL_DoneWithChar:

	dbf	d0,xpkHUFF_D_CreateDecrunchTreeLeafLoop

	move.l	a0,xpkHUFF_DRS_CrunchedDataBuffer(a5)


;*********************
;*** decrunch data ***
;*********************


DecrunchData:

;__________________________________________________________


	IFEQ	DecrunchCode


	move.l	xpkHUFF_DRS_DecrunchedLength(a5),d0
	subq.l	#1,d0

	move.l	xpkHUFF_DRS_CrunchedDataBuffer(a5),a0
	move.l	xpkHUFF_DRS_DecrunchedDataBuffer(a5),a1

	moveq	#0,d1			;force read of first byte below
					;register
	move.l	xpkHUFF_DRS_RootNode(a5),a3


xpkHUFF_DD_ByteLoop:

	move.l	a3,a2			;root node

xpkHUFF_DD_InnerLoop:

	dbf	d1,xpkHUFF_DD_IL_DontGetNewSourceByte

	move.b	(a0)+,d2
	moveq	#7,d1

xpkHUFF_DD_IL_DontGetNewSourceByte:

	add.b	d2,d2
	bcs.s	xpkHUFF_DD_RightSubTree

xpkHUFF_DD_LeftSubTree:

	move.l	xpkHUFF_DTNS_LeftSubTree(a2),a2

	move.w	xpkHUFF_DTNS_TypeAndChar(a2),d3
	bpl.s	xpkHUFF_DD_InnerLoop
	bra.s	xpkHUFF_DD_WriteChar


xpkHUFF_DD_RightSubTree:

	move.l	xpkHUFF_DTNS_RightSubTree(a2),a2

	move.w	xpkHUFF_DTNS_TypeAndChar(a2),d3
	bpl.s	xpkHUFF_DD_InnerLoop
;	bra.s	xpkHUFF_DD_WriteChar



xpkHUFF_DD_WriteChar:

	move.b	d3,(a1)+

	dbf	d0,xpkHUFF_DD_ByteLoop

	bsr.s	xpkHUFF_FreeMem

	moveq	#XPKERR_OK,d0

	rts				;back to mapping code

xpkHUFF_AllocDecrunchReentryBufferFailed:

	moveq	#XPKERR_NOMEM,d0
	rts


	ENDC		;of IFEQ DecrunchCode


;__________________________________________________________


	IFEQ	((DecrunchCode-1)*(DecrunchCode-2)*(DecrunchCode-3)*(DecrunchCode-4))


;*************************************************
;*** calculate crunched length (without table) ***
;*************************************************

	move.l	xpkHUFF_DRS_CrunchedDataTreeBuffer(a5),d0
	add.l	xpkHUFF_DRS_CrunchedLength(a5),d0	;equ *byte behind
							;crunched data
	sub.l	xpkHUFF_DRS_CrunchedDataBuffer(a5),d0	;equ size of crunched
							;data without tree
							;information


	IFEQ	((DecrunchCode-1)*(DecrunchCode-2)*(DecrunchCode-4))


;******************************************************************************
;*** here the size of the crunched data excluding tree information is in d0 ***
;******************************************************************************

	subq.l	#1,d0					;-1: last byte might
							;not be used
							;completely


	IFEQ	DecrunchCode-2
	lsr.l	#1,d0					;we are processing a
	ENDC						;word below


	IFEQ	DecrunchCode-4
	lsr.l	#2,d0					;we are processing a
	ENDC						;long below


	subq.l	#1,d0					;-1: dbf

	move.l	xpkHUFF_DRS_CrunchedDataBuffer(a5),a0
	move.l	xpkHUFF_DRS_DecrunchedDataBuffer(a5),a1

	move.l	xpkHUFF_DRS_RootNode(a5),a3
	move.l	a3,a2					;root node



DD_ProcessOneCrunchedByteWordLongLoop:

	IFEQ	DecrunchCode-1
	move.b	(a0)+,d2
	ENDC

	IFEQ	DecrunchCode-2
	move.w	(a0)+,d2
	ENDC

	IFEQ	DecrunchCode-4
	move.l	(a0)+,d2
	ENDC


ProcessOneBit	MACRO

	IFEQ	DecrunchCode-1
	add.b	d2,d2				;leftmost bit to carry
	ENDC

	IFEQ	DecrunchCode-2
	add.w	d2,d2				;leftmost bit to carry
	ENDC

	IFEQ	DecrunchCode-4
	add.l	d2,d2				;leftmost bit to carry
	ENDC



	bcc.s	DD_LeftSubTree\@

DD_RightSubTree\@:

	move.l	xpkHUFF_DTNS_RightSubTree(a2),a2

	move.w	xpkHUFF_DTNS_TypeAndChar(a2),d3
	bpl.s	DD_ProceedWithNextBit\@

	move.b	d3,(a1)+			;write char
	move.l	a3,a2				;start again at root node

	bra.s	DD_CharWritten\@

DD_LeftSubTree\@:

	move.l	xpkHUFF_DTNS_LeftSubTree(a2),a2

	move.w	xpkHUFF_DTNS_TypeAndChar(a2),d3
	bpl.s	DD_ProceedWithNextBit\@

	move.b	d3,(a1)+			;write char
	move.l	a3,a2				;start again at root node

;	bra.s	DD_CharWritten\@

DD_CharWritten\@:
DD_ProceedWithNextBit\@:


	ENDM

	IFEQ	DecrunchCode-1

	ProcessOneBit		;1
	ProcessOneBit		;2
	ProcessOneBit		;3
	ProcessOneBit		;4
	ProcessOneBit		;5
	ProcessOneBit		;6
	ProcessOneBit		;7
	ProcessOneBit		;8

	ENDC

	IFEQ	DecrunchCode-2			;I know I could use a smarter
						;condition here, but I'm lazy
						;and it doesn't affect
						;executable speed

	ProcessOneBit		;1
	ProcessOneBit		;2
	ProcessOneBit		;3
	ProcessOneBit		;4
	ProcessOneBit		;5
	ProcessOneBit		;6
	ProcessOneBit		;7
	ProcessOneBit		;8
	ProcessOneBit		;9
	ProcessOneBit		;10
	ProcessOneBit		;11
	ProcessOneBit		;12
	ProcessOneBit		;13
	ProcessOneBit		;14
	ProcessOneBit		;15
	ProcessOneBit		;16

	ENDC

	IFEQ	DecrunchCode-4

	ProcessOneBit		;1
	ProcessOneBit		;2
	ProcessOneBit		;3
	ProcessOneBit		;4
	ProcessOneBit		;5
	ProcessOneBit		;6
	ProcessOneBit		;7
	ProcessOneBit		;8
	ProcessOneBit		;9
	ProcessOneBit		;10
	ProcessOneBit		;11
	ProcessOneBit		;12
	ProcessOneBit		;13
	ProcessOneBit		;14
	ProcessOneBit		;15
	ProcessOneBit		;16
	ProcessOneBit		;17
	ProcessOneBit		;18
	ProcessOneBit		;19
	ProcessOneBit		;20
	ProcessOneBit		;21
	ProcessOneBit		;22
	ProcessOneBit		;23
	ProcessOneBit		;24
	ProcessOneBit		;25
	ProcessOneBit		;26
	ProcessOneBit		;27
	ProcessOneBit		;28
	ProcessOneBit		;29
	ProcessOneBit		;30
	ProcessOneBit		;31
	ProcessOneBit		;32

	ENDC



	dbf	d0,DD_ProcessOneCrunchedByteWordLongLoop


	ENDC	;of IFEQ ((DecrunchCode-1)*(DecrunchCode-2)*(DecrunchCode-4))

;__________________________________________________________


	IFEQ	DecrunchCode-3		;68030+ cache oriented decrunch code


;******************************************************************************
;*** here the size of the crunched data excluding tree information is in d0 ***
;******************************************************************************

	subq.l	#1,d0				;-1: last byte might not be used completely


	lsr.l	#2,d0				;we are processing a whole long below


	subq.l	#1,d0				;-1: dbf


	move.l	xpkHUFF_DRS_CrunchedDataBuffer(a5),a0
	move.l	xpkHUFF_DRS_DecrunchedDataBuffer(a5),a1

	move.l	xpkHUFF_DRS_RootNode(a5),a3
	move.l	a3,a2				;root node



DD_ProcessOneCrunchedByteLoop:

	move.l	(a0)+,d2

	bsr.s	DD_ProcessFourBitSubRoutine	; 1- 4
	bsr.s	DD_ProcessFourBitSubRoutine	; 5- 8
	bsr.s	DD_ProcessFourBitSubRoutine	; 9-12
	bsr.s	DD_ProcessFourBitSubRoutine	;13-16
	bsr.s	DD_ProcessFourBitSubRoutine	;17-20
	bsr.s	DD_ProcessFourBitSubRoutine	;21-24
	bsr.s	DD_ProcessFourBitSubRoutine	;25-28
	bsr.s	DD_ProcessFourBitSubRoutine	;29-32

	dbf	d0,DD_ProcessOneCrunchedByteLoop

	bra	DD_AlmostDone




DD_ProcessFourBitSubRoutine:

;____________________________

ProcessOneBitCodeMACRO	MACRO

	add.l	d2,d2				;leftmost bit to carry

	bcc.s	DD_LeftSubTree\@

DD_RightSubTree\@:

	move.l	xpkHUFF_DTNS_RightSubTree(a2),a2

	move.w	xpkHUFF_DTNS_TypeAndChar(a2),d3
	bpl.s	DD_ProceedWithNextBit\@

	move.b	d3,(a1)+			;write char
	move.l	a3,a2				;start again at root node

	IFEQ	\1-0
	bra.s	DD_CharWritten\@
	ENDC

	IFEQ	\1-1
	rts
	ENDC


DD_LeftSubTree\@:

	move.l	xpkHUFF_DTNS_LeftSubTree(a2),a2

	move.w	xpkHUFF_DTNS_TypeAndChar(a2),d3
	bpl.s	DD_ProceedWithNextBit\@

	move.b	d3,(a1)+			;write char
	move.l	a3,a2				;start again at root node

;	bra.s	DD_CharWritten\@

DD_CharWritten\@:
DD_ProceedWithNextBit\@:

	IFEQ	\1-1
	rts
	ENDC

	ENDM

;____________________________


	ProcessOneBitCodeMACRO	0	;1
	ProcessOneBitCodeMACRO	0	;2
	ProcessOneBitCodeMACRO	0	;3
	ProcessOneBitCodeMACRO	1	;4	;last MACRO will have rts
						;instead of bra


DD_AlmostDone:


	ENDC ;of IFEQ DecrunchCode-3 ;68030+ cache oriented decrunch code

;__________________________________________________________



ProcessLastByteOrBytesOfCrunchedData:

	move.l	a1,d1
	sub.l	xpkHUFF_DRS_DecrunchedDataBuffer(a5),d1

;= number of bytes already decrunched to output buffer

	move.l	xpkHUFF_DRS_DecrunchedLength(a5),d0
	sub.l	d1,d0

;= number of bytes still to decrunch (ranges from 1..31, depending on decrunch code, too)

	beq.s	DD_Done

	subq.l	#1,d0					;dbf

	moveq	#0,d4					;force read of next byte below

	

;note: the loop above was over the crunched bytes, the loop below is over
;       uncrunched bytes, therefore we have a different code here

;____________________________




DD_Loop:


	dbf	d4,DD_DontGetNewByte

	move.b	(a0)+,d2
	moveq	#7,d4


DD_DontGetNewByte:

	add.b	d2,d2			;leftmost bit to carry
	bcc.s	DD_LeftSubTree


DD_RightSubTree:

	move.l	xpkHUFF_DTNS_RightSubTree(a2),a2

	move.w	xpkHUFF_DTNS_TypeAndChar(a2),d3
	bpl.s	DD_Loop
	bra.s	DD_WriteChar


DD_LeftSubTree:

	move.l	xpkHUFF_DTNS_LeftSubTree(a2),a2

	move.w	xpkHUFF_DTNS_TypeAndChar(a2),d3
	bpl.s	DD_Loop
;	bra.s	DD_WriteChar


DD_WriteChar:

	move.b	d3,(a1)+				;write decrunched byte
	move.l	a3,a2					;start again at root
							;node
	dbf	d0,DD_Loop


;____________________________


DD_Done:

	move.l	xpkHUFF_CRS_xpkSubParams(a5),a0

	bsr.s	xpkHUFF_FreeMem

	moveq	#XPKERR_OK,d0

	rts						;back to mapping code

xpkHUFF_AllocDecrunchReentryBufferFailed:

	move.l	a2,a0					;subparams

	moveq	#XPKERR_NOMEM,d0
	rts


	ENDC	;of IFEQ ((DecrunchCode-1)*(DecrunchCode-2)*(DecrunchCode-3)*(DecrunchCode-4))



xpkHUFF_UnknownCrunchMethod:

	move.l	xpkHUFF_DRS_xpkSubParams(a5),a0
	bsr.s	xpkHUFF_FreeMem
	moveq	#XPKERR_OLDSUBLIB,d0
	rts


xpkHUFF_FreeMem:

	movem.l	d0/d1/a0/a1,-(a7)

	move.l	#xpkHUFF_DRS_SIZEOF,d0
	move.l	a5,a1
	move.l	4.w,a6
	jsr	FreeMem(a6)

	movem.l	(a7)+,d0/d1/a0/a1

	rts



;==========================================================


	cnop	0,4

EndCode:

