*  Patch.a  --  TrackSalve
*
*   #    date    by    Comment
*  -- --------- ---- ---------------------
*   0 29-Oct-89  DWR   Created for Tracksalve
*

*               nolist
                ifnd    IFILES
IFILES          set     1
                INCLUDE "Call.i"
                INCLUDE "globdefs.i"
                INCLUDE "ts.i"
                INCLUDE "exec/tasks.i"
                INCLUDE "hardware/custom.i"
                include "graphics/text.i"
                include "graphics/rastport.i"
*               include "intuition/intuitionbase.i"
*               include "intuition/intuition.i"
*               include "intuition/screens.i"
                include "devices/inputevent.i"

                ifnd    INTUITION_INTUITION_I
GADGETUP        equ     $00000040
RAWKEY          equ     $00000400
WINDOWDEPTH     equ     $0004
WINDOWDRAG      equ     $0002
SIMPLE_REFRESH  equ     $0040
NOCAREREFRESH   equ     $00020000
ACTIVATE        equ     $1000
GADGHCOMP       equ     $0000
GADGHBOX        equ     $0001
GADGIMAGE       equ     $0004
GADGHIMAGE      equ     $0002
BOOLGADGET      equ     $0001
REQGADGET       equ     $1000
RELVERIFY       equ     $0001
ENDGADGET       equ     $0004
wd_LeftEdge     equ     4
wd_UserPort     equ     86
im_Class        equ     20
im_Code         equ     24
im_IAddress     equ     28
                endc

                ifnd    INTUITION_SCREENS_I
WBENCHSCREEN    equ     $0001
sc_DetailPen    equ     $14a
                endc

                ifnd    INTUITION_INTUITIONBASE_I
ib_FirstScreen  equ     $3c
                endc

                endc

*               list

                SECTION text,code


*********************************************************
*
*  Begin of code that extents/replaces Trackdisk code.  This is made globally known because some
*  routine must copy us from here to an allocated space.  Because all references to the location-
*  counter until TSCodeEnd are done from within this part or from patched Trackdisk code, none
*  of these locations should be made external available.
*

                Func    TSCodeBegin,_TSCodeBegin


*********************************************************
*
*  We jump to this address when changing from ROM to TS.  Special inits must be done here.
*

                move.b  #F_RAM,UNIT_pad(a3)             Set flags to executing in RAM and clear all others
                ;bra.s  TDE_TaskLoop                    Continue normal loop


*********************************************************
*
*  Extension of Trackdisks main task loop.  The first thing we do is a check whether we
*  initiated a terminal state.  If so, we do not check external commands anymore, but
*  continue to finish the special functions and return to ROM-code in the end.
*  Otherwise we compare the external commands with the already active functions and
*  if they differ, we see that they are initiated.
*  We are free to use a2, but its value may have been changed upon entry.
*  A6 contains SysBase and the device pointer must be restored by a pull from the stack.
*
*  Upon entry:  a3      TDU structure (Trackdisk unit)
*               a6      SysBase
*
*  Usage:       a2      UnitData structure
*

TDE_TaskLoop    move.l  (a7)+,a6                        Perform replaced instruction. Restore device pointer
                move.l  TDU_TCB+TC_Userdata(a3),a2      Lea UnitData structure
                btst    #B_TERM,UNIT_pad(a3)            Change of control?
                bne.s   TDE_TermTS                      Yes..
                move.b  UD_Cmd1(a2),d0                  Command from outside
                cmp.b   UNIT_pad(a3),d0                 Same as presently active?
                beq     TD_TaskLoop                     Yes, continue task..

                btst    #B_TERM,d0                      Change of control?
                bne     TDE_TermTS                      Yes..
                bsr     CheckReadOnly                   Check change of write protection
                move.b  UD_Cmd1(a2),d0                  Get commands
                bsr     CheckSalve                      Check change of readerror handling
                move.b  UD_Cmd1(a2),d0                  Get commands
                bsr     CheckNoClick                    Check change of diskchange detection
                move.b  UD_Cmd1(a2),d0                  Get commands
                bsr     CheckVerify                     Check change of verify function
                bra     TD_TaskLoop                     Continue task..

*--     Initiate or continue terminal state.  Stay in the taskloop until all functions are switched off.

TDE_TermTS      bset.b  #B_TERM,UNIT_pad(a3)            Set status of TS to terminating
                moveq.l #0,d0                           Readonly off
                bsr     CheckReadOnly                   Check change of write protection
                moveq.l #0,d0                           Salve off
                bsr     CheckSalve                      Check change of readerror handling
                moveq.l #0,d0                           NoClick off
                bsr     CheckNoClick                    Check change of diskchange detection
                moveq.l #0,d0                           Verify off
                bsr     CheckVerify                     Check change of verify function
                move.b  UNIT_pad(a3),d0                 Current active functions
                and.b   #FUNCTIONS-F_RAM,d0             All extra functions terminated?
                bne     TD_TaskLoop                     No, continue task..

*--     We realy stop here.  The taskloop hereafter continues in ROM-code.

                move.l  a6,-(a7)                        Preserve a6 AND undo the already executed "move.l (a7)+,a6"
                move.l  TDD_SysBase(a6),a6              Trackdisk's SysBase
                move.l  UD_ReturnTD(a2),-(a7)           Push ROM-address. With a "rts" we are back in ROM
                movem.l a4/a5,-(a7)
                LibCall Forbid                          We do daring things here, like freeing code we are executing!
                move.l  UD_TSControl(a2),a5             Lea the Control structure
                move.l  TSC_TSHook(a5),a4               Lea anchor of all our knowledge
                move.l  a4,a0
                LibCall ObtainSemaphore                 We want to be blocked here until others are ready with TSC

                moveq.l #0,d0
                move.b  TDU_UnitNr(a3),d0               Trackdisk unit number
                bclr.b  d0,TSC_InUse(a5)                Tell global structure we stopped using it.
                lsl.l   #2,d0                           Scale to pointer-index
                clr.l   TSC_UnitData(a5,d0)             Clear UnitData pointer in TSC structure
                move.l  UD_Userdata(a2),TDU_TCB+TC_Userdata(a3) Restore original value in Task control block
                move.b  #0,UNIT_pad(a3)                 Back to what it was perhaps
                lea.l   UD_TDReq(a2),a1                 Trackdisk IORequest
                cmp.l   IO_UNIT(a1),a3                  Our request? (of course it is)
                bne.s   10$                             No, skip close..
                LibCall CloseDevice
10$

*--     Return memory of the TSControl structure if there are no other users of it anymore.

                tst.b   TSC_InUse(a5)                   Any users left?
                bne.s   30$                             Yes, do not free it..
                move.l  TSC_Buffer(a5),d1               The install program allocates this buffer.  Its presence may
                                                        ;       not yet have been processed
                beq.s   20$                             No buffer..
                move.l  d1,a1                           Buffer
                move.l  #SB_SIZE,d0                     Its size
                LibCall FreeMem                         Back to the system
20$             move.l  TSC_IntuBase(a5),a1             Close Intuition
                LibCall CloseLibrary
                move.l  a5,a1                           TSControl structure
                move.l  TSC_Size(a5),d0                 Its size (about 10k)
                LibCall FreeMem                         Free it
                clr.l   TSH_TSControl(a4)               No TSControl structure anymore in TSHook
30$

*--     Maybe the TSControl structure does not exist anymore.  We cannot return from Permit() here, so we jump to it.
*       The return address is already on the stack and points to the correct position in ROM-code.

                move.l  a4,a0                           Lea TSHook structure
                LibCall ReleaseSemaphore                We are (almost) ready with the TSControl structure
                movem.l (a7)+,a4/a5
                xref    _LVOPermit
                jmp     _LVOPermit(a6)                  Taskswitching on and back to ROM


*-------------------------------------------------------*
*
*  CheckSalve
*
*  This routine handles the switch from salve function on and off.  If there are no other
*  users of this function left, release the large chip-buffer (about 25k).  If the function
*  is switched on, check the presence of the buffer.  If not there, allocate it.
*
*  Input:       d0      UD_Cmd1
*               a2      UnitData structure
*               a3      Trackdisk unit structure
*               a6      Trackdisk device base
*
*  Return:      -
*

CheckSalve      move.b  UNIT_pad(a3),d1                 Presently active functions
                and.b   #F_SALVE,d0                     Select salve-bit in command
                and.b   #F_SALVE,d1                     Select salve-bit in active functions
                cmp.b   d0,d1                           Salve-action changed?
                bne.s   CheckSalve10                    Yes, see that it is done..
                rts

*--     We must protect the allocation, freeing and administration of the salve buffer with the TSC semaphore.

CheckSalve10    movem.l d2/d3/a5/a6,-(a7)
                move.l  d0,d2                           Keep salve command
                move.b  TDU_UnitNr(a3),d3               Unit number
                move.l  UD_TSControl(a2),a5             TSControl structure
                move.l  TDD_SysBase(a6),a6              SysBase for Semaphore calls
                move.l  TSC_TSHook(a5),a0               Lea semaphore
                LibCall ObtainSemaphore                 We want to be blocked here until others are ready with TSC
                btst    #B_SALVE,d2                     Salve requested?
                beq.s   CS_SalveOff                     No..

