* --------------------------------------------------------------------- *
* LSSUP.A	- Assembly support routines for ls.c			*
* Copyright (c) 1988 by Justin V. McCormick. All Rights Reserved.	*
* --------------------------------------------------------------------- *
	IDNT	"lssup.a"

	include "asm:inc/macros.i"

; Assembler options for CAPE
	BASEREG	B
	SMALLOBJ
	OPTIMON
	ADDSYM
	DEBUG

; Equates
fib_FileName	equ	$8
fib_Size	equ	$7C
fib_NumBlocks	equ	$80
fib_DateStamp	equ	$84

ds_Days		equ	$0
ds_Minute	equ	$4
ds_Tick		equ	$8

_LVOAddPort	equ	$FFFFFE9E
_LVOAllocMem	equ	$FFFFFF3A
_LVOAllocSignal	equ	$FFFFFEB6
_LVODebug	equ	$FFFFFF8E
_LVOFindTask	equ	$FFFFFEDA
_LVOFreeMem	equ	$FFFFFF2E
_LVOFreeSignal	equ	$FFFFFEB0
_LVOGetMsg	equ	$FFFFFE8C
_LVOPutMsg	equ	$FFFFFE92
_LVORawDoFmt	equ	$FFFFFDF6
_LVORemPort	equ	$FFFFFE98
_LVORead	equ	$FFFFFFD6
_LVOWaitPort	equ	$FFFFFE80
_LVOWrite	equ	$FFFFFFD0

pr_ConsoleTask		EQU	$A4 
MEMF_CLEAR		EQU	$10000 
MEMF_PUBLIC		EQU	$1 
sp_Msg			EQU	$0 
sp_Pkt			EQU	$14 
sp_SIZEOF		EQU	$44 
dp_Link			EQU	$0 
dp_Port			EQU	$4 
dp_Arg1			EQU	$14 
dp_Type			EQU	$8 
ACTION_SCREEN_MODE	EQU	$3E2 
LN_NAME			EQU	$A 
LN_PRI			EQU	$9 
LN_TYPE			EQU	$8 
MP_FLAGS		EQU	$E 
MP_MSGLIST		EQU	$14 
MP_SIGBIT		EQU	$F 
MP_SIGTASK		EQU	$10 
MP_SIZE			EQU	$22 
NT_MSGPORT		EQU	$4 
PA_SIGNAL		EQU	$0 

* External constants
	XREF	_DOSBase
	XREF	_Out
	XREF	_In
* --------------------------------------------------------------------- *
	SECTION	lssup,CODE

* ------------------------------------------------------------------------- *
* void asprintf(wstr, formatstring, args)
*   char *wstr;
*   char *formatstring;
*   char **args;
* 
* Synopsis: Given formatstring and args to format, formats output to wstr.
* Similar to sprintf(), except doesn't handle floats.
* ------------------------------------------------------------------------- *
	XDEF	_asprintf
_asprintf:
	link	a5,#0
	movem.l	d0-d2/a0-a3,-(sp)	;Save everything we might clobber

* Call format function to convert fmtstring and args to buffer on the stack
	movea.l	12(a5),a0		;Grab format string
	lea	16(a5),a1		;Grab EA of arguments
	lea	kput1,a2		;Grab EA of output subroutine
	movea.l	8(a5),a3		;Grab EA of dest workspace
	SYS	RawDoFmt,4		;Format it into workspace

	movem.l	(sp)+,d0-d2/a0-a3	;Restore registers
	unlk	a5			;And stack frame
	rts

* ------------------------------------------------------------------------- *
* RawDoFmt() output routine for xprintf, called for each formatted char.
* Takes byte in d0 and puts in buffer pointed to by a3, then increments a3.
* ------------------------------------------------------------------------- *
	XDEF	kput1
kput1:
	move.b	d0,(a3)+
	rts

