
* EXAMPLE OF USE!

	
	move.w #$4000,$dff09a	

	jsr INIT_DISK

	move.l #loadspace,a5	;address of load
	move.l #filename,a6		;address of filename
	moveq #0,d7		;load mode set

	jsr LOAD_FILE

	tst.w d7			;check for failure
	beq.s loadok

	move.w #$7fff,d0		;red screen = error
erlp	move.w #$f00,$dff180
	dbf d0,erlp

loadok	jsr MOTOR_OFF
	move.w #$c000,$dff09a
	rts

filename	dc.b "df0:testfile",$00

	even

loadspace dcb.l $8000,$00

*****************************************************************************

  *   System Independant Multi-Disk AmigaDos File Loader V1.1- 26/4/94   *
  *----------------------------------------------------------------------*

                    * By PHIL RUSTON AKA:PHIL!94/LSD *

* You MUST Call "INIT_DISK" before loading for the 1st time ever!

* To LOAD A FILE Set:-

* A6 = Address of 0 ended filename (MUST include a path, subdirs allowed.)
* A5 = Load address.
* D7 = Mode: 0 = Load, 1 = Search and Return file length in A5 only.

* Then call: "LOAD_FILE" - All registers preserved except A5 and D7.

* A5 returns as last byte address of file + 1
* D7 returns as 0 if load OK. Or one of the following errors:

* 01 = No speed signal from motor      (disk removed whilst loading?)
* 02 = No DMA transfer time out        (not a dos disk / ''       '')
* 03 = Disk removed from drive         (disk removed whilst loading)
* 04 = Can't find that disk block      (disk corrupt)
* 05 = Wrong track marker ID           ('')
* 06 = Checksum error on disk block    ('')
* 07 = Block requested out of range    ('')
* 08 = Not a file                      (Its a directory!)
* 09 = File not found                  
* 0a = That drive is not connected.    (Direct drive access failed)
* 0b = There is no disk in that drive. ( ''                    '' )
* 0c = Disk requester.                 (Disk specified not in any drive)
* 0d = No path given                   (I require disk or drive name!)

* Dont forget to call "MOTOR_OFF" when finished loading for a while!!
* But NOT between each in batch of files!

* See DOCS for more info - If you use this code please credit me and
* consider sendimg me a token of your appreciation.. Cash welcome!

*******************************************************************************

	section loadcode,code

**************
* Initialize *
**************

INIT_DISK	movem.l a0-a6/d0-d7,-(a7)
	move.l #$dff000,a0
	move.l #$bfd000,a1		
	move.l #Variables,a2
	clr.l attempts(a2)
	clr.l tracksides(a2)
	clr.l track_in(a2)
	clr.l load_address(a2)
	bsr Motor_off

	bset #3,drives_avail(a2)	;df0: assumed always available
	moveq #4,d1
ID_drives	move.b #$7f,$100(a1)	;what externals are connected?
	bclr d1,$100(a1)		
	move.w #100,d7
	bsr cia_wait
	move.b #$ff,$100(a1)		
	bclr d1,$100(a1)
	moveq #$1f,d7
	moveq #0,d0
df1idloop	lsl.l #1,d0
	bclr d1,$100(a1)
	btst #5,$1001(a1)
	beq.s nxtbit
	ori.w #1,d0
nxtbit	bset d1,$100(a1)
	dbf d7,df1idloop
	not.l d0
	cmpi.l #-1,d0		
	bne.s no_drive		
	bset d1,drives_avail(a2)

no_drive	addq.w #1,d1
	cmpi.w #7,d1
	bne.s ID_drives
	bsr motor_off

	moveq #0,d0		;now identify any disks present.
	moveq #3,d1		
id_dsk_lp	btst d1,drives_avail(a2)
	beq.s no_drv
	move.b d0,use_drive(a2)
	move.b #$ff,$100(a1)
	bclr d1,$100(a1)
	bsr stepin
	bsr initialize_drive
	btst #2,$1001(a1)
	beq.s no_drv
	move.b #1,start_motor(a2)
	bsr identify_disk
