 ***************************************************************************
 *                                                                         *
 *                           DBCruncher V1.41                              *
 *                                                                         *
 *                                  by                                     *
 *                                                                         *
 *                            Mauro Panigada                               *
 *                                                                         *
 ***************************************************************************
 
 ; VERSIONE 1.41:
 ;    - Finito l' 11 Agosto 1994.
 ;    - Static Huffman Tree.
 ;    - !(NON ANCORA DB)! (DB sta per Dynamic Bushman...)
 ;    - 12 Agosto 1994: migliorato parser linea comandi.
 ;    - 12 Agosto 1994: aggiunto una specie di CRC.
 ;    -  7 Giugno 1995: corretti alcuni bugs (proprio tutti?)
 ;    -  8 Giugno 1995: aggiunto comando -r (recrunch)
 ;
 ; $VER: DBCruncher v1.41
 ;


VERSION		EQU	2		; V1.41
VBIT		EQU	3


		incdir	"dh0:progr/assem/include/"
		include	"exec/types.i"
		include	"exec/libraries.i"
		include	"exec/memory.i"
		include	"exec/exec_lib.i"
		include	"dos/dos.i"
		include	"dos/dosextens.i"
		include	"dos/dos_lib.i"


CALL		MACRO
		jsr	_LVO\1(a6)
		ENDM

VAR0		MACRO
VarLength	SET	0
		ENDM

VARW		MACRO
\1		EQU	VarLength
VarLength	SET	VarLength+2
		ENDM

VARL		MACRO
\1		EQU	VarLength
VarLength	SET	VarLength+4
		ENDM

NAME		MACRO
\1		EQU	VarLength
		ENDM

VARMB		MACRO
\1		EQU	VarLength
VarLength	SET	VarLength+\2
		ENDM

VARMW		MACRO
		VARMB	\1,(\2*2)
		ENDM

VARML		MACRO
		VARMB	\1,(\2*4)
		ENDM


SpecChar	EQU	0
NumChar		EQU	256+SpecChar		; special char (reserved)
FileLen		EQU	4
HeadLen		EQU	4
TotalWHead	EQU	FileLen+HeadLen

LIMIT		EQU	0		; 0=limiti non attivi.
MAXBIT		EQU	15
REDUCTION	EQU	3

_LVOPPLoadData	EQU	-30


*========================================================== OFFSETS =======

		VAR0
		VARL	_ArgPtr
		VARL	_DestName

		VARL	_DosBase
		VARL	_PPBase
		VARL	_StackBase		; per sicurezza
		VARL	_Output

		VARL	_DestHand
		VARL	_SourceHand

		VARL	_BFileLength
		VARL	_AFileLength
		VARL	_CrunchedLen
		VARL	_ArgLength

		VARL	_DReadMem
		VARL	_ReadMem
		VARL	_WriteMem
		VARL	_DWriteMem

		NAME	_MomVar		;\
		VARW	_MomAVar	; |
		VARW	_MomBVar	; | Variabili momentanee.
					; | Forse non serviranno tutte!
		NAME	_MomVar2	; |
		VARW	_MomCVar	; |
		VARW	_MomDVar	;/

		VARW	_NumChar
		VARW	_UsedChar
		VARW	_DUsedChar
		VARW	_Root

		VARW	_BitData		; per get- putbits
		VARW	_Counter

		VARW	_FileCRC

		IFNE	LIMIT
		VARW	_Reduction		; fattore di riduzione
		VARW	_AddValue
		VARW	_ReducFlag
		ENDC

		NAME	_Left
		VARMW	_Heap,NumChar*2		; array WORD HEAP[512]
		VARMW	_Pred,NumChar*2		; array WORD PRED[512]
		NAME	_Right
		VARML	_Freq,NumChar*2		; array LONG FREQ[512]

		VARMW	_Heap0,NumChar		; buffer per copia e decr.
		VARML	_CodeData,NumChar	; due array per la genera-
		VARMW	_CodeLen,NumChar	; zione dei codici

		VARMB	_CharBuffer,128		; per eleaborazione CL(I)

		VARW	_OptR			; se attiva opt -r


*========================================================== CODICE ========

Start		movem.l	a0/d0,-(sp)
		movea.l	4.w,a6				; alloca mem per
		move.l	#VarLength,d0			; variabili (puro!)
		move.l	#MEMF_PUBLIC!MEMF_CLEAR,d1
		CALL	AllocMem
		tst.l	d0
		beq	QExit
		move.l	d0,a4

		movem.l	(sp)+,a0/d0		; salva argomenti
		move.l	sp,_StackBase(a4)
		move.l	d0,_ArgLength(a4)
		move.l	a0,_ArgPtr(a4)
		clr.b	-1(a0,d0)

		lea	DosName(pc),a1		; apre la dos
		moveq	#0,d0
		CALL	OpenLibrary
		move.l	d0,_DosBase(a4)
		beq	Exit

		move.l	d0,a6
		CALL	Output
		move.l	d0,_Output(a4)

		movea.l	4.w,a6
		lea	PPName(pc),a1
		moveq	#0,d0
		CALL	OpenLibrary
		move.l	d0,_PPBase(a4)
					; se non apre, non decomprime PP!

		cmp.l	#1,_ArgLength(a4)	; no argomenti?
		beq	Help

		move.l	_ArgPtr(a4),a0		; prende nome file desti-