* --------------------------------------------------------------------- *
* void GetWinBounds(width, height)
*   long *width, *height;
*
* Find current console window, determine width and height
* in terms of current font, update width and height VPARMS passed.
* --------------------------------------------------------------------- *
rpstr	equ	-32
rpport	equ	-12
packet	equ	-8
conid	equ	-4

width	equ	8
height	equ	12

	XDEF	_GetWinBounds
_GetWinBounds:
	link	a5,#-32
	movem.l	d2-d4/a2,-(sp)

	suba.l	a1,a1
	SYS	FindTask,4		;d0 = FindTask(0L), our process
	movea.l	d0,a0			;Transfer to address reg
	move.l	pr_ConsoleTask(a0),conid(a5) ;Save proc->pr_ConsoleTask

	moveq	#0,d4			;Clear our success status register

	moveq	#0,d0
	movea.l	d0,a0
	bsr.w	CreatePort
	move.l	d0,rpport(a5)		;rpport = CreatePort(0L, 0L)
	 beq.w	gwbdone			;Oops, no signals or ram available!
	move.l	#MEMF_PUBLIC+MEMF_CLEAR,d1
	moveq	#sp_SIZEOF,d0
	SYS	AllocMem
	move.l	d0,packet(a5)		;packet = AllocMem(sizeof(*packet),MEMF_PUBLIC|MEMF_CLEAR)
	 beq.w	gwbfreeport		;Oops, no ram, free up port

* Okay, we got our process id, reply port, and packet
* Now toggle the console into raw mode
	movea.l	rpport(a5),a2
	movea.l	d0,a1
	movea.l	conid(a5),a0
	moveq	#1,d0
	bsr.w	SetConsoleType		;SetConsoleType(1L, conid, packet, rpport)

* Request a window bounds report
	moveq	#4,d3
	lea	gwbrstr(a4),a0
	move.l	a0,d2
	move.l	_Out(a4),d1
	SYS	Write,_DOSBase(a4)	;Write(Output(), "\2330 q", 4L);
	cmpi.l	#$0004,d0		;Did the console choke on it?
	 bne.w	gwbsetcook		;hmmm, see if we can back out gracefully

* Read the report string into stack buffer
	moveq	#16,d3			;Don't let it get longer than 16 characters
	lea	rpstr(a5),a0		;Point to input string area
	move.l	a0,d2
	move.l	_In(a4),d1
	SYS	Read			;Read(Input(), rpstr, 16L)
	move.l	d0,d4			;Save read length while we close shop

* Turn the console back to cooked mode pronto to avoid cursor blink
gwbsetcook:
	movea.l	rpport(a5),a2
	movea.l	packet(a5),a1
	movea.l	conid(a5),a0
	moveq	#0,d0
	bsr.w	SetConsoleType		;SetConsoleType(0L, conid, packet, rpport)

* Release resources we borrowed
gwbfreepack:
	move.l	packet(a5),d0		;Did we allocate a packet?
	 beq.b	gwbfreeport		;nay, check for port to free
	movea.l	d0,a1
	moveq	#sp_SIZEOF,d0
	SYS	FreeMem			;Else FreeMem(packet, sizeof(*packet))

gwbfreeport:
	move.l	rpport(a5),d0		;if (rpport)...
	 beq.w	gwbdone			;nope
	bsr.w	DeletePort		;Else DeletePort(rpport)

* Finally, sanity check window bounds report string
* d4 = length of report string according to Read()
	cmpi.l	#9,d4			;Less than 8 characters returned?
	 ble.w	gwbdone			;hmmm, phonky bounds report from DOS?
	lea	rpstr(a5),a2		;a2 = rpstr
	cmpi.b	#';',4(a2)		;Matches a typical report template?
	 bne.w	gwbdone			;nope, got some weird junk back?
	cmpi.b	#'r',-1(a2,d4.w)	;Last byte is 'r' for report?
	 bne.w	gwbdone			;Nope, message fubar!

