home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Meeting Pearls 3
/
Meeting_Pearls_III.iso
/
Pearls
/
disk
/
Floppy
/
HackDisk
/
hackdisk.s
< prev
next >
Wrap
Text File
|
1993-04-04
|
70KB
|
3,030 lines
********************************************************************************
* hackdisk.s -- my very own version of trackdisk.device
* Copyright © 1992,93 by Dan Babcock
*
* Version history:
* V1.00 04/25/92 First version. Supports 880K drives only.
* V1.01 04/28/92 Optimized CopyMem
* V1.02 04/30/92 More intelligent write algorithm fixes performance problem
* V1.03 05/03/92 Sets IO_ACTUAL
* V1.04 06/24/92 Fixes a couple bugs: RemChangeInt works now, and disk change
* errors are only reported when it makes sense.
* CrossDOSV5/CrossPC flushed out these bugs.
* V1.10 07/04/92 Compatible with Kickstart 1.2 and higher.
* Fixed Seek (IO_OFFSET is in bytes, not tracks).
* Released on Fish 697.
* V1.11 08/03/92 Fixed bug in readtrack routine - buffer not cleared if error
* in some cases (no sync).
* Fills TC_Userdata with bogus value to fool CrossDOS under Kick
* 1.2/1.3.
* Puts -1 in IO_DEVICE on open error. Some (though not many)
* applications depend on this.
* Using a slightly more creative "invalid sync word" (was 0).
* This should fix many drive incompatibility problems.
* Glitch in disk check code fixed (only affected non-NoClick mode).
* V1.12 08/16/92 CMD_STOP/CMD_START should work now.
* V2.00 12/26/92 Supports Chinon/Commodore FB357A 150RPM HD floppy drives.
* Support added for 5.25 inch drives (NOT TESTED!!!)
* Added track number to verify error display (only under 2.0+).
* Added a tiny module at the end (see the comments for details).
* Released on Fish 803.
* V2.01 01/22/93 Bug fix: write protection detected (broke in 2.00)
* V2.02 03/13/93 Now performs an AllocUnit on all units in the initroutine
********************************************************************************
;This source was assembled with Macro68 release 3.
exeobj
objfile 'devs:hackdisk.device'
MC68000
multipass
;Standard register usage:
;A6 - ExecBase or $dff000
;A5 (A_DEVICE) - device ptr
;A4 (A_UNIT) - unit ptr
;A3 (A_IO) - IORequest
IFND SYS
SYS macro
jsr _LVO\1(a6)
endm
ENDC
IFND _custom
_custom equ $dff000
ENDC
;Set INFO_LEVEL to 1 for full debugging output over the internal serial port.
INFO_LEVEL equ 0
A_DEVICE equr a5
A_UNIT equr a4
A_IO equr a3
VERSION equ 127
REVISION equ 0
MYPRI equ 0
MD_NUMUNITS equ 4
STACKSIZE equ 4000
TASKPRI equ 5
RETRYCNT equ 3
;Put a message to the serial port. Used like so:
;
;PUTDEBUG <'Init: called'>
;
;Parameters can be printed out by pushing them on the stack and
;adding the appropriate C printf-style % formatting commands.
PUTDEBUG macro ;<msg>
ifne INFO_LEVEL
movem.l d0-d1/a0-a1,-(sp)
lea .msg\@(pc),a0 ;Point to static format string
lea 16(sp),a1 ;Point to args
bsr KPutFmt
movem.l (sp)+,d0-d1/a0-a1
bra .end\@
.msg\@:
dc.b \1,$a,0
even
.end\@:
endc
endm
*--------------------------------------------------------------------
*
* Driver error defines
*
*--------------------------------------------------------------------
;TDERR_NotSpecified EQU 20 ; general catchall
;TDERR_NoSecHdr EQU 21 ; couldn't even find a sector
;TDERR_BadSecPreamble EQU 22 ; sector looked wrong
;TDERR_BadSecID EQU 23 ; ditto
;TDERR_BadHdrSum EQU 24 ; header had incorrect checksum
;TDERR_BadSecSum EQU 25 ; data had incorrect checksum
;TDERR_TooFewSecs EQU 26 ; couldn't find enough sectors
;TDERR_BadSecHdr EQU 27 ; another "sector looked wrong"
;TDERR_WriteProt EQU 28 ; can't write to a protected disk
;TDERR_DiskChanged EQU 29 ; no disk in the drive
;TDERR_SeekError EQU 30 ; couldn't find track 0
;TDERR_NoMem EQU 31 ; ran out of memory
;TDERR_BadUnitNum EQU 32 ; asked for a unit > NUMUNITS
;TDERR_BadDriveType EQU 33 ; not a drive that trackdisk groks
;TDERR_DriveInUse EQU 34 ; someone else allocated the drive
;TDERR_PostReset EQU 35 ; user hit reset; awaiting doom
; STRUCTURE TIMEVAL,0
; ULONG TV_SECS
; ULONG TV_MICRO
; LABEL TV_SIZE
;Unit structure
*--------------------------------------------------------------------
*
* Public portion of unit structure
*
*--------------------------------------------------------------------
;*------ UNIT_FLAG definitions:
;These are bogus, but I won't re-define them.
; BITDEF UNIT,ACTIVE,0 ; driver is active
; BITDEF UNIT,INTASK,1 ; running in driver's task
BITDEF UNIT,DiskInDrive,2
BITDEF UNIT,WriteProtected,3
; STRUCTURE TDU_PUBLICUNIT,UNIT_SIZE
; UWORD TDU_COMP01TRACK ; track for first precomp
; UWORD TDU_COMP10TRACK ; track for second precomp
; UWORD TDU_COMP11TRACK ; track for third precomp
; ULONG TDU_STEPDELAY ; time to wait after stepping
; ULONG TDU_SETTLEDELAY ; time to wait after seeking
; UBYTE TDU_RETRYCNT ; # of times to retry
; UBYTE TDU_PUBFLAGS ; public flags, see below
; UWORD TDU_CURRTRK ; track heads are over
; (ONLY ACCESS WHILE UNIT IS STOPPED!)
; ULONG TDU_CALIBRATEDELAY ; time to wait after stepping
; for recalibrate
; ULONG TDU_COUNTER ; counter for disk changes
; (ONLY ACCESS WHILE UNIT IS STOPPED!)
; LABEL TDU_PUBLICUNITSIZE
;*--------------------------------------------------------------------
STRUCTURE MyUnit,TDU_PUBLICUNITSIZE
STRUCT Drive_LastCheck,TV_SIZE ;last time diskchange was checked
STRUCT ChangeIntList,MLH_SIZE ;must be initialized!!!
LONG TDRemoveInt
BYTE UnitNum
BYTE unit_pad1
;The following will be InitStruct'ed in GetDriveType
LABEL DriveParams
BYTE DriveType
BYTE NumTracks
LONG BytesPerDisk
LONG SectorMask
WORD GapCount
LONG SectorsPerTrack
LONG RawBufSize
WORD WriteDskLen
WORD ReadDskLen
WORD VerifyDskLen
WORD MaxValidSec
WORD FirstSector
LABEL EndDriveParams
;MUST be word-aligned!!!
LABEL MyUnit_Sizeof
DriveParams_Sizeof equ EndDriveParams-DriveParams
;*
;* Flags for TDU_PUBFLAGS:
;*
; BITDEF TDP,NOCLICK,0 ; set to enable noclickstart
BITDEF TDP,VERIFY,1
;Device global structure -- accessed as positive offsets from device base
;Note: The stuff in LIB_SIZE is *required*. Anything else is up to you...
STRUCTURE DeviceGlobals,LIB_SIZE
STRUCT TaskPort,MP_SIZE ;MsgPort for task
STRUCT Task,TC_SIZE
STRUCT TaskStack,STACKSIZE
STRUCT TempTimeVal,TV_SIZE
LONG SegList ;not used for ROM version
STRUCT TimerIORequest,IOTV_SIZE
STRUCT TimerPort,MP_SIZE
STRUCT LastCheck,TV_SIZE ;last time diskchange was checked
STRUCT DiskResourceUnit,DRU_SIZE ;must be initialized!!!
STRUCT DiskResourcePort,MP_SIZE ;must be initialized!!!
;Allocated memory pointers
LONG RawBuffer
LONG DecodedBuffer
LONG VerifyBuffer
STRUCT SectorLabels,22*16
STRUCT Unit0,MyUnit_Sizeof
STRUCT Unit1,MyUnit_Sizeof
STRUCT Unit2,MyUnit_Sizeof
STRUCT Unit3,MyUnit_Sizeof
LONG GraphBase
LONG IntBase
LONG DiskResourceBase
LONG CIABase
WORD SyncCount
WORD IndexDskLen
WORD BlockIntDskLen
LONG WriteMap
LABEL StartSigs
LONG SyncSig
LONG BlockSig
LABEL EndSigs
BYTE BufferTrack
BYTE BufferDrive
BYTE DEV_FLAGS
BYTE MotorState
BYTE InquireBits
LABEL MyDev_Sizeof
NumSigs equ (EndSigs-StartSigs)/4
;Bit definitions for DEV_FLAGS:
BITDEF DEV,Dirty,0 ;buffered track has been changed
BITDEF DEV,Stopped,1 ;device is stopped
BITDEF DEV,Verify,2 ;used by BlockInt for verify
;A romtag structure. After your driver is brought in from disk, the
;disk image will be scanned for this structure to discover magic constants
;about you (such as where to start running you from...).
RomTag:
dc.w RTC_MATCHWORD ;$4AFC ('illegal' opcode)
dc.l RomTag
dc.l EndCode ;pointer to end of code
dc.b RTF_AUTOINIT+RTF_COLDSTART ;set things up automatically
dc.b VERSION
dc.b NT_DEVICE ;module type (either device or library)
dc.b MYPRI ;usually not important
dc.l Name ;name used in OpenDevice
dc.l IDString ;optional
dc.l Init ;more init info
Name: dc.b 'trackdisk.device',0
even
IDString: dc.b 'Hackdisk V2.02 - Copyright © 1992,93 by Dan Babcock',$a,0
even
DiskResourceName:
dc.b 'disk.resource',0
TimerName:
dc.b 'timer.device',0
GraphName:
dc.b 'graphics.library',0
CIAName: dc.b 'ciab.resource',0
IntName: dc.b 'intuition.library',0
even
;The romtag specified that we were "RTF_AUTOINIT". This means that the
;RT_INIT structure member points to one of these tables below. If the
;AUTOINIT bit was not set then RT_INIT would point to a routine to run.
Init: dc.l MyDev_Sizeof ;data space size (at least
;LIB_SIZE!!)
dc.l FuncTable ;pointer to function initializers
dc.l DataTable ;pointer to data initializers
dc.l InitRoutine ;routine to run
FuncTable:
dc.w -1 ;this indicates that the following are offsets,
;rather than absolute addresses
dc.w Open-FuncTable ;standard system routines
dc.w _Close-FuncTable
dc.w Expunge-FuncTable
dc.w .Null-FuncTable ;Reserved for future use!
dc.w BeginIO-FuncTable ;device definitions
dc.w AbortIO-FuncTable
dc.w -1 ;function table end marker
.Null: moveq #0,d0
rts
;The data table initializes static data structures. The format is
;specified in exec/InitStruct routine's manual pages. The
;INITBYTE/INITWORD/INITLONG macros are in the file "exec/initializers.i".
;The first argument is the offset from the device base for this
;byte/word/long. The second argument is the value to put in that cell.
;The table is null terminated
DataTable:
INITBYTE LN_TYPE,NT_DEVICE
INITLONG LN_NAME,Name
INITBYTE LIB_FLAGS,LIBF_SUMUSED+LIBF_CHANGED
INITWORD LIB_VERSION,VERSION
INITWORD LIB_REVISION,REVISION
INITLONG LIB_IDSTRING,IDString
dc.w 0 ;terminate list (only one byte needed)
CmdTable: dc.w Invalid-CmdTable ;0 CMD_INVALID
dc.w Invalid-CmdTable ;1 CMD_RESET
dc.w Read-CmdTable ;2 CMD_READ
dc.w Write-CmdTable ;3 CMD_WRITE / ETD_
dc.w Update-CmdTable ;4 CMD_UPDATE / ETD_
dc.w Clear-CmdTable ;5 CMD_CLEAR / ETD_
dc.w MyStop-CmdTable ;6 CMD_STOP / ETD_
dc.w Start-CmdTable ;7 CMD_START
dc.w Invalid-CmdTable ;8 CMD_FLUSH
dc.w Motor-CmdTable ;9 TD_MOTOR / ETD_
dc.w TDSeek-CmdTable ;10 TD_SEEK / ETD_
dc.w Format-CmdTable ;11 TD_FORMAT
dc.w TDRemove-CmdTable ;12 TD_REMOVE
dc.w ChangeNum-CmdTable ;13 TD_CHANGENUM
dc.w ChangeState-CmdTable ;14 TD_CHANGESTATE
dc.w ProtStatus-CmdTable ;15 TD_PROTSTATUS
dc.w TDRawRead-CmdTable ;16 TD_RAWREAD
dc.w RawWrite-CmdTable ;17 TD_RAWWRITE
dc.w TDGetDriveType-CmdTable ;18 TD_GETDRIVETYPE
dc.w GetNumTracks-CmdTable ;19 TD_GETNUMTRACKS
dc.w AddChangeInt-CmdTable ;20 TD_ADDCHANGEINT
dc.w RemChangeInt-CmdTable ;21 TD_REMCHANGEINT
dc.w GetGeometry-CmdTable ;22 TD_GETGEOMETRY
dc.w Invalid-CmdTable ;23 TD_EJECT
EndCmdTable:
HighestCommand equ ((EndCmdTable-CmdTable)/2)-1
Invalid: move.b #IOERR_NOCMD,IO_ERROR(A_IO)
rts
InitRoutine:
;A0 - segment
;D0 - device ptr
;Returns with the device ptr still in D0 if successful, otherwise zero.
PUTDEBUG <'InitRoutine: Entered'>
movem.l d1-d7/a0-a6,-(sp)
move.l d0,A_DEVICE
move.l a0,SegList(A_DEVICE)
move.l 4,a6
;Initialize ports
lea TaskPort(A_DEVICE),a0
bsr InitPort
lea TimerPort(A_DEVICE),a0
bsr InitPort
;Invalidate track buffer
bsr Clear
;Open libraries/resources
lea DiskResourceName(pc),a1 ;'disk.resource'
SYS OpenResource
move.l d0,DiskResourceBase(A_DEVICE)
beq .Failed
;Try to allocate all the units.
move.l d0,a6
moveq #0,d2
.AllocDisk:
move.l d2,d0
IFND _LVOAllocUnit
_LVOAllocUnit equ -6
ENDC
SYS AllocUnit
addq.l #1,d2
cmp.w #4,d2
blo .AllocDisk
move.l 4,a6
lea CIAName(pc),a1 ;'ciab.resource'
SYS OpenResource
move.l d0,CIABase(A_DEVICE)
beq .Failed
lea GraphName(pc),a1
SYS OldOpenLibrary
move.l d0,GraphBase(A_DEVICE)
beq .Failed
lea IntName(pc),a1
SYS OldOpenLibrary
move.l d0,IntBase(A_DEVICE)
beq .Failed
;Allocate buffer memory.
move.l #DISK_RawBufSize*2,d0
move.l #MEMF_CHIP,d1
SYS AllocMem
move.l d0,RawBuffer(A_DEVICE)
beq .Failed
move.l #512*22,d0
move.l #MEMF_CHIP,d1
SYS AllocMem
move.l d0,DecodedBuffer(A_DEVICE)
beq .Failed
move.l #(1088*22)+2,d0
move.l #MEMF_CHIP,d1
SYS AllocMem
move.l d0,VerifyBuffer(A_DEVICE)
beq .Failed
;Set up disk.resource structure.
lea DiskResourcePort(A_DEVICE),a0
bsr InitPort
move.l a0,DiskResourceUnit+MN_REPLYPORT(A_DEVICE)
; move.b #PA_SIGNAL,MP_FLAGS(a0) ;not needed (0)
lea Name(pc),a0
move.l a0,DiskResourceUnit+LN_NAME(A_DEVICE)
move.l A_DEVICE,DiskResourceUnit+DRU_DISCBLOCK+IS_DATA(A_DEVICE)
move.l A_DEVICE,DiskResourceUnit+DRU_DISCSYNC+IS_DATA(A_DEVICE)
move.l A_DEVICE,DiskResourceUnit+DRU_INDEX+IS_DATA(A_DEVICE)
lea BlockInt(pc),a0
move.l a0,DiskResourceUnit+DRU_DISCBLOCK+IS_CODE(A_DEVICE)
lea SyncInt(pc),a0
move.l a0,DiskResourceUnit+DRU_DISCSYNC+IS_CODE(A_DEVICE)
lea IndexInt(pc),a0
move.l a0,DiskResourceUnit+DRU_INDEX+IS_CODE(A_DEVICE)
moveq #NT_INTERRUPT,d0
move.b d0,DiskResourceUnit+DRU_DISCBLOCK+LN_TYPE(A_DEVICE)
move.b d0,DiskResourceUnit+DRU_DISCSYNC+LN_TYPE(A_DEVICE)
move.b d0,DiskResourceUnit+DRU_INDEX+LN_TYPE(A_DEVICE)
;Initialize timer IORequest (except signal)
lea TimerPort(A_DEVICE),a0
; move.b #PA_SIGNAL,MP_FLAGS(a0) ;not needed (0)
move.l a0,TimerIORequest+MN_REPLYPORT(A_DEVICE)
lea TimerName(pc),a0
moveq #UNIT_MICROHZ,d0
lea TimerIORequest(A_DEVICE),a1
moveq #0,d1
SYS OpenDevice
tst.l d0
bne .Failed
;*** Set up unit structures
moveq #0,d2
lea Unit0(A_DEVICE),A_UNIT
.UnitLoop:
move.b d2,UnitNum(A_UNIT)
lea ChangeIntList(A_UNIT),a0
NEWLIST a0
bset #1,TDU_PUBFLAGS(A_UNIT) ;verify ON!
move.b #RETRYCNT,TDU_RETRYCNT(A_UNIT)
lea MyUnit_Sizeof(A_UNIT),A_UNIT
addq.b #1,d2
cmp.b #MD_NUMUNITS,d2
blo .UnitLoop
;Set up task
move.b #PA_IGNORE,TaskPort+MP_FLAGS(A_DEVICE)
move.b #NT_MSGPORT,TaskPort+LN_TYPE(A_DEVICE)
lea Task(A_DEVICE),a1 ;Task Control Block
;To make CrossDOS happy under 1.2/1.3
st TC_Userdata(a1)
lea Name(pc),a0
move.l a0,LN_NAME(a1)
move.b #TASKPRI,LN_PRI(a1)
move.b #NT_TASK,LN_TYPE(a1)
lea TaskCode(pc),a2 ;initial PC
sub.l a3,a3 ;final PC
lea TaskStack(A_DEVICE),a0
move.l a0,TC_SPLOWER(a1)
lea STACKSIZE(a0),a0
move.l a0,TC_SPUPPER(a1)
move.l A_DEVICE,-(a0) ;pass device pointer to task
move.l a0,TC_SPREG(a1)
move.l a1,TaskPort+MP_SIGTASK(A_DEVICE)
SYS AddTask
move.l A_DEVICE,d0
PUTDEBUG <'InitRoutine: Done'>
.End: movem.l (sp)+,d1-d7/a0-a6
rts
.Failed:
PUTDEBUG <'InitRoutine: Failed'>
; bsr CloseEverything
moveq #0,d0
bra .End
InitPort:
;Enter with pointer to port in A0. All registers preserved.
push a0
lea MP_MSGLIST(a0),a0
NEWLIST a0
pop a0
rts
comment |
CloseEverything:
;This routines cleans up device stuff. Enter with A_DEVICE.
movem.l d0-d1/a0-a1/a6,-(sp)
move.l 4,a6
;Close timer
tst.b TimerIORequest+IO_ERROR(A_DEVICE)
beq .SkipTimer
lea TimerIORequest(A_DEVICE),a1
SYS CloseDevice
.SkipTimer:
;Remove task
lea Name(pc),a1
SYS FindTask
tst.l d0
beq .SkipTask
move.l d0,a1
SYS RemTask
.SkipTask:
;Close libraries
move.l GraphBase(A_DEVICE),d0
beq .SkipGfx
move.l d0,a1
SYS CloseLibrary
.SkipGfx:
move.l IntBase(A_DEVICE),d0
beq .SkipInt
move.l d0,a1
SYS CloseLibrary
.SkipInt:
;Free CHIP RAM. (Note: This will change when I support other drive types).
move.l RawBuffer(A_DEVICE),d0
beq .SkipRaw
move.l d0,a1
move.l #DISK_RawBufSize,d0
SYS FreeMem
.SkipRaw:
move.l DecodedBuffer(A_DEVICE),d0
beq .SkipDecoded
move.l d0,a1
move.l #512*11,d0
SYS FreeMem
.SkipDecoded:
move.l VerifyBuffer(A_DEVICE),d0
beq .SkipVerify
move.l d0,a1
move.l #(1088*11)+2,d0
SYS FreeMem
.SkipVerify:
.End: movem.l (sp)+,d0-d1/a0-a1/a6
rts
|
;******************************* Task code ******************************
TaskCode:
PUTDEBUG <'Task: Entered'>
move.l 4,a6
;Grab the arguments passed down from our parent
move.l 4(sp),A_DEVICE ;Device pointer
;General initialization
lea StartSigs(A_DEVICE),a2
moveq #NumSigs-1,d2 ;Number of signals to allocate-1
.SigLoop:
moveq #-1,d0 ;-1 is any signal at all
SYS AllocSignal ;Allocate signals for I/O interrupts
moveq #0,d1 ;Convert bit number signal mask
bset d0,d1
move.l d1,(a2)+ ;Save in unit structure
dbra d2,.SigLoop
moveq #-1,d0 ;-1 is any signal at all
SYS AllocSignal ;Allocate a signal
move.b d0,TaskPort+MP_SIGBIT(A_DEVICE)
move.b #PA_SIGNAL,TaskPort+MP_FLAGS(A_DEVICE) ;Make message port "live"
moveq #-1,d0 ;-1 is any signal at all
SYS AllocSignal ;Allocate a signal
move.b d0,TimerPort+MP_SIGBIT(A_DEVICE)
move.l ThisTask(a6),TimerPort+MP_SIGTASK(A_DEVICE)
moveq #-1,d0 ;-1 is any signal at all
SYS AllocSignal ;Allocate a signal
move.b d0,DiskResourcePort+MP_SIGBIT(A_DEVICE)
move.l ThisTask(a6),DiskResourcePort+MP_SIGTASK(A_DEVICE)
bsr GetDrive
bsr Inquire
;Get drive types
moveq #MD_NUMUNITS-1,d0
lea Unit0(A_DEVICE),A_UNIT
.TypeLoop:
bsr GetDriveType
lea MyUnit_Sizeof(A_UNIT),A_UNIT
dbra d0,.TypeLoop
bsr SeekZeroAll ;Send all drives to track zero.
bsr FreeDrive
bra .NextMessage
;Main loop: Wait for a new message and handle disk changes.
.MainLoop:
moveq #0,d0
move.b MP_SIGBIT+TaskPort(A_DEVICE),d1
bset d1,d0
move.l #500000,d1
bsr TimeOutWait
tst.l d0
bne .NextMessage
;0.5 seconds have passed without receiving any work, so we check for disk
;changes, then drop into .NextMessage.
;Note: A6 undefined.
.CheckDiskChange:
moveq #MD_NUMUNITS-1,d2
move.b InquireBits(A_DEVICE),d3
lea Unit0(A_DEVICE),A_UNIT
bsr GetDrive
move.l TimerIORequest+IO_DEVICE(A_DEVICE),a6
;Algorithm (for each unit):
;Check hardware diskchange bit. If disk was ejected, seek to track zero,
;clear DiskInDrive bit. End.
;If no disk in drive, check NoClick bit. If set, immediately step toward
;track zero (using custom step routine). If clear, check the
;Drive_LastChecked time against the current time. If less than 2.5 seconds,
;End. If >2.5 seconds, step the head toward track zero, unless already on
;track zero. Check the hardware diskchange bit. If a disk is present in the
;drive set the DiskInDrive bit and check the hardware write protect status,
;setting WriteProtected as needed. End.
;Note: We also increment the diskchange counter in the public part of the
;unit structure, if a disk was inserted or removed.
.UnitLoop:
move.b UnitNum(A_UNIT),d4
btst d4,d3
beq .NoCheck
lea TempTimeVal(A_DEVICE),a0
bsr GetSysTime
btst #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
beq .ThinkNoDisk
bsr SelectDriveSameMotor
btst #CIAB_DSKCHANGE,ciaapra
bne .NoChange
;Disk was removed.
bclr #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
bsr SeekZero
bsr MotorOff
bsr Clear
bra .Change
.ThinkNoDisk:
btst #TDPB_NOCLICK,TDU_PUBFLAGS(A_UNIT)
beq .Click
bsr SelectDriveSameMotor
bset #CIAB_DSKDIREC,ciabprb ;set to "out" (lower tracks)
bset #CIAB_DSKSTEP,ciabprb
bclr #CIAB_DSKSTEP,ciabprb ;step head
bset #CIAB_DSKSTEP,ciabprb
bsr Deselect
move.l TDU_SETTLEDELAY(A_UNIT),d0
bsr delay
bsr SelectDriveSameMotor
bra .ChkChg
.Click:
lea Drive_LastCheck(A_UNIT),a1
SYS SubTime
cmp.l #2,TV_SECS(a0)
blo .NoCheck
bhi .SkipMicro
cmp.l #500000,TV_MICRO(a0)
blo .NoCheck
.SkipMicro:
lea Drive_LastCheck(A_UNIT),a0
bsr GetSysTime
move.b UnitNum(A_UNIT),d2 ;for Seek
move.w TDU_CURRTRK(A_UNIT),d3
subq.w #2,d3
bpl .NotZero
moveq #2,d3
.NotZero: bsr SelectDriveSameMotor
bsr SeekNoUpdate
.ChkChg: btst #CIAB_DSKCHANGE,ciaapra
beq .NoChange ;no disk in drive
bset #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
bclr #UNITB_WriteProtected,UNIT_FLAGS(A_UNIT)
btst #CIAB_DSKPROT,ciaapra
bne .Change ;not write protected
bset #UNITB_WriteProtected,UNIT_FLAGS(A_UNIT)
.Change: bsr GetDriveType ;reread drive type (leaves drive deselected)
; bsr Deselect
addq.l #1,TDU_COUNTER(A_UNIT)
;This code notifies users of TD_REMOVE and TD_ADDCHANGEINT via Cause.
push a6
move.l 4,a6
SYS Forbid
move.l TDRemoveInt(A_UNIT),d0
beq .NoRemoveInt
move.l d0,a1
SYS Cause
.NoRemoveInt:
move.l ChangeIntList+LH_HEAD(A_UNIT),a2
.IntLoop: move.l (a2),d0
beq .DoneInt
move.l IO_DATA(a2),a1
move.l d0,a2
SYS Cause
bra .IntLoop
.DoneInt: SYS Permit
pop a6
.NoChange:
lea LastCheck(A_DEVICE),a0
bsr GetSysTime
.NoCheck:
lea MyUnit_Sizeof(A_UNIT),A_UNIT
dbra d2,.UnitLoop
move.l 4,a6
bsr FreeDrive
.NextMessage:
btst #DEVB_Stopped,DEV_FLAGS(A_DEVICE) ;See if we are stopped
bne .MainLoop ;device is stopped, so ignore messages
lea TaskPort(A_DEVICE),a0
SYS GetMsg ;Get the next request
tst.l d0
beq .MainLoop ;no message?
move.l d0,A_IO ;Do this request
move.l IO_UNIT(A_IO),A_UNIT
;Handle TDB_EXTCOM and dispatch command
move.w IO_COMMAND(A_IO),d0
IFNE INFO_LEVEL
swap d0
clr.w d0
swap d0
move.l d0,-(sp)
PUTDEBUG <'Command=%ld'>
addq.l #4,sp
ENDC
bclr #TDB_EXTCOM,d0
beq .NoExt
move.l TDU_COUNTER(A_UNIT),d1
cmp.l IOTD_COUNT(A_IO),d1
bls .NoExt
.ChgErr: PUTDEBUG <'Change error!'>
move.b #TDERR_DiskChanged,IO_ERROR(A_IO)
bra .Reply
.NoExt: move.l #%000000111000111000011100,d1 ;specifies which commands should check for disk
btst d0,d1
beq .NoDiskNeeded
btst #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
beq .ChgErr
.NoDiskNeeded:
bsr GetDrive
lea CmdTable(pc),a0
add.w d0,d0
add.w (a0,d0.w),a0
moveq #0,d7 ;clear error flag
jsr (a0)
bsr FreeDrive
cmp.w #TD_ADDCHANGEINT,IO_COMMAND(A_IO)
beq .SkipReply
.Reply: move.l A_IO,a1
SYS ReplyMsg
.SkipReply:
;If at least 0.5 seconds has passed since last checking diskchange, do so
;now.
move.l TimerIORequest+IO_DEVICE(A_DEVICE),a6
lea TempTimeVal(A_DEVICE),a0
bsr GetSysTime
lea LastCheck(A_DEVICE),a1
SYS SubTime
tst.l TV_SECS(a0)
bne .CheckDiskChange
cmp.l #500000,TV_MICRO(a0)
bhs .CheckDiskChange
lea LastCheck(A_DEVICE),a0
bsr GetSysTime
move.l 4,a6
bra .NextMessage
GetSysTime:
cmp.w #36,LIB_VERSION(a6)
bls .OldKS
jmp _LVOGetSysTime(a6)
.OldKS:
;Compatibility routine for 1.2/1.3
movem.l d0-d1/a0-a2/a6,-(sp)
move.l a0,a2
lea TimerIORequest(A_DEVICE),a1
move.l 4,a6
move.w #TR_GETSYSTIME,IO_COMMAND(a1)
SYS DoIO
lea TimerIORequest(A_DEVICE),a1
move.l IOTV_TIME+TV_SECS(a1),TV_SECS(a2)
move.l IOTV_TIME+TV_MICRO(a1),TV_MICRO(a2)
movem.l (sp)+,d0-d1/a0-a2/a6
rts
;******************** External device routines ***********************
Open:
;A6 - device ptr
;A1 - IORequest
;D0 - unit number
;D1 - flags (not used)
;Important: On error, MUST put -1 in IO_DEVICE. This routine has no return
;value in d0 (return code is in IO_ERROR).
PUTDEBUG <'Open: Entered'>
moveq #MD_NUMUNITS,d1
cmp.l d1,d0
bhs .Error
btst d0,InquireBits(a6)
beq .Error
lea Unit0(a6),a0
mulu.w #MyUnit_Sizeof,d0
add.l d0,a0
move.l a0,IO_UNIT(a1)
addq.w #1,LIB_OPENCNT(a6)
clr.b IO_ERROR(a1) ;no error
move.b #NT_REPLYMSG,LN_TYPE(a1) ;Mark IORequest as "complete"
PUTDEBUG <'Open: Done'>
rts
.Error:
PUTDEBUG <'Open: Failed'>
;VirusX didn't like this.
; move.b #IOERR_OPENFAIL,IO_ERROR(a1)
;NOTE: Trackdisk will return TDERR_BadDriveType if TDB_ALLOW_NON_3_5 flag set
;in "flags" and the drive is not present. (This is just trivia).
move.b #TDERR_BadUnitNum,IO_ERROR(a1)
moveq #-1,d0
move.l d0,IO_DEVICE(a1)
rts
_Close:
;A6 - device ptr
;A1 - IORequest
;Must return either 0 or, if the device wishes to be unloaded, the segment
;list.
PUTDEBUG <'Close: Called'>
moveq #0,d0
rts
Expunge:
;A6 - device ptr
;Must return either 0 or SegList ptr in D0.
moveq #0,d0
rts
BeginIO:
;A1 - IORequest
;A6 - device ptr
movem.l d7/a3-a6,-(sp)
move.l a1,A_IO
move.l IO_UNIT(A_IO),A_UNIT
move.l a6,A_DEVICE
move.l 4,a6
clr.b IO_ERROR(A_IO)
move.b #NT_MESSAGE,LN_TYPE(A_IO) ;So WaitIO() is guaranteed to work
;Decide whether the command is immediate or queued
move.w IO_COMMAND(A_IO),d1
bclr #15,d1
cmp.w #HighestCommand,d1
bhi .BadCmd
move.l #%111011001111000110000011,d0 ;specifies what commands are immediate
btst d1,d0
beq .Queue
add.w d1,d1
lea CmdTable(pc),a0
add.w (a0,d1.w),a0
moveq #0,d7 ;clear error flag
jsr (a0)
.Reply: btst #IOB_QUICK,IO_FLAGS(A_IO)
bne .End
move.l A_IO,a1
SYS ReplyMsg
.End: movem.l (sp)+,d7/a3-a6
rts
.Queue: bclr #IOB_QUICK,IO_FLAGS(A_IO) ;We did NOT complete this quickly
lea TaskPort(A_DEVICE),a0
move.l A_IO,a1
SYS PutMsg
bra .End
.BadCmd: move.b #IOERR_NOCMD,IO_ERROR(a1)
bra .Reply
AbortIO:
;A6 - device ptr
;A1 - IORequest
move.b #IOERR_ABORTED,IO_ERROR(a1) ;We always say we succeed(ed)
moveq #0,d0 ;another success code
rts
;**************** Device command (IO_COMMAND) routines *************************
;Note: A6 = ExecBase upon entering a command routine.
;The low-level routines load $DFF000 into A6 when needed.
SaveRegs setrl d0-d3/d6-d7/a0-a2/a6
Read:
PUTDEBUG <'Read: Called'>
movem.l SaveRegs,-(sp)
bsr RWSetup
tst.l d7
bne FinishRW
bsr DISK_Update ;required because of complex write scheme
;Check for sector label nonsense
tst.b IO_COMMAND(A_IO)
bpl .NoLabel
move.l IOTD_SECLABEL(A_IO),d2
bne ReadSecLabel
.NoLabel:
bsr DISK_Read
PUTDEBUG <'Read: Done'>
bra FinishRW
Format:
Write: movem.l SaveRegs,-(sp)
bsr RWSetup
tst.l d7
bne FinishRW
;Check for sector label nonsense
tst.b IO_COMMAND(A_IO)
bpl .NoLabel
move.l IOTD_SECLABEL(A_IO),d2
bne WriteSecLabel
.NoLabel:
bsr DISK_Write
FinishRW: clr.l IO_ACTUAL(A_IO)
move.b d7,IO_ERROR(A_IO)
bne .Skip
move.l IO_LENGTH(A_IO),IO_ACTUAL(A_IO)
.Skip: movem.l (sp)+,SaveRegs
rts
RWSetup:
move.l SectorsPerTrack(A_UNIT),d6
lsl.l #8,d6
add.l d6,d6 ;*512
move.l #_custom,a6
move.l IO_LENGTH(A_IO),d0
move.l IO_OFFSET(A_IO),d1
move.l IO_DATA(A_IO),a0
move.l d1,d2 ;offset
add.l d0,d2 ;length
cmp.l BytesPerDisk(A_UNIT),d2
bhi .Error
bsr SelectDrive
bra SelectSide
.Error: moveq #DISK_BadParameter,d7
rts
;Here are the routines for dealing with read/write requests that involve
;the sector label. Note that, unlike the usual read/write routines of my
;trackdisk, the normal parameter restrictions (e.g. IO_OFFSET and IO_LENGTH
;must be a multiple of 512) MUST be observed. And I don't check for illegal
;parameters, either. These routines are optimized for compactness rather
;than performance because they are rarely (if ever) used.
;Enter with:
;IO_LENGTH: D0
;IO_OFFSET: D1
;IO_DATA: A0
ReadSecLabel:
PUTDEBUG <'READSECLABEL!'>
move.l d2,a1
move.l d0,d2
move.l #512,d0
.Read: bsr DISK_Read
move.l d1,d3
add.l d0,d1
add.l d0,a0
divu.w d6,d3 ;offset/tracksize
swap d3 ;remainder=sector# (in bytes)
lsr.w #5,d3 ;divide by 32 to get label offset
lea SectorLabels(A_DEVICE),a2
lea (a2,d3.w),a2 ;get pointer to label
moveq #3,d3
.. move.l (a2)+,(a1)+ ;copy label
dbra d3,..
sub.l d0,d2
bne .Read
bra FinishRW
WriteSecLabel:
PUTDEBUG <'WRITESECLABEL!'>
move.l d2,a1
move.l d0,d2
.Write:
move.l d1,d3
divu.w d6,d3 ;offset/tracksize
swap d3 ;remainder=sector# (in bytes)
lsr.w #5,d3 ;divide by 32 to get label offset
;We check to see whether we can write a full track. (Somehow, I can't
;ignore performance completely :-)).
tst.w d3
bne .Read
cmp.l d6,d0
bhs .FullTrack
.Read: moveq #0,d0
bsr DISK_Read ;force a track read
lea SectorLabels(A_DEVICE),a2
lea (a2,d3.w),a2 ;get pointer to label
moveq #3,d3
.. move.l (a1)+,(a2)+ ;copy label
dbra d3,..
move.l #512,d0
.L1: bsr DISK_Write
add.l d0,d1
add.l d0,a0
sub.l d0,d2
bne .Write
bra FinishRW
.FullTrack:
lea SectorLabels(A_DEVICE),a2
moveq #((16*22)/4)-1,d3
.. move.l (a1)+,(a2)+ ;copy all sector labels
dbra d3,..
move.l d6,d0
bra .L1
Update: move.l d7,-(sp)
moveq #0,d7
bsr DISK_Update
move.b d7,IO_ERROR(A_IO)
move.l (sp)+,d7
rts
Clear:
;This routine marks the track buffer as invalid.
st BufferDrive(A_DEVICE)
bclr #DEVB_Dirty,DEV_FLAGS(A_DEVICE)
rts
Motor:
;Controls the drive motor
;If IO_LENGTH=1, motor on
; IO_LENGTH=0, motor off
;Old motor state (0=off, anything else means on) stored in IO_ACTUAL.
push d2
clr.l IO_ACTUAL(A_IO)
move.b UnitNum(A_UNIT),d2
btst d2,MotorState(A_DEVICE)
beq .WasOff
move.l #1,IO_ACTUAL(A_IO)
.WasOff: tst.l IO_LENGTH(A_IO)
beq .Off
;Turn motor on
bsr SelectDrive
bra .End
.Off:
;Turn motor off
bclr d2,MotorState(A_DEVICE)
bsr Deselect
bset #CIAB_DSKMOTOR,ciabprb ;motor off
addq.b #3,d2
bclr d2,ciabprb ;select drive X
.End: pop d2
rts
ChangeState:
;Returns whether there is a disk currently in the drive
;IO_ACTUAL=0 if disk in drive
;* EXECUTED IMMEDIATELY, POSSIBILY WITHIN AN INTERRUPT *
clr.l IO_ACTUAL(A_IO)
btst #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
bne .End
move.l #1,IO_ACTUAL(A_IO) ;no disk in drive
.End: rts
ChangeNum:
;* EXECUTED IMMEDIATELY, POSSIBILY WITHIN AN INTERRUPT *
move.l TDU_COUNTER(A_UNIT),IO_ACTUAL(A_IO)
rts
ProtStatus:
;IO_ACTUAL=0 if disk is not write protected
;* EXECUTED IMMEDIATELY, POSSIBILY WITHIN AN INTERRUPT *
;Very important for compatibility: we must return an error (disk changed)
;if there's no disk in the drive.
btst #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
bne .OK
move.b #TDERR_DiskChanged,IO_ERROR(A_IO)
rts
.OK: clr.l IO_ACTUAL(A_IO)
btst #UNITB_WriteProtected,UNIT_FLAGS(A_UNIT)
beq .End ;not write protected
move.l #1,IO_ACTUAL(A_IO) ;no disk in drive
.End: rts
TDGetDriveType:
;Returns type of drive in IO_ACTUAL
;* EXECUTED IMMEDIATELY, POSSIBILY WITHIN AN INTERRUPT *
clr.l IO_ACTUAL(A_IO)
move.b DriveType(A_UNIT),IO_ACTUAL+3(A_IO)
rts
GetNumTracks:
;Returns number of tracks (note: not cylinders) in IO_ACTUAL
;* EXECUTED IMMEDIATELY, POSSIBILY WITHIN AN INTERRUPT *
clr.l IO_ACTUAL(A_IO)
move.b NumTracks(A_UNIT),IO_ACTUAL+3(A_IO)
rts
GetGeometry:
;Fills in DriveGeometry structure pointed to by IO_DATA
movem.l d0-d1/a0,-(sp)
move.l IO_DATA(A_IO),a0
move.l #512,dg_SectorSize(a0) ;in bytes
move.l #2,dg_Heads(a0) ;number of surfaces
clr.l dg_BufMemType(a0) ;preferred buffer memory type
clr.b dg_DeviceType(a0) ;codes as defined in the SCSI-2 spec
move.b #DGF_REMOVABLE,dg_Flags(a0) ;flags, including removable
clr.w dg_Reserved(a0)
move.l SectorsPerTrack(A_UNIT),d0
move.l d0,dg_TrackSectors(a0) ;number of sectors/track
add.l d0,d0
move.l d0,dg_CylSectors(a0) ;number of sectors/cylinder
moveq #0,d1
move.b NumTracks(A_UNIT),d1
lsr.l #1,d1 ;convert tracks -> cylinders
move.l d1,dg_Cylinders(a0) ;number of cylinders
mulu.w d0,d1
move.l d1,dg_TotalSectors(a0) ;total # of sectors on drive
movem.l (sp)+,d0-d1/a0
rts
MyStop: bset #DEVB_Stopped,DEV_FLAGS(A_DEVICE)
bsr Update
bra Clear
Start: movem.l d0-d1/a0-a1,-(sp)
bclr #DEVB_Stopped,DEV_FLAGS(A_DEVICE)
moveq #0,d0
move.b MP_SIGBIT+TaskPort(A_DEVICE),d1
bset d1,d0
lea Task(A_DEVICE),a1
SYS Signal
movem.l (sp)+,d0-d1/a0-a1
rts
;Note: This seek routine can't really be depended on by user programs,
;because the disk.resource will corrupt the side select bit.
TDSeek: movem.l d0/d3,-(sp)
bsr SelectDriveSameMotor
move.l IO_OFFSET(A_IO),d3
cmp.l BytesPerDisk(A_UNIT),d3
bhs .Error
move.l SectorsPerTrack(A_UNIT),d0
lsl.l #8,d0
add.l d0,d0 ;*512 = bytes per track
divu.w d0,d3
bsr Seek
.End: movem.l (sp)+,d0/d3
rts
.Error: move.b #TDERR_NotSpecified,IO_ERROR(A_IO)
bra .End
TDRawRead:
movem.l d0-d1/d3/a6,-(sp)
move.l #_custom,a6
moveq #0,d1
bsr DoRaw
movem.l (sp)+,d0-d1/d3/a6
rts
RawWrite:
movem.l d0-d1/d3/a6,-(sp)
move.l #_custom,a6
btst #UNITB_WriteProtected,UNIT_FLAGS(A_UNIT)
bne .WPErr
move.w #1<<14,d1
bsr DoRaw
move.l #3*1000,d0
bsr delay ;post-write delay
.End: movem.l (sp)+,d0-d1/d3/a6
rts
.WPErr: move.b #TDERR_WriteProt,IO_ERROR(A_IO)
bra .End
DoRaw:
;Enter with bit 14 set in d1 according to read/write (for dsklen)
;Uses d0-d1/d3 (not saved)
;Assumes $dff000 in A6
clr.l IO_ACTUAL(A_IO)
bsr SelectDrive
move.l IO_OFFSET(A_IO),d3
cmp.b NumTracks(A_UNIT),d3
bhs .Error
bsr Seek
tst.l d7
bne .Error
move.l IO_DATA(A_IO),dskpt(a6)
move.w #ADKF_WORDSYNC,adkcon(a6)
btst #IOTDB_WORDSYNC,IO_FLAGS(A_IO)
beq .SkipSync
move.w #ADKF_SETCLR+ADKF_WORDSYNC,adkcon(a6)
.SkipSync:
move.w #ADKF_MSBSYNC,adkcon(a6)
move.w #ADKF_SETCLR+ADKF_FAST+ADKF_MFMPREC,adkcon(a6)
bsr PreComp
move.w #DMAF_SETCLR+DMAF_MASTER+DMAF_DISK,dmacon(a6) ;enable disk DMA
move.w #$4489,dsksync(a6) ;set magic sync word
bsr ClearSigs
move.w #INTF_SETCLR+INTF_DSKBLK,intena(a6) ;enable int
move.l IO_LENGTH(A_IO),d0
cmp.l #32766,d0
bhi .Error
lsr.l #1,d0
bset #15,d0
or.w d1,d0
btst #IOTDB_INDEXSYNC,IO_FLAGS(A_IO)
beq .NoIndex
move.w d0,IndexDskLen(A_DEVICE)
bsr EnableIndex
bra .Wait
.NoIndex:
move.w d0,dsklen(a6)
move.w d0,dsklen(a6)
.Wait: move.l BlockSig(A_DEVICE),d0
move.l #900*1000*2,d1
bsr TimeOutWait
beq .NoSync
move.w #0,dsklen(a6)
move.w #INTF_DSKBLK,intena(a6) ;disable int
.EndIO: bsr DisableIndex
bsr ClearSigs
tst.b IO_ERROR(A_IO)
bne .EndRTS
move.l IO_LENGTH(A_IO),IO_ACTUAL(A_IO)
.EndRTS: rts
.Error: move.b #TDERR_NotSpecified,IO_ERROR(A_IO)
rts
.NoSync: bsr StopDMA
move.b #TDERR_NoSecHdr,IO_ERROR(A_IO)
bra .EndIO
AddChangeInt:
;This command uses the linkage fields of the IORequest to add a ChangeInt
;request to a list maintained in the unit structure. This must NOT be
;ReplyMsg'ed to avoid destroying the linkage fields - a special compare in
;the task code handles this.
movem.l d0-d1/a0-a1,-(sp)
move.l A_IO,a1
lea ChangeIntList(A_UNIT),a0
SYS AddHead ;could use any list add routine
movem.l (sp)+,d0-d1/a0-a1
rts
RemChangeInt:
;This command unlinks the AddChangeInt request from the task's port.
;This is (and must be) an immediate command.
;Note that because this command occurs asyncronously to the task code, the
;task code that scans the ChangeInt list must be protected with a Forbid.
movem.l d0-d1/a0-a1,-(sp)
SYS Forbid
move.l A_IO,a1
SYS Remove
SYS Permit
movem.l (sp)+,d0-d1/a0-a1
rts
TDRemove:
;This command is obsolete, but is unfortuntely used by the ROM filesystem.
;It accepts an Interrupt structure (for Cause) in IO_DATA. Only one user per
;unit is permitted.
tst.l TDRemoveInt(A_UNIT)
beq .Ok
move.b #TDERR_DriveInUse,IO_ERROR(A_IO)
rts
.Ok: move.l IO_DATA(A_IO),TDRemoveInt(A_UNIT)
rts
GetDrive:
;Obtain the use of this unit via disk.resource.
movem.l d0-d1/a0-a2/a6,-(sp)
lea DiskResourceUnit(A_DEVICE),a2
.GetUnit: move.l DiskResourceBase(A_DEVICE),a6
move.l a2,a1
jsr DR_GETUNIT(a6)
tst.l d0
bne .End
lea DiskResourcePort(A_DEVICE),a0
move.l 4,a6
SYS WaitPort
bra .GetUnit
.End: movem.l (sp)+,d0-d1/a0-a2/a6
rts
FreeDrive:
;Release this unit to other users of disk.resource.
movem.l d0-d1/a0-a1/a6,-(sp)
move.l DiskResourceBase(A_DEVICE),a6
jsr DR_GIVEUNIT(a6)
movem.l (sp)+,d0-d1/a0-a1/a6
rts
;***************************** Low-level disk code **************************
;Standard register usage:
;A6 - $dff000
;A5 (A_DEVICE) - device ptr
;A4 (A_UNIT) - unit ptr
;A3 (A_IO) - pointer to IO request
;For reading/writing:
;D0.L - length (bytes)
;D1.L - offset (bytes)
;A0.L - buffer
;NOTE: Error code returned in D7.L - 0 if ok, else error
;General errors
DISK_BadParameter equ TDERR_NotSpecified
;Read errors
DISK_NoSync equ TDERR_NoSecHdr
DISK_BadHeader equ TDERR_BadHdrSum
DISK_BadData equ TDERR_BadSecSum
;Write errors
DISK_WriteProtected equ TDERR_WriteProt
DISK_VerifyError equ TDERR_NotSpecified
IFND dskpt
dskpt equ dskpth
ENDC
;Note: This is a public value, but it is NOT meant to be changed!!!
DISK_RawBufSize equ 13630
ciabprb equ $bfd100
ciaapra equ $bfe001
ciabddrb equ $bfd300
ciaaddra equ $bfe201
***************************************************************
;Important disk parameters, summary:
;Step rate: 3ms. 4ms when looking for track zero.
;Settle time: 15ms
;Post-write delay: 3ms (officially 1.3ms)
;Side select delay: 2ms (officially 1.3ms)
***************************************************************
DISK_Read:
movem.l d0-d5/a0/a1,-(sp)
move.l a0,a1 ;destination ptr in A1
move.l d0,d5
.ReadLoop:
move.l d1,d3
divu.w d6,d3 ;d3.w is track #
move.l d3,d4
clr.w d4
swap d4 ;d4.l is byte offset into track
bsr MinSeek
tst.l d4 ;any offset?
bne .Complex ;yes
cmp.l d6,d5 ;check remaining length
blo .Complex ;if not a track, forget it
move.l a1,d0
btst #0,d0 ;word aligned?
bne .Complex ;no, do it the long way
move.l a1,a0
bsr ReadTrackAndDecodeNoBuffer
tst.l d7
bne .End
sub.l d6,d5 ;update length
beq .End
add.l d6,a1 ;update destination pointer
add.l d6,d1 ;update offset
bra .ReadLoop
.Complex:
move.l DecodedBuffer(A_DEVICE),a0
bsr ReadTrackAndDecodeBuffered
tst.l d7
bne .End
add.l d4,a0
sub.l d6,d4
neg.l d4
add.l d4,d1 ;update offset
;D4.L - number of bytes that could be transferred from this track
;D5.L - number of bytes left in the entire Read request
cmp.l d5,d4
bhs .FinishUp
sub.l d4,d5 ;update length
move.l d4,d0
bsr CopyMem
add.l d0,a1 ;update destination pointer
bra .ReadLoop
.FinishUp:
move.l d5,d0
bsr CopyMem ;use number of bytes left in entire request
.End: movem.l (sp)+,d0-d5/a0/a1
rts
SelectDrive:
;Selects drive AND turns on motor
;Selects UnitNum(A_UNIT).
movem.l d0/d2,-(sp)
move.b UnitNum(A_UNIT),d2
bsr Deselect
bclr #CIAB_DSKMOTOR,ciabprb ;motor on
move.l d2,d0
addq.b #3,d0
bclr d0,ciabprb ;select drive X
bset d2,MotorState(A_DEVICE)
moveq #4,d2
.Wait: btst #CIAB_DSKRDY,ciaapra
beq .End
move.l #100*1000,d0
bsr delay
dbra d2,.Wait
.End: movem.l (sp)+,d0/d2
rts
Deselect: or.b #CIAF_DSKSEL0+CIAF_DSKSEL1+CIAF_DSKSEL2+CIAF_DSKSEL3,ciabprb ;Deselect all drives
rts
SelectDriveSameMotor:
;Select a drive WITHOUT changing the current motor state
;Selects UnitNum(A_UNIT).
push d2
move.b UnitNum(A_UNIT),d2
bsr Deselect
bclr #CIAB_DSKMOTOR,ciabprb ;motor on
btst d2,MotorState(A_DEVICE)
bne .WasOn
bset #CIAB_DSKMOTOR,ciabprb ;motor off
.WasOn: addq.b #3,d2
bclr d2,ciabprb ;select drive X
pop d2
_RTS: rts
DISK_Update:
;Flush track buffer if "dirty"
bclr #DEVB_Dirty,DEV_FLAGS(A_DEVICE)
beq _RTS
movem.l d0-d2/a0/a2/a6,-(sp)
move.l #_custom,a6
;This is somewhat tricky, because the currently selected drive may not be
;the drive that we want to write to!
move.b UnitNum(A_UNIT),d0
move.w TDU_CURRTRK(A_UNIT),d1
move.b BufferDrive(A_DEVICE),UnitNum(A_UNIT) ;fake drive
move.b BufferTrack(A_DEVICE),TDU_CURRTRK+1(A_UNIT) ;fake track
bsr SelectDrive
bsr SelectSide
;The logic of this routine is complicated by the need to support the write
;optimization scheme (see DISK_Write for details). What we need to do is
;check the WriteMap to determine whether a ReadTrackAndDecode is required
;before finally writing...
move.l WriteMap(A_DEVICE),d2
and.l SectorMask(A_UNIT),d2
cmp.l SectorMask(A_UNIT),d2
beq .SkipRead
move.l DecodedBuffer(A_DEVICE),a0
bsr ReadTrackAndDecode
tst.l d7
bne .End
.SkipRead:
move.l d7,WriteMap(A_DEVICE)
move.l DecodedBuffer(A_DEVICE),a2
bsr EncodeAndWriteTrack
move.b d0,UnitNum(A_UNIT)
move.w d1,TDU_CURRTRK(A_UNIT)
bsr SelectDrive
.End: movem.l (sp)+,d0-d2/a0/a2/a6
rts
SelectSide:
;Select the correct side based on current track. This routine MUST be
;called by certain routines (e.g. Update, Write) because the side select is
;lost after a GiveUnit!
bclr #CIAB_DSKSIDE,ciabprb ;set to upper
btst #0,TDU_CURRTRK+1(A_UNIT)
bne .skip
bset #CIAB_DSKSIDE,ciabprb ;set to lower
.skip: push d0
move.l #2*1000,d0
bsr delay
pop d0
rts
;Enter with destination track in d3.b
;Note: This routine checks for valid track numbers -- if either the
;current track number or the destination is invalid, PANIC.
SaveRegs setrl d0/d3-d4
SeekNoUpdate:
;Perform a seek but don't call DISK_Update -- used by main routine when
;checking for disk changes.
movem.l SaveRegs,-(sp)
bra SeekAfterUpdate
MinSeek:
;Only call MinSeek if it is certain that the side select has not been
;changed!
cmp.b TDU_CURRTRK+1(A_UNIT),d3
bne Seek
move.w d0,-(sp)
move.b BufferDrive(A_DEVICE),d0
cmp.b UnitNum(A_UNIT),d0
bne .Seek
move.w (sp)+,d0
rts
.Seek: move.w (sp)+,d0
Seek: movem.l SaveRegs,-(sp)
bsr DISK_Update
SeekAfterUpdate:
tst.l d7
bne .End
move.l TDU_STEPDELAY(A_UNIT),d0
cmp.b NumTracks(A_UNIT),d3
bhs .Error
;Set head
bclr #CIAB_DSKSIDE,ciabprb ;set to upper
btst #0,d3
bne .skip
bset #CIAB_DSKSIDE,ciabprb ;set to lower
.skip:
move.w TDU_CURRTRK(A_UNIT),d4
lsr.b #1,d4
cmp.b #80,d4
bhs .Error
move.b d3,TDU_CURRTRK+1(A_UNIT)
lsr.b #1,d3
bset #CIAB_DSKDIREC,ciabprb ;set to "out" (lower tracks)
sub.b d3,d4
beq .SideSelectOnly
bhi .StepIn ;Go if DISK_CurrentTrack > Destination
bclr #CIAB_DSKDIREC,ciabprb ;set to "in" (higher tracks)
neg.b d4
.StepIn:
bsr SelectDriveSameMotor
bset #CIAB_DSKSTEP,ciabprb
bclr #CIAB_DSKSTEP,ciabprb ;step head
bset #CIAB_DSKSTEP,ciabprb
bsr Deselect
bsr delay
subq.b #1,d4
bne .StepIn
move.l TDU_SETTLEDELAY(A_UNIT),d0
bsr delay
bsr SelectDriveSameMotor
.End: movem.l (sp)+,SaveRegs
rts
.SideSelectOnly:
move.l #2*1000,d0
bsr delay
bra .End
.Error:
; move.w #$f00,$dff180
bra .End
;*************************** Delay code ****************************
delay:
;Enter with microseconds in D0.L
movem.l d0-d1/a0-a1/a6,-(sp)
lea TimerIORequest(A_DEVICE),a1
move.w #TR_ADDREQUEST,IO_COMMAND(a1)
clr.l TV_SECS+IO_SIZE(a1)
move.l d0,TV_MICRO+IO_SIZE(a1)
move.l 4,a6
SYS DoIO
;Clear signal bit (TimeOutWait depends on the signal bit being 'correct').
lea TimerIORequest(A_DEVICE),a1
move.l MN_REPLYPORT(a1),a0
move.b MP_SIGBIT(a0),d0
moveq #0,d1
bset d0,d1
moveq #0,d0
SYS SetSignal
movem.l (sp)+,d0-d1/a0-a1/a6
rts
TimeOutWait:
;Wait for signal mask in D0, but include a time-out specified in D1 (usecs).
;Returns wait mask in D0, or 0 if a time-out occured (Z flag will be set).
movem.l d1-d4/a0-a2/a6,-(sp)
move.l d0,d3
move.l 4,a6
lea TimerIORequest(A_DEVICE),a1
move.l a1,a2
move.w #TR_ADDREQUEST,IO_COMMAND(a1)
clr.l TV_SECS+IO_SIZE(a1)
move.l d1,TV_MICRO+IO_SIZE(a1)
move.l MN_REPLYPORT(a1),a0
moveq #0,d2
move.b MP_SIGBIT(a0),d1
bset d1,d2
SYS SendIO
move.l d3,d0
add.l d2,d0 ;equivilent to OR in this case
SYS Wait
and.l d3,d0 ;exclude timer signal
move.l d0,d4
beq .TimeOut ;if zero, timer signal was the only signal
move.l a2,a1
SYS AbortIO
.TimeOut: move.l a2,a1
SYS WaitIO
moveq #0,d0 ;value
move.l d2,d1 ;mask (timer sig)
SYS SetSignal ;make SURE the timer signal is clear
move.l d4,d0
movem.l (sp)+,d1-d4/a0-a2/a6
rts
;*************************** Interrupt routines ************************
;------------------------------------------
;Register contents upon entering a handler:
;D1 - (INTENAR) AND (INTREQR)
;A0 - $dff000
;A1 - data ptr (device ptr in this case)
;A6 - ExecBase
;d0, d1, a0, a1, a5, and a6 are scratch.
;-----------------------------------------
;However, the disk.resource autodoc indicates the following arrangement for
;interrupts installed by GetUnit:
;D0/D1/A0/A1 are scratch
;A1 points to IS_DATA (device pointer in this case).
;Make no other assumptions!
SyncInt: addq.w #1,SyncCount(a1)
move.l SyncSig(a1),d0
lea Task(a1),a1
push a6
move.l 4,a6
SYS Signal
pop a6
;Delay approximately 63us to avoid two sync interrupts
;This could be improved.
moveq #1,d0
.L1: move.b vhposr+_custom,d1
.L2: cmp.b vhposr+_custom,d1
beq .L2
dbra d0,.L1
move.w #INTF_DSKSYNC,intreq+_custom ;clear sync
rts
BlockInt:
push a6
move.l #_custom,a6
move.w #INTF_DSKBLK,intreq(a6)
;Test DEVB_Verify flag. If set, initiate a read into the verify buffer.
bclr #DEVB_Verify,DEV_FLAGS(a1)
beq .NoVerify
move.w #INTF_SETCLR+INTF_DSKSYNC,intena(a6) ;enable sync int
move.l VerifyBuffer(a1),dskpt(a6)
move.w #ADKF_SETCLR+ADKF_WORDSYNC,adkcon(a6)
move.w #0,dsklen(a6)
move.w BlockIntDskLen(a1),dsklen(a6)
move.w BlockIntDskLen(a1),dsklen(a6)
move.w #$4489,dsksync(a6) ;set magic sync word
;Now the first sync interrupt we get will be known to be valid, because the
;DMA read is fully started.
bra .End
.NoVerify:
move.l BlockSig(a1),d0
lea Task(a1),a1
move.l 4,a6
SYS Signal
.End: pop a6
rts
IndexInt:
move.w IndexDskLen(a1),d0
beq .End
move.w d0,dsklen+_custom
move.w d0,dsklen+_custom
clr.w IndexDskLen(a1)
.End: rts
EnableIndex:
movem.l d0-d1/a0-a1/a6,-(sp)
move.l CIABase(A_DEVICE),a6
moveq #16,d0
SYS SetICR ;clear interrupt
move.l #16+128,d0
SYS AbleICR ;enable interrupt
movem.l (sp)+,d0-d1/a0-a1/a6
rts
DisableIndex:
movem.l d0-d1/a0-a1/a6,-(sp)
move.l CIABase(A_DEVICE),a6
moveq #16,d0
SYS AbleICR ;disable interrupt
moveq #16,d0
SYS SetICR ;clear interrupt
movem.l (sp)+,d0-d1/a0-a1/a6
rts
;*********************** End interrupt routines ************************
ClearSigs:
movem.l d0-d1/a0-a1/a6,-(sp)
move.w #INTF_DSKBLK+INTF_DSKSYNC,intreq+_custom ;clear sync+done
clr.w SyncCount(A_DEVICE)
clr.w IndexDskLen(A_DEVICE)
move.l 4,a6
moveq #0,d0 ;value
move.l SyncSig(A_DEVICE),d1
add.l BlockSig(A_DEVICE),d1
SYS SetSignal
movem.l (sp)+,d0-d1/a0-a1/a6
rts
SetRegs:
;This routine enables the block interrupt, but leaves sync interrupts
;disabled.
move.l RawBuffer(A_DEVICE),dskpt(a6)
move.w #ADKF_MSBSYNC+ADKF_PRECOMP0+ADKF_PRECOMP1,adkcon(a6)
move.w #ADKF_SETCLR+ADKF_FAST+ADKF_WORDSYNC+ADKF_MFMPREC,adkcon(a6)
move.w #DMAF_SETCLR+DMAF_MASTER+DMAF_DISK,dmacon(a6) ;enable disk DMA
bsr ClearSigs
move.w #INTF_SETCLR+INTF_DSKBLK,intena(a6) ;enable block int
rts
PreComp:
;Enter with $dff000 in A6
movem.l d0-d1,-(sp)
move.w #ADKF_PRECOMP0+ADKF_PRECOMP1,adkcon(a6) ;no precomp
move.w TDU_CURRTRK(A_UNIT),d0
cmp.w TDU_COMP01TRACK(A_UNIT),d0
bls .End
move.w #ADKF_SETCLR+ADKF_PRECOMP0,d1
cmp.w TDU_COMP10TRACK(A_UNIT),d0
bls .Done
move.w #ADKF_SETCLR+ADKF_PRECOMP1,d1
cmp.w TDU_COMP11TRACK(A_UNIT),d0
bls .Done
move.w #ADKF_SETCLR+ADKF_PRECOMP0+ADKF_PRECOMP1,d1
.Done: move.w d1,adkcon(a6)
.End: movem.l (sp)+,d0-d1
rts
ScanSync:
;Scan for sync mark
;A2 - pointer to raw data -- updated
;Error code in D7
bsr WaitWordSync
tst.l d7
bne .End
;This routine is trickier than it appears. The trick is that we must NOT
;assume a $4489 at the beginning of our buffer. This phenomenon occurs when
;the DMA starts in the middle of the first sync word. The second sync word
;is thrown away by the hardware. It sounds exotic, but it actually happens
;quite often!
push a0
move.l RawBuffer(A_DEVICE),a0
cmp.l a0,a2
add.l RawBufSize(A_UNIT),a0
beq .found ;if start of buffer, don't scan for sync!!!
.Sync: cmpi.w #$4489,(a2)+
beq .found
cmp.l a2,a0
bhi .Sync
.Error: pop a0
moveq #DISK_NoSync,d7
rts
.found: cmpi.w #$4489,(a2)
bne .ok
addq.l #2,a2
cmp.l a2,a0
bhi .found
bra .Error
.ok: pop a0
.End: rts
StopDMA: move.w #0,dsklen(a6)
move.w #1<<15,dsklen(a6)
move.w #1<<15,dsklen(a6) ;zero-length DMA transfer
move.w #INTF_DSKBLK+INTF_DSKSYNC,intena(a6) ;disable ints
bra ClearSigs
WaitWordSync:
;Wait for a sync mark or disk block done
;Will return an error in 300ms if nothing happens.
movem.l d0-d1,-(sp)
.Wait: tst.w SyncCount(A_DEVICE)
bne .Sync
move.l SyncSig(A_DEVICE),d0
add.l BlockSig(A_DEVICE),d0
move.l #300*1000*2,d1 ;time-out (300ms)
bsr TimeOutWait
beq .Error
and.l BlockSig(A_DEVICE),d0
bne .Done
bra .Wait
.Sync: subq.w #1,SyncCount(A_DEVICE)
.End: movem.l (sp)+,d0-d1
rts
.Done: move.w #$C000,SyncCount(A_DEVICE) ;this is obscure - should change
bra .End
.Error: moveq #DISK_NoSync,d7
bra .End
ReadTrackAndDecodeBuffered:
push d0
move.l WriteMap(A_DEVICE),d0
beq .OK ;no optimized writes to worry about
and.l SectorMask(A_UNIT),d0
cmp.l SectorMask(A_UNIT),d0
beq .End
moveq #-1,d0
move.l d0,WriteMap(A_DEVICE) ;mark sectors as filled
bra .ReadTrack
.OK: move.b UnitNum(A_UNIT),d0
cmp.b BufferDrive(A_DEVICE),d0
bne .ReadTrack
move.w TDU_CURRTRK(A_UNIT),d0
cmp.b BufferTrack(A_DEVICE),d0
bne .ReadTrack
.End: pop d0
rts
.ReadTrack:
pop d0
ReadTrackAndDecode:
;Input: Buffer ptr in A0
;Returns error code in D7
move.b UnitNum(A_UNIT),BufferDrive(A_DEVICE)
move.b TDU_CURRTRK+1(A_UNIT),BufferTrack(A_DEVICE)
ReadTrackAndDecodeNoBuffer:
move.w d0,-(sp)
move.b TDU_RETRYCNT(A_UNIT),d0
.Retry: moveq #0,d7
bsr ReadTrackAndDecodeNoRetry
tst.l d7
beq .End
subq.b #1,d0
bpl .Retry
;Could not recover from error - clear the buffer
bsr Clear
.End: move.w (sp)+,d0
rts
ReadTrackAndDecodeNoRetry:
movem.l d0-d6/a0-a2,-(sp)
;Initiate a raw track read
;Because we are using WORDSYNC interrupts, this code is VERY VERY tricky!!!!!
bsr SetRegs
move.w #$0123,dsksync(a6) ;set invalid sync word
;At this point sync interrupts will stop happenning, because $0123 will
;(hopefully!) never occur. So, we clear the interrupt and enable it.
bsr ClearSigs
move.w #INTF_SETCLR+INTF_DSKSYNC,intena(a6) ;enable sync int
move.w #0,dsklen(a6)
move.w ReadDskLen(A_UNIT),dsklen(a6)
move.w ReadDskLen(A_UNIT),dsklen(a6)
move.w #$4489,dsksync(a6) ;set magic sync word
;Now the first sync interrupt we get will be known to be valid, because the
;DMA read is fully started.
;Decode track
move.l RawBuffer(A_DEVICE),a2
move.l SectorsPerTrack(A_UNIT),d3
subq.l #1,d3
move.l #$55555555,d2
;Wait for first sync marks...
bsr WaitWordSync
tst.l d7
bne .End
.SecLoop:
bsr ScanSync
tst.l d7
bne .End
bsr DecodeLong ;get header
subq.l #8,a2
and.w #$ff00,d0 ;mask off sector number
move.l d0,d1
cmp.w MaxValidSec(A_UNIT),d1
bhi .HeaderError
add.w d1,d1 ;convert to sector offset
lsr.w #4,d0 ;get sector label offset
lea SectorLabels(A_DEVICE),a1
lea (a1,d0.w),a1
;This code supports the write optimization...
lsr.w #4,d0 ;sector number
move.l WriteMap(A_DEVICE),d4
btst d0,d4 ;should we avoid reading this sector?
bne .EndLoop ;yes, skip to next sector
move.l d1,-(sp)
moveq #0,d5
move.l (a2)+,d0
eor.l d0,d5
move.l (a2)+,d0
eor.l d0,d5
;Decode and checksum sector label
moveq #3,d6
.Label: move.l 16(a2),d4
eor.l d4,d5
and.l d2,d4
move.l (a2)+,d1
eor.l d1,d5
and.l d2,d1
add.l d1,d1
or.l d1,d4
move.l d4,(a1)+
dbra d6,.Label
and.l d2,d5
lea 16(a2),a2 ;point at header checksum
move.l (sp)+,d1
bsr DecodeLong ;header checksum
cmp.l d0,d5
bne .HeaderError
;Verify track position
swap d1
cmp.b TDU_CURRTRK+1(A_UNIT),d1
bne .WrongTrack
swap d1
bsr DecodeLong ;data area checksum
lea (a0,d1.w),a1 ;compute destination
;Decode and checksum data block
moveq #127,d6
moveq #0,d5
.L1: move.l 512(a2),d4
eor.l d4,d5
and.l d2,d4
move.l (a2)+,d1
eor.l d1,d5
and.l d2,d1
add.l d1,d1
or.l d1,d4
move.l d4,(a1)+
dbra d6,.L1
and.l d2,d5
lea 512(a2),a2
cmp.l d0,d5 ;check data area checksum
bne .DataError
.EndLoop: dbra d3,.SecLoop
.End: bsr StopDMA
movem.l (sp)+,d0-d6/a0-a2
rts
.HeaderError:
moveq #DISK_BadHeader,d7
bra .End
.DataError:
moveq #DISK_BadData,d7
bra .End
.WrongTrack:
moveq #TDERR_SeekError,d7
move.w TDU_CURRTRK(A_UNIT),d3
bsr SeekZero
bsr SelectDrive
bsr SeekNoUpdate
bra .End
DecodeLong:
;A2 - ptr to buffer -- updated
;D2 - $55555555
;D0 - result
move.l d1,-(sp)
move.l (a2)+,d0
move.l (a2)+,d1
and.l d2,d0
and.l d2,d1
add.l d0,d0 ;was lsl.l #1,d0
or.l d1,d0
move.l (sp)+,d1
rts
EncodeLong:
;Enter with data to be encoded in D0.L
;and pointer to destination in A0 -- updated
;Exit with checksum in D5
movem.l d0-d4,-(sp)
moveq #0,d5
move.l #$55555555,d4
move.l d0,d3
lsr.l #1,d0
bsr Encode
move.l d3,d0
bsr Encode
and.l #$55555555,d5
movem.l (sp)+,d0-d4
rts
Encode:
;Enter with longword to code in D0.L and #$55555555 in D4
;uses d0,d1,d2,a0 -- not saved
;Accumulates checksum in D5
and.l d4,d0
move.l d0,d2
eor.l d4,d2
move.l d2,d1
add.l d2,d2
lsr.l #1,d1
bset #31,d1
and.l d2,d1
or.l d1,d0
btst #0,-1(a0)
beq .ok
bclr #31,d0
.ok: eor.l d0,d5
move.l d0,(a0)+
rts
EncodeBlock:
;Destination is always chip RAM (RawBuffer).
;Source could be in chip RAM or fast RAM (in A2).
movem.l d0-d1/a0-a1/a6,-(sp)
move.l 4,a6
move.l a2,a1
SYS TypeOfMem
and.l #MEMF_CHIP,d0
bne .Chip
movem.l (sp)+,d0-d1/a0-a1/a6
bra EncodeBlockCPU
.Chip: movem.l (sp)+,d0-d1/a0-a1/a6
bra EncodeBlockBlit
EncodeBlockCPU:
;Enter with pointer to source data in A2 -- updated
;Enter with pointer to destination in A0 -- updated
;Exit with checksum in D5
move.l d6,-(sp)
moveq #0,d5
move.w #(512/4)-1,d6
EncodeBlockSub:
;Number of longwords to encode (minus one) in D6.w
movem.l d0-d4,-(sp)
;Encode odd bits
push a2
move.w d6,d3
move.l #$55555555,d4
.L1: move.l (a2)+,d0
lsr.l #1,d0
bsr Encode
dbra d3,.L1
;Encode even bits
pop a2
move.w d6,d3
.L2: move.l (a2)+,d0
bsr Encode
dbra d3,.L2
and.l #$55555555,d5
movem.l (sp)+,d0-d4
move.l (sp)+,d6
rts
EncodeSectorLabels:
;D5 (checksum) must be initialized by caller
move.l d6,-(sp)
move.w #(16/4)-1,d6
bra EncodeBlockSub
EncodeBlockBlit:
;Enter with pointer to source data in A2 -- updated
;Enter with pointer to destination in A0 -- updated
;Exit with checksum in D5
movem.l d0-d2/a0-a1/a6,-(sp)
push a0
move.l GraphBase(A_DEVICE),a6
SYS OwnBlitter
move.l (sp),a0
move.l #_custom,a1
move.w #$808,d0 ;BLTSIZE
SYS WaitBlit
move.w #$ffff,bltafwm(a1)
move.w #$ffff,bltalwm(a1)
clr.w bltbmod(a1)
clr.w bltamod(a1)
clr.w bltdmod(a1)
move.w #$5555,bltcdat(a1)
move.l a2,bltbpt(a1)
move.l a2,bltapt(a1)
move.l a0,bltdpt(a1)
move.w #$1db1,bltcon0(a1)
clr.w bltcon1(a1)
move.w d0,bltsize(a1)
SYS WaitBlit
move.l a0,bltbpt(a1)
move.l a2,bltapt(a1)
move.l a0,bltdpt(a1)
move.w #$2d8c,bltcon0(a1)
move.w d0,bltsize(a1)
movem.l a0/a2,-(sp)
lea 510(a2),a2 ;ptr to end of src
lea 1022(a0),a0
SYS WaitBlit
move.l a2,bltbpt(a1) ;src end
move.l a2,bltapt(a1) ;src end
move.l a0,bltdpt(a1) ;dst end
move.w #$0db1,bltcon0(a1)
move.w #$1002,bltcon1(a1) ;decrement
move.w d0,bltsize(a1)
movem.l (sp)+,a0/a2
lea 512(a0),a0
SYS WaitBlit
move.l a0,bltbpt(a1)
move.l a2,bltapt(a1)
move.l a0,bltdpt(a1)
move.w #$1d8c,bltcon0(a1)
clr.w bltcon1(a1)
move.w d0,bltsize(a1)
pop a0
SYS WaitBlit
bsr Correct
lea 512(a0),a0
bsr Correct
lea -512(a0),a0
move.w #(1024/4)-1,d0
move.l #$55555555,d2
moveq #0,d5
.. move.l (a0)+,d1
eor.l d1,d5
dbra d0,..
and.l d2,d5
SYS DisownBlitter
movem.l (sp)+,d0-d2/a0-a1/a6
lea 512(a2),a2 ;update source pointer
lea 1024(a0),a0 ;update destination pointer
rts
;This routine corrects the high bit of the current byte based on the
;low bit of the previous byte.
Correct:
push d0
move.b (a0),d0
btst #0,-1(a0)
bne .ResetClock
btst #6,d0
bne .end
bset #7,d0
bra .end1
.ResetClock:
bclr #7,d0
.end1: move.b d0,(a0)
.end: pop d0
rts
DISK_Wait:
;Assumes $dff000 in A6.
movem.l d0-d1,-(sp)
tst.w SyncCount(A_DEVICE)
bmi .OK ;if WaitWordSync detected a BlockSig, don't wait!
move.l BlockSig(A_DEVICE),d0
move.l #300*1000*2,d1
bsr TimeOutWait
bne .OK
moveq #DISK_NoSync,d7
.OK: move.w #INTF_DSKBLK+INTF_DSKSYNC,intena(a6) ;disable ints
bsr ClearSigs
movem.l (sp)+,d0-d1
rts
EncodeAndWriteTrack:
;Enter with pointer to source data in A2
movem.l d0-d6/a0-a2,-(sp)
btst #CIAB_DSKPROT,ciaapra ;check write protect status
beq .Protected
move.l RawBuffer(A_DEVICE),a0
;Gap = 1660 bytes - 2 bytes for hardware bug
move.l #$aaaaaaaa,d1 ;10101010...
move.w GapCount(A_UNIT),d0 ;414/829
.. move.l d1,(a0)+
dbra d0,..
subq.l #2,a0 ;leave room for 2 extra bytes at the very end
move.l SectorsPerTrack(A_UNIT),d1 ;number of sectors
moveq #0,d3 ;sector count
.SecLoop:
move.l #$aaaaaaaa,(a0)
bsr Correct
addq.l #4,a0
move.l #$44894489,(a0)+
move.l #$ff000000,d0
moveq #0,d6
move.w TDU_CURRTRK(A_UNIT),d6
swap d6
or.l d6,d0
move.l d3,d6
lsl.l #8,d6
or.l d6,d0
or.l d1,d0
bsr EncodeLong ;header
;Encode sector label
push a2
lea SectorLabels(A_DEVICE),a2
move.l d3,d0
lsl.l #4,d0 ;sector*16
lea (a2,d0.w),a2
bsr EncodeSectorLabels
pop a2
move.l d5,d0
bsr EncodeLong ;header checksum
move.l a0,d2 ;save raw data pointer
addq.l #8,a0
bsr EncodeBlock ;encode data block
move.l d5,d0
exg a0,d2
bsr EncodeLong ;data block checksum
bsr Correct
move.l d2,a0
addq.l #1,d3
subq.l #1,d1
bne .SecLoop
move.w #$aaa8,(a0)
bsr Correct ;extra word to avoid hardware bug
;Physically write the data
.WriteAgain:
bsr SetRegs
bsr PreComp
move.w #ADKF_WORDSYNC,adkcon(a6) ;turn OFF wordsync!!!
move.w #$0123,dsksync(a6)
bsr ClearSigs
move.w VerifyDskLen(A_UNIT),BlockIntDskLen(A_DEVICE)
btst #TDPB_VERIFY,TDU_PUBFLAGS(A_UNIT)
beq .SkipVerify
bset #DEVB_Verify,DEV_FLAGS(A_DEVICE)
.SkipVerify:
move.l VerifyBuffer(A_DEVICE),a0
clr.l (a0)+
clr.l (a0)
move.w #0,dsklen(a6)
move.w WriteDskLen(A_UNIT),dsklen(a6)
move.w WriteDskLen(A_UNIT),dsklen(a6)
;This piece of code (commented out) tests the function of the rare
;"interrupt delayed" requester. (I've never seen it appear in actual use).
comment |
;TEST
push a6
move.l 4,a6
SYS Disable
move.w #5000,d0
.L1: move.b vhposr+_custom,d1
.L2: cmp.b vhposr+_custom,d1
beq .L2
dbra d0,.L1
SYS Enable
pop a6
|
btst #TDPB_VERIFY,TDU_PUBFLAGS(A_UNIT)
beq .NoVerify
;VERIFY
;We verify by comparing the raw MFM data in RawBuffer (what we just wrote)
;and VerifyBuffer (what is coming in). Due to the ingenious method of
;verifying (thanks to Sebastiano Vigna!) the data comes in sector-by-sector
;in the same order that we wrote it.
move.l VerifyBuffer(A_DEVICE),a0
move.l RawBuffer(A_DEVICE),a2
add.w FirstSector(A_UNIT),a2 ;1666/3326 - first sector minus sync
bsr WaitWordSync
tst.l d7
bne .VerifyError
bsr WaitWordSync
tst.l d7
bne .VerifyError
;Now we have our first sector in the verify buffer. Scan for a
;sync mark. (There may be 1 or 2 sync marks).
cmp.w #$4489,(a0)+
bne .VerifyError
cmp.w #$4489,(a0)
bne .HaveSync
addq.l #2,a0
.HaveSync:
;We go through a rather elaborate procedure here to make sure that we've
;started reading with sector 0. (If not, display a requester informing the
;user that something is locking out level-1 interrupts for a long period of
;time).
push a2
move.l #$55555555,d2
move.l a0,a2
bsr DecodeLong
pop a2
and.w #$ff00,d0
beq .Sector0
move.l a0,a1
moveq #9,d0
moveq #0,d1
.. move.l (a1)+,d3
eor.l d3,d1
dbra d0,..
and.l d2,d1
push a2
move.l a1,a2
bsr DecodeLong
pop a2
cmp.l d0,d1
bne .VerifyError
;Display an informational requester.
bsr StopDMA
push a6
move.l IntBase(A_DEVICE),a6
cmp.w #36,LIB_VERSION(a6)
bhi .KS20
;Running under 1.3. Put up a DisplayAlert.
moveq #0,d0 ;alert type (recoverable)
lea .AlertLockOut(pc),a0
moveq #20,d1 ;height
SYS DisplayAlert
pop a6
bra .WriteAgain
;Running under 2.0. Put up a EasyRequest.
.KS20:
sub.l a0,a0
sub.l a2,a2
lea .LockOut(pc),a1
SYS EasyRequestArgs
pop a6
bra .WriteAgain
;Note: Probably should compare with the blitter, but this will be fairly
;fast.
.Sector0:
move.w #270-1,d0
.. cmp.l (a0)+,(a2)+
dbne d0,..
bne .VerifyError
;Now we are over the initial hump of the first sync mark. The rest of the
;compare is even easier.
move.l SectorsPerTrack(A_UNIT),d1
subq.l #3,d1
.VLoop: bsr WaitWordSync
tst.l d7
bne .VerifyError
move.w #272-1,d0
.. cmp.l (a0)+,(a2)+
dbne d0,..
bne .VerifyError
dbra d1,.VLoop
;We have one more sector to verify. This time we must wait for "Block
;done", rather than another sync.
bsr DISK_Wait
tst.l d7
bne .VerifyError
move.w #272-1,d0
.. cmp.l (a0)+,(a2)+
dbne d0,..
bne .VerifyError
bra .End
.VerifyError:
moveq #0,d7 ;don't propagate the error to the app
;We go here if an error is detected during the verify. We first shut down
;the read operation that may be in progress, then put up a requester and let
;the user choose whether to retry or abort.
bsr StopDMA ;stop!!
push a6
move.l IntBase(A_DEVICE),a6
cmp.w #36,LIB_VERSION(a6)
bhi .DoKS20
;Running under 1.3. Put up a DisplayAlert.
moveq #0,d0 ;alert type (recoverable)
lea .AlertVError(pc),a0
moveq #20,d1 ;height
SYS DisplayAlert
;D0 is set to 'TRUE' if the LEFT button was pressed.
pop a6
tst.l d0
bne .WriteAgain ;go if left button pressed
bra .End
;Running under 2.0. Put up a EasyRequest.
.DoKS20: sub.l a0,a0
sub.l a2,a2
lea .VError(pc),a1
push a3
move.w TDU_CURRTRK(A_UNIT),-(sp)
clr.w -(sp)
move.l sp,a3
SYS EasyRequestArgs
addq.l #4,sp
pop a3
pop a6
tst.l d0
bne .WriteAgain ;go if left gadget hit
bra .End
.NoVerify:
bsr DISK_Wait
move.l #4*1000,d0
bsr delay ;post-write delay
.End: movem.l (sp)+,d0-d6/a0-a2
rts
.Protected:
moveq #DISK_WriteProtected,d7
bra .End
.AlertVError:
dc.w 10 ;x coordinate
dc.b 10 ;y coordinate
dc.b '*** VERIFY ERROR !!! *** Hit LEFT button to RETRY'
dc.b ', or RIGHT button to CANCEL.',0
dc.b 0 ;continuation byte
even
.AlertLockOut:
dc.w 10 ;x coordinate
dc.b 10 ;y coordinate
dc.b 'Disk block interrupt delayed by >10ms.'
dc.b ' Press mouse button.',0
dc.b 0 ;continuation byte
even
.VError: dc.l es_SIZEOF
dc.l 0
dc.l .Title
dc.l .MainText
dc.l .GadgetText
.LockOut: dc.l es_SIZEOF
dc.l 0
dc.l .Title
dc.l .LockOutTxt
dc.l .Okay
.LockOutTxt:
dc.b 'Disk block interrupt delayed by >10ms caused erroneous'
dc.b ' verify.',0
.Okay: dc.b 'If you say so. Try it again!',0
.Title: dc.b 'hackdisk.device message',0
.MainText:
dc.b '*** VERIFY ERROR!!! ***',$a,'Track %ld',0
.GadgetText:
dc.b 'Retry|Cancel',0
even
DISK_Write:
;Error code returned in D7, as always.
movem.l d0-d5/a0-a2,-(sp)
btst #UNITB_WriteProtected,UNIT_FLAGS(A_UNIT)
bne .ProtError
move.l d0,d5 ;length
;The meat of the write routine...
.WriteLoop:
move.l d1,d3 ;offset
divu.w d6,d3 ;d3.w is track #
move.l d3,d4
clr.w d4
swap d4 ;d4.l is byte offset into track
bsr MinSeek
tst.l d4 ;any offset?
bne .Complex ;yes
cmp.l d6,d5 ;at least a track left?
blo .Complex ;no
move.l a0,d0
btst #0,d0 ;word aligned?
bne .Complex ;no, do it the long way
move.l a0,a2
bsr EncodeAndWriteTrack
tst.l d7
bne .End
sub.l d6,d5 ;update length
beq .End
add.l d6,a0 ;update source pointer
add.l d6,d1 ;update offset
bra .WriteLoop
.Complex:
;This part is somewhat difficult. We check the offset and length parameters
;to see whether they're a multiple of 512. If so, we keep track of which
;sectors will be written in the buffer. This information is later used by
;Update to determine whether a part of the original track must be read in.
;(We don't attempt this optimization if the user is writing some odd number
;of bytes...This is probably why trackdisk has the limits that it does).
tst.l d5
beq .End ;nothing left, forget it
move.l d4,d0
and.w #%111111111,d0
bne .NoOpt
move.l d4,d0
move.l d5,d2
and.w #%111111111,d2
bne .NoOpt
move.l d5,d2
lsr.l #8,d0
lsr.l #1,d0 ;get starting sector number
lsr.l #8,d2
lsr.l #1,d2 ;get length in sectors
move.l WriteMap(A_DEVICE),d7
.OptLoop: bset d0,d7
addq.b #1,d0
cmp.b #32,d0
beq .EOpt
subq.l #1,d2
bne .OptLoop
.EOpt: move.l d7,WriteMap(A_DEVICE)
moveq #0,d7
;This is normally done by ReadTrackAndDecode.
move.b UnitNum(A_UNIT),BufferDrive(A_DEVICE)
move.b TDU_CURRTRK+1(A_UNIT),BufferTrack(A_DEVICE)
bra .Opt ;don't read (yet)
.NoOpt: push a0
move.l DecodedBuffer(A_DEVICE),a0
bsr ReadTrackAndDecodeBuffered
pop a0
tst.l d7
bne .End
.Opt: move.l DecodedBuffer(A_DEVICE),a1
add.l d4,a1 ;add byte offset into track
sub.l d6,d4
neg.l d4
add.l d4,d1 ;update offset
;D4.L - number of bytes that could be transferred from this track
;D5.L - number of bytes left in the entire Read request
cmp.l d5,d4
bhs .FinalWrite
sub.l d4,d5 ;update length
move.l d4,d0
bsr CopyMem
add.l d0,a0 ;update dest pointer
bset #DEVB_Dirty,DEV_FLAGS(A_DEVICE)
bra .WriteLoop
.FinalWrite:
move.l d5,d0
bsr CopyMem
bset #DEVB_Dirty,DEV_FLAGS(A_DEVICE)
.End: movem.l (sp)+,d0-d5/a0-a2
rts
.ProtError:
moveq #DISK_WriteProtected,d7
bra .End
MotorOff:
;MotorOff turns off all drive motors and leaves all drives deselected
bsr Deselect
bset #CIAB_DSKMOTOR,ciabprb ;motor off
and.b #~(CIAF_DSKSEL0+CIAF_DSKSEL1+CIAF_DSKSEL2+CIAF_DSKSEL3),ciabprb ;select all drives
bsr Deselect
clr.b MotorState(A_DEVICE)
rts
Inquire:
IFND _LVOGetUnitID
_LVOGetUnitID equ -30
ENDC
movem.l d0-d2/a0-a1/a6,-(sp)
moveq #0,d2
move.l DiskResourceBase(A_DEVICE),a6
.Loop: move.l d2,d0
SYS GetUnitID
moveq #-1,d1
cmp.l d0,d1
beq .Next
bset d2,InquireBits(A_DEVICE)
.Next: addq.l #1,d2
cmp.w #MD_NUMUNITS,d2
blo .Loop
movem.l (sp)+,d0-d2/a0-a1/a6
rts
GetDriveType:
;Determines type of drive and places the proper code into DriveType(A_UNIT).
;One of: DRIVE3_5, DRIVE5_25, DRIVE3_5_150RPM
;See the Hardware Reference Manual, Appendix E for a description of
;the procedure.
;Note: Drive zero is always present. In fact, the identification
;scheme does not work with drive zero (except for the half-speed drive).
;Warning! Leaves drive deselected!
movem.l d0-d1/d3-d5/a0-a2/a6,-(sp)
move.b UnitNum(A_UNIT),d3
addq.b #3,d3
moveq #7,d1
move.l #ciabprb,a0
bsr Deselect
moveq #31,d4
moveq #0,d5 ;identification longword
bclr d1,(a0) ;motor on
bset d3,(a0) ;deselect drive
bclr d3,(a0) ;select drive
bset d1,(a0) ;motor off
bset d3,(a0) ;deselect drive
bclr d3,(a0) ;select drive
bset d3,(a0) ;deselect drive
.ReadIdent:
bclr d3,(a0) ;select drive
btst #5,ciaapra ;test ready
beq .zero
bset d4,d5
.zero: bset d3,(a0) ;deselect drive
dbra d4,.ReadIdent
;D5 contains the drive ID.
lea DriveParams(A_UNIT),a0
move.w #(DriveParams_Sizeof/2)-1,d0
.. clr.w (a0)+
dbra d0,..
lea HDParams(pc),a1
cmp.l #DRT_150RPM,d5
beq .DoInitStruct
lea LDParams(pc),a1
; cmp.l #DRT_AMIGA,d5
tst.l d5
beq .DoInitStruct
moveq #-1,d0
cmp.l d0,d5
beq .DoInitStruct
lea OldParams(pc),a1 ;5.25 inch drive
.DoInitStruct:
moveq #0,d0 ;area to clear
move.l A_UNIT,a2
move.l 4,a6
SYS InitStruct
.End: movem.l (sp)+,d0-d1/d3-d5/a0-a2/a6
rts
SeekZeroAll:
movem.l d0-d2/A_UNIT,-(sp)
move.b InquireBits(A_DEVICE),d0
lea Unit0(A_DEVICE),A_UNIT
moveq #0,d1
moveq #MD_NUMUNITS-1,d2
.Loop: btst d1,d0
beq .Next
bsr SeekZero
.Next: lea MyUnit_Sizeof(A_UNIT),A_UNIT
addq.b #1,d1
dbra d2,.Loop
movem.l (sp)+,d0-d2/A_UNIT
rts
SeekZero:
;Places the drive in UnitNum(A_UNIT) on track zero.
;Drive does not need to be selected in advance.
;Drive will be left _deselected_!
movem.l d0-d1,-(sp)
.StepLoop:
bsr SelectDriveSameMotor
bset #CIAB_DSKDIREC,ciabprb ;set to "out" (lower tracks)
btst #CIAB_DSKTRACK0,ciaapra ;check track zero flag
beq .EndStepLoop
bset #CIAB_DSKSTEP,ciabprb
bclr #CIAB_DSKSTEP,ciabprb ;step head
bset #CIAB_DSKSTEP,ciabprb
bsr Deselect
move.l TDU_CALIBRATEDELAY(A_UNIT),d0
bsr delay
bra .StepLoop
.EndStepLoop:
bsr Deselect
move.l TDU_SETTLEDELAY(A_UNIT),d0
bsr delay
clr.w TDU_CURRTRK(A_UNIT)
movem.l (sp)+,d0-d1
rts
CopyMemSlow:
;Only to be called by CopyMem
move.l 4,a6
SYS CopyMem
movem.l (sp)+,d0-d7/a0-a6
rts
CopyMem:
;A0 - source
;A1 - destination
;D0 - size
movem.l d0-d7/a0-a6,-(sp)
move.l a0,d1
btst #0,d1
bne CopyMemSlow
move.l a1,d1
btst #0,d1
bne CopyMemSlow
.More: cmp.l #512,d0
blo CopyMemSlow
;Copy 480 bytes
n set 0
REPT 10
movem.l (a0)+,d1-d7/a2-a6
movem.l d1-d7/a2-a6,n*48(a1)
n set n+1
ENDR
;Copy 32 bytes
movem.l (a0)+,d1-d7/a2
movem.l d1-d7/a2,480(a1)
lea 512(a1),a1
sub.l #512,d0
bne .More
movem.l (sp)+,d0-d7/a0-a6
rts
LDParams:
INITWORD TDU_COMP10TRACK,-1
INITWORD TDU_COMP11TRACK,-1
INITWORD TDU_COMP01TRACK,80
INITLONG TDU_STEPDELAY,3*1000
INITLONG TDU_SETTLEDELAY,15*1000
INITLONG TDU_CALIBRATEDELAY,4*1000
INITBYTE NumTracks,160
INITBYTE DriveType,DRIVE3_5
INITLONG BytesPerDisk,901120
INITLONG SectorMask,%11111111111
INITWORD GapCount,414
INITLONG SectorsPerTrack,11
INITLONG RawBufSize,13630
INITWORD FirstSector,1666
INITWORD MaxValidSec,$0a00
INITWORD WriteDskLen,$da9e
INITWORD ReadDskLen,$9a9e
INITWORD VerifyDskLen,$9761
dc.w 0
OldParams: ;for 5.25 inch drives
INITWORD TDU_COMP10TRACK,-1
INITWORD TDU_COMP11TRACK,-1
INITWORD TDU_COMP01TRACK,80/2
INITLONG TDU_STEPDELAY,3*1000*2
INITLONG TDU_SETTLEDELAY,15*1000*2
INITLONG TDU_CALIBRATEDELAY,4*1000*2
INITBYTE NumTracks,80
INITBYTE DriveType,DRIVE5_25
INITLONG BytesPerDisk,901120/2
INITLONG SectorMask,%11111111111
INITWORD GapCount,414
INITLONG SectorsPerTrack,11
INITLONG RawBufSize,13630
INITWORD FirstSector,1666
INITWORD MaxValidSec,$0a00
INITWORD WriteDskLen,$da9e
INITWORD ReadDskLen,$9a9e
INITWORD VerifyDskLen,$9761
dc.w 0
HDParams:
INITWORD TDU_COMP10TRACK,-1
INITWORD TDU_COMP11TRACK,-1
INITWORD TDU_COMP01TRACK,80
INITLONG TDU_STEPDELAY,3*1000
INITLONG TDU_SETTLEDELAY,15*1000
INITLONG TDU_CALIBRATEDELAY,4*1000
INITBYTE NumTracks,160
INITBYTE DriveType,DRIVE3_5_150RPM
INITLONG BytesPerDisk,901120*2
INITLONG SectorMask,%1111111111111111111111
INITWORD GapCount,829
INITLONG SectorsPerTrack,22
INITLONG RawBufSize,13630*2
INITWORD FirstSector,3326
INITWORD MaxValidSec,$1500
INITWORD WriteDskLen,$f53c
INITWORD ReadDskLen,$b53b
INITWORD VerifyDskLen,$aec1
dc.w 0
EndCode:
;************************* Northgate fix *********************************
;This tiny module performs a keyboard handshake. Its purpose is to wake up a
;Northgate keyboard on accelerated systems. If you don't fit that description
;then just ignore it. It's harmless.
NRomTag: dc.w RTC_MATCHWORD ;$4AFC ('illegal' opcode)
dc.l NRomTag
dc.l NEndCode ;pointer to end of code
dc.b RTF_COLDSTART
dc.b 0 ;version
dc.b NT_LIBRARY ;module type (either device or library)
dc.b 0 ;priority
dc.l FixName ;name
dc.l FixName ;IDString
dc.l Northgate ;init routine
FixName: dc.b 'Northgate_fix',$a,0
even
Northgate:
;Perform a keyboard handshake
or.b #$40,$BFEE01
clr.b $bfec01
move.l #$bfe001,a0
move.w #$46*3,d0
.. move.b (a0),(a0)
dbra d0,..
and.b #$BF,$BFEE01
rts
NEndCode:
;*************************** Debugging stuff *************************
IFNE INFO_LEVEL
KPutFmt: move.l a2,-(sp)
lea KPutChar(pc),a2
bsr KDoFmt
move.l (sp)+,a2
rts
KDoFmt: move.l a6,-(sp)
move.l 4,a6
SYS RawDoFmt
move.l (sp)+,a6
rts
KPutChar:
;Serial
comment |
move.l a6,-(sp)
move.l 4,a6
SYS RawPutChar
move.l (sp)+,a6
rts
|
;Printer
comment |
move.b #$ff,$bfe301
.Print: btst #0,$bfd000
bne .Print
move.b d0,$bfe101
rts
|
;Memory
tst.l MemPtr
bne .OK
move.l #$500000,MemPtr
.OK:
push a0
move.l MemPtr(pc),a0
move.b d0,(a0)+
move.l a0,MemPtr
pop a0
rts
MemPtr: dc.l 0
ENDC