
;Comment out next line to make non-detatching program
DETATCH		EQU	0

DefaultBuffers	EQU	256		number of sectors to remember
WindowSmallX	EQU	$171		Small window sizes
WindowSmallY	EQU	$a
WindowBigX	EQU	$1a8		Big window sizes
WindowBigY	EQU	$6a

MoreGadget	EQU	$100		Gadget id numbers
FewerGadget	EQU	$101

	rsreset				structure of a remembered sector
NextHash	rs.l	1		hash table links
PrevHash	rs.l	1
NextInList	rs.l	1		links for deallocation of sectors
PrevInList	rs.l	1
SectorOffset	rs.l	1		disk offset of sector in bytes
WhichDrive	rs.w	1		drive number of sector
Sector		rs.b	$200		sectors data
SectorSize	rs.w	0

	include	"df0:Source/CacheClock.i"

Start	movem.l	d0/a0,-(sp)		save CLI line for later
	move.l	$4.w,a6			Open dos library
	lea	DosName(pc),a1
	jsr	OpenLibrary(a6)
	move.l	d0,DosBase		save base address
	beq	NoDos

	sub.l	a1,a1			Standard CLI/workbench startup
	move.l	$4.w,a6
	jsr	FindTask(a6)

	move.l	d0,a2
	tst.l	pr_CLI(a2)
	bne.s	CLIcall
	lea	pr_MsgPort(a2),a0	Started from workbench
	move.l	$4.w,a6
	jsr	WaitPort(a6)		wait for workbench message

	lea	pr_MsgPort(a2),a0	get that message
	move.l	$4.w,a6
	jsr	GetMsg(a6)
	move.l	d0,WorkbenchMsg		save it for later

	movem.l	(sp)+,d0/a0		ignore registers
	bra.s	StartJoin

CLIcall	movem.l	(sp)+,d0/a3
	clr.b	-1(a3,d0.l)		Null terminate command line
	bsr	ParseCLI		CLI call, so parse command line
	beq.s	NoDos			Bad CLI command ?

StartJoin
	IFD	DETATCH
;detatch main part of program from CLI
	lea	Start-4(pc),a0		a0 is ptr to next segment in list
	move.l	(a0),d3			d3 = BPTR to next hunk
	clr.l	(a0)			unlink second hunk so that it is
;					not de-allocated when this terminates

	move.l	#ProcessName,d1
	moveq	#0,d2			priority of 0
	move.l	#4000,d4		4000 byte stack
	move.l	DosBase,a6
	jsr	CreateProc(a6)		create process
	ENDC

	IFND	DETATCH			code for non-detatching program
	bsr	_main
	ENDC

	move.l	WorkbenchMsg(pc),d0
	beq.s	NoDos
	move.l	$4.w,a6		Stop other tasks from interrupting when
	jsr	Forbid(a6)	closing down otherwise workbench crashes

	move.l	WorkbenchMsg(pc),a1	Workbench open, so signal task ended
	jsr	ReplyMsg(a6)

NoDos	moveq	#0,d0
	rts


**************** Reads an ascii number from (a3)+ (CLI input)
* GetCLInumber *
**************** returns d0 = number
GetCLInumber
	moveq	#0,d0		default number = 0
SkipSpaces
	moveq	#0,d1
	move.b	(a3),d1		skip spaces after command
	cmp.b	#" ",d1
	beq.s	SkipSpc
	cmp.b	#"	",d1
	bne.s	StartNumber
SkipSpc	addq.w	#1,a3
	bra.s	SkipSpaces

StartNumber
	move.b	(a3),d1		get next digit
	sub.w	#"0",d1
	bmi.s	EndNumber
	cmp.w	#10,d1
	bpl.s	EndNumber
	add.l	d0,d0		multiply d0 by 10 & add d1 quickly 
	add.l	d0,d1
	add.l	d0,d0
	add.l	d0,d0
	add.l	d1,d0
	addq.w	#1,a3
	bra.s	StartNumber
EndNumber
	rts

************	Execute given CLI commands (a3) = line to parse
* ParseCLI *
************	returns d0=-1, CLI command ok, d0=0, bad CLI command
ParseCLI
	lea	MyWindow,a0		quick reference for altering structure

GetNextChr
	move.b	(a3)+,d0		Parse CLI command
	beq	EndOfCLI
	cmp.b	#" ",d0			Ignore spaces
	beq.s	GetNextChr
	cmp.b	#"	",d0
	beq.s	GetNextChr
	cmp.b	#"-",d0			check for switches
	beq.s	DoSwitches
	bra.s	BadCLI

DoSwitches
	move.b	(a3)+,d0		Set switches
	beq.s	EndOfCLI
	cmp.b	#" ",d0
	beq.s	GetNextChr
	cmp.b	#"	",d0
	beq.s	GetNextChr
	and.w	#$5f,d0			conversion from lower to upper case
	cmp.w	#"B",d0
	bne.s	NotOptB
	bsr	GetCLInumber		-b(number of blocks)
	move.l	d0,AddDefaultSectors+2
	bra.s	DoSwitches

NotOptB	cmp.w	#"X",d0
	bne.s	NotOptX
	bsr	GetCLInumber		-x(x position of window)
	move.w	d0,nw_LeftEdge(a0)
	bra.s	DoSwitches

NotOptX	cmp.w	#"Y",d0
	bne.s	NotOptY
	bsr	GetCLInumber		-y(y position of window)
	move.w	d0,nw_TopEdge(a0)
	bra.s	DoSwitches

NotOptY	cmp.w	#"L",d0
	bne.s	BadCLI
	clr.b	GadgetSmall		-l (large window)

	move.w	#WindowBigX,nw_Width(a0)	Change window sizes
	move.w	#WindowBigY,nw_Height(a0)
	cmp.w	#220,nw_LeftEdge(a0)	if window x not changed, then move
	bne.s	DoSwitches		back a bit, so that it will open
	move.w	#216,nw_LeftEdge(a0)
	bra.s	DoSwitches

EndOfCLI
	moveq	#-1,d0			exit - CLI command OK
	rts

BadCLI	move.l	$4.w,a6
	move.l	DosBase,a6		Get CLI output file handle
	jsr	Output(a6)

	move.l	d0,d1
	move.l	#BadCLIMsg,d2		write message to CLI window
	move.l	#BadEndMsg-BadCLIMsg,d3
	jsr	Write(a6)

	move.l	a6,a1			close dos.library
	move.l	$4.w,a6
	jsr	CloseLibrary(a6)

	moveq	#0,d0			exit - error
	rts


WorkbenchMsg	dc.l	0		Workbench calling message

BadCLIMsg
	dc.b	"CacheClock [-options]",10
	dc.b	"	-b number	Default number of blocks",10
	dc.b	"	-l		Large window",10
	dc.b	"	-x number	Default X co-ordinate",10
	dc.b	"	-y number	Default Y co-ordinate",10,10
	dc.b	"E.g. CacheClock -b500 -lx200 -y10",10,10,0
BadEndMsg
DosName		dc.b	"dos.library",0
	even					word aling program counter

	IFD	DETATCH
;for detatching version, want main part in a separate hunk
	SECTION	Program,CODE
	ENDC

_main	move.l	a7,StartSP		Save start SP
	lea	IntName(pc),a1
	move.l	$4.w,a6
	jsr	OpenLibrary(a6)
	move.l	d0,IntBase
	beq	NoInt

	lea	GfxName(pc),a1
	jsr	OpenLibrary(a6)
	move.l	d0,GfxBase
	beq	NoGfx

	bsr	GetTrackdiskBase	Get trackdisk library
	tst.l	d0
	beq.s	Exit

	bsr	OpenProgramPort		check to see if already active
	bne	Exit

	bsr	OpenMyWindow		Open window, & draw it if needed
	beq.s	ExitPort

	jsr	OpenMyTimer
	beq.s	ExitTimer

	bsr	AddDefaultSectors	Get 256 blocks
	move.b	GadgetSmall(pc),d0
	bne.s	NoBigWind
	bsr	DrawBigWindow