* Parse the height and width variables from the field now
* Our report format looks like this in hex:
* 	9b 31 3b 31 3b y2 y1 3b x2 x1 20 72
* Or in ascii:
*	<0x9b>1;1;20;77 r
* Which would indicate a width of 77 cols and a height of 20 rows for
* the current console device
*
* REGS:	a2 points to beginning of 'r' terminated string

	lea	5(a2),a2		;Point to first char of Y size
	moveq	#0,d1			;Clear out work reg

* Convert ascii rows value to LONG, update host data
	move.b	(a2)+,d1		;Grab a Y
	subi.w	#'0',d1			;Less ascii offset
	cmpi.b	#';',(a2)		;Any more Y digits?
	 beq.b	1$			;Nope
	mulu	#10,d1			;Else shift by 10
	add.b	(a2)+,d1		;Add least significant Y digit
	subi.b	#'0',d1			;Less ascii offset
	cmpi.b	#';',(a2)		;Any more Y digits?
	 beq.b	1$			;Nope
	mulu	#$000a,d1		;Else shift by 10
	add.b	(a2)+,d1		;Add least significant Y digit
	subi.b	#'0',d1			;Less ascii offset
					;We'll assume screen height < 999 rows	
1$
* Convert ascii columns value to LONG, update host data
	addq.w	#1,a2			;Move past the ';' separator
	moveq	#0,d2			;Zap work reg
	move.b	(a2)+,d2		;Grab msd of X
	cmpi.b	#' ',d2			;Premature end?
	 beq.w	gwbdone			;Huh, must be garbage - don't update VPARMS
	cmpi.b	#';',d2			;Also a possible error
	 beq.w	gwbdone
	cmpi.b	#'r',d2			;And what about this?
	 beq.w	gwbdone

	subi.b	#'0',d2			;Okay, adjust ascii offset
	cmpi.b	#' ',(a2)		;Hit end of report?
	 beq.b	2$			;Yep
	mulu	#$000a,d2		;Else shift by 10
	add.b	(a2)+,d2		;Add next digit
	subi.b	#'0',d2			;Ascii adjust
	cmpi.b	#' ',(a2)		;Hit end of report?
	 beq.b	2$			;Yep
	mulu	#$000a,d2		;Else shift by 10
	add.b	(a2),d2			;Add next digit
	subi.b	#'0',d2			;Ascii adjust

2$
* Finally, update parameters by reference
	movea.l	height(a5),a0		;Grab height VPARM
	move.l	d1,(a0)			;*height = d1
	movea.l	width(a5),a0		;Grab width VPARM
	move.l	d2,(a0)			;*width = d2

gwbdone:
	movem.l	(sp)+,d2-d4/a2
	unlk	a5
	rts

* --------------------------------------------------------------------- *
* __asm void SetConsoleType(flag, id, packet, port)
*   register __d0 long flag;
*   register __a0 struct Process *id;
*   register __a1 struct StandardPacket *packet;
*   register __a2 struct MsgPort *port;
*
* Flag = 1L -- Raw mode
*      = 0L -- Cooked mode
* --------------------------------------------------------------------- *
	XDEF	SetConsoleType
SetConsoleType:
	movem.l	a3/a5,-(sp)

	movea.l	a0,a3			;Copy process pointer
	movea.l	a1,a5			;Copy packet pointer
	lea	sp_Pkt(a5),a0		;a0 = &packet->sp_Pkt
	move.l	a0,sp_Msg+LN_NAME(a5)	;p->sp_Msg.mn_Node.ln_Name = &p->sp_Pkt
	lea	sp_Msg(a5),a0		;a0 = &packet->sp_Msg
	move.l	a0,sp_Pkt+dp_Link(a5)	;p->sp_Pkt.dp_Link = &p->sp_Msg
	move.l	a2,sp_Pkt+dp_Port(a5)	;p->sp_Pkt.dp_Port = replyport
	move.l	#ACTION_SCREEN_MODE,sp_Pkt+dp_Type(a5)	;Set function

	tst.w	d0			;On or Off?
	 beq.w	1$
	move.l	#-1,sp_Pkt+dp_Arg1(a5)	;RAW ON
	bra.b	2$