SkipSpace	cmp.b	#" ",(a0)+		; nazione
		beq.s	SkipSpace
		subq.w	#1,a0

		cmp.b	#"-",(a0)		; c'e` una opzione!
		bne.s	ArgOK
		cmp.b	#"r",1(a0)
		bne	Help

		move.w	#$ffff,_OptR(a4)	; st
		addq.l	#3,a0
		subq.l	#3,_ArgLength(a4)
		move.l	a0,_ArgPtr(a4)

ArgOK		move.l	_ArgLength(a4),d0
		subq.w	#1,d0
SpaceLoop	cmp.b	#" ",(a0)+
		dbeq	d0,SpaceLoop
		tst.w	d0
		bmi.s	NoDestSpec
		clr.b	-1(a0)
SkipDSpc	cmp.b	#" ",(a0)+
		beq.s	SkipDSpc
		subq.w	#1,a0
		move.l	a0,_DestName(a4)
		bra.s	DestOK
NoDestSpec	clr.b	-1(a0)
		lea	_CharBuffer(a4),a1
		move.l	a1,a0
		move.l	_ArgPtr(a4),a2
CopyName	tst.b	(a2)
		beq.s	EndCopyName
		move.b	(a2)+,(a1)+
		bra.s	CopyName
EndCopyName	move.b	#".",(a1)+
		move.b	#"d",(a1)+
		move.b	#"b",(a1)+
		clr.b	(a1)
		lea	_CharBuffer(a4),a0
		move.l	a0,_DestName(a4)

DestOK		move.l	_DosBase(a4),a6		; apre destinazione
		move.l	_ArgPtr(a4),d1		; apre sorgente
		move.l	#MODE_OLDFILE,d2
		CALL	Open
		move.l	d0,_SourceHand(a4)
		beq	NoSource

		move.l	d0,d1			; calcola la lunghezza
		moveq	#0,d2
		moveq	#OFFSET_END,d3
		CALL	Seek
		move.l	_SourceHand(a4),d1
		moveq	#0,d2
		moveq	#OFFSET_BEGINNING,d3
		CALL	Seek
		move.l	d0,_AFileLength(a4)
		move.l	d0,_BFileLength(a4)
		beq	ReadingError

		lea	_MomVar(a4),a0
		move.l	a0,a5
		move.l	a0,d2
		moveq	#4,d3
		move.l	_SourceHand(a4),d1
		CALL	Read

		cmp.l	#"DBCR",(a5)
		bne.s	OpenDest

		lea	_CharBuffer(a4),a0
		tst.b	(a0)
		beq.s	OpenDest

SrchDot		cmp.b	#".",(a0)+
		bne.s	SrchDot
		move.b	#"d",(a0)+
		move.b	#"a",(a0)+
		move.b	#"t",(a0)+
		clr.b	(a0)

OpenDest	move.l	_DestName(a4),d1	; apre dest
		move.l	#MODE_NEWFILE,d2
		move.l	_DosBase(a4),a6
		CALL	Open
		move.l	d0,_DestHand(a4)
		beq	NoDest

		lea	Loading.msg(pc),a0
		bsr	_Write

		move.l	_ArgPtr(a4),a0
		move.l	_PPBase(a4),d0
		beq.s	NoPPLoad
		move.l	d0,a6
		lea	_DReadMem(a4),a1
		lea	_AFileLength(a4),a2
		moveq	#2,d0
		moveq	#MEMF_PUBLIC,d1
		CALL	PPLoadData
		tst.l	d0
		bne.s	NoPPLoad
		move.l	_AFileLength(a4),_BFileLength(a4)
		move.l	_DReadMem(a4),_ReadMem(a4)
		bra.s	LoadedSource

NoPPLoad	move.l	_AFileLength(a4),d0
		moveq	#MEMF_PUBLIC,d1		; alloca due buffers per
		movea.l	4.w,a6			; il nostro lavoro
		CALL	AllocMem
		move.l	d0,_ReadMem(a4)
		move.l	d0,_DReadMem(a4)
		beq	NoRMem

		move.l	_SourceHand(a4),d1	; si posiziona all'inizio
		moveq	#0,d2			; del file.
		moveq	#OFFSET_BEGINNING,d3
		move.l	_DosBase(a4),a6
		CALL	Seek

		move.l	_ReadMem(a4),d2
		move.l	_SourceHand(a4),d1
		move.l	_AFileLength(a4),d3
		CALL	Read
		cmp.l	_AFileLength(a4),d0
		bne	ReadingError