NoBigWind
	bsr	RefreshStats		redraw window stats if big window

	bsr	PatchTrackdisk		Install trackdisk patch


CheckGadgetsLoop
	bsr	CheckGadgets
	tst.l	d0			Close window pressed ?
	bne.s	FreeAllSectors

	lea	TimerPort(pc),a0	Check for timer interrupt
	move.l	$4.w,a6
	jsr	GetMsg(a6)

	tst.l	d0
	beq.s	NotTimerInt
	bsr	SetTimer		reset timer for one second
NotTimerInt
	bsr	RefreshStats
	move.l	Signals(pc),d0
	move.l	$4.w,a6
	jsr	Wait(a6)
	bra.s	CheckGadgetsLoop


FreeAllSectors
	bsr	FreeSector		Free memory used by program
	tst.l	d0
	bne.s	FreeAllSectors

	bsr	CloseMyTimer
ExitTimer
	bsr	CloseMyWindow
ExitPort
	bsr	CloseProgramPort
	bsr	RestoreTrackdisk



Exit	move.l	GfxBase(pc),a1		Close used libraries
	move.l	$4.w,a6
	jsr	CloseLibrary(a6)

NoGfx	move.l	IntBase(pc),a1
	move.l	$4.w,a6
	jsr	CloseLibrary(a6)

NoInt	move.l	StartSP(pc),a7

;for detatching version, free memory used by second part of program, and
;then do a rts. This can be done easily by Jumping to a UnloadSeg call
;We cannot JSR to UnLoadSeg, then rts, since this code area will not be
;allocated, and a crash may occur.
	IFD	DETATCH
	move.l	#_main-4,d1		make BPTR to first section
	lsr.l	#2,d1
	move.l	DosBase(pc),a6
	jmp	UnLoadSeg(a6)		free memory used by detached process
	ENDC

;for non-detaching version, just do a rts
	IFND	DETATCH
	rts
	ENDC

*******************	Opens program port, & checks to see if program
* OpenProgramPort *	is already active (quits if so)
*******************	returns d0=0 port opened ok
OpenProgramPort
	move.l	$4.w,a6
	jsr	Forbid(a6)

	lea	PortName(pc),a1
	jsr	FindPort(a6)

	move.l	d0,d4
	bne.s	AlreadyActive

	lea	ProgramPort(pc),a2
	bsr	InitPort
	tst.l	d0
	beq.s	AlreadyActive

	move.l	#PortName,LN_NAME(a2)
	move.l	a2,a1
	move.l	$4.w,a6
	jsr	AddPort(a6)
	moveq	#0,d4

AlreadyActive
	jsr	Permit(a6)
	move.l	d4,d0
	rts


********************	Closes the main programs port & frees it's signal
* CloseProgramPort *
********************
CloseProgramPort
	move.l	$4.w,a6
	jsr	Forbid(a6)

	lea	ProgramPort(pc),a1	Remove port
	jsr	RemPort(a6)

	moveq	#0,d0			free signal bit used by port
	move.b	MP_SIGBIT(a1),d0
	lea	ProgramPort(pc),a1
	jsr	FreeSignal(a6)
	jmp	Permit(a6)

ProcessName	dc.b	"CacheClock",0
ScreenName	dc.b	"CacheClock Disk accelerator and Clock",0
TitleName	dc.b	"CacheClock V1.1 © 1993 Justin Leck",0
GfxName		dc.b	"graphics.library",0
IntName		dc.b	"intuition.library",0

FontName	dc.b	"topaz.font",0
PortName	dc.b	"CacheClock.port",0
TimerName	dc.b	"timer.device",0
TrackName	dc.b	"trackdisk.device",0
		even

****************	Open the programs window & font
* OpenMyWindow *
****************	returns d0=0, window open bad
OpenMyWindow
	lea	MyWindow(pc),a0
	move.l	IntBase(pc),a6
	jsr	OpenWindow(a6)

	move.l	d0,Window
	beq.s	BadOpen
	move.l	Window(pc),a0
	move.l	wd_RPort(a0),RastPort	Get windows rastport
	move.l	wd_UserPort(a0),a0
	moveq	#0,d0
	move.b	$f(a0),d0
	moveq	#1,d1
	asl.l	d0,d1
	move.l	d1,Signals		Get windows signal

	lea	FontAttr(pc),a0		Open Topaz 8 font
	move.l	GfxBase(pc),a6
	jsr	OpenFont(a6)
	move.l	d0,TopazFont
	beq.s	NoFont

	move.l	d0,a0
	move.l	RastPort(pc),a1		use that font
	move.l	GfxBase(pc),a6
	jsr	SetFont(a6)
NoFont
	move.l	a2,-(a7)
	move.l	Window(pc),a0
	lea	TitleName(pc),a1
	lea	ScreenName(pc),a2
	move.l	IntBase(pc),a6
	jsr	SetWindowTitles(a6)
	move.l	(a7)+,a2

BadOpen	move.l	Window(pc),d0
	rts

*****************	Shut my window & font
* CloseMyWindow *
*****************
CloseMyWindow
	move.l	Window(pc),d0
	beq.s	NoWind

	move.l	$4.w,a6

DelNMsg	move.l	Window(pc),a0		Reply to all unanswered messages
	move.l	wd_UserPort(a0),a0
	jsr	GetMsg(a6)

	tst.l	d0
	beq.s	LastMsg
	move.l	d0,a1
	jsr	ReplyMsg(a6)
	bra.s	DelNMsg
LastMsg	

	move.l	Window(pc),a0
	move.l	IntBase(pc),a6
	jsr	CloseWindow(a6)

NoWind	move.l	TopazFont(pc),d0
	beq.s	NoFnt
	move.l	d0,a1
	move.l	GfxBase(pc),a6
	jsr	CloseFont(a6)

NoFnt	rts


************	Start timer running & request signal in 1 second
* SetTimer *
************
SetTimer
	lea	TimerIOreq(pc),a1
	move.w	#TR_ADDREQUEST,IO_COMMAND(a1)
	move.l	#1,IOTV_TIME+TV_SECS(a1)
	clr.l	IOTV_TIME+TV_MICRO(a1)
	move.l	$4.w,a6
	jmp	SendIO(a6)


***************	Opens timer B, and sets it to interrupt every 2 seconds
* OpenMyTimer *
***************	returns d0=0, open timer failed
OpenMyTimer
	lea	TimerPort(pc),a2		Set up port for IO request
	bsr	InitPort
	tst.l	d0
	beq.s	NoTimePort

	lea	TimerIOreq(pc),a1
	move.b	#NT_MESSAGE,LN_TYPE(a1)		set up IO request structure
	move.l	a2,MN_REPLYPORT(a1)
	move.w	#IOSTD_SIZE,MN_LENGTH(a1)

	lea	TimerName(pc),a0	Open timer.device
	moveq	#1,d0
	moveq	#0,d1
	move.l	$4.w,a6
	jsr	OpenDevice(a6)
	tst.l	d0
	bne.s	NoTimerDevice

	lea	TimerPort(pc),a0	Get Timers signal
	moveq	#0,d0
	move.b	$f(a0),d0
	moveq	#1,d1
	asl.l	d0,d1
	or.l	d1,Signals

	bsr	SetTimer		Set timer to interrupt in 2 secs

	moveq	#1,d0
NoTimePort
	rts

NoTimerDevice
	lea	TimerPort(pc),a0
	moveq	#0,d0			free signal bit used by port
	move.b	MP_SIGBIT(a2),d0
	move.l	$4.w,a6
	jmp	FreeSignal(a6)