1$
	clr.l	sp_Pkt+dp_Arg1(a5)	;RAW OFF
2$
	movea.l	a3,a0
	movea.l	a5,a1
	SYS	PutMsg,4		;PutMsg(proc, packet)

	movea.l	a2,a0
	SYS	WaitPort		;WaitPort(port)
	movea.l	a2,a0
	SYS	GetMsg			;(void)GetMsg(port)

	movem.l	(sp)+,a3/a5
	rts

* ------------------------------------------------------------------------- *
* struct MsgPort *CreatePort(name, pri) (a0/d0)
* ------------------------------------------------------------------------- *
	XDEF	CreatePort
CreatePort:
	movem.l	d5/d7/a2/a5,-(sp)

	move.l	a0,a5			;Save Name
	move.l	d0,d5			;Save Pri

* Allocate a free signal, crap out if we can't
	moveq	#-1,d0
	SYS	AllocSignal,4
	cmp.l	#-1,d0			;Did we get a signal?
	 bne.b	cpgotsig		;Yep
	moveq	#0,d0			;Otherwise return NULL
	bra.w	cpdone

cpgotsig:
	move.l	d0,d7			;Save our signal

* Allocate memory for MsgPort
	moveq.l	#MP_SIZE,d0		;Size of MsgPort
	move.l	#MEMF_PUBLIC!MEMF_CLEAR,d1	;Type of memory
	SYS	AllocMem		;Allocate it
	tst.l	d0			;Did we get it?
	 bne.b	cpgotport		;Yep

	move.l	d7,d0			;Otherwise crap out, free signal
	SYS	FreeSignal
	moveq	#0,d0			;Return NULL
	bra.w	cpdone

cpgotport:
	move.l	d0,a2			;This is our new port!
	move.l	a5,LN_NAME(a2)		;port->mp_Node.ln_Name = name
	move.b	d5,LN_PRI(a2)		;port->mp_Node.ln_Pri = priority
	move.b	#NT_MSGPORT,LN_TYPE(a2) ;port->mp_Node.ln_Type = NT_MSGPORT
	move.b	#PA_SIGNAL,MP_FLAGS(a2) ;port->mp_Flags = PA_SIGNAL
	move.b	d7,MP_SIGBIT(a2)	;port->mp_SIGBIT = sigBit
	suba.l	a1,a1
	SYS	FindTask
	move.l	d0,MP_SIGTASK(a2)	;port->mp_SIGTASK = FindTask(0L)
	
	cmpa.l	#0,a5			;Is this a new name?
	beq.b	cpnoname		;Nope, add it to the msg list

	movea.l	a2,a1
	SYS	AddPort			;Otherwise add this port
	move.l	a2,d0			;Return port pointer
	bra.b	cpdone

cpnoname:
* Initialized New List head
	lea	MP_MSGLIST(a2),a0 	;a0 = &port->mp_MsgList
	move.l	a0,(a0)			;list->lh_Head = list
	addq.l	#4,(a0)			;list->lh_Head += 4L
	clr.l	4(a0)			;list->lh_Tail = 0L
	move.l	a0,8(a0)		;list->lh_TailPred = list
	move.l	a2,d0			;Return port pointer

cpdone:
	movem.l	(sp)+,d5/d7/a2/a5
	rts

* ------------------------------------------------------------------------- *
* DeletePort(port)(d0)
* ------------------------------------------------------------------------- *
	XDEF	DeletePort
DeletePort:
	move.l	a5,-(sp)

	move.l	d0,a5
	tst.l	LN_NAME(a5)		;Is there a name?
	beq.s	dpnoname
	
	move.l	d0,a1
	SYS	RemPort,4		;RemPort(port)
	
