Agile File Loader

Hits: 1596

Category: TutorialsAmigaCrackingLoaders
Submitted by: musashi9
Date: 2007-06-22 02:33
No tags
; Disk routine (pinched from Agile - sorry guys)

CIAAPRA	EQU	$bfe001		Cia A port register A
CIABPRB	EQU	$bfd100		Cia B port register B
CIABICR	EQU	$bfdd00		Cia B interrupt control register
CUSTOM	EQU	$dff000		Custom hardware
INTREQR	EQU	$1e		Read interrupt request
DSKPTH	EQU	$20		Disk buffer pointer
DSKLEN	EQU	$24		Disk access length
DSKSYNC	EQU	$7e		Disk sync word
DMACON	EQU	$96		Write DMA control
INTREQ	EQU	$9c		Write interrupt request
ADKCON	EQU	$9e		Write audio and disk controller

StepDel	EQU	$bb7		Step delay (dbf, naughty!)

; a0 is address to load file at, d0 is no. of file

	movem.l	d0-d7/a0-a6,-(sp)	Store registers
	move.l	a0,a1			Copy addr. to load into a1
	lea	Filename(pc),a0		Get filename in a0
	bsr	MkName			Put d0 (hex) into filename string
	lea	Variables(pc),a6	a6 = Variables base
	lea	CIABPRB,a5		a5 = CIA base (CIABPRB)
	lea	CUSTOM,a4		a4 = custom hardware base
	move.l	a1,20(a6)		Store addr. to load in vars
	clr.w	10(a6)			Set 'no track in buffer'
	move.b	#%11111111,(a5)		All CIABPRB high
	move.b	#%10000111,(a5)		SEL0-3 low (all motors now off)
	move.b	#%01111111,(a5)		Ready for motors on again
	bclr	#3,(a5)			Select drive 0, motor on
	tst.w	0(a6)			}
	nop				}	blowed if I know why
	not.w	0(a6)			}
	clr.l	2(a6)			Clear current track and ?
	clr.l	6(a6)			Clear sec. to load, on track
	btst	#4,CIABICR-CIABPRB(a5)	Test index (pointlessly)
.wtindx	btst	#4,CIABICR-CIABPRB(a5)	Test index
	beq.s	.wtindx			Repeat until one occurs
	bset	#1,(a5)			Dir = out towards edge
.gttk0	btst	#4,CIAAPRA-CIABPRB(A5)	Are we at track 0?
	beq.s	.gottk0
.wtrdy	BTST	#5,CIAAPRA-CIABPRB(A5)	Drive ready?
	bne.s	.wtrdy			Wait until it is
	bclr	#0,(a5)			Set step
	nop				Wait a few cycles
	bset	#0,(a5)			Clear step
	move.w	#StepDel,D0	NAUGHTY - should use CIAs!
.stpdel	dbf	d0,.stpdel		Step delay
	bra.s	.gttk0			Repeat until we get to track 0
.gottk0	bsr	GetHash			Calculate filename's hash value
	move.w	#880,d0			Root block no. in d0
	bsr	ReadSec
	tst.l	d7			Was there an error?
	bne	DoneAll			Clear up and return if so
	move.w	18(a6),d0		Filename's hash offset into d0
	move.l	(a3,d0.w),d0		Get no. of file header block
	beq	DoneAll			If no file, clear up and return
.hshchn	bsr	ReadSec			Read sector
	tst.l	d7			Was there an error?
	bne.s	DoneAll			If so clear up, return
	lea	$1B0(a3),a2		a2 = pointer to filename in block
	moveq	#0,d0			Byte -> word
	move.b	(a2)+,d0		Get name length byte
	cmp.w	16(a6),d0		Compare with wanted filename length
	beq.s	.sameln			Go on if same lengths
.wrongF	move.l	$1F0(A3),d0		Get next hash chain
	beq.s	DoneAll			If none, file not found - return
	bra.s	.hshchn			Otherwise follow down the hash chain

.sameln	subq.w	#1,d0			Length -1 for dbf
	move.l	12(a6),a1		Required filename ptr. in a1