****************	Shuts down the use of timer.device
* CloseMyTimer *
****************
CloseMyTimer
	lea	TimerIOreq(pc),a1	Timer still doing IO
	move.l	$4.w,a6
	jsr	CheckIO(a6)

	tst.l	d0
	bne.s	FreeTimerMsg
	lea	TimerIOreq(pc),a1	yes, so abort it
	move.l	$4.w,a6
	jsr	AbortIO(a6)

FreeTimerMsg
	lea	TimerPort(pc),a0	Free messages used by port
	jsr	GetMsg(a6)
	tst.l	d0
	bne.s	FreeTimerMsg

	lea	TimerIOreq(pc),a1	close timer.device
	move.l	$4.w,a6
	jsr	CloseDevice(a6)

	lea	TimerPort(pc),a0
	moveq	#0,d0			free signal bit used by port
	move.b	MP_SIGBIT(a2),d0
	jmp	FreeSignal(a6)


************	Sets up port a2 and grabs a signal for it
* InitPort *
************
InitPort
	clr.l	LN_NAME(a2)
	clr.b	LN_PRI(a2)
	move.b	#NT_MSGPORT,LN_TYPE
	clr.b	MP_FLAGS(a2)

	moveq	#-1,d0
	move.l	$4.w,a6
	jsr	AllocSignal(a6)
	cmp.l	#$ffffffff,d0
	beq.s	NoSignals
	move.b	d0,MP_SIGBIT(a2)

	sub.l	a1,a1
	move.l	$4.w,a6
	jsr	FindTask(a6)

	move.l	d0,MP_SIGTASK(a2)
	lea	MP_MSGLIST(a2),a0
	move.l	a0,(a0)
	addq.l	#4,(a0)
	clr.l	LH_TAIL(a0)
	move.l	a0,LH_TAILPRED(a0)

	move.l	a2,d0
	rts
NoSignals
	moveq	#0,d0
	rts


********************	Gets library base for trackdisk.device
* GetTrackdiskBase *
********************
GetTrackdiskBase
	move.l	a2,-(sp)

	lea	TrackdiskPort(pc),a2		Set up port for IO request
	bsr	InitPort
	tst.l	d0
	beq.s	BadTrackPort

	lea	TrackdiskIOreq(pc),a1
	move.b	#NT_MESSAGE,LN_TYPE(a1)		set up IO request structure
	move.l	a2,MN_REPLYPORT(a1)
	move.w	#IOSTD_SIZE,MN_LENGTH(a1)

	lea	TrackName(pc),a0		open trackdisk.device
	moveq	#0,d0
	moveq	#0,d1
	move.l	$4.w,a6
	jsr	OpenDevice(a6)
	tst.l	d0
	bne.s	BadTrackdisk

	lea	TrackdiskIOreq(pc),a0		Get trackdisk library offsets
	move.l	IO_DEVICE(a0),TrackBase

	lea	TrackdiskIOreq(pc),a1		close trackdisk.device
	move.l	$4.w,a6
	jsr	CloseDevice(a6)

BadTrackdisk
	moveq	#0,d0			free signal bit used by port
	move.b	MP_SIGBIT(a2),d0
	jsr	FreeSignal(a6)

	move.l	TrackBase(pc),d0
BadTrackPort
	move.l	(sp)+,a2
	rts


******************
* PatchTrackdisk *
******************
PatchTrackdisk
	move.l	$4.w,a6		Disable system
	jsr	Forbid(a6)
	jsr	Disable(a6)

	move.l	TrackBase(pc),a1	Install trackdisk patch
	lea	-$1e.w,a0
	move.l	#NewTrackDisk,d0
	jsr	SetFunction(a6)
	move.l	d0,OldTrackDisk+2

	jsr	Enable(a6)	Enable system
	jmp	Permit(a6)


********************
* RestoreTrackdisk *
********************
RestoreTrackdisk
	move.l	$4.w,a6		Disable system
	jsr	Forbid(a6)
	jsr	Disable(a6)

	move.l	TrackBase(pc),a1	restore trackdisk
	lea	-$1e.w,a0
	move.l	OldTrackDisk+2(pc),d0
	jsr	SetFunction(a6)

	jsr	Enable(a6)	Enable system
	jmp	Permit(a6)

DrawSmall	rts

*****************	Draws the outline for the big window
* DrawBigWindow *
*****************
DrawBigWindow
	move.b	GadgetSmall(pc),d0
	bne.s	DrawSmall

	bsr	SetColour0		Clear big window
	move.l	Window(pc),a0
	moveq	#4,d0
	moveq	#$c,d1
	move.w	wd_Width(a0),d2
	subq.w	#8,d2
	move.w	wd_Height(a0),d3
	subq.w	#3,d3
	move.l	RastPort(pc),a1
	move.l	GfxBase,a6
	jsr	RectFill(a6)

	moveq	#$6,d0			Draw infotext panel
	moveq	#$c,d1
	moveq	#$5a,d2
	moveq	#$46,d3
	bsr	DrawPanel

	moveq	#$a,d0			Draw infotext underline
	moveq	#$1b,d1
	move.l	#$52,d2
	moveq	#1,d3
	bsr	DrawWb2Box

	moveq	#$64,d0			Draw drive stats panel
	moveq	#$c,d1
	move.l	#$f0,d2
	moveq	#$46,d3
	bsr	DrawPanel

	moveq	#$68,d0			draw drive stats underline
	moveq	#$1b,d1
	move.l	#$e8,d2
	moveq	#1,d3
	bsr	DrawWb2Box

	move.l	#$158,d0		Draw panel for totals
	moveq	#$c,d1
	moveq	#$4a,d2
	moveq	#$46,d3
	bsr	DrawPanel

	lea	InfoTx(pc),a2		Draw all text onto window
	bsr	DrawTexts

	move.l	#$15c,d0
	moveq	#$1b,d1
	moveq	#$42,d2
	moveq	#$1,d3
	bsr	DrawWb2Box

	moveq	#$a,d0			Fewer box
	moveq	#$57,d1
	moveq	#$54,d2
	moveq	#$c,d3
	bsr	DrawWb2Box

	move.l	#$15b,d0		More box
	moveq	#$57,d1
	moveq	#$42,d2
	moveq	#$c,d3
	bsr	DrawWb2Box

	moveq	#$64,d0			Time & memory box
	moveq	#$56,d1
	move.l	#$f0,d2
	moveq	#$e,d3
	bra	DrawPanel

****************	Check for gadget presses, and timer interrupts
* CheckGagdets *
****************	returns d0 = 0, close window pressed
CheckGadgets
	movem.l	d4/a2/a3,-(a7)

NextWindowMsg
	move.l	Window(pc),a0
	move.l	wd_UserPort(a0),a0
	move.l	$4.w,a6
	jsr	GetMsg(a6)

	tst.l	d0
	beq	CheckMoreFew
	move.l	d0,a2
	move.l	d0,a1
	move.l	im_Class(a1),d4
	move.l	im_IAddress(a1),a3
	jsr	ReplyMsg(a6)

	cmp.l	#GADGETDOWN,d4			Check for fewer/more pressed
	bne.s	NotGadDown
	cmp.w	#MoreGadget,gg_GadgetID(a3)
	bne.s	NoMoreP
	st	MorePressed
NoMoreP	cmp.w	#FewerGadget,gg_GadgetID(a3)
	bne.s	NotGadDown
	st	FewerPressed

NotGadDown
	cmp.l	#GADGETUP,d4			Check for fewer/more released
	bne.s	NotGadgetUp
	cmp.w	#MoreGadget,gg_GadgetID(a3)
	bne.s	NoMoreR
	clr.b	MorePressed
NoMoreR	cmp.w	#FewerGadget,gg_GadgetID(a3)
	bne.s	NotGadgetUp
	clr.b	FewerPressed

NotGadgetUp
	cmp.l	#MENUPICK,d4
	bne.s	NotMenuPick
	move.b	GadgetSmall(pc),d0
	beq.s	ToSmall
	bsr	ExpandWindow		go big window
	bra.s	NotMenuPick