LoadedSource	moveq	#MEMF_PUBLIC,d1
		move.l	_BFileLength(a4),d0
		move.l	4.w,a6
		CALL	AllocMem
		move.l	d0,_WriteMem(a4)
		beq	NoWMem

		tst.w	_OptR(a4)
		bmi.s	ROptActive

		move.l	_ReadMem(a4),a3
		cmp.l	#"DBCR",(a3)
		beq	DecrunchDB		; mio file crunciato

ROptActive
		IFNE	LIMIT
		move.l	_AFileLength(a4),d1
		move.l	#(NumChar*4)*2,d0
		cmp.l	d0,d1
		bcs	TooShort
		ENDC

EndChecking
CrunchDB	lea	CrunchS.msg(pc),a0
		bsr	_Write

		lea	WriteHead.msg(pc),a0	; scrive testatina
		bsr	_Write

		move.l	_WriteMem(a4),a0
		move.l	a0,d2
		move.l	#"DBCR",(a0)+
		move.l	_AFileLength(a4),d0
		moveq	#VERSION,d1
		ror.l	#VBIT,d1
		or.l	d1,d0			; versione
		move.l	d0,(a0)+
		move.l	_DestHand(a4),d1
		moveq	#TotalWHead,d3
		move.l	_DosBase(a4),a6
		CALL	Write

RemakeTable	IFNE	LIMIT
		move.w	#REDUCTION,_Reduction(a4)
		move.w	#1,_AddValue(a4)
		ENDC

		lea	CalcFreq.msg(pc),a0	; calcola char.s freq.
		bsr	_Write
		bsr	_CalcFreq

CalcCRC		move.l	_AFileLength(a4),d0
		move.l	_ReadMem(a4),a0
		bsr	_CalcCRC
		move.w	d0,_MomVar(a4)
		lea	_MomVar(a4),a0
		move.l	a0,d2
		move.l	_DestHand(a4),d1
		moveq	#2,d3
		CALL	Write			; scrive CRC

		IFNE	LIMIT
		lea	_Freq(a4),a0
		move.l	_WriteMem(a4),a1
		move.l	_BFileLength(a4),d0
		movea.l	4.w,a6
		CALL	CopyMemQuick
		ENDC

Crunching	lea	Crunch.msg(pc),a0
		bsr	_Write

RemakeTree	move.w	#NumChar,_NumChar(a4)
		bsr	_CalcUsed
		move.w	d2,_UsedChar(a4)
		move.w	d2,_DUsedChar(a4)

		lea	_Heap(a4),a0
		lea	_Heap0(a4),a1
		move.w	#NumChar*2,d0		; in byte (numchar word)
		movea.l	4.w,a6
		CALL	CopyMemQuick

		move.l	_WriteMem(a4),_DWriteMem(a4)
		clr.w	_BitData(a4)
		move.w	#15,_Counter(a4)		; WORD-1

		lea	_Freq(a4),a0
		lea	_Pred(a4),a1
		move.w	#NumChar,d2
		lsl.w	#2,d2

		cmp.w	#1,_DUsedChar(a4)
		bne.s	MakeTree
MakeRoot	lea	_Heap(a4),a2
		move.w	(a2)+,d0
		move.w	d0,_Root(a4)
		lea	_Pred(a4),a2
		move.w	d0,d1
		lsl.w	#1,d1
		move.w	d0,(a2,d1)
		bra.s	EndMakeTree		; =root

MakeTree	cmp.w	#1,_DUsedChar(a4)	; root!
		beq.s	EndMakeTree
		bsr	_SearchMinor
		move.w	d2,d3
		lsr.w	#2,d3
		lsl.w	#1,d0
		lsl.w	#1,d1
		move.w	d3,(a1,d0)		; nodo precedente
		move.w	d3,(a1,d1)		; nodo precedente
		not.w	(a1,d1)			; -sulla destra!
		move.w	d3,_Root(a4)
		move.l	_MomVar2(a4),d7
		move.l	d7,(a0,d2)		; nuovo nodo in coda
		addq.l	#4,d2			; per il prossimo
		addq.w	#1,_NumChar(a4)		; per _CalcUsed
		bra.s	MakeTree

EndMakeTree	lea	_Heap0(a4),a0		; heap originale
		lea	_Pred(a4),a1
		lea	_CodeLen(a4),a2
		lea	_CodeData(a4),a3
		move.w	_UsedChar(a4),d7
		subq.w	#1,d7
CreateCLoop	moveq	#0,d2			; d2=len codice
		moveq	#0,d3			; d3=codice
		move.w	(a0)+,d1
		lsl.w	#1,d1
		move.w	d1,_MomAVar(a4)
