; Universal Drive Patch
; Copyright (C) 1997 Joseph Fenton. All rights reserved.
include /DriveIDCK/system.gs
include dh0:devpac/include2.0/include/resources/disk.i
include dh0:devpac/include2.0/include/exec/exec.i
include dh0:devpac/include2.0/include/devices/timer.i
output c:udp
;--------------------------------------------------------------------
; note: this program cannot be run from WB
entry bra.b start
; Standard 2.x/3.x version string
dc.b 0,'$VER: Universal Drive Patch 2.0 (3.6.97)',0
CNOP 0,4
; The main program code.
start movea.l 4.w,a6
CALLSYS MACRO
JSR _LVO\1(A6)
ENDM
CALLSYS Forbid
; get memory for AE drive command function
moveq #4,d0
move.l #MEMF_CLEAR+MEMF_CHIP+MEMF_PUBLIC,d1
cmpi.w #39,LIB_VERSION(a6)
blo.b .ac ; pre-39 was buggy in reverse
ori.l #MEMF_REVERSE,d1
.ac CALLSYS AllocMem
move.l d0,ChipPtr
bne.b .ok0
CALLSYS Permit
move.l #ERROR_NO_FREE_STORE,d0
rts
; get memory for ReadUnitID patch code
.ok0 move.l #CODE_LENGTH,d0
move.l #MEMF_CLEAR+MEMF_PUBLIC,d1
cmpi.w #39,LIB_VERSION(a6)
blo.b .af ; pre-39 was buggy in reverse
ori.l #MEMF_REVERSE,d1
.af CALLSYS AllocMem
move.l d0,MemPtr
bne.b .ok1
moveq #4,d0
movea.l ChipPtr,a1
CALLSYS FreeMem
CALLSYS Permit
move.l #ERROR_NO_FREE_STORE,d0
rts
; get timer.device base
.ok1 lea DeviceList(a6),a0
lea timerName,a1
CALLSYS FindName
move.l d0,TimerBase
bne.b .ok2
move.l #CODE_LENGTH,d0
movea.l MemPtr,a1
CALLSYS FreeMem
moveq #4,d0
movea.l ChipPtr,a1
CALLSYS FreeMem
CALLSYS Permit
move.l #RETURN_WARN,d0
rts
; copy patch code into the allocated memory
.ok2 lea ReadUnitID,a0
move.l MemPtr,a1
move.l #CODE_LENGTH,d0
CALLSYS CopyMem
; get disk.resource base
lea DskRsrcName,a1
CALLSYS OpenResource
tst.l d0
bne.b .ok3
move.l #CODE_LENGTH,d0
movea.l MemPtr,a1
CALLSYS FreeMem
moveq #4,d0
movea.l ChipPtr,a1
CALLSYS FreeMem
CALLSYS Permit
move.l #RETURN_WARN,d0
rts
; Patch disk.resource ReadUnitID() function.
; This is the system-friendly method!
.ok3 lea _LVOReadUnitID,a0 ; function offset
movea.l d0,a1 ; library base
move.l MemPtr,d0 ; new function pointer
CALLSYS SetFunction
; Exit leaving patch in memory. You don't have to 'run' this program.
CALLSYS Permit
moveq #RETURN_OK,d0
rts
;--------------------------------------
CNOP 0,4
MemPtr dc.l 0
DskRsrcName dc.b 'disk.resource',0
even
timerName dc.b 'timer.device',0
;--------------------------------------------------------------------
CNOP 0,4
; This is the new ReadUnitID() function. It handles the disk identification
; and AEHD drive controlling.
ReadUnitID movem.l d2-d3/a3,-(a7)
move.l d0,d2 ; unit number
move.b #8,d3
lsl.b d0,d3 ; drive select bit
lea $30(a6),a0 ; unit id table
lsl.l #2,d0 ; offset for unit
lea (a0,d0.w),a3 ; ptr to this unit id
bsr.w GetDriveID ; get 32bit ID
cmpi.l #DRT_EMPTY,d0 ; drive present?
bne.b .1 ; yes, check for HD
tst.l d2 ; check unit number
bne.b .2 ; external, no drive
moveq #DRT_AMIGA,d0 ; assume internal present
.1 bsr.w CheckDrive ; check disk type
.2 move.l d0,(a3) ; set disk type
; Check for whether ReadUnitIDwas called from trackdisk.device. If not,
; simply exit back to the calling code. If called from trackdisk, cleanup
; the stack, determine which setup vector to return in a1, and return to
; trackdisk at appropriate offset.
movea.l 12(a7),a3 ; return address
cmpi.l #$2C5F4E75,(a3) ; movea.l (a7)+,a6 rts
bne.b .4 ; not trackdisk
movea.l 20(a7),a3 ; next return addr
cmpi.w #$B0AB,(a3) ; cmp.l d16(a3),d0
bne.b .4 ; not trackdisk
; trackdisk is trying to determine the disk type
movem.l (a7)+,d2-d3/a3
addq.l #4,a7 ; pop 1st return address
movea.l (a7)+,a6
movea.l (a7)+,a1 ; trackdisk return addr
; Determine structure offset for different versions of trackdisk. This
; allows the code to be mostly independent of the version of trackdisk.
; This works for versions 37 through 40.
move.w 2(a1),d2 ; prev ID offset
subi.w #$54,d2 ; offset for this version
cmp.l $54(a3,d2.w),d0 ; same type as previously?
bne.b .3 ; no
jmp (a1) ; yes, return to trackdisk
; determine which setup routine is needed for this disk type
.3 pea $22(a1) ; new return address
lea SetupDD(pc),a1
cmpi.l #DRT_AMIGA,d0
beq.b .5 ; 3.5" DD
lea SetupAE(pc),a1
cmpi.l #$0F03FFFF,d0
beq.b .5 ; AEHD drive in HD mode
lea SetupHD(pc),a1
cmpi.l #DRT_150RPM,d0
beq.b .5 ; 3.5" HD
; not recognized, return and use previous disk type settings
movea.l (a7)+,a1 ; return address
lea 4-$22(a1),a1 ; branch if same address
cmp.l d0,d0 ; same as previous
jmp (a1)
.4 movem.l (a7)+,d2-d3/a3
.5 rts
;--------------------------------------
CNOP 0,4
; Read the drive ID bits and set the AEHD speed select if needed.
GetDriveID not.b d3 ; drive select low
lea $BFD100,a0
; turn motor on, then off to reset drive ID circuit
move.b #$7F,d0
move.b d0,(a0) ; assert motor
and.b d3,d0
move.b d0,(a0) ; select drive, motor on
move.b #$FF,(a0) ; deassert motor
move.b d3,(a0) ; select drive, motor off
move.b #$FF,(a0) ; deselect drive
; read drive ID bits
moveq #31,d1 ; 32 bits
moveq #0,d0
.1 lsl.l #1,d0
move.b d3,(a0) ; select drive
btst #5,$BFE001 ; get ID bit
beq.b .2
bset #0,d0
.2 move.b #$FF,(a0) ; deselect drive
dbra d1,.1 ; next bit
not.b d3 ; drive select high
move.l d0,d1
andi.l #$FF1FFFFF,d1 ; AEHD drive ID mask
cmpi.l #$0F03FFFF,d1 ; AEHD drive?
beq.b .3 ; yes
rts
; AEHD drive - determine the disk type and set the drive speed.
.3 btst #21,d0 ; disk type bit
bne.w AEHD ; HD disk
bra.w AEDD ; DD disk
;--------------------------------------
CNOP 0,4
; Determine if disk reported as DD is really HD by timing the track
; using the index pulse.
CheckDrive cmpi.l #DRT_AMIGA,d0 ; 3.5" DD?
bne.w .exit ; no, exit
cmpi.l #$0F03FFFF,d1 ; AEHD drive?
beq.w .exit ; yes, exit
not.b d3 ; drive select low
lea $BFD000,a0
move.b #$7F,d2
move.b d2,$100(a0) ; assert motor
and.b d3,d2
move.b d2,$100(a0) ; select drive, motor on
bsr.w GetSystemTime
move.l d0,d2
addi.l #800,d2 ; .8 sec
.loop0 btst #5,$1001(a0) ; /RDY
beq.b .1 ; motor up to speed
bsr.b GetSystemTime
cmp.l d2,d0
blt.b .loop0 ; wait for motor to spin up
.1 tst.b $D00(a0) ; clear icr
bsr.b GetSystemTime
move.l d0,d2
addi.l #500,d2 ; .5 sec
.loop1 btst #4,$D00(a0) ; /INDEX = FLG bit in icr
bne.b .2 ; found index
bsr.b GetSystemTime
cmp.l d2,d0
blt.b .loop1 ; wait for index pulse
bra.b .dd ; timeout
.2 move.l d0,d2
addi.l #500,d2 ; .5 sec
.loop2 btst #4,$D00(a0) ; /INDEX = FLG bit in icr
bne.b .3 ; found index
bsr.b GetSystemTime
cmp.l d2,d0
blt.b .loop2 ; wait for index pulse
bra.b .dd ; timeout
.3 sub.l d0,d2 ; time left in .5 sec
cmpi.l #250,d2
blt.b .hd ; took more than .25 sec
.dd moveq #DRT_AMIGA,d0 ; 3.5" DD
bra.b .4
.hd move.l #DRT_150RPM,d0
.4 not.b d3 ; drive select high
move.b #$FF,$100(a0) ; deselect drive
.exit rts
;------------------
CNOP 0,4
GetSystemTime movem.l d1/a0-a1/a6,-(a7)
lea SysTime(pc),a0
movea.l TimerBase(pc),a6
CALLSYS GetSysTime
lea SysTime(pc),a0
move.l #$FFFFF,d0
and.l (a0)+,d0 ; tv_secs & 000FFFFF
mulu.w #1000,d0 ; ms
move.l (a0),d1 ; tv_micro
divu.w #1000,d1 ; ms
andi.l #$FFFF,d1
add.l d1,d0 ; total ms
movem.l (a7)+,d1/a0-a1/a6
rts
;--------------------------------------
CNOP 0,4
; Set the AEHD drive to the proper speed for the disk type.
AEDD moveq #DRT_AMIGA,d0 ; 3.5" DD
move.b #$F9,d2 ; set DD speed opcode
bra.b SetAE
AEHD move.l d1,d0 ; AEHD drive in HD mode
move.b #$FB,d2 ; set HD speed opcode
SetAE movem.l d0/a1,-(a7)
lea $BFD000,a0
lea $DFF000,a1
not.b d3 ; drive select low
move.b d2,$100(a0) ; speed
and.b d3,d2
move.b d2,$100(a0) ; select drive, set speed
; write a longword of 0 to activate the AEHD setting
move.w #$4000,dsklen(a1)
move.w #$1002,intreq(a1) ; clear disk ints
move.l ChipPtr(pc),dskpt(a1)
move.w #$C002,dsklen(a1)
move.w #$C002,dsklen(a1) ; start write to set speed
; wait for DSKBLK
move.l #$20000,d2
.loop move.w intreqr(a1),d0 ; ints
btst #1,d0 ; DSKBLK
bne.b .1 ; done
subq.l #1,d2
bne.b .loop
; timeout, stop the DMA
move.w #$4000,dsklen(a1)
.1 move.w #$4000,dsklen(a1) ; stop any disk DMA
move.w #2,intreq(a1) ; clear DSKBLK
move.b #$FF,$100(a0) ; deselect drive
not.b d3 ; drive select high
movem.l (a7)+,d0/a1
rts
;--------------------------------------
CNOP 0,4
; These routines setup the trackdisk variables needed to distinguish
; one format from another. They call a common track buffer setup.
SetupDD move.l #880*1024,$44(a3,d2.w) ; total storage
move.l #300000,$50(a3,d2.w) ; timeout
move.w #11,$4E(a3,d2.w) ; # sec/trk
move.w #15296,$48(a3,d2.w) ; track length + overlap
move.w #13628,$4A(a3,d2.w) ; track length
move.w #1660,$4C(a3,d2.w) ; gap length
move.l #DRT_AMIGA,$54(a3,d2.w) ; disk type
move.b #DRIVE3_5,$40(a3,d2.w) ; drive type
bra.b SetupTrkBuffer
CNOP 0,4
SetupAE move.l #1520*1024,$44(a3,d2.w) ; total storage
move.l #500000,$50(a3,d2.w) ; timeout
move.w #19,$4E(a3,d2.w) ; # sec/trk
move.w #24004,$48(a3,d2.w) ; track length + overlap
move.w #22334,$4A(a3,d2.w) ; track length
move.w #1662,$4C(a3,d2.w) ; gap length
move.l #$0F03FFFF,$54(a3,d2.w) ; disk type
move.b #4,$40(a3,d2.w) ; drive type
bra.b SetupTrkBuffer
CNOP 0,4
SetupHD move.l #1760*1024,$44(a3,d2.w) ; total storage
move.l #600000,$50(a3,d2.w) ; timeout
move.w #22,$4E(a3,d2.w) ; # sec/trk
move.w #30584,$48(a3,d2.w) ; track length + overlap
move.w #27256,$4A(a3,d2.w) ; track length
move.w #3320,$4C(a3,d2.w) ; gap length
move.l #DRT_150RPM,$54(a3,d2.w) ; disk type
move.b #DRIVE3_5_150RPM,$40(a3,d2.w) ; drive type
SetupTrkBuffer movea.l $68(a3,d2.w),a1 ; track buffer ptr
move.w #-1,(a1) ; track invalid
clr.b 2(a1) ; clear track flags
move.w $4C(a3,d2.w),d0 ; gap length
lea $68(a1,d0.w),a0
adda.w d2,a0
adda.w d2,a0 ; start of data area
move.l a0,4(a1) ; track data ptr
suba.w d0,a0 ; start of gap area
lsr.w #2,d0 ; # longs in track gap
subq.w #1,d0 ; for dbra
move.l #$AAAAAAAA,d1 ; MFM zero value
.1 move.l d1,(a0)+ ; clear track gap
dbra d0,.1
rts
;--------------------------------------
CNOP 0,4
ChipPtr dc.l 0
TimerBase dc.l 0
SysTime dc.l 0,0
;--------------------------------------------------------------------
CODE_END:
CODE_LENGTH equ CODE_END-ReadUnitID
end