                .DO     External
                .LSTON
                .Page
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;>
;>      Module: Utils.B1
;>
;>      BANK 1 MODULE
;>
;>      This module contains routines that are part of the main utility
;>      module and that must be kept in Bank 1.
;>
;>      PROCEDURE RBuf_To_Buf2
;>      PROCEDURE Buf2_To_RBuf
;>      PROCEDURE WrBuf_To_Buf2
;>      PROCEDURE Buf2_To_WrBuf
;>      PROCEDURE RBuf_To_Spr
;>      PROCEDURE Spr_To_RBuf
;>      PROCEDURE WrBuf_To_Spr
;>      PROCEDURE Spr_To_WrBuf
;>      PROCEDURE Zero_RdBuf
;>      PROCEDURE Zero_Fmt
;>      FUNCTION Get_Cyl_H_S( PhysicalBlock : 3 BYTES { !!rC:E } ) :
;>                          Cylinder : WORD { !!rC }
;>                          Head : BYTE { !rE }
;>                          Sector : BYTE { !rF }
;>      FUNCTION GoodHdr : BOOLEAN
;>                         Cylinder : WORD { !!r0 }
;>                         Head : BYTE { !r2 }
;>                         Sector : BYTE { !r3 }
;>      PROCEDURE BlockMove( Destination : PTR { !!r2 }
;>                           Source : PTR { !!r0 }
;>                         )
;>      FUNCTION UpDate_Hdr : BOOLEAN
;>      FUNCTION Get_Type( DriverType : BYTE { !r8 } ) :
;>                       BlockType : 3 BITS { !r8/bits 3:1 }
;>                       BlockNumber : 3 BYTES { !rC:E }
;>
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                .LSTOFF
                .FIN
                .DO     External
                .LSTON
                .Page
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;>
;>      Procedures: 
;>                 Ld_Stand_Stat  { move standard status to output buffer }
;>                 RBuf_To_Buf2   { move ReadBuffer to Buffer2 }
;>                 Buf2_To_RBuf   { move Buffer2 to ReadBuffer }
;>                 WrBuf_To_Buf2  { move WriteBuffer to Buffer2 }
;>                 Buf2_To_WrBuf  { move Buffer2 to WriteBuffer }
;>                 RBuf_To_Spr    { move ReadBuffer to SpareArray }
;>                 Spr_To_RBuf    { move SpareArray to ReadBuffer }
;>                 Zero_RdBuf     { move zeros to ReadBuffer }
;>
;>      Inputs: { none }
;>
;>      Outputs: { none }
;>
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                .LSTOFF
                .FIN
                .DO     Internal
                .LSTON
                .Page
                .FIN
                
Buf2_To_RBuf:    Ld     !r0,#.HIBYTE. Buf2Array
                 Ld     !r1,#.LOWBYTE. Buf2Array
                 Ld     !r2,#.HIBYTE. RDummy
                 Ld     !r3,#.LOWBYTE. RDummy
                Jr      B_Move

WrBuf_To_Buf2:   Ld     !r0,#.HIBYTE. ( WBuffer1-1 )
                 Ld     !r1,#.LOWBYTE. ( WBuffer1-1 )
                Jr      X_To_Buf2
                
Buf2_To_WrBuf:   Ld     !r0,#.HIBYTE. Buf2Array
                 Ld     !r1,#.LOWBYTE. Buf2Array
                 Ld     !r2,#.HIBYTE. ( WBuffer1-1 )
                 Ld     !r3,#.LOWBYTE. ( WBuffer1-1 )
                Jr      B_Move
                
Spr_To_RBuf:     Ld     !r0,#.HIBYTE. SpareArray
                 Ld     !r1,#.LOWBYTE. SpareArray
                 Ld     !r2,#.HIBYTE. RBuffer1
                 Ld     !r3,#.LOWBYTE. RBuffer1
                Jr      B_Move

RBuf_To_Spr:     Ld     !r0,#.HIBYTE. RBuffer1
                 Ld     !r1,#.LOWBYTE. RBuffer1
X_To_Spr:        Ld     !r2,#.HIBYTE. SpareArray
                 Ld     !r3,#.LOWBYTE. SpareArray
                Jr      B_Move
                
WrBuf_To_Spr:    Ld     !r0,#.HIBYTE. WBuffer1
                 Ld     !r1,#.LOWBYTE. WBuffer1
                 Jr     X_To_Spr
                 
RBuf_To_Buf2:   Cp      DataType,#User_Type ;nop if sparetable data
                Jr      Nz,Return_Vector
                 Ld     !r0,#.HIBYTE. RDummy
                 Ld     !r1,#.LOWBYTE. RDummy
X_To_Buf2:       Ld     !r2,#.HIBYTE. Buf2Array
                 Ld     !r3,#.LOWBYTE. Buf2Array
