home *** CD-ROM | disk | FTP | other *** search
/ World of A1200 / World_Of_A1200.iso / programs / disk / misc / hackdisk / hackdisk.s < prev    next >
Text File  |  1995-02-27  |  69KB  |  3,023 lines

  1. ********************************************************************************
  2. * hackdisk.s -- my very own version of trackdisk.device
  3. * Copyright (C) 1992 by Dan Babcock
  4. *
  5. * Version history:
  6. * V1.00 04/25/92 First version. Supports 880K drives only.
  7. * V1.01 04/28/92 Optimized CopyMem
  8. * V1.02 04/30/92 More intelligent write algorithm fixes performance problem
  9. * V1.03 05/03/92 Sets IO_ACTUAL
  10. * V1.04 06/24/92 Fixes a couple bugs: RemChangeInt works now, and disk change
  11. *                errors are only reported when it makes sense.
  12. *                CrossDOSV5/CrossPC flushed out these bugs.
  13. * V1.10 07/04/92 Compatible with Kickstart 1.2 and higher.
  14. *                Fixed Seek (IO_OFFSET is in bytes, not tracks).
  15. *                Released on Fish 697!
  16. * V1.11 08/03/92 Fixed bug in readtrack routine - buffer not cleared if error
  17. *                in some cases (no sync).
  18. *                Fills TC_Userdata with bogus value to fool CrossDOS under Kick
  19. *                1.2/1.3.
  20. *                Puts -1 in IO_DEVICE on open error. Some (though not many)
  21. *                applications depend on this.
  22. *                Using a slightly more creative "invalid sync word" (was 0).
  23. *                This should fix many drive incompatibility problems.
  24. *                Glitch in disk check code fixed (only affected non-NoClick mode).
  25. * V1.12 08/16/92 CMD_STOP/CMD_START should work now.
  26. ********************************************************************************
  27.  
  28. ;This source was assembled with Macro68 release 3.
  29.  
  30.     exeobj
  31.     objfile    'devs:hackdisk.device'
  32.     MC68000
  33.     multipass
  34.  
  35. ;Standard register usage:
  36. ;A6            - ExecBase or $dff000
  37. ;A5 (A_DEVICE) - device ptr
  38. ;A4 (A_UNIT)   - unit ptr
  39. ;A3 (A_IO)     - IORequest
  40.  
  41.     IFND    SYS
  42. SYS    macro
  43.     jsr    _LVO\1(a6)
  44.     endm
  45.     ENDC
  46.  
  47.     IFND    _custom
  48. _custom    equ    $dff000
  49.     ENDC
  50.  
  51. ;There are supposed to be two modes of operation, the RamKick (ROM) mode
  52. ;and the DEVS:/mountlist (DISK) mode. However, the disk mode is only
  53. ;half-implemented, because I decided at some point it was useless.
  54. ROM    equ    1
  55. DISK    equ    2    ;NOTE: Not currently functioning!
  56. TYPE    equ    ROM
  57.  
  58. ;Set INFO_LEVEL to 1 for full debugging output over the internal serial port.
  59. INFO_LEVEL    equ    0
  60.  
  61. A_DEVICE    equr    a5
  62. A_UNIT    equr    a4
  63. A_IO    equr    a3
  64.  
  65.  
  66. VERSION    equ    127
  67. REVISION    equ    12
  68. MYPRI    equ    0
  69.  
  70. MD_NUMUNITS    equ    4
  71. STACKSIZE    equ    4000
  72. TASKPRI    equ    5
  73.  
  74. RETRYCNT    equ    3
  75.  
  76. ;Put a message to the serial port.  Used like so:
  77. ;
  78. ;PUTDEBUG    <'Init: called'>
  79. ;
  80. ;Parameters can be printed out by pushing them on the stack and
  81. ;adding the appropriate C printf-style % formatting commands.
  82.  
  83. PUTDEBUG     macro    ;<msg>
  84.     ifne    INFO_LEVEL
  85.     movem.l    d0-d1/a0-a1,-(sp)
  86.     lea    .msg\@(pc),a0    ;Point to static format string
  87.     lea    16(sp),a1    ;Point to args
  88.     bsr    KPutFmt
  89.     movem.l    (sp)+,d0-d1/a0-a1
  90.     bra    .end\@
  91. .msg\@:
  92.     dc.b    \1,$a,0
  93.     even
  94. .end\@:
  95.     endc
  96.     endm
  97.  
  98. *--------------------------------------------------------------------
  99. *
  100. * Driver error defines
  101. *
  102. *--------------------------------------------------------------------
  103.  
  104. ;TDERR_NotSpecified    EQU    20    ; general catchall
  105. ;TDERR_NoSecHdr        EQU    21    ; couldn't even find a sector
  106. ;TDERR_BadSecPreamble    EQU    22    ; sector looked wrong
  107. ;TDERR_BadSecID        EQU    23    ; ditto
  108. ;TDERR_BadHdrSum        EQU    24    ; header had incorrect checksum
  109. ;TDERR_BadSecSum        EQU    25    ; data had incorrect checksum
  110. ;TDERR_TooFewSecs    EQU    26    ; couldn't find enough sectors
  111. ;TDERR_BadSecHdr        EQU    27    ; another "sector looked wrong"
  112. ;TDERR_WriteProt        EQU    28    ; can't write to a protected disk
  113. ;TDERR_DiskChanged    EQU    29    ; no disk in the drive
  114. ;TDERR_SeekError        EQU    30    ; couldn't find track 0
  115. ;TDERR_NoMem        EQU    31    ; ran out of memory
  116. ;TDERR_BadUnitNum    EQU    32    ; asked for a unit > NUMUNITS
  117. ;TDERR_BadDriveType    EQU    33    ; not a drive that trackdisk groks
  118. ;TDERR_DriveInUse    EQU    34    ; someone else allocated the drive
  119. ;TDERR_PostReset        EQU    35    ; user hit reset; awaiting doom
  120.  
  121. ; STRUCTURE TIMEVAL,0
  122. ;    ULONG    TV_SECS
  123. ;    ULONG    TV_MICRO
  124. ;    LABEL    TV_SIZE
  125.  
  126. ;Unit structure
  127.  
  128. *--------------------------------------------------------------------
  129. *
  130. * Public portion of unit structure
  131. *
  132. *--------------------------------------------------------------------
  133.  
  134. ;*------ UNIT_FLAG definitions:
  135.  
  136. ;These are bogus, but I won't re-define them.
  137. ;    BITDEF  UNIT,ACTIVE,0        ; driver is active
  138. ;    BITDEF  UNIT,INTASK,1        ; running in driver's task
  139.     BITDEF    UNIT,DiskInDrive,2
  140.     BITDEF    UNIT,WriteProtected,3
  141.  
  142. ; STRUCTURE TDU_PUBLICUNIT,UNIT_SIZE
  143. ;    UWORD    TDU_COMP01TRACK        ; track for first precomp
  144. ;    UWORD    TDU_COMP10TRACK        ; track for second precomp
  145. ;    UWORD    TDU_COMP11TRACK        ; track for third precomp
  146. ;    ULONG    TDU_STEPDELAY        ; time to wait after stepping
  147. ;    ULONG    TDU_SETTLEDELAY        ; time to wait after seeking
  148. ;    UBYTE    TDU_RETRYCNT        ; # of times to retry
  149. ;    UBYTE    TDU_PUBFLAGS        ; public flags, see below
  150. ;    UWORD    TDU_CURRTRK        ; track heads are over
  151.                     ;  (ONLY ACCESS WHILE UNIT IS STOPPED!)
  152. ;    ULONG    TDU_CALIBRATEDELAY    ; time to wait after stepping
  153.                     ; for recalibrate
  154. ;    ULONG    TDU_COUNTER        ; counter for disk changes
  155.                     ;  (ONLY ACCESS WHILE UNIT IS STOPPED!)
  156. ;    LABEL    TDU_PUBLICUNITSIZE
  157. ;*--------------------------------------------------------------------
  158.  
  159.     STRUCTURE    MyUnit,TDU_PUBLICUNITSIZE
  160.     STRUCT    Drive_LastCheck,TV_SIZE    ;last time diskchange was checked
  161.     STRUCT    ChangeIntList,MLH_SIZE    ;must be initialized!!!
  162.     LONG    TDRemoveInt
  163.  
  164.     IFEQ    TYPE-DISK
  165. ;not used by ROM driver
  166.     STRUCT    ChangeInt,IS_SIZE
  167.     STRUCT    TDPort,MP_SIZE
  168.     STRUCT    TDIORequest,IOTD_SIZE
  169.     ENDC
  170.  
  171.     BYTE    UnitNum
  172.     BYTE    MyUnit_Pad
  173. ;MUST be word-aligned!!!
  174.     LABEL    MyUnit_Sizeof
  175.  
  176. ;*
  177. ;* Flags for TDU_PUBFLAGS:
  178. ;*
  179. ;    BITDEF    TDP,NOCLICK,0        ; set to enable noclickstart
  180.     BITDEF    TDP,VERIFY,1
  181.  
  182. ;Device global structure -- accessed as positive offsets from device base
  183. ;Note: The stuff in LIB_SIZE is *required*. Anything else is up to you...
  184.     STRUCTURE    DeviceGlobals,LIB_SIZE
  185.     STRUCT    TaskPort,MP_SIZE    ;MsgPort for task
  186.     STRUCT    Task,TC_SIZE
  187.     STRUCT    TaskStack,STACKSIZE
  188.     STRUCT    TempTimeVal,TV_SIZE
  189.     LONG    SegList    ;not used for ROM version
  190.     STRUCT    TimerIORequest,IOTV_SIZE
  191.     STRUCT    TimerPort,MP_SIZE
  192.     STRUCT    LastCheck,TV_SIZE    ;last time diskchange was checked
  193.     STRUCT    DiskResourceUnit,DRU_SIZE    ;must be initialized!!!
  194.     STRUCT    DiskResourcePort,MP_SIZE    ;must be initialized!!!
  195.  
  196. ;Allocated memory pointers
  197.     LONG    RawBuffer
  198.     LONG    DecodedBuffer
  199.     LONG    VerifyBuffer
  200.  
  201.     STRUCT    SectorLabels,11*16
  202.     STRUCT    Unit0,MyUnit_Sizeof
  203.     STRUCT    Unit1,MyUnit_Sizeof
  204.     STRUCT    Unit2,MyUnit_Sizeof
  205.     STRUCT    Unit3,MyUnit_Sizeof
  206.     LONG    GraphBase
  207.     LONG    IntBase
  208.     LONG    DiskResourceBase
  209.     LONG    CIABase
  210.     WORD    SyncCount
  211.     WORD    IndexDskLen
  212.     LONG    WriteMap
  213.  
  214.     LABEL    StartSigs
  215.     LONG    SyncSig
  216.     LONG    BlockSig
  217.     LABEL    EndSigs
  218.  
  219.     BYTE    BufferTrack
  220.     BYTE    BufferDrive
  221.     BYTE    DEV_FLAGS
  222.     BYTE    MotorState
  223.     BYTE    InquireBits
  224.     LABEL    MyDev_Sizeof
  225.  
  226. NumSigs    equ    (EndSigs-StartSigs)/4
  227.  
  228. ;Bit definitions for DEV_FLAGS:
  229.     BITDEF    DEV,Dirty,0    ;buffered track has been changed
  230.     BITDEF    DEV,Stopped,1    ;device is stopped
  231.     BITDEF    DEV,Verify,2    ;used by BlockInt for verify
  232.  
  233. ;    BITDEF    DEV,Test,3    ;for debugging only
  234.  
  235.  
  236.     IFEQ    TYPE-DISK
  237. ;The first executable location.  This should return an error in case someone
  238. ;tried to run us as a program (instead of loading us as a device).
  239.     moveq    #-1,d0
  240.     rts
  241.     ENDC
  242.  
  243. ;A romtag structure.  After your driver is brought in from disk, the
  244. ;disk image will be scanned for this structure to discover magic constants
  245. ;about you (such as where to start running you from...).
  246.  
  247. RomTag:
  248.     dc.w    RTC_MATCHWORD    ;$4AFC ('illegal' opcode)
  249.     dc.l    RomTag
  250.     dc.l    EndCode    ;pointer to end of code
  251.     dc.b    RTF_AUTOINIT+RTF_COLDSTART    ;set things up automatically
  252.     dc.b    VERSION
  253.     dc.b    NT_DEVICE    ;module type (either device or library)
  254.     dc.b    MYPRI    ;usually not important
  255.     dc.l    Name    ;name used in OpenDevice
  256.     dc.l    IDString    ;optional
  257.     dc.l    Init    ;more init info
  258.  
  259.     IFEQ    TYPE-ROM
  260. Name:    dc.b    'trackdisk.device',0
  261.     even
  262.     ENDC
  263.     IFEQ    TYPE-DISK
  264. Name:    dc.b    'hackdisk.device',0        ;this MUST be the same as the
  265.     even                ;disk-resident name!
  266.     ENDC
  267.  
  268. IDString:    dc.b    'Hackdisk V1.12 - Copyright (C) 1992 by Dan Babcock',$a,0
  269.     even
  270. DiskResourceName:
  271.     dc.b    'disk.resource',0
  272. TimerName:
  273.     dc.b    'timer.device',0
  274. GraphName:
  275.     dc.b    'graphics.library',0
  276. CIAName:    dc.b    'ciab.resource',0
  277. IntName:    dc.b    'intuition.library',0
  278.     IFEQ    TYPE-DISK
  279. TDName:    dc.b    'trackdisk.device',0
  280.     ENDC
  281.     even
  282.  
  283. ;The romtag specified that we were "RTF_AUTOINIT".  This means that the
  284. ;RT_INIT structure member points to one of these tables below.  If the
  285. ;AUTOINIT bit was not set then RT_INIT would point to a routine to run.
  286.  
  287. Init:    dc.l    MyDev_Sizeof    ;data space size (at least
  288.                 ;LIB_SIZE!!)
  289.     dc.l    FuncTable    ;pointer to function initializers
  290.     dc.l    DataTable    ;pointer to data initializers
  291.     dc.l    InitRoutine    ;routine to run
  292.  
  293. FuncTable:
  294.     dc.w    -1    ;this indicates that the following are offsets,
  295.             ;rather than absolute addresses
  296.     dc.w    Open-FuncTable    ;standard system routines
  297.     dc.w    _Close-FuncTable
  298.     dc.w    Expunge-FuncTable
  299.     dc.w    .Null-FuncTable    ;Reserved for future use!
  300.     dc.w    BeginIO-FuncTable    ;device definitions
  301.     dc.w    AbortIO-FuncTable
  302.     dc.w    -1    ;function table end marker
  303. .Null:    moveq    #0,d0
  304.     rts
  305.  
  306. ;The data table initializes static data structures. The format is
  307. ;specified in exec/InitStruct routine's manual pages.  The
  308. ;INITBYTE/INITWORD/INITLONG macros are in the file "exec/initializers.i".
  309. ;The first argument is the offset from the device base for this
  310. ;byte/word/long. The second argument is the value to put in that cell.
  311. ;The table is null terminated
  312.  
  313. DataTable:
  314.     INITBYTE    LN_TYPE,NT_DEVICE
  315.     INITLONG    LN_NAME,Name
  316.     INITBYTE    LIB_FLAGS,LIBF_SUMUSED+LIBF_CHANGED
  317.     INITWORD    LIB_VERSION,VERSION
  318.     INITWORD    LIB_REVISION,REVISION
  319.     INITLONG    LIB_IDSTRING,IDString
  320.     dc.w    0    ;terminate list (only one byte needed)
  321.  
  322. CmdTable:    dc.w    Invalid-CmdTable        ;0    CMD_INVALID
  323.     dc.w    Invalid-CmdTable        ;1    CMD_RESET
  324.     dc.w    Read-CmdTable        ;2    CMD_READ
  325.     dc.w    Write-CmdTable        ;3    CMD_WRITE / ETD_
  326.     dc.w    Update-CmdTable        ;4    CMD_UPDATE / ETD_
  327.     dc.w    Clear-CmdTable        ;5    CMD_CLEAR / ETD_
  328.     dc.w    MyStop-CmdTable        ;6    CMD_STOP / ETD_
  329.     dc.w    Start-CmdTable        ;7    CMD_START
  330.     dc.w    Invalid-CmdTable        ;8    CMD_FLUSH
  331.     dc.w    Motor-CmdTable        ;9    TD_MOTOR / ETD_
  332.     dc.w    TDSeek-CmdTable        ;10    TD_SEEK / ETD_
  333.     dc.w    Format-CmdTable        ;11    TD_FORMAT
  334.     dc.w    TDRemove-CmdTable        ;12    TD_REMOVE
  335.     dc.w    ChangeNum-CmdTable        ;13    TD_CHANGENUM
  336.     dc.w    ChangeState-CmdTable    ;14    TD_CHANGESTATE
  337.     dc.w    ProtStatus-CmdTable        ;15    TD_PROTSTATUS
  338.     dc.w    TDRawRead-CmdTable        ;16    TD_RAWREAD
  339.     dc.w    RawWrite-CmdTable        ;17    TD_RAWWRITE
  340.     dc.w    GetDriveType-CmdTable    ;18    TD_GETDRIVETYPE
  341.     dc.w    GetNumTracks-CmdTable    ;19    TD_GETNUMTRACKS
  342.     dc.w    AddChangeInt-CmdTable    ;20    TD_ADDCHANGEINT
  343.     dc.w    RemChangeInt-CmdTable    ;21    TD_REMCHANGEINT
  344.     dc.w    GetGeometry-CmdTable    ;22    TD_GETGEOMETRY
  345.     dc.w    Invalid-CmdTable        ;23    TD_EJECT
  346. EndCmdTable:
  347. HighestCommand    equ    ((EndCmdTable-CmdTable)/2)-1
  348. Invalid:    move.b    #IOERR_NOCMD,IO_ERROR(A_IO)
  349.     rts
  350.  
  351. InitRoutine:
  352. ;A0 - segment
  353. ;D0 - device ptr
  354.  
  355. ;Returns with the device ptr still in D0 if successful, otherwise zero.
  356.  
  357.     PUTDEBUG    <'InitRoutine: Entered'>
  358.     movem.l    d1-d7/a0-a6,-(sp)
  359.     move.l    d0,A_DEVICE
  360.     move.l    a0,SegList(A_DEVICE)
  361.     move.l    4,a6
  362.  
  363. ;Initialize ports
  364.     lea    TaskPort(A_DEVICE),a0
  365.     bsr    InitPort
  366.     lea    TimerPort(A_DEVICE),a0
  367.     bsr    InitPort
  368.  
  369. ;Invalidate track buffer
  370.     bsr    Clear
  371.  
  372. ;Open libraries/resources
  373.  
  374.     lea    DiskResourceName(pc),a1    ;'disk.resource'
  375.     SYS    OpenResource
  376.     move.l    d0,DiskResourceBase(A_DEVICE)
  377.     beq    .Failed
  378.     lea    CIAName(pc),a1    ;'ciab.resource'
  379.     SYS    OpenResource
  380.     move.l    d0,CIABase(A_DEVICE)
  381.     beq    .Failed
  382.  
  383.     lea    GraphName(pc),a1
  384.     SYS    OldOpenLibrary
  385.     move.l    d0,GraphBase(A_DEVICE)
  386.     beq    .Failed
  387.     lea    IntName(pc),a1
  388.     SYS    OldOpenLibrary
  389.     move.l    d0,IntBase(A_DEVICE)
  390.     beq    .Failed
  391.  
  392. ;Allocate CHIP memory. (Note: This will change when I support other drive
  393. ;types).
  394.     move.l    #DISK_RawBufSize,d0
  395.     move.l    #MEMF_CHIP,d1
  396.     SYS    AllocMem
  397.     move.l    d0,RawBuffer(A_DEVICE)
  398.     beq    .Failed
  399.  
  400.     move.l    #512*11,d0
  401.     move.l    #MEMF_CHIP,d1
  402.     SYS    AllocMem
  403.     move.l    d0,DecodedBuffer(A_DEVICE)
  404.     beq    .Failed
  405.  
  406.     move.l    #(1088*11)+2,d0
  407.     move.l    #MEMF_CHIP,d1
  408.     SYS    AllocMem
  409.     move.l    d0,VerifyBuffer(A_DEVICE)
  410.     beq    .Failed
  411.  
  412. ;Set up disk.resource structure.
  413.     lea    DiskResourcePort(A_DEVICE),a0
  414.     bsr    InitPort
  415.     move.l    a0,DiskResourceUnit+MN_REPLYPORT(A_DEVICE)
  416. ;    move.b    #PA_SIGNAL,MP_FLAGS(a0)    ;not needed (0)
  417.     lea    Name(pc),a0
  418.     move.l    a0,DiskResourceUnit+LN_NAME(A_DEVICE)
  419.  
  420.     move.l    A_DEVICE,DiskResourceUnit+DRU_DISCBLOCK+IS_DATA(A_DEVICE)
  421.     move.l    A_DEVICE,DiskResourceUnit+DRU_DISCSYNC+IS_DATA(A_DEVICE)
  422.     move.l    A_DEVICE,DiskResourceUnit+DRU_INDEX+IS_DATA(A_DEVICE)
  423.     lea    BlockInt(pc),a0
  424.     move.l    a0,DiskResourceUnit+DRU_DISCBLOCK+IS_CODE(A_DEVICE)
  425.     lea    SyncInt(pc),a0
  426.     move.l    a0,DiskResourceUnit+DRU_DISCSYNC+IS_CODE(A_DEVICE)
  427.     lea    IndexInt(pc),a0
  428.     move.l    a0,DiskResourceUnit+DRU_INDEX+IS_CODE(A_DEVICE)
  429.     moveq    #NT_INTERRUPT,d0
  430.     move.b    d0,DiskResourceUnit+DRU_DISCBLOCK+LN_TYPE(A_DEVICE)
  431.     move.b    d0,DiskResourceUnit+DRU_DISCSYNC+LN_TYPE(A_DEVICE)
  432.     move.b    d0,DiskResourceUnit+DRU_INDEX+LN_TYPE(A_DEVICE)
  433.  
  434. ;Initialize timer IORequest (except signal)
  435.     lea    TimerPort(A_DEVICE),a0
  436. ;    move.b    #PA_SIGNAL,MP_FLAGS(a0)    ;not needed (0)
  437.     move.l    a0,TimerIORequest+MN_REPLYPORT(A_DEVICE)
  438.     lea    TimerName(pc),a0
  439.     moveq    #UNIT_MICROHZ,d0
  440.     lea    TimerIORequest(A_DEVICE),a1
  441.     moveq    #0,d1
  442.     SYS    OpenDevice
  443.     tst.l    d0
  444.     bne    .Failed
  445.  
  446. ;*** Set up unit structures
  447.     moveq    #0,d2
  448.     lea    Unit0(A_DEVICE),A_UNIT
  449. .UnitLoop:
  450.  
  451.     IFEQ    TYPE-DISK
  452.     lea    TDPort(A_UNIT),a0
  453.     bsr    InitPort
  454.     move.l    a0,TDIORequest+MN_REPLYPORT(A_UNIT)
  455.     ENDC
  456.  
  457.     move.b    d2,UnitNum(A_UNIT)
  458.     lea    ChangeIntList(A_UNIT),a0
  459.     NEWLIST    a0
  460.  
  461.     bset    #1,TDU_PUBFLAGS(A_UNIT)    ;verify ON!
  462.     moveq    #-1,d0
  463.     move.w    #80,TDU_COMP01TRACK(A_UNIT)
  464.     move.w    d0,TDU_COMP10TRACK(A_UNIT)
  465.     move.w    d0,TDU_COMP11TRACK(A_UNIT)
  466.     move.l    #3*1000,TDU_STEPDELAY(A_UNIT)
  467.     move.l    #15*1000,TDU_SETTLEDELAY(A_UNIT)
  468.     move.b    #RETRYCNT,TDU_RETRYCNT(A_UNIT)
  469.     move.l    #4*1000,TDU_CALIBRATEDELAY(A_UNIT)
  470.  
  471.     lea    MyUnit_Sizeof(A_UNIT),A_UNIT
  472.     addq.b    #1,d2
  473.     cmp.b    #MD_NUMUNITS,d2
  474.     blo    .UnitLoop
  475.  
  476. ;Set up task
  477.     move.b    #PA_IGNORE,TaskPort+MP_FLAGS(A_DEVICE)
  478.     move.b    #NT_MSGPORT,TaskPort+LN_TYPE(A_DEVICE)
  479.     lea    Task(A_DEVICE),a1    ;Task Control Block
  480.  
  481. ;To make CrossDOS happy under 1.2/1.3
  482.     st    TC_Userdata(a1)
  483.  
  484.     lea    Name(pc),a0
  485.     move.l    a0,LN_NAME(a1)
  486.     move.b    #TASKPRI,LN_PRI(a1)
  487.     move.b    #NT_TASK,LN_TYPE(a1)
  488.     lea    TaskCode(pc),a2    ;initial PC
  489.     sub.l    a3,a3    ;final PC
  490.     lea    TaskStack(A_DEVICE),a0
  491.     move.l    a0,TC_SPLOWER(a1)
  492.     lea    STACKSIZE(a0),a0
  493.     move.l    a0,TC_SPUPPER(a1)
  494.  
  495.     move.l    A_DEVICE,-(a0)    ;pass device pointer to task
  496.     move.l    a0,TC_SPREG(a1)
  497.     move.l    a1,TaskPort+MP_SIGTASK(A_DEVICE)
  498.     SYS    AddTask
  499.  
  500.     move.l    A_DEVICE,d0
  501.     PUTDEBUG    <'InitRoutine: Done'>
  502. .End:    movem.l    (sp)+,d1-d7/a0-a6
  503.     rts
  504. .Failed:
  505.     PUTDEBUG    <'InitRoutine: Failed'>
  506.     bsr    CloseEverything
  507.     moveq    #0,d0
  508.     bra    .End
  509.  
  510. InitPort:
  511. ;Enter with pointer to port in A0. All registers preserved.
  512.     push    a0
  513.     lea    MP_MSGLIST(a0),a0
  514.     NEWLIST    a0
  515.     pop    a0
  516.     rts
  517.  
  518. CloseEverything:
  519. ;This routines cleans up device stuff. Enter with A_DEVICE.
  520.  
  521.     movem.l    d0-d1/a0-a1/a6,-(sp)
  522.     move.l    4,a6
  523. ;Close timer
  524.     tst.b    TimerIORequest+IO_ERROR(A_DEVICE)
  525.     beq    .SkipTimer
  526.     lea    TimerIORequest(A_DEVICE),a1
  527.     SYS    CloseDevice
  528. .SkipTimer:
  529.  
  530. ;Remove task
  531.     lea    Name(pc),a1
  532.     SYS    FindTask
  533.     tst.l    d0
  534.     beq    .SkipTask
  535.     move.l    d0,a1
  536.     SYS    RemTask
  537. .SkipTask:
  538.  
  539. ;Close libraries
  540.     move.l    GraphBase(A_DEVICE),d0
  541.     beq    .SkipGfx
  542.     move.l    d0,a1
  543.     SYS    CloseLibrary
  544. .SkipGfx:
  545.     move.l    IntBase(A_DEVICE),d0
  546.     beq    .SkipInt
  547.     move.l    d0,a1
  548.     SYS    CloseLibrary
  549. .SkipInt:
  550.  
  551. ;Free CHIP RAM. (Note: This will change when I support other drive types).
  552.     move.l    RawBuffer(A_DEVICE),d0
  553.     beq    .SkipRaw
  554.     move.l    d0,a1
  555.     move.l    #DISK_RawBufSize,d0
  556.     SYS    FreeMem
  557. .SkipRaw:
  558.     move.l    DecodedBuffer(A_DEVICE),d0
  559.     beq    .SkipDecoded
  560.     move.l    d0,a1
  561.     move.l    #512*11,d0
  562.     SYS    FreeMem
  563. .SkipDecoded:
  564.     move.l    VerifyBuffer(A_DEVICE),d0
  565.     beq    .SkipVerify
  566.     move.l    d0,a1
  567.     move.l    #(1088*11)+2,d0
  568.     SYS    FreeMem
  569. .SkipVerify:
  570. .End:    movem.l    (sp)+,d0-d1/a0-a1/a6
  571.     rts
  572.  
  573. ;******************************* Task code ******************************
  574.  
  575. TaskCode:
  576.     PUTDEBUG    <'Task: Entered'>
  577.     move.l    4,a6
  578.  
  579. ;Grab the arguments passed down from our parent
  580.     move.l    4(sp),A_DEVICE    ;Device pointer
  581.  
  582. ;General initialization
  583.  
  584.     lea    StartSigs(A_DEVICE),a2
  585.     moveq    #NumSigs-1,d2    ;Number of signals to allocate-1
  586. .SigLoop:
  587.     moveq    #-1,d0    ;-1 is any signal at all
  588.     SYS    AllocSignal    ;Allocate signals for I/O interrupts
  589.     moveq    #0,d1    ;Convert bit number signal mask
  590.     bset    d0,d1
  591.     move.l    d1,(a2)+    ;Save in unit structure
  592.     dbra    d2,.SigLoop
  593.  
  594.     moveq    #-1,d0    ;-1 is any signal at all
  595.     SYS    AllocSignal    ;Allocate a signal
  596.     move.b    d0,TaskPort+MP_SIGBIT(A_DEVICE)
  597.     move.b    #PA_SIGNAL,TaskPort+MP_FLAGS(A_DEVICE)    ;Make message port "live"
  598.  
  599.     moveq    #-1,d0    ;-1 is any signal at all
  600.     SYS    AllocSignal    ;Allocate a signal
  601.     move.b    d0,TimerPort+MP_SIGBIT(A_DEVICE)
  602.     move.l    ThisTask(a6),TimerPort+MP_SIGTASK(A_DEVICE)
  603.  
  604.     moveq    #-1,d0    ;-1 is any signal at all
  605.     SYS    AllocSignal    ;Allocate a signal
  606.     move.b    d0,DiskResourcePort+MP_SIGBIT(A_DEVICE)
  607.     move.l    ThisTask(a6),DiskResourcePort+MP_SIGTASK(A_DEVICE)
  608.  
  609.     bsr    GetDrive
  610.     bsr    Inquire
  611.  
  612. ;Disk version only:
  613. ;Open trackdisk.device for all valid units and install diskchange handler.
  614.     IFEQ    TYPE-DISK
  615.     moveq    #MD_NUMUNITS-1,d2
  616.     move.b    InquireBits(A_DEVICE),d3
  617.     lea    Unit0(A_DEVICE),A_UNIT
  618. .TDLoop:
  619.     moveq    #-1,d0    ;-1 is any signal at all
  620.     SYS    AllocSignal    ;Allocate a signal
  621.     move.b    d0,TDPort+MP_SIGBIT(A_UNIT)
  622.     move.l    ThisTask(a6),TDPort+MP_SIGTASK(A_UNIT)
  623.     moveq    #0,d0
  624.     move.b    UnitNum(A_UNIT),d0
  625.     btst    d0,d3
  626.     beq    .NextTD
  627.     moveq    #0,d1
  628.     lea    TDName(pc),a0
  629.     lea    TDIORequest(A_DEVICE),a1
  630.     SYS    OpenDevice
  631.     tst.l    d0
  632.     beq    .TDOK
  633. ;Should never get here
  634. ..    move.w    #$0f00,$dff180
  635.     bra    ..
  636. .TDOK:
  637.  
  638. ;Install a diskchange handler via TD_ADDCHANGEINT.
  639.     lea    TDIORequest(A_DEVICE),a1
  640.     lea    ChangeInt(A_UNIT),a0
  641.     lea    ChangeIntCode(pc),a2
  642.     move.l    a2,IS_CODE(a0)
  643.     move.l    A_UNIT,IS_DATA(a0)
  644.     move.l    a0,IO_DATA(a1)
  645.     move.l    #IS_SIZE,IO_LENGTH(a1)    ;this is dumb
  646.     move.w    #TD_ADDCHANGEINT,IO_COMMAND(a1)
  647.     SYS    SendIO
  648.  
  649. .NextTD:    lea    MyUnit_Sizeof(A_UNIT),A_UNIT
  650.     dbra    d2,.TDLoop
  651.     ENDC
  652.  
  653.     IFNE    INFO_LEVEL
  654.     push    d0
  655.     moveq    #0,d0
  656.     move.b    InquireBits(A_DEVICE),d0
  657.     move.l    d0,-(sp)
  658.     PUTDEBUG    <'InquireBits=%lx'>
  659.     addq.l    #4,sp
  660.     pop    d0
  661.     ENDC
  662.  
  663.     bsr    SeekZeroAll    ;Send all drives to track zero.
  664.     bsr    FreeDrive
  665.  
  666.     bra    .NextMessage
  667.  
  668. ;Main loop: Wait for a new message and handle disk changes.
  669.  
  670. .MainLoop:
  671.  
  672. ;    PUTDEBUG    <'Task: Waiting'>
  673.     moveq    #0,d0
  674.     move.b    MP_SIGBIT+TaskPort(A_DEVICE),d1
  675.     bset    d1,d0
  676.     move.l    #500000,d1
  677.     bsr    TimeOutWait
  678.     tst.l    d0
  679.     bne    .NextMessage
  680.  
  681. ;0.5 seconds have passed without receiving any work, so we check for disk
  682. ;changes, then drop into .NextMessage.
  683. ;Note: A6 undefined.
  684.  
  685. .CheckDiskChange:
  686.     moveq    #MD_NUMUNITS-1,d2
  687.     move.b    InquireBits(A_DEVICE),d3
  688.     lea    Unit0(A_DEVICE),A_UNIT
  689.     bsr    GetDrive
  690.     move.l    TimerIORequest+IO_DEVICE(A_DEVICE),a6
  691.  
  692. ;Algorithm (for each unit):
  693. ;Check hardware diskchange bit. If disk was ejected, seek to track zero,
  694. ;clear DiskInDrive bit. End.
  695. ;If no disk in drive, check NoClick bit. If set, immediately step toward
  696. ;track zero (using custom step routine). If clear, check the
  697. ;Drive_LastChecked time against the current time. If less than 2.5 seconds,
  698. ;End. If >2.5 seconds, step the head toward track zero, unless already on
  699. ;track zero. Check the hardware diskchange bit. If a disk is present in the
  700. ;drive set the DiskInDrive bit and check the hardware write protect status,
  701. ;setting WriteProtected as needed. End.
  702. ;Note: We also increment the diskchange counter in the public part of the
  703. ;unit structure, if a disk was inserted or removed.
  704.  
  705. .UnitLoop:
  706.     move.b    UnitNum(A_UNIT),d4
  707.     btst    d4,d3
  708.     beq    .NoCheck
  709.  
  710.     lea    TempTimeVal(A_DEVICE),a0
  711.     bsr    GetSysTime
  712.  
  713.     btst    #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
  714.     beq    .ThinkNoDisk
  715.     bsr    SelectDriveSameMotor
  716.     btst    #CIAB_DSKCHANGE,ciaapra
  717.     bne    .NoChange
  718. ;Disk was removed.
  719.     bclr    #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
  720.     bsr    SeekZero
  721.     bsr    MotorOff
  722.     bsr    Clear
  723.     bra    .Change
  724. .ThinkNoDisk:
  725.     btst    #TDPB_NOCLICK,TDU_PUBFLAGS(A_UNIT)
  726.     beq    .Click
  727.     bsr    SelectDriveSameMotor
  728.     bset    #CIAB_DSKDIREC,ciabprb    ;set to "out" (lower tracks)
  729.     bset    #CIAB_DSKSTEP,ciabprb
  730.     bclr    #CIAB_DSKSTEP,ciabprb    ;step head
  731.     bset    #CIAB_DSKSTEP,ciabprb
  732.     bsr    Deselect
  733.     move.l    TDU_SETTLEDELAY(A_UNIT),d0
  734.     bsr    delay
  735.     bsr    SelectDriveSameMotor
  736.     bra    .ChkChg
  737. .Click:
  738.     lea    Drive_LastCheck(A_UNIT),a1
  739.     SYS    SubTime
  740.     cmp.l    #2,TV_SECS(a0)
  741.     blo    .NoCheck
  742.     bhi    .SkipMicro
  743.     cmp.l    #500000,TV_MICRO(a0)
  744.     blo    .NoCheck
  745. .SkipMicro:
  746.     lea    Drive_LastCheck(A_UNIT),a0
  747.     bsr    GetSysTime
  748.     move.b    UnitNum(A_UNIT),d2    ;for Seek
  749.     move.w    TDU_CURRTRK(A_UNIT),d3
  750.     subq.w    #2,d3
  751.     bpl    .NotZero
  752.     moveq    #2,d3
  753. .NotZero:    bsr    SelectDriveSameMotor
  754.     bsr    SeekNoUpdate
  755. .ChkChg:    btst    #CIAB_DSKCHANGE,ciaapra
  756.     beq    .NoChange    ;no disk in drive
  757.     bset    #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
  758.     bclr    #UNITB_WriteProtected,UNIT_FLAGS(A_UNIT)
  759.     btst    #CIAB_DSKPROT,ciaapra
  760.     bne    .Change    ;not write protected
  761.     bset    #UNITB_WriteProtected,UNIT_FLAGS(A_UNIT)
  762. .Change:
  763.     bsr    Deselect
  764.     addq.l    #1,TDU_COUNTER(A_UNIT)
  765.  
  766. ;This code notifies users of TD_REMOVE and TD_ADDCHANGEINT via Cause.
  767.  
  768.     push    a6
  769.     move.l    4,a6
  770.     SYS    Forbid
  771.     move.l    TDRemoveInt(A_UNIT),d0
  772.     beq    .NoRemoveInt
  773.     move.l    d0,a1
  774.     SYS    Cause
  775. .NoRemoveInt:
  776.     move.l    ChangeIntList+LH_HEAD(A_UNIT),a2
  777. .IntLoop:    move.l    (a2),d0
  778.     beq    .DoneInt
  779.     move.l    IO_DATA(a2),a1
  780.     move.l    d0,a2
  781.     SYS    Cause
  782.     bra    .IntLoop
  783. .DoneInt:    SYS    Permit
  784.     pop    a6
  785. .NoChange:
  786.     lea    LastCheck(A_DEVICE),a0
  787.     bsr    GetSysTime
  788. .NoCheck:
  789.     lea    MyUnit_Sizeof(A_UNIT),A_UNIT
  790.     dbra    d2,.UnitLoop
  791.     move.l    4,a6
  792.     bsr    FreeDrive
  793.  
  794. .NextMessage:
  795.     btst    #DEVB_Stopped,DEV_FLAGS(A_DEVICE)    ;See if we are stopped
  796.     bne    .MainLoop    ;device is stopped, so ignore messages
  797.  
  798.     lea    TaskPort(A_DEVICE),a0
  799.     SYS    GetMsg    ;Get the next request
  800.     tst.l    d0
  801.     beq    .MainLoop    ;no message?
  802.     move.l    d0,A_IO    ;Do this request
  803.     move.l    IO_UNIT(A_IO),A_UNIT
  804.  
  805. ;Handle TDB_EXTCOM and dispatch command
  806.     move.w    IO_COMMAND(A_IO),d0
  807.  
  808.     IFNE    INFO_LEVEL
  809.     swap    d0
  810.     clr.w    d0
  811.     swap    d0
  812.     move.l    d0,-(sp)
  813.     PUTDEBUG    <'Command=%ld'>
  814.     addq.l    #4,sp
  815.     ENDC
  816.  
  817.     bclr    #TDB_EXTCOM,d0
  818.     beq    .NoExt
  819.     move.l    TDU_COUNTER(A_UNIT),d1
  820.     cmp.l    IOTD_COUNT(A_IO),d1
  821.     bls    .NoExt
  822. .ChgErr:    PUTDEBUG    <'Change error!'>
  823.     move.b    #TDERR_DiskChanged,IO_ERROR(A_IO)
  824.     bra    .Reply
  825. .NoExt:    move.l    #%000000111000111000011100,d1    ;specifies which commands should check for disk
  826.     btst    d0,d1
  827.     beq    .NoDiskNeeded
  828.     btst    #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
  829.     beq    .ChgErr
  830. .NoDiskNeeded:
  831.     bsr    GetDrive
  832.     lea    CmdTable(pc),a0
  833.     add.w    d0,d0
  834.     add.w    (a0,d0.w),a0
  835.     moveq    #0,d7    ;clear error flag
  836.     jsr    (a0)
  837.     bsr    FreeDrive
  838.     cmp.w    #TD_ADDCHANGEINT,IO_COMMAND(A_IO)
  839.     beq    .SkipReply
  840. .Reply:    move.l    A_IO,a1
  841.     SYS    ReplyMsg
  842. .SkipReply:
  843.  
  844. ;If at least 0.5 seconds has passed since last checking diskchange, do so
  845. ;now.
  846.     move.l    TimerIORequest+IO_DEVICE(A_DEVICE),a6
  847.     lea    TempTimeVal(A_DEVICE),a0
  848.     bsr    GetSysTime
  849.     lea    LastCheck(A_DEVICE),a1
  850.     SYS    SubTime
  851.     tst.l    TV_SECS(a0)
  852.     bne    .CheckDiskChange
  853.     cmp.l    #500000,TV_MICRO(a0)
  854.     bhs    .CheckDiskChange
  855.     lea    LastCheck(A_DEVICE),a0
  856.     bsr    GetSysTime
  857.     move.l    4,a6
  858.     bra    .NextMessage
  859.  
  860. GetSysTime:
  861.     cmp.w    #36,LIB_VERSION(a6)
  862.     bls    .OldKS
  863.     jmp    _LVOGetSysTime(a6)
  864. .OldKS:
  865. ;Compatibility routine for 1.2/1.3
  866.     movem.l    d0-d1/a0-a2/a6,-(sp)
  867.     move.l    a0,a2
  868.     lea    TimerIORequest(A_DEVICE),a1
  869.     move.l    4,a6
  870.     move.w    #TR_GETSYSTIME,IO_COMMAND(a1)
  871.     SYS    DoIO
  872.     lea    TimerIORequest(A_DEVICE),a1
  873.     move.l    IOTV_TIME+TV_SECS(a1),TV_SECS(a2)
  874.     move.l    IOTV_TIME+TV_MICRO(a1),TV_MICRO(a2)
  875.     movem.l    (sp)+,d0-d1/a0-a2/a6
  876.     rts
  877.  
  878.  
  879.     IFEQ    TYPE-DISK
  880. ChangeIntCode:
  881.     rts
  882.     ENDC
  883.  
  884. ;******************** External device routines ***********************
  885.  
  886. Open:
  887.  
  888. ;A6 - device ptr
  889. ;A1 - IORequest
  890. ;D0 - unit number
  891. ;D1 - flags (not used)
  892.  
  893. ;Important: On error, MUST put -1 in IO_DEVICE. This routine has no return
  894. ;value in d0 (return code is in IO_ERROR).
  895.  
  896.     PUTDEBUG    <'Open: Entered'>
  897.     moveq    #MD_NUMUNITS,d1
  898.     cmp.l    d1,d0
  899.     bhs    .Error
  900.     btst    d0,InquireBits(a6)
  901.     beq    .Error
  902.     lea    Unit0(a6),a0
  903.     mulu.w    #MyUnit_Sizeof,d0
  904.     add.l    d0,a0
  905.     move.l    a0,IO_UNIT(a1)
  906.     addq.w    #1,LIB_OPENCNT(a6)
  907.     clr.b    IO_ERROR(a1)    ;no error
  908.     move.b    #NT_REPLYMSG,LN_TYPE(a1)    ;Mark IORequest as "complete"
  909.     PUTDEBUG    <'Open: Done'>
  910.     rts
  911. .Error:
  912.     PUTDEBUG    <'Open: Failed'>
  913.  
  914. ;VirusX didn't like this.
  915. ;    move.b    #IOERR_OPENFAIL,IO_ERROR(a1)
  916.  
  917. ;NOTE: Trackdisk will return TDERR_BadDriveType if TDB_ALLOW_NON_3_5 flag set
  918. ;in "flags" and the drive is not present. (This is just trivia).
  919.  
  920.     move.b    #TDERR_BadUnitNum,IO_ERROR(a1)
  921.     moveq    #-1,d0
  922.     move.l    d0,IO_DEVICE(a1)
  923.     rts
  924.  
  925. _Close:
  926.  
  927. ;A6 - device ptr
  928. ;A1 - IORequest
  929.  
  930. ;Must return either 0 or, if the device wishes to be unloaded, the segment
  931. ;list.
  932.  
  933.     PUTDEBUG    <'Close: Called'>
  934.     IFEQ    TYPE-ROM
  935.     moveq    #0,d0
  936.     rts
  937.     ENDC
  938.  
  939.     IFEQ    TYPE-DISK
  940.     moveq    #0,d0
  941.     subq.w    #1,LIB_OPENCNT(a6)    ;Mark us as having one fewer openers
  942.     bne    .End    ;See if there is anyone left with us open
  943.     btst    #LIBB_DELEXP,LIB_FLAGS(a6)    ;See if we have a delayed expunge pending
  944.     beq    .End
  945.     bsr    Expunge    ;Expunge will return with SegList in D0
  946. .End:    rts
  947.     ENDC
  948.  
  949. Expunge:
  950.  
  951. ;A6 - device ptr
  952.  
  953. ;Must return either 0 or SegList ptr in D0.
  954.  
  955.     IFEQ    TYPE-ROM
  956.     moveq    #0,d0
  957.     rts
  958.     ENDC
  959.  
  960.     IFEQ    TYPE-DISK
  961.     movem.l    d2/a2/a6,-(sp)
  962.     tst.w    LIB_OPENCNT(a6)    ;See if anyone has us open
  963.     bne    .Delay
  964.     bsr    CloseEverything    ;clean up
  965.     move.l    SegList(a6),d2    ;Store our seglist in d2
  966.     move.l    a6,a1    ;Unlink from device list
  967.     move.l    a6,a2    ;save
  968.     move.l    4,a6
  969.     SYS    Remove     ;Remove first (before FreeMem)
  970.     move.l    a2,a1     ;device ptr
  971.     moveq    #0,d0
  972.     move.w    LIB_NEGSIZE(a2),d0
  973.     sub.l    d0,a1    ;Calculate base of functions
  974.     add.w    LIB_POSSIZE(a2),d0    ;Calculate size of functions + data area
  975.     SYS    FreeMem
  976.     move.l    d2,d0    ;Set up our return value
  977. .End:    movem.l    (sp)+,d2/a2/a6
  978.     rts
  979. .Delay:    bset    #LIBB_DELEXP,LIB_FLAGS(a6)    ;Set the delayed expunge flag
  980.     moveq    #0,d0
  981.     bra    .End
  982.     ENDC
  983.  
  984. BeginIO:
  985. ;A1 - IORequest
  986. ;A6 - device ptr
  987.  
  988.     movem.l    d7/a3-a6,-(sp)
  989.     move.l    a1,A_IO
  990.     move.l    IO_UNIT(A_IO),A_UNIT
  991.     move.l    a6,A_DEVICE
  992.     move.l    4,a6
  993.  
  994.     clr.b    IO_ERROR(A_IO)
  995.     move.b    #NT_MESSAGE,LN_TYPE(A_IO)    ;So WaitIO() is guaranteed to work
  996.  
  997. ;Decide whether the command is immediate or queued
  998.     move.w    IO_COMMAND(A_IO),d1
  999.     bclr    #15,d1
  1000.     cmp.w    #HighestCommand,d1
  1001.     bhi    .BadCmd
  1002.     move.l    #%111011001111000110000011,d0    ;specifies what commands are immediate
  1003.     btst    d1,d0
  1004.     beq    .Queue
  1005.     add.w    d1,d1
  1006.     lea    CmdTable(pc),a0
  1007.     add.w    (a0,d1.w),a0
  1008.     moveq    #0,d7    ;clear error flag
  1009.     jsr    (a0)
  1010. .Reply:    btst    #IOB_QUICK,IO_FLAGS(A_IO)
  1011.     bne    .End
  1012.     move.l    A_IO,a1
  1013.     SYS    ReplyMsg
  1014. .End:    movem.l    (sp)+,d7/a3-a6
  1015.     rts
  1016. .Queue:    bclr    #IOB_QUICK,IO_FLAGS(A_IO)    ;We did NOT complete this quickly
  1017.     lea    TaskPort(A_DEVICE),a0
  1018.     move.l    A_IO,a1
  1019.     SYS    PutMsg
  1020.     bra    .End
  1021. .BadCmd:    move.b    #IOERR_NOCMD,IO_ERROR(a1)
  1022.     bra    .Reply
  1023.  
  1024. AbortIO:
  1025.  
  1026. ;A6 - device ptr
  1027. ;A1 - IORequest
  1028.  
  1029.     move.b    #IOERR_ABORTED,IO_ERROR(a1)    ;We always say we succeed(ed)
  1030.     moveq    #0,d0    ;another success code
  1031.     rts
  1032.  
  1033. ;**************** Device command (IO_COMMAND) routines *************************
  1034.  
  1035. ;Note: A6 = ExecBase upon entering a command routine.
  1036. ;The low-level routines load $DFF000 into A6 when needed.
  1037.  
  1038. SaveRegs    setrl    d0-d3/d6-d7/a0-a2/a6
  1039.  
  1040. Read:
  1041.     PUTDEBUG    <'Read: Called'>
  1042.     movem.l    SaveRegs,-(sp)
  1043.     bsr    RWSetup
  1044.     tst.l    d7
  1045.     bne    FinishRW
  1046.  
  1047.     bsr    DISK_Update    ;required because of complex write scheme
  1048. ;Check for sector label nonsense
  1049.     btst    #7,IO_COMMAND(A_IO)
  1050.     beq    .NoLabel
  1051.     move.l    IOTD_SECLABEL(A_IO),d2
  1052.     bne    ReadSecLabel
  1053. .NoLabel:
  1054.  
  1055.     bsr    DISK_Read
  1056.     PUTDEBUG    <'Read: Done'>
  1057.     bra    FinishRW
  1058. Format:
  1059. Write:    movem.l    SaveRegs,-(sp)
  1060.     bsr    RWSetup
  1061.     tst.l    d7
  1062.     bne    FinishRW
  1063.  
  1064. ;Check for sector label nonsense
  1065.     tst.b    IO_COMMAND(A_IO)
  1066.     bpl    .NoLabel
  1067.     move.l    IOTD_SECLABEL(A_IO),d2
  1068.     bne    WriteSecLabel
  1069. .NoLabel:
  1070.  
  1071.     bsr    DISK_Write
  1072. FinishRW:    clr.l    IO_ACTUAL(A_IO)
  1073.     move.b    d7,IO_ERROR(A_IO)
  1074.  
  1075. ;temporary for debugging
  1076. ;    tst.b    IO_ERROR(A_IO)
  1077. ;    beq    .OK
  1078. ;    move.b    IO_ERROR(A_IO),$100
  1079. ;.OK:
  1080. ;
  1081.  
  1082.  
  1083.     bne    .Skip
  1084.     move.l    IO_LENGTH(A_IO),IO_ACTUAL(A_IO)
  1085. .Skip:    movem.l    (sp)+,SaveRegs
  1086.     rts
  1087. RWSetup:
  1088.     move.l    #512*11,d6
  1089.     move.l    #_custom,a6
  1090.     move.l    IO_LENGTH(A_IO),d0
  1091.     move.l    IO_OFFSET(A_IO),d1
  1092.     move.l    IO_DATA(A_IO),a0
  1093.     move.l    d1,d2    ;offset
  1094.     add.l    d0,d2    ;length
  1095.     cmp.l    #901120,d2
  1096.     bhi    .Error
  1097.     bsr    SelectDrive
  1098.     bra    SelectSide
  1099. .Error:    moveq    #DISK_BadParameter,d7
  1100.     rts
  1101.  
  1102. ;Here are the routines for dealing with read/write requests that involve
  1103. ;the sector label. Note that, unlike the usual read/write routines of my
  1104. ;trackdisk, the normal parameter restrictions (e.g. IO_OFFSET and IO_LENGTH
  1105. ;must be a multiple of 512) MUST be observed. And I don't check for illegal
  1106. ;parameters, either. These routines are optimized for compactness rather
  1107. ;than performance because they are rarely (if ever) used.
  1108. ;Enter with:
  1109. ;IO_LENGTH: D0
  1110. ;IO_OFFSET: D1
  1111. ;IO_DATA:   A0
  1112.  
  1113. ReadSecLabel:
  1114.     PUTDEBUG    <'READSECLABEL!'>
  1115.  
  1116.     move.l    d2,a1
  1117.     move.l    d0,d2
  1118.     move.l    #512,d0
  1119. .Read:    bsr    DISK_Read
  1120.     move.l    d1,d3
  1121.     add.l    d0,d1
  1122.     add.l    d0,a0
  1123.     divu.w    d6,d3    ;offset/tracksize
  1124.     swap    d3    ;remainder=sector# (in bytes)
  1125.     lsr.w    #5,d3    ;divide by 32 to get label offset
  1126.     lea    SectorLabels(A_DEVICE),a2
  1127.     lea    (a2,d3.w),a2    ;get pointer to label
  1128.     moveq    #3,d3
  1129. ..    move.l    (a2)+,(a1)+    ;copy label
  1130.     dbra    d3,..
  1131.     sub.l    d0,d2
  1132.     bne    .Read
  1133.     bra    FinishRW
  1134.  
  1135. WriteSecLabel:
  1136.     PUTDEBUG    <'WRITESECLABEL!'>
  1137.  
  1138.     move.l    d2,a1
  1139.     move.l    d0,d2
  1140. .Write:
  1141.     move.l    d1,d3
  1142.     divu.w    d6,d3    ;offset/tracksize
  1143.     swap    d3    ;remainder=sector# (in bytes)
  1144.     lsr.w    #5,d3    ;divide by 32 to get label offset
  1145.  
  1146. ;We check to see whether we can write a full track. (Somehow, I can't
  1147. ;ignore performance completely :-)).
  1148.     tst.w    d3
  1149.     bne    .Read
  1150.     cmp.l    d6,d0
  1151.     bhs    .FullTrack
  1152. .Read:    moveq    #0,d0
  1153.     bsr    DISK_Read    ;force a track read
  1154.     lea    SectorLabels(A_DEVICE),a2
  1155.     lea    (a2,d3.w),a2    ;get pointer to label
  1156.     moveq    #3,d3
  1157. ..    move.l    (a1)+,(a2)+    ;copy label
  1158.     dbra    d3,..
  1159.     move.l    #512,d0
  1160. .L1:    bsr    DISK_Write
  1161.     add.l    d0,d1
  1162.     add.l    d0,a0
  1163.     sub.l    d0,d2
  1164.     bne    .Write
  1165.     bra    FinishRW
  1166. .FullTrack:
  1167.     lea    SectorLabels(A_DEVICE),a2
  1168.     moveq    #((16*11)/4)-1,d3
  1169. ..    move.l    (a1)+,(a2)+    ;copy all sector labels
  1170.     dbra    d3,..
  1171.     move.l    d6,d0
  1172.     bra    .L1
  1173.  
  1174. Update:    move.l    d7,-(sp)
  1175.     moveq    #0,d7
  1176.     bsr    DISK_Update
  1177.     move.b    d7,IO_ERROR(A_IO)
  1178.     move.l    (sp)+,d7
  1179.     rts
  1180.  
  1181. Clear:
  1182. ;This routine marks the track buffer as invalid.
  1183.     st    BufferDrive(A_DEVICE)
  1184.     bclr    #DEVB_Dirty,DEV_FLAGS(A_DEVICE)
  1185.     rts
  1186.  
  1187. Motor:
  1188. ;Controls the drive motor
  1189. ;If IO_LENGTH=1, motor on
  1190. ;   IO_LENGTH=0, motor off
  1191. ;Old motor state (0=off, anything else means on) stored in IO_ACTUAL.
  1192.  
  1193.     push    d2
  1194.     clr.l    IO_ACTUAL(A_IO)
  1195.     move.b    UnitNum(A_UNIT),d2
  1196.     btst    d2,MotorState(A_DEVICE)
  1197.     beq    .WasOff
  1198.     move.l    #1,IO_ACTUAL(A_IO)
  1199. .WasOff:    tst.l    IO_LENGTH(A_IO)
  1200.     beq    .Off
  1201. ;Turn motor on
  1202.     bsr    SelectDrive
  1203.     bra    .End
  1204. .Off:
  1205. ;Turn motor off
  1206.     bclr    d2,MotorState(A_DEVICE)
  1207.     bsr    Deselect
  1208.     bset    #CIAB_DSKMOTOR,ciabprb    ;motor off
  1209.     addq.b    #3,d2
  1210.     bclr    d2,ciabprb    ;select drive X
  1211. .End:    pop    d2
  1212.     rts
  1213.  
  1214. ChangeState:
  1215. ;Returns whether there is a disk currently in the drive
  1216. ;IO_ACTUAL=0 if disk in drive
  1217. ;* EXECUTED IMMEDIATELY, POSSIBILY WITHIN AN INTERRUPT *
  1218.  
  1219.     clr.l    IO_ACTUAL(A_IO)
  1220.     btst    #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
  1221.     bne    .End
  1222.     move.l    #1,IO_ACTUAL(A_IO)    ;no disk in drive
  1223. .End:    rts
  1224.  
  1225. ChangeNum:
  1226. ;* EXECUTED IMMEDIATELY, POSSIBILY WITHIN AN INTERRUPT *
  1227.     move.l    TDU_COUNTER(A_UNIT),IO_ACTUAL(A_IO)
  1228.     rts
  1229.  
  1230. ProtStatus:
  1231. ;IO_ACTUAL=0 if disk is not write protected
  1232. ;* EXECUTED IMMEDIATELY, POSSIBILY WITHIN AN INTERRUPT *
  1233.  
  1234. ;Very important for compatibility: we must return an error (disk changed)
  1235. ;if there's no disk in the drive.
  1236.     btst    #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
  1237.     bne    .OK
  1238.     move.b    #TDERR_DiskChanged,IO_ERROR(A_IO)
  1239.     rts
  1240.  
  1241. .OK:    clr.l    IO_ACTUAL(A_IO)
  1242.     btst    #UNITB_WriteProtected,UNIT_FLAGS(A_UNIT)
  1243.     beq    .End    ;not write protected
  1244.     move.l    #1,IO_ACTUAL(A_IO)    ;no disk in drive
  1245. .End:    rts
  1246.  
  1247. GetDriveType:
  1248. ;Returns type of drive in IO_ACTUAL
  1249. ;* EXECUTED IMMEDIATELY, POSSIBILY WITHIN AN INTERRUPT *
  1250.     move.l    #DRIVE3_5,IO_ACTUAL(A_IO)
  1251.     rts
  1252.  
  1253. GetNumTracks:
  1254. ;Returns number of tracks (note: not cylinders) in IO_ACTUAL
  1255. ;* EXECUTED IMMEDIATELY, POSSIBILY WITHIN AN INTERRUPT *
  1256.     move.l    #160,IO_ACTUAL(A_IO)
  1257.     rts
  1258.  
  1259. GetGeometry:
  1260. ;Fills in DriveGeometry structure pointed to by IO_DATA
  1261.     push    a0
  1262.     move.l    IO_DATA(A_IO),a0
  1263.     move.l    #512,dg_SectorSize(a0)    ;in bytes
  1264.     move.l    #1760,dg_TotalSectors(a0)    ;total # of sectors on drive
  1265.     move.l    #80,dg_Cylinders(a0)    ;number of cylinders
  1266.     move.l    #22,dg_CylSectors(a0)    ;number of sectors/cylinder
  1267.     move.l    #2,dg_Heads(a0)    ;number of surfaces
  1268.     move.l    #11,dg_TrackSectors(a0)    ;number of sectors/track
  1269.     clr.l    dg_BufMemType(a0)    ;preferred buffer memory type
  1270.     clr.b    dg_DeviceType(a0)    ;codes as defined in the SCSI-2 spec
  1271.     move.b    #DGF_REMOVABLE,dg_Flags(a0)    ;flags, including removable
  1272.     clr.w    dg_Reserved(a0)
  1273.     pop    a0
  1274.     rts
  1275.  
  1276. MyStop:    bset    #DEVB_Stopped,DEV_FLAGS(A_DEVICE)
  1277.     bsr    Update
  1278.     bra    Clear
  1279. Start:    movem.l    d0-d1/a0-a1,-(sp)
  1280.     bclr    #DEVB_Stopped,DEV_FLAGS(A_DEVICE)
  1281.     moveq    #0,d0
  1282.     move.b    MP_SIGBIT+TaskPort(A_DEVICE),d1
  1283.     bset    d1,d0
  1284.     lea    Task(A_DEVICE),a1
  1285.     SYS    Signal
  1286.     movem.l    (sp)+,d0-d1/a0-a1
  1287.     rts
  1288.  
  1289. ;Note: This seek routine can't really be depended on by user programs,
  1290. ;because the disk.resource will corrupt the side select bit.
  1291. TDSeek:    push    d3
  1292.     bsr    SelectDriveSameMotor
  1293.     move.l    IO_OFFSET(A_IO),d3
  1294.     divu.w    #512*11,d3
  1295.     cmp.w    #159,d3
  1296.     bhi    .Error
  1297.     bsr    Seek
  1298. .End:    pop    d3
  1299.     rts
  1300. .Error:    move.b    #TDERR_NotSpecified,IO_ERROR(A_IO)
  1301.     bra    .End
  1302.  
  1303. TDRawRead:
  1304.     movem.l    d0-d1/d3/a6,-(sp)
  1305.     move.l    #_custom,a6
  1306.     moveq    #0,d1
  1307.     bsr    DoRaw
  1308.     movem.l    (sp)+,d0-d1/d3/a6
  1309.     rts
  1310.  
  1311. RawWrite:
  1312.     movem.l    d0-d1/d3/a6,-(sp)
  1313.     move.l    #_custom,a6
  1314.     btst    #UNITB_WriteProtected,UNIT_FLAGS(A_UNIT)
  1315.     bne    .WPErr
  1316.     move.w    #1<<14,d1
  1317.     bsr    DoRaw
  1318.     move.l    #3*1000,d0
  1319.     bsr    delay    ;post-write delay
  1320. .End:    movem.l    (sp)+,d0-d1/d3/a6
  1321.     rts
  1322. .WPErr:    move.b    #TDERR_WriteProt,IO_ERROR(A_IO)
  1323.     bra    .End
  1324.  
  1325. DoRaw:
  1326. ;Enter with bit 14 set in d1 according to read/write (for dsklen)
  1327. ;Uses d0-d1/d3 (not saved)
  1328. ;Assumes $dff000 in A6
  1329.  
  1330.     clr.l    IO_ACTUAL(A_IO)
  1331.     bsr    SelectDrive
  1332.     move.l    IO_OFFSET(A_IO),d3
  1333.     cmp.l    #159,d3
  1334.     bhi    .Error
  1335.     bsr    Seek
  1336.     tst.l    d7
  1337.     bne    .Error
  1338.  
  1339.     move.l    IO_DATA(A_IO),dskpt(a6)
  1340.     move.w    #ADKF_WORDSYNC,adkcon(a6)
  1341.     btst    #IOTDB_WORDSYNC,IO_FLAGS(A_IO)
  1342.     beq    .SkipSync
  1343.     move.w    #ADKF_SETCLR+ADKF_WORDSYNC,adkcon(a6)
  1344. .SkipSync:
  1345.     move.w    #ADKF_MSBSYNC,adkcon(a6)
  1346.     move.w    #ADKF_SETCLR+ADKF_FAST+ADKF_MFMPREC,adkcon(a6)
  1347.     bsr    PreComp
  1348.     move.w    #DMAF_SETCLR+DMAF_MASTER+DMAF_DISK,dmacon(a6)    ;enable disk DMA
  1349.     move.w    #$4489,dsksync(a6)    ;set magic sync word
  1350.     bsr    ClearSigs
  1351.     move.w    #INTF_SETCLR+INTF_DSKBLK,intena(a6)    ;enable int
  1352.     move.l    IO_LENGTH(A_IO),d0
  1353.  
  1354.     cmp.l    #32766,d0
  1355.     bhi    .Error
  1356.     lsr.l    #1,d0
  1357.     bset    #15,d0
  1358.     or.w    d1,d0
  1359.  
  1360.     btst    #IOTDB_INDEXSYNC,IO_FLAGS(A_IO)
  1361.     beq    .NoIndex
  1362.     move.w    d0,IndexDskLen(A_DEVICE)
  1363.     bsr    EnableIndex
  1364.     bra    .Wait
  1365. .NoIndex:
  1366.  
  1367.     move.w    d0,dsklen(a6)
  1368.     move.w    d0,dsklen(a6)
  1369. .Wait:    move.l    BlockSig(A_DEVICE),d0
  1370.     move.l    #900*1000,d1
  1371.     bsr    TimeOutWait
  1372.     beq    .NoSync
  1373.     move.w    #0,dsklen(a6)
  1374.     move.w    #INTF_DSKBLK,intena(a6)    ;disable int
  1375. .EndIO:    bsr    DisableIndex
  1376.     bsr    ClearSigs
  1377.     tst.b    IO_ERROR(A_IO)
  1378.     bne    .EndRTS
  1379.     move.l    IO_LENGTH(A_IO),IO_ACTUAL(A_IO)
  1380. .EndRTS:    rts
  1381. .Error:    move.b    #TDERR_NotSpecified,IO_ERROR(A_IO)
  1382.     rts
  1383. .NoSync:    bsr    StopDMA
  1384.     move.b    #TDERR_NoSecHdr,IO_ERROR(A_IO)
  1385.     bra    .EndIO
  1386.  
  1387.  
  1388. AddChangeInt:
  1389. ;This command uses the linkage fields of the IORequest to add a ChangeInt
  1390. ;request to a list maintained in the unit structure. This must NOT be
  1391. ;ReplyMsg'ed to avoid destroying the linkage fields - a special compare in
  1392. ;the task code handles this.
  1393.     movem.l    d0-d1/a0-a1,-(sp)
  1394.     move.l    A_IO,a1
  1395.     lea    ChangeIntList(A_UNIT),a0
  1396.     SYS    AddHead    ;could use any list add routine
  1397.     movem.l    (sp)+,d0-d1/a0-a1
  1398.     rts
  1399.  
  1400. RemChangeInt:
  1401. ;This command unlinks the AddChangeInt request from the task's port.
  1402. ;This is (and must be) an immediate command.
  1403. ;Note that because this command occurs asyncronously to the task code, the
  1404. ;task code that scans the ChangeInt list must be protected with a Forbid.
  1405.  
  1406.     movem.l    d0-d1/a0-a1,-(sp)
  1407.     SYS    Forbid
  1408.     move.l    A_IO,a1
  1409.     SYS    Remove
  1410.     SYS    Permit
  1411.     movem.l    (sp)+,d0-d1/a0-a1
  1412.     rts
  1413.  
  1414. TDRemove:
  1415. ;This command is obsolete, but is unfortuntely used by the ROM filesystem.
  1416. ;It accepts an Interrupt structure (for Cause) in IO_DATA. Only one user per
  1417. ;unit is permitted.
  1418.  
  1419.     tst.l    TDRemoveInt(A_UNIT)
  1420.     beq    .Ok
  1421.     move.b    #TDERR_DriveInUse,IO_ERROR(A_IO)
  1422.     rts
  1423. .Ok:    move.l    IO_DATA(A_IO),TDRemoveInt(A_UNIT)
  1424.     rts
  1425.  
  1426. GetDrive:
  1427. ;Obtain the use of this unit via disk.resource.
  1428.  
  1429. ;    PUTDEBUG    <'GetDrive: Called'>
  1430.     movem.l    d0-d1/a0-a2/a6,-(sp)
  1431.     lea    DiskResourceUnit(A_DEVICE),a2
  1432. .GetUnit:
  1433.     move.l    DiskResourceBase(A_DEVICE),a6
  1434.     move.l    a2,a1
  1435.     jsr    DR_GETUNIT(a6)
  1436.     tst.l    d0
  1437.     bne    .End
  1438.     lea    DiskResourcePort(A_DEVICE),a0
  1439.     move.l    4,a6
  1440.     SYS    WaitPort
  1441.     bra    .GetUnit
  1442. .End:
  1443.  
  1444.     IFEQ    TYPE-DISK
  1445.     move.l    TDUnit(A_UNIT),d0
  1446.     beq    .EndEnd
  1447.     move.l    d0,a0
  1448.     move.w    TDU_CURRTRK(a0),TDU_CURRTRK(A_UNIT)
  1449.     ENDC
  1450.  
  1451. .EndEnd:    movem.l    (sp)+,d0-d1/a0-a2/a6
  1452. ;    PUTDEBUG    <'GetDrive: Done'>
  1453.     rts
  1454.  
  1455. FreeDrive:
  1456. ;Release this unit to other users of disk.resource.
  1457.  
  1458.     movem.l    d0-d1/a0-a1/a6,-(sp)
  1459.  
  1460.     IFEQ    TYPE-DISK
  1461.     move.l    TDUnit(A_UNIT),d0
  1462.     beq    .Skip
  1463.     move.l    d0,a0
  1464.     move.w    TDU_CURRTRK(A_UNIT),TDU_CURRTRK(a0)
  1465. .Skip:
  1466.     ENDC
  1467.  
  1468.     move.l    DiskResourceBase(A_DEVICE),a6
  1469.     jsr    DR_GIVEUNIT(a6)
  1470.     movem.l    (sp)+,d0-d1/a0-a1/a6
  1471.     rts
  1472.  
  1473. ;***************************** Low-level disk code **************************
  1474.  
  1475. ;Standard register usage:
  1476. ;A6            - $dff000
  1477. ;A5 (A_DEVICE) - device ptr
  1478. ;A4 (A_UNIT)   - unit ptr
  1479. ;A3 (A_IO)     - pointer to IO request
  1480.  
  1481. ;For reading/writing:
  1482. ;D0.L - length (bytes)
  1483. ;D1.L - offset (bytes)
  1484. ;A0.L - buffer
  1485.  
  1486. ;NOTE: Error code returned in D7.L - 0 if ok, else error
  1487.  
  1488. ;General errors
  1489. DISK_BadParameter    equ    TDERR_NotSpecified
  1490.  
  1491. ;Read errors
  1492. DISK_NoSync    equ    TDERR_NoSecHdr
  1493. DISK_BadHeader    equ    TDERR_BadHdrSum
  1494. DISK_BadData    equ    TDERR_BadSecSum
  1495.  
  1496. ;Write errors
  1497. DISK_WriteProtected    equ    TDERR_WriteProt
  1498. DISK_VerifyError    equ    TDERR_NotSpecified
  1499.  
  1500.     IFND    dskpt
  1501. dskpt    equ    dskpth
  1502.     ENDC
  1503.  
  1504. ;Note: This is a public value, but it is NOT meant to be changed!!!
  1505. DISK_RawBufSize    equ    13630
  1506.  
  1507. ciabprb    equ    $bfd100
  1508. ciaapra    equ    $bfe001
  1509. ciabddrb    equ    $bfd300
  1510. ciaaddra    equ    $bfe201
  1511.  
  1512. ***************************************************************
  1513. ;Important disk parameters, summary:
  1514.  
  1515. ;Step rate: 3ms. 4ms when looking for track zero.
  1516. ;Settle time: 15ms
  1517. ;Post-write delay: 3ms (officially 1.3ms)
  1518. ;Side select delay: 2ms (officially 1.3ms)
  1519. ***************************************************************
  1520.  
  1521. DISK_Read:
  1522.     movem.l    d0-d5/a0/a1,-(sp)
  1523.  
  1524.     move.l    a0,a1    ;destination ptr in A1
  1525.     move.l    d0,d5
  1526.  
  1527. ;The meat of the read routine...
  1528.  
  1529. .ReadLoop:
  1530.     move.l    d1,d3
  1531.     divu.w    d6,d3    ;d3.w is track #
  1532.     move.l    d3,d4
  1533.     clr.w    d4
  1534.     swap    d4    ;d4.l is byte offset into track
  1535.     bsr    MinSeek
  1536.     tst.l    d4    ;any offset?
  1537.     bne    .Complex    ;yes
  1538.     cmp.l    d6,d5    ;check remaining length
  1539.     blo    .Complex    ;if not a track, forget it
  1540.     move.l    a1,d0
  1541.     btst    #0,d0    ;word aligned?
  1542.     bne    .Complex    ;no, do it the long way
  1543.     move.l    a1,a0
  1544.     bsr    ReadTrackAndDecodeNoBuffer
  1545.     tst.l    d7
  1546.     bne    .End
  1547.     sub.l    d6,d5    ;update length
  1548.     beq    .End
  1549.     add.l    d6,a1    ;update destination pointer
  1550.     add.l    d6,d1    ;update offset
  1551.     bra    .ReadLoop
  1552.  
  1553. .Complex:
  1554.     move.l    DecodedBuffer(A_DEVICE),a0
  1555.     bsr    ReadTrackAndDecodeBuffered
  1556.     tst.l    d7
  1557.     bne    .End
  1558.     add.l    d4,a0
  1559.     sub.l    d6,d4
  1560.     neg.l    d4
  1561.     add.l    d4,d1    ;update offset
  1562.  
  1563. ;D4.L - number of bytes that could be transferred from this track
  1564. ;D5.L - number of bytes left in the entire Read request
  1565.  
  1566.     cmp.l    d5,d4
  1567.     bhs    .FinishUp
  1568.     sub.l    d4,d5    ;update length
  1569.     move.l    d4,d0
  1570.     bsr    CopyMem
  1571.     add.l    d0,a1    ;update destination pointer
  1572.     bra    .ReadLoop
  1573. .FinishUp:
  1574.     move.l    d5,d0
  1575.     bsr    CopyMem    ;use number of bytes left in entire request
  1576. .End:    movem.l    (sp)+,d0-d5/a0/a1
  1577.     rts
  1578.  
  1579. SelectDrive:
  1580. ;Selects drive AND turns on motor
  1581. ;Selects UnitNum(A_UNIT).
  1582.  
  1583.     movem.l    d0/d2,-(sp)
  1584.     move.b    UnitNum(A_UNIT),d2
  1585.     bsr    Deselect
  1586.     bclr    #CIAB_DSKMOTOR,ciabprb    ;motor on
  1587.     move.l    d2,d0
  1588.     addq.b    #3,d0
  1589.     bclr    d0,ciabprb    ;select drive X
  1590.     bset    d2,MotorState(A_DEVICE)
  1591.  
  1592.     moveq    #4,d2
  1593. .Wait:    btst    #CIAB_DSKRDY,ciaapra
  1594.     beq    .End
  1595.     move.l    #100*1000,d0
  1596.     bsr    delay
  1597.     dbra    d2,.Wait    
  1598.  
  1599. .End:    movem.l    (sp)+,d0/d2
  1600.     rts
  1601.  
  1602. Deselect:    or.b    #CIAF_DSKSEL0+CIAF_DSKSEL1+CIAF_DSKSEL2+CIAF_DSKSEL3,ciabprb    ;Deselect all drives
  1603.     rts
  1604.  
  1605. SelectDriveSameMotor:
  1606. ;Select a drive WITHOUT changing the current motor state
  1607. ;Selects UnitNum(A_UNIT).
  1608.  
  1609.     push    d2
  1610.     move.b    UnitNum(A_UNIT),d2
  1611.     bsr    Deselect
  1612.     bclr    #CIAB_DSKMOTOR,ciabprb    ;motor on
  1613.     btst    d2,MotorState(A_DEVICE)
  1614.     bne    .WasOn
  1615.     bset    #CIAB_DSKMOTOR,ciabprb    ;motor off
  1616. .WasOn:    addq.b    #3,d2
  1617.     bclr    d2,ciabprb    ;select drive X
  1618.     pop    d2
  1619. _RTS:    rts
  1620.  
  1621. DISK_Update:
  1622. ;Flush track buffer if "dirty"
  1623.  
  1624.     bclr    #DEVB_Dirty,DEV_FLAGS(A_DEVICE)
  1625.     beq    _RTS
  1626.     movem.l    d0-d2/a0/a2/a6,-(sp)
  1627.     move.l    #_custom,a6
  1628.  
  1629. ;This is somewhat tricky, because the currently selected drive may not be
  1630. ;the drive that we want to write to!
  1631.     move.b    UnitNum(A_UNIT),d0
  1632.     move.w    TDU_CURRTRK(A_UNIT),d1
  1633.     move.b    BufferDrive(A_DEVICE),UnitNum(A_UNIT)    ;fake drive
  1634.     move.b    BufferTrack(A_DEVICE),TDU_CURRTRK+1(A_UNIT)    ;fake track
  1635.     bsr    SelectDrive
  1636.     bsr    SelectSide
  1637.  
  1638. ;The logic of this routine is complicated by the need to support the write
  1639. ;optimization scheme (see DISK_Write for details). What we need to do is
  1640. ;check the WriteMap to determine whether a ReadTrackAndDecode is required
  1641. ;before finally writing...
  1642.     move.l    WriteMap(A_DEVICE),d2
  1643.     and.l    #%11111111111,d2    ;changes for different drive types!
  1644.     cmp.l    #%11111111111,d2    ;changes for different drive types!
  1645.     beq    .SkipRead
  1646.     move.l    DecodedBuffer(A_DEVICE),a0
  1647.     bsr    ReadTrackAndDecode
  1648.     tst.l    d7
  1649.     bne    .End
  1650. .SkipRead:
  1651.     move.l    d7,WriteMap(A_DEVICE)
  1652.  
  1653.     move.l    DecodedBuffer(A_DEVICE),a2
  1654.     bsr    EncodeAndWriteTrack
  1655.     move.b    d0,UnitNum(A_UNIT)
  1656.     move.w    d1,TDU_CURRTRK(A_UNIT)
  1657.     bsr    SelectDrive
  1658. .End:    movem.l    (sp)+,d0-d2/a0/a2/a6
  1659.     rts
  1660.  
  1661. SelectSide:
  1662. ;Select the correct side based on current track. This routine MUST be
  1663. ;called by certain routines (e.g. Update, Write) because the side select is
  1664. ;lost after a GiveUnit!
  1665.     bclr    #CIAB_DSKSIDE,ciabprb    ;set to upper
  1666.     btst    #0,TDU_CURRTRK+1(A_UNIT)
  1667.     bne    .skip
  1668.     bset    #CIAB_DSKSIDE,ciabprb    ;set to lower
  1669. .skip:    push    d0
  1670.     move.l    #2*1000,d0
  1671.     bsr    delay
  1672.     pop    d0
  1673.     rts
  1674.  
  1675. ;Enter with destination track in d3.b
  1676.  
  1677. ;Note: This routine checks for valid track numbers -- if either the
  1678. ;current track number or the destination is invalid, PANIC.
  1679.  
  1680. SaveRegs    setrl    d0/d3-d4
  1681.  
  1682. SeekNoUpdate:
  1683. ;Perform a seek but don't call DISK_Update -- used by main routine when
  1684. ;checking for disk changes.
  1685.  
  1686.     movem.l    SaveRegs,-(sp)
  1687.     bra    SeekAfterUpdate
  1688.  
  1689. MinSeek:
  1690. ;Only call MinSeek if it is certain that the side select has not been
  1691. ;changed!
  1692.     cmp.b    TDU_CURRTRK+1(A_UNIT),d3
  1693.     bne    Seek
  1694.     move.w    d0,-(sp)
  1695.     move.b    BufferDrive(A_DEVICE),d0
  1696.     cmp.b    UnitNum(A_UNIT),d0
  1697.     bne    .Seek
  1698.     move.w    (sp)+,d0
  1699.     rts
  1700. .Seek:    move.w    (sp)+,d0
  1701.  
  1702. Seek:    movem.l    SaveRegs,-(sp)
  1703.  
  1704.     bsr    DISK_Update
  1705. SeekAfterUpdate:
  1706.     tst.l    d7
  1707.     bne    .End
  1708.  
  1709.     move.l    TDU_STEPDELAY(A_UNIT),d0
  1710.     cmp.b    #160,d3
  1711.     bhs    .Error
  1712.  
  1713. ;Set head
  1714.     bclr    #CIAB_DSKSIDE,ciabprb    ;set to upper
  1715.     btst    #0,d3
  1716.     bne    .skip
  1717.     bset    #CIAB_DSKSIDE,ciabprb    ;set to lower
  1718. .skip:
  1719.     move.w    TDU_CURRTRK(A_UNIT),d4
  1720.     lsr.b    #1,d4
  1721.     cmp.b    #80,d4
  1722.     bhs    .Error
  1723.     move.b    d3,TDU_CURRTRK+1(A_UNIT)
  1724.     lsr.b    #1,d3
  1725.     bset    #CIAB_DSKDIREC,ciabprb    ;set to "out" (lower tracks)
  1726.     sub.b    d3,d4
  1727.     beq    .SideSelectOnly
  1728.     bhi    .StepIn    ;Go if DISK_CurrentTrack > Destination
  1729.     bclr    #CIAB_DSKDIREC,ciabprb    ;set to "in" (higher tracks)
  1730.     neg.b    d4
  1731. .StepIn:
  1732.     bsr    SelectDriveSameMotor
  1733.     bset    #CIAB_DSKSTEP,ciabprb
  1734.     bclr    #CIAB_DSKSTEP,ciabprb    ;step head
  1735.     bset    #CIAB_DSKSTEP,ciabprb
  1736.     bsr    Deselect
  1737.     bsr    delay
  1738.     subq.b    #1,d4
  1739.     bne    .StepIn
  1740.     move.l    TDU_SETTLEDELAY(A_UNIT),d0
  1741.     bsr    delay
  1742.     bsr    SelectDriveSameMotor
  1743. .End:    movem.l    (sp)+,SaveRegs
  1744.     rts
  1745. .SideSelectOnly:
  1746.     move.l    #2*1000,d0
  1747.     bsr    delay
  1748.     bra    .End
  1749. .Error:
  1750. ;    move.w    #$f00,$dff180
  1751.     bra    .Error
  1752.  
  1753. ;*************************** Delay code ****************************
  1754.  
  1755. delay:
  1756.  
  1757. ;Enter with microseconds in D0.L
  1758.  
  1759.     movem.l    d0-d1/a0-a1/a6,-(sp)
  1760.     lea    TimerIORequest(A_DEVICE),a1
  1761.     move.w    #TR_ADDREQUEST,IO_COMMAND(a1)
  1762.     clr.l    TV_SECS+IO_SIZE(a1)
  1763.     move.l    d0,TV_MICRO+IO_SIZE(a1)
  1764.     move.l    4,a6
  1765.     SYS    DoIO
  1766.  
  1767. ;Clear signal bit (TimeOutWait depends on the signal bit being 'correct').
  1768.     lea    TimerIORequest(A_DEVICE),a1
  1769.     move.l    MN_REPLYPORT(a1),a0
  1770.     move.b    MP_SIGBIT(a0),d0
  1771.     moveq    #0,d1
  1772.     bset    d0,d1
  1773.     moveq    #0,d0
  1774.     SYS    SetSignal
  1775.     movem.l    (sp)+,d0-d1/a0-a1/a6
  1776.     rts
  1777.  
  1778. TimeOutWait:
  1779.  
  1780. ;Wait for signal mask in D0, but include a time-out specified in D1 (usecs).
  1781. ;Returns wait mask in D0, or 0 if a time-out occured (Z flag will be set).
  1782.  
  1783.     movem.l    d1-d4/a0-a2/a6,-(sp)
  1784.     move.l    d0,d3
  1785.     move.l    4,a6
  1786.     lea    TimerIORequest(A_DEVICE),a1
  1787.     move.l    a1,a2
  1788.     move.w    #TR_ADDREQUEST,IO_COMMAND(a1)
  1789.     clr.l    TV_SECS+IO_SIZE(a1)
  1790.     move.l    d1,TV_MICRO+IO_SIZE(a1)
  1791.     move.l    MN_REPLYPORT(a1),a0
  1792.     moveq    #0,d2
  1793.     move.b    MP_SIGBIT(a0),d1
  1794.     bset    d1,d2
  1795.     SYS    SendIO
  1796.     move.l    d3,d0
  1797.     add.l    d2,d0    ;equivilent to OR in this case
  1798.     SYS    Wait
  1799.     and.l    d3,d0    ;exclude timer signal
  1800.     move.l    d0,d4
  1801.     beq    .TimeOut    ;if zero, timer signal was the only signal
  1802.     move.l    a2,a1
  1803.     SYS    AbortIO
  1804. .TimeOut:    move.l    a2,a1
  1805.     SYS    WaitIO
  1806.     moveq    #0,d0    ;value
  1807.     move.l    d2,d1    ;mask (timer sig)
  1808.     SYS    SetSignal    ;make SURE the timer signal is clear
  1809.     move.l    d4,d0
  1810.     movem.l    (sp)+,d1-d4/a0-a2/a6
  1811.     rts
  1812.  
  1813. ;*************************** Interrupt routines ************************
  1814.  
  1815. ;------------------------------------------
  1816. ;Register contents upon entering a handler:
  1817. ;D1 - (INTENAR) AND (INTREQR)
  1818. ;A0 - $dff000
  1819. ;A1 - data ptr (device ptr in this case)
  1820. ;A6 - ExecBase 
  1821.  
  1822. ;d0, d1, a0, a1, a5, and a6 are scratch.
  1823. ;-----------------------------------------
  1824.  
  1825. ;However, the disk.resource autodoc indicates the following arrangement for
  1826. ;interrupts installed by GetUnit:
  1827.  
  1828. ;D0/D1/A0/A1 are scratch
  1829. ;A1 points to IS_DATA (device pointer in this case).
  1830. ;Make no other assumptions!
  1831.  
  1832. SyncInt:    addq.w    #1,SyncCount(a1)
  1833.     move.l    SyncSig(a1),d0
  1834.     lea    Task(a1),a1
  1835.     push    a6
  1836.     move.l    4,a6
  1837.     SYS    Signal
  1838.     pop    a6
  1839.  
  1840. ;Delay approximately 63us to avoid two sync interrupts
  1841. ;This could be improved.
  1842.     moveq    #1,d0
  1843. .L1:    move.b    vhposr+_custom,d1
  1844. .L2:    cmp.b    vhposr+_custom,d1
  1845.     beq    .L2
  1846.     dbra    d0,.L1
  1847.     move.w    #INTF_DSKSYNC,intreq+_custom    ;clear sync
  1848.     rts    
  1849.  
  1850. BlockInt:
  1851.     push    a6
  1852.     move.l    #_custom,a6
  1853.     move.w    #INTF_DSKBLK,intreq(a6)
  1854.  
  1855. ;Test DEVB_Verify flag. If set, initiate a read into the verify buffer.
  1856.     bclr    #DEVB_Verify,DEV_FLAGS(a1)
  1857.     beq    .NoVerify
  1858.  
  1859.     move.w    #INTF_SETCLR+INTF_DSKSYNC,intena(a6)    ;enable sync int
  1860.     move.l    VerifyBuffer(a1),dskpt(a6)
  1861.     move.w    #ADKF_SETCLR+ADKF_WORDSYNC,adkcon(a6)
  1862.     move.w    #0,dsklen(a6)
  1863.     move.w    #$9761,dsklen(a6)
  1864.     move.w    #$9761,dsklen(a6)
  1865.     move.w    #$4489,dsksync(a6)    ;set magic sync word
  1866. ;Now the first sync interrupt we get will be known to be valid, because the
  1867. ;DMA read is fully started.
  1868.     bra    .End
  1869.  
  1870. .NoVerify:
  1871.     move.l    BlockSig(a1),d0
  1872.     lea    Task(a1),a1
  1873.     move.l    4,a6
  1874.     SYS    Signal
  1875. .End:    pop    a6
  1876.     rts
  1877.  
  1878. IndexInt:
  1879.     move.w    IndexDskLen(a1),d0
  1880.     beq    .End
  1881.     move.w    d0,dsklen+_custom
  1882.     move.w    d0,dsklen+_custom
  1883.     clr.w    IndexDskLen(a1)
  1884. .End:    rts
  1885.  
  1886. EnableIndex:
  1887.     movem.l    d0-d1/a0-a1/a6,-(sp)
  1888.     move.l    CIABase(A_DEVICE),a6
  1889.     moveq    #16,d0
  1890.     SYS    SetICR    ;clear interrupt
  1891.     move.l    #16+128,d0
  1892.     SYS    AbleICR    ;enable interrupt
  1893.     movem.l    (sp)+,d0-d1/a0-a1/a6
  1894.     rts
  1895. DisableIndex:
  1896.     movem.l    d0-d1/a0-a1/a6,-(sp)
  1897.     move.l    CIABase(A_DEVICE),a6
  1898.     moveq    #16,d0
  1899.     SYS    AbleICR    ;disable interrupt
  1900.     moveq    #16,d0
  1901.     SYS    SetICR    ;clear interrupt
  1902.     movem.l    (sp)+,d0-d1/a0-a1/a6
  1903.     rts
  1904.  
  1905. ;*********************** End interrupt routines ************************
  1906.  
  1907. ClearSigs:
  1908.     movem.l    d0-d1/a0-a1/a6,-(sp)
  1909.     move.w    #INTF_DSKBLK+INTF_DSKSYNC,intreq+_custom    ;clear sync+done
  1910.     clr.w    SyncCount(A_DEVICE)
  1911.     clr.w    IndexDskLen(A_DEVICE)
  1912.     move.l    4,a6
  1913.     moveq    #0,d0    ;value
  1914.     move.l    SyncSig(A_DEVICE),d1
  1915.     add.l    BlockSig(A_DEVICE),d1
  1916.     SYS    SetSignal
  1917.     movem.l    (sp)+,d0-d1/a0-a1/a6
  1918.     rts
  1919.  
  1920. SetRegs:
  1921. ;This routine enables the block interrupt, but leaves sync interrupts
  1922. ;disabled.
  1923.  
  1924.     move.l    RawBuffer(A_DEVICE),dskpt(a6)
  1925.     move.w    #ADKF_MSBSYNC+ADKF_PRECOMP0+ADKF_PRECOMP1,adkcon(a6)
  1926.     move.w    #ADKF_SETCLR+ADKF_FAST+ADKF_WORDSYNC+ADKF_MFMPREC,adkcon(a6)
  1927.     move.w    #DMAF_SETCLR+DMAF_MASTER+DMAF_DISK,dmacon(a6)    ;enable disk DMA
  1928.     bsr    ClearSigs
  1929.     move.w    #INTF_SETCLR+INTF_DSKBLK,intena(a6)    ;enable block int
  1930.     rts
  1931.  
  1932. PreComp:
  1933. ;Enter with $dff000 in A6
  1934.     movem.l    d0-d1,-(sp)
  1935.     move.w    #ADKF_PRECOMP0+ADKF_PRECOMP1,adkcon(a6)    ;no precomp
  1936.     move.w    TDU_CURRTRK(A_UNIT),d0
  1937.     cmp.w    TDU_COMP01TRACK(A_UNIT),d0
  1938.     bls    .End
  1939.     move.w    #ADKF_SETCLR+ADKF_PRECOMP0,d1
  1940.     cmp.w    TDU_COMP10TRACK(A_UNIT),d0
  1941.     bls    .Done
  1942.     move.w    #ADKF_SETCLR+ADKF_PRECOMP1,d1
  1943.     cmp.w    TDU_COMP11TRACK(A_UNIT),d0
  1944.     bls    .Done
  1945.     move.w    #ADKF_SETCLR+ADKF_PRECOMP0+ADKF_PRECOMP1,d1
  1946. .Done:    move.w    d1,adkcon(a6)
  1947. .End:    movem.l    (sp)+,d0-d1
  1948.     rts
  1949.  
  1950. ScanSync:
  1951. ;Scan for sync mark
  1952. ;A2 - pointer to raw data -- updated
  1953. ;Error code in D7
  1954.  
  1955.     bsr    WaitWordSync
  1956.     tst.l    d7
  1957.     bne    .End
  1958.  
  1959. ;This routine is trickier than it appears. The trick is that we must NOT
  1960. ;assume a $4489 at the beginning of our buffer. This phenomenon occurs when
  1961. ;the DMA starts in the middle of the first sync word. The second sync word
  1962. ;is thrown away by the hardware. It sounds exotic, but it actually happens
  1963. ;quite often!
  1964.  
  1965.     push    a4
  1966.     move.l    RawBuffer(A_DEVICE),a4
  1967.     cmp.l    a4,a2
  1968.     lea    DISK_RawBufSize(a4),a4    ;does not affect flags
  1969.     beq    .found    ;if start of buffer, don't scan for sync!!!
  1970.  
  1971. .Sync:    cmpi.w    #$4489,(a2)+
  1972.     beq    .found
  1973.     cmp.l    a2,a4
  1974.     bhi    .Sync
  1975. .Error:    pop    a4
  1976.     moveq    #DISK_NoSync,d7
  1977.     rts
  1978. .found:    cmpi.w    #$4489,(a2)
  1979.     bne    .ok
  1980.     addq.l    #2,a2
  1981.     cmp.l    a2,a4
  1982.     bhi    .found
  1983.     bra    .Error
  1984. .ok:    pop    a4
  1985. .End:    rts
  1986.  
  1987. StopDMA:    move.w    #0,dsklen(a6)
  1988.     move.w    #1<<15,dsklen(a6)
  1989.     move.w    #1<<15,dsklen(a6)    ;zero-length DMA transfer
  1990.     move.w    #INTF_DSKBLK+INTF_DSKSYNC,intena(a6)    ;disable ints
  1991.     bra    ClearSigs
  1992.  
  1993. WaitWordSync:
  1994. ;Wait for a sync mark or disk block done
  1995. ;Will return an error in 300ms if nothing happens.
  1996.  
  1997.     movem.l    d0-d1,-(sp)
  1998. .Wait:    tst.w    SyncCount(A_DEVICE)
  1999.     bne    .Sync
  2000.     move.l    SyncSig(A_DEVICE),d0
  2001.     add.l    BlockSig(A_DEVICE),d0
  2002.     move.l    #300*1000,d1    ;time-out (300ms)
  2003.     bsr    TimeOutWait
  2004.     beq    .Error
  2005.     and.l    BlockSig(A_DEVICE),d0
  2006.     bne    .Done
  2007.     bra    .Wait
  2008. .Sync:    subq.w    #1,SyncCount(A_DEVICE)
  2009. .End:    movem.l    (sp)+,d0-d1
  2010.     rts
  2011. .Done:    move.w    #$C000,SyncCount(A_DEVICE)    ;this is obscure - should change
  2012.     bra    .End
  2013. .Error:    moveq    #DISK_NoSync,d7
  2014.     bra    .End
  2015.  
  2016. ReadTrackAndDecodeBuffered:
  2017.     push    d0
  2018.     move.l    WriteMap(A_DEVICE),d0
  2019.     beq    .OK    ;no optimized writes to worry about
  2020.     and.l    #%11111111111,d0    ;changes for different drive types!
  2021.     cmp.l    #%11111111111,d0    ;changes for different drive types!
  2022.     beq    .End
  2023.     moveq    #-1,d0
  2024.     move.l    d0,WriteMap(A_DEVICE)    ;mark sectors as filled
  2025.     bra    .ReadTrack
  2026.  
  2027. .OK:    move.b    UnitNum(A_UNIT),d0
  2028.     cmp.b    BufferDrive(A_DEVICE),d0
  2029.     bne    .ReadTrack
  2030.     move.w    TDU_CURRTRK(A_UNIT),d0
  2031.     cmp.b    BufferTrack(A_DEVICE),d0
  2032.     bne    .ReadTrack
  2033. .End:    pop    d0
  2034.     rts
  2035. .ReadTrack:
  2036.     pop    d0
  2037.  
  2038. ReadTrackAndDecode:
  2039.  
  2040. ;Input: Buffer ptr in A0
  2041. ;Returns error code in D7
  2042.  
  2043.     move.b    UnitNum(A_UNIT),BufferDrive(A_DEVICE)
  2044.     move.b    TDU_CURRTRK+1(A_UNIT),BufferTrack(A_DEVICE)
  2045.  
  2046. ReadTrackAndDecodeNoBuffer:
  2047.     move.w    d0,-(sp)
  2048.     move.b    TDU_RETRYCNT(A_UNIT),d0
  2049. .Retry:    moveq    #0,d7
  2050.     bsr    ReadTrackAndDecodeNoRetry
  2051.     tst.l    d7
  2052.     beq    .End
  2053.     subq.b    #1,d0
  2054.     bpl    .Retry
  2055. ;Could not recover from error - clear the buffer
  2056.     bsr    Clear
  2057.  
  2058. .End:    move.w    (sp)+,d0
  2059.     rts
  2060.  
  2061. ReadTrackAndDecodeNoRetry:
  2062.     movem.l    d0-d6/a0-a2,-(sp)
  2063.  
  2064. ;Initiate a raw track read
  2065. ;Because we are using WORDSYNC interrupts, this code is VERY VERY tricky!!!!!
  2066.  
  2067.     bsr    SetRegs
  2068.     move.w    #$0123,dsksync(a6)    ;set invalid sync word
  2069. ;At this point sync interrupts will stop happenning, because $0000 will
  2070. ;never occur. So, we clear the interrupt and enable it.
  2071.     bsr    ClearSigs
  2072.     move.w    #INTF_SETCLR+INTF_DSKSYNC,intena(a6)    ;enable sync int
  2073.     move.w    #0,dsklen(a6)
  2074.     move.w    #$9A9E,dsklen(a6)
  2075.     move.w    #$9A9E,dsklen(a6)    ;read approx. 13,628 bytes
  2076.     move.w    #$4489,dsksync(a6)    ;set magic sync word
  2077.  
  2078. ;Now the first sync interrupt we get will be known to be valid, because the
  2079. ;DMA read is fully started.
  2080.  
  2081. ;Decode track
  2082.     move.l    RawBuffer(A_DEVICE),a2
  2083.  
  2084.     moveq    #11-1,d3    ;# of sectors
  2085.     move.l    #$55555555,d2
  2086.  
  2087. ;Wait for first sync marks...
  2088.     bsr    WaitWordSync
  2089.     tst.l    d7
  2090.     bne    .End
  2091.  
  2092. .SecLoop:
  2093.     bsr    ScanSync
  2094.     tst.l    d7
  2095.     bne    .End
  2096.  
  2097.     bsr    DecodeLong    ;get header
  2098.     subq.l    #8,a2
  2099.  
  2100.     and.w    #$ff00,d0    ;mask off sector number
  2101.     move.l    d0,d1
  2102.     cmp.w    #$0a00,d1
  2103.     bhi    .HeaderError
  2104.     add.w    d1,d1    ;convert to sector offset
  2105.     lsr.w    #4,d0    ;get sector label offset
  2106.     lea    SectorLabels(A_DEVICE),a1
  2107.     lea    (a1,d0.w),a1
  2108.  
  2109. ;This code supports the write optimization...
  2110.     lsr.w    #4,d0    ;sector number
  2111.     move.l    WriteMap(A_DEVICE),d4
  2112.     btst    d0,d4    ;should we avoid reading this sector?
  2113.     bne    .EndLoop    ;yes, skip to next sector
  2114.  
  2115.     move.l    d1,-(sp)
  2116.     moveq    #0,d5
  2117.     move.l    (a2)+,d0
  2118.     eor.l    d0,d5
  2119.     move.l    (a2)+,d0
  2120.     eor.l    d0,d5
  2121. ;Decode and checksum sector label
  2122.     moveq    #3,d6
  2123. .Label:    move.l    16(a2),d4
  2124.     eor.l    d4,d5
  2125.     and.l    d2,d4
  2126.     move.l    (a2)+,d1
  2127.     eor.l    d1,d5
  2128.     and.l    d2,d1
  2129.     add.l    d1,d1
  2130.     or.l    d1,d4
  2131.     move.l    d4,(a1)+
  2132.     dbra    d6,.Label
  2133.     and.l    d2,d5
  2134.     lea    16(a2),a2    ;point at header checksum
  2135.     move.l    (sp)+,d1
  2136.     bsr    DecodeLong    ;header checksum
  2137.     cmp.l    d0,d5
  2138.     bne    .HeaderError
  2139.  
  2140. ;Verify track position
  2141.     swap    d1
  2142.     cmp.b    TDU_CURRTRK+1(A_UNIT),d1
  2143.     bne    .WrongTrack
  2144.     swap    d1
  2145.  
  2146.     bsr    DecodeLong    ;data area checksum
  2147.     lea    (a0,d1.w),a1    ;compute destination
  2148.  
  2149. ;Decode and checksum data block
  2150.     moveq    #127,d6
  2151.     moveq    #0,d5
  2152. .L1:    move.l    512(a2),d4
  2153.     eor.l    d4,d5
  2154.     and.l    d2,d4
  2155.     move.l    (a2)+,d1
  2156.     eor.l    d1,d5
  2157.     and.l    d2,d1
  2158.     add.l    d1,d1
  2159.     or.l    d1,d4
  2160.     move.l    d4,(a1)+
  2161.     dbra    d6,.L1
  2162.     and.l    d2,d5
  2163.     lea    512(a2),a2
  2164.  
  2165.     cmp.l    d0,d5    ;check data area checksum
  2166.     bne    .DataError
  2167. .EndLoop:    dbra    d3,.SecLoop
  2168.  
  2169. .End:    bsr    StopDMA
  2170.     movem.l    (sp)+,d0-d6/a0-a2
  2171.     rts
  2172. .HeaderError:
  2173.     moveq    #DISK_BadHeader,d7
  2174.     bra    .End
  2175. .DataError:
  2176.     moveq    #DISK_BadData,d7
  2177.     bra    .End
  2178. .WrongTrack:
  2179.     moveq    #TDERR_SeekError,d7
  2180.     move.w    TDU_CURRTRK(A_UNIT),d3
  2181.     bsr    SeekZero
  2182.     bsr    SelectDrive
  2183.     bsr    SeekNoUpdate
  2184.     bra    .End
  2185.  
  2186. DecodeLong:
  2187. ;A2 - ptr to buffer -- updated
  2188. ;D2 - $55555555
  2189. ;D0 - result
  2190.  
  2191.     move.l    d1,-(sp)
  2192.     move.l    (a2)+,d0
  2193.     move.l    (a2)+,d1
  2194.     and.l    d2,d0
  2195.     and.l    d2,d1
  2196.     add.l    d0,d0    ;was lsl.l #1,d0
  2197.     or.l    d1,d0
  2198.     move.l    (sp)+,d1
  2199.     rts
  2200.  
  2201. EncodeLong:
  2202. ;Enter with data to be encoded in D0.L
  2203. ;and pointer to destination in A0 -- updated
  2204. ;Exit with checksum in D5
  2205.  
  2206.     movem.l    d0-d4,-(sp)
  2207.     moveq    #0,d5
  2208.     move.l    #$55555555,d4
  2209.     move.l    d0,d3
  2210.     lsr.l    #1,d0
  2211.     bsr    Encode
  2212.     move.l    d3,d0
  2213.     bsr    Encode
  2214.     and.l    #$55555555,d5
  2215.     movem.l    (sp)+,d0-d4
  2216.     rts
  2217.  
  2218. Encode:
  2219. ;Enter with longword to code in D0.L and #$55555555 in D4
  2220. ;uses d0,d1,d2,a0 -- not saved
  2221.  
  2222. ;Accumulates checksum in D5
  2223.  
  2224.     and.l    d4,d0
  2225.     move.l    d0,d2
  2226.     eor.l    d4,d2
  2227.     move.l    d2,d1
  2228.     add.l    d2,d2
  2229.     lsr.l    #1,d1
  2230.     bset    #31,d1
  2231.     and.l    d2,d1
  2232.     or.l    d1,d0
  2233.     btst    #0,-1(a0)
  2234.     beq    .ok
  2235.     bclr    #31,d0
  2236. .ok:    eor.l    d0,d5
  2237.     move.l    d0,(a0)+
  2238.     rts
  2239.  
  2240. EncodeBlock:
  2241. ;Destination is always chip RAM (RawBuffer).
  2242. ;Source could be in chip RAM or fast RAM (in A2).
  2243.  
  2244.     movem.l    d0-d1/a0-a1/a6,-(sp)
  2245.     move.l    4,a6
  2246.     move.l    a2,a1
  2247.     SYS    TypeOfMem
  2248.     and.l    #MEMF_CHIP,d0
  2249.     bne    .Chip
  2250.     movem.l    (sp)+,d0-d1/a0-a1/a6
  2251.     bra    EncodeBlockCPU
  2252. .Chip:    movem.l    (sp)+,d0-d1/a0-a1/a6
  2253.     bra    EncodeBlockBlit
  2254.  
  2255. EncodeBlockCPU:
  2256. ;Enter with pointer to source data in A2 -- updated
  2257. ;Enter with pointer to destination in A0 -- updated
  2258.  
  2259. ;Exit with checksum in D5
  2260.     move.l    d6,-(sp)
  2261.     moveq    #0,d5
  2262.     move.w    #(512/4)-1,d6
  2263.  
  2264. EncodeBlockSub:
  2265. ;Number of longwords to encode (minus one) in D6.w
  2266.     movem.l    d0-d4,-(sp)
  2267.  
  2268. ;Encode odd bits
  2269.     push    a2
  2270.     move.w    d6,d3
  2271.     move.l    #$55555555,d4
  2272. .L1:    move.l    (a2)+,d0
  2273.     lsr.l    #1,d0
  2274.     bsr    Encode
  2275.     dbra    d3,.L1
  2276.  
  2277. ;Encode even bits
  2278.     pop    a2
  2279.     move.w    d6,d3
  2280. .L2:    move.l    (a2)+,d0
  2281.     bsr    Encode
  2282.     dbra    d3,.L2
  2283.     and.l    #$55555555,d5
  2284.     movem.l    (sp)+,d0-d4
  2285.     move.l    (sp)+,d6
  2286.     rts
  2287.  
  2288. EncodeSectorLabels:
  2289. ;D5 (checksum) must be initialized by caller
  2290.     move.l    d6,-(sp)
  2291.     move.w    #(16/4)-1,d6
  2292.     bra    EncodeBlockSub
  2293.  
  2294. EncodeBlockBlit:
  2295. ;Enter with pointer to source data in A2 -- updated
  2296. ;Enter with pointer to destination in A0 -- updated
  2297.  
  2298. ;Exit with checksum in D5
  2299.  
  2300.     movem.l    d0-d2/a0-a1/a6,-(sp)
  2301.     push    a0
  2302.     move.l    GraphBase(A_DEVICE),a6
  2303.     SYS    OwnBlitter
  2304.     move.l    (sp),a0
  2305.     move.l    #_custom,a1
  2306.  
  2307.     move.w    #$808,d0    ;BLTSIZE
  2308.  
  2309.     SYS    WaitBlit
  2310.  
  2311.     move.w    #$ffff,bltafwm(a1)
  2312.     move.w    #$ffff,bltalwm(a1)
  2313.     clr.w    bltbmod(a1)
  2314.     clr.w    bltamod(a1)
  2315.     clr.w    bltdmod(a1)
  2316.     move.w    #$5555,bltcdat(a1)
  2317.  
  2318.     move.l    a2,bltbpt(a1)
  2319.     move.l    a2,bltapt(a1)
  2320.     move.l    a0,bltdpt(a1)
  2321.     move.w    #$1db1,bltcon0(a1)
  2322.     clr.w    bltcon1(a1)
  2323.     move.w    d0,bltsize(a1)
  2324.  
  2325.     SYS    WaitBlit
  2326.  
  2327.     move.l    a0,bltbpt(a1)
  2328.     move.l    a2,bltapt(a1)
  2329.     move.l    a0,bltdpt(a1)
  2330.     move.w    #$2d8c,bltcon0(a1)
  2331.     move.w    d0,bltsize(a1)
  2332.     movem.l    a0/a2,-(sp)
  2333.     lea    510(a2),a2    ;ptr to end of src
  2334.     lea    1022(a0),a0
  2335.  
  2336.     SYS    WaitBlit
  2337.  
  2338.     move.l    a2,bltbpt(a1)    ;src end
  2339.     move.l    a2,bltapt(a1)    ;src end
  2340.     move.l    a0,bltdpt(a1)    ;dst end
  2341.     move.w    #$0db1,bltcon0(a1)
  2342.     move.w    #$1002,bltcon1(a1)    ;decrement
  2343.     move.w    d0,bltsize(a1)
  2344.     movem.l    (sp)+,a0/a2
  2345.     lea    512(a0),a0
  2346.  
  2347.     SYS    WaitBlit
  2348.  
  2349.     move.l    a0,bltbpt(a1)
  2350.     move.l    a2,bltapt(a1)
  2351.     move.l    a0,bltdpt(a1)
  2352.     move.w    #$1d8c,bltcon0(a1)
  2353.     clr.w    bltcon1(a1)
  2354.     move.w    d0,bltsize(a1)
  2355.     pop    a0
  2356.  
  2357.     SYS    WaitBlit
  2358.  
  2359.     bsr    Correct
  2360.     lea    512(a0),a0
  2361.     bsr    Correct
  2362.     lea    -512(a0),a0
  2363.  
  2364.     move.w    #(1024/4)-1,d0
  2365.     move.l    #$55555555,d2
  2366.     moveq    #0,d5
  2367. ..    move.l    (a0)+,d1
  2368.     eor.l    d1,d5
  2369.     dbra    d0,..
  2370.     and.l    d2,d5
  2371.  
  2372.     SYS    DisownBlitter
  2373.     movem.l    (sp)+,d0-d2/a0-a1/a6
  2374.     lea    512(a2),a2    ;update source pointer
  2375.     lea    1024(a0),a0    ;update destination pointer
  2376.     rts
  2377.  
  2378. ;This routine corrects the high bit of the current byte based on the
  2379. ;low bit of the previous byte.
  2380.  
  2381. Correct:
  2382.     push    d0
  2383.     move.b    (a0),d0
  2384.     btst    #0,-1(a0)
  2385.     bne    .ResetClock
  2386.     btst    #6,d0
  2387.     bne    .end
  2388.     bset    #7,d0
  2389.     bra    .end1
  2390. .ResetClock:
  2391.     bclr    #7,d0
  2392. .end1:    move.b    d0,(a0)
  2393. .end:    pop    d0
  2394.     rts
  2395.  
  2396. DISK_Wait:
  2397. ;Assumes $dff000 in A6.
  2398.     movem.l    d0-d1,-(sp)
  2399.     tst.w    SyncCount(A_DEVICE)
  2400.     bmi    .OK    ;if WaitWordSync detected a BlockSig, don't wait!
  2401.     move.l    BlockSig(A_DEVICE),d0
  2402.     move.l    #300*1000,d1
  2403.     bsr    TimeOutWait
  2404.     bne    .OK
  2405.     moveq    #DISK_NoSync,d7
  2406. .OK:    move.w    #INTF_DSKBLK+INTF_DSKSYNC,intena(a6)    ;disable ints
  2407.     bsr    ClearSigs
  2408.     movem.l    (sp)+,d0-d1
  2409.     rts
  2410.  
  2411. EncodeAndWriteTrack:
  2412. ;Enter with pointer to source data in A2
  2413.  
  2414.     movem.l    d0-d6/a0-a2,-(sp)
  2415.  
  2416.     btst    #CIAB_DSKPROT,ciaapra    ;check write protect status
  2417.     beq    .Protected
  2418.  
  2419.     move.l    RawBuffer(A_DEVICE),a0
  2420.  
  2421. ;Gap = 1660 bytes - 2 bytes for hardware bug
  2422.     move.l    #$aaaaaaaa,d1    ;10101010...
  2423.     move.w    #414,d0
  2424. ..    move.l    d1,(a0)+
  2425.     dbra    d0,..
  2426.     subq.l    #2,a0    ;leave room for 2 extra bytes at the very end
  2427.  
  2428.     moveq    #11,d1    ;number of sectors
  2429.     moveq    #0,d3    ;sector count
  2430. .SecLoop:
  2431.     move.l    #$aaaaaaaa,(a0)
  2432.     bsr    Correct
  2433.     addq.l    #4,a0
  2434.     move.l    #$44894489,(a0)+
  2435.     move.l    #$ff000000,d0
  2436.     moveq    #0,d6
  2437.     move.w    TDU_CURRTRK(A_UNIT),d6
  2438.     swap    d6
  2439.     or.l    d6,d0
  2440.     move.l    d3,d6
  2441.     lsl.l    #8,d6
  2442.     or.l    d6,d0
  2443.     or.l    d1,d0
  2444.     bsr    EncodeLong    ;header
  2445.  
  2446. ;Encode sector label
  2447.     push    a2
  2448.     lea    SectorLabels(A_DEVICE),a2
  2449.     move.l    d3,d0
  2450.     lsl.l    #4,d0    ;sector*16
  2451.     lea    (a2,d0.w),a2
  2452.     bsr    EncodeSectorLabels
  2453.     pop    a2
  2454.  
  2455.     move.l    d5,d0
  2456.     bsr    EncodeLong    ;header checksum
  2457.     move.l    a0,d2    ;save raw data pointer
  2458.     addq.l    #8,a0
  2459.     bsr    EncodeBlock    ;encode data block
  2460.     move.l    d5,d0
  2461.     exg    a0,d2
  2462.     bsr    EncodeLong    ;data block checksum
  2463.     bsr    Correct
  2464.     move.l    d2,a0
  2465.     addq.l    #1,d3
  2466.     subq.l    #1,d1
  2467.     bne    .SecLoop
  2468.  
  2469.     move.w    #$aaa8,(a0)
  2470.     bsr    Correct    ;extra word to avoid hardware bug
  2471.  
  2472. ;Physically write the data
  2473. .WriteAgain:
  2474.     bsr    SetRegs
  2475.     bsr    PreComp
  2476.     move.w    #ADKF_WORDSYNC,adkcon(a6)    ;turn OFF wordsync!!!
  2477.     move.w    #$0123,dsksync(a6)
  2478.     bsr    ClearSigs
  2479.  
  2480.     btst    #TDPB_VERIFY,TDU_PUBFLAGS(A_UNIT)
  2481.     beq    .SkipVerify
  2482.     bset    #DEVB_Verify,DEV_FLAGS(A_DEVICE)
  2483. .SkipVerify:
  2484.  
  2485.     move.l    VerifyBuffer(A_DEVICE),a0
  2486.     clr.l    (a0)+
  2487.     clr.l    (a0)
  2488.     move.w    #0,dsklen(a6)
  2489.     move.w    #$DA9E,dsklen(a6)
  2490.     move.w    #$DA9E,dsklen(a6)    ;write approx. 13,628 bytes
  2491.  
  2492. ;This piece of code (commented out) tests the function of the rare
  2493. ;"interrupt delayed" requester. (I've never seen it appear in actual use).
  2494.     comment |
  2495. ;TEST
  2496.     push    a6
  2497.     move.l    4,a6
  2498.     SYS    Disable
  2499.     move.w    #5000,d0
  2500. .L1:    move.b    vhposr+_custom,d1
  2501. .L2:    cmp.b    vhposr+_custom,d1
  2502.     beq    .L2
  2503.     dbra    d0,.L1
  2504.     SYS    Enable
  2505.     pop    a6
  2506. |
  2507.  
  2508.     btst    #TDPB_VERIFY,TDU_PUBFLAGS(A_UNIT)
  2509.     beq    .NoVerify
  2510.  
  2511. ;VERIFY
  2512.  
  2513. ;We verify by comparing the raw MFM data in RawBuffer (what we just wrote)
  2514. ;and VerifyBuffer (what is coming in). Due to the ingenious method of
  2515. ;verifying (thanks to Sebastiano Vigna!) the data comes in sector-by-sector
  2516. ;in the same order that we wrote it.
  2517.  
  2518.     move.l    VerifyBuffer(A_DEVICE),a0
  2519.     move.l    RawBuffer(A_DEVICE),a2
  2520.     lea    1666(a2),a2    ;first sector (minus sync)
  2521.     bsr    WaitWordSync
  2522.     tst.l    d7
  2523.     bne    .VerifyError
  2524.     bsr    WaitWordSync
  2525.     tst.l    d7
  2526.     bne    .VerifyError
  2527. ;Now we have our first sector in the verify buffer. Scan for a
  2528. ;sync mark. (There may be 1 or 2 sync marks).
  2529.     cmp.w    #$4489,(a0)+
  2530.     bne    .VerifyError
  2531.     cmp.w    #$4489,(a0)
  2532.     bne    .HaveSync
  2533.     addq.l    #2,a0
  2534. .HaveSync:
  2535.  
  2536. ;We go through a rather elaborate procedure here to make sure that we've
  2537. ;started reading with sector 0. (If not, display a requester informing the
  2538. ;user that something is locking out level-1 interrupts for a long period of
  2539. ;time).
  2540.     push    a2
  2541.     move.l    #$55555555,d2
  2542.     move.l    a0,a2
  2543.     bsr    DecodeLong
  2544.     pop    a2
  2545.     and.w    #$ff00,d0
  2546.     beq    .Sector0
  2547.     move.l    a0,a1
  2548.     moveq    #9,d0
  2549.     moveq    #0,d1
  2550. ..    move.l    (a1)+,d3
  2551.     eor.l    d3,d1
  2552.     dbra    d0,..
  2553.     and.l    d2,d1
  2554.     push    a2
  2555.     move.l    a1,a2
  2556.     bsr    DecodeLong
  2557.     pop    a2
  2558.     cmp.l    d0,d1
  2559.     bne    .VerifyError
  2560.  
  2561. ;Display an informational requester.
  2562.     bsr    StopDMA
  2563.     push    a6
  2564.     move.l    IntBase(A_DEVICE),a6
  2565.     cmp.w    #36,LIB_VERSION(a6)
  2566.     bhi    .KS20
  2567.  
  2568. ;Running under 1.3. Put up a DisplayAlert.
  2569.     moveq    #0,d0    ;alert type (recoverable)
  2570.     lea    .AlertLockOut(pc),a0
  2571.     moveq    #20,d1    ;height
  2572.     SYS    DisplayAlert
  2573.     pop    a6
  2574.     bra    .WriteAgain
  2575.  
  2576. ;Running under 2.0. Put up a EasyRequest.
  2577. .KS20:
  2578.     sub.l    a0,a0
  2579.     sub.l    a2,a2
  2580.     lea    .LockOut(pc),a1
  2581.     SYS    EasyRequestArgs
  2582.     pop    a6
  2583.     bra    .WriteAgain
  2584.  
  2585. ;Note: Probably should compare with the blitter, but this will be fairly
  2586. ;fast.
  2587.  
  2588. .Sector0:
  2589.     move.w    #270-1,d0
  2590. ..    cmp.l    (a0)+,(a2)+
  2591.     dbne    d0,..
  2592.     bne    .VerifyError
  2593.  
  2594. ;Now we are over the initial hump of the first sync mark. The rest of the
  2595. ;compare is even easier.
  2596.     moveq    #9-1,d1
  2597. .VLoop:    bsr    WaitWordSync
  2598.     tst.l    d7
  2599.     bne    .VerifyError
  2600.     move.w    #272-1,d0
  2601. ..    cmp.l    (a0)+,(a2)+
  2602.     dbne    d0,..
  2603.     bne    .VerifyError
  2604.     dbra    d1,.VLoop
  2605.  
  2606. ;We have one more sector to verify. This time we must wait for "Block
  2607. ;done", rather than another sync.
  2608.  
  2609.     bsr    DISK_Wait
  2610.     tst.l    d7
  2611.     bne    .VerifyError
  2612.     move.w    #272-1,d0
  2613. ..    cmp.l    (a0)+,(a2)+
  2614.     dbne    d0,..
  2615.     bne    .VerifyError
  2616.     bra    .End
  2617.  
  2618. .VerifyError:
  2619.     moveq    #0,d7    ;don't propagate the error to the app
  2620. ;We go here if an error is detected during the verify. We first shut down
  2621. ;the read operation that may be in progress, then put up a requester and let
  2622. ;the user choose whether to retry or abort.
  2623.  
  2624.     bsr    StopDMA    ;stop!!
  2625.     push    a6
  2626.     move.l    IntBase(A_DEVICE),a6
  2627.     cmp.w    #36,LIB_VERSION(a6)
  2628.     bhi    .DoKS20
  2629.  
  2630. ;Running under 1.3. Put up a DisplayAlert.
  2631.     moveq    #0,d0    ;alert type (recoverable)
  2632.     lea    .AlertVError(pc),a0
  2633.     moveq    #20,d1    ;height
  2634.     SYS    DisplayAlert
  2635. ;D0 is set to 'TRUE' if the LEFT button was pressed.
  2636.     pop    a6
  2637.     tst.l    d0
  2638.     bne    .WriteAgain    ;go if left button pressed
  2639.     bra    .End
  2640.  
  2641. ;Running under 2.0. Put up a EasyRequest.
  2642. .DoKS20:    sub.l    a0,a0
  2643.     sub.l    a2,a2
  2644.     lea    .VError(pc),a1
  2645.     SYS    EasyRequestArgs
  2646.     pop    a6
  2647.     tst.l    d0
  2648.     bne    .WriteAgain    ;go if left gadget hit
  2649.     bra    .End
  2650. .NoVerify:
  2651.     bsr    DISK_Wait
  2652.     move.l    #4*1000,d0
  2653.     bsr    delay    ;post-write delay
  2654. .End:    movem.l    (sp)+,d0-d6/a0-a2
  2655.     rts
  2656. .Protected:
  2657.     moveq    #DISK_WriteProtected,d7
  2658.     bra    .End
  2659.  
  2660. .AlertVError:
  2661.     dc.w    10    ;x coordinate
  2662.     dc.b    10    ;y coordinate
  2663.     dc.b    '*** VERIFY ERROR !!! ***  Hit LEFT button to RETRY'
  2664.     dc.b    ', or RIGHT button to CANCEL.',0
  2665.     dc.b    0    ;continuation byte
  2666.     even
  2667.  
  2668. .AlertLockOut:
  2669.     dc.w    10    ;x coordinate
  2670.     dc.b    10    ;y coordinate
  2671.     dc.b    'Disk block interrupt delayed by >10ms.'
  2672.     dc.b    '  Press mouse button.',0
  2673.     dc.b    0    ;continuation byte
  2674.     even
  2675.  
  2676. .VError:    dc.l    es_SIZEOF
  2677.     dc.l    0
  2678.     dc.l    .Title
  2679.     dc.l    .MainText
  2680.     dc.l    .GadgetText
  2681. .LockOut:    dc.l    es_SIZEOF
  2682.     dc.l    0
  2683.     dc.l    .Title
  2684.     dc.l    .LockOutTxt
  2685.     dc.l    .Okay
  2686. .LockOutTxt:
  2687.     dc.b    'Disk block interrupt delayed by >10ms caused erroneous'
  2688.     dc.b    ' verify.',0
  2689. .Okay:    dc.b    'If you say so. Try it again!',0
  2690. .Title:    dc.b    'hackdisk.device message',0
  2691. .MainText:
  2692.     dc.b    '*** VERIFY ERROR!!! ***',0
  2693. .GadgetText:
  2694.     dc.b    'Retry|Cancel',0
  2695.     even
  2696.  
  2697. DISK_Write:
  2698.  
  2699. ;Error code returned in D7, as always.
  2700.  
  2701.     movem.l    d0-d5/a0-a2,-(sp)
  2702.  
  2703.     btst    #UNITB_WriteProtected,UNIT_FLAGS(A_UNIT)
  2704.     bne    .ProtError
  2705.  
  2706.     move.l    d0,d5    ;length
  2707.  
  2708. ;The meat of the write routine...
  2709.  
  2710. .WriteLoop:
  2711.     move.l    d1,d3    ;offset
  2712.     divu.w    d6,d3    ;d3.w is track #
  2713.     move.l    d3,d4
  2714.     clr.w    d4
  2715.     swap    d4    ;d4.l is byte offset into track
  2716.     bsr    MinSeek
  2717.     tst.l    d4    ;any offset?
  2718.     bne    .Complex    ;yes
  2719.     cmp.l    d6,d5    ;at least a track left?
  2720.     blo    .Complex    ;no
  2721.     move.l    a0,d0
  2722.     btst    #0,d0    ;word aligned?
  2723.     bne    .Complex    ;no, do it the long way
  2724.     move.l    a0,a2
  2725.     bsr    EncodeAndWriteTrack
  2726.     tst.l    d7
  2727.     bne    .End
  2728.     sub.l    d6,d5    ;update length
  2729.     beq    .End
  2730.     add.l    d6,a0    ;update source pointer
  2731.     add.l    d6,d1    ;update offset
  2732.     bra    .WriteLoop
  2733.  
  2734. .Complex:
  2735.  
  2736. ;This part is somewhat difficult. We check the offset and length parameters
  2737. ;to see whether they're a multiple of 512. If so, we keep track of which
  2738. ;sectors will be written in the buffer. This information is later used by
  2739. ;Update to determine whether a part of the original track must be read in.
  2740. ;(We don't attempt this optimization if the user is writing some odd number
  2741. ;of bytes...This is probably why trackdisk has the limits that it does).
  2742.  
  2743.     tst.l    d5
  2744.     beq    .End    ;nothing left, forget it
  2745.     move.l    d4,d0
  2746.     and.w    #%111111111,d0
  2747.     bne    .NoOpt
  2748.     move.l    d4,d0
  2749.     move.l    d5,d2
  2750.     and.w    #%111111111,d2
  2751.     bne    .NoOpt
  2752.     move.l    d5,d2
  2753.     lsr.l    #8,d0
  2754.     lsr.l    #1,d0    ;get starting sector number
  2755.     lsr.l    #8,d2
  2756.     lsr.l    #1,d2    ;get length in sectors
  2757.     move.l    WriteMap(A_DEVICE),d7
  2758. .OptLoop:    bset    d0,d7
  2759.     addq.b    #1,d0
  2760.     cmp.b    #32,d0
  2761.     beq    .EOpt
  2762.     subq.l    #1,d2
  2763.     bne    .OptLoop
  2764. .EOpt:    move.l    d7,WriteMap(A_DEVICE)
  2765.     moveq    #0,d7
  2766.  
  2767. ;This is normally done by ReadTrackAndDecode.
  2768.     move.b    UnitNum(A_UNIT),BufferDrive(A_DEVICE)
  2769.     move.b    TDU_CURRTRK+1(A_UNIT),BufferTrack(A_DEVICE)
  2770.     bra    .Opt    ;don't read (yet)
  2771.  
  2772. .NoOpt:    push    a0
  2773.     move.l    DecodedBuffer(A_DEVICE),a0
  2774.     bsr    ReadTrackAndDecodeBuffered
  2775.     pop    a0
  2776.     tst.l    d7
  2777.     bne    .End
  2778. .Opt:    move.l    DecodedBuffer(A_DEVICE),a1
  2779.     add.l    d4,a1    ;add byte offset into track
  2780.     sub.l    d6,d4
  2781.     neg.l    d4
  2782.     add.l    d4,d1    ;update offset
  2783.  
  2784. ;D4.L - number of bytes that could be transferred from this track
  2785. ;D5.L - number of bytes left in the entire Read request
  2786.  
  2787.     cmp.l    d5,d4
  2788.     bhs    .FinalWrite
  2789.     sub.l    d4,d5    ;update length
  2790.     move.l    d4,d0
  2791.     bsr    CopyMem
  2792.     add.l    d0,a0    ;update dest pointer
  2793.     bset    #DEVB_Dirty,DEV_FLAGS(A_DEVICE)
  2794.     bra    .WriteLoop
  2795.  
  2796. .FinalWrite:
  2797.     move.l    d5,d0
  2798.     bsr    CopyMem
  2799.     bset    #DEVB_Dirty,DEV_FLAGS(A_DEVICE)
  2800. .End:    movem.l    (sp)+,d0-d5/a0-a2
  2801.     rts
  2802. .ProtError:
  2803.     moveq    #DISK_WriteProtected,d7
  2804.     bra    .End
  2805.  
  2806. MotorOff:
  2807. ;MotorOff turns off all drive motors and leaves all drives deselected
  2808.     bsr    Deselect
  2809.     bset    #CIAB_DSKMOTOR,ciabprb    ;motor off
  2810.     and.b    #~(CIAF_DSKSEL0+CIAF_DSKSEL1+CIAF_DSKSEL2+CIAF_DSKSEL3),ciabprb    ;select all drives
  2811.     bsr    Deselect
  2812.     clr.b    MotorState(A_DEVICE)
  2813.     rts
  2814.  
  2815. Inquire:
  2816.     IFND    _LVOGetUnitID
  2817. _LVOGetUnitID    equ    -30
  2818.     ENDC
  2819.     movem.l    d0-d2/a0-a1/a6,-(sp)
  2820.     moveq    #0,d2
  2821.     move.l    DiskResourceBase(A_DEVICE),a6
  2822. .Loop:    move.l    d2,d0
  2823.     SYS    GetUnitID
  2824.     tst.l    d0
  2825.     bne    .Next
  2826.     bset    d2,InquireBits(A_DEVICE)
  2827. .Next:    addq.l    #1,d2
  2828.     cmp.w    #MD_NUMUNITS,d2
  2829.     blo    .Loop
  2830.     movem.l    (sp)+,d0-d2/a0-a1/a6
  2831.     rts
  2832.  
  2833.     comment |
  2834. ;Find out what drives are in the system
  2835. ;Exit with "drive map" in D0 (bit 0 = drive 0, bit 1 = drive 1, etc.)
  2836.  
  2837. ;See the Hardware Reference Manual, Appendix E for a description of
  2838. ;the procedure.
  2839.  
  2840. ;Note: Drive zero is always present. In fact, the identification 
  2841. ;scheme does not work with drive zero (except for the half-speed drive).
  2842.  
  2843. Inquire:
  2844.     movem.l    d1-d5/a0,-(sp)
  2845.     moveq    #4,d3
  2846.     moveq    #1,d2    ;drive count
  2847.     moveq    #1,d0    ;drive map (drive zero always present)
  2848.     moveq    #7,d1
  2849.     move.l    #ciabprb,a0
  2850.  
  2851.     bsr    Deselect
  2852.  
  2853. .InquireLoop:
  2854.     moveq    #31,d4
  2855.     moveq    #0,d5    ;identification longword
  2856.  
  2857.     bclr    d1,(a0)    ;motor on
  2858.     bset    d3,(a0)    ;deselect drive
  2859.     bclr    d3,(a0)    ;select drive
  2860.  
  2861.     bset    d1,(a0)    ;motor off
  2862.     bset    d3,(a0)    ;deselect drive
  2863.     bclr    d3,(a0)    ;select drive
  2864.     bset    d3,(a0)    ;deselect drive
  2865.  
  2866. .ReadIdent:
  2867.     bclr    d3,(a0)    ;select drive
  2868.     btst    #5,ciaapra    ;test ready
  2869.     beq    .zero
  2870.     bset    d4,d5
  2871. .zero:    bset    d3,(a0)    ;deselect drive
  2872.     dbra    d4,.ReadIdent
  2873.  
  2874.     tst.l    d5
  2875.     bne    .Skip
  2876.     bset    d2,d0
  2877. .Skip:    addq.l    #1,d3
  2878.     addq.l    #1,d2
  2879.     cmp.b    #4,d2
  2880.     bne    .InquireLoop
  2881.     movem.l    (sp)+,d1-d5/a0
  2882.     rts
  2883. |
  2884.  
  2885. SeekZeroAll:
  2886.     movem.l    d0-d2/A_UNIT,-(sp)
  2887.     move.b    InquireBits(A_DEVICE),d0
  2888.     lea    Unit0(A_DEVICE),A_UNIT
  2889.     moveq    #0,d1
  2890.     moveq    #MD_NUMUNITS-1,d2
  2891. .Loop:    btst    d1,d0
  2892.     beq    .Next
  2893.     bsr    SeekZero
  2894. .Next:    lea    MyUnit_Sizeof(A_UNIT),A_UNIT
  2895.     addq.b    #1,d1
  2896.     dbra    d2,.Loop
  2897.     movem.l    (sp)+,d0-d2/A_UNIT
  2898.     rts
  2899.  
  2900. SeekZero:
  2901. ;Places the drive in UnitNum(A_UNIT) on track zero.
  2902. ;Drive does not need to be selected in advance.
  2903. ;Drive will be left _deselected_!
  2904.  
  2905.     movem.l    d0-d1,-(sp)
  2906.  
  2907. .StepLoop:
  2908.     bsr    SelectDriveSameMotor
  2909.     bset    #CIAB_DSKDIREC,ciabprb    ;set to "out" (lower tracks)
  2910.     btst    #CIAB_DSKTRACK0,ciaapra    ;check track zero flag
  2911.     beq    .EndStepLoop
  2912.     bset    #CIAB_DSKSTEP,ciabprb
  2913.     bclr    #CIAB_DSKSTEP,ciabprb    ;step head
  2914.     bset    #CIAB_DSKSTEP,ciabprb
  2915.     bsr    Deselect
  2916.     move.l    TDU_CALIBRATEDELAY(A_UNIT),d0
  2917.     bsr    delay
  2918.     bra    .StepLoop
  2919.  
  2920. .EndStepLoop:
  2921.     bsr    Deselect
  2922.     move.l    TDU_SETTLEDELAY(A_UNIT),d0
  2923.     bsr    delay
  2924.     clr.w    TDU_CURRTRK(A_UNIT)
  2925.     movem.l    (sp)+,d0-d1
  2926.     rts
  2927.  
  2928. CopyMemSlow:
  2929. ;Only to be called by CopyMem
  2930.     move.l    4,a6
  2931.     SYS    CopyMem
  2932.     movem.l    (sp)+,d0-d7/a0-a6
  2933.     rts
  2934.  
  2935. CopyMem:
  2936.  
  2937. ;A0 - source
  2938. ;A1 - destination
  2939. ;D0 - size
  2940.  
  2941.     movem.l    d0-d7/a0-a6,-(sp)
  2942.  
  2943.     move.l    a0,d1
  2944.     btst    #0,d1
  2945.     bne    CopyMemSlow
  2946.     move.l    a1,d1
  2947.     btst    #0,d1
  2948.     bne    CopyMemSlow
  2949.  
  2950.  
  2951. .More:    cmp.l    #512,d0
  2952.     blo    CopyMemSlow
  2953.  
  2954. ;Copy 480 bytes
  2955. n    set    0
  2956.     REPT    10
  2957.     movem.l    (a0)+,d1-d7/a2-a6
  2958.     movem.l    d1-d7/a2-a6,n*48(a1)
  2959. n    set    n+1
  2960.     ENDR
  2961. ;Copy 32 bytes
  2962.     movem.l    (a0)+,d1-d7/a2
  2963.     movem.l    d1-d7/a2,480(a1)
  2964.     lea    512(a1),a1
  2965.     sub.l    #512,d0
  2966.     bne    .More
  2967.     movem.l    (sp)+,d0-d7/a0-a6
  2968.     rts
  2969.  
  2970. ;*************************** Debugging stuff *************************
  2971.  
  2972.     IFNE    INFO_LEVEL
  2973.  
  2974. KPutFmt:    move.l    a2,-(sp)
  2975.     lea    KPutChar(pc),a2
  2976.     bsr    KDoFmt
  2977.     move.l    (sp)+,a2
  2978.     rts
  2979.  
  2980. KDoFmt:    move.l    a6,-(sp)
  2981.     move.l    4,a6
  2982.     SYS    RawDoFmt
  2983.     move.l    (sp)+,a6
  2984.     rts
  2985.  
  2986. KPutChar:
  2987.  
  2988. ;Serial
  2989.     comment |
  2990.     move.l    a6,-(sp)
  2991.     move.l    4,a6
  2992.     SYS    RawPutChar
  2993.     move.l    (sp)+,a6
  2994.     rts
  2995. |
  2996.  
  2997. ;Printer
  2998.     comment |
  2999.     move.b    #$ff,$bfe301
  3000. .Print:    btst    #0,$bfd000
  3001.     bne    .Print
  3002.     move.b    d0,$bfe101
  3003.     rts
  3004. |
  3005.  
  3006. ;Memory
  3007.     tst.l    MemPtr
  3008.     bne    .OK
  3009.     move.l    #$500000,MemPtr
  3010. .OK:
  3011.     push    a0
  3012.     move.l    MemPtr(pc),a0
  3013.     move.b    d0,(a0)+
  3014.     move.l    a0,MemPtr
  3015.     pop    a0
  3016.     rts
  3017.  
  3018. MemPtr:    dc.l    0
  3019.  
  3020.     ENDC
  3021.  
  3022. EndCode:
  3023.