ToSmall	bsr	ShrinkWindow		go small window

NotMenuPick
	cmp.l	#CLOSEWINDOW,d4			Check for Close window
	beq.s	CloseWind

CheckMoreFew
	move.b	MorePressed(pc),d0	Check for more pressed / released
	beq.s	NoChkMore
	btst	#7,MoreGadgFlags+1
	bne.s	MoreHit
	clr.b	MorePressed		more released
	bra.s	NoChkFew
MoreHit	bsr	AddSector		more still pressed

Refresh	bsr	RefreshStats
	moveq	#1,d1
	move.l	DosBase(pc),a6
	jsr	Delay(a6)
	bra	NextWindowMsg

FewerHit
	bsr	FreeSector		fewer still pressed
	bra.s	Refresh

NoChkMore
	move.b	FewerPressed(pc),d0	Check for fewer pressed / released
	beq.s	NoChkFew
	btst	#7,FewerGadgFlags+1
	bne.s	FewerHit
	clr.b	FewerPressed		fewer released
NoChkFew
	moveq	#0,d0
	bra.s	EndMsgs

CloseWind
	moveq	#1,d0
EndMsgs	movem.l	(a7)+,d4/a2/a3
	rts

*************	Draws a panel at (d0,d1), width (d2,d3) & clears the insides
* DrawPanel *
*************
DrawPanel
	movem.l	d0/d1/d2/d3,-(sp)
	bsr	DrawWb2Box
	movem.l	(sp),d0/d1/d2/d3
	addq.w	#1,d0
	addq.w	#1,d1
	subq.w	#2,d2
	subq.w	#2,d3
	bsr	DrawWb2Box

	bsr	SetColour0		Clear box just drawn
	movem.l	(sp)+,d0/d1/d2/d3
	add.w	d0,d2
	add.w	d1,d3
	addq.w	#3,d0
	addq.w	#2,d1
	subq.w	#3,d2
	subq.w	#2,d3
	move.l	RastPort(pc),a1
	move.l	GfxBase,a6
	jmp	RectFill(a6)


*************	Displays onscreen a number of texts, from (a2)+
* DrawTexts *
*************
DrawTexts
	bsr	SetColour1
DrawNextText
	moveq	#0,d0
	move.w	(a2)+,d0
	cmp.w	#-1,d0
	beq.s	EndDrawTexts
	moveq	#0,d1
	move.w	(a2)+,d1
	move.l	RastPort(pc),a1
	move.l	GfxBase(pc),a6
	jsr	Move(a6)		move to start of text

	move.l	a2,a0
ToEndSt	tst.b	(a2)+
	bne.s	ToEndSt
	move.l	a2,d0
	sub.l	a0,d0
	move.w	d0,d1		word align a0 to next text to draw
	and.w	#1,d1
	add.w	d1,a2

	subq.w	#1,d0
	move.l	RastPort(pc),a1
	move.l	GfxBase(pc),a6
	jsr	Text(a6)
	bra.s	DrawNextText

EndDrawTexts
	rts


InfoTx	dc.w	$1f,$18
	dc.b	"Drive",0
	dc.w	$1f,$26
	dc.b	"Reads",0
	dc.w	$1b,$30
	dc.b	"Writes",0
	dc.w	$17,$3a
	dc.b	"ReadHits",0
	dc.w	$b,$44
	dc.b	"Percentage",0
	dc.w	$15,$4e
	dc.b	"Buffers",0

	dc.w	$80,$18
	dc.b	"A",0
	dc.w	$ba,$18
	dc.b	"B",0
	dc.w	$f4,$18
	dc.b	"C",0
	dc.w	$12e,$18
	dc.b	"D",0

	dc.w	$21,$60
	dc.b	"Fewer",0

	dc.w	$16d,$60
	dc.b	"More",0

	
	dc.w	$169,$19
	dc.b	"Total",0
	dc.w	-1


SpacesTx	dc.b	"       ",0

TimeMemoryTx	dc.b	"Free:"
MemoryStore	dc.b	"????????  Time:"
TimeStore	dc.b	"??:??:??"

SmallMenu	dc.b	" Chip:"
ChipAmount	dc.b	"???? Fast:"
FastAmount	dc.b	"????? Time:"
SmallTime	dc.b	"??:??:?? "

nums	dc.l	100000000,10000000,1000000,100000,10000,1000,100,10,1,-1

**************	writes number d0 into buffer (a1)
* TextNumber *	leading zeros are omitted
**************	returns d0 = length of string
TextNumber
	movem.l	d6/d7,-(sp)
	move.l	a1,-(sp)

	moveq	#0,d7			d7 = 1, include zeros
	lea	nums(pc),a0

NextDivisor
	move.l	(a0)+,d1
	bmi.s	NumberDone

	moveq	#"0",d6
CompareAgain
	cmp.l	d1,d0
	blt.s	NoSubtract
	addq.w	#1,d6
	sub.l	d1,d0
	moveq	#1,d7
	bra.s	CompareAgain
NoSubtract
	tst.w	d7
	beq.s	NextDivisor
	move.b	d6,(a1)+		Add digit to display buffer
	bra.s	NextDivisor
NumberDone
	tst.w	d7
	bne.s	NumberNot0
	move.b	#"0",(a1)+
NumberNot0
	clr.b	(a1)
	sub.l	(sp)+,a1
	move.l	a1,d0
	movem.l	(sp)+,d6/d7
	rts

NumberBuffer	ds.b	10

**********************	Displays number (d2) at (d0,d1)
* PrintCentralNumber *
**********************
PrintCentralNumber
	movem.l	d0/d1/d2/d3,-(sp)

	move.l	RastPort(pc),a1		Clear space for number to go in
	move.l	GfxBase,a6
	jsr	Move(a6)

	lea	SpacesTx(pc),a0
	moveq	#7,d0
	move.l	RastPort(pc),a1
	move.l	GfxBase(pc),a6
	jsr	Text(a6)

	move.l	$8(sp),d0		Convert number to ascii
	lea	NumberBuffer(pc),a1
	bsr	TextNumber
	move.l	d0,d3

	lea	NumberBuffer(pc),a0	Get length of string in pixels
	move.l	RastPort(pc),a1
	move.l	GfxBase(pc),a6
	jsr	TextLength(a6)

	lsr.l	#1,d0

	moveq	#$3a,d1
	sub.l	d0,d1
	move.l	d1,d0

	add.l	(sp),d0
	sub.l	#$1e,d0
	move.l	4(sp),d1
	move.l	RastPort(pc),a1
	move.l	GfxBase,a6
	jsr	Move(a6)

	lea	NumberBuffer(pc),a0
	move.l	d3,d0
	move.l	RastPort(pc),a1
	move.l	GfxBase(pc),a6
	jsr	Text(a6)
	movem.l	(sp)+,d0/d1/d2/d3
	rts


**************	converts system date into ascii values from (a0)+
* GetSysTime *
**************
GetSysTime
	move.l	a0,-(sp)
	move.l	#Date,d1
	move.l	DosBase(pc),a6
	jsr	DateStamp(a6)
	lea	Date(pc),a0
	move.l	(sp)+,a1

	move.l	ds_Minute(a0),d0
	moveq	#"0",d1			Work out tens of hours
CalcHT	cmp.l	#600,d0
	blt.s	EndHrT
	addq.w	#1,d1
	sub.l	#600,d0
	bra.s	CalcHT
EndHrT	move.b	d1,(a1)
	moveq	#"0",d1
CalcHrs	cmp.l	#60,d0			work out hours
	blt.s	EndHrs
	addq.w	#1,d1
	sub.l	#60,d0
	bra.s	CalcHrs
EndHrs	move.b	d1,1(a1)
	moveq	#"0",d1