dpnoname:
	move.b	#$ff,LN_TYPE(a5) 	;port->mp_Node.ln_Type = 0xff
	move.l	#-1,MP_MSGLIST(a5) 	;port->mp_MsgList.lh_Head = -1L

	moveq	#0,d0
	move.b	MP_SIGBIT(a5),d0	;d0 = port->mp_SigBit
	SYS	FreeSignal,4		;FreeSignal(d0)

	moveq	#MP_SIZE,d0
	move.l	a5,a1
	SYS	FreeMem			;FreeMem(port, sizeof(*port))

	move.l	(sp)+,a5
	rts

* ------------------------------------------------------------------------
* char *FibFileDate(fib_date)
*   register struct DateStamp *fib_date;
*
*   Calculate date based on DateStamp structure and return a pointer
* to the formatted date string.
* ------------------------------------------------------------------------
	XDEF	_FibFileDate
_FibFileDate:
	link	a5,#0
	movem.l	d3-d7/a4,-(sp)

	movea.l	8(a5),a1		;Grab datestamp pointer
	moveq	#78,d7			;Initial year = 1978

	move.l	(a1),d5			;days = fib_date->ds_Days
	 blt	ffdbaddate		;Hey! you can't be negative! Invalid date...

* Determine what year it is
	divu	#1461,d5
	move.l	d5,d0			;Stash it
	ext.l	d5
	lsl.l	#2,d5
	add.l	d5,d7			;year += (days / 1461) * 4

* Count how many months into that year
ffdgetmo:
	swap	d0			;days %= 1461
	move.w	d0,d5

1$	tst.w	d5			;Out of days yet?
	 beq	3$			;Yep, done here

	move.w	#365,d6			;Else month_days = 365
	move.w	d7,d0			;Grab year
	andi.w	#3,d0			;if (year & 3) == 0 Leap year?
	 bne	2$			;Nope
	addq.w	#1,d6			;Otherwise bump month_days

2$	cmp.w	d6,d5			;is day < month_days?
	 blt	3$			;yep, done here
	sub.w	d6,d5			;otherwise day -= month_days

	addq.l	#1,d7			; year++
	bra	1$
3$

* Count how many days into that month of that year
ffdgetday:
;for (i = 0, day++; i < 12; i++)
	moveq	#0,d4			;current month = 0
	moveq	#0,d6			;Zap hinybs
	addq.w	#1,d5
	lea	_dayspermonth(a4),a0

1$
	move.b	0(a0,d4.w),d6		;month_days = dayspermonth[i]

	cmpi.w	#1,d4			;if (i == 1 && (year & 3) == 0)
	 bne	2$
	move.w	d7,d0
	andi.w	#3,d0
	 bne	2$
	addq.w	#1,d6			;month_days++

2$	cmp.w	d6,d5			;if (day <= month_days)
	 ble	4$			;Break out, found the right month

	sub.w	d6,d5			;Else, day -= month_days

	addq.w	#1,d4			;i++
3$	cmpi.w	#12,d4			;Done all months yet?
	 blt	1$			;Nope

4$
ffdprint:
1$	cmpi.l	#99,d7			;while (year >= 100)
	 ble	2$
	subi.l	#100,d7			;year -= 100
	bra	1$
2$
;asprintf(datestr, "%02d-%02d-%02d %02d:%02d:%02d", i + 1, day, year, hour, min, sec)
	move.l	8(a1),d0		;sec = fib_date->ds_Tick / 50;
	divu	#50,d0
	move.w	d0,-(sp)		;Push secs

	moveq	#0,d0			;Zap reg
	move.w	6(a1),d0		;min = fib_date->ds_Minute
	move.w	d0,d1			;Clone it
	divu	#60,d0
	move.w	d0,d3			;hour = min / 60
	mulu	#60,d0
	sub.w	d0,d1			;min -= hour * 60
	move.w	d1,-(sp)		;Push mins

	move.w	d3,-(sp)		;Push hours
	addq.w	#1,d4			;Push day of month (offset by 1!)
	move.w	d5,-(sp)		;Push month
	move.w	d4,-(sp)
	move.w	d7,-(sp)		;Push year
	pea	_datepat(a4)		;Push the format pattern
	pea	_datestr(a4)		;Push destination buffer
	jsr	_asprintf	
	lea	20(sp),sp
	lea	_datestr(a4),a0
	move.l	a0,d0			;return((char *)&datestr[0])