.letlop	move.b	(a2)+,d1		Get byte from current file's name
	cmp.b	#"a"-1,d1		Test case (badly)
	ble.s	.isupr			Go on if upper case
	sub.b	#32,d1			Else convert to upper case
.isupr	cmp.b	(a1)+,d1		Compare with required filename
	bne.s	.wrongF			If different, wrong file
	dbf	d0,.letlop		Repeat to compare all letters

.loadlp	move.l	$10(a3),d0	Get first/next data block
	beq.s	DoneAll		If none, clean up and return
	cmp.w	#1759,d0	Above valid range?
	bgt.s	DoneAll		If so, clean up and return
	bsr.s	ReadSec		Otherwise load it
	tst.l	d7		If there was an error
	bne.s	DoneAll		then clear up and return
	lea	$200(a3),a0	Get end-of-block pointer in a0
	move.l	$14(a3),d1	Get checksum long
	clr.l	$14(a3)		Clear same long in block
	move.l	a3,a1		Copy block pointer to a1
	moveq	#0,d0		Clear sum
.sumlop	add.l	(a1)+,d0	Add long to sum
	cmp.l	a0,a1		At end of block
	blt.s	.sumlop		Sum all longs
	neg.l	d0		Negate sum
	eor.l	d0,d1		Compare with stored sum
	bne.s	DoneAll		If checksum fails, clean up and return
	move.l	$c(a3),d0	Get no. of bytes in this block in d0
	subq.w	#1,d0		Prepare for dbf
	lea	$18(a3),a1	Get pointer to data in a1
	move.l	20(a6),a0	Get address to load at in a0
.cpbyte	move.b	(a1)+,(a0)+	Copy data as bytes
	dbf	d0,.cpbyte	Repeat
	move.l	a0,20(a6)	Store new load address
	bra.s	.loadlp		Carry on loading

; Routine to switch off motors and return
DoneAll	move.b	#%11111111,(a5)		Select everything
	move.b	#%10000111,(a5)		All motors off
	move.b	#%11111111,(a5)		Left off
	movem.l	(sp)+,d0-d7/a0-a6	Restore registers
	rte				Return

; Routine to read a sector (in d0) from the disk
; Pointer to the loaded sector is returned in a3

ReadSec	tst.w	10(a6)			Check buffer status
	bmi.s	L701E6			Leave alone if negative
	move.w	#1,10(A6)		Otherwise set to 1 (pointless)
L701E6	moveq	#0,d7			d7 is error flag, none yet
	move.w	d0,6(a6)		Store sector to load in vars
	ext.l	d0			word -> long
	divu	#11,d0			sector number...
	swap	d0			...mod 11
	move.w	d0,8(a6)		gives sector on track, store in vars
	swap	d0			recover division result
	move.l	d0,d1			Copy to d1
	lsr.w	#1,d1			Get cylinder no. in d1.w
	tst.w	10(a6)			Is there a track in the buffer?
	bpl.s	.nedred			If not, will have to read one
	cmp.w	2(a6),d1		Is it the right cylinder?
	bne.s	.nedred			If not, will have to read one
	move.w	d0,d2			Copy track no. to d2
	and.w	#1,d2			Mask to get side
	cmp.w	4(a6),d2		Is the right side in the buffer?
	beq	L7036E			If so, buffer is prepared
.nedred	bset	#2,(a5)			Select side 0
	clr.w	4(a6)			Flag side 0 in vars
	btst	#0,d0			See which side is required
	beq.s	.sideok			Go on if 0; side is correct
	move.w	#1,4(a6)		Flag side 1 in vars
	bclr	#2,(a5)			Select side 1
.sideok	move.w	d1,d0			Copy d0 to d1
	bsr	GetTkd0			Go to that track
	move.w	#$7F00,ADKCON(a4)	Clear top byte of ADKCON
	move.w	#$9500,ADKCON(a4)	MFM, no precomp, wordsync, FAST(MFM)
	move.w	#$4489,DSKSYNC(a4)	Standard sync
	move.l	#$23600,a3		Get buffer pointer
	move.l	#$55555555,d6		Get clock value in d6