CalcMT	cmp.l	#10,d0			work out tens of minutes
	blt.s	EndMinT
	addq.w	#1,d1
	sub.l	#10,d0
	bra.s	CalcMT
EndMinT	move.b	d1,3(a1)
	add.w	#$30,d0			get minutes
	move.b	d0,4(a1)

	move.l	ds_Tick(a0),d0
	moveq	#"0",d1
CalcST	cmp.l	#500,d0			work out tens of seconds
	blt.s	EndSecT
	addq.w	#1,d1
	sub.l	#500,d0
	bra.s	CalcST
EndSecT	move.b	d1,6(a1)
	moveq	#"0",d1
CalcSec	cmp.l	#50,d0			work out seconds
	blt.s	EndSecs
	addq.w	#1,d1
	sub.l	#50,d0
	bra.s	CalcSec
EndSecs	move.b	d1,7(a1)
	rts


****************	Redraws numbers on screen if needed
* RefreshStats *
****************
RefreshStats
	bsr	SetColour1
	move.b	GadgetSmall(pc),d0
	bne.s	SmallRefresh

	bsr	DisplayMemoryTime
	bsr	DisplayReads
	bsr	DisplayWrites
	bsr	DisplayReadHits
	bsr	DisplayBuffers
	rts
SmallRefresh
	lea	ChipAmount(pc),a0
	moveq	#4,d0
ClrMems	move.b	#" ",0(a0,d0.w)
	move.b	#" ",FastAmount-ChipAmount+1(a0,d0.w)
	dbra	d0,ClrMems
	move.b	#" ",FastAmount-ChipAmount(a0)

	moveq	#2,d1			Get amount of chip ram
	move.l	$4.w,a6
	jsr	AvailMem(a6)
	moveq	#10,d1
	lsr.l	d1,d0
	lea	ChipAmount(pc),a1	Copy Chip amount
	bsr	TextNumber
	lea	ChipAmount(pc),a1	change \0 to a space
	move.b	#" ",0(a1,d0.w)

	moveq	#4,d1			Get amount of fast ram
	move.l	$4.w,a6
	jsr	AvailMem(a6)
	moveq	#10,d1
	lsr.l	d1,d0
	lea	FastAmount(pc),a1	Copy fast amount
	bsr	TextNumber
	lea	FastAmount(pc),a1
	move.b	#" ",0(a1,d0.w)		change \0 to a space

	lea	SmallTime(pc),a0
	bsr	GetSysTime
	moveq	#$1d,d0
	moveq	#$7,d1
	move.l	RastPort(pc),a1
	move.l	GfxBase,a6
	jsr	Move(a6)


	lea	SmallMenu(pc),a0	Display time & memory left
	moveq	#36,d0
	move.l	RastPort(pc),a1
	move.l	GfxBase(pc),a6
	jmp	Text(a6)


*********************	Displays memory, time and totals
* DisplayMemoryTime *
*********************
DisplayMemoryTime
	movem.l	d2/d4,-(sp)
	lea	MemoryStore(pc),a0	Clear last memory count
	moveq	#8,d0
ClrMemSt
	move.b	#" ",0(a0,d0.w)
	dbra	d0,ClrMemSt

	moveq	#2,d1			Get amount of chip ram
	move.l	$4.w,a6
	jsr	AvailMem(a6)
	move.l	d0,d4
	moveq	#4,d1			Add to the amount of fast ram
	jsr	AvailMem(a6)
	add.l	d4,d0

	lea	MemoryStore(pc),a1	Copy memory number
	bsr	TextNumber
	lea	MemoryStore(pc),a1	Copy memory number
	move.b	#" ",0(a1,d0.w)

	lea	TimeStore(pc),a0
	bsr	GetSysTime

	moveq	#$6c,d0
	moveq	#$60,d1
	move.l	RastPort(pc),a1
	move.l	GfxBase,a6
	jsr	Move(a6)


	lea	TimeMemoryTx(pc),a0	Display time & memory left
	moveq	#28,d0
	move.l	RastPort(pc),a1
	move.l	GfxBase(pc),a6
	jsr	Text(a6)


	move.l	TotalReads(pc),d2	Display number of reads if changed
	move.b	ForcePrint(pc),d0		Must print ?
	bne.s	FrceRds
	cmp.l	LastTotalReads(pc),d2
	beq.s	ReadsSame
FrceRds	move.l	d2,LastTotalReads

	move.l	#$161,d0
	moveq	#$26,d1
	bsr	PrintCentralNumber

ReadsSame
	move.l	TotalWrites(pc),d2	Display number of writes if changed
	move.b	ForcePrint(pc),d0		Must print ?
	bne.s	FrceWts
	cmp.l	LastTotalWrites(pc),d2
	beq.s	WritesSame
FrceWts	move.l	d2,LastTotalWrites

	move.l	#$161,d0
	moveq	#$30,d1
	bsr	PrintCentralNumber

WritesSame
	move.l	TotalRHits(pc),d2	Display number of readhits if changed
	move.b	ForcePrint(pc),d0		Must print ?
	bne.s	FrceRHt
	cmp.l	LastTotalRHits(pc),d2
	beq.s	RHitsSame
FrceRHt	move.l	d2,LastTotalRHits

	move.l	#$161,d0
	moveq	#$3a,d1
	bsr	PrintCentralNumber

RHitsSame

	move.l	NumBlocks(pc),d2	Display number of blocks if changed
	move.b	ForcePrint(pc),d0		Must print ?
	bne.s	FrceBlk
	cmp.l	LastNumBlocks(pc),d2
	beq.s	BlocksSame
FrceBlk	move.l	d2,LastNumBlocks

	move.l	#$161,d0
	moveq	#$4e,d1
	bsr	PrintCentralNumber

BlocksSame
	movem.l	(sp)+,d2/d4
	rts

****************	Displays the number of Reads
* DisplayReads *
****************
DisplayReads
	movem.l	d2/d4,-(a7)
	moveq	#0,d4

DispNxtRead
	move.l	d4,d0
	add.l	d0,d0
	add.l	d0,d0
	lea	NumReads(pc),a0
	move.l	0(a0,d0.w),d2

	move.b	ForcePrint(pc),d1		Must print ?
	bne.s	FrceRd

	cmp.l	$10(a0,d0.w),d2
	beq.s	NoReadChange
FrceRd	move.l	d2,$10(a0,d0.w)

	move.l	d4,d0			Work out X position
	mulu	#$3a,d0
	add.l	#$68,d0
	move.l	d0,-(sp)		Save X pos for printing percentage
	moveq	#$26,d1
	bsr	PrintCentralNumber

	move.l	d4,d0			Work out percentage
	add.l	d0,d0			= ReadHits*100/Reads
	add.l	d0,d0
	lea	NumReads(pc),a0
	move.l	0(a0,d0.l),d1		Trap for division by 0
	beq.s	Reads0
	move.l	d1,-(sp)
	lea	NumReadHits(pc),a0
	move.l	0(a0,d0.l),d0
	moveq	#100,d1
	jsr	LongMult
	move.l	d0,d1
	move.l	(sp)+,d0
	jsr	LongDivide
	move.l	d0,d2
	bra.s	ReadsN
Reads0	moveq	#0,d2

ReadsN	move.l	(sp)+,d0		Display percentage
	moveq	#$44,d1
	bsr	PrintCentralNumber

NoReadChange
	addq.l	#1,d4
	cmp.l	#4,d4
	bne.s	DispNxtRead
	movem.l	(a7)+,d2/d4
	rts

*****************	Displays the number of Writes
* DisplayWrites *
*****************
DisplayWrites
	movem.l	d2/d4,-(a7)
	moveq	#0,d4

DispNxtWrite
	move.l	d4,d0
	asl.l	#2,d0
	lea	NumWrites(pc),a0
	move.l	0(a0,d0.w),d2

	move.b	ForcePrint(pc),d1		Must print ?
	bne.s	FrceWte

	cmp.l	$10(a0,d0.w),d2
	beq.s	NoWriteChange