ffddone:
	movem.l	(sp)+,d3-d7/a4
	unlk	a5
	rts

ffdbaddate:
	lea	_baddatestr(a4),a0	;return (" <Invalid Date> ");
	move.l	a0,d0
	bra	ffddone

*----------------------------------------------------------------------
* LONG iswild(name)
*   char *name;
*
* Search a string for wild characters, return 1 if found
*----------------------------------------------------------------------
	XDEF	_iswild
_iswild:
	movea.l	4(sp),a0		;Grab string pointer
	moveq	#0,d0			;Clear out our character register
ischk1:
	move.b	(a0)+,d0		;Grab a char
	 beq	iwdone			;Might be end of string?
	cmpi.b	#'*',d0			;Is it *?
	 beq	iswdone			;yep, is wild
	cmpi.b	#'?',d0			;Is it a qmark
	 bne	ischk1			;Nope, check next character

iswdone:
	moveq	#1,d0
iwdone:
	rts


* ------------------------------------------------------------------------
; Compare a wild card name with a normal name
; LONG wildmatch (name, wild)
;   char *name, *wild;
* ------------------------------------------------------------------------
	XDEF	_wildmatch
_wildmatch:
	link	a5,#-64
	movem.l	d3/a2-a3,-(sp)

	movea.l	8(a5),a2		;Grab name
	movea.l	12(a5),a3		;Grab pattern
	lea	-64(a5),a0		;back[0][0]
	lea	-60(a5),a1		;back[0][1]

	moveq	#0,d3			;bi = 0

wmloop1:
	tst.b	(a2)			;End of name?
	 bne	wmnoteon
	tst.b	(a3)			;End of pattern?
	 beq	wmmatched		;Yep, we matched

wmnoteon:
	cmpi.b	#'*',(a3)		;Is it a splat?
	 bne	wmnotstar		;Nope, maybe '?'

	cmpi.w	#64,d3			;Have we hit max expression depth?
	 beq	wmnomatch		;Yep, ran out of room in recursion table

;back[bi][0] = w
	move.l	a3,0(a0,d3.w)		;Stash pointer to this '*' in table

;back[bi][1] = n
	move.l	a2,0(a1,d3.w)

	addq.w	#8,d3			;++bi
	addq.w	#1,a3			;++w
	bra	wmloop1			;Check next

wmgoback:
	subq.w	#8,d3			;--bi
	move.l	a0,d0
wmback1:
	tst.w	d3			;while (bi >= 0 && *back[bi][1] == '\x0')
	 blt	wmbacked
	movea.l	0(a1,d3.l),a0
	tst.b	(a0)
	 bne	wmbacked

	subq.w	#8,d3			;--bi
	bra	wmback1

wmbacked:
	tst.w	d3			;if (bi < 0)
	 blt	wmnomatch		;return (0)

	movea.l	d0,a0
	movea.l	0(a0,d3.w),a3		;w = back[bi][0] + 1
	addq.w	#1,a3	

	addq.l	#1,0(a1,d3.w)
	movea.l	0(a1,d3.l),a2		;n = ++back[bi][1]

	addq.w	#8,d3			;++bi
	bra	wmloop1

wmnotstar:
	cmpi.b	#'?',(a3)		;Is it '?'
	 bne	wmnotqmark

	tst.b	(a2)			;Reached end of string?
	 bne	wmincpoint		;Nope, move on to next char

	tst.w	d3			;Are we at top level of expression?
	 beq	wmnomatch		;Yep, expression didn't match
	bra	wmgoback		;Otherwise pop a level and try to match