BitGLoop	move.w	(a1,d1),d4
		tst.w	d4
		smi	_MomBVar(a4)
		bpl.s	SkipNotter
		not.w	d4
SkipNotter	addq.w	#1,d2
		move.w	d4,d1			; nodo precedente
		lsl.w	#1,d1
		move.w	_MomBVar(a4),d5
		lsl.w	#1,d5
		addx.l	d3,d3
		move.w	_Root(a4),d5
		cmp.w	d4,d5
		bne.s	BitGLoop

EndRoot		move.w	_MomAVar(a4),d1
		move.w	d2,(a2,d1)
		lsl.w	#1,d1
		move.l	d3,(a3,d1)		; _codedata a longword
	
		IFNE	LIMIT			; solo se LIMIT=1 (<>0)
		tst.b	_ReducFlag(a4)
		bne	ReMTableLoop
		cmp.w	#MAXBIT,d2
		bls.s	ReMTableLoop
		move.l	_BFileLength(a4),d0
		cmp.l	#(NumChar*4)*2,d0
		bcs.s	ReMTableLoop
		bsr	_GetMax
		move.l	_WriteMem(a4),a0
		move.w	#NumChar-1,d4
		move.l	d0,d3
		move.w	_Reduction(a4),d1
		lsr.l	d1,d3
		beq.s	ReMTableLoop
		move.w	_AddValue(a4),d2
		and.l	#$ffff,d2		; sicurezza
		cmp.w	d3,d2
		bcs.s	MaxAVLow
		sub.w	#1,d1
		move.w	d1,_Reduction(a4)
		move.w	#1,_AddValue(a4)
MaxAVLow	and.l	#$ffff,d1		; sicurezza
		bmi.s	Continue
ReducLoop	move.l	(a0)+,d0
		beq.s	Skip0Freq
		cmp.l	d0,d3
		bcs.s	Skip0Freq
		add.l	d2,-4(a0)
Skip0Freq	dbf	d4,ReducLoop
		add.w	#1,_AddValue(a4)
JumpRT		move.l	_WriteMem(a4),a0
		lea	_Freq(a4),a1
		move.w	#(NumChar*4)*2,d0
		movea.l	4.w,a6
		CALL	CopyMemQuick
		bra	RemakeTree
		ENDC				; fine codice opzionale

ReMTableLoop	dbf	d7,CreateCLoop

		IFNE	LIMIT
		bra.s	BitCrunch

Continue	st	_ReducFlag(a4)
		bra.s	JumpRT
		ENDC

BitCrunch	move.w	#NumChar-1,d2
		moveq	#0,d5
		lea	_CodeData(a4),a0
		lea	_CodeLen(a4),a1
TreeLoop	move.w	d5,d3
		lsl.w	#1,d3
		move.w	(a1,d3),d0
		move.w	d0,d4
		bne.s	WriteBitTrue
		moveq	#1,d0
		moveq	#1,d1
		bsr	_PutBits
		bra.s	NoUsedCode
WriteBitTrue	moveq	#0,d0
		moveq	#1,d1
		bsr	_PutBits
		move.w	d4,d0
		moveq	#5,d1
		bsr	_PutBits
		move.w	(a1,d3),d1
		lsl.w	#1,d3
		move.l	(a0,d3),d0
		bsr	_PutBits
NoUsedCode	addq.w	#1,d5
		move.l	_CrunchedLen(a4),d0
		move.l	_AFileLength(a4),d1
		cmp.l	d0,d1
		bcs	TooShort		; len_cr > len_orig
		dbf	d2,TreeLoop

		move.l	_DWriteMem(a4),a0
		move.w	_BitData(a4),(a0)+
		move.l	_WriteMem(a4),d2
		move.l	_CrunchedLen(a4),d3
		addq.l	#2,d3
		move.l	_DestHand(a4),d1
		move.l	_DosBase(a4),a6
		CALL	Write

		move.l	_WriteMem(a4),_DWriteMem(a4)
		clr.l	_CrunchedLen(a4)
		move.w	#15,_Counter(a4)
		clr.w	_BitData(a4)

		move.l	_ReadMem(a4),a2
		lea	_CodeData(a4),a3
		lea	_CodeLen(a4),a5
		movea.l	4.w,a6
		move.l	_AFileLength(a4),d2
		move.l	d2,d7
CrunchBit	move.l	_CrunchedLen(a4),d6
		cmp.l	d6,d7
		bcs	TooShort
		move.b	(a2)+,d0
		and.w	#$ff,d0
		lsl.w	#1,d0
		move.w	(a5,d0),d1
		lsl.w	#1,d0
		move.l	(a3,d0),d0
		bsr	_PutBits2
		movem.l	d0-d1/a0-a1,-(sp)
		moveq	#0,d0
		move.l	#$1000,d1
		CALL	SetSignal
		and.l	#$1000,d0
		bne	UserBreak		; fermati!
		movem.l	(sp)+,d0-d1/a0-a1
		subq.l	#1,d2
		bne.s	CrunchBit

		move.l	_DWriteMem(a4),a0
		move.w	_BitData(a4),(a0)+	; recupera l'ultima word
		move.l	_WriteMem(a4),d2
		move.l	_DestHand(a4),d1
		move.l	_CrunchedLen(a4),d3
		addq.l	#2,d3
		move.l	_DosBase(a4),a6
		CALL	Write