no_drv	bsr motor_off
	addq.w #1,d1
	addq.w #1,d0
	cmpi.w #4,d0
	bne.s id_dsk_lp
	movem.l (a7)+,a0-a6/d0-d7
	rts

*****************************************************************************

***************
* File Loader *
***************

LOAD_FILE	movem.l d0-d6/a0-a4/a6,-(a7)
	move.l #$bfd000,a1
	move.l #Variables,a2
	move.l a5,load_address(a2)
	move.b d7,mode(a2)

	move.l a6,a0		;analyse file name for path.
	moveq #0,d0
find_path	tst.b (a0)
	beq no_path
	cmpi.b #":",(a0)+
	beq.s found_path
	addq.w #1,d0
	cmpi.w #32,d0
	bne.s find_path
	bra no_path
found_path
	moveq #0,d2		;direct drive access (df0: etc?)
	move.l #direct_names,a0
lk_direct	moveq #4,d1
	bsr compare_text
	tst.w d7
	bne.s direct_path
	addq.w #4,a0
	addq.w #1,d2
	cmpi.w #4,d2
	bne.s lk_direct		
	bra not_direct

direct_path
	
	move.l d2,d1		;direct drive access requested
	addq #3,d1		;but is that drive available?
	btst d1,drives_avail(a2)
	bne.s drv_avl
	move.b #$0a,error(a2)	;no - error $0a
	bra load_end	
drv_avl
	cmp.b use_drive(a2),d2	;same drive used previously?
	beq Go_load
	bsr motor_off
	move.b d2,use_drive(a2)	;select it if not
	move.b #$ff,$100(a1)
	bclr d1,$100(a1)
	bsr wait1msec

same_drv1	btst #2,$1001(a1)		;disk in?
	beq Dchngd1
	move.b #1,start_motor(a2)
	bra Go_load
	
Dchngd1	bsr test_step
	btst #2,$1001(a1)		;disk in now?
	bne.s Dnowin
	move.b #$0b,error(a2)	;no disk in that drive-error $0b
	bra load_end	

Dnowin	move.b #1,start_motor(a2)
	bsr initialize_drive
	bsr identify_disk
          bra Go_load

not_direct
	
	lea disk_names(a2),a0	;look through currently inserted
	addq.w #1,a0
	moveq #0,d2                   ;disks for that diskname.

lk_disk	move.w d0,d1
	cmp.b -$1(a0),d0		;compare diskname length
	bne.s not_in2
	bsr compare_text
	tst.w d7
	bne.s got_path

not_in2	add.w #$20,a0		;look in next drives name-store
	addq.w #1,d2
	cmpi.w #4,d2
	bne.s lk_disk	
	bra not_inserted

got_path	cmp.b use_drive(a2),d2	;same drive used previously?
	beq Go_load
	bsr motor_off
	move.b d2,use_drive(a2)	;select it if not
	move.b #$ff,$100(a1)
	move.l d2,d1
	addq.w #3,d1
	bclr d1,$100(a1)
	bsr wait1msec
	btst #2,$1001(a1)		;disk in?
	beq Doutnow		
	move.b #1,start_motor(a2)
	bra Go_load

Doutnow	subq.w #1,a0		;ejected - clear stored name
	moveq #$1f,d7		
clr_name	clr.b (a0)+
	dbf d7,clr_name		

not_inserted

	moveq #0,d2		;see if any disks have been
tdrv_loop	move.w d2,d1		;replaced, and identify any new
	add.w #3,d1		;disks. If any match req then
	bsr motor_off		;load.
	btst d1,drives_avail(a2)
	beq.s nxt_drv
	move.b #$ff,$100(a1)
	bclr d1,$100(a1)
	bsr wait1msec
	btst #2,$1001(a1)
	bne.s nxt_drv
	move.b d2,use_drive(a2)
	bsr test_step
	btst #2,$1001(a1)
	bne.s dsk_now_in
	lea disk_names(a2),a0	;still no disk so wipe diskname
	move.w d2,d1
	lsl.w #5,d1
	add.w d1,a0
	moveq #$1f,d1