CS_SalveOn      bsr     CheckBufAvail                   Check presence and allocate if necessary
                beq.s   CS_Rtn                          No success..
                bset.b  #B_SALVE,UNIT_pad(a3)           Set function active
                bset.b  d3,TSC_Salve(a5)                Tell global structure that we use the buffer
                bra.s   CS_Rtn                          Done..

CS_SalveOff     bclr.b  #B_SALVE,UNIT_pad(a3)           Set function off
                bclr.b  d3,TSC_Salve(a5)                We do not use the buffer into global structure
                bsr     CheckBufFree

CS_Rtn          move.l  TSC_TSHook(a5),a0               Lea semaphore
                LibCall ReleaseSemaphore                We are ready with the TSControl structure
                movem.l (a7)+,d2/d3/a5/a6
                rts

*-------------------------------------------------------*
*
*  CheckVerify
*
*  Handle the switch from verify function on and off.  If there are no other users of this
*  function left, release the large chip-buffer (about 25k). If the function is switched on,
*  check the presence of the buffer.  If not there, allocate it.
*
*  Input:       d0      UD_Cmd1
*               a2      UnitData structure
*               a3      Trackdisk unit structure
*               a6      Trackdisk device base
*
*  Return:      -
*

CheckVerify     move.b  UNIT_pad(a3),d1                 Presently active functions
                and.b   #F_VERIFY,d0                    Select Verify-bit in command
                and.b   #F_VERIFY,d1                    Select Verify-bit in active functions
                cmp.b   d0,d1                           Verify-action changed?
                bne.s   CheckVerify10                   Yes, see that it is done..
                rts

*--     We must protect the allocation, freeing and administration of the salve buffer with the TSC semaphore.

CheckVerify10   movem.l d2/d3/a5/a6,-(a7)
                move.l  d0,d2                           Keep Verify command
                move.b  TDU_UnitNr(a3),d3               Unit number
                move.l  UD_TSControl(a2),a5             TSControl structure
                move.l  TDD_SysBase(a6),a6              SysBase for Semaphore calls
                move.l  TSC_TSHook(a5),a0               Lea semaphore
                LibCall ObtainSemaphore                 We want to be blocked here until others are ready with TSC
                btst    #B_VERIFY,d2                    Verify requested?
                beq.s   CV_VerifyOff                    No..

CV_VerifyOn     bsr     CheckBufAvail                   Check presence and allocate if necessary
                beq.s   CV_Rtn                          No success..
                bset.b  #B_VERIFY,UNIT_pad(a3)          Set function active
                bset.b  d3,TSC_Verify(a5)               Tell global structure that we use the buffer
                bra.s   CV_Rtn                          Done..

CV_VerifyOff    bclr.b  #B_VERIFY,UNIT_pad(a3)          Set function off
                bclr.b  d3,TSC_Verify(a5)               We do not use the buffer into global structure
                bsr     CheckBufFree

CV_Rtn          move.l  TSC_TSHook(a5),a0               Lea semaphore
                LibCall ReleaseSemaphore                We are ready with the TSControl structure
                movem.l (a7)+,d2/d3/a5/a6
                rts

*-------------------------------------------------------*
*
*  Local subroutine of CheckSalve and CheckVerify
*

CheckBufAvail   tst.l   TSC_Buffer(a5)                  Is a buffer present?
                bne.s   10$                             Yes, skip allocation
                move.l  #SB_SIZE,d0                     No, its size
                move.l  #MEMF_CHIP,d1                   And type
                LibCall AllocMem                        Ask system about 25k chip
                move.l  d0,TSC_Buffer(a5)               Into global structure. This buffer is shared
10$             rts


CheckBufFree    tst.b   TSC_Salve(a5)                   Any users left?
                bne.s   10$                             Yes, done..
                tst.b   TSC_Verify(a5)                  Any users left?
                bne.s   10$                             Yes, done..
                move.l  TSC_Buffer(a5),d1               Get buffer
                beq.s   10$                             No buffer..
                move.l  d1,a1                           Buffer
                move.l  #SB_SIZE,d0                     Its size
                LibCall FreeMem                         Back to system
                clr.l   TSC_Buffer(a5)                  No buffer anymore
10$             rts


*-------------------------------------------------------*
*
*  CheckNoClick
*
*  Handle the switch from the NoClick function on and off.
*
*  Input:       d0      UD_Cmd1
*               a2      UnitData structure
*               a3      Trackdisk unit structure
*               a6      Trackdisk device base
*
*  Return:      -
*

CheckNoClick    btst    #B_NOCLICK,d0
                beq.s   10$
                bset.b  #B_NOCLICK,UNIT_pad(a3)
                rts
10$             bclr.b  #B_NOCLICK,UNIT_pad(a3)
                rts

*-------------------------------------------------------*
*
*  CheckReadOnly
*
*  Switching ReadOnly on and off must be done a little careful.  Although we have nothing to
*  do with DOS, (and we do not want too, do we?) it would be inconvenient to switch to
*  readonly when DOS is writing.  There is no way to be sure, but an indication might be the
*  state of the motor.  It is the user who initiates the switch.  He must do it at a quiet
*  moment.  Tell him in the manual!  Again we cannot tell DOS directly that we changed the
*  write protection of the disk, but we simulate a disk-extraction, a shift of the disk
*  protect tab and a disk insertion.  We can skip this if the drive is empty or if the
*  present disk is already write protected by its tab.
*
*  Input:       d0      UD_Cmd1
*               a2      UnitData structure
*               a3      Trackdisk unit structure
*               a6      Trackdisk device base
*
*  Return:      -
*

CheckReadOnly   btst    #B_READONLY,d0                  This drive to be protected?
                beq.s   RO_Off                          No, check reverse action..
                btst.b  #B_READONLY,UNIT_pad(a3)        Check real bit
                beq.s   RO_CheckChange                  We have to change from r/w to ro..
                rts

RO_Off          btst.b  #B_READONLY,UNIT_pad(a3)        Check active function bit
                bne.s   RO_CheckChange                  Was set to write-protect, try change to R/W..
                rts

RO_CheckChange  btst.b  #1,TDU_Flags(a3)                Is there a disk in the drive?
                bne.s   RO_ChangeROSec                  No, just flip RO-bit..
                btst.b  #4,TDU_Flags(a3)                Is there a protected disk in the drive?
                bne.s   RO_ChangeROSec                  Yes, just flip RO-bit..
                btst.b  #CIAB_DSKMOTOR,TDU_CiabPb(a3)   Motor off? (We TRY to avoid change during disk (write) access)
                beq.s   RO_Rtn                          No, try again later..
RO_ChangeRO     bchg.b  #B_READONLY,UNIT_pad(a3)        Change real bit. This will be checked before the hardware
*               pea.l   RO_Rtn(pc)                      Push return address, now we are inside a subroutine
                movem.l d2/a2,-(a7)                     Imitate the disk-presence-check inits
                lea.l   _ciab+ciaprb,a2                 CIAB-PRB
                bra     TD_NoDisk                       Continue subroutine handling disk absence..

RO_ChangeROSec  bchg.b  #B_READONLY,UNIT_pad(a3)        Change function bit. This is checked before the hardware is
RO_Rtn          rts


*********************************************************
*
*  NoClick extentions
*

TDE_NoClick     btst.b  #B_NOCLICK,UNIT_pad(a3)         Do we use this feature?
                bne.s   10$                             Yes..
                bchg.b  #CIAB_DSKDIREC,TDU_CiabPb(a3)   No, change direction bit in cia B image
                rts
10$             bset.b  #CIAB_DSKDIREC,TDU_CiabPb(a3)   Set direction bit to outwards in cia B image
                rts


*********************************************************
*
*  ReadOnly extentions
*
*  We can modify here the protect status of the disk
*

TDE_WriteProt   btst.b  #B_READONLY,UNIT_pad(a3)        Is this drive readonly?
                bne.s   TDE_ReadOnly                    Yes..
                btst.b  #CIAB_DSKPROT,_ciaa+ciapra      No, but perhaps this disk?
                rts

TDE_WriteProt1  btst.b  #B_READONLY,UNIT_pad(a3)        Is this drive readonly?
                bne.s   TDE_ReadOnly                    Yes..
                btst    #CIAB_DSKPROT,d2                No, but perhaps this disk?
                rts

TDE_ReadOnly    cmp.l   d0,d0                           Set zero bit, meaning write protected
                rts


*********************************************************
*
*  Readerror extentions
*
*  When arrived here, there is a read error.  Some original code is executed here (%).
*  When Trackdisk has finished its retry scheme without success, we will give it a try
*

TDE_ReadError   addq.b  #1,TDU_Retries(a3)              % Increment retry counter
                move.b  TDU_Retries(a3),d0              % Get present value of it
                cmp.b   TDU_RETRYCNT(a3),d0             % Lower or equal to limit?
                ble     TD_ReadTrkRetry                 A9EA Yes, retry..
                btst.b  #B_SALVE,UNIT_pad(a3)           No, TD stops, are we going to do something about it?
                beq     TD_ReadTrkRtn                   A9F8 No, function not active, continue TD's error handling..