B_Move:         Call    BlockMove
Return_Vector:  Jp      Bank_Ret
                
Spr_To_WrBuf:    Ld     !r0,#.HIBYTE. SpareArray
                 Ld     !r1,#.LOWBYTE. SpareArray
                 Ld     !r2,#.HIBYTE. WBuffer1
                 Ld     !r3,#.LOWBYTE. WBuffer1
                Jr      B_Move

Zero_RdBuf:      Ld     !r2,#.HIBYTE. ReadArray
                 Ld     !r3,#.LOWBYTE. ReadArray
                Call    ZeroBlock
                Jr      Return_Vector

Zero_Fmt:        Ld     !r2,#.HIBYTE. ReadArray
                 Ld     !r3,#.LOWBYTE. ReadArray
                 Ld     !rE,#.HIBYTE. ( FBuffer1 - ReadArray )
                 Ld     !r0,#0 ;init block to pattern
Zero_FLp:       Lde     @!!r2,!r0
                Incw    !!r2
                Dec     !rE
                Jr      Nz,Zero_Flp
                 Ld     !r2,#.HIBYTE. FBuffer1
                 Ld     !r3,#.LOWBYTE. FBuffer1
                 Ld     !rE,#.HIBYTE. ( FEndGap - FBuffer1 )
                 Ld     !rF,#.LOWBYTE. ( FEndGap - FBuffer1 )
                 Ld     !r0,#$C6 ;init block to pattern
Zero_FLp1:      Lde     @!!r2,!r0
                Incw    !!r2
                Decw    !!rE
                Jr      Nz,Zero_Flp1
                Jp      Return_Vector
                
                .LSTOFF
                .DO     External
                .LSTON
                .Page
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;>
;>      Function: Get_Cyl_H_S  { Get Cylinder, Head, and Sector }
;>
;>      This routine extracts the cylinder, head, and sector information
;>      from a physical block number.
;>
;>      Inputs:
;>              PhysicalBlockNumber : 3 BYTES { !rC:E }
;>
;>      Outputs:
;>              HiCylinder : BYTE { !rC }
;>              LoCylinder : BYTE { !rD }
;>              Head       : BYTE { !rE }
;>              Sector     : BYTE { !rF }
;>
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                .LSTOFF
                .FIN
                .DO     Internal
                .LSTON
                .Page
                .FIN
                
Get_Cyl_H_S:    
                Call    DivHdsSctrs     ;return Cylinder #, remainder = !r0:3
                
                Push    !r0             ;get ready to pass results to caller
                Push    !r1

                Call    DivSctrs        ;return with !r1 = Head, !r2 = Sector
                
                Ld      !rF,!r3         ;Sector
                Ld      !rE,!r2         ;Head
                Pop     !rD             ;Lo Cylinder
                Pop     !rC             ;Hi Cylinder
                
                Ld      PSector,!rF     ;UpDate physical sector
                
                Jp      Bank_Ret
                
                .LSTOFF
                .DO     External
                .LSTON
                .Page
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;>
;>      Function: UpDate_Cur_Cyl
;>
;>      This function is responsible for reading the header off of
;>      an arbitrary sector. The cylinder information is extracted
;>      from the header and placed into the current cylinder memory
;>      locations.
;>
;>      Inputs: { none }
;>
;>      Outputs:
;>              UpDate_Cur_Cyl : BOOLEAN { Zero flag is set if NOT( ReadHdr ) }
;>
;>      Global Variables Changed:
;>              Cur_Cylinder
;>
;>      Algorithm:
;>
;>      BEGIN
;>       Temp := GoodHdr( ReadHdr )
;>       IF Temp
;>        THEN Cur_Cylinder := ReadHdr.Cylinder
;>       UpDate_Cur_Cyl := Temp
;>      END
;>
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                .LSTOFF
                .FIN
                .DO     Internal
                .LSTON
                .Page
                .FIN
                
UpDate_Cur_Cyl:
                Call    GoodHdr
                Jr      Z,UD_C_C_End
                
                Ld      Cur_Cyl,!r0
                Ld      Cur_Cyl+1,!r1
                Or      !r1,#01         ;return non-zero status
                