.fndlst	move.l	a3,a1			Copy buffer pointer to a1
	move.w	#2,INTREQ(a4)		Clear disk interrupt request
	move.l	a3,DSKPTH(a4)		Set disk buffer
.watrdy	btst	#5,CIAAPRA-CIABPRB(a5)	Disk ready?
	bne.s	.watrdy			Wait until it is
	move.w	#$8210,DMACON(a4)	Disk DMA on
	move.w	#$8008,DSKLEN(a4)
	move.w	#$8008,DSKLEN(a4)	Read len = sector info only
.wtdkdn	btst	#1,INTREQR+1(a4)	Disk DMA done interrupt?
	beq.s	.wtdkdn			Wait until one occurs
.skpsnc	cmp.w	#$4489,(a1)+		Compare syncs at (buffer+)
	beq.s	.skpsnc			Keep going until past
	subq.l	#2,a1			a1 points to last sync
	move.l	(a1),d0
	move.l	4(a1),d1		Get longs of sector info
	and.l	d6,d0
	and.l	d6,d1			Mask with clock
	add.l	d0,d0			Shift first one left
	or.l	d0,d1			Recombine
	cmp.b	#1,d1			Just before track gap?
	bne.s	.fndlst			If not keep looking for last sector
	move.l	a3,DSKPTH(a4)		Set buffer pointer again
	move.w	#$8210,DMACON(a4)	Ensure disk DMA is still on
	move.w	#$9770,DSKLEN(a4)
	move.w	#$9770,DSKLEN(a4)	Now read whole track
	move.w	#2,INTREQ(a4)		Clear disk DMA done int. request
	move.l	#$186A0,d1		d1 = quite a long time
.wtdmad	subq.l	#1,d1			Count down
	bne.s	.notstk			Go on if not end of count
	not.l	d7			Else flag an error
	move.w	#$4000,DSKLEN(a4)	Stop disk DMA
	rts				Return
.notstk	btst	#1,INTREQR+1(A4)	Disk DMA done interrupt?
	beq.s	.wtdmad			If not, keep counting
	move.w	#$4000,DSKLEN(a4)	Stop disk DMA

	moveq	#10,d5		11 sectors to decode
	move.l	a3,a2		Put them back over the buffer
	move.l	a3,a1		Save buffer pointer for later
.seclop	move.w	#$3ff,d0	Amount of space to look for sync in
.fndsnc	cmp.w	#$4489,(a3)+	Look for sync
	beq.s	.gotsc		Go on if found it
	dbf	d0,.fndsnc	Keep trying
	not.l	d7		If sync missing, flag error
	rts			and return
.gotsc	cmp.w	#$4489,(a3)	Looking at sync?
	bne.s	.skpsnk		If not, have passed it
	addq.l	#2,a3		Otherwise skip over it
.skpsnk	movem.l	(a3)+,d0-d1	Get two longs
	and.l	d6,d0
	and.l	d6,d1		And them with clock
	add.l	d0,d0		Shift first one left
	or.l	d1,d0		Recombine
	lsr.w	#8,d0		Shift right for sector number
	move.w	d0,(a2)+	Store sector number over buffer
	lea	$30(a3),a3	Skip sector info; a3 points to data
	moveq	#$7f,d2		128 longs of data to decode
.decdat	move.l	$200(a3),d1	Get second long of data
	move.l	(a3)+,d0	Get first long of data
	and.l	d6,d0
	and.l	d6,d1		And them with clock
	add.l	d0,d0		Shift first one left
	or.l	d1,d0		Recombine
	move.l	d0,(a2)+	Store over buffer again
	dbf	d2,.decdat	Repeat for all data
	dbf	d5,.seclop	Repeat for all sectors

; Track is now decoded as sectorno.w, data.b repeated 11 times
; Now bubblesort the sectors into order
.bubble	move.l	a1,a2		Get buffer pointer again
	moveq	#9,d0		10 (sector and next sector)s to compare
	moveq	#0,d1		d1 is count of sectors swapped