*
*  We now are going to try to salve this track.  We will read the track in a large buffer.
*  This read is so big that there will be an unbroken track in the buffer.  If we find a
*  valid sector address in this unbroken track, we can calculate the position of the
*  track in the buffer.
*
*  We have:     a2      TD's trackbuffer
*               a3      TD's Unit structure
*
*  We will use: a4      UnitData structure
*               a5      TrackSalveControl structure/ SalveBuffer structure
*

                movem.l a4/a5,-(a7)
                move.l  TDU_TCB+TC_Userdata(a3),a4      Lea UnitData structure
                move.l  UD_TSControl(a4),a5             Lea TSControl structure
                bsr     ObtainBuffer
                move.l  a0,a5                           Extra large read buffer
                moveq.l #-1,d0                          Motor on
                bsr     TD_SwitchMotor
                lea.l   SB_Data(a5),a0                  Begin of data in buffer
                move.l  a0,SB_Begin(a5)
                lea.l   (2*SPT-1)*SE_SIZE+GAPSIZE(a0),a0 Add size we search in for syncs
                move.l  a0,SB_End(a5)
                move.w  #BADTRACKBITS,UD_SavedL(a4)     Init log saved labels
                move.w  #BADTRACKBITS,UD_SavedD(a4)     Init log saved data
                moveq.l #3,d6                           Init retry number

RE_SalveLoop    lea.l   SB_Data(a5),a0                  Destination for disk-read
                move.l  #2*SPT*SE_SIZE+GAPSIZE,d0       Number of bytes to read
                bsr     TD_ReadDisk                     A524 Read d0 bytes into (a0)
                tst.l   d0                              DiskChange?
                beq.s   10$                             No..
                move.b  d0,TB_FirstSec(a2)              Yes, store error in trackbuffer for TD
                bra.s   RE_Rtn                          Leave..
10$             clr.w   SB_CorrectTrk(a5)               Reset counter of headers with good tracknumber
                clr.w   SB_WrongTrk(a5)                 Reset counter of headers with wrong tracknumber
                bsr     Salve                           Analyse the buffer and save what is possible
                beq.s   RE_Rtn                          All sectors and labels salved
                move.w  SB_CorrectTrk(a5),d0            How many times did we encounter good tracknumbers
                cmp.w   SB_WrongTrk(a5),d0              And how many bad?
                bpl.s   20$                             More good..
                subq.l  #1,d6                           Restore, just subtract one
                ble.s   RE_Rtn
                move.w  #-1,TDU_PTrack(a3)              Set current track to "Unknown"
                moveq.l #0,d0
                move.w  TDU_XTrack(a3),d0               Get track number we want
                bsr     TD_Seek                         A3DA Restore and seek
                bra.s   RE_SalveLoop                    Try again to salve the track
20$             subq.l  #2,d6                           No restore, we read the correct track already, subtract two
                bhi.s   RE_SalveLoop

RE_Rtn          bsr     ReleaseBuffer
                movem.l (a7)+,a4/a5
                bra     TD_ReadTrkRtn                   Continue TD


*-------------------------------------------------------*
*
*  --  Salve  --
*
*  We search for a sync.  If found we try to get a reliable sector address from the
*  succeeding data.  This address tells us how much and the position of the track preceding
*  this sector.  We try to recover these sectors if we did not already.  The area after the
*  found sector is also checked for sector presence, but if no success search will continue
*  from the found sync.
*
*  Input:       a2      TB_  Trackdisk trackbuffer
*               a3      TDU_ Trackdisk Unit structure
*               a4      UD_  TrackSalve UnitData structure
*               a5      SB_  TrackSalve SalveBuffer structure
*
*  Usage:       d2      Scratch
*               d5      Pointer to sector in imaginary track
*               d6      Pointer to sector of last detected sync
*               d7      Shiftfactor
*
*  Returns:     d0      Zero if all sectors and labels saved
*


Salve           movem.l d2/d5-d7,-(a7)
                subq.l  #HE_SIZE,a7                     Four bytes local storage

                move.l  SB_Begin(a5),d6                 Begin of buffer
                subq.l  #8,d6                           Undo next instruction

*--     Scan buffer for syncs.  If found, test it for a valid header.

ST_SearchLoop   addq.l  #8,d6                           Point to word past previous sync found
                move.l  SB_End(a5),d0                   End of search area
                sub.l   d6,d0                           Minus begin of search area is search size
                ble     ST_EndSearch                    End of buffer reached..
                move.l  d6,a0                           Actual begin of search
                bsr     SearchSync                      Search next sync on this track
                bmi     ST_EndSearch                    No sync found, leave..
                subq.l  #6,a0                           Point to begin of potential sector
                move.l  a0,d6                           Keep potential sector found
                bsr     GetHeader                       Test validity and decode header
                ble.s   ST_SearchLoop                   Not valid

*--     We have a sector header.  Figure a track around it.

                move.l  d0,(a7)                         Local header for loop control
                moveq.l #SPT,d0                         Number of sectors per track
                sub.b   HE_ToGap(a7),d0                 Subtract ahead of us, leaves us with number of sectors before us
                sub.b   d0,HE_SecNo(a7)                 Set number of first sector, still positive?
                bpl.s   10$                             Yes..
                add.b   #SPT,HE_SecNo(a7)               No, wrap
10$             move.b  #SPT,HE_ToGap(a7)               New gap countdown
                mulu    #SE_SIZE,d0                     Convert number of sectors to bytes before our position
                move.l  d6,d5                           Pointer to present sector
                sub.l   d0,d5                           Pointer to first potential sector

*--     Scan imaginary track for storable sectors

ST_TrackLoop    cmp.l   SB_Begin(a5),d5                 Are we before begin of the buffer?
                bmi     ST_NextSector                   Yes, next sector..
                cmp.l   SB_End(a5),d5                   Are we past the end of the buffer?
                bpl.s   ST_SearchLoop                   Yes, stop with this track..
                move.b  HE_SecNo(a7),d0                 Sector number
                move.l  UD_SavedL(a4),d1                Log of stored labels and sectors
                beq     ST_AllSaved                     All stored, leave this routine..
                btst    d0,d1                           Is the data of this sector already stored?
                bne.s   ST_SaveSector                   No, try to..
                swap    d1                              Yes, test the label
                btst    d0,d1                           This label aready saved?
                beq     ST_NextSector                   Yes, try next sector in our imaginary track..

*--     Try to store as much as possible from the sector d5 is pointing to.

ST_SaveSector   move.l  d5,a0                           Ea of present sector
                bsr     GetHeader                       Get its header if possible
                bmi     ST_NextSector                   Wrong track..
                beq.s   ST_SaveData                     No header, perhaps the sector's data is OK
                cmp.l   (a7),d0                         This MUST be the same or what?
                bne     ST_VeryBad                      Very bad indeed
                move.l  d5,a0                           Ea of present sector
                lea.l   SE_SumD(a0),a0                  Lea data checksum
                bsr     DecodeLong                      Decode it
                move.l  d0,d2                           Keep for compare
                move.l  #TD_SECTOR/2,d0                 Size of data-part of sector in longs
                bsr     CheckSum                        Calculate checksum over data
                cmp.l   d2,d0                           Same as found on disk?
                beq.s   ST_CopySector                   Yes, save to copy the complete sector..
                move.b  HE_SecNo(a7),d0                 No, but we have a valid header, the numbers are ok and if this
                move.w  UD_SavedD(a4),d1                  sector is not yet saved, we better still copy it
                btst    d0,d1                           Sector already saved?
                beq     ST_SaveLabel                    Yes, skip bad sector copy
                bra.s   ST_LabelOK                      We copy the sector, but skip the bitclear..
ST_CopySector   move.b  HE_SecNo(a7),d0                 Number of this sector
                move.w  UD_SavedD(a4),d1                Data save-log
                bclr    d0,d1                           Set data saved
                move.w  d1,UD_SavedD(a4)
ST_LabelOK      move.b  HE_SecNo(a7),d0                 Number of this sector
                move.w  UD_SavedL(a4),d1                Label save-log
                bclr    d0,d1                           Set label saved
                move.w  d1,UD_SavedL(a4)
                ext.w   d0                              Cnv sectornumber byte to word
                mulu    #SE_SIZE,d0                     To byte offset
                lea.l   TB_Data(a2),a1                  TD's buffer, begin of sectors
                lea.l   SE_Label(a1,d0),a1              Lea destination of our data
                move.l  d5,a0                           Ea of present source sector
                lea.l   SE_Label(a0),a0                 Lea begin of part to copy
                move.w  #((SSIZE/2)<<HSIZEBITS)+1,d0    Size of sector for blitter
                move.w  #SSIZE,d2                       Bytesize
                bsr     BlitMove                        Copy to TD's buffer
                bra     ST_NextSector                   Loop for next sector