wmnotqmark:
	move.b	(a2),d0			;Grab a char from bstr
	cmpi.b	#$40,d0			;less than @ character?
	 bls	1$			;Yep
	cmpi.b	#$5a,d0			;Greater than Z?
	 bhi	1$			;Yep
	addi.b	#$20,d0
1$
	move.b	(a3),d1			;Grab a char from bstr
	cmpi.b	#$40,d1			;less than @ character?
	 bls	2$			;Yep
	cmpi.b	#$5a,d1			;Greater than Z?
	 bhi	2$			;Yep
	addi.b	#$20,d1
2$
	cmp.b	d0,d1			;*n = *w?
	 beq	wmincpoint		;Yep, move on past

	tst.w	d3			;Are we at top expression level?
	 beq	wmnomatch		;Yep, they didn't match
	bra	wmgoback		;Nope, process next part

wmincpoint:
	tst.b	(a2)			;Done with name?
	 beq	wmnamend		;Yep
	addq.w	#1,a2			;Otherwise increment name pointer

wmnamend:
	tst.b	(a3)			;End of pattern?
	 beq	wmmatched		;Yep, we matched
	addq.w	#1,a3			;Otherwise inc wild pointer, match next char
	bra	wmloop1

wmmatched:
	moveq	#1,d0
	bra	wmdone

wmnomatch:
	moveq	#0,d0

wmdone:
	movem.l	(sp)+,d3/a2-a3
	unlk	a5
	rts

* --------------------------------------------------------------------- *
* void SortFibs (keytype, direction, fibheadp)
*		   d0       d1         a0
*  int keytype;
*  struct FibEntry *fibheadp;
*
* Selection sort a linked list of FibEntrys based on a keycode 		*
* --------------------------------------------------------------------- *
	XDEF	_SortFibs
_SortFibs:
	link	a5,#0
	movem.l	d2-d4/a2-a3/a6,-(sp)

	move.l	d0,d2			;Save keytype
	move.l	d1,d4			;Save direction of sort
	movea.l	a0,d3			;Save fibheadp
	movea.l	a0,a2			;a2 = a0 = i

sfILoop:
	cmp.l	(a2),d3			;i->NextFib != fibheadp?
	 beq	sfdone			;Nope, wrapped around to start
	movea.l	a2,a6			;k = i
	movea.l	(a2),a3			;j = i->NextFib

sfJLoop:
	cmp.l	a3,d3			;j != fibheadp?
	 beq	sfJdone			;Nope compared them all, see if swapped any
	movea.l	8(a6),a0		;a0 = k->Fibp
	movea.l	8(a3),a1		;a1 = j->Fibp
	move.l	d2,d0			;d0 = keytype
	jsr	_CompFibs		;d0 = CompFibs(keytype, k->Fibp, j->Fibp)
	tst.w	d4			;Reverse sort?
	 beq	1$			;Nope
	bchg.l	#0,d0			;Else reverse sense of return
1$
	tst.l	d0			;Return != 0?
	 beq	sfNextJ			;Nope, these two are in order
	movea.l	a3,a6			;else k = j, this is new swap

sfNextJ:
	movea.l	(a3),a3			;j = j->NextFib
	bra	sfJLoop			;Check bounds

sfJdone:
	cmpa.l	a6,a2			;k != i, did we swap?
	 beq	sfNextI			;Nope, i was in correct position already
	move.l	8(a6),d0
	move.l	8(a2),8(a6)
	move.l	d0,8(a2)		;Else SwapFibs (k, i)

sfNextI:
	movea.l	(a2),a2			;i = i->NextFib
	bra	sfILoop			;Check bounds

sfdone:
	movem.l	(sp)+,d2-d4/a2-a3/a6
	unlk	a5
	rts

* --------------------------------------------------------------------- *
* int CompFibs (keytype, a, b)
*		 d0     a0 a1
*   int keytype;
*   struct FileInfoBlock *a, *b;
*
* Used by SortFibs to determine precedence of Fibs.
* --------------------------------------------------------------------- *
	XDEF	_CompFibs