wipe_dn	clr.b (a0)+
	dbf d1,wipe_dn	
	bra.s nxt_drv
dsk_now_in
	move.b #1,start_motor(a2)
	bsr initialize_drive
	bsr identify_disk
	lea disk_names(a2),a0	;correct diskname
	move.w d2,d1
	lsl.w #5,d1
	add.w d1,a0
	cmp.b (a0),d0		;compare diskname length
	bne.s nxt_drv
	move.w d0,d1
	addq.w #1,a0
	bsr compare_text
	tst.w d7		
	bne Go_load

nxt_drv	addq.w #1,d2
	cmpi.w #4,d2
	bne.s tdrv_loop
	move.b #$0c,error(a2)	;disk not in any drive - error $c
	bra Load_end

no_path	move.b #$0d,error(a2)	;no path - error $d
	bra Load_end

Go_load	lea $01(a6,d0.w),a6		;bypass path part of filename
	bsr Find_file

Load_end	move.b #$ff,track_in(a2)
	tst.b error(a2)
	beq.s no_error
	bsr motor_off
	move.w #1000,d7
	bsr cia_wait
no_error	moveq #0,d7
	move.b error(a2),d7
	movem.l (a7)+,d0-d6/a0-a4/a6
	rts

****************************************************************************

********************
* Shut down drives *
********************

MOTOR_OFF	movem.l a1/a2/d7,-(a7)
	move.l #$bfd000,a1
	move.l #Variables,a2
	move.b #$ff,$100(a1)
	move.b #$87,$100(a1)
	bsr wait1msec
	move.b #$ff,$100(a1)
	move.b #$ff,use_drive(a2)
	moveq #100,d7
	bsr cia_wait
	movem.l (a7)+,a1/a2/d7
	rts

****************************************************************************

*********************
* Get Main Root Dir *
*********************

Find_file	clr.b error(a2)
	move.l #block_buffer,a0	;get disk's root dir.
	move.w #880,d0
	bsr Get_block
	tst.b error(a2)
	bne file_eror

******************************
* Find File's Root Directory *
******************************

Find_files_root_dir_loop
	
	move.l a6,a4
	moveq #0,d0	

dir_loop	move.b (a4),d7		;find file's root dir
	beq.s root_dir
	cmpi.b #"/",d7		;and get length of subdir name
	beq.s oblique 		;if required.
	addq.w #1,a4
	addq.w #1,d0
	bra dir_loop

oblique	move.w d0,d7		;d0 = dir name length
	move.w d0,d6		
	subq.w #1,d7

	bsr hash_name		;hash dir name into d0

	lsl.w #2,d0
	move.l $18(a0,d0.w),d0	;header block for this hash
	beq file_not_found

get_dir_header

	bsr Get_block
	tst.b error(a2)
	bne file_eror

	bsr compare_names
	tst.w d7
	beq.s next_in_hash_chain
	addq.w #1,a4
	move.l a4,a6
	bra find_files_root_dir_loop

next_in_hash_chain

	move.l $1f0(a0),d0
	bne get_dir_header
	bra file_not_found

**************************
* Locate The File Itself *
**************************

root_dir	move.w d0,d7		;length of file name
	move.w d0,d6
	subq.w #1,d7

	bsr hash_name	

	lsl.w #2,d0
	move.l $18(a0,d0.w),d0	;header block number for this hash
	beq file_not_found

get_header

	bsr Get_block
	tst.b error(a2)
	bne file_eror

	bsr compare_names
	tst.w d7
	bne.s correct_file_header

	move.l $1f0(a0),d0		;any more in hash chain?
	bne get_header
	bra file_not_found