UD_C_C_End:     Jp      Bank_Ret
                 
                .LSTOFF
                .DO     External
                .LSTON
                .Page
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;>
;>      Function: GoodHdr
;>
;>      This function is responsible for reading a header off of a 
;>      sector and checking that header for validity, i.e., whether
;>      the cylinder, head, and sector ( and their complements ) look
;>      reasonable.
;>
;>      Inputs: { none }
;>
;>      Outputs:
;>              GoodHdr : BOOLEAN { Zero flag is set if Header invalid }
;>              GoodHdr.Cylinder : WORD { !!r0 }
;>              GoodHdr.Head     : BYTE { !r2 }
;>              GoodHdr.Sector   : BYTE { !r3 }
;>
;>      Algorithm:
;>
;>      BEGIN
;>       ReadHdr
;>       IF ( Inverse( ReadHdr.Cylinder ) = ReadHdr.InverseCylinder ) AND
;>          ( Inverse( ReadHdr.HdSctr ) = ReadHdr.InverseHdSctr )
;>        THEN
;>              GoodHdr := True
;>              GoodHdr.Cylinder := ReadHdr.Cylinder
;>              GoodHdr.Head := ReadHdr.HdSctr/bits 7:6
;>              GoodHdr.Sector := ReadHdr.HdSctr/bits 5:0
;>        ELSE
;>              GoodHdr := False
;>      END
;>
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                .LSTOFF
                .FIN
                .DO     Internal
                .LSTON
                .Page
                .FIN
                
GoodHdr:
                Ld      !r4,#8 ;try hard to read a good header
                
GdHdr_1:        Call   ReadHdr

                Ld     !r2,#.HIBYTE. RHHeader
                Ld     !r3,#.LOWBYTE. RHHeader
                Ld     !r0,#ScrReg0
                Ld     !r1,#6  ;load 6 bytes

GdHdr_Lp:       Ldei   @!r0,@!!r2
                Djnz   !r1,GdHdr_Lp
                
                Srp    #Wrk_Scr ;context switch
                
                Xor    !r3,!r0 ;check for valid header
                Xor    !r4,!r1
                Xor    !r5,!r2
                
                And    !r3,!r4
                And    !r3,!r5
                Com    !r3
                
                Srp     #Wrk_Sys ;context switch
                Clr     ScrReg6 ;assume bad header
                Jr      Z,GdHdr_2
                
                Tm      Excpt_Stat,#Recovery ;check for Recovery on
                Jr      Z,GdHdr_End
                
                Call    ChkOff_NoOff ;set auto_offset if not already on
                
                 Ld     !r2,#.HIBYTE. 10 ;wait 100 ms before retrying
                 Ld     !r3,#.LOWBYTE. 10
                Call    MsWait
                
                Djnz    !r4,GdHdr_1 ;retry if bad header
                Jr      GdHdr_End
                
GdHdr_2:        Ld      !r0,ScrReg0 ;GoodHdr.Cylinder := ReadHdr.Cylinder
                Ld      !r1,ScrReg1
                Ld      !r2,ScrReg2 ;GoodHdr.Head := ReadHdr.HdSctr/bits 7:6
                Swap    !r2
                Rr      !r2
                Rr      !r2
                And     !r2,#$03    ;just leave head info in register
                Ld      !r3,ScrReg2 ;GoodHdr.Sector := ReadHdr.HdSctr/bits 5:0
                And     !r3,#$3F
                Ld      ScrReg6,#1
                
GdHdr_End:      Or      ScrReg6,ScrReg6 ;set zero flag
                Jp      Bank_Ret
                
                .LSTOFF
                .DO    External
                .LSTON
                .Page
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;>
;>      Procedure: BlockMove
;>
;>      This procedure performs the following:
;>
;>      ( Destination ) <-- ( Source )
;>
;>      Where destination and source are both the same size ( namely
;>      exactly 532 bytes plus CRC and ECC ).
;>
;>      Inputs:
;>              Destination : PTR { !!r2 }
;>              Source      : PTR { !!r0 }
;>
;>      Outputs: { none }
;>
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                .LSTOFF
                .FIN
                .DO     Internal
                .LSTON
                .Page
                .FIN

BlockMove:
                Call    Ext_Push
                
                Ld      !r4,#.HIBYTE. BlockLength
                Ld      !r5,#.LOWBYTE. BlockLength
                
Blk_Move:       Lde     !r6,@!!r0
                Lde     @!!r2,!r6
                Incw    !!r0
                Incw    !!r2
                Decw    !!r4
                Jr      Nz,Blk_Move
                
                Call    Ext_Pop
                Ret
                
                .LSTOFF
                .DO     External
                .LSTON
                .Page
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;>
;>      Function: UpDate_Hdr  { UpDate Header }
;>
;>      This function is responsible for comparing the header information
;>      in the buffer ( it is assumed that a ReadHdr operation has just
;>      been performed and that the header info is currently residing in
;>      the ReadHdr buffer space ) to that of the global cylinder variable.
;>      If the two do not match, then Cur_Cyl is updated to reflect the
;>      ReadHdr operations findings and the function returns a False
;>      value indicating that a seek is in order.
;>
;>      Inputs: { none }
;>
;>      Outputs:
;>              UpDate_Hdr : BOOLEAN { zero flag is true if off-track }
;>
;>      Global Variables Used:
;>              Cylinder
;>
;>      Global Variables Changed:
;>              On_Track, Cur_Cyl
;>
;>      Algorithm:
;>
;>      BEGIN
;>       DiskStatus.On_Track := False
;>       IF GoodHdr
;>        THEN
;>         IF ( RHHeader.Cylinder <> Cylinder )
;>          THEN
;>              Cur_Cyl := RHHeader.Cylinder
;>              On_Track := False
;>              UpDate_Hdr := False
;>          ELSE
;>              UpDate_Hdr := True
;>              DiskStatus.On_Track := True
;>      END
;>
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                .LSTOFF
                .FIN
                .DO     Internal
                .LSTON
                .Page
                .FIN