ST_SaveData     move.b  HE_SecNo(a7),d0                 Sector number
                move.w  UD_SavedD(a4),d1                Saved data log
                btst    d0,d1                           Data of this sector already saved?
                beq.s   ST_NextSector                   Yes..
                move.l  d5,a0                           Ea of present sector
                lea.l   SE_SumD(a0),a0                  Lea data checksum
                bsr     DecodeLong                      Decode it
                move.l  d0,d2                           Keep for compare
                move.l  #TD_SECTOR/2,d0                 Size of data-part of sector in longs
                bsr     CheckSum                        Calculate checksum over data
                cmp.l   d2,d0                           Same as found on disk?
                bne.s   ST_NextSector                   No, no header and bad data, skip copy, next perhaps..
                moveq.l #0,d0
                move.b  HE_SecNo(a7),d0                 Number of present sector
                move.w  UD_SavedD(a4),d1                Saved data log
                bclr    d0,d1                           Set data of this sector saved
                move.w  d1,UD_SavedD(a4)
                mulu    #SE_SIZE,d0                     Sector number times sectorsize to find offset in TD's buffer
                lea.l   TB_Data(a2),a1                  TD's buffer, begin of sectors
                lea.l   SE_SumD(a1,d0),a1               Lea destination of our data
                move.l  d5,a0                           Ea of source sector
                lea.l   SE_SumD(a0),a0                  Lea Data checksum, begin of copy to TD's buffer
                move.w  #((DSIZE/2)<<HSIZEBITS)+1,d0    Size of datapart and its checksum ready for the blitter
                move.w  #DSIZE,d2                       Bytesize of block
                bsr     BlitMove                        Copy to TD's buffer
                bra.s   ST_NextSector                   Continue loop..


ST_SaveLabel  ; move.b  HE_SecNo(a7),d0                 Present sector number
                move.l  UD_SavedL(a4),d1                Saved labels log
                bclr    d0,d1                           We have a good one here
                beq.s   ST_NextSector                   Was already saved, continue..
                move.l  d1,UD_SavedL(a4)                Nice, we can save a label
                ext.w   d0
                mulu.w  #SE_SIZE,d0                     To byte offset
                lea.l   TB_Data(a2),a1                  Lea begin of sectors
                lea.l   SE_Label(a1,d0),a1              Lea of label in destination sector in TD's buffer
                move.l  d5,a0                           Ea of present sector
                lea.l   SE_Label(a0),a0                 Lea label in source sector
                move.w  #((LSIZE/2)<<HSIZEBITS)+1,d0    Size of label ready for the blitter
                moveq.l #LSIZE,d2                       Bytesize of block to move
                bsr     BlitMove                        Move label to Trackdisks buffer
                bra.s   ST_NextSector                   Continue loop..

*--     Track-scan loop control.

ST_NextSector   move.b  HE_SecNo(a7),d0                 Sector number
                addq.b  #1,d0                           Next sector
                cmp.b   #SPT,d0                         Must we wrap?
                bcs.b   10$                             No..
                moveq.l #0,d0                           Yes, sector 0
10$             move.b  d0,HE_SecNo(a7)
                add.l   #SE_SIZE,d5                     Present sector pointer to next sector
                subq.b  #1,HE_ToGap(a7)                 Decrement remaining sectors in our track, any left?
                bhi     ST_TrackLoop                    Yes, process it..
                bra     ST_SearchLoop                   No, we are ready with our track, try to find a new one..

ST_EndSearch
ST_AllSaved     move.l  UD_SavedL(a4),d0

ST_Rtn          addq.l  #HE_SIZE,a7                     Clean up local
                tst.l   d0                              Return proper condition codes
                movem.l (a7)+,d2/d5-d7
                rts

ST_VeryBad      move.l  #BADTRACKBITS<<16+BADTRACKBITS,d0 Assume all labels and sectors to be unreliable
                move.l  d0,UD_SavedL(a4)
                bra.s   ST_Rtn


*-------------------------------------------------------*
*
*  GetHeader
*
*  Subroutine for Salve.  Test validity of the first part of a Trackdisk sector.  Set sector
*  address in SB_Header and return in d0 if valid.  Update statistics regarding tracknumber.
*  Return wrong track as a special case.
*
*  Input:       d7      Shift factor
*               a0      Sector structure, SE_
*               a2      Trackdisk trackbuffer structure, TB_
*               a5      SalveBuffer structure, SB_
*
*  Return:      d0      Header, -1 on wrong track, 0 on other errors.
*               a0      Ea of SE_Label
*

GetHeader       addq.l  #SE_Header,a0
                moveq.l #HE_SIZE/2+TD_LABELSIZE/2,d0    Size of checksum area (longs)
                bsr     CheckSum                        Calculate checksum
                move.l  d0,-(a7)                        Keep checksum
                bsr     DecodeLong                      Decode it
                cmp.l   (a7)+,d0                        The same?
                bne.s   GH_NotGood                      No..
                add.w   #(SE_Header-SE_SumD),a0         Lea header again
                bsr     DecodeLong
                move.l  d0,(a5)                         Make header available
                cmp.b   #DISKFORMAT,(a5)                Our format?
                bne.s   GH_NotGood                      No..
                move.w  TB_TrkNo(a2),d1                 Track number under head
                cmp.b   HE_TrkNo(a5),d1                 Same as in header?
                bne.s   GH_WrongTrack                   No..
                addq.w  #1,SB_CorrectTrk(a5)            Yes, one for us
                rts
GH_WrongTrack   addq.w  #1,SB_WrongTrk(a5)              One for them
                moveq.l #-1,d0                          Error (restore?)
                rts
GH_NotGood      moveq.l #0,d0                           Error
                rts

*-------------------------------------------------------*
*
*  DecodeLong
*
*  Get two longwords from shifted memory and decode them into one longword
*
*  Input:       d7      Shift factor
*               a0      Data source
*
*  Return:      d0      Decoded longword
*               a0      Proper aligned for next access
*

DecodeLong      movem.l d2/d3,-(a7)
                moveq.l #16,d3                          Shift modulus
                sub.l   d7,d3                           Complementary shift number
                move.l  (a0)+,d0                        Get 48 bits (long + max shift)
                move.w  (a0),d2
                lsl.l   d7,d0                           Shift first 32 bits left
                lsr.w   d3,d2                           Shift last 16 bits right
                or.w    d2,d0                           Put them together into one longword
                move.l  (a0)+,d1                        Get 48 bits (long + max shift)
                move.w  (a0),d2
                lsl.l   d7,d1                           Shift first 32 bits left
                lsr.w   d3,d2                           Shift last 16 bits right
                or.w    d2,d1                           Put them together into one longword
                and.l   #$55555555,d0                   Remove MFM bits
                and.l   #$55555555,d1
                lsl.l   #1,d0                           Line up
                or.l    d1,d0                           Melt
                movem.l (a7)+,d2/d3
                rts


*-------------------------------------------------------*
*
*  CheckSum
*
*  Calculate checksum over shifted memory
*
*  Input:       d0      Length in longs
*               d7      Shift factor
*               a0      Begin of block
*
*  Return:      d0      Checksum
*               a0      Aligned to begin of next block
*

CheckSum        movem.l d2/d3,-(a7)
                moveq.l #-1,d1                          Build mask for first access
                lsr.l   d7,d1                           Shift mask
                move.l  (a0)+,d2                        Get part of first long
                and.l   d1,d2                           Mask non-involved bits
                subq.l  #2,d0
10$             move.l  (a0)+,d3                        Get longs
                eor.l   d3,d2                           Checksum algorithm
                dbra    d0,10$                          Loop
                move.l  (a0),d3                         Get last part of last long
                not.l   d1                              Flip mask
                and.l   d1,d3                           Mask non-involved bits
                eor.l   d3,d2                           Checksum algorithm
                rol.l   d7,d2                           Unshift
                and.l   #$55555555,d2                   Select relevant bits
                move.l  d2,d0                           Checksum
                movem.l (a7)+,d2/d3
                rts

*-------------------------------------------------------*
*
*  SearchSync
*
*  Search for a Sync. If found, return its address on word boundary and
*  how many bits it is shifted to the right. (max 15)
*
*  Input:       d0.w    Number of words to check
*               a0      Start address to search
*
*  Returns:     d0      Words left
*               d7      Bitshift
*               a0      Sync
*

SearchSync      move.w  (a0)+,d1
                cmp.w   #SYNCSYNC>>0,d1
                beq.s   SS_Found0
                cmp.w   #SYNCSYNC>>1,d1
                beq.s   SS_Found1
                cmp.w   #SYNCSYNC>>2,d1
                beq.s   SS_Found2
                cmp.w   #SYNCSYNC>>3,d1
                beq.s   SS_Found3
                cmp.w   #SYNCSYNC>>4,d1
                beq.s   SS_Found4
                cmp.w   #SYNCSYNC>>5,d1
                beq.s   SS_Found5
                cmp.w   #SYNCSYNC>>6,d1
                beq.s   SS_Found6
                cmp.w   #SYNCSYNC>>7,d1
                beq.s   SS_Found7
                cmp.w   #SYNCSYNC>>8,d1
                beq.s   SS_Found8
                cmp.w   #SYNCSYNC>>9,d1
                beq.s   SS_Found9
                cmp.w   #SYNCSYNC>>10,d1
                beq.s   SS_Found10
                cmp.w   #SYNCSYNC>>11,d1
                beq.s   SS_Found11
                cmp.w   #SYNCSYNC>>12,d1
                beq.s   SS_Found12
                cmp.w   #SYNCSYNC>>13,d1
                beq.s   SS_Found13
                cmp.w   #SYNCSYNC>>14,d1
                beq.s   SS_Found14
                cmp.w   #SYNCSYNC>>15,d1
                beq.s   SS_Found15
                dbra    d0,SearchSync                   Loop d0 words
                moveq.l #-1,d7                          Set not found