Exit		move.l	_StackBase(a4),sp	; per scaramanzia!
		move.l	_WriteMem(a4),d0
		beq.s	Free0
		move.l	d0,a1
		move.l	_BFileLength(a4),d0
		movea.l	4.w,a6
		CALL	FreeMem
		move.l	4.w,a6
Free0		move.l	_DReadMem(a4),d0
		beq.s	Free1
		move.l	d0,a1
		move.l	_AFileLength(a4),d0
		movea.l	4.w,a6
		CALL	FreeMem
Free1		move.l	_SourceHand(a4),d1
		beq.s	Free2
		movea.l	_DosBase(a4),a6
		CALL	Close
Free2		move.l	_DestHand(a4),d1
		beq.s	Free3
		movea.l	_DosBase(a4),a6
		CALL	Close
Free3		move.l	_DosBase(a4),d0
		beq.s	Free4
		lea	Return.msg(pc),a0
		bsr	_Write
		move.l	d0,a1
		movea.l	4.w,a6
		CALL	CloseLibrary
Free4		move.l	_PPBase(a4),d0
		beq.s	Free5
		move.l	d0,a1
		move.l	4.w,a6
		CALL	CloseLibrary
Free5		move.l	a4,a1
		move.l	#VarLength,d0
		movea.l	4.w,a6
		CALL	FreeMem
QExit		rts


*================================================================  ========

Help		lea	Help.msg(pc),a0
		bsr	_Write
SExit		bra	Exit

NoSource	lea	NoSource.msg(pc),a0
		bsr	_Write
DeleteDest	move.l	_DestHand(a4),d1
		move.l	_DosBase(a4),a6
		CALL	Close
		clr.l	_DestHand(a4)
		move.l	_DestName(a4),d1
		CALL	DeleteFile
		bra.s	SExit

NoDest		lea	NoDest.msg(pc),a0
		bsr	_Write
		bra.s	SExit

NoWMem
NoRMem		lea	MemP.msg(pc),a0
		bsr	_Write
		bra.s	DeleteDest

ReadingError	lea	Reading.msg(pc),a0
		bsr	_Write
		bra.s	DeleteDest

NoMatchVer	lea	NoMatch.msg(pc),a0
		bsr	_Write
		bra	DeleteDest

TooShort	lea	TooShort.msg(pc),a0
		bsr	_Write
		bra	DeleteDest

UserBreak	movem.l	(sp)+,d0-d1/a0-a1
		lea	UserBreak.msg(pc),a0
		bsr	_Write
		bra	DeleteDest

TreeErr		lea	TreeError.msg(pc),a0
		bsr	_Write
		bra	DeleteDest

ErrorCRC	lea	CRC.msg(pc),a0
		bsr	_Write
		bra	DeleteDest


*======================================================= DECRUNCHER =======

DecrunchDB	move.l	_WriteMem(a4),a1
		move.l	_BFileLength(a4),d0
		movea.l	4.w,a6
		CALL	FreeMem

		clr.l	_WriteMem(a4)

		adda.w	#4,a3
		move.l	(a3)+,d0
		move.w	(a3),_FileCRC(a4)
		move.l	d0,d2
		and.l	#$1fffffff,d0
		move.l	d0,_BFileLength(a4)
		and.l	#$e0000000,d2
		rol.l	#VBIT,d2
		cmp.b	#VERSION,d2
		bne	NoMatchVer

		moveq	#MEMF_PUBLIC,d1
		CALL	AllocMem
		move.l	d0,_WriteMem(a4)
		beq	NoWMem

		lea	DecrunchS.msg(pc),a0
		bsr	_Write

		lea	ReadHead.msg(pc),a0
		bsr	_Write

		move.l	_ReadMem(a4),a0
		lea	12(a0),a0
		move.l	a0,_ReadMem(a4)
		move.w	#16,_Counter(a4)
		move.w	-2(a0),_BitData(a4)

		lea	_CodeData(a4),a0
		lea	_CodeLen(a4),a1
		move.w	#NumChar-1,d2
		moveq	#0,d5
LoadBitCode	moveq	#1,d1
		bsr	_GetBits
		tst.w	d0
		bne.s	NoUCode
		moveq	#5,d1
		bsr	_GetBits
		move.w	d5,d3
		lsl.w	#1,d3
		move.w	d0,(a1,d3)
		move.w	d0,d1
		bsr	_GetBits
		lsl.w	#1,d3
		move.l	d0,(a0,d3)