UpDate_Hdr:
                Call    Ext_Push
                
                And     DiskStatus,#$FF-On_Track
                
                Call    GoodHdr
                Jr      Z,UpDt_Recal

UpDate_OnTrck:  Ld      Cur_Cyl,!r0     ;Cur_Cyl := RHHeader.Cylinder
                Ld      Cur_Cyl+1,!r1
                
                Xor     !r0,Cylinder ;check for correct cylinder address
                Xor     !r1,Cylinder+1
                Or      !r0,!r1
                Ld      !r0,#0 ;assume failure
                Jr      Nz,UpDate_Err
                
                Or      DiskStatus,#On_Track
                Ld      !r0,#1

UpDate_Err:     Or      !r0,!r0
UpDate_H_End:   Push    Flags
                Call    Ext_Pop
                Pop     Flags
                Jp      Bank_Ret
                
UpDt_Recal:      Ld     !r0,#DataRecal
                Call    Restore
                Ld      !r0,#0
                Jr      UpDate_Err
                
                .LSTOFF
                .DO     External
                .LSTON
                .Page
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;>
;>      Function: Get_Type
;>
;>      This function is responsible for converting Profile-style
;>      requests for the spare table and i.d. blocks into Widget
;>      types and blocknumbers. Get_Type also performs a bounds check
;>      on the blocknumber.
;>
;>      Inputs:
;>              DriverType      : BYTE { !r8 }
;>
;>      Outputs:
;>              BlockType   : BYTE { !r8 }
;>              BlockNumber : 3 BYTES { !rC:E }
;>
;>      Algorithm:
;>
;>      BEGIN
;>       BlockNumber := LogicalBlock
;>       IF ( DriverType = Profile )
;>        THEN
;>              CASE LogicalBlockNumber OF
;>
;>               FFFFFF : BlockType := SpareTable
;>                        BlockNumber := 1
;>
;>               FFFFFE : BlockType := ID
;>                        BlockNumber := 1
;>
;>              OTHERWISE  Type := Data
;>
;>        ELSE Type := Data
;>
;>       IF ( BlockNumber > MaxLogicalBlockNumber )
;>        THEN Abort
;>      END
;>
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                .LSTOFF
                .FIN
                .DO     Internal
                .LSTON
                .Page
                .FIN

Get_Type:
                Call    Load_Logical
                
                Ld      !r0,!r8
                Ld      !r8,#User_Type
                
                Or      !r0,!r0 ;IF ( Driver_Type  = Profile )
                Jr      Nz,Get_Type_Check
                
                 Ld     !r0,#$FF ;IF BlockNumber = $-2...
                 Ld     !r1,#$FF
                 Ld     !r2,#$FE
                Call    Sub3 ;compare
                Or      !r0,!r1
                Or      !r0,!r2
                Jr      Nz,G_T_ChkID
                
                Ld      !r8,#SprTbl_Type
                
G_T_ProCnvrt:   Clr     !rC
                Clr     !rD
                Ld      !rE,#1
                Jr      Get_Type_Check
                
G_T_ChkID:       Ld     !r0,#$FF ;IF BlockNumber = #$-1...
                 Ld     !r1,#$FF
                 Ld     !r2,#$FF
                Call    Sub3 ;compare
                Or      !r0,!r1
                Or      !r0,!r2
                Jr      Nz,Get_Type_Check
                Ld      !r8,#ID_Type
                Jr      G_T_ProCnvrt
                
Get_Type_Check:  Ld     !r0,#HiMaxLogical ;IF ( BlockNumber > MaxLogical )...
                 Ld     !r1,#MidMaxLogical
                 Ld     !r2,#LoMaxLogical
                Call    Sub3 ;compare
                Jr      Uge,Get_Type_End
                
                 Ld     !r0,#2 ;byte 2
                 Ld     !r1,#Illegal_Block
                Call    SetStatus
                
                Call    Abort
                
Get_Type_End:   Jp      Bank_Ret

                .LSTOFF
                