SS_Rtn          subq.l  #2,a0
SS_Rtn10        rts

SS_Found0       moveq.l #0,d7
                bra.s   SS_Rtn10
SS_Found1       moveq.l #1,d7
                bra.s   SS_Rtn
SS_Found2       moveq.l #2,d7
                bra.s   SS_Rtn
SS_Found3       moveq.l #3,d7
                bra.s   SS_Rtn
SS_Found4       moveq.l #4,d7
                bra.s   SS_Rtn
SS_Found5       moveq.l #5,d7
                bra.s   SS_Rtn
SS_Found6       moveq.l #6,d7
                bra.s   SS_Rtn
SS_Found7       moveq.l #7,d7
                bra.s   SS_Rtn
SS_Found8       moveq.l #8,d7
                bra.s   SS_Rtn
SS_Found9       moveq.l #9,d7
                bra.s   SS_Rtn
SS_Found10      moveq.l #10,d7
                bra.s   SS_Rtn
SS_Found11      moveq.l #11,d7
                bra.s   SS_Rtn
SS_Found12      moveq.l #12,d7
                bra.s   SS_Rtn
SS_Found13      moveq.l #13,d7
                bra.s   SS_Rtn
SS_Found14      moveq.l #14,d7
                bra.s   SS_Rtn
SS_Found15      moveq.l #15,d7
                bra.s   SS_Rtn


*-------------------------------------------------------*
*
*  BlitMove
*
*  Move a block of data from source to destination and shift it left by some bits.
*
*  Input:       d0.w    Blitsize ready to pass to the blitter
*               d2.w    Bytesize of block to move
*               d7.w    Shift factor: number of bits that memory IS shifted to the right
*               a0      Source
*               a1      Destination
*
*  In linear mode the blitter can move at most 2^10 (1024) words. Therefore we use      its
*  rectancular mode. We can set the horizontal size to 1 to 128 words, thus limiting our
*  blit to multiples of these.

BlitMove        movem.l d3-d5,-(a7)
                sub.w   #TSBN_SIZE,a7                   Extended blitnode on the stack
                moveq.l #0,d4                           First let's clear all unused and undefined
                move.l  d4,(a7)                            parts of the blinode as stated in
                move.w  d4,bn_stat(a7)                     I&A Introduction-5 Obey 3&4
                move.l  d4,bn_cleanup(a7)

*  We use the blitter in ascending mode. Then the blitter performs shifts to the right.
*  Therfore we translate our order to shift left into shift right.  IF we shift, and we
*  stick to our blitsize, we loose some bits. This is because we express in blitsize
*  the size of the track in words instead of bits.  If these bits do not begin on a
*  word-boundary they do not end either.   Because the blitter can move maximal 1024
*  block of 1 to 128 words, and we do not determine the blocksize (the calling routine is),
*  we take care of this word by hand (the cpu).

                move.l  d7,d4                           Shiftfactor (to left)
                neg.w   d4                              We want 16 minus shift results in requested shift to right, zero?
                beq.s   10$                             Yes, no shift..
                subq.l  #2,a1                           Destination one word lower
                move.w  (a1),d3                         Keep first word to restore after blit
                move.l  -2(a0,d2.w),d5                  Get last two words of block (out of reach of the blitter)
                lsl.l   d7,d5                           Do the shift of the last word here
                swap.w  d5                              D5 holds now the last word
10$             move.w  d0,bn_blitsize(a7)              The calling routine has aready assembled the correct value
                moveq.l #BSHIFTSHIFT,d0                 Shift shift factor 12 bits left, thus clearing all other
                lsl.w   d0,d4                             bits: no fill, ascending and area mode
                move.w  d4,TSBN_C1(a7)                  Prepared blitter command
                move.l  a0,TSBN_SceB(a7)                Source
                move.l  a1,TSBN_Dest(a7)                Destination
                move.l  a3,TSBN_Unit(a7)                TD-Unit structure
                lea.l   QF_Shift(pc),a0                 Lea blitter function
                move.l  a0,bn_function(a7)              Function into blitnode
                move.l  a7,a1                           Pointer to our blitnode
                move.l  a6,-(a7)
                move.l  TDD_GfxBase(a6),a6              The graphics library coordinates blitter usage
                LibCall QBlit                           Control is returned immedately so we must wait here
                move.l  (a7)+,a6
                bsr     TD_WaitTDPort                   A70A Wait for a message we sent to ourself from within Gfx
                tst.w   TSBN_C1(a7)                     Shift not zero?: Did we modify the destination pointer?
                beq.s   20$                             No, blit fitted in word boundary..
                move.l  TSBN_Dest(a7),a0                Yes, get destination (pointing one word earlier)
                move.w  d3,(a0)                         Restore original value of word before desired destination
                move.w  d5,0(a0,d2.w)                   Last word which was out of reach of the blitter
20$             add.w   #TSBN_SIZE,a7                   Free blitnode
                movem.l (a7)+,d3-d5
                rts


*-------------------------------------------------------*
*
*  QBlit function. Parameters are passed via the blitnode structure.  We have exclusive control
*  over the blitter here.
*  NB!  This code is executed under control of other regimes. Behave well.
*  We tell the blitter to move and shift our block.
*
*  Input:       a0      Pointer to blitter
*               a1      Pointer to blitnode
*
*  Returns:     d0      -1, meaning not ready
*

QF_Shift        moveq.l #0,d0
                move.w  d0,bltbmod(a0)                  No modulus for B
                move.w  d0,bltdmod(a0)                  No modulus for D
BLITLF          set     ABC+ABNC+NABC+NABNC             D=B
BLITUSE         set     SRCB+DEST                       B is source and D destination
                move.w  #BLITLF+BLITUSE,bltcon0(a0)
                move.w  TSBN_C1(a1),bltcon1(a0)         Shift factor into BSH (B-shift) ascending
                move.l  TSBN_SceB(a1),bltbpt(a0)        Source into  B
                move.l  TSBN_Dest(a1),bltdpt(a0)        Destination into  D
                move.w  bn_blitsize(a1),bltsize(a0)     Start blit by writing size
                lea.l   QF_End(pc),a0                   Lea terminating function
                move.l  a0,bn_function(a1)              Function into blit node
                moveq.l #-1,d0
                rts

*-------------------------------------------------------*
*
*  QBlit function. Parameters are passed via the blitnode structure.  We have exclusive control
*  over the blitter here.
*  NB!  This code is executed under control of other regimes. Behave well.
*  We signal the td task that the blit is done. (Here we are NOT the TD task)
*
*  Input:       a0      Pointer to blitter
*               a1      Pointer to blitnode
*
*  Returns:     d0      0, meaning ready
*

QF_End          move.l  TSBN_Unit(a1),a1                TD unit pointer
                bsr     TD_Reply
                moveq.l #0,d0                           Return 0 to stop QBlit
                rts


*********************************************************
*
*  Read extensions.
*
*  Adapt reads (and writes) to error handling based on error flags for each sector apart.
*  Trackdisk sets the tracknumber in its trackbuffer to -1, meaning no valid track in the
*  buffer.  TD does this if it finds a TB_FirstSec larger than possible.  We prevent TD doing
*  so. Instead we check TD_FirstSec each time TD_TrkNo would have been checked.  This enables
*  us to access a faulty trackbuffer when we use the salve function.
*


TDE_Read        cmp.b   #SPT,TB_FirstSec(a0)            Error in this track?
                bmi     TD_CmdRead                      A890 No..
                btst.b  #B_SALVE,UNIT_pad(a3)           Yes, but is the track salved?
                bne.s   10$                             Yes..
                move.b  TB_FirstSec(a0),IO_ERROR(a2)    No, set error in IORequest
                bra     TD_EndRW                        A920 Return error..
10$

*--     Here we have a read command on a salved track.  Always copy data, but if this data is bad, set IO_ERROR

                move.l  TDU_TCB+TC_Userdata(a3),a1      Lea UnitData structure
                move.l  UD_SavedL(a1),d1                Get pattern with bad sectors and labels
                moveq.l #0,d0
                move.b  TDU_IOSector(a3),d0             Current sector number
                btst    d0,d1                           Faulting sector?
                bne.s   20$                             Yes, set error..
                btst.b  #0,TDU_Flags(a3)                No, label transfer?
                beq     TD_CmdRead10                    A8A0 No, no error, continue..
                swap.w  d1                              Yes, label transfer, get label log
                btst    d0,d1                           A8A0 Faulting label?
                beq     TD_CmdRead10                    No..
20$             move.b  TB_FirstSec(a0),IO_ERROR(a2)    Set error
                bra     TD_CmdRead10                    A8A0




*********************************************************
*
*  ObtainBuffer -  ReleaseBuffer
*
*  Handling of permission to use the buffer
*
*  Input:       a3      TD unit structure
*
*  Returns:     a0      buffer
*