.cmpsec	move.w	(a2),d2		Get no. of current sector in d2
	cmp.w	$202(a2),d2	Compare with no. of next sector
	blt.s	.sortok		If less, go on
	move.l	a2,a3		Copy current sec. pointer to a3
	move.w	#$100,d2	One sector + sector no. words to swap
.swpwrd	move.w	$202(a3),d3
	move.w	(a3),$202(a3)
	move.w	d3,(a3)+	Swap a word of sector data
	dbf	d2,.swpwrd	Repeat 
	addq.w	#1,d1		Inc. no. of sectors swapped
.sortok	lea	$202(a2),a2	Get next sector
	dbf	d0,.cmpsec	Repeat for 10 (sector and next sector)s
	tst.w	d1		Did we have to swap anything?
	bne.s	.bubble		If so, try again

	lea	2(a1),a2	a2 is pointer to first sector data
	moveq	#10,d0		11 sectors to shift back
.seccpy	moveq	#$7f,d1		128 longs of data in each sector
.cpydat	move.l	(a2)+,(a1)+	Copy longs of data back down buffer
	dbf	d1,.cpydat	Repeat for all data
	addq.l	#2,a2		Skip sector no., no longer needed
	dbf	d0,.seccpy	Copy other sectors directly after

; At this point, the track is prepared in the buffer, so we just need
; to return a pointer to the sector in a3
L7036E	move.l	#$23600,a3	Get buffer pointer
	moveq	#9,d0		Constant for shift
	moveq	#0,d1		word -> long
	move.w	8(a6),d1	Sector no. on track .w into d1
	asl.l	d0,d1		d1 << 9 = d1 * 512
	add.l	d1,a3		a3 = pointer to required sector
	move.w	#-1,10(a6)	Flag 'there is a track in the buffer'

; Routine to go to track d0
GetTkd0	move.w	2(a6),d1	Current track in d1
	move.w	d0,2(a6)	Store which track it will be
	btst	#4,CIABICR-CIABPRB(a5)	Test for index (pointlessly?)
.wtndex	btst	#4,CIABICR-CIABPRB(a5)	Test for index
	beq.s	.wtndex		Wait until one occurs
	bclr	#1,(a5)		Dir = in towards centre
	sub.w	d1,d0		Get no. of tracks to shift
	beq.s	.aret		Return if already at track
	bpl.s	.centre		Positive is towards centre, dir is correct
	bset	#1,(a5)		Dir = out towards edge
	neg.w	d0		Positive no. of steps to make
.centre	subq.w	#1,d0		Minus 1 for dbf
.steplp	btst	#5,CIAAPRA-CIABPRB(a5)	Test for disk ready
	bne.s	.steplp		Wait until it is
	bclr	#0,(a5)		Set step signal
	nop			Wait a while
	bset	#0,(a5)		Clear step signal
	move.w	#StepDel,d1	Step delay
.stepdl	dbf	d1,.stepdl	- NAUGHTY! should use CIA timers
	dbf	d0,.steplp	Step as many times as needed
.aret	rts			Return

; Calculate hash table value for filename at a0
; Won't work with real filenames, only $xx ones
; stores result in variables

GetHash	move.l	a0,12(a6)	Store filename in vars
	move.l	a0,a1		Copy to a1
	moveq	#0,d0		Clear length counter
.getlen	tst.b	(a1)+		End of filename?
	beq.s	.gotlen		Go on if so
	addq.w	#1,d0		Else increase length counter,
	bra.s	.getlen		repeat

.gotlen	move.w	d0,d1		Copy filename length to d1
	move.w	d0,16(a6)	Store filename length in vars
	subq.w	#1,d1		Prepare d1 (filename len) for dbf
.hshlop	mulu	#13,d0		d0 = filename length * 13
	moveq	#0,d2		Clear d2 for byte -> word
	move.b	(a0),d2		Get letter of filename
	cmp.b	#"a"-1,d2	Test case (badly)
	ble.s	.letok		If less or equal, is upper case
	sub.b	#32,d2		Otherwise sub 32 to make it upper case