NoUCode		addq.w	#1,d5
		dbf	d2,LoadBitCode

		movem.l	a0-a1,-(sp)
		lea	Remake.msg(pc),a0
		bsr	_Write
		movem.l	(sp)+,a0-a1

RemakeTable	lea	_Left(a4),a3		; rami
		lea	_Right(a4),a2
		move.w	#NumChar-1,d7		; d7=caratteri totali
		moveq	#0,d5			; d5=carattere attuale
SetRoot		moveq	#0,d6			; d6=$100=256 nodo
SkpBlankCode	move.w	(a1,d5),d4		; d4=code len
		bne.s	NoZeroCode
		addq.w	#2,d5
		subq.w	#1,d7
		bmi	EndRmkTbl
		bra.s	SkpBlankCode
NoZeroCode	moveq	#32,d1
		sub.w	d4,d1
		move.w	d5,d3
		lsl.w	#1,d3
		move.l	(a0,d3),d2

		movem.l	d3-d4,-(sp)
		moveq	#0,d3
		subq.w	#1,d4
ChgDir		lsr.l	#1,d2			; dalla root alla foglia
		addx.l	d3,d3
		dbf	d4,ChgDir
		move.l	d3,d2
		movem.l	(sp)+,d3-d4

		lsl.l	d1,d2			; d2=code allineato left.l
		subq.w	#1,d4
OldNode		lsl.l	#1,d2
		bcc.s	OnLeft
OnRight		move.l	a2,a5
		move.l	a3,a6