ObtainBuffer    move.l  a2,-(a7)
                move.l  TDU_TCB+TC_Userdata(a3),a2      Lea UnitData structure
                move.l  UD_TSControl(a2),a2             Lea TSControl structure
                lea.l   TSC_OwnBuffer(a2),a0            Lea Semaphore for salvage system usage
                move.l  a6,-(a7)
                move.l  TDD_SysBase(a6),a6              Trackdisk's SysBase
                LibCall AttemptSemaphore                Try to lock Salve system
                move.l  (a7)+,a6
                tst.l   d0                              Locked?
                bne.s   10$                             Yes..
                moveq.l #0,d0                           No, motor off
                bsr     TD_SwitchMotor
                lea.l   TSC_OwnBuffer(a2),a0            Lea Semaphore for salvage system usage
                move.l  a6,-(a7)
                move.l  TDD_SysBase(a6),a6              Trackdisk's SysBase
                LibCall ObtainSemaphore                 Wait for permission to use it
                move.l  (a7)+,a6
10$             move.l  TSC_Buffer(a2),a0               The buffer itself
                move.l  (a7)+,a2
                rts


ReleaseBuffer   move.l  TDU_TCB+TC_Userdata(a3),a0      Lea UnitData structure
                move.l  UD_TSControl(a0),a0             Lea TSControl structure
                lea.l   TSC_OwnBuffer(a0),a0            Lea Semaphore for salvage system usage
                move.l  a6,-(a7)
                move.l  TDD_SysBase(a6),a6              Trackdisk's SysBase
                LibCall ReleaseSemaphore
                move.l  (a7)+,a6
                rts


*********************************************************
*
*  At one point in TD_NormTrack all data has been moved to their definitive position.  After this point
*  all sectorheaders are recalculated.  If we are verifying we do not want any changes, just the move.
*  So at this position we want to leave TD_NormTrack.  TD_NormTrack calls some subroutine at that point
*  and we are there to check whether we are verifying or not.
*

TDE_NormTrack   tst.l   24(a7)                          The original caller of TD_NormTrack had zero saved here
                beq.w   TD_CorrectEdge                  Zero, proceed normal..
                ;addq.l #4,a7                           Non-zero: TD_NormTrack was called by the verify routine
                bra.w   TD_NormTrackRtn                 Skip header revision..


*********************************************************
*
*  Write extensions.
*
*  Write Trackdisks buffer to disk and check whether verify is enabled.  Do not verify if the
*  TD_WriteTrack returned an error.  Get the verify buffer and read the just written track into
*  it.  Tidy the buffer up and compare it with the just written.  In case of some error, display
*  a requester and rewrite if asked for.
*
*  Input:       a3      Trackdisk Unit structure
*               a6      Trackdisk device base
*
*  Returns:     d0      Write error
*
*  Usage        d2      Error
*               a2      Verify buffer
*               a4      Trackdisk buffer written to disk
*

TDR_WriteTrackF clr.b   TB_FirstSec(a0)                 Format does not use and set this field
TDR_WriteTrack  bsr     TD_WriteTrack                   Normal write track
                btst.b  #B_VERIFY,UNIT_pad(a3)          Is write-verify enabled?
                bne.s   20$                             Yes..
10$             rts

20$             tst.l   d0                              Did TD_WriteTrack had an error?
                bne.s   10$                             Yes, we do not verify..

                movem.l d2/a2/a4,-(a7)
                bsr     ObtainBuffer                    Get buffer to read into from disk
                move.l  a0,a2
                move.l  TDU_DiskBuf(a3),a4              The buffer which was written to disk
                move.l  a2,TDU_DiskBuf(a3)              The buffer into we will read from disk
                move.w  (a4),(a2)                       Tracknumber
                moveq.l #1,d0                           Motor on
                bsr     TD_SwitchMotor
                moveq.l #0,d0
                move.w  (a2),d0                         Tracknumber
                bsr     TD_Seek                         A3DA Seek to it
                lea.l   TB_Data+4(a2),a0                Position in buffer to begin to read
                move.w  #(SPT+1)*SE_SIZE+GAPSIZE,d0     Size of read
                bsr     TD_ReadDisk                     Read from disk
                tst.l   d0                              DiskChange?
                bne.s   30$                             Yes, that is a verify error..
                move.w  TDU_XTrack(a3),d2               Keep current value
                move.w  (a4),TDU_XTrack(a3)             TD_NormTrack uses this value as the current track
                moveq.l #-1,d0                          Tell (modified) TD_NormTrack to skip header revision
                bsr     TD_NormTrack                    Tidy the track up
                move.w  d2,TDU_XTrack(a3)               Restore original value
                move.l  a4,TDU_DiskBuf(a3)              TD's write buffer back into TD's global
                cmp.b   #SPT,d0                         Error?
                bcc.s   30$                             Yes..
                move.b  d0,TB_FirstSec(a2)              First sector in the verify buffer
                move.l  a2,a0                           Verify buffer
                move.l  a4,a1                           Original written buffer
                bsr     CompareTracks                   Compare both buffers (they are not exactly the same)
                beq.s   VT_Rtn                          No error..

*--     Let's request. TD has a 512 byte stack. Not enough to call Intuition..  But we have here a buffer of 26k!

30$             moveq.l #0,d0                           Motor off during request
                bsr     TD_SwitchMotor
                add.w   #10000,a2                       Make a little room for stack
                exg     a2,a7                           Set a7 to the buffer
                move.w  TB_TrkNo(a4),d0                 Tracknumber
                bsr     VerifyAlert                     Display the requester
                move.l  a2,a7                           TD's stack again
VT_Rtn          move.l  d0,d2                           Answer of user: zero is ignore
                bsr     ReleaseBuffer                   We do not need the verify buffer anymore
                move.l  d2,d0                           Rewrite?
                movem.l (a7)+,d2/a2/a4
                bne     TDR_WriteTrack                  The user wanted a rewrite..
                rts


*-------------------------------------------------------*
*
*  CompareTracks
*
*  Compare data in two trackbuffers.  A simple compare is not possible.  The data should be
*  the same but may be arranged differently.
*
*
*  Input:       a0      Trackbuffer
*               a1      Trackbuffer
*
*  Returns:     d0      Zero if buffers are the same, -1 in case of error
*

CompareTracks   moveq.l #SPT,d0                         Total number of sectors
                add.b   TB_FirstSec(a0),d0              Add first sector of the first buffer
                sub.b   TB_FirstSec(a1),d0              Subtract first sector of the other buffer
                divu    #SPT,d0                         Subtract SPT if larger than SPT
                swap.w  d0                              Remainder is sector shift between both buffers
                mulu    #SE_SIZE,d0                     Byte size of first part
                move.w  #SPT*SE_SIZE,d1                 Total size
                sub.w   d0,d1                           Size of second part

                lea.l   TB_Data(a0),a0                  Begin of compare in first buffer
                lea.l   TB_Data(a1),a1                  Begin of sectors in second buffer
                add.w   d0,a1                           Add to find first sector to compare in second buffer
                lsr.w   #2,d0                           Cnv to long counter
                lsr.w   #2,d1                           Cnv to long counter
                cmp.w   d0,d0                           Preset equal condition
                bra.s   CT_Loop01                       Enter loop

CT_Loop0        cmp.l   (a0)+,(a1)+
CT_Loop01       dbne    d1,CT_Loop0                     Terminates on not equal or d1==-1
                bne.s   CT_Error                        Buffers are not the same..
                subq.w  #1,d0                           We do not compare the first long
                bmi.s   CT_OK                           Nothing to compare anymore
                addq.l  #4,a0                           Skip SectStart
                lea.l   -SPT*SE_SIZE+4(a1),a1           Compare buffer to one long past begin
                cmp.w   d0,d0                           Preset equal condition
                bra.s   CT_Loop11                       Enter loop
CT_Loop1        cmp.l   (a0)+,(a1)+
CT_Loop11       dbne    d0,CT_Loop1                     Terminates on not equal or d1==-1
                beq.s   CT_OK                           Buffers are the same..
CT_Error        moveq.l #-1,d0                          I'm so sorry
                bra.s   CT_Rtn
CT_OK           moveq.l #0,d0                           No error
CT_Rtn          rts


*-------------------------------------------------------*
*
*  VerifyAlert  -  Notify user of the verity error and ask him what to do.
*
*  To do so, we use a requester with the choise to ignore the event (inherent in TD) or to
*  rewrite the track.  This is more complicated as it looks at first sight.  It would be
*  nice if an AutoRequest could be used (simple & small), but the idea behind write verify
*  requires an airtight approach.  If there is not enough memory to build the AutoRequest,
*  Intuition defaults to an Alert.  But if there is not enough memory to build the Alert,
*  Intuition simply returns FALSE.  As if the user selected "Ignore".  Useless for our
*  purposes.  So we have to build our own requester.  We have no window open, so we open a
*  window for just one use:  to show the requester.  A requester has the fe. ff feature to
*  block all input to the window.  Intuition permits all kinds of events to penetrate to
*  the SysRequest.  I do not know an easy way (we are patching TD, remember?) to get the same
*  result.  The other approach is to build the gadgets directly in the window.  Then we
*  receive all kind of events, including Left-Amiga-V and Left-Amiga-N.  Pity that DMouse
*  cannot see that we are requesting.  But otherwise we have no shortcuts.  So?
*
*  Input:       d0      Tracknumber
*               a3      TD Unit structure
*               a6      TD device pointer
*
*  Returns:     d0      TRUE for rewrite, FALSE for ignore
*