.letok	move.b	d2,(a0)+	Store back in filename
	add.w	d2,d0		Add to hash total in d0
	and.w	#$7ff,d0	Keep d0 in range
	dbf	d1,.hshlop	Repeat for all bytes of filename
	divu	#72,D0		Hash total...
	swap	d0		...mod 72...
	addq.w	#6,d0 6...
	asl.w	#2,D0		...times 4
	move.w	d0,18(a6)	store in variables

; Routine to put the number in d0 into the filename at a0 ("$xx")
MkName	move.w	d0,d1		Copy fileno. to d1
	move.w	d1,d2		and d2
	lsr.w	#4,d2		d2 is upper nibble
	bsr.s	D2toHex		convert to hex ascii
	move.b	d2,1(a0)	store as first digit of filename
	move.w	d1,d2		move lower nibble into d2
	bsr.s	D2toHex		convert to hex
	move.b	d2,2(a0)	store as second nibble
	rts			return

; Routine to convert d2 into hex ascii
D2toHex	and.w	#$f,d2		Only numbers from 0-15
	add.w	#"0",d2		Convert to ascii
	cmp.w	#"9",D2		letter or number?
	ble.s	.digit		return if number
	addq.w	#7,d2		else add 7 to convert to letter
.digit	rts			return

Variables:	dc.w	0		 0 = .w
		dc.w	0		 2 = Current track .w
		dc.w	0		 4 = Side in buffer (0/1) .w
		dc.w	0		 6 = sector to load .w
		dc.w	0		 8 = sector no. on track .w
		dc.w	0		10 = Valid track in buffer .w
		dc.l	0		12 = Pointer to filename .l
		dc.w	0		16 = Length of filename in bytes .w
		dc.w	0		18 = filename's hash offset .w
		dc.l	0		20 = Address to load at .l

Filename	dc.b	"$00",0		Filename to load
FileDownload: Agile File Loader
Filesize: 0KB, downloaded 42 times
Powered by the best online Amiga mod player: FLOD

Some more you may like:
File-name LoaderSpiv's File LoaderSurge File Loader + Misc stuffLSD - Single File Intro


Leave a Comment!

: Use this calculator
Your comment will be available for editing for 10 minutes
2007-06-22 03:51

1. musashi9 writes

I Found this on a Source disk compiled by BB from EAB
I think the file names need to be 01 02 03 0f ect...
2007-06-25 21:09

2. StingRay writes

using empty dbf loops for timing is not the way to go. :) at least not if you want your crack working on 680x0 machines. =)
2007-06-26 13:10

3. WayneK writes

I'm sure the dbf-timing loop was just put in there to make Codetapper feel all warm + fuzzy inside :)
2007-06-26 15:08

4. StingRay writes

Hahaha, yes, that was probably the only reason to use that very dbf timing loop here :D
2011-04-29 22:08

5. kaf writes

What's with the loop to do a sequence of small reads to probe for the track gap? It's pointless, it will slow down track reads by 50% on average (waiting an extra 1/2 revolution before starting the full track read), and I wonder whether it works at all in emulators, which might reasonably start all DMAed reads from the track start.
Reply to comment #5
2014-02-05 11:00

6. Phantasm writes

i disagree about the pointlessness of the system of probing used. It reduces the size of the mfm buffer required if you read the whole track starting from the first sector after the track gap. I was thinking this is particularly useful for a trackloader used in cracks. in many cases the custom mfm routines use only a single sync marker so only need a smaller mfm buffer than what is required to guarantee a successfully read of a dos track. this can cause issues if you need to try and find additional spare memory to use as an mfm buffer.
Reply to comment #6
2014-02-05 13:30

7. Phantasm writes

maybe a loader that reads the track one sector at a time in a loop filling up the mfm buffer as it goes would be a good approach to give the best of both worlds. so the sectors could be read in whatever order they appear as the disk spins but you dont need such a large mfm buffer to handle the track gap and all 11 complete sectors. I've never seen such a loader though.