MkLeftNode	move.w	(a5,d6),d0
		beq.s	NullNode
		cmp.w	#$100,d0
		bcs	TreeErr			; e` errore!
		sub.w	#$100,d0
		move.w	d0,d6
		bra.s	LpOldNode0
NullNode	move.w	d6,d1
SrchNxtNode	addq.w	#2,d1
		move.w	(a5,d1),d0
		bne.s	SrchNxtNode
		move.w	(a6,d1),d0
		bne.s	SrchNxtNode
		add.w	#$100,d1
		move.w	d1,(a5,d6)
		bra.s	LpOldNode
OnLeft		move.l	a3,a5
		move.l	a2,a6
		bra.s	MkLeftNode
LpOldNode	move.w	d1,d6
		sub.w	#$100,d6
LpOldNode0	dbf	d4,OldNode
		move.w	d5,d1
		lsr.w	#1,d1
		move.w	d1,(a5,d6)		; foglia
		move.w	#$ffff,(a6,d6)
		addq.w	#2,d5			; carattere succ.
		dbf	d7,SetRoot


EndRmkTbl	lea	Decrunch.msg(pc),a0
		bsr	_Write

		move.l	_ReadMem(a4),a0
		adda.w	#2,a0
		move.l	a0,_ReadMem(a4)
		move.w	#16,_Counter(a4)
		move.w	-2(a0),_BitData(a4)

Decrunch	move.l	_BFileLength(a4),d7
		move.l	_WriteMem(a4),a5
		moveq	#0,d6
RdLoop0		moveq	#32,d1
		bsr	_GetBits
		move.w	#32,d4
		move.l	d0,d3
RdLoop		subq.w	#1,d4
		bmi.s	RdLoop0
		lsl.l	#1,d3
		bcc.s	LeftCase		
RightCase	move.l	a2,a0
DoCase		move.w	(a0,d6),d6
		cmp.w	#$100,d6
		bcs.s	OutChar
		sub.w	#$100,d6
		move.w	(a0,d6),d0
		cmp.w	#$100,d0
		bcs.s	OutChar0
		bra.s	RdLoop
LeftCase	move.l	a3,a0
		bra.s	DoCase
OutChar0	move.w	d0,d6
OutChar		move.b	d6,(a5)+
		moveq	#0,d6
		subq.l	#1,d7
		bne.s	RdLoop

		move.l	_WriteMem(a4),a0
		move.l	_BFileLength(a4),d0
		bsr	_CalcCRC
		move.w	_FileCRC(a4),d1
		cmp.w	d1,d0
		bne	ErrorCRC

		move.l	_BFileLength(a4),d3
		move.l	_DestHand(a4),d1
		move.l	_WriteMem(a4),d2
		CALL	Write

		bra	Exit


*======================================================= SUBROUTINES ======

GETList		REG	a2/d1-d3		; per sicurezza

_GetBits	movem.l	GETList,-(sp)		; d1=numero di bit da
		subq.w	#1,d1			; prendere
		moveq	#0,d0
		move.w	_BitData(a4),d2
		move.w	_Counter(a4),d3
LoopBit		lsl.w	#1,d2
		addx.l	d0,d0
		subq.w	#1,d3
		bne.s	InLoop
NextWord	move.w	#16,d3
		move.l	_ReadMem(a4),a2
		move.w	(a2)+,d2
		move.l	a2,_ReadMem(a4)
InLoop		dbf	d1,LoopBit
		move.w	d2,_BitData(a4)
		move.w	d2,$dff180
		move.w	d3,_Counter(a4)
		movem.l	(sp)+,GETList		; d0=bit presi
		rts


PUTList		REG	a0/d0-d3

_PutBits	movem.l	PUTList,-(sp)		; d0=dati da prendere.
		subq.w	#1,d1			; d1=num bit da considerare
		move.w	d1,d2
		moveq	#0,d3
		move.w	d0,$dff180
InvertLoop	lsr.l	#1,d0
		addx.l	d3,d3
		dbf	d1,InvertLoop
		;move.l	d0,d3
		move.w	_BitData(a4),d0
		move.w	_Counter(a4),d1
PUTLoop		lsr.l	#1,d3
		bcc.s	NoSetBit
		bset.l	d1,d0
NoSetBit	subq.w	#1,d1
		bpl.s	PInLoop
WWord		move.w	#15,d1
		move.l	_DWriteMem(a4),a0
		move.w	d0,(a0)+
		addq.l	#2,_CrunchedLen(a4)
		move.l	a0,_DWriteMem(a4)
		moveq	#0,d0
PInLoop		dbf	d2,PUTLoop
		move.w	d0,_BitData(a4)
		move.w	d1,_Counter(a4)
		movem.l	(sp)+,PUTList
		rts

_PutBits2	movem.l	PUTList,-(sp)		; d0=dati da prendere.
		subq.w	#1,d1			; d1=num bit da considerare
		move.w	d1,d2
		moveq	#0,d3
		move.w	d0,$dff180
		move.l	d0,d3
		move.w	_BitData(a4),d0
		move.w	_Counter(a4),d1
PUTLoop2	lsr.l	#1,d3
		bcc.s	NoSetBit2
		bset.l	d1,d0
NoSetBit2	subq.w	#1,d1
		bpl.s	PInLoop2
WWord2		move.w	#15,d1
		move.l	_DWriteMem(a4),a0
		move.w	d0,(a0)+
		addq.l	#2,_CrunchedLen(a4)
		move.l	a0,_DWriteMem(a4)
		moveq	#0,d0
PInLoop2	dbf	d2,PUTLoop2
		move.w	d0,_BitData(a4)
		move.w	d1,_Counter(a4)
		movem.l	(sp)+,PUTList
		rts



SMList		REG	a0-a3/d2-d7

_SearchMinor	movem.l	SMList,-(sp)
		lea	_Heap(a4),a1
		move.w	_DUsedChar(a4),d3
		bsr	_GetLess
		move.w	d0,_MomAVar(a4)
		move.l	d1,_MomVar2(a4)
		bsr	_CalcUsed
		lea	_Freq(a4),a0
		lea	_Heap(a4),a1
		move.w	_DUsedChar(a4),d3
		bsr	_GetLess
		add.l	d1,_MomVar2(a4)
		move.w	d0,_MomBVar(a4)
		bsr	_CalcUsed
		move.w	_DUsedChar(a4),d0
		lsl.w	#1,d0
		move.w	_NumChar(a4),d1
		lea	_Heap(a4),a1
		move.w	d1,(a1,d0)
		addq.w	#1,_DUsedChar(a4)
		move.w	_MomAVar(a4),d0
		move.w	_MomBVar(a4),d1
RFound		movem.l	(sp)+,SMList
		rts


_GetLess	cmp.w	#1,d3
		beq.s	CodeCase1
		subq.w	#2,d3			; a1=Heap
		move.w	(a1)+,d5		; a0=Freq
NextHeap0	move.w	d5,d6			; d3=char
		move.w	d6,d7
		lsl.w	#2,d6
		move.l	(a0,d6),d5
NextHeap	move.w	(a1)+,d1
NoUsedHeap	move.w	d1,d4
		lsl.w	#2,d4
		move.l	(a0,d4),d6
		cmp.l	d6,d5
		bcs.s	NoNewMinor
		move.w	d1,d7
		move.l	d6,d5
NoNewMinor	dbf	d3,NextHeap
KSkip		subq.w	#1,_DUsedChar(a4)
		move.w	d7,d0
		lsl.w	#2,d7
		move.l	(a0,d7),d1
		clr.l	(a0,d7)
		rts
CodeCase1	move.w	(a1)+,d0
		move.w	d0,d2
		lsl.w	#2,d2
		move.l	(a0,d2),d1
		clr.l	(a0,d2)			; azzera freq per non pren-
		subq.w	#1,_DUsedChar(a4)	; derla di nuovo.
		rts


		IFNE	LIMIT
_GetMax		move.w	#NumChar-2,d6
		move.l	_WriteMem(a4),a0
		move.l	(a0)+,d5
GetMaxLoop	move.l	(a0)+,d4
		cmp.l	d5,d4
		bls.s	ReGMLoop
		move.l	d4,d5
ReGMLoop	dbf	d6,GetMaxLoop
		move.l	d5,d0
		rts
		ENDC


_CalcUsed	moveq	#0,d0
		moveq	#0,d2
		move.w	_NumChar(a4),d1
		subq.w	#1,d1
		lea	_Freq(a4),a0
		lea	_Heap(a4),a1
CUCLoop		tst.l	(a0)+
		beq.s	CUCInLoop
		move.w	d0,(a1)+
		addq.w	#1,d2
CUCInLoop	addq.w	#1,d0
		dbf	d1,CUCLoop
		rts


_Write		movea.l	_DosBase(a4),a6
		move.l	a0,d2
		move.l	_Output(a4),d1
		moveq	#0,d3
LenLoop		tst.b	(a0)+
		beq.s	DoWrite
		addq.w	#1,d3
		bra.s	LenLoop
DoWrite		CALL	Write
		move.l	a6,d0
		rts


_CalcFreq	lea	_Freq(a4),a1
		move.l	_DReadMem(a4),a2
		move.l	_AFileLength(a4),d1
CalcLoop	moveq	#0,d5
		move.b	(a2)+,d5
		lsl.w	#2,d5
NoNewChar	addq.l	#1,(a1,d5)
		subq.l	#1,d1
		bne.s	CalcLoop
		rts


_CalcCRC	moveq	#0,d1		; un CRC finto e rozzo di soli
LoopCRC		move.b	(a0)+,d2	; 16 bit per tutto il file!
		eor.b	d2,d1
		rol.w	#1,d1
		subq.l	#1,d0
		bne.s	LoopCRC
		move.w	d1,d0
		rts


*========================================================= DATI ===========

DosName		dc.b	'dos.library',0
		even

PPName		dc.b	'powerpacker.library',0
		even

NoSource.msg	dc.b	10,27,'[43m'
		dc.b	'SOURCE NOT FOUND!',27,'[0m',10,0
		even

NoDest.msg	dc.b	10,27,'[43m'
		dc.b	'DESTINATION NOT FOUND!',27,'[0m',10,0
		even

MemP.msg	dc.b	10,27,'[43m'
		dc.b	'NO MEMORY FOR ME!',27,'[0m',10,0
		even

TooShort.msg	dc.b	10,27,'[43m'
		dc.b	'NO CRUNCHABLE!',27,'[0m',10,0
		even

Reading.msg	dc.b	10,27,'[43m'
		dc.b	'READING ERROR!',27,'[0m',10,0
		even

NoMatch.msg	dc.b	10,27,'[43m'
		dc.b	'NO MATCH VERSION!',27,'[0m',10,0
		even

CRC.msg		dc.b	10,27,'[43m'
		dc.b	'ERROR: FILE NOT DECRUNCHED!',27,'[0m',10,0
		even

TreeError.msg	dc.b	10,27,'[43m'
		dc.b	'ERROR MAKING TREE!',27,'[0m',10,0
		even

UserBreak.msg	dc.b	10,27,'[33m'
		dc.b	'*** USER BREAK',27,'[0m',10,0
		even

Help.msg	dc.b	27,'c',10
		dc.b	27,'[1m',27,'[33m'
		dc.b	'DBCruncher V1.42',27,'[0m'
		dc.b	' by Mauro Panigada',10,10
		dc.b	27,'[32m','USAGE:',10,27,'[0m'
		dc.b	'  DBCruncher [coms] <source> [<destination>]',10,10
		dc.b	'  Commands can be:',10
		dc.b	'     -r       recrunch file.',10,10
		dc.b	'DBCruncher works with all kinds of file, but'
		dc.b	' the executable ones will be no',10
		dc.b	'executable! I used Static Huffman. PowerPacker'
		dc.b	' data allowed!',10,0
		even

CrunchS.msg	dc.b	10,27,'[42m',27,'[1m'
		dc.b	'Crunching sequence:'
		dc.b	27,'[0m',10,10,0
		even

DecrunchS.msg	dc.b	10,27,'[42m',27,'[1m'
		dc.b	'Decrunching sequence:'
		dc.b	27,'[0m',10,10,0
		even

WriteHead.msg	dc.b	'Writing heading...'
Return.msg	dc.b	10,0
		even

ReadHead.msg	dc.b	'Reading heading...',10,0
		even

Remake.msg	dc.b	'Remaking table...',10,0
		even

Loading.msg	dc.b	10,'Loading file...',10,0
		even

CalcFreq.msg	dc.b	'Calculating frequences...',10,0
		even

Redun.msg	dc.b	'Searching for redundance...',10,0
		even

Crunch.msg	dc.b	'Crunching file...',10,0
		even

Decrunch.msg	dc.b	'Decrunching file...',10,0
		even


		END