correct_file_header

	cmpi.l #-3,$1fc(a0)		;make sure its a file header
	beq.s file_header
	move.b #8,error(a2)		;not a file header-error 8
	bra file_eror

file_header

	move.l load_address(a2),a5
	move.l $144(a0),d6		;length of file.
	tst.b mode(a2)
	beq.s transfer
	move.l d6,a5
	rts
transfer
	subq.l #1,d6	

transfer_loop

	move.l $10(a0),d0		;next block location
	beq file_not_found

	bsr Get_block
	tst.b error(a2)
	bne file_eror

****************************************
* Download data to file's load address *
****************************************

	lea $18(a0),a3		;1st data longword in dos_block
	moveq #$79,d7
make_file	subq.l #4,d6
	bmi.s last_bytes
	move.l (a3)+,(a5)+	
	dbf d7,make_file
	bra transfer_loop
last_bytes
	addq.w #4,d6
lb_loop	move.b (a3)+,(a5)+	
	dbf d6,lb_loop
	rts

file_not_found

	move.b #9,error(a2)		;file not found-error 8
file_eror	rts

*****************************
* File Load Called Routines *
*****************************

hash_name	move.l a6,a3
hash_loop	moveq #0,d2		;hash file name at a6
	move.b (a3)+,d2		;length-1 in d7.
	cmpi.b #$60,d2
	bls.s capital
	cmpi.b #$7a,d2
	bhi.s capital
	subi.b #$20,d2
capital	mulu #13,d0
	add.l d2,d0
	andi.l #$7ff,d0
	dbf d7,hash_loop
	divu #72,d0
	swap d0			;d0=hash number
	rts

compare_names

	moveq #0,d7
	cmp.b $1b0(a0),d6		;d6=length of name being tested
	bne.s name_fail
	move.w d6,d5
	move.l a6,a3
	lea $1b1(a0),a5
	subq.w #1,d5	
comp_loop	move.b (a3)+,d1		;Case desensitize filename
	move.b (a5)+,d2		;compare.
	andi.b #$df,d1
	andi.b #$df,d2
	cmp.b d1,d2
	dbne d5,comp_loop
	tst.w d5
	bpl.s name_fail
	moveq #1,d7
name_fail	rts

compare_text
	
	movem.l a0/a6/d2/d3,-(a7)	;Text comparison.
	subq.w #1,d1
	moveq #0,d7		;d1 = number of chars to compare
cmp_loop2	move.b (a0)+,d2		;Strings at A0 and A6
	move.b (a6)+,d3		;d7: 0 = different, 1 = same.
	andi.b #$df,d2
	andi.b #$df,d3
	cmp.b d2,d3
	bne not_same
	dbf d1,cmp_loop2
txt_same	moveq #1,d7
not_same	movem.l (a7)+,a0/a6/d2/d3
	rts

*****************************************************************************

*********************************
* Get a disk block into buffer! *
*********************************

* D0 will equal block number required ($0 - $6df)

Get_block	movem.l a0-a6/d0-d7,-(a7)
	move.w #$0504,attempts(a2)
	andi.l #$ffff,d0
	cmpi.w #$6df,d0		;check range
	bls blk_in_range
	move.b #7,error(a2)
	bra no_reload

blk_in_range

	move.l d0,d1
	divu #11,d1		;what cylinder is that on?
	cmp.b track_in(a2),d1
	beq.s Trksde_in		;that track is already in.

Reload	bsr Fetch_trackside		;loads track required
	tst.b error(a2)
	bne No_reload

Trksde_in	clr.b error(a2)
	move.l #MFMbuffer,a3
	lea $31fe(a3),a0
find_sync	move.w #$4489,d7
fs_loop	cmp.w (a3)+,d7
	beq found_sync
	cmp.l a0,a3
	bls.s fs_loop
	move.b #4,error(a2)		;couldnt find that sector-error 4
	bra MFM_error

found_sync	

	cmp.w (a3),d7
	beq.s syncstart
	subq.w #2,a3