VerifyAlert     movem.l d2-d4/a2/a4/a5,-(a7)

*--     Insert head number in requester string

                moveq.l #'0'>>1,d1                      Prepare for asci conversion
                lsr.b   #1,d0                           Get head number in X-bit
                roxl.b  #1,d1                           Make asci zero or one
                lea.l   TXT_HeadNr(pc),a0               Destination
                move.b  d1,(a0)                         Store into requester string

*--     D0 now contains the cylinder number.  Convert via BCD to asci and store into string

                move.l  a7,a2                           Predecrement pointer to local word
                clr.w   -(a7)                           One word local
                moveq.l #7,d1                           Binary 2 nibbles to 2 bcd nibbles conversion
10$             move.l  a2,a0                           Destination
                move.l  a2,a1                           Destination
                lsl.b   #1,d0                           Binary shift left
                abcd.b  -(a0),-(a1)                     Bcd shift left
                dbra    d1,10$                          Loop for eigth bits

                move.w  (a7)+,d1                        Get bcd word
                lea.l   TXT_TrackNr(pc),a0              Destination
                move.b  d1,d0                           Bcd value
                lsr.b   #4,d1                           Next nibble
                or.b    #'0',d1                         Convert to asci
                move.b  d1,(a0)+                        Store in alert string
                and.b   #$0f,d0                         Mask nibble
                or.b    #'0',d0                         Convert to asci
                move.b  d0,(a0)                         Store in alert string

*--     Unit number in string

                moveq.l #'0',d0                         Asci first unit
                add.b   TDU_UnitNr(a3),d0               Unit number
                lea.l   TXT_UnitNr(pc),a0               Position in requester string
                move.b  d0,(a0)                         Store unit number

*--     Open a window on the Workbench screen and show a requester in it.

                moveq.l #-1,d2                          Set flag to requester failed
                move.l  TDU_TCB+TC_Userdata(a3),a0      Lea UnitData structure
                move.l  UD_TSControl(a0),a0             TSControl structure
                move.l  a6,-(a7)                        Trackdisk device base
                move.l  TDD_SysBase(a6),a5              ExecBase
                move.l  TSC_IntuBase(a0),a6             Our starter got IntuitionBase already for us
                LibCall WBenchToFront
                move.l  ib_FirstScreen(a6),a0
                move.w  sc_DetailPen(a0),d0             Negative
                lea.l   TSCodeBegin(pc),a0
                move.w  d0,IT_Body0-TSCodeBegin(a0)
                move.w  d0,IT_Body1-TSCodeBegin(a0)
                move.b  d0,IMG_ReqOnOff-TSCodeBegin(a0)
                rol.w   #8,d0                           Positive
                move.w  d0,IT_Positive-TSCodeBegin(a0)
                move.w  d0,IT_Negative-TSCodeBegin(a0)
                move.b  d0,IMG_GadOnOff-TSCodeBegin(a0)
                lea.l   NEW_Requester(pc),a0            NewWindow structure
                LibCall OpenWindow
                move.l  d0,a4                           Window structure, success?
                beq.s   NoReqWindow                     No..

*--     Wait for an answer of the user

                exg.l   a5,a6                           SysBase in a6
IDCMP_Loop      move.l  wd_UserPort(a4),a0              IDCMP message port
                LibCall WaitPort                        Wait for a message from Intuition
                move.l  wd_UserPort(a4),a0              IDCMP message port
                LibCall GetMsg                          Remove message from queue
                move.l  d0,a1
                move.l  im_Class(a1),d3                 Keep Class
                move.l  im_IAddress(a1),d2              Keep message initiator
                move.l  im_Code(a1),d4                  Keep Code AND Qualifier
                LibCall ReplyMsg                        Message back to Intuition

*--     Perhaps the user clicked one of the gadgets

                cmp.l   #GADGETUP,d3                    Gadget clicked?
                bne.s   VA_ChkRawkey                    No, check keyboard shortcut
                lea.l   GAD_Negative(pc),a0             Get one of both message inititors
                sub.l   a0,d2                           Set return value to zero if this gadget was clicked
                beq.s   CloseReqWindow                  "Ignore" was clicked..
                moveq.l #1,d2                           Not equal, other gadget must have been clicked, return positive
                bra.s   CloseReqWindow

*--     Perhaps the user pressed a 'B' or 'V'

VA_ChkRawkey    cmp.l   #RAWKEY,d3                      Keyboard event?
                bne.s   IDCMP_Loop                      No, ignore message..
                and.w   #IEQUALIFIER_LCOMMAND,d4        Left-Amiga pressed?
                beq.s   IDCMP_Loop                      No, ignore..
                swap.w  d4                              Select code field
                moveq.l #0,d2                           Returnvalue for "Ignore"
                cmp.w   #$0035,d4                       Keycode for 'B'
                beq.s   CloseReqWindow                  Same, return ignore..
                moveq.l #1,d2                           Returnvalue for "Rewrite"
                cmp.w   #$0034,d4                       Keycode for 'V'
                bne.s   IDCMP_Loop                      Not what we want, ignore message..

*--     Get present position of the window and close it

CloseReqWindow  exg.l   a5,a6                           IntuitionBase again
                lea.l   NEW_Requester(pc),a0            NewWindow structure
                move.l  wd_LeftEdge(a4),(a0)            Install present window position into new window
                move.l  a4,a0                           Window structure
                LibCall CloseWindow
NoReqWindow     move.l  a6,a5                           IntuitionBase
                move.l  (a7)+,a6                        TD base
                move.l  d2,d0                           Result
                bpl.s   VA_Rtn                          Positive, user replied on requester..

*--     For some reason there was no requester, so bleep screens and blink drive led, return rewrite

                exg     a5,a6                           Negative, no requester, get IntuBase
                sub.l   a0,a0                           All screens
                LibCall DisplayBeep                     Bleep
                exg     a5,a6                           TD Base
                moveq.l #6,d2                           Blink drive led 3 times
10$             move.l  d2,d0                           Get counter
                and.l   #1,d0                           Select LSB (toggles)
                bsr.w   TD_SwitchMotor                  Motor on and off
                move.l  #200000,d0                      Sleep for a while
                bsr.w   TD_Sleep
                dbra    d2,10$                          Loop
                moveq.l #-1,d0                          Set rewrite as result

VA_Rtn          movem.l (a7)+,d2-d4/a2/a4/a5
                rts


*-------------------------------------------------------*
*
*  Requester data structures.
*
*  These structures have pointers to each other.  Therefore we cannot move them without making
*  adaptions.  We can do two things.  Normal relocation followed by an adaption after the move,
*  or not relocate at loadtime and perform complete relocation later.  We choose the latter
*  because this saves relocation info in the exe and the relocation routine is not
*  larger/smaller/easyer/harder.
*  Because the relocation table is not to be included in the resident structure, it is
*  physically placed somewhere at the end of this file.
*  The "Body text" is tied to the "positve text".  This saves us Intuprinting the requester text.
*  Because code is outgrowing already the pan, the font is fixed to topaz 8.  All calculations
*  regarding window size, gadget positions, text postitions are done by hand and here and now.
*

NEW_Requester   dc.w    0,0
                dc.w    242,55
                dc.b    -1,-1
                dc.l    GADGETUP+RAWKEY
                dc.l    WINDOWDEPTH+WINDOWDRAG+SIMPLE_REFRESH+NOCAREREFRESH+ACTIVATE
NEW_ReqGadget   dc.l    GAD_Positive-TSCodeBegin
                dc.l    0
NEW_ReqTitle    dc.l    TXT_VerTitle-TSCodeBegin
                dc.l    0,0
                dc.w    0,0,0,0
                dc.w    WBENCHSCREEN

GAD_Positive    dc.l    GAD_Negative-TSCodeBegin
                dc.w    26,35
                dc.w    82,16
                dc.w    GADGIMAGE+GADGHCOMP
                dc.w    RELVERIFY+ENDGADGET
                dc.w    BOOLGADGET+REQGADGET
GAD_PosRender   dc.l    IMG_Gadgets-TSCodeBegin
                dc.l    0
GAD_PosIText    dc.l    IT_Positive-TSCodeBegin
                dc.l    0
                dc.l    0
                dc.w    0
                dc.l    0

GAD_Negative    dc.l    0
                dc.w    134,35
                dc.w    82,16
                dc.w    GADGIMAGE+GADGHCOMP
                dc.w    RELVERIFY+ENDGADGET
                dc.w    BOOLGADGET+REQGADGET
GAD_NegRender   dc.l    IMG_Request-TSCodeBegin
                dc.l    0
GAD_NegIText    dc.l    IT_Negative-TSCodeBegin
                dc.l    0
                dc.l    0
                dc.w    0
                dc.l    0

IMG_Request     dc.w    -130,-23
                dc.w    234,41,0
                dc.l    0
                dc.b    0
IMG_ReqOnOff    dc.b    0
IMG_ReqNext     dc.l    IMG_Gadgets-TSCodeBegin

IMG_Gadgets     dc.w    0,0
                dc.w    82,16,0
                dc.l    0
                dc.b    0
IMG_GadOnOff    dc.b    0
                dc.l    0

IT_Positive     dc.b    0,0,RP_JAM2,0
                dc.w    13,4