FrceWte	move.l	d2,$10(a0,d0.w)

	move.l	d4,d0
	mulu	#$3a,d0
	add.l	#$68,d0
	moveq	#$30,d1
	bsr	PrintCentralNumber

NoWriteChange
	addq.l	#1,d4
	cmp.l	#4,d4
	bne.s	DispNxtWrite
	movem.l	(a7)+,d2/d4
	rts

*******************	Displays the number of Read hits
* DisplayReadHits *
*******************
DisplayReadHits
	movem.l	d2/d4,-(a7)
	moveq	#0,d4

DispNxtReadHit
	move.l	d4,d0
	asl.l	#2,d0
	lea	NumReadHits(pc),a0
	move.l	0(a0,d0.w),d2

	move.b	ForcePrint(pc),d1		Must print ?
	bne.s	FrceRdH

	cmp.l	$10(a0,d0.w),d2
	beq.s	NoReadHitChange
FrceRdH	move.l	d2,$10(a0,d0.w)

	move.l	d4,d0
	mulu	#$3a,d0
	add.l	#$68,d0
	moveq	#$3a,d1
	bsr	PrintCentralNumber

NoReadHitChange
	addq.l	#1,d4
	cmp.l	#4,d4
	bne.s	DispNxtReadHit
	movem.l	(a7)+,d2/d4
	rts

******************	Displays the number of buffers
* DisplayBuffers *
******************
DisplayBuffers
	movem.l	d2/d4,-(a7)
	moveq	#0,d4

DispNxtBuf
	move.l	d4,d0
	asl.l	#2,d0
	lea	NumBuffers(pc),a0
	move.l	0(a0,d0.w),d2

	move.b	ForcePrint(pc),d1		Must print ?
	bne.s	FrceBuf

	cmp.l	$10(a0,d0.w),d2
	beq.s	NoBufChange
FrceBuf	move.l	d2,$10(a0,d0.w)

	move.l	d4,d0
	mulu	#$3a,d0
	add.l	#$68,d0
	moveq	#$4e,d1
	bsr	PrintCentralNumber

NoBufChange
	addq.l	#1,d4
	cmp.l	#4,d4
	bne.s	DispNxtBuf
	movem.l	(a7)+,d2/d4
	rts


**************	Draws a workbench 2 style box at (d0,d1) width (d2,d3)
* DrawWb2Box *
**************
DrawWb2Box
	add.l	d0,d2
	add.l	d1,d3
	movem.l	d0/d1/d2/d3,-(sp)

	move.l	d3,d1
	move.l	RastPort(pc),a1
	move.l	GfxBase(pc),a6
	jsr	Move(a6)
	bsr	SetColour2
	movem.l	(sp),d0/d1
	move.l	RastPort(pc),a1
	jsr	Draw(a6)
	move.l	8(sp),d0
	move.l	4(sp),d1
	move.l	RastPort(pc),a1
	jsr	Draw(a6)

	bsr	SetColour1
	move.l	RastPort(pc),a1
	movem.l	8(sp),d0/d1
	move.l	RastPort(pc),a1
	jsr	Draw(a6)

	move.l	(sp),d0
	move.l	12(sp),d1
	move.l	RastPort(pc),a1
	jsr	Draw(a6)
	lea	$10(sp),sp
	rts



****************	Changes window to the large one with stats
* ExpandWindow *
****************
ExpandWindow
	move.b	GadgetSmall(pc),d0
	beq	WindowAlreadyBig
	clr.b	GadgetSmall

	move.l	Window(pc),a0
	move.l	wd_WScreen(a0),a1

	move.w	$c(a1),d0		Check for enough room in X direction
	sub.w	wd_LeftEdge(a0),d0
	sub.w	#$1aa,d0
	ext.l	d0
	bmi.s	XposBAD
	moveq	#0,d0
XposBAD

	move.w	$e(a1),d1		Check for enough room in Y direction
	sub.w	wd_TopEdge(a0),d1
	sub.w	#$70,d1
	ext.l	d1
	bmi.s	YposBAD
	moveq	#0,d1
YposBAD
	tst.l	d0
	bne.s	MvWindo
	tst.l	d1
	beq.s	NoMoveWindow

MvWindo	move.l	wd_LeftEdge(a0),-(sp)	Not enough room, so move window

	move.l	IntBase(pc),a6
	jsr	MoveWindow(a6)

CheckWindowMoved
	move.l	Window(pc),a0		Window been moved yet
	move.l	wd_LeftEdge(a0),d0
	cmp.l	(sp),d0
	bne.s	MvdWind
	moveq	#1,d1			no, so wait a tick
	move.l	DosBase(pc),a6
	jsr	Delay(a6)
	bra.s	CheckWindowMoved
MvdWind	addq.w	#4,sp

NoMoveWindow
	moveq	#WindowBigX-WindowSmallX,d0
	moveq	#WindowBigY-WindowSmallY,d1
	move.l	Window(pc),a0
	move.l	IntBase(pc),a6
	jsr	SizeWindow(a6)

WaitExpandWind
	move.l	Window(pc),a0		Wait for window to expand
	cmp.w	#WindowBigX,wd_Width(a0)
	beq.s	WindowExpanded
	moveq	#1,d1
	move.l	DosBase(pc),a6
	jsr	Delay(a6)
	bra.s	WaitExpandWind

WindowExpanded
	bsr	DrawBigWindow
	st	ForcePrint
	bsr	RefreshStats
	clr	ForcePrint

WindowAlreadyBig
	rts

****************	shrinks the window to the small sized one
* ShrinkWindow *
****************
ShrinkWindow
	move.b	GadgetSmall(pc),d0	Is window already small ?
	bne	WindowAlreadySmall
	st	GadgetSmall

	moveq	#WindowSmallX-WindowBigX,d0		No, so shrink it
	moveq	#WindowSmallY-WindowBigY,d1
	move.l	Window(pc),a0
	move.l	IntBase(pc),a6
	jsr	SizeWindow(a6)

WindowAlreadySmall
	rts

**************	Searchs hash list for sector IO_OFFSET(a1), drive d2
* FindSector *	d0 = 0, search failed. d0=1, a0=Sector
**************
FindSector
	move.l	IO_OFFSET(a1),d0
	lsr.l	#7,d0
	and.l	#$1fc,d0
	lea	SectorHeaders(pc),a2
	add.w	d0,a2
	move.l	(a2),d0
SearchHash
	beq.s	NoSector
	move.l	d0,a0
	cmp.w	WhichDrive(a0),d2
	bne.s	WrongSector
	move.l	SectorOffset(a0),d0
	cmp.l	IO_OFFSET(a1),d0
	beq.s	RightSector
WrongSector
	move.l	NextHash(a0),d0
	bra.s	SearchHash
RightSector
	moveq	#1,d0
	rts

NoSector
	rts

****************  Trackdisk patch, so that all read / writes are logged
* NewTrackDisk * and cached if possible
****************
NewTrackDisk
	movem.l	d2/a2,-(a7)

	move.l	IO_UNIT(a1),d1		Add current unit to list of drives
	lea	DevUnits(pc),a0
	moveq	#0,d2			d2 = current drive number * 4
TryNdev	tst.l	(a0)
	bne.s	IsDvice
	move.l	d1,(a0)
	bra.s	FoundDevice

IsDvice	cmp.l	(a0)+,d1
	beq.s	FoundDevice
	addq.w	#4,d2
	cmp.w	#$10,d2
	bne.s	TryNdev
FoundDevice

	cmp.w	#CMD_READ,IO_COMMAND(a1)
	bne.s	NotCMD_READ
	addq.l	#1,TotalReads			reading sector
	lea	NumReads(pc),a0
	addq.l	#1,0(a0,d2.w)

	bsr.s	FindSector			sector in cache ?
	beq.s	IgnoreCMD

	bsr	ReadFromCache			yes, so copy it
	bra	DoneIO