_CompFibs:
	tst.w	d0			;Alphabetize?
	 bne	cfnalpha		;Nope

* Compare lexigraphically, ignoring case differences
cfalpha:
	lea	fib_FileName(a0),a0	;a = &Fipb->fib_FileName
	lea	fib_FileName(a1),a1	;b = &Fipb->fib_FileName

;  for(; *a && tolower(*a) == tolower(*b); a++, b++);
lccstart:
	tst.b	(a0)			;Is there a char here at source?
	 beq	lcceostr		;Nope, fell off the end

	move.b	(a1)+,d1		;Grab a char from bstr
	cmpi.b	#$40,d1			;less than @ character?
	 bls	1$			;Yep
	cmpi.b	#$5a,d1			;Greater than Z?
	 bhi	1$			;Yep
	addi.b	#$20,d1
1$
	move.b	(a0)+,d0		;Grab a char from astr
	cmpi.b	#$40,d0			;less than @ character?
	 bls	2$			;Yep
	cmpi.b	#$5a,d0			;Greater than Z?
	 bhi	2$			;Yep
	addi.b	#$20,d0
2$
	cmp.b	d0,d1			;are they the same?
	 beq	lccstart		;Yep, compare next pair of chars

lcceostr:
	sub.b	d1,d0			;return(tolower(*astr) - tolower(*bstr))
	 bgt	cftrue			; > 0?, return TRUE
	bra	cffalse			;Else return FALSE

cfnalpha:
	subq.w	#1,d0			;Size?
	 bne	cfnsize			;Nope

* Compare fib_Sizes
	move.l	fib_Size(a1),d0		;d0 = bfib->fib_Size
	cmp.l	fib_Size(a0),d0		;a->fib_Size > b->fib_Size?
	 blt	cftrue			;Yep, return TRUE
	 bgt	cffalse			;<, return FALSE
	bra	cfalpha			;Else it's a tie, alphabetize

cfnsize:
	subq.w	#1,d0			;Time?
	 bne	cffalse			;Nope, an error!

* Compare fib_DateStamps
	lea	fib_DateStamp(a0),a0	;a = &afib->fib_DateStamp
	lea	fib_DateStamp(a1),a1	;b = &bfib->fib_DateStamp
	move.l	ds_Days(a1),d0		;d0 = bdate->ds_Days
	cmp.l	ds_Days(a0),d0		;a->ds_Days > b->ds_Days?
	 blt	cftrue			;Yep, a is older
	 bgt	cffalse			;Else if a < b, b is older
					;Else they are the same day, check min/tick
	move.l	ds_Minute(a0),d0
	sub.l	ds_Minute(a1),d0	;d0 = amin - bmin
	muls	#3000,d0		;     * 3000
	add.l	ds_Tick(a0),d0
	sub.l	ds_Tick(a1),d0		;     + atick - btick
	 bgt	cftrue			;Hey, a > b
	 blt	cffalse			;a < b, return false
	bra	cfalpha			;Else its the same date, alphabetize

cftrue:
	moveq	#1,d0
	rts
cffalse:
	moveq	#0,d0
	rts

* --------------------------------------------------------------------- *
	SECTION	__MERGED,DATA
* --------------------------------------------------------------------- *
	XDEF	_dayspermonth
_dayspermonth:
	dc.b	31,28,31,30,31,30,31,31,30,31,30,31
	XDEF	_datepat
_datepat:
	dc.b	'%02d-%02d-%02d %02d:%02d:%02d',0
	CNOP	0,2
	XDEF	_baddatestr
_baddatestr:
	dc.b	'00-00-00 00:00:00',0
	CNOP	0,2

	XDEF	gwbrstr
gwbrstr:
	dc.b	$9b,'0 q'

* --------------------------------------------------------------------- *
	SECTION	__MERGED,BSS
* --------------------------------------------------------------------- *
	XDEF	_datestr
_datestr:
	ds.b	40

* --------------------------------------------------------------------- *
	END
* --------------------------------------------------------------------- *