syncstart	lea $2a(a3),a4		;Check header checksum.
	bsr decode_lw		
	move.l $2(a3),d3
	move.l $6(a3),d4
	andi.l #$55555555,d3
	andi.l #$55555555,d4
	eor.l d4,d3
	cmp.l d3,d5
	bne Not_sector

	lea $2(a3),a4		;Track mark = Head position?
	bsr decode_lw
	move.l d5,d4		
	swap d4
	moveq #0,d7
	move.b use_drive(a2),d7
	cmp.b tracksides(a2,d7.w),d4
	beq Track_mark_ok
	move.b #5,error(a2)		;wrong track mark - error 5
	bra MFM_error

Track_mark_ok

	lsr.w #8,d5		;Is this the sector required?
	move.l d1,d7
	swap d7
	cmp.b d5,d7
	beq.s Found_block
	
Not_sector

	add.w #$430,a3      	;loop until find correct sector
	bra find_sync

Found_Block

	moveq #0,d2		;Put converted mfm at dest addr.
	move.l #$55555555,d7
	move.l #block_buffer,a6
	lea $3a(a3),a4		
	lea $23a(a3),a5
	moveq #$7f,d3
	
next_lw	move.l (a4)+,d6
	move.l (a5)+,d5
	and.l d7,d6
	and.l d7,d5
	eor.l d6,d2		;update checksum
	eor.l d5,d2
	add.l d6,d6
	or.l d5,d6
	move.l d6,(a6)+
	dbf d3,next_lw
	
	sub.w #$208,a4
	bsr decode_lw
	cmp.l d5,d2
	beq.s no_reload
	move.b #6,error(a2)		;checksum error - error 6
	
MFM_error	subq.b #1,attempts(a2)
	bne Reload
	bsr initialize_drive
	move.b #5,attempts(a2)
	subq.b #1,attempts+1(a2)
	bne Reload
	
No_reload	movem.l (a7)+,a0-a6/d0-d7
	rts

*****************************************************************************

*********************
* MFM Track loader! *
*********************

* D1 will equal trackside 0 - 159

Fetch_trackside
	
	movem.l a0/d0-d3,-(a7)	
	move.l #$dff000,a0
	clr.b error(a2)		
	tst.b start_motor(a2)	;motor on already?
	beq.s motor_ok

	clr.b start_motor(a2)
	move.b #$7f,$100(a1)
	moveq #3,d7
	add.b use_drive(a2),d7
	bclr d7,$100(a1)
	move.w #200,d2		;wait for correct motor speed.
wdskrdy	moveq #10,d7
	bsr cia_wait		
	btst #5,$1001(a1)		
	dbeq d2,wdskrdy
	tst.w d2
	bpl.s motor_ok
	move.b #1,error(a2)		;no speed signal - error 1
	btst #2,$1001(a1)				
	bne.s disk_ok
	move.b #3,error(a2)		;disk removed - error 3
disk_ok	bra trackload_end

motor_ok	move.w d1,d3
	lsr.b #1,d3		;select disk side to load from.
	bcc.s head1
	bclr #2,$100(a1)
	bra.s checkhead
head1	bset #2,$100(a1)

checkhead	moveq #0,d7
	move.b use_drive(a2),d7
	move.b tracksides(a2,d7.w),d2	;ensure head is over correct
	lsr.b #1,d2		;track
	cmp.b d2,d3
	beq.s headok
	bls.s seekout
	bsr stepin
	bra.s checkhead 
seekout	bsr stepout	
	bra.s checkhead

headok	moveq #0,d7
	move.b use_drive(a2),d7
	move.b d1,tracksides(a2,d7.w)
	move.w #$2,$9c(a0)		;clear disk block irq
	moveq #18,d7		;settle wait.
	bsr cia_wait

	move.l #mfmbuffer,$20(a0)
	move.w #$4000,$24(a0)	
	move.w #$8010,$96(a0)	;enable dma
	move.w #$6800,$9e(a0)	
	move.w #$9500,$9e(a0)
	move.w #$4489,$7e(a0)	;sync
	move.w #$9900,$24(a0)	
	move.w #$9900,$24(a0)	

	move.w #200,d2