IT_PosFont      dc.l    TA_Topaz8-TSCodeBegin
IT_PosText      dc.l    TXT_Positive-TSCodeBegin
IT_PosNext      dc.l    IT_Body0-TSCodeBegin

IT_Negative     dc.b    0,0,RP_JAM2,0
                dc.w    17,4
IT_NegFont      dc.l    TA_Topaz8-TSCodeBegin
IT_NegText      dc.l    TXT_Negative-TSCodeBegin
                dc.l    0

IT_Body0        dc.b    0,0,RP_JAM2,0
                dc.w    -14,-20                         Relative to the positive gadget
IT_B0Font       dc.l    TA_Topaz8-TSCodeBegin
IT_B0Text       dc.l    TXT_Body0-TSCodeBegin
IT_B0Next       dc.l    IT_Body1-TSCodeBegin

IT_Body1        dc.b    0,0,RP_JAM2,0
                dc.w    -14,-11                         Relative to the positive gadget
IT_B1Font       dc.l    TA_Topaz8-TSCodeBegin
IT_B1Text       dc.l    TXT_Body1-TSCodeBegin
                dc.l    0

TA_Topaz8       dc.l    TXT_FontName-TSCodeBegin
                dc.w    8
                dc.b    FS_NORMAL,0

TXT_VerTitle    dc.b    ' TrackSalve 1.0 ',0
TXT_FontName    dc.b    'topaz.font',0
TXT_Body0       dc.b    'Write verify error detected',0
TXT_Body1       dc.b    'on unit '
TXT_UnitNr      dc.b            'x, track '
TXT_TrackNr     dc.b                     'xx, head '
TXT_HeadNr      dc.b                              'x',0

TXT_Positive    dc.b    'Rewrite',0
TXT_Negative    dc.b    'Ignore',0

                cnop    0,2


*********************************************************
*
*  End of code to be copied to the allocated space.  Would this be the allocted space,
*  then the TD-copy would begin here.
*  From here it is allowed again to make the location counter external available.
*

TSCodeEnd
TD


TD_NoDisk       equ     TD+$00DE        99C2
TD_Seek         equ     TD+$0AF6        A3DA
TD_SwitchMotor  equ     TD+$0B7E        A462
TD_ReadDisk     equ     TD+$0C40        A524
TD_WaitTDPort   equ     TD+$0E26        A70A
TD_ReadTrkRetry equ     TD+$1106        A9Ea
TD_ReadTrkRtn   equ     TD+$1114        A9F8
TD_TaskLoop     equ     TD+$1580        AE64
TD_Reply        equ     TD+$0E0E        A6F2
TD_CmdWrite     equ     TD+$0F34        A818
TD_CmdRead      equ     TD+$0FAC        A890
TD_CmdRead10    equ     TD+$0FBC        A8A0
TD_EndRW        equ     TD+$103C        A920
TD_P101b        equ     TD+$0F04        A7E8
TD_WriteTrack   equ     TD+$1074        A958
TD_NormTrack    equ     TD+$16FE        AFE2
TD_NormTrackRtn equ     TD+$18F0        B1D4
TD_CorrectEdge  equ     TD+$14DA        ADBE
TD_Sleep        equ     TD+$0C0C        A4F0


*********************************************************
*
*  This table contains data which will be put into the trackdisk code we copied from ROM.  Its location must
*  directly follow TSCodeEnd for offset calculations.  So do not reposition this table without an adaption of
*  the Patch macro.
*  The format of the table is a stucture of this form:
*
*  WORD         OffsetInTD              Offset relative to rt_MatchTag
*  UWORD        PatchSize               Size of patch in words
*  STRUCT       Patch,PatchSize*2       The patch itself
*
*  The Patch table consists of a series of patch structures following each other.  The end of the table
*  is reached when a patch is met with a PatchSize of zero.
*

Patch           MACRO   <Offset in TD>,<Patchsize in bytes>
                dc.w    \1
                dc.w    (\2)/2
TS              set     *-TD-\1
                ENDM

                Func    TSPatchTable,_TSPatchTable

*--     Bug patches

                Patch   $1A38,P000e-P000b               B31C Bug in td_RawWrite
P000b           cmp.l   #$8000,d0
P000e
                Patch   $00E6,P001e-P001b               AAAAAAAAARRRRRRRGGGHHHH...
P001b           dc.w    $0016                           GetUnit before GiveUnit patch
P001e

*--     Let toplevel task loop flow through our code

                Patch   $1592,P100e-P100b               AE76 Toplevel task loop
P100b           bra.w   TS+TDE_TaskLoop
P100e

*--     Check first with us befire clicking

                Patch   $0104,P200e-P200b               99E8 Presence test
P200b           bsr.w   TS+TDE_NoClick
                nop
P200e

*--     Check readonly simutation before writes or returning td_ProtStatus

                Patch   $0D22,P300e-P300b               A606 Write routine
P300b           bsr.w   TS+TDE_WriteProt
                nop
                nop
P300e
                Patch   $1194,P301e-P301b               AA78 Tab-test for td_ProtStatus
P301b           bsr.w   TS+TDE_WriteProt1
P301e
*               Patch   $0162,P302e-P302b               9A46 Disk login, firt detection disk presence
*P302b          bsr.w   TS+TDE_WriteProt1
*P302e

*--     Read (write) adaptions to the salve function

                Patch   $10F8,P400e-P400b               A9DC Read error after normalisation
P400b           bra.w   TS+TDE_ReadError
P400e
                Patch   $0EC8,P401e-P401b               A7AC
P401b           bne.s   TS+TD_P101b                     A7E8 The track we want is in the buffer..
P401e
                Patch   $0F04,P402e-P402b               A7E8
P402b           move.l  TDU_IOReq(a3),a2                Get IORequest
                move.l  TDU_DiskBuf(a3),a0              Track buffer
                cmp.b   #CMD_WRITE,IO_COMMAND+1(a2)     Write?
                bne     TS+TDE_Read                     No, read..
                cmp.b   #SPT,TB_FirstSec(a0)            Yes, write. No error in track?
                bcs.s   TS+TD_CmdWrite                  A818 No, go write
                move.b  TB_FirstSec(a0),IO_ERROR(a2)
                bra     TS+TD_EndRW                     A920 Bad return
P402e
                Patch   $102A,P403e-P403b               A90E
P403b           blt     TS+TD+$0F04                     A7E8
P403e

*--     Verify adaptions to write routines

                Patch   $07C4,P500e-P500b               A0A8
P500b           bsr     TS+TDR_WriteTrack
P500e
                Patch   $0EDC,P502e-P502b               A7C0
P502b           bsr     TS+TDR_WriteTrack
P502e
                Patch   $11D6,P503e-P503b               AABA
P503b           bsr     TS+TDR_WriteTrack
P503e
                Patch   $15D2,P504e-P504b               AEB6
P504b           bsr     TS+TDR_WriteTrack
P504e
                Patch   $16FE,P505e-P505b               AFE2
P505b           movem.l d0/d2-d6/a2,-(a7)               D0 added to list, has become an argument
P505e
                Patch   $18FA,P506e-P506b
P506b           movem.l (a7)+,d1/d2-d6/a2               D1 added to list as a dummy pull to compensate above
P506e
                Patch   $180E,P507e-P507b               B0F2
P507b           bsr.w   TS+TDE_NormTrack
P507e

*--     Verify adaption to Format routine

                Patch   $0864,P551e-P551b               A148
P551b           move.w  d2,TB_TrkNo(a0)
                move.l  a0,TDU_DiskBuf(a3)
                bsr.w   TS+TDR_WriteTrackF
P551e
                Patch   0,0


*********************************************************
*
*  The relocation table of the code between TSCodeBegin and TSCodeEnd.
*

                Func    TSCodeRelocTable

                dc.w    NEW_ReqGadget-TSCodeBegin
                dc.w    NEW_ReqTitle-TSCodeBegin
                dc.w    IMG_ReqNext-TSCodeBegin
                dc.w    GAD_Positive-TSCodeBegin
                dc.w    GAD_PosRender-TSCodeBegin
                dc.w    GAD_PosIText-TSCodeBegin
                dc.w    GAD_NegRender-TSCodeBegin
                dc.w    GAD_NegIText-TSCodeBegin
                dc.w    IT_B0Font-TSCodeBegin
                dc.w    IT_B0Text-TSCodeBegin
                dc.w    IT_B0Next-TSCodeBegin
                dc.w    IT_B1Font-TSCodeBegin
                dc.w    IT_B1Text-TSCodeBegin
                dc.w    IT_PosFont-TSCodeBegin
                dc.w    IT_PosText-TSCodeBegin
                dc.w    IT_PosNext-TSCodeBegin
                dc.w    IT_NegFont-TSCodeBegin
                dc.w    IT_NegText-TSCodeBegin
                dc.w    TA_Topaz8-TSCodeBegin
                dc.w    0


*********************************************************
*
*  The size of the code to be copied is EndTSCode-_TSCode.  The size of the buffer is
*  also assembler laid down.
*  How do we pass these values to a C-module?
*  For now with globals, but constants would be more appropriate..
*

                SECTION __MERGED,data

                gc_l    _TSCodeSize,TSCodeEnd-TSCodeBegin
                gc_l    _BufferSize,SB_SIZE

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

                END