NotCMD_READ
	cmp.w	#CMD_WRITE,IO_COMMAND(a1)
	bne.s	NotCMD_WRITE
	addq.l	#1,TotalWrites			writing sector
	lea	NumWrites(pc),a0
	addq.l	#1,0(a0,d2.w)
	bsr	WriteToCache
	bra.s	IgnoreCMD

NotCMD_WRITE
	cmp.w	#CMD_CLEAR,IO_COMMAND(a1)
	bne.s	IgnoreCMD
	bsr	DiskChange			disk change

IgnoreCMD
	move.l	a1,-(a7)
OldTrackDisk
	jsr	$0
	move.l	(a7)+,a1

	cmp.w	#CMD_READ,IO_COMMAND(a1)
	bne.s	DoneIO
	bsr	SaveReadSector

DoneIO	movem.l	(a7)+,d2/a2
	rts


*************	Adds a sector to the list of sectors
* AddSector *
*************	returns d0=0, ran out of memory
AddSector
	move.l	a2,-(a7)
	move.l	#$10001,d1
	move.l	#SectorSize,d0
	move.l	$4.w,a6
	jsr	AllocMem(a6)

	move.l	d0,a2
	beq.s	NoMemAddSec

	jsr	Forbid(a6)

	move.l	UnusedSectors(pc),NextInList(a2)
	move.l	a2,UnusedSectors

	jsr	Permit(a6)
	addq.l	#1,NumBlocks
	moveq	#1,d0

NoMemAddSec
	move.l	(a7)+,a2
	rts

*********************	Adds 256 sectors to the sector list
* AddDefaultSectors *
*********************
AddDefaultSectors
	move.l	#DefaultBuffers,d2
	beq.s	MemAddErr
AddNxSec
	bsr.s	AddSector
	tst.l	d0
	beq.s	MemAddErr
	subq.l	#1,d2
	bne.s	AddNxSec
MemAddErr
	rts


**************	Frees One sector used by the program
* FreeSector *
**************	returns d0 = sector to free
FreeSector
	move.l	a3,-(sp)
	move.l	$4.w,a6
	jsr	Forbid(a6)

	move.l	UnusedSectors(pc),d0
	beq.s	DeAlcSector
	move.l	d0,a3
	move.l	NextInList(a3),UnusedSectors
	bra.s	FreeSec

DeAlcSector
	move.l	LastSector(pc),d0
	beq.s	NoSects
	move.l	d0,a3
	bsr	RemoveSector
	move.w	WhichDrive(a3),d0
	lea	NumBuffers(pc),a0
	subq.l	#1,0(a0,d0.w)
FreeSec
	subq.l	#1,NumBlocks
	move.l	a3,a1
	move.l	#SectorSize,d0
	jsr	FreeMem(a6)
	move.l	a3,d0

NoSects	jsr	Permit(a6)
	move.l	(sp)+,a3
	rts


**************	Copies $200 bytes from (a0)+ to (a1)+
* CopySector *
**************
CopySector
	movem.l	d0/d1/d2/d3/d4/d5/d6/d7/a2/a3/a4/a5/a6,-(a7)
	REPT	9
	movem.l	(a0)+,d0/d1/d2/d3/d4/d5/d6/d7/a2/a3/a4/a5/a6
	movem.l	d0/d1/d2/d3/d4/d5/d6/d7/a2/a3/a4/a5/a6,(a1)
	lea	$34(a1),a1
	ENDR
	movem.l	(a0)+,d0/d1/d2/d3/d4/d5/d6/d7/a2/a3/a4
	movem.l	d0/d1/d2/d3/d4/d5/d6/d7/a2/a3/a4,(a1)
	movem.l	(a7)+,d0/d1/d2/d3/d4/d5/d6/d7/a2/a3/a4/a5/a6
	rts

**************** Removes Sector a3 from sector list.
* RemoveSector *
****************
RemoveSector
	move.l	NextHash(a3),d0		Remove sector from hash list
	beq.s	NoNxHash
	move.l	d0,a0
	move.l	PrevHash(a3),PrevHash(a0)
NoNxHash
	move.l	PrevHash(a3),a0
	move.l	d0,NextHash(a0)

	move.l	NextInList(a3),d0	Remove sector from sector list
	beq.s	NoNext
	move.l	d0,a0
	move.l	PrevInList(a3),PrevInList(a0)
DoPrev	move.l	PrevInList(a3),d0
	beq.s	NoPriorSec
	move.l	d0,a0
	move.l	NextInList(a3),NextInList(a0)
	rts
NoNext	move.l	PrevInList(a3),LastSector	Deleting last sector in list
	bra.s	DoPrev
NoPriorSec
	move.l	NextInList(a3),FirstSector	Deleting first sector in list
	rts


**************** Inserts sector a3 into cache list at 1st position
* Add1stSector * a2 = Hash chain to add to
****************
Add1stSector
	move.l	(a2),d0		Insert sector into hash chain
	beq.s	No2ndHash
	move.l	d0,a0
	move.l	a3,PrevHash(a0)
No2ndHash
	move.l	a3,(a2)
	move.l	a2,PrevHash(a3)
	move.l	d0,NextHash(a3)

	move.l	FirstSector(pc),d0	Insert sector into sector list

	move.l	a3,FirstSector
	clr.l	PrevInList(a3)
	move.l	d0,NextInList(a3)
	bne.s	Not1stSec
	move.l	a3,LastSector
	rts
Not1stSec
	move.l	d0,a0
	move.l	a3,PrevInList(a0)
	rts


*****************	Read sector from cache
* ReadFromCache *
*****************
ReadFromCache
	movem.l	a3/a4/a6,-(a7)
	move.l	a0,a3
	move.l	a1,a4
	move.l	$4.w,a6
	jsr	Forbid(a6)
	bsr	RemoveSector
	bsr.s	Add1stSector

	lea	Sector(a3),a0
	move.l	IO_DATA(a4),a1
	bsr	CopySector

	clr.b	IO_ERROR(a4)
	move.l	#$200,IO_SIZE(a4)
	addq.l	#1,TotalRHits

	lea	NumReadHits(pc),a0
	addq.l	#1,0(a0,d2.w)

	btst	#1,IO_FLAGS(a4)
	bne.s	NoRdMsg
	and.b	#$fe,IO_FLAGS(a4)	SendIO, so send done message
	move.l	a4,a1
	jsr	ReplyMsg(a6)
NoRdMsg	move.l	a4,a1
	jsr	Permit(a6)
	movem.l	(a7)+,a3/a4/a6
	rts

**************** Copies data from sector to be written into a buffer
* WriteToCache *
****************
WriteToCache
	movem.l	a1/a3/a4/a6,-(a7)
	move.l	a1,a4
	move.l	$4.w,a6
	jsr	Forbid(a6)
	move.l	a4,a1
	bsr	FindSector
	beq.s	WriteToSector

	move.l	a0,a3
	bsr	RemoveSector

InsertWrite
	bsr	Add1stSector

	move.l	IO_DATA(a4),a0
	lea	Sector(a3),a1
	bsr	CopySector
NoWtStrucs
	jsr	Permit(a6)
	movem.l	(sp)+,a1/a3/a4/a6
	rts

WriteToSector
	bsr	AllocateSector
	tst.l	d0
	beq.s	NoWtStrucs
	bra.s	InsertWrite


**************	Disk changed, so flush all buffers used by drive d2
* DiskChange *
**************
DiskChange
	movem.l	a3/a4/a6,-(a7)
	move.l	$4.w,a6
	jsr	Forbid(a6)

	lea	NumBuffers(pc),a4
	lea	0(a4,d2.w),a4

	move.l	FirstSector(pc),a3