waitdirq	moveq #10,d7
	bsr cia_wait		
	btst #1,$1f(a0)		
	dbne d2,waitdirq
	tst.w d2
	bpl.s read_ok
	move.b #2,error(a2)		;No DMA finish-Time Out-error 2

read_ok	move.b d1,track_in(a2)
	move.w #$4000,$24(a0)	
	move.w #$10,$96(a0)		;disable dma

Trackload_end

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

*******************************************************************************

******************************
* MFM + disk h/ware routines *
******************************

decode_lw	move.l #$55555555,d7	;decode two consec mfm longwords
	move.l (a4),d5		;into d5
	move.l $4(a4),d4	
	and.l d7,d5	
	and.l d7,d4
	add.l d5,d5
	or.l d4,d5		
	rts

stepout	moveq #0,d7
	move.b use_drive(a2),d7
	subq.b #2,tracksides(a2,d7.w)
	bset #1,$100(a1)
	bra.s step

stepin	moveq #0,d7
	move.b use_drive(a2),d7
	addq.b #2,tracksides(a2,d7.w)
	bclr #1,$100(a1)
step	bsr.s shortwait
	bclr #0,$100(a1)
	bsr.s shortwait
	bset #0,$100(a1)
	moveq #4,d7
	bsr cia_wait
	rts
shortwait
	nop
	nop
	nop
	nop
	rts

initialize_drive

	btst #$4,$1001(a1)		
	beq.s gottrack0
	bsr.s stepout
	bra.s initialize_drive

gottrack0	moveq #0,d7
	move.b use_drive(a2),d7
	clr.b tracksides(a2,d7.w)	
	move.w #20,d7
	bsr cia_wait
	rts

Identify_disk

	movem.l a5/a6/d0-d1/d4-d7,-(a7)
	move.w #$370,d0
	bsr get_block
	moveq #0,d6
	move.b use_drive(a2),d6
	lea disk_names(a2),a5
	lsl.w #5,d6
	add.w d6,a5
	tst.b error(a2)
	beq.s dirblk_ok
	moveq #$1f,d7		;disk bad - clear name
clr_name2	clr.b (a5)+
	dbf d7,clr_name2
	bra identifd

dirblk_ok	move.l #block_buffer+$1b0,a6	;copy name to store
	moveq #0,d7
	move.b (a6),d7
copy_name	move.b (a6)+,(a5)+
	dbf d7,copy_name		

Identifd	move.b #$ff,track_in(a2)
	movem.l (a7)+,a5/a6/d0-d1/d4-d7
	rts

test_step	moveq #0,d7
	move.b use_drive(a2),d7
	btst #1,tracksides(a2,d7.w)
	beq.s chkstp_in
	bsr stepout
	bra stepdone
chkstp_in	bsr stepin		
stepdone	rts


wait1msec	moveq #1,d7

cia_wait	move.b #$08,$f00(a1)	;set one shot / stop timer
	move.b #$cc,$600(a1)	;set timer lo
	move.b #$02,$700(a1)	;set timer hi (starts counter)
ciawlp2	btst #0,$f00(a1)		;wait for ciab timer b to timeout
	bne.s ciawlp2
	subq.w #1,d7
	bne.s cia_wait		;d1 = milliseconds to wait.
	rts

*******************************************************************************

attempts     equ $0
error    	   equ $2
start_motor  equ $3
tracksides   equ $4
track_in     equ $8
use_drive    equ $9
drives_avail equ $a
mode         equ $b
load_address equ $c
disk_names   equ $10

variables	dcb.l $24,$0

direct_names

	dc.b "df0:df1:df2:df3:"
	
block_buffer	

	dcb.w $100,$0000


	section chipstuff,data_c

mfmbuffer	dcb.w $1910,$0000

*******************************************************************************