FreeNextSec
	move.l	a3,d0
	beq.s	EndDelSectors
	cmp.w	WhichDrive(a3),d2
	bne.s	SkipSector
	subq.l	#1,(a4)
	bsr	RemoveSector
	move.l	NextInList(a3),d0
	move.l	UnusedSectors(pc),NextInList(a3)
	move.l	a3,UnusedSectors
	move.l	d0,a3
	bra.s	FreeNextSec

SkipSector
	move.l	NextInList(a3),a3
	bra.s	FreeNextSec

EndDelSectors
	jsr	Permit(a6)
	movem.l	(a7)+,a3/a4/a6
	rts


******************	Grabs the next free sector, or deallocates the first 
* AllocateSector *	one. Then fills in some info. a4 = IOrequest
******************	returns d0=sector structure (0=error)
AllocateSector
	move.l	UnusedSectors(pc),d0
	beq.s	DeAllocSector
	move.l	d0,a3
	move.l	NextInList(a3),UnusedSectors

FillStrucInfo
	move.l	IO_OFFSET(a4),d0
	move.l	d0,SectorOffset(a3)
	move.w	d2,WhichDrive(a3)

	lsr.l	#7,d0
	and.l	#$1fc,d0
	lea	SectorHeaders(pc),a2
	add.w	d0,a2
	lea	NumBuffers(pc),a0
	addq.l	#1,0(a0,d2.w)
	move.l	a3,d0
NoSectors
	rts

DeAllocSector
	move.l	LastSector(pc),d0
	beq.s	NoSectors
	move.l	d0,a3
	bsr	RemoveSector
	move.w	WhichDrive(a3),d0
	lea	NumBuffers(pc),a0
	subq.l	#1,0(a0,d0.w)
	bra.s	FillStrucInfo


****************** Saves sector being read in
* SaveReadSector *
******************
SaveReadSector
	movem.l	a3/a4/a6,-(sp)
	move.l	a1,a4
	bsr.s	AllocateSector
	tst.l	d0
	beq.s	NoRdStrucs

WaitRdSector
	move.l	a4,a1
	move.l	$4.w,a6
	jsr	CheckIO(a6)
	tst.l	d0
	bne.s	RdDone
	move.l	GfxBase,a6
	jsr	WaitTOF(a6)
	bra.s	WaitRdSector

RdDone
	jsr	Forbid(a6)

	bsr	Add1stSector
	move.l	IO_DATA(a4),a0
	lea	Sector(a3),a1
	bsr	CopySector

NoRdStrucs
	move.l	$4.w,a6
	jsr	Permit(a6)
	movem.l	(sp)+,a3/a4/a6
	rts


SetColour0
	moveq	#0,d0
SetCol	move.l	RastPort(pc),a1
	move.l	GfxBase,a6
	jmp	SetAPen(a6)
SetColour1
	moveq	#1,d0
	bra.s	SetCol
SetColour2
	moveq	#2,d0
	bra.s	SetCol
SetColour3
	moveq	#3,d0
	bra.s	SetCol

**************	divides d1/d0, result in d0, remainder in d1
* LongDivide *
**************	uses shift & subtract method
LongDivide
	movem.l	d2/d3/d4,-(sp)
	move.l	d0,d4
	eor.l	d1,d4		d4 is sign of result

	move.l	d0,d2		make both parameters positive
	beq.s	DivideBy0
	bpl.s	div0p
	neg.l	d2
div0p	tst.l	d1
	bpl.s	div1p
	neg.l	d1
div1p

	moveq	#$1f,d3		transfer divsor to top bit positions
ToEnd	add.l	d2,d2
	dbcs	d3,ToEnd
	roxr.l	#1,d2

	neg.w	d3
	add.w	#$1f,d3		d3 = number of times to shift
	moveq	#0,d0
nextbit	add.l	d0,d0
	cmp.l	d2,d1
	bcs.s	nosub
	addq.w	#1,d0
	sub.l	d2,d1
nosub	lsr.l	#1,d2
	dbra	d3,nextbit
	tst.l	d4
	bpl.s	divpos
	neg.l	d0
divpos	movem.l	(sp)+,d2/d3/d4
	rts
DivideBy0
	moveq	#-1,d0		prevent divide by 0 crashing
	bra.s	divpos

************	calculates d0 = d0 * d1 for long integers
* LongMult *
************
LongMult
	movem.l	d1/d2/d3,-(a7)
	move.w	d1,d2
	mulu	d0,d2
	move.l	d1,d3
	swap	d3
	mulu	d0,d3
	swap	d3
	clr.w	d3
	add.l	d3,d2
	swap	d0
	mulu	d1,d0
	swap	d0
	clr.w	d0
	add.l	d2,d0
	movem.l	(a7)+,d1/d2/d3
	rts


IntBase		dc.l	0
GfxBase		dc.l	0
DosBase		dc.l	0
TrackBase	dc.l	0

StartSP		dc.l	0

FontAttr	dc.l	FontName	font structure for topaz 8
		dc.w	8,0
TopazFont	dc.l	0

FewerButton	dc.l	0		Next gadget
		dc.w	$a,$57,$54,$d	GadX,GadY, widths
FewerGadgFlags	dc.w	0		Flags
		dc.w	3
		dc.w	1
		dc.l	0,0,0,0,0
		dc.w	FewerGadget
		dc.l	0

MoreButton	dc.l	FewerButton	Next gadget
		dc.w	$15b,$57,$43,$d	GadX,GadY, widths
MoreGadgFlags	dc.w	0		Flags
		dc.w	3		Activation
		dc.w	1		Type
		dc.l	0,0		Renders
		dc.l	0,0,0		Intuitext,Mutual Exclude,Special Info
		dc.w	MoreGadget	Gadget ID
		dc.l	0		

MyWindow
	dc.w	220,0				leftedge,TopEdge
	dc.w	WindowSmallX,WindowSmallY	Width,height
	dc.b	0,1				Pens
	dc.l	CLOSEWINDOW+GADGETUP+GADGETDOWN+MENUPICK IDCMP flags
	dc.l	WINDOWDRAG+WINDOWDEPTH+WINDOWCLOSE+NOCAREREFRESH
	dc.l	MoreButton			1st Gadget
	dc.l	0				checkmark
	dc.l	0				title (filled in later)
	dc.l	0,0				current screen,bitmap
	dc.w	0,0,WindowBigX,WindowBigY	Min sizes,Max sizes
	dc.w	1				Workbenchscreen

Window		dc.l	0
MorePressed	dc.b	0
FewerPressed	dc.b	0
Signals		dc.l	0	Signals used by program to wait for

RastPort	dc.l	0

ForcePrint	dc.b	0
GadgetSmall	dc.b	-1

TrackdiskIOreq
TimerIOreq	ds.b	IOSTD_SIZE
TrackdiskPort
TimerPort	ds.b	MP_SIZE
TimerPortOk	dc.l	0

ProgramPort	ds.b	MP_SIZE

Date		ds.b	ds_SIZEOF

DevUnits	dc.l	0,0,0,0

SectorHeaders	ds.l	$80

FirstSector	dc.l	0	1st & last sectors for allocation/deallocation
LastSector	dc.l	0

UnusedSectors	dc.l	0

TotalReads	dc.l	0
LastTotalReads	dc.l	-1
TotalWrites	dc.l	0
LastTotalWrites	dc.l	-1
TotalRHits	dc.l	0
LastTotalRHits	dc.l	-1

NumBlocks	dc.l	0		Number of allocated blocks
LastNumBlocks	dc.l	-1		last number of blocks

NumReads	dc.l	0,0,0,0		Number of reads for drives
		dc.l	-1,-1,-1,-1	last number of reads
NumWrites	dc.l	0,0,0,0
		dc.l	-1,-1,-1,-1
NumReadHits	dc.l	0,0,0,0
		dc.l	-1,-1,-1,-1
NumBuffers	dc.l	0,0,0,0
		dc.l	-1,-1,-1,-1

