	cpu Z8601
	page 0
	include stddefZ8.inc

;********************************************************************
;
;              Apple Widget Controller - EPROM firmware
;                      Version 341-0289-D (1A45)
;            original code (c) by Apple Computer Inc., 1983
;          'Widget-1O: System-Code: Rev:1-A.4.5 Dec 31, 1983'
;
;
;                    disassembly by Al Kossow, 2011
;  commented and converted into as source by Patrick Schaefer, 2011
;
;    use as build 2011-08-02 or later. Last changes: 2011-08-11 PS
;
;********************************************************************

DontListIncls	SET 1			; set to skip listing for .inc

W_10MB		SET 1			; choose your disk capacity
W_20MB		SET 0
W_40MB		SET 0
DevSubType	SET 0			; 0 = System, 1 = Diag Firmware

HiRevNumber	SET 01Ah		; not used here but good to know
LoRevNumber	SET 045h
ROMsize		SET 8192
Checksum0	SET 0D47Ch		; for ROM test
Checksum1	SET 0EF78h
Passwrd1	SET 0F078h		; upper nibble high shifted right
Passwrd2	SET 03C1Eh


; this source contains Bank0.Assem (0000), Format.Assem (00C5), Cmnd.Assem
; (0195), Spare.Assem (0350), Spare1.Assem (046E), Cache.Assem (063A),
; Data.X.Assem (06B1), SprUtils.Assem (0786), Cmnd0.Assem (0818), Cmnd1.Assem
; (0999), Cmnd2.Assem (0C74), Write.Assem (0DB8), Read.Assem (0EA9),
; Utils.Assem (0F8D), and Srvo0.Assem in the lower 4 kB.
;
; Bank1.Assem (1000), RdHdr.B1.Assem (1007), Utils.B1.Assem (    ),
; Spr1.B1.Assem (    ), Math.B1.Assem (1565), Srvo1.B1.Assem (1605),
;  , Srvo2.B1.Assem (1837), SlfTst.B1.Assem (1A22),
; Cache.B1.Assem (1B41), Progs.B1.Assem (1CC1), Ecc.B1.Assem (1F06) are
; located in the upper 4kB.

	include defs.inc		; Defs1.Assem, Defs2.Assem
	include vectors0288.inc		; addresses referred to in Z8 ROM


;********************************************************************
; Module Bank0.Assem
;
;********************************************************************
	org 0000h
	phase 1000h		; EPROM will start at 4k
	assume RP:Wrk_Sys
		DW Checksum0
		DB 0			; bank 0
PassWord	DW Passwrd1, Passwrd2
FmtDelay	DB 01Fh			; busy wait for a while
RWI_Value	DW RWI_Cylinder
;
; bank 0 vector table
B0_VctTab
Start_Vector	jp Start_Command
LL_Vector	jp Load_Logical
LH_Vector	jp Load_Header
RdL_Vector	jp Rd_Leave
CS_Vector	jp ClrNormStat
LdPw_Vector	ld R2, #Load_PassWord /256
		ld R3, #Load_PassWord #256
		call Bank_Call
		ret
Free_Vector	ld R2, #Strt_FreeProcess /256
		ld R3, #Strt_FreeProcess #256
		call Bank_Call
		ret
ExtStk_Vector	ld R2, #Init_ExtStack /256
		ld R3, #Init_ExtStack #256
		call Bank_Call
		ret
ZrRd_Vector	ld R2, #Zero_RdBuf /256
		ld R3, #0B6h		; points to ret, should be "Zero_RdBuf #256"!
		call Bank_Call
		ret
ClrStat_Vector	ld R2, #ClearStatus /256
		ld R3, #ClearStatus #256
		call Bank_Call
		ret
SlfTst_Vector	ld R2, #SelfTest /256
		ld R3, #SelfTest #256
		call Bank_Call
		ret
SprTbl_Vector	ld R2, #Load_SprTbl /256
		ld R3, #Load_SprTbl #256
		call Bank_Call
		ret
LC_Vector	ld R2, #Load_Cache /256
		ld R3, #Load_Cache #256
		call Bank_Call
		ret
Scan_Vector	ld R2, #D_Scan /256
		ld R3, #D_Scan #256
		call Bank_Call
		jp Rd_Leave
IScan_Vector:	ld R0, #DevSubType	; check for SystemCode
		or R0, R0
		jr NZ, IScan_Ret
		tm Excpt_Stat, #PwrRst	; do scan only after power reset
		jr Z, IScan_SprChk
		ld R2, #Scan /256
		ld R3, #Scan #256
		call Bank_Call
IScan_SprChk	ld R2, #Chk_SprCnt /256
		ld R3, #Chk_SprCnt #256
		call Bank_Call
IScan_Ret	ret
WrBlk_Vector	call WriteBlock
VctrB0_Ret	jp Bank_Ret
RdBlk_Vector	call ReadBlock
		jr VctrB0_Ret
SC_Vector	call SrchCache
		jr VctrB0_Ret
Seek_Vector	ld R2, #Seek /256
		ld R3, #Seek #256
		call Bank_Call
		jp Bank_Ret
ExtPush_Vector	ld R2, #Ext_Push /256
		ld R3, #Ext_Push #256
		call Bank_Call
		ret
ExtPop_Vector	ld R2, #Ext_Pop /256
		ld R3, #Ext_Pop #256
		call Bank_Call
		ret
;
DeviceParams
	if W_10MB
		DB "Widget-10    "
		DB 00, 01, 000h+DevSubType
		DB HiRevNumber, LoRevNumber
		DB 000h, 04Ch, 000h	; number of logical blocks
		DW 532			; number of bytes per block
		DW NbrTracks		; number of cylinders is 514
		DB NbrHds		; number of heads is 2
		DB NbrSctrs		; NbrSctrs
		DB 0, 0, 76		; number of spare possible
	endif
	if W_20MB
		DB "Widget-20    "
		DB 00, 01, 010h+DevSubType
		DB HiRevNumber, LoRevNumber
		DB 000h, 098h, 000h	; number of logical blocks
		DW 532			; number of bytes per block
		DW NbrTracks		; number of cylinders is 514
		DB NbrHds		; number of heads is 2
		DB NbrSctrs		; NbrSctrs
		DB 0, 0, 76		; number of spare possible
	endif
	if W_40MB
		DB "Widget-40    "
		DB 00, 01, 020h+DevSubType
		DB HiRevNumber, LoRevNumber
		DB 001h, 030h, 000h	; number of logical blocks
		DW 532			; number of bytes per block
		DW NbrTracks		; number of cylinders is 514
		DB NbrHds		; number of heads is 2
		DB NbrSctrs		; NbrSctrs
		DB 0, 0, 76		; number of spare possible
	endif
Dev_Parm_Length	EQU $ - DeviceParams



;********************************************************************
; Module Format.Assem
;
; This module contains all but the very most primitive of
; procedures that concern themselves with performing a format
; operation.
;
;********************************************************************

;********************************************************************
;
; Procedure: FormatTrack
;
;  This function is responsible for formatting the track at which
;  the heads are currently positioned.
;
; Inputs: Offset:     BYTE (R4)
;         InterLeave: BYTE (R5)
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   For i:=1 To Length(FormatArray) Do
;    FormatArray[i]:=0
;   While not(index) Do Begin End
;   Turn on AC erase {FmenL}
;   If IndexMark
;    Then
;     Turn off AC erase
;     While (Offset>0) Do
;      While not(SectorMark) Do Begin End
;      While SectorMark Do Begin End
;      Offset:=Offset-1
;     InterleaveFactor:=InterLeaveTable[InterLeave]
;     Sector:=0
;     For i:=1 To NbrSctrs Do
;      If not(FormatBlock) Then Abort
;      Sector:=(Sector+InterLeaveFactor) mod NbrSctrs
;  End
;
;********************************************************************

	if W_10MB
InterTable	DB 2, 1, 7, 10, 8, 13, 3	; interleave table
	elseif
InterTable	DB 2, 1, 26, 10, 8, 13, 22	; interleave table
	endif

FormatTrack	Ld R2,#Zero_RdBuf /256
		Ld R3,#Zero_RdBuf #256
		Call Bank_Call
		Or DiskStat,#Wr_Op
; do one revolution AC erase
FmtT_1		Tm P3, #IndexMark	; while not index
		jr Z, FmtT_1
		and P0, #0FFh-AcEraseL	; turn on AC erase
FmtT_2		tm P3, #IndexMark	; while index
		jr NZ, FmtT_2
FmtT_3		tm P3, #IndexMark	; while not index
		jr Z, FmtT_3
		or P0, #AcEraseL	; turn off AC erase
		or R4, R4		; test for zero offset
		jr Z, Fmt_Begin
; skip the first (offset) sectors
Fmt_Off1	And IRQ, #0FFh-Irq_Sector	; clear old sector mark
Fmt_Off2	tm IRQ, #Irq_Sector	; wait for next mark to pass
		jr Z, Fmt_Off2
		djnz R4, Fmt_Off1
; and here we go
Fmt_Begin	ld R2, #InterTable /256
		ld R3, #InterTable #256
		add R3, R5		; get offset into table
		adc R2, #0
		ldc R6, @RR2
		clr Sector		; sector := 0
		ld R5, #NbrSctrs
FmtTrk_2	ld R1, #Dmt_FmtTrack
		call FormatBlock	; write one sector
		jr NZ, FmtT_Until	; problems?
		ld R9, R0		; pass reason for abort
		call Abort
FmtT_Until	add Sector, R6		; sector := sector + InterLeaveFactor
		cp Sector, #NbrSctrs	; sector := sector mod NbrSctrs
		jr LT, FmtT2_Until
		sub Sector, #NbrSctrs	; do mod by subtraction
FmtT2_Until	djnz R5, FmtTrk_2
		jp Bank_Ret


;********************************************************************
;
; Procedure: LocateSector
;
;  This procedure returns to the caller just after the sector
;  mark representing the sector JUST BEFORE the sector of the
;  last seek address.
;
; Inputs: none
;
; Outputs: none
;
; Global Variables Used: Sector
;
; Algorithm:
;
;  Begin
;   Offset:=SpareTable.FmtOffset
;   InterL:=SpareTable.InterLeave
;   SectorCount:=0
;   Temp:=0
;   While not(Index) Do Begin End
;   While (Offset<>0) Do
;    Wait for End of Sector Mark
;   While (Temp mod NbrSctrs <> Sector) Do
;    Temp:=Temp+InterL
;    i:=i+2 {count sectors at 2:1 interleave}
;   While (i<>0) Do
;    Wait for End of Sector Mark
;    i:=i+1
;  End
;
;********************************************************************

LocateSector	ld R0, #Dmt_LctSctr
		call Set_Dmt
		push RP			; save context
		srp #Wrk_Sys2
	assume RP:Wrk_Sys2
		ld R2, #FmtOffset /256
		ld R3, #FmtOffset #256
		lde R4, @RR2		; load offset value
		or Head, Head		; check for even/odd head
		jr Z, Lct_Offset
		swap R4
Lct_Offset	and R4, #0Fh
		incw RR2		; point to interleave value
		lde R5, @RR2
		ld R2, #InterTable /256
		ld R3, #InterTable #256
		add R3, R5		; add in interleave value
		adc R2, #0
		ldc R5, @RR2		; get real interleave value
;
LCtSctr1	tm p3, #IndexMark	; while index do begin end
		jr NZ, LctSctr1
LctSctr2	tm p3, #IndexMark	; while not index do begin end3
		jr Z, LctSctr2
		and IRQ, #0FFh-Irq_Sector	; clear any old sector marks
		or R4, R4		; if offset = 0 then we're already here
		jr Z, LctSctr4
		ld R2, R4
		call LctSctr3
LctSctr4	or Sector, Sector	; if sector = 0 then we're already there
		jr Z, LctDone
		clr R2
		clr R4
LctSctr5	cp R4, Sector		; see if we've found the sector
		jr Z, LctSctr6
		add R2, #2
		add R4, R5		; tem := temp mod NbrSctrs
		cp R4, #NbrSctrs
		jr LT, LctSctr5
		sub R4, #NbrSctrs
		jr LctSctr5
LctSctr6	call LctSctr3
LctDone		call Clr_Dmt
		pop RP			; restore original context
		jp Bank_Ret
;
LctSctr3	tm IRQ, #Irq_Sector	; wait for next sector
		jr Z, LctSctr3
		and IRQ, #0FFh-Irq_Sector	; mask old interrupt
		djnz R2, LctSctr3	; count the number of sectors in offset
		ret



;********************************************************************
; Module Cmnd.Assem
;
; This module controls the way in which commands received by
; the Host are executed. Its main responsibility is to determine
; whether a command string is protected by a check byte (the
; original ProFile commands are not) and then decode the command
; and pass control over to the correct driver routine to
; execute the command. All exception handling at this level is
; controlled by the individual command routines.
;
;********************************************************************

;********************************************************************
;
; Procedure: StartCommand
;
;  Assumes a command string in external memory; the
;  command byte must be in location $0000 (external)
;
; Algorithm:
;
;  Begin
;   Initialize internal and external stack ptrs
;   ZeroHeader
;   Excpt_Stat.Buf_Damage:=false
;   Excpt_Stat.Nzero_Stat:=false
;   If Cache_Index = Cahe_Length
;    Then Cache_Index:=0
;   OpCode:=ExtMem[0]
;   If OpCode.CommandType is FreeProcess type
;    Then goto Strt_FreeProcess
;   If OpCode.CommandType is protected by a checkbyte
;    Then
;     If not(Check_Command_String)
;      Then Abort(Cmnd_Driver_Exception,
;                 Start_Command, CheckByte_Mismatch)
;   If OpCode.Instruction > CommandLimit[CommandType]
;    Then Abort(Cmnd_Driver_Exception,
;               Start_Command, IllegalOpCode, OpCode)
;   JMP @Command^[CommandType].RoutineTable[Instruction]
;  End
;
;********************************************************************

Start_Command	srp #Wrk_Sys		; get into a reasonable state
	assume RP:Wrk_Sys
		clr SPH
		ld SPL, #Stack_Top
		ld R2, #StackPtr /256	; init external stack
		ld R3, #StackPtr #256
		ld R0, #TopOfStack /256
		lde @RR2, R0
		incw RR2
		ld R0, #TopOfStack #256
		lde @RR2, R0
		cp Cache_Index, #CacheLength
		jr LT, St_Load_Cmnd
		clr Cache_Index
St_Load_Cmnd	ld R2, #Cmnd_Ptr /256
		ld R3, #Cmnd_Ptr #256
		ld R14, #CStatus4 /256
		ld R15, #CStatus4 #256
		ld R1, #8		; move 8 bytes
St_L_Lp		lde R0, @RR2
		lde @RR14, R0
		incw RR2
		incw RR14
		djnz R1, St_L_Lp
; check for $F0 command which means 'do nothing, go idle'
Strt_ZH		ld R14, #CStatus4 /256
		ld R15, #CStatus4 #256
		lde R4, @RR14
		ld R6, R4
		and R6, #CmndType
		cp R6, #0F0h		; check for 'free bus command'
		jr NZ, Start_ChkDiag
		ld R2, #Strt_FreeProcess /256
		ld R3, #Strt_FreeProcess #256
		call Bank_Call
; commands starting with $10 are diagnostic commands
Start_ChkDiag	cp R6, #10h		; check for diagnostic command
		jr NZ, Strt_Swap
		ld R2, #Set_SeekNeeded /256	; invalidate cache
		ld R3, #Set_SeekNeeded #256
		call Bank_Call
		ld Seek_Type, #Access_Offset
Strt_Swap	swap R6			; r6 := CommandType
		jr Z, Chk_Inst		; jump if no check byte in string
;
		ld R8, R4
		and R8, #0Fh		; get length of command string
		incw RR14		; get opcode
		lde R4, @RR14
		ld R14, #Cmnd_Ptr /256
		ld R15, #Cmnd_Ptr #256
		call Chk_Chk_Byte
		jr Z, Chk_Inst
		call Abort
;
Chk_Inst	cp R6, #Max_Cmnd_Types	; check for illegal type
		jr GT, Inst_Abort
		or SlfTst_Result, SlfTst_Result	; check if we're not healthy
		jr Z, Chk_Limits
		cp R6, #1		; only allow diagnostic commands
		jr Z, Chk_Limits	;  when selftest failed
		call Abort
;
Chk_Limits	ld R14, #Cmnd_Limits /256
		ld R15, #Cmnd_Limits #256
		add R15, R6		; get offset into table
		adc R14, #0
		ldc R0, @RR14		; get limit
		ld R1, R4		; get instruction
		cp R1, R0
		jr LE, Ok_Inst		; jump if legal instruction
;
Inst_Abort	call Abort
;
Ok_Inst		ld R14, #Cmnd_Ptrs /256
		ld R15, #Cmnd_Ptrs #256
		rl R6
		add R15, R6		; get offset into table
		adc R14, #0
		ldc R12, @RR14		; get inst routine ptr
		incw RR14
		ldc R13, @RR14
		rl R1
		add R13, R1		; get offset into table
		adc R12, #0
		ldc R14, @RR12		; get address to routine
		incw RR12
		ldc R15, @RR12
		jp @RR14		; jump to routine

;********************************************************************
:
; Command Routine Tables
;
;********************************************************************

Cmnd_Limits	DB Pro_Cmnds
		DB Diag_Cmnds
		DB Sys_Cmnds

Cmnd_Ptrs	DW Pro_Inst
		DW Diag_Inst
		DW Sys_Inst

Pro_Inst	DW Pro_Read
		DW Pro_Write
		DW Pro_WrVer

Diag_Inst	DW D_Read_ID		; cmnd 0
		DW Read_CStatus		; cmnd 1
		DW Read_SStatus		; cmnd 2
		DW Send_ServoCmnd	; cmnd 3
		DW Send_Seek		; cmnd 4
		DW Send_Restore		; cmnd 5
		DW Set_Recovery		; cmnd 6
		DW 0000Ch		; cmnd 7 = Soft_Reset
		DW Send_Park		; cmnd 8
		DW D_Read		; cmnd 9
		DW D_ReadHdr		; cmnd A
		DW D_Write		; cmnd B
		DW Store_Map		; cmnd C
		DW D_Read_SprTbl	; cmnd D
		DW Wr_SprTbl		; cmnd E + password
		DW Format		; cmnd F + password
		DW D_Init_SprTbl	; cmnd 10 + password
		DW Read_Abort		; cmnd 11
		DW D_RstSrvo		; cmnd 12
		DW Scan_Vector		; cmnd 13

Sys_Inst	DW Sys_Read
		DW Sys_Write
		DW Sys_WrV



;********************************************************************
;
; Procedure: Pro_Read {ProFile read}
;
;  This procedure emulates a ProFile read operation.
;
; Inputs: none
;
; Outputs: (none)
;
; Local Variables: BlockStatus: BYTE {R4}
;                  BlockNumber: 3 BYTES {R12:14}
;
; Global Variables Changed: LogicalBlockNumber
;
; Algorithm:
;
;  Begin
;   ClearStatus
;   LogicalBlockNumber:=Command.LogicalBlockNumber
;   Ack_Read(Pro_Read_Response)
;   BlockType, BlockNumber:=Get_Type(ProFile)
;   Seek_Type:=Access {seek without Auto-Offset}
;   SearchStatus, Element.Ptr:=OverLappedSeek(Read, BlockType,
;                                             BlockNumber, 0)
;   If not(Read_Common)
;    Then Data_Ex_Handler(Read_Common.ErrorType)
;    Else
;     If BlkStat.SprCode=Bad_Block
;      Then
;       BlockMove(Buf2Array, RDummy)
;       Data_Ex_Handler(SpareBlock)
;   Move4(StatusArray, Status)
;   Clr_Bsy(StatusArray)
;  End
;
;********************************************************************

Pro_Read	ld Wrk_lo+10,#Read_Response
		call Ack_Read
;
		call ClrNormStat
		ld R0, #Pro_Log_Offset
		call Ld_LgclBlk		; LogicalBlockNumber := ...
		ld R8, #ProFile
		ld R2, #Get_Type /256
		ld R3, #Get_Type #256
		call Bank_Call
		cp R8, #SprTbl_Type 	; spare table requested?
		jp Z, Read_SprTbl
		cp R8, #ID_Type		; ID block requested?
		jp Z, Read_ID
; get 'real' data block
		ld Seek_Type, #Access
		ld Data_Type, #User_Type
		and DiskStat, #0FFh-Wr_Op-Long_Seek
		ld R2, #OverLap /256
		ld R3, #OverLap #256
		call Bank_Call
		call Read_Common
Pro_Rd_Exit	jr Z, ProRd_Err
		tm BlkStat, #B_Block	; check if block read was a bad block
		jr Z, Rd_Leave
;
Pro_Rd_BB	call Pro_Rd_BB1
ProRd_Err	call Data_Ex_Handler
;
Rd_Leave	ld Wrk_lo+10, #Init_Response
		clr Wrk_lo+11		; Cmnd_Pending, IBsy:=false
Rd_Leave2	call Ld_Stand_Stat
Rd_Leave1	ld Wrk_lo+12, #StatusArray /256
		ld Wrk_lo+13, #StatusArray #256
		jp Clr_Bsy
;

Pro_Rd_BB1	LD R2, #RBuf_To_Buf2 /256
		LD R3, #RBuf_To_Buf2 #256
		call Bank_Call
		call Rd_SprBlock
		ret
;********************************************************************
Ld_Stand_Stat	LD R2, #CStatus0 /256
		ld R3, #CStatus0 #256
		ld R14, #StatusArray /256
		ld R15, #StatusArray #256
		call Move4_B0
		ret


;********************************************************************
;
; Function: Read_Common
;
;  This function is responsible for all the common functions
;  performed by all read commands.
;
; Inputs: none
;
; Outputs: Read_Common:     BOOLEAN {zero flag, true if data exception}
;          ErrorCode.Error: BOOLEAN {R0/Bit 7}
;          ErrorCode.Type:  7 BITS {R0/Bits 6:0}
;
; Local Variables: Retry:     BYTE {R5}
;                  ErrorCode: BYTE {R4}
;
; Algorithm:
;
;  Begin
;   ErrorCode.Error:=false
;   If not(ReadBlock)
;    Then
;     ErrorCode.Error:=true
;     ErrorCode.Type:=Undetermined
;     If Recovery
;      Then
;       If ServoError Or NoHeaderFound
;        Then
;         ErrorCode.Error:=false
;         Retry:=4
;         Repeat
;          ServoRecovery
;          Retry:=Retry-1
;         Until not(Retry=0) Or ReadBlock
;       If not(ReadBlock)
;        Then
;         ErrorCode.Error:=true
;         ErrorCode.Type:=Undetermined
;       If ServoError
;        Then
;         SetStatus(Byte0, Status_Servo_Error)
;         Abort
;       If NoHeaderFounf
;        Then ErrorCode.Type:=BadBlock
;        Else
;         If ErrCnt>0
;          Then
;           If ReadErrorCount>2 And ReadErrorCount<10
;            Then ErrorCode.Type:=Spare.Block
;            Else
;             If ReadErrorCount=10
;              Then
;               If ECC
;                Then ErrorCode.Type:=SpareBlock
;                Else ErrorCode.Type:=BadBlock
;          Else ErrorCode.Type:=ReadError
;   ReadCommon:=not(ErrorCode.Error)
;  End
;
;********************************************************************

Read_Common	and DiskStat, #0FFh-Wr_Op
		clr R4
		call ReadBlock
		jr NZ, Rd_Cmn_End
		ld R4, #RdError		; ErrorCode:=Error, undetermined
		tm Excpt_Stat, #Recovery	; If recovery Then ...
		jr Z, Rd_Cmn_End
		ld R5, #4		; Retry:=4
Rd_Cmn_Lp	tm RdStat, #RdSrvoErr+RdNoHdrFnd
		jr Z, Rd_Cmn_Crct
;
		ld R2, #SrvoRcvry /256
		ld R3, #SrvoRcvry #256
		call Bank_Call
		call ReadBlock
		clr R4
		jr NZ, Rd_Cmn_End
		djnz R5, Rd_Cmn_Lp
;
Rd_Cmn_Servo	tm RdStat, #RdSrvoErr
		jr Z, Rd_Cmn_Hdr
		clr R0			; Byte 0
		ld R1, #Stat_Srvo
		call SetStatus
		call Abort
;
Rd_Cmn_Hdr	tm RdStat, #RdNoHdrFnd
		jr Z, Rd_Cmn_Crct
		ld R4, #Error+Ex_HdrBad
		jr Rd_Cmn_End
;
Rd_Cmn_Crct	ld R0, RdErrCnt
		and R0, #0Fh		; mask off unwanted status
		cp R0, #SprThresh
		jr LE, Rd_Cmn_RdErr
		cp R0, #10
		jr Z, Rd_BadBlock
Rd_SprBlock	ld R4, #Error+Ex_SprBlock
		jr Rd_Cmn_End
;
Rd_Cmn_RdErr	ld R4, #Error+Ex_ReadErr
		jr Rd_Cmn_End
;
Rd_BadBlock	ld R4, #Error+Ex_BadBlock
;
Rd_Cmn_End	ld R0, R4
		tcm R0, #RdError
		jp Bank_Ret



;********************************************************************
; Module Spare.Assem
;
; This module contains all the routines that pertain
; to the management of the spare table.
;
;********************************************************************

;********************************************************************
;
; Procedure: SprBlock {spare a block}
;
;  This procedure is responsible for relocating a logical
;  block (note that the block type can be USER or SPARETABLE)
;  from either the user data area or the spare area to
;  some location within the spare area.
;
;  This procedure is capable of gobbling up ALL available spare
;  block space and will ABORT if no space is available.
;
; Inputs: SpareType: BIT {R10/Bit 4}
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   If SpareType=Spare
;    Then
;     If BlkStat.SpareCode=BadBlock
;      Then
;       DeleteSpare
;       SpareCount(Dec_BadCnt)
;     BlockMove(WBuffer1, Buffer2)
;     If not(WrVer_Common)
;      Then SpareBlock(SpareType)
;    Else SpareBlock(BadBlockType)
;   UpDate_SprTbl
;   BlockMove(Buffer2, RBuffer1)
;  End
;
;********************************************************************

SprBlock	tm R10, #Spare		; If SpareType=Spare
		jr Z, SprBlk_1
;
		call Get_Spr_Code
		cp R0, #B_Block
		jr NZ, SprBlk_2
;
		call DeleteSpare
		ld R0, #Dec_BadCnt
		ld R2, #SpareCount /256
		ld R3, #SpareCount #256
		call Bank_Call
SprBlk_2	call ReSeek
		ld R4, #10		; bang on this block for a while
		ld R5, #0		; init error count
Spr_WrVer	ld R2, #Buf2_To_WrBuf /256
		ld R3, #Buf2_To_WrBuf #256
		call Bank_Call
		ld RdErrCnt, #10	; assume failure
		call WrVer_Common
		jr NZ, SprWrV_1
		or WrStat, WrStat	; test WrStat
		jr NZ, SprBlk_1
		ld R0, RdErrCnt
		and R0, #0Fh		; mask off status info
		jr Z, SprBlk_1
		cp R0, #SprThresh
		jr GT, SprBlk_1		; spare the block
		add R5, R0		; bump cumulative error count
SprWrV_1	djnz R4, Spr_WrVer
		cp R5, #SprThresh	; take a percentage of total reads
		jr LE, SprBlk_3
;
SprBlk_1	call SpareBlock
;
SprBlk_3	ld R2, #UpDate_SprTbl /256
		ld R3, #UpDate_SprTbl #256
		Call Bank_Call
		ld R2, #Buf2_To_RBuf /256
		ld R3, #Buf2_To_RBuf #256
		Call Bank_Call
		ret


;********************************************************************
;
; Procedure: SpareBlock
;
;  This procedure performs the actual sparing.
;
; Inputs: SpareType: BIT {R10/Bit 4}
;
; Outputs: none
;
; Local Variables: SparingASpare: BOOLEAN {R9}
;                  SpareLocation: BYTE {R4}
;                  Error:         BOOLEAN {R5}
;
; Algorithm:
;
;  Begin
;   If BlkStat.SpareCode=BadBlock
;    Then
;     AddSpare(GetNewSpare(Load_Logical), Load_Logical, BadBlock)
;     SpareCount(Inc_BadCnt)
;    Else
;     Repeat
;      If BlkStat.SpareCode=Spare
;       Then
;        Location:=SrchSpTabl(Load_Logical)
;        Ptr:=Get_Ptr(Location)
;        Ptr^.Useable:=false
;      SpareCount(Inc_SprCnt)
;      BlkStat.SpareCode:=SpareBlock
;      Location:=GetNewSpare(Load_Logical)
;      AddSpare(Location, Load_Logical, SpareBlock)
;      Seek_Type:=Access_Offset
;      Seek(Get_Cyl_H_S(MulR0_m(Location)))
;     Until WrVer_Common
;  End
;
;********************************************************************

SpareBlock	ld R0, #1		; byte 1
		ld R1, #Stat_Spare
		call SetStatus
		tm R10, #Spare
		jr NZ, S_Blk_Rpt
;
		call Get_Spr_Code
		cp R0, #B_Block
		jr Z, S_Blk_End
;
		call Load_Logical
		call GetNewSpare
		ld R15, R0
		ld R8, #BadBlock
		call AddSpare
		ld R0, #Inc_BadCnt
		ld R2, #SpareCount /256
		ld R3, #SpareCount #256
		call Bank_Call
		jr S_Blk_End
;
S_Blk_Rpt	call Load_Logical
		call Spr_Enter
		jr NZ, S_Blk_End
		cp R0, #Error+Ex_ReadErr	; check for sparing threshold
		jr NZ, S_Blk_Rpt
S_Blk_End	jp Bank_Ret
;
Spr_Enter	call Get_Spr_Code
		cp R0, #S_Block
		jr NZ, S_Blk_New
		call ExtPush_Vector
		call SrchSpTabl
		push R1
		push FLAGS
		call ExtPop_Vector
		pop FLAGS
		pop R1
		jr NZ, S_Blk_Unuse
		call Abort
;
S_Blk_Unuse	ld R0, R1
		call Get_Ptr
		lde R0, @RR2		; element useable:=false
		and R0, #0FFh-Useable
		lde @RR2, R0
;
S_Blk_New	ld R0, #Inc_SprCnt
		ld R2, #SpareCount /256
		ld R3, #SpareCount #256
		call Bank_Call
		and BlkStat, #0FCh	; mask out the SpareCode stuff
		or BlkStat, #S_Block
		call GetNewSpare
		ld R15, R0
		ld R8, #Spare
		call AddSpare
		ld Seek_Type, #Access_Offset
		ld R0, R15		; get location back
		inc R0			; count 1..76
		ld R2, #MulR0_m /256
		ld R3, #MulR0_m #256
		call Bank_Call
		ld R2, #Get_Cyl_H_S /256
		ld R3, #Get_Cyl_H_S #256
		call Bank_Call
		ld R0, R15
		ld R2, #ReMap_Sector /256
		ld R3, #ReMap_Sector #256
		call Bank_Call
		ld R15, R0
		call Seek_Vector
		cp Data_Type, #User_type
		jr Z, Spr_LdBuf2
		ld R2, #SprChkSum /256	; calculate new checkbyte
		ld R3, #SprChkSum #256
		call Bank_Call
		ld R2, #Spr_To_WrBuf /256
		ld R3, #Spr_To_WrBuf #256
		jr Spr_LdWrBuf
Spr_LdBuf2	ld R2, #Buf2_To_WrBuf /256
		ld R3, #Buf2_To_WrBuf #256
Spr_LdWrBuf	call Bank_Call
		call WrVer_Common
		jp Bank_Ret



;********************************************************************
; Module Spare1.Assem (a continuation of Spare)
;********************************************************************

;********************************************************************
;
; Function: SrchSpTabl {search spare table}
;
;  This procedure is responsible for checking to see if the
;  block number that is passed into it is currently in the
;  spare table. If hte block is found to be in the spare table
;  then the physical block number of the spare block is passed
;  back to the caller, as well as the PTR to the location of
;  spared block's element within the spare table. In any case,
;  a byte of status is always passed back to the caller describing
;  the state of the logical block within the spare table.
;
; Inputs: LogicalBlockNumber: 3 BYTES {R12:14}
;         ElementType:        BYTE {R15}
;
; Outputs: SrchSpTabl:  BOOLEAN {zero flag set if not found in table}
;          PhysicalBlockNumber: 3 BYTES {R12:14}
;          Status:              BYTE {R0}
;          ElementPtr:          BYTE {R1}
;
; Local Variables: HeadPtr: BYTE {R0}
;                  Ptr:     BYTE{R0, offset; RR8, actual Ptr}
;                  Found:   BYTE {R7}
;
; Algorithm:
;
;  Begin
;   Case DiskCapacity Of
;    10MB: k:=256; m:=256
;    20MB: k:=128; m:=512
;    40MB: k:= 64; m:=1024
;    HeadPtr:=Get_HeadPtr
;    If HeadPtr.Nil
;     Then PhysicalBlockNumber:=LogicalBlockNumber +
;                               LogicalBlockNumber DIV k
;     Else
;      Ptr:=HeadPtr.Ptr
;      Ptr:=Ptr*4 {calc offset into spare table}
;      Done:=false
;      Found:=false
;      While not(Done) Do
;       If Ptr^.Used And Ptr^.Useable And
;          Ptr^.Type=Element And
;          Ptr^.Token=LogicalBlockNumber/bits 0:9
;        Then
;         PhysicalBlock:=Ptr^.Location * m
;         Done:=true
;         Found:=true
;        Else
;         Ptr:=Ptr^.Ptr * 4
;         If Ptr^.Nil
;          Then Done:=true
;   Status:=Ptr^.Status
;   ElementPtr:=Ptr
;   If not(Found) Then SrchSpTabl:=false
;  End
;
;********************************************************************

SrchSpTabl	call Get_HeadPtr
		jr Z, NotInTabl
;
		clr R7			; Found:=false
SrchLp		ld ScrReg1, R0		; save current ptr
		call Get_Ptr
		lde R1, @RR2		; get element status
		ld ScrReg0, R1		; save element status
		tm R1, #Used		; If Used
		jr Z, SrchLpElse
		tm R1, #Useable		;  And Useable
		jr Z, SrchLpElse
		ld R0, Data_Type
		tm R1, R0		;  And Type is correct
		jr Z, SrchLpElse
		incw RR2
		lde R1, @RR2
		and R1, #3		;  And Token =
		ld R0, R13		;   LogicalBlockNumber / bits 0:9
		and R0, #3
		cp R0, R1
		jr NZ, SrchLpElse
		incw RR2		; point to bits 0:7 of token
		lde R0, @RR2
		cp R0, R14
		jr NZ, SrchLpElse
		tm ScrReg0, #Spare	; check if BadBlock
		jr NZ, Srch_Spare
		or R7, #Found
		jr Srch_Sp1
;
Srch_Spare	ld R0, ScrReg1
		inc R0			; number of spare blocks 1..76
; ***** INLINE: MulR0_m *****
		clr R14			; Result:= R0 * 256
		ld R13, R0
		clr R12
	if W_20MB || W_40MB
		rlc R14			; Result := Result * 2
		rlc R13
		rlc R12
	endif
	if W_40MB
		rlc R14			; Result := Result * 2
		rlc R13
		rlc R12
	endif
; ***************************
		or R7, #Found
		jr SrchDone
;
SrchLpElse	tm ScrReg0, #Nil	; test if element.Ptr=Nil
		jr NZ, NotInTabl
;
		ld R0, ScrReg1		; get address of current element
		call Get_Ptr
		add R3, #3		; point to next Ptr
		adc R2, #0
		lde R0, @RR2
		jr SrchLp
;
NotInTabl	clr R7			; return Not_Found status
Srch_Sp1	ld ScrRegD, R13
; ***** INLINE: Div3_k *****
		ld R2, R13		; shift right 1 byte
		ld R1, R12
		clr R0
	if W_20MB
		ld R15, #1		; shift left once for DIV 128
	endif
	if W_40MB
		ld R15, #2
	endif
	if W_20MB || W_40MB
		ld R3, R14		; shift left 1 bit
Div3_k_Lp	rlc R3			; move R3 bit 7 into carry flag
		rlc R2			; then shift all 3 bytes
		rlc R1
		rlc R0
		djnz R15, Div3_k_Lp
	endif
; ***************************
		add R14, R2		; PhysicalBlock:=LogicalBlock +
		adc R13, R1		;              LogicalBlock DIV k
		adc R12, R0
		ld R0, R13		; save rollover byte
	if W_10MB
		cp R0, ScrRegD		; check for rollover
	endif
	if W_20MB
		and R0, #0FEh		; mask off MOD 512 bits
		and ScrRegD, #0FEh
		cp R0, ScrRegD		; check for rollover
	endif
	if W_40MB
		and R0, #0FCh		; mask off MOD 1024 bits
		and ScrRegD, #0FCh
		cp R0, ScrRegD		; check for rollover
	endif
		jr Z, SrchDone
		add R14, #1		; otherwise account for rollover
		adc R13, #0
		adc R12, #0
;
SrchDone	or R7, R7		; set status
		ld R0, ScrReg0		; return status
		ld R1, ScrReg1		; return ptr to element
		jp Bank_Ret


;********************************************************************
;
; Function: GetNewSpare
;
;  This function accepts either a physical block number or
;  a logical block number and returns a one byte index into
;  the Spare Table's bit map describing the location of the
;  block that can be used as a spare
;
; Inputs: BlockNumber: 3 BYTES {R12:14}
;
; Outputs: GetNewSpare BYTE {R0}
;
; Global Variables Used: SpareBitMap
;
; Local Variables: Bit:   ScrReg0
;                  Temp1: ScrReg1
;                  Temp2: ScrReg2
;                  NoHis: ScrReg3/bit 7
;                  NoLos: ScrReg3/bit 6
;
; Algorithm:
;
;  Begin
;   Bit:=SrchSpTabl div k {get physical blocknumber divided by
;                          the number of blocks between spares }
;   If SpareBitMap[Bit]=0
;    Then GetNewSpare:=Bit
;    Else
;     NoHis:=false
;     NoLos:=false
;     Temp1:=Bit
;     While not(NoHis) and SpareBitMap[Temp1]=1 Do
;      Temp1:=Temp1+1
;      If Temp1>=76 Then NoHis:=True
;     Temp2:=Bit
;     While not(NoLos) and SpareBitMap[Temp2]=1 Do
;      Temp2:=Temp2-1
;      If Temp2<0 Then NoLos:=true
;     If NoHis and NoLos
;      Then Abort {spare table full}
;      Else
;       If NoHis
;        Then GetNewSpare:=Temp2
;        Else
;         If Temp1-Bit > Bit-Temp2
;          Then GetNewSpare:=Temp2
;          Else GetNewSpare:=Temp1
;  End
;
;********************************************************************

GetNewSpare	call ExtPush_Vector	; save state
	if W_10MB
		ld R4, R13		; Div3_k, store result in R4
	endif
		ld R13, R4
		ld R12, #TestBitMap
		call TSC_BitMap		; If BitMap[Bit]=0 ...
		ld R0, R4		; assume Bit is unused
		jr Z, Gns_End		; jump if Then
;
		ld R5, R4		; Else ...
		clr R7
Gns_Lp1		ld R13, R5		; test for bit map location = 0
		ld R12, #TestBitMap
		call TSC_BitMap
		jr Z, Gns_Lp1End
		inc R5			; bump Temp1
		cp R5, #76
		jr LT, Gns_Lp1
		or R7, #80h		; NoHis:=true
Gns_Lp1End	ld R6, R4
Gns_Lp2		ld R13, R6		; test for bit map location = 0
		ld R12, #TestBitMap
		call TSC_BitMap
		jr Z, Gns_Lp2End
		dec R6
		jr GT, Gns_Lp2
		or R7, #40h		; NoLos:=true
Gns_Lp2End	tcm R7, #0C0h		; If NoHis and NoLos...
		jr NZ, Gns_Chk_Hi
;
		ld R0, #1		; status byte 1
		ld R1, #SprBlk_Hard	; spare table full
		call SetStatus
		call Abort
;
Gns_Chk_Hi	ld R0, R6		; assume NoHis
		tm R7, #80h		; test for NoHis
		jr NZ, Gns_End
;
		ld R0, R5		; assume NoLos
		tm R7, #40h		; test for NoLos
		jr NZ, Gns_End
;
		ld R1, R5
		sub R1, R4		; otherwise find which is closer
		sub R4, R6
		ld R0, R6		; assume Temp2 is closer
		cp R1, R4
		jr NC, Gns_End
		ld R0, R5		; otherwise Temp1 is closer
Gns_End		push R0
		call ExtPop_Vector
		pop R0
		jp Bank_Ret


;********************************************************************
;
; Procedure: AddSpare {add an element to the spare table}
;
;  This procedure is responsible for adding an element to the spare
;  table. It accepts a 1 byte value describing the location within
;  the spare table (it is assumed that the caller has already used
;  GetNewSpare) as well as the LogicalBlockNumber and whether the
;  block being added to the table is a Spare block or a Bad Block.
;
; Inputs: BlockType:          3 BITS {R8/bits 3:1}
;         SpareType:          BOOLEAN {R8/bit4}
;         Location:           BYTE {R15}
;         LogicalBlockNumber: 3 BYTES {R12:14}
;
; Outputs: none
;
; Global Variables Used: SpareCount
;
; Algorithm:
;
;  Begin
;   HeadPtr:=Get_HeadPtr(LogicalBlockNumber)
;   If HeadPtr.Nil
;    Then
;     HeadPtr.Nil:=False
;     HeadPtr.Ptr:=Location
;     SegPtrArray[LogicalBlockNumber/bits 10:16]:= HeadPtr
;    Else
;     Ptr:=HeadPtr.Ptr
;     Ptr:=Get_EoList(Ptr)
;     Ptr^.Nil:=False
;     Ptr^.Ptr:=Location
;   Ptr:=Get_Ptr(Location)
;   Ptr^.Nil:=True
;   Ptr^.Used:=True
;   Ptr^.Useable:=True
;   Ptr^.Spare:=Spare
;   Ptr^.Type:=SpareType
;   Ptr^.Token:=LogicalBlockNumber/bits 0:9
;   TCS_BitMap(Set, Location) {set the bit map location for the add}
;   If SpareType=Spare
;    Then SpareCount:=SpareCount+1
;    Else BadCount:=BadCount+1
;  End
;
;********************************************************************

Addspare	call Get_HeadPtr	; If HeadPtr.Nil ...
		jr NZ, ADS_Else1
;
		and R0, #0FFh-Nil	; Then HeadPtr.Nil:=false
		or R0, R15		;      HeadPtr.Ptr:=Location
		lde @RR2, R0		; create link
		jr ADS_UpDate
;
ADS_Else1:	call Get_EoList		; search till end of list
		and R1, #0FFh-Nil	; Ptr^.Nil:=false
		lde @RR2, R1		; update table
		add R3, #3		; get Ptr^.Ptr
		adc R2, #0
		lde @RR2, R15		; create link
ADS_UpDate	ld R0, R15		; get structure ptr
		call Get_Ptr		; create a read ptr out of it
		ld R0, #Nil+Used+Useable
		or R0, R8		; merge Spare/Bad Block/Type info
		or R0, Data_Type
		lde @RR2, R0
		incw RR2		; point to element.HiToken
		ld R0, R13		; get HiToken
		and R0, #3
		lde @RR2, R0
		incw RR2		; piont to element.LoToken
		lde @RR2, R14		; store LoToken
		ld R12, #SetBitMap
		ld R13, R15
		call TSC_BitMap		; update the bit map
		jp Bank_Ret


;********************************************************************
;
; Procedure: DeleteSpare {delete an element from the spare table}
;
;  This procedure is responsible for deleting an element from the spare
;  table. It accepts a 1 byte value describing the location within
;  the spare table (it is assumed that the caller has already used
;  GetNewSpare) as well as the LogicalBlockNumber.
;
; Inputs: Location:           BYTE {R15}
;         LogicalBlockNumber: 3 BYTES {R12:14}
;
; Outputs: none
;
; Local Variables: Ptr1^.Status: BYTE {ScrRegF}
;
; Global Variables Used: SpareCount
;
; Algorithm:
;
;  Begin
;   Location:=SrchSpTabl(Load_Logical)
;   If not(SrchSpTabl.Found) Then Abort
;   If Get_Head(LogicalBlockNumber).Ptr = Location
;    Then Head.Nil:=true
;    Else
;     Ptr1:=Get_Ptr(Location)
;     If Ptr1^.Nil=true
;      Then Ptr(PreviousElement)^.Nil:=true
;      Else Ptr(PreviousElement)^.Ptr:=Ptr1^.Ptr
;   zero out the deleted element
;   TCS_BitMap(Clear, Location) {free up the bit map location}
;  End
;
;********************************************************************

DeleteSpare	call Load_Logical
		call SrchSpTabl
		ld R15, R1
		jr NZ, Chk_HdPtr
		call Abort
;
Chk_HdPtr	call Load_Logical
		call Get_HeadPtr	; If Get_HeadPtr ...
		ld ScrRegE, R0
		ld R0, R15
		call Get_Ptr
		lde R0, @RR2
		tm R0, #Nil
		jr Z, Chk_Chain
		cp ScrRegE, R15
		jr NZ, Chk_Chain	; Else ...
;
		call Get_HeadPtr
		ld R0, #Nil		; Then Head.Nil:=true
		lde @RR2, R0
		jr Zero_Element
;
Chk_Chain	ld ScrRegF, R0
		or R0, #Nil
		lde @RR2, R0		; break the chain
		ld R0, ScrRegE
		call Get_EoList		; get Ptr(Previous) in ScrReg0,1
		ld R0, ScrRegF		; get the original status back
		tm R0, #Nil		; If Ptr1^.Nil
		ld R2, ScrReg0		; get Ptr(Previous)
		ld R3, ScrReg1
		jr Z, D_Chk_Else	; Else ...
;
		lde R0, @RR2
		or R0, #80h
		lde @RR2, R0
		jr Zero_Element
;
D_Chk_Else	cp R15, ScrRegE
		jr NZ, Get_Previous
		call Get_HeadPtr
		jr Save_Previous
;
Get_Previous	add R3, #3		; get to Ptr(Previous)^.Ptr
		adc R2, #0
Save_Previous	push R2
		push R3
		ld R0, R15
		call Get_Ptr
		add R3, #3		; get to Ptr1^.Ptr
		adc R2, #0
		lde R0, @RR2
		pop R3
		pop R2
		lde @RR2, R0		; Ptr(Previous)^.Ptr:=Ptr1^.Ptr
Zero_Element	ld R0, R15		; get Ptr1 once more
		call Get_Ptr
		ld R0, #0FFh		; initial element
		ld R1, #4		; zero 4 bytes
Zero_E_Lp	lde @RR2, R0
		incw RR2
		djnz R1, Zero_E_Lp
		ld R12, #ClearBitMap
		ld R13, R15
		call TSC_BitMap
		jp Bank_Ret



;********************************************************************
; Module Cache.Assem
;
; This name of this module is a bit misleading: there is no true
; cache implemented at this time. However, there is an attempt made
; at some primitive look-ahead methods to reduce the access time
; of the disk. This module contains those routines that are
; chiefly involved with the look-ahead algorithm.
;
;********************************************************************

;********************************************************************
;
; Function: CnvrtLogical  {convert logical block}
;
;  This function is responsible for converting a logical block
;  number to a physical block number by first searching the spare
;  table and then doing the appropriate arithmetic to arrive at
;  the cylinder, head, and sector value.
;
; Inputs: LogicalBlockNumber: 3 BYTES {R12:14}
;
; Outputs: Cylinder: WORD {R12:13}
;          Head:     BYTE {R14}
;          Sector:   BYTE {R15}
;          Status:   BYTE {R0, returned from search spare table}
;          Ptr:      BYTE {R1, returned from search spare table}
;
; Local Variables: PhysicalBlock: 3 BYTES {R12:14}
;                  Temp:          3 BYTES {R1:3}
;
; Algorithm:
;
;  Begin
;   If LogicalBlock>MaxLogicalBlock Then Abort
;   PhysicalBlock:=SearchSpareTable(LogicalBlock)
;   Cylinder:=PhysicalBlock div (Heads*Sectors)
;   Temp:=PhysicalBlock mod (Heads*Sectors)
;   Head:=Temp div Sectors
;   Sector:=Temp mod Sectors
;  End
;
;********************************************************************

CnvrtLogical
Cnvrtsrch	call SrchSpTabl	; check if logical block is in spare table
		push FLAGS		; save results
		push R0			; save status
		push R1			; save ptr
		ld R2, #Get_Cyl_H_S /256
		ld R3, #Get_Cyl_H_S #256
		call Bank_Call		; get cylinder, head, and sector
		ld R0, R15		; pass physical sector
		ld R2, #ReMap_Sector /256
		ld R3, #ReMap_Sector #256
		call Bank_Call
		ld R15, R0		; R15:=logical sector
		pop R1			; Element.Ptr
		pop R0			; Search Status
		pop FLAGS
		jp Bank_Ret


;********************************************************************
;
; Function: SrchCache
;
;  This function searches the block cache and returns both a
;  boolean variable (indicating whether the block is in the
;  cache and if there is a seek associated with it), as well
;  as a info as to whether a head select may be needed. The
;  cache may be searched sequentially, or randomly (as is the
;  case for multiblock commands).
;
; Inputs: LogicalBlock: 3 BYTES {R12:14}
;         Offset:       BYTE {R0}
;         Working register set must be Wrk_Sys.
;
; Outputs: SrchCache:  BOOLEAN {zero flag is set if block is not
;                               in the cache or if there is a seek
;                               needed to get to the block}
;          HeadSector: BYTE {R0}
;
; Algorithm:
;
;  Begin
;   i:=1
;   SrchCache:=false
;   If CacheArray[Offset].LogicalBlock = LogicalBlock
;    Then
;     Found:=true
;     CacheStat:=CacheStatus[Offset]
;     If not(CacheStat.Seek)
;      Then SrchCache:=true
;  End
;
;********************************************************************

SrchCache	srp #Wrk_Scr
	assume RP:Wrk_Scr
		ld R4, #CacheLength
Srch_C_Lp	ld R10, #CacheArray /256
		ld R11, #CacheArray #256
		ld R0, Cache_Index
		rl R0			; multiply by 4
		rl R0
		add R11, R0		; index into array
		adc R10, #0
		ld R8, #ScrReg0
		ldei @R8, @RR10		; load CacheArray.Logical
		ldei @R8, @RR10
		ldei @R8, @RR10
		xor R0, Wrk_Sys+12	; compare values
		xor R1, Wrk_Sys+13
		xor R2, Wrk_Sys+14
		or R0, R1		; test for zero
		or R0, R2
		jr NZ, Srch_More
;
		ld R8, #CacheStat /256	; get CacheStatus
		ld R9, #CacheStat #256
		add R9, Cache_Index
		inc Cache_Index
		lde R1, @RR8
		ld BlkStat, R1		; save search result
		lde R0, @RR10		; get head/sector info
Srch_C_End	srp #Wrk_Sys		; get back to normal system context
	assume RP:Wrk_Sys
		ld R0, ScrReg0		; pass head/sector info back
		tm BlkStat, #CachSeek	; set seek needed flag
		ret
;
Srch_More	inc Cache_Index
		cp Cache_Index, #CacheLength	; check for cache overflow
		jr LT, Srch_M_1
;
		clr Cache_Index
Srch_M_1	djnz R4, Srch_C_Lp
		ld BlkStat, #CachSeek
		clr Cache_Index
		jr Srch_C_End



;********************************************************************
; Module Data.X.Assem
;
; Data Exception Handling
;
;********************************************************************

;********************************************************************
;
; Procedure: Data_Ex_Handler  {data exception handler}
;
;  This procedure is responsible for cleaning up after some sort
;  of data error (spareing, bad-blocking, etc). It is
;  assumed that if spareing is going to occur that correct data must
;  be residing in Buffer2.
;
; Inputs: ErrorCode: 7 BITS {R0/Bits 6:0}
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   If not(DiskStatus.Wr_Op) Then SetStatus(ReadError)
;   Case ErrorCode Of
;    0: SetStatus(OperationFailed)
;    2: Spare(Spare)
;    4: Spare(BadBlock)
;       SetStatus(OperationFailed)
;    6: SetStatus(ErrorCount)
;    8: SetStatus(NoHeaderFound)
;       Spare(Spare)
;    Otherwise Abort
;  End
;
;********************************************************************

Data_Ex_Handler	push R0			; save error code
		call ExtPush_Vector	; save state
		tm DiskStat, #Wr_Op
		jr Z, Data_Ex_RdErr
		ld R0, #3		; load status byte 3
		ld R1, WrErrCnt
		call SetStatus
		jr Data_Ex_Case
Data_Ex_RdErr	call SS_ReadErr
Data_Ex_Case	ld R2, #Set_SeekNeeded /256	; invalidate cache
		ld R3, #Set_SeekNeeded #256
		call Bank_Call		; update cache on next Pro/Sys command
		pop R1			; get error code back
		and R1, #7Fh		; mask off error flag
		tm R1, #1		; see if error code is odd
		jr NZ, Data_Ex_Abort
		cp R1, #Ex_Case_Max	; bounds check
		jr LE, Data_Ex_Cmnd
Data_Ex_Abort	call Abort
Data_Ex_Cmnd	ld R2, #Ex_Jp_Tbl /256
		ld R3, #Ex_Jp_Tbl #256
		add R3, R1		; add offset
		adc R2, #0
		ldc R14, @RR2		; get routine to execute
		incw RR2
		ldc R15, @RR2
		jp @RR14		; go to routine

Ex_Jp_Tbl	DW X_Undeter
		DW X_Spare
		DW X_BadBlock
		DW X_ReadErr
		DW X_HdrBadBlock
		DW X_HdrSpare

X_Undeter	call SS_OpFail
		jr Except_Return
;
X_Spare		ld R10, #Spare
X_Spr_Call	call SprBlock
		jr Except_Return
;
X_BadBlock	ld R4, #2		; retry 2 times
X_BB_Lp		push R4
		call Bad_Recover
		pop R4
		jr NZ, X_Spare
		djnz R4, X_BB_Lp
		call SS_OpFail
		ld R10, #BadBlock
		jr X_Spr_Call
;
X_ReadErr	call SS_ReadErr
		ld R2, #Buf2_To_RBuf /256
		ld R3, #Buf2_To_RBuf #256
		call Bank_Call
;
Except_Return	call ExtPop_Vector
		jp Bank_Ret
;
X_HdrBadBlock	call Bad_Recover
		jr NZ, X_Spare
		call SS_NoHdr
		call LocateSector
		ld R2, #ReadHdr /256
		ld R3, #ReadHdr #256
		call Bank_Call
		jr Z, X_Hdr_Crct
		ld R2, #RBuf_To_Buf2 /256
		ld R3, #RBuf_To_Buf2 #256
		call Bank_Call
		jr X_Spare
;
X_Hdr_Crct	ld R2, #Ecc /256
		ld R3, #Ecc #256
		call Bank_Call
		jr Z, X_BadBlock
;
X_HdrSpare	call SS_NoHdr
		jr X_Spare


;********************************************************************
;
; Function: Bad_Recover
;
;  This function is called when a block can not be read, either the
;  header can not be found or there is a hard data error. The purpose
;  here is to use the auto offset capabilities of the servo controller
;  and reread the block.
;
; Inputs: none
;
; Outputs: Bad_Recover: BOOLEAN {zero flag set if failure on retry}
;
;********************************************************************

Bad_Recover	tm DiskStat, #Offset_On	; check for auto_offset
		jr NZ, B_Rcvr_Read
		call ReSeek
B_Rcvr_Read	call Read_Common
		jr NZ, Bad_Rcvr_Ret
		cp R0, #Error+Ex_ReadErr
		jr Z, Bad_Rc_Spr
		cp R0, #Error+Ex_BadBlock
		jr Z, Bad_Rc_Ecc
		cp R0, #Error+Ex_SprBlock
		jr Z, Bad_Rc_Spr
		ld R0, #0
Bad_Rc_Set	or R0, R0
		jr Bad_Rcvr_Ret
Bad_Rc_Spr	ld R0, #1
		jr Bad_Rc_Set
Bad_Rc_Ecc	ld R2, #Ecc /256
		ld R3, #Ecc #256
		call Bank_Call
Bad_Rcvr_Ret	ret



;********************************************************************
; Module SprUtils.Assem
;
; This module contains all the primitive utility procedures
;
;********************************************************************

Get_HeadPtr	ld R1, R13
		and R1, #0FCh
		ld R0, R12
		rrc R0
		rrc R1
		rcf
		rrc R1
		ld R2, #SegPtrArray /256
		ld R3, #SegPtrArray #256
		add R3, R1
		adc R2, #0
		lde R0, @RR2
		tcm R0, #Nil
		ret

Get_EoList	ld ScrReg3, R0
		call Get_Ptr
		lde R1, @RR2
		tm R1, #Nil
		jr NZ, Get_EL_Done
		ld ScrReg0, R2
		ld ScrReg1, R3
		add R3, #3
		adc R2, #0
		lde R0, @RR2
		jr Get_EoList
Get_EL_Done	ret

TSC_BitMap 	ld R0, R13
		ld R3, #3
loc_17C2	rcf
		rrc R0
		djnz R3, loc_17C2
		ld R2, #SpareBitMap /256
		ld R3, #SpareBitMap #256
		add R3, R0
		adc R2, #0
		lde R1, @RR2
		and R13, #7
		ld R0, #TestBitMap
		jr Z, loc_17DD
loc_17D9	rr R0
		djnz R13, loc_17D9
loc_17DD	push R0
		tm R12, #TestBitMap
		jr NZ, loc_17F3
		tm R12, #SetBitMap
		jr NZ, loc_17EF
		com R0
		and R1, R0
		jr loc_17F1
;
loc_17EF	or R1, R0
loc_17F1	lde @RR2, R1
loc_17F3	pop R0
		tm R1, R0
		ret

Get_Ptr		ld R2, #SpareTable /256
		ld R3, #SpareTable #256
		push R0
		ld R1, R0
		clr R0
		add R1, R1
		add R1, R1
		adc R0, #0
		add R3, R1
		adc R2, R0
		pop R0
		jp Bank_Ret

Get_Spr_Code	ld R0, BlkStat
		and R0, #S_Block+B_Block
		ret



;********************************************************************
; Module Cmnd0.Assem   {Command Driver 0}
;
;********************************************************************

;********************************************************************
;
; Procedure: Read_ID
;
;  This procedure is responsible for letting the host system
;  know what type of device it is talking to
;
; Inputs: none
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   ClrNormStat
;   ZeroRdBuf
;   ID.DeviceName:=DeviceName
;   ID.DeviceNumber:=DeviceNumber
;   ID.Revision:=RevisionNumber
;   ID.Capacity:=Capacity
;   ID.BlockSize:=BlockSize
;   Move4(StatusArray, Status)
;   Goto Rd_Leave
;  End
;
;********************************************************************

D_Read_ID	call ClrNormStat
		ld Wrk_lo+10, #D_R_ID_Response
		call Ack_Read
;
Read_ID		ld R2, #Zero_RdBuf /256
		ld R3, #0B6h		; points to ret, should be "Zero_RdBuf #256"!
		call Bank_Call
		ld R12, #RBuffer1 /256
		ld R13, #RBuffer1 #256
		ld R14, #DeviceParams /256
		ld R15, #DeviceParams #256
		ld R1, #Dev_Parm_Length
Read_ID_Lp	ldc R0, @RR14
		lde @RR12, R0
		incw RR12
		incw RR14
		djnz R1, Read_ID_Lp
		clr R0
		clr R1
		ld R14, #SprCount /256
		ld R15, #SprCount #256
		lde R2, @RR14		; get spare count
		call Movel_3
		incw RR14		; point to bad block count
		lde R2, @RR14
		call Movel_3
		ld R2, #FmtOffset /256
		ld R3, #FmtOffset #256
		ld R14, R12
		ld R15, R13
		call Move4_B0
		jp Rd_Leave
;
Movel_3		ld R3, #3		; move 3 bytes
		ld R4, RP
Movel_3_Lp	ldei @RR12, @R4
		djnz R3, Movel_3_Lp
		ret


;********************************************************************
;
; Procedure: Read_SprTbl
;
;  This procedure is responsible for passing the spare
;  table on to the host. Note that this information is
;  sent in it's raw form and that it is up to the host
;  to correctly interpret it.
;
; Inputs: none
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   RBuffer1:=SpareArray
;   Move4(StatusArray, Status)
;   Clr_Bsy(StatusArray)
;  End
;
;********************************************************************

D_Read_SprTbl	call ClrNormStat
		ld Wrk_lo+10, #D_R_Spr_Resp
		call Ack_Read
Read_SprTbl	ld R2, #Spr_To_RBuf /256
		ld R3, #Spr_To_RBuf #256
		call Bank_Call
		jp Rd_Leave


;********************************************************************
;
; Procedure: Pro_Write
;
;  This procedure emulates the ProFile write command.
;
; Inputs: none
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   ClrNormStat
;   LoadLogicalBlock(Pro_Log_Offset)
;   GetType(Widget)  {check logical block bounds}
;   Seek_Type:=Access_Offset
;   OverLap(User_Type, Write_Op, Write_Response, BlockNumber, 0)
;   If BlkStat.SprCode=BadBlock
;    Then
;     BlockMove(Buf2Array, WBuffer1-1)
;     Data_Ex_Handler(SpareBlock)
;    Else
;     If not(Wr_Common)
;      Then Data_Ex_Handler(Wr_Common.ErrorCode)
;   Rd_Leave
;  End
;
;********************************************************************

Pro_Write	ld Wrk_lo+10, #Wr_Response
		call Pro_Wr_SetUp
		tm BlkStat, #B_Block	; If BadBlock
		jr Z, ProWr_1
Pro_Wr_BB	call Wr_BBlock
		jr Wr_Leave
ProWr_1		call Wr_Common
ProWr_Exit	jr NZ, Wr_Leave
		call Data_Ex_Handler
Wr_Leave	jp Rd_Leave
;
Pro_Wr_SetUp	call ClrNormStat
		call Get_Wr_Data
		ld R0, #Pro_Log_Offset
		call Ld_LgclBlk		; LogicalBlockNumber:=...
		ld R8, #Widget
		ld R2, #Get_Type /256
		ld R3, #Get_Type #256
		call Bank_Call		; check block bounds
		ld Seek_Type, #Access_Offset
		ld Data_Type, #User_Type
		or DiskStat, #Wr_Op
		ld R2, #OverLap /256
		ld R3, #OverLap #256
		call Bank_Call
		tm DiskStat, #Offset_On
		jr NZ, P_W_S_End
		call ReSeek
P_W_S_End	ret

;********************************************************************
Wr_BBlock	ld R2, #WrBuf_To_Buf2 /256
		ld R3, #WrBuf_To_Buf2 #256
		call Bank_Call
		ld R0, #Error+Ex_SprBlock
		call Data_Ex_Handler
		ret


;********************************************************************
;
; Function: Wr_Common
;
;  This function handles all of the write specific details
;  for all of Widget's write commands.
;
; Inputs: none
;
; Outputs: Wr_Common:       BOOLEAN {zero flag is set if data exception}
;          ErrorCode.Error: BOOLEAN {R0/Bit 7}
;          ErrorCode.Type:  7 BITS {R0/Bits 6:0}
;
; Local Variables: Retry:     BYTE {R5}
;                  ErrorCode: BYTE {R4}
;
; Algorithm:
;
;  Begin
;   ErrorCode.Error:=false
;    If not(WriteBlock)
;     Then
;      ErrorCode.Error:=true
;      ErrorCode.Type:=Undetermined
;       If Recovery
;        Then
;         If ServoError Or NoHeaderFound
;          Then
;           ErrorCode.Error:=false
;           Retry:=4
;           Repeat
;            ServoRecovery
;            Retry:=Retry-1
;           Until not(Retry=0) or WriteBlock
;           If not(WriteBlock)
;            Then
;             ErrorCode.Error:=true
;             ErrorCode.Type:=Undetermined
;           If ServoError
;            Then
;             SetStatus(Byte0, Status_ServoError)
;             Abort
;           If NoHeaderFound
;            Then
;             MoveBlock(Buffer2, WriteBuffer)
;             ErrorCode.Type:=SpareBlock
;   Write_Common:=not(ErrorCode.Error)
;  End
;
;********************************************************************

Wr_Common	ld ScrReg2, #WrBlkFence	/256	; check for host overflow
		ld ScrReg3, #WrBlkFence	#256
		ld R2, #Chk_PassWord /256
		ld R3, #Chk_PassWord #256
		call Bank_Call
		jr NZ, Wr_Cmn_Ok
;
		ld R0, #0		; status byte zero
		ld R1, #WrBuf_OR	; buffer overrun
		call SetStatus
		call Abort
Wr_Cmn_Ok	push R4
		or DiskStat, #Wr_Op
		clr R4
		call WriteBlock
		jr NZ, Wr_Cmd_End
;
		ld R2, #WrBuf_To_Buf2 /256
		ld R3, #WrBuf_To_Buf2 #256
		call Bank_Call
		ld R4, #Error		; ErrorCode:=Error, undetermined
		tm Excpt_Stat, #Recovery	; If Recovery Then...
		jr Z, Wr_Cmd_End
;
		ld R5, #4		; Retry :=4
Wr_Cmn_Lp	ld R2, #SrvoRcvry /256
		ld R3, #SrvoRcvry #256
		call Bank_Call
		ld R2, #Buf2_To_WrBuf /256
		ld R3, #Buf2_To_WrBuf #256
		call Bank_Call
		call WriteBlock
		clr R4
		jr NZ, Wr_Cmd_End
		djnz R5, Wr_Cmn_Lp
		ld R4, #Error
;
Wr_Cmn_Servo	tm WrStat, #WrSrvoErr
		jr Z, Wr_Cmn_Hdr
		clr R0			; status byte 0
		ld R1, #Stat_Srvo
		call SetStatus
		call Abort
;
Wr_Cmn_Hdr	tm WrStat, #WrNoHdrFnd
		jr Z, Wr_Cmd_End
		ld R4, #Error+Ex_HdrSpr
;
Wr_Cmd_End	ld R0, R4
		pop R4			; retrieve register from stack
		tcm R0, #WrError
		jp Bank_Ret


;********************************************************************
;
; Procedure: Pro_WrVer
;
;  This procedure emulates the ProFile write/verify command.
;
; Inputs: none
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   ClrNormStat
;   LoadLogicalBlock(Pro_Log_Offset)
;   GetType(Widget)  {check logical block bounds}
;   Seek_Type:=Access_Offset
;   OverLap(User_Type, Write_Op, WrVer_Response, BlockNumber, 0)
;   If BlkStat.SprCode=BadBlock
;    Then
;     BlockMove(Buf2Array, WBuffer1-1)
;     Data_Ex_Handler(SpareBlock)
;    Else
;     If not(WrVer_Common)
;      Then Data_Ex_Handler(WrVer_Common.ErrorCode)
;   Rd_Leave
;  End
;
;********************************************************************

Sys_WrV		ld Wrk_lo+10, #Sys_WrVer_Resp
		jr Pro_WrVer
Pro_WrVer	ld Wrk_lo+10, #WrVer_Response
Pro_WrV_2	call Pro_Wr_SetUp
		ld R2, #WrBuf_To_Buf2 /256
		ld R3, #WrBuf_To_Buf2 #256
		call Bank_Call
		tm BlkStat, #B_Block	; If BadBlock
		jr Z, Pro_WrV_1
		call Wr_BBlock
		jr WrVer_Leave
Pro_WrV_1	call WrVer_Common
		jr NZ, WrVer_Leave
		call Data_Ex_Handler
WrVer_Leave	jp Rd_Leave


;********************************************************************
;
; Function: WrVer_Common
;
;  This function has the responsibility of making certain (within
;  reasonable limits) thant when a block is written to the disk that
;  it can also be read back. WriteVerify comes in two flavors:
;  Conservative and NotConservative. The attempt here is to make
;  the WriteVerify routine attractive to end users as well as provide
;  a means for the controller to have a high degree of confidence
;  in writing some of it's more important blocks.
;
; Inputs: none
;
; Outputs: WrVer_Common: BOOLEAN {zero flag is set if data exception}
;
; Algorithm:
;
;  Begin
;   If Wr_Common
;    Then
;     DiskStat.Wr_Op:=False
;     Rd_Common
;     If Rd_Common.ErrorType=Ex_HdrBad
;      Then Rd_Common.ErrorType:=Ex_HdrSpr
;      Else
;       If Rd_Common.ErrorType=Ex_BadBlock
;        Then Rd_Common.ErrorType:=Ex_SprBlock
;  End
;
;********************************************************************

WrVer_Common	call ExtPush_Vector
		or DiskStat, #Wr_Op
		call Wr_Common
		jr Z, WrVer_Ret
;
		and DiskStat, #0FFh-Wr_Op
		call Read_Common
		jr NZ, WrVer_Ret
;
		ld R1, R0
		and R1, #7Fh		; mask off error bit
		cp R1, #Ex_HdrBad
		ld R2, #Error+Ex_HdrSpr
		jr Z, WrV_ChgEx
		cp R1, #Ex_BadBlock
		ld R2, #Error+Ex_SprBlock
		jr NZ, WrVer_Ret
WrV_ChgEx	ld R0, R2
WrVer_Ret	push R0
		call ExtPop_Vector
		pop R0
		tcm R0, #Error		; set error condition
		jp Bank_Ret



;********************************************************************
; Module Cmnd1.Assem  {continuation of the command processor module}
;
;********************************************************************

;********************************************************************
;
; Procedure: D_Read
;
;  This procedure is used by the Host to invoke the primitive
;  read function. The block that is read by this routine is the
;  last seek address (cylinder, head, and sector). It is
;  best to turn recovery OFF before using this command - as it
;  is when using all diagnostic commands.
;
; Inputs: none
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   ClrNormStat
;   Ack_Read(D_Read_Response)
;   If not(Read_Common)
;    Then Data_Ex_Handler(Read_Common.ErrorCode)
;   Rd_Leave
;  End
;
;********************************************************************

D_Read		call ClrNormStat
		ld Wrk_lo+10, #D_Read_Response
		call Ack_Read
		ld Data_Type, #User_Type
		and DiskStat, #0FFh-Wr_Op
		call Read_Common
		jr NZ, D_Read_End
		ld R0, #Error
		call Data_Ex_Handler
D_Read_End	jp Rd_Leave


;********************************************************************
;
; Procedure: D_ReadHdr
;
;  This diagnostic command is used to read the header (and whatever
;  the contents may be) of the block pointed to by the last seek
;  address. The host should be cautioned that the buffer that the
;  controller points it to at the completion of the read is longer
;  than a normal read buffer by the length of the header and the
;  data gap.
;
; Inputs: none
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   ClrNormStat
;   Ack_Read(D_RdHdr_Response)
;   Sector:=LdParam1.First
;   LocateSector
;   ReadHdr
;    If ReadHdr.ServoErr Then SetStatus(Byte0, ServoErr)
;    If ReadHdr.CrcErr Then SetStatus(Byte0, ReadError)
;   Move4(RdHdrStatusArray, CStatus0)
;   Clr_Bsy(RdHdrStatusArray)
;  End
;
;********************************************************************

D_ReadHdr	call ClrNormStat
		ld Wrk_lo+10, #D_RdHdr_Resp
		call Ack_Read
		call Ld_Param1
		ld Sector, R0		; load new sector
		ld R2, #LocateSector /256
		ld R3, #LocateSector #256
		call Bank_Call
		ld Data_Type, #User_Type
		and DiskStat, #0FFh-Wr_Op
		ld R2, #ReadHdr /256
		ld R3, #ReadHdr #256
		call Bank_Call
		push R0			; save status byte
		tm R0, #RdHSrvoErr	; If ReadHdr.ServoErr
		jr Z, D_RdH_Crc
		clr R0			; status byte 0
		ld R1, #Stat_Srvo
		call SetStatus
;
D_RdH_Crc	pop R0
		tm R0, #RdCrcErr
		jr Z, D_RdH_End
		clr R0
		ld R1, #Stat_Rd_Err
		call SetStatus
;
D_RdH_End	ld R2, #CStatus0 /256
		ld R3, #CStatus0 #256
		ld R14, #RdH_Stat_Array /256
		ld R15, #RdH_Stat_Array #256
		call Move4_B0
		srp #Wrk_lo
	assume RP:Wrk_lo
		ld R10, #Init_Response
		clr R11
		ld R12, #RdH_Stat_Array /256
		ld R13, #RdH_Stat_Array #256
		jp Clr_Bsy


;********************************************************************
;
; Procedure: D_Write
;
;  This procedure, like D_Read, allows the host to perform
;  a single write to the last seek address. KEEP IN MIND THAT
;  THIS ROUTINE WILL ALLOW YOU TO WRITE ANYWHERE ON THE DISK THAT
;  YOU CARE TO WRITE!!!!
;
; Inputs: none
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   ClrNormStat
;   Get_Wr_Data(D_Write_Response)
;   If not(Write_Common)
;    Then Data_Ex_Handler(Wr_Common.ErrorCode)
;   Rd_Leave
;  End
;
;********************************************************************

	assume RP:Wrk_Sys
D_Write		call ClrNormStat
		ld Data_Type, #User_Type
		or DiskStat, #Wr_Op
		ld Wrk_lo+10, #D_Write_Resp
		call Get_Wr_Data
		call Wr_Common
		jr NZ, D_Write_End
		ld R0, #Error
		call Data_Ex_Handler
D_Write_End	jp Rd_Leave


;********************************************************************
;
; Procedure: Read_CStatus
;
;  This procedure allows the host to read any of the controllers
;  status'. These status' all come in 4 byte quantities:
;
;  Status0: Standard_Statud {sent to host at the completion
;                            of a command - note that a request
;                            for status is NOT a command}
;  Status1: Last_Logical_Block {0, HiByte; MidByte, LoByte}
;  Status2: Last_Seek_Address {HiCylinder, LoCylinder, Head, Sector}
;  Status3: Current_Cylinder {HiCylinder, LoCylinder, 0, 0}
;  Status4: Internal_Stat {Excpt_Stat, DiskStat, BlkStat, 0}
;  Status5: Cntrl_Port {0, SlfTst_Result, Port2, Controller_Port}
;  Status6: Error_Status {RdStat, RdErrCnt, WrStat, WrErrCnt}
;  Status7: Last_Seek_Address {Lst_HiCyl, Lst_LoCyl, Lst_Head,
;                              Lst_Sector}
;
;  {all illegal status requests will return standard status}
;
;********************************************************************

	assume RP:Wrk_Sys
Read_CStatus	ld Wrk_lo+10, #Rd_Stat_Resp
		call Ack_Read
		ld R14, #StatusArray /256
		ld R15, #StatusArray #256
		call Ld_Param1
		and R0, #0Fh		; mask off all but legal status request
		cp R0, #7		; check for except case
		jp GT, Rd_Leave
;
		ld R2, #Stat_Table /256
		ld R3, #Stat_Table #256
		rl R0
		add R3, R0
		adc R2, #0
		ldc R12, @RR2
		incw RR2
		ldc R13, @RR2
		jp @RR12

Stat_Table	DW Rd_Leave
		DW Ld_Stat1
		DW Ld_Stat2
		DW Ld_Stat3
		DW Ld_Stat4
		DW Ld_Stat5
		DW Ld_Stat6
		DW Ld_Stat7

Ld_Stat1	incw RR14
		ld R2, #LogicalBlock /256
		ld R3, #LogicalBlock #256
		call Move4_B0
		jr Ld_Stat_End
;
Ld_Stat2	ld R0, #Cylinder
Ld_Stat2_1	ld R1, #4		; move 4 bytes
Ld_Stat2_Lp	ldei @RR14, @R0
		djnz R1, Ld_Stat2_Lp
		jr Ld_Stat_End
;
Ld_Stat3	ld R0, #Cur_Cyl
		jr Ld_Stat2_1
;
Ld_Stat4	ld R0, Excpt_Stat
		ld R1, DiskStat
		ld R2, BlkStat
		ld R3, Data_Type
		jr Ld_Stat_Reg
;
Ld_Stat5	ld R0, #0
		ld R1, SlfTst_Result
		ld R2, P2		; Port 2
		ld R12, #StatusPort /256
		ld R13, #StatusPort #256
		lde R3, @RR12
		jr Ld_Stat_Reg
;
Ld_Stat6	ld R0, RdStat
		ld R1, RdErrCnt
		ld R2, WrStat
		ld R3, WrErrCnt
		jr Ld_Stat_Reg
;
Ld_Stat7	ld R0, Lst_HiCyl
		ld R1, Lst_LoCyl
		ld R2, Lst_Head
		ld R3, Lst_Sector
;
Ld_Stat_Reg	ld R4, RP
		ld R5, #4		; move 4 bytes
Stat5_Lp	ldei @RR14, @R4
		djnz R5, Stat5_Lp
Ld_Stat_End	ld >Wrk_lo+10, #Init_Response
		clr Wrk_lo+11
		jp Rd_Leave1


;********************************************************************
;
; Procedure: Read_SStatus
;
;  This procedure is used by the host to examine the servo processor's
;  status information. The controller makes NO assumptions concerning
;  knowledge or meaning of the status bits, nor does the controller
;  interpret the status requests sent by the host. In other words,
;  when the host issues a request for status to the servo, it is
;  his responsibility to make certain that this request is meaningful
;
;  The returned status is preceded by Standard Status.
;
;********************************************************************

	assume RP:Wrk_Sys
Read_SStatus	call ClrNormStat
		ld Wrk_lo+10, #Rd_SStat_Resp
		call Ack_Read
		call Ld_Param1
		call Set_Dmt
		ld ScrReg3, R0
		ld ScrReg0, #ReadStatus
		ld ScrReg1, #0
		ld ScrReg2, #0
		ld R2, #ServoStatus /256
		ld R3, #ServoStatus #256
		call Bank_Call
		call Clr_Dmt
		call Ld_Stand_Stat
		ld R2, #SStatus0 /256
		ld R3, #SStatus0 #256
		call Move4_B0
		jr Ld_Stat_End
Ld_Param1	ld ScrReg4, RP
		push RP
		srp #Wrk_Scr
	save
	assume RP:Wrk_Scr
		ld R14, #(CStatus4+2) /256
		ld R15, #(CStatus4+2) #256
		ld R5, #4		; load 4 bytes
Rd_SStat_Lp	ldei @R4, @RR14
		djnz R5, Rd_SStat_Lp
		pop RP			; get back to original context
	restore
		ret


;********************************************************************
;
; Procedure: Send_ServoCmnd
;
;  This procedure is used by the host to send the servo a command
;  of any type EXCEPT a status request (use Read_SStatus for that).
;  The controller will Abort the operation if this servo command
;  is sent to it.
;
;  Also, as in Read_SStatus, the host is responsible for the
;  command string that it sends to the servo - all the Widget
;  controller will do is pass along the command string.
;
;********************************************************************

	assume RP:Wrk_Sys
Send_ServoCmnd	call ClrNormStat
		ld Wrk_lo+10, #Sd_S_C_Resp
		call Ack_Read
		call Ld_Param1		; get servo command
		or R0, R0		; check for Status Command
		jr NZ, S_Scmnd
		call Abort
S_Scmnd		call Set_Dmt
		ld ScrReg0, R0
		ld ScrReg1, R1
		ld ScrReg2, R2
		ld ScrReg3, R3
		ld R2, #ServoCmnd /256
		ld R3, #ServoCmnd #256
		call Bank_Call
		call Clr_Dmt
		jp Rd_Leave


;********************************************************************
;
; Procedure: Send_Seek
;
;  This procedure allows the host to position the heads ANYWHERE
;  on the disk service that it pleases - the only consideration
;  is to be aware of the crash stop positions!
;
;  The form of the parameters passed into the controller are
;  HiCylinder, LowCylinder, Head, Sector. Keep in mind that these
;  values will be used to check for a valid header if and when
;  a state machine operation is initiated.
;
;********************************************************************

Send_Seek	call ClrNormStat
		ld Wrk_lo+10, #S_Seek_Response
		call Ack_Read
		call Ld_Param1		; get parameters from command processor
		ld Seek_Type, #Access
		ld R12, R0		; pass params to Seek
		ld R13, R1
		ld R14, R2
		ld R15, R3
		call New_Seek
		ld R2, #Set_SeekNeeded /256	; invalidate cache
		ld R3, #Set_SeekNeeded #256
		call Bank_Call
		jp Rd_Leave


;********************************************************************
;
; Procedure: Send_Restore
;
;  This procedure allows the host to issue either a Format Recal or
;  a Data Recal to the servo without having to build a primitive
;  servo command. The value of which type of restore is to be
;  performed is passed in with the command string.
;
;********************************************************************

Send_Restore	call ClrNormStat
		ld Wrk_lo+10, #S_Rstr_Response
		call Ack_Read
		call Ld_Param1		; get recal type
		cp R0, #DataRecal
		jr Z, S_Restore
		cp R0, #FrmtRecal
		jr Z, S_Restore
		call Abort
S_Restore	ld R2, #Restore /256
		ld R3, #Restore #256
		call Bank_Call
		jp Rd_Leave


;********************************************************************
;
; Procedure: Set_Recovery
;
;  This procedure is used by the host to set the global
;  variable Recovery either on or off. This variable controls
;  the behaviour of Widget when an exception occurs; in the
;  case of Recovery = true, the controller tries everything it
;  can to correct the exception and continue about it's day.
;  When Recovery = false, however, the controller will do nothing
;  about the problem and report the error to the host.
;
;********************************************************************

Set_Recovery	call ClrNormStat
		ld Wrk_lo+10, #Set_Rcvr_Resp
		call Ack_Read
		call Ld_Param1		; get set/reset param
		or Excpt_Stat, #Recovery	; assume true
		or R0, R0		; test for true or false
		jr NZ, Set_Rcvr_Store
		and Excpt_Stat, #0FFh-Recovery	; clear old bit
Set_Rcvr_Store	jp Rd_Leave


;********************************************************************
;
; Procedure: Send_Park
;
;  This diagnostic command allows the host to issue a Park
;  command to the drive. This command has the effect of
;  sending the drive heads off the data surface.
;
; Inputs: none
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   ClrNormStat
;   Ack_Read(S_Park_Response)
;   Park_Heads
;   Rd_Leave
;  End
;
;********************************************************************

Send_Park	call	ClrNormStat
		ld	Wrk_lo+10, #S_Park_Resp
		call	Ack_Read
		ld	R2, #Park_Heads /256
		ld	R3, #Park_Heads #256
		call	Bank_Call
		jp	Rd_Leave


;********************************************************************
;
; Procedure: Store_Map
;
;  This command allows the host to logically remap (i.e. change
;  the interleave factor through a remapping of the sector addresses)
;  The way in which the Map is downloaded into the SpareTable is
;  by doing a write sequence from the Host to the controller.
;
; Inputs: none
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   Get_Write_Data(St_Map_Response)
;   For i:= 0 To NbrSctrs-1 Do
;    If WriteBuffer[i]>=NbrSctrs Then Abort
;    Map_Table[i]:=WriteBuffer[i]
;   UpDate_SpareTable
;  End
;
;********************************************************************

Store_Map	call ClrNormStat
		ld Wrk_lo+10, #St_Map_Response
		call Ack_Read
		ld R2, #Auto_Offset /256
		ld R3, #Auto_Offset #256
		call Bank_Call
		jp Rd_Leave


;********************************************************************
;
; Procedure: Wr_SprTbl
;
;  This command allows the host to update the spare table
;  without the controller's intervention. BE CAREFUL WITH
;  THIS ONE!
;
; Inputs: none
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   If not(Chk_PassWord(CommandString#1)) Then Abort
;   Ack_Read(Wr_Spr_Response)
;   Clr_Bsy(SpareArray)
;  End
;
;********************************************************************

	assume RP:Wrk_Sys
Wr_SprTbl	ld Wrk_lo+10, #Wr_Spr_Resp
		call Get_Wr_Data
		call ClrNormStat
		ld ScrReg2, #(CStatus4+2) /256
		ld ScrReg3, #(CStatus4+2) #256
		ld R2, #Chk_PassWord /256
		ld R3, #Chk_PassWord #256
		call Bank_Call
		jr NZ, Wr_Spr1
		call Abort
;
Wr_Spr1		ld R2, #WrBuf_To_Spr /256
		ld R3, #WrBuf_To_Spr #256
		call Bank_Call
		ld R2, #UpDate_SprTbl /256
		ld R3, #UpDate_SprTbl #256
		call Bank_Call
		jp Rd_Leave


;********************************************************************
;
; Procedure: Format
;
;  This procedure allows the host to format the track
;  that the heads are currently positioned over.
;
;  BE ULTRA CAREFUL HERE!!!! THIS COMMAND DESTROYS ALL
;  DATA ON THE TRACK.... DATA RECOVERY WILL BE IMPOSSIBLE
;  AFTER A TRACK HAS BEEN FORMATTED.
;
;  The two parameters that are passed into the controller
;  by the host allow the host to specify the offset from
;  index mark (in sectors) that Sector 0 will be located
;  and the interleave factor.
;
; Inputs: none
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   ClrNormStat
;   If not(Chk_PassWord(CStatus4+3)) Then Abort
;   Ack_Read(Fmt_Response)
;   If not(FormatTrack(CStatus4+1, CStatus4+2)
;    Then Abort
;   Rd_Leave
;  End
;
;********************************************************************

	assume RP:Wrk_Sys
Format		ld Wrk_lo+10, #Fmt_Response
		call Ack_Read
		call Chk_FmtParms
		ld R4, R0
		ld R5, R1
		ld R2, #FormatTrack /256
		ld R3, #FormatTrack #256
		call Bank_Call
		jp Rd_Leave
;
Chk_FmtParms	call ClrNormStat
		ld ScrReg2, #(CStatus4+4) /256
		ld ScrReg3, #(CStatus4+4) #256
		ld R2, #Chk_PassWord /256
		ld R3, #Chk_PassWord #256
		call Bank_Call
		jr NZ, Format1
		call Abort		; password mismatch, bye!
;
Format1		call  Ld_Param1
		tm R0, #0FFh-(NbrSctrs-4)
		jr Z, Fmt_Chk_Inter
Format_Abort	ld R9, R0
		ld R10, R1
		call Abort
;
Fmt_Chk_Inter	cp R1, #Max_InterLeave
		jr UGT, Format_Abort
		ret


;********************************************************************
;
; Procedure: Read_Abort
;
;  This command allows the host to get a snapshot of
;  the controller's register set a the time of the last abort.
;  If there has been no abort, the results of this command
;  will be invalid.
;
; Inputs: none
;
; Outputs: none
;
;********************************************************************

Read_Abort	call ClrNormStat
		ld Wrk_lo+10, #Rd_Abrt_Resp
		call Ack_Read
		ld R0, #Abort_Stat /256
		ld R1, #Abort_Stat #256
		ld R2, #RBuffer1 /256
		ld R3, #RBuffer1 #256
		ld R4, #0		; copy 256 bytes
Rd_Abt_Lp	lde R5, @RR0
		lde @RR2, R5
		incw RR0
		incw RR2
		djnz R4, Rd_Abt_Lp
		jp Rd_Leave


;********************************************************************
;
; Procedure: D_RstSrvo
;
;  This command allows the host to perform a servo HW reset.
;
; Inputs: none
;
; Outputs: none
;
;********************************************************************

D_RstSrvo	call ClrNormStat
		ld Wrk_lo+10, #RstSrvo_Resp
		call Ack_Read
		ld R2, #ResetServo /256
		ld R3, #ResetServo #256
		call Bank_Call
		jp Rd_Leave


;********************************************************************
;
; Procedure: D_Init_SprTbl
;
;  This command allows the host to initialize the spare table
;  of the disk.
;
; Inputs: none
;
; Outputs: none
;
;********************************************************************

	assume RP:Wrk_Sys
D_Init_SprTbl	call ClrNormStat
		ld Wrk_lo+10, #I_Spr_Response
		call Ack_Read
		ld ScrReg2, #(CStatus4+4) /256
		ld ScrReg3, #(CStatus4+4) #256
		ld R2, #Chk_PassWord /256
		ld R3, #Chk_PassWord #256
		call Bank_Call
		jr NZ, D_I_SprTbl
		call Abort		; password mismatch, go home
;
D_I_SprTbl	call Ld_Param1
		ld R4, R0		; pass offset value
		ld R5, R1		; pass interleave value
		ld R2, #Init_SprTbl /256
		ld R3, #Init_SprTbl #256
		call Bank_Call
		jp Rd_Leave



;********************************************************************
; Module Cmnd2.Assem  {continuation of the command processor module}
;
; This module contains all the code associated with processing
; the System commands.
;
;********************************************************************

;********************************************************************
;
; Procedure: Sys_Read
;
;  This procedure is the System Read command for Widget. It
;  allows the host to read up to 8 sequential blocks without
;  sending any additional commands. Between each block status
;  is passed back to the host with the data, and the host must
;  set CMD before the next block can be read. In general, the protocol
;  is about the same as in Pro_Read.
;
; Inputs: none
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   Ld_LgclBlk(Wid_Log_Offset)
;   Count:=Ld_Param1.FirstParam
;   Offset:=0
;   BlockNumber:=Load_Logical
;   ClrNormStat
;   Get_Type_Check(BlockNumber+Count)
;   Seek_Type:=Access
;   DiskStat.Wr_Op:=false
;   OverLap
;   If not(ReadBlock)
;    Then
;     SS_RdErr
;     Goto Pro_Rd_Exit
;   While Always Do
;    BlockNumber:=BlockNumber+1
;    If not(SrchCache)
;     Then OverLap
;    If not(Read_Fast)
;     Then Goto Pro_Rd_Exit
;    If Count>1
;     Then
;      Command_Pending, IBsy:=true, Response:=Sys_Rd_Resp
;      Rd_Leave2
;     Else RdLeave
;    Count:=Count-1
;  End
;
;********************************************************************

Sys_Read	ld Wrk_lo+10, #Sys_Rd_Resp
		call Sys_SetUp
Sys_Rd_Seek	ld Seek_Type, #Access
		and DiskStat, #0FFh-Wr_Op
		call Sys_Rw_Seek
		call ReadBlock
		jr NZ, Sys_Rd_Next
		jr Sys_RdErr
;
Sys_Rd_Lp	call Sys_Inc_Blk
		jr NZ, Sys_Rd_Seek
		and R0, #3Fh		; mask off head value
		ld Sector, R0
Sys_Rd_OvrLp	call Read_Fast
		jr Z, Sys_RdErr
;
Sys_Rd_Next	tm BlkStat, #B_Block	; check if bad block came back!
		jr NZ, Sys_Rd_BB
Sys_Rd_N1	ld Wrk_lo+10, #Sys_Rd_Resp
		ld Wrk_lo+11, #Cmnd_Pending+IBsy
		djnz R4, Sys_Rd_More
		jp Rd_Leave
;
Sys_Rd_More	call Rd_Leave1
		jr Sys_Rd_Lp
;
Sys_RdErr	push R4			; save block count
		call Read_Common	; retry for exception handling
Sys_RdErr1	pop R4			; get block count
		push FLAGS		; save result
		push R0			; save return code
		call Load_Logical	; restore block number sequence
		pop R0
		pop FLAGS
		jr NZ, Sys_Rd_Next
		call Data_Ex_Handler
		jr Sys_Chk_Ftl
;
Sys_Rd_BB	push R4			; save block count
		call Pro_Rd_BB1
		jr Sys_RdErr1
;
Sys_Chk_Ftl	call Chk_FatalStat
		jp NZ, Rd_Leave		; get out if fatal error
		call ClrNormStat
		jr Sys_Rd_N1
;
Chk_FatalStat	ld R2, #CStatus0 /256
		ld R3, #CStatus0 #256
		lde R0, @RR2		; get 1st byte of standard stat
		tm R0, #Op_Failed	; check if operation failed
		ret
;
Sys_SetUp	call Ack_Read
		call ClrNormStat
		ld R0, #Sys_Log_Offset
		call Ld_LgclBlk
		call Ld_Param1
		or R0, R0		; check for zero count
		jr NZ, Sys_Set1
		call Abort
Sys_Set1	ld R4, R0		; Count:=...
		call Load_Logical
		ld R0, R4
		dec R0			; account for numbering 1..n
		add R14, R0
		adc R13, #0
		adc R12, #0
		ld R2, #Get_Type_Check /256
		ld R3, #Get_Type_Check #256
		call Bank_Call
		call Load_Logical
		ld Data_Type, #User_Type
		ld Wrk_lo+12, #WBuffer1 /256	; get ready for more writes
		ld Wrk_lo+13, #WBuffer1 #256
		ret
;
Sys_Rw_Seek	push R4			; save state
		ld R2, #OverLap /256
		ld R3, #OverLap #256
		call Bank_Call
		pop R4
		call Load_Logical
		ret
;
Sys_Inc_Blk	add R14, #1		; BlockNumber:=BlockNumber+1
		adc R13, #0
		adc R12, #0
		ld R2, #LogicalBlock /256
		ld R3, #LogicalBlock #256
		ld R1, #3
		ld R0, #Wrk_Sys+12
Sys_Inc_Lp	ldei @RR2, @R0
		djnz R1, Sys_Inc_Lp
		call SrchCache		; get the new physical adr
		tm BlkStat, #CachSeek+CachHdChg	; check for head change
		ret


;********************************************************************
;
; Procedure: Sys_Write
;
;  This procedure is the System Write command for Widget. In
;  general it is a major departure from the ProFile write
;  protocol: Status is not sent back to the host unless
;  there is useful information in the status -- the fact that
;  there were no errors in a block transfer is communicated
;  to the host via Response Byte that is handshaken across
;  at CMD/BSY time. If an error has occurred, then the host
;  is to recognize this state and come back to read the status
;  from the controller, otherwise it is assumed that the next
;  thing that the controller wants is write data. Note that
;  the second handshake (data received acknowledgement)
;  has been removed from the protocol.
;
; Inputs: none
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   Ld_LgclBlk(Wid_Log_Offset)
;   Count:=Ld_Param1.FirstParam
;   BlockNumber:=Load_Logical
;   ClrNormStat
;   Get_Type_Check(BlockNumber+Count)
;   Seek_Type:=Access_Offset
;   DiskStat.Wr_Op:=true
;   OverLap
;   If not(ReadBlock)
;    Then
;     SS_RdErr
;     Goto Pro_Rd_Exit
;   While Always Do
;    BlockNumber:=BlockNumber+1
;    If not(SrchCache)
;     Then OverLap
;    If not(Write_Common)
;     Then Data_Ex_Handler(Wr_Common.Error.Code)
;    If not(Read_Fast)
;     Then Goto Pro_Rd_Exit
;     Else
;      If BlkStat.Bad_Block
;       Then Goto Pro_Rd_BB
;    If Count>1
;     Then
;      Command_Pending, IBsy:=true, Response:=Sys_Wr_Resp
;      Rd_Leave2
;     Else RdLeave
;    Count:=Count-1
;  End
;
;********************************************************************

Sys_Write	ld Wrk_lo+10, #Sys_Wr_Resp
		call Sys_SetUp
Sys_Wr_Seek	ld Seek_Type, #Access+Offset
		or DiskStat, #Wr_Op
		call Sys_Rw_Seek
		tm BlkStat, #B_Block	; check if bad block
		jr NZ, Sys_Wr_BB
		call WriteBlock
		jr NZ, Sys_Wr_Next
		jr Sys_WrErr
;
Sys_Wr_Lp	call Sys_Inc_Blk
		jr NZ, Sys_Wr_Seek
		tm DiskStat, #Offset_On	; check for offset on all writes
		jr Z, Sys_Wr_Seek
		and R0, #3Fh		; mask off head value
		ld Sector, R0
Sys_Wr_OvrLp	tm BlkStat, #B_Block	; check if bad block
		jp NZ, Sys_Wr_BB
		call Write_Fast
		jr Z, Sys_WrErr
;
Sys_Wr_Next	ld Wrk_lo+10, #Sys_Wr_Resp
		ld Wrk_lo+11, #Cmnd_Pending+IBsy+MultiWr
		ld Wrk_lo+12, #WBuffer1 /256
		ld Wrk_lo+13, #WBuffer1 #256
		djnz R4, Sys_Wr_More
;
		ld Wrk_lo+10, #Sys_WrEnd_Resp
Sys_Wr_HS	ld Wrk_lo+11, #Cmnd_Pending+IBsy
		call Rd_Leave2
		jp Rd_Leave
;
Sys_Wr_More	call Clr_Bsy
		jr Sys_Wr_Lp
;
Sys_WrErr:	call Wr_Common		; retry for exception handling
		jr NZ, Sys_Wr_Next
;
		call Data_Ex_Handler
Sys_WrChk	call Chk_FatalStat
		jr NZ, Sys_WrE1
		call ClrNormStat
		jr Sys_Wr_Next
Sys_WrE1	ld Wrk_lo+10, #Sys_WrEx_Resp
		jr Sys_Wr_HS
;
Sys_Wr_BB	call Wr_BBlock
		jr Sys_WrChk



;********************************************************************
; Module Write.Assem
;
; This module contains all but the very most primitive of
; procedures (the routines that are resident are listed
; in the external spec's) that concern themselves with performing
; a write operation.
;
;********************************************************************

;********************************************************************
;
; Function: WriteBlock
;
;********************************************************************

WriteBlock 	clr R9			; clear booleans
		ld R8, #10		; RdRetryCnt:=10
		clr R11			; ErrCnt:=0
		or DiskStat, #Wr_Op
;
WriteBlk2	ld R2, #RHeader /256	; initialize gaps
		ld R3, #RHeader #256
		call Load_Header
		ld R0, #0
		ld R1, #14
WrBk_GapLp 	lde @RR2, R0		; write 14x $00
		inc R3
		djnz R1, WrBk_GapLp
;
		ld R0, #1
		lde @RR2, R0		; then $01
		inc R3
		ld R0, #0		; then $00
		lde @RR2, R0
		jr WriteBlk1		; skip over 2nd entry
;
Write_Fast	clr R9			; clear booleans
		ld R8, #10		; RdRetryCnt:=10
		clr R11			; ErrCnt:=0
		or DiskStat, #Wr_Op
		ld R2, #(RHeader+2) /256	; sector
		ld R3, #(RHeader+2) #256
		lde R0, @RR2
		and R0, #0C0h
		or R0, Sector
		lde @RR2, R0
		add R3, #3
		com R0
		lde @RR2, R0
WriteBlk1	call Wr_Resident
		ld R1, R0		; get machine status register
		and R1, #YMask
		or R10, R10
		jr Z, WrBlk_Failed
		cp R1, #Norm_State 	; statemachine healthy?
		jr NZ, WrBlk_Abnorm
;
		or R9, #WrSuccess
		ld R1, R0		; servo ok?
		tm R1, #ServoErr
		jr NZ, WrBlk_ServoErr
		tm R1, #ServoRdy
		jr Z, WrBlk_ServoErr
		tm R0, #WrtNvldL	; ECC error?
		jr Z, WrBlk_BadEcc
;
		and R9, #0FFh-WrError
WrBlk_Rpt1	tm Excpt_Stat, #Recovery
		jr Z, WrBlk_End
		tm R9, #WrError
		jr Z, WrBlk_End
		djnz R8, WrBlk_Rpt	; try again if fault occurred
;
WrBlk_End	ld WrErrCnt, R11
		ld R0, R9		; send status back to caller
		ld WrStat, R9
		tcm R0, #WrError	; set zero flag if error
		ret
;
WrBlk_Rpt	ld R2, #ZeroHeader /256	; clear up header
		ld R3, #ZeroHeader #256
		call Bank_Call
		jr WriteBlk2		; and try again
;
WrBlk_Abnorm	call Reset_StMach
		ld R10, R0
		call Abort
;
WrBlk_Failed	and R9, #0FFh-WrSuccess
		or R9, #WrError+WrNoHdrFnd
		or R11, #20h
		jr WrBlk_End
;
WrBlk_ServoErr	or R9, #WrError+WrSrvoErr
		jr WrBlk_End
;
WrBlk_BadEcc	or R9, #WrError
		inc R11
		jr WrBlk_Rpt1
;


;********************************************************************
;
; Procedure: ClrNormStat
;
;  This procedure zeroes the header buffer and the standard
;  status CStatus0. Spare table warning, power-on, and selftest
;  faults are preserved in the status bytes.
;
;********************************************************************

ClrNormStat 	ld R2, #CStatus0 /256
		ld R3, #CStatus0 #256
		clr R0
		ld R1, #4		; clear four bytes
ClrNmStat_Lp	lde @RR2, R0
		incw RR2
		djnz R1, ClrNmStat_Lp
;
		ld R2, #ZeroHeader /256	; clear header
		ld R3, #ZeroHeader #256
		call Bank_Call
;
		tm Excpt_Stat, #SprTbl_Warn
		jr Z, ClrNmStat1
		call SS_SprWarn		; preserve spare table warning
;
ClrNmStat1	tm Excpt_Stat, #PwrRst
		jr Z, ClrNmStat2
		ld R0, #2		; status byte 2
		ld R1, #Power_Reset
		call SetStatus		; report cold start
		and Excpt_Stat, #0FFh-PwrRst
;
ClrNmStat2	or SlfTst_Result, SlfTst_Result
		jr Z, ClrNmStat3
		ld R0, #1		; status byte 1
		ld R1, #Stat_SlfTst
		call SetStatus		; report selftest errors
;
ClrNmStat3	jp Bank_Ret


;********************************************************************
;
; Procedure: Move4_B0   {in Bank 0}
;
;  This procedure copies four bytes located in external RAM.
;
; Inputs: Src: PTR {RR2}
;         Dst: PTR {RR14}
;
; Outputs: none
;
; Global Variables Changed: RAM @RR14
;
; Local Variables Used: R1
;
;********************************************************************

Move4_B0	ld R1, #4		; counter
Move4_B0_Lp	lde R0, @RR2		; get from RR2
		lde @RR14, R0		; put into RR14
		incw RR2
		incw RR14
		djnz R1, Move4_B0_Lp
		jp Bank_Ret



;********************************************************************
; Module Read.Assem
;
; This module contains all but the very most primitive of
; procedures (the routines that are resident are listed
; in the external spec's) that concern themselves with performing
; a read operation.
;
;********************************************************************

;********************************************************************
;
; Function: ReadBlock
;
;  This function assumes that the heads are positioned over the
;  correct track and reads the block of data whose header
;  matched the header that is made up of the cylinder, head
;  and sector information that is stored in memory.
;
; Inputs: Parent: BYTE {R8}
;
; Outputs: ReadBlock: BOOLEAN {zero flag, true if error in ReadBlock}
;          Status:    BYTE {R0}
;          RdErrCnt:  BYTE {R1}
;
; Global Variables Used: Cylinder, Head, Sector, Recovery
;
; Local Variables Used: RdRetryCnt:  BYTE {R8}
;                       RdError:     BOOLEAN {R9/bit 7}
;                       RdExcept:    BOOLEAN {R9/bit 6}
;                       RdSuccess:   BOOLEAN {R9/bit 5}
;                       SectorsRead: BOOLEAN {R9/bit 4}
;
; Algorithm:
;
;  Begin
;   SetDeadManTimer(ReadBlock, Parent)
;   RdRetryCnt:=10
;   RdErrCnt:=0
;   RdError:=false
;   RdExcept:=false
;   NoHeaderFound:=false
;   SectorsRead:=2xNbrSctrs {try to find header for two rotations}
;   Repeat
;    RHeader[1]:=HiCylinder
;    RHeader[2]:=LoCylinder
;    RHeader[3]/bits 7:6:=Head
;    RHeader[3]/bits 5:0:=Sector
;    RHeader[4]:=invert(RHeader[1])
;    RHeader[5]:=invert(RHeader[2])
;    RHeader[6]:=invert(RHeader[3])
;    ReadArray[RDummy-1]:=0
;   /-
; R | Set-up external RAM address counter for read
; E | Msel0:1:=Disk<-->Mem
; S | While SectorMark Do Begin End
; I | StartL:=true
; D | While not(SectorDnL) Do Begin End
; E | Status:=StatusPort
; N | StartL:=false
; T | Msel0:1:=Z8<-->Mem
;   \-
;    Case Status.State Of
;     NormalEndState:        RdSuccess:=true
;     NoMatchingHeaderFound: RdSuccess:=false
;                            RdError:=true
;                            NoHeaderFound:=true
;     AbnormalState:         Reset_StateMachine
;                            Abort
;    If Status.ServoErr Or not(Status.ServoRdy)
;     Then
;      RdError:=true
;      RdExcept:=true
;    If Status.CrcErr And RdSuccess
;     Then
;      RdError:=true
;      RdErrCnt:=RdErrCnt+1
;      RdRetryCnt:=RdRetryCnt-1
;     Else
;      If RdError
;       Then
;        BlockMove(Buffer2, RBuffer1)
;        RdRetryCnt:=RdRetryCnt-1
;   Until not(Recovery) Or not(RdError) Or RdRetryCnt=0 Or
;         RdExcept Or NoHeaderFound
;   ClearDeadManTimer
;   Status:=R9
;  End
;
;********************************************************************

ReadBlock	clr R9			; clear booleans
		ld R8, #10		; RdRetryCnt:=10
		clr R11			; ErrCnt:=0
		and DiskStat, #0FFh-Wr_Op	; make sure we are reading
RdBlk_Rpt	ld R2, #RHeader /256	; initialize gaps
		ld R3, #RHeader #256
		call Load_Header
		ld R0, #0
		lde @RR2, R0
		ld R2, #(RDummy-1) /256
		ld R3, #(RDummy-1) #256
		lde @RR2, R0
		incw RR2
		lde @RR2, R0
		jr Do_Read
;
Read_Fast	clr R9			; clear booleans
		ld R8, #10		; RdRetryCnt:=10
		clr R11			; ErrCnt:=0
		and DiskStat, #0FFh-Wr_Op	; make certain we are reading
		ld R2, #(RHeader+2) /256	; get location of Sector
		ld R3, #(RHeader+2) #256
		lde R0, @RR2		; get current head/sector value
		and R0, #0C0h		; mask out old sector value
		or R0, Sector		; merge in new sector
		lde @RR2, R0		; and store it
		add R3, #3		; get location of inverse head/sector
		com R0
		lde @RR2, R0		; and store it, too!
Do_Read		call Rd_Resident	; go internal to the Z8
		ld R1, R0		; Case Status.State
		and R1, #YMask
		or R10, R10
		jr Z, RdBlk_NoHdr
		cp R1, #Norm_State
		jr NZ, RdBlk_AbNorm
;
RdBlk_Norm	or R9, #RdSuccess
		ld R1, R0		; If ServoErr Or not(ServoRdy)
		tm R1, #ServoErr
		jr NZ, RD_ServoErr
		tm R1, #ServoRdy
		jr Z, RD_ServoErr
;
Rd_ServoOk	tm R0, #CrcErrL		; If Status.CrcErr
		jr Z, RD_BadCrc
		tm R0, #WrtNvldL	; Or Status.EccErr
		jr Z, RD_BadEcc
;
Rd_NoCrcErr	tm R9, #RdError		; Then If RdError
		jr NZ, RdBlk_Rmove
;
RdBlk_Until	tm Excpt_Stat, #Recovery
		jr Z, RdBlk_End
		tm R9, #RdError
		jr NZ, RdB_Rpt1
;
RdBlk_End	ld RdErrCnt, R11
		ld R0, R9		; send status back to caller
		ld RdStat, R9
		tcm R0, #RdError	; set zero flag if error
		ret
;
RdB_Rpt1	ld R2, #ZeroHeader /256
		ld R3, #ZeroHeader #256
		call Bank_Call
		jp RdBlk_Rpt
;
RdBlk_AbNorm	call Reset_StMach
		ld R10, R0
		call Abort
;
RdBlk_NoHdr	and R9, #0FFh-RdSuccess
		or R9, #Error
		tm R11, #Hdr_MisMatch
		jr NZ, Rd_HdrErr
		or R11, #Hdr_MisMatch
		tm Excpt_Stat, #Recovery	; auto offset ONLY if recovery on
		jr Z, RdBlk_Until
		call ReSeek		; set auto-offset
		jr RdBlk_Until
;
Rd_HdrErr	or R9, #RdNoHdrFnd+RdError
		jr RdBlk_End
;
RD_ServoErr	or R9, #RdError+RdSrvoErr	; Then RdError And RdServoErr
		jr RdBlk_End
;
RD_BadCrc	or R9, #RdError+RdCrcErr	; Else
		inc R11
		or R11, #CrcStat
		tm R0, #WrtNvldL	; check for ECC error, too
		jr NZ, Rd_B_Crc_1
RD_Bad1		or R11, #EccStat
		tm Excpt_Stat, #Recovery	; re-seek only if recovery is on
		jr Z, Rd_B_Crc_1
		tm DiskStat, #Offset_On		; set auto-offset if needed
		jr NZ, Rd_B_Crc_1
		call ReSeek
Rd_B_Crc_1	djnz R8, RdBlk_Until
		jr RdBlk_End
;
RD_BadEcc	or R9, #RdError+RdCrcErr
		inc R11
		jr RD_Bad1
;
RdBlk_Rmove	ld R2, #RBuf_To_Buf2 /256
		ld R3, #RBuf_To_Buf2 #256
		call Bank_Call
		jr Rd_B_Crc_1



;********************************************************************
; Module Srvo0.Assem
;
; This module contains all the specifications and source code for
; controlling the Widget Servo Board (i.e. communication
; protocol, seek and head positioning, spare table lookup, etc).
;
;********************************************************************

;********************************************************************
;
; Function: OverLap  {overlapped seek}
;
;  This fuction allows the drive to begin a seek operation (if
;  one is needed) before getting tied up with the SOS driver.
;
; Inputs: Response:    BYTE {R10}
;         CommandType: BYTE {R9}
;         BlockType:   BYTE {R8}
;         BlockNumber: 3 BYTES {R12:14}
;         Random:      BOOLEAN {R7/bit 7}
;         Offset:      3 BITS {R7/bit 2:0}
;
; Outputs: Block Status: BYTE {R0}
;
; Local Variables: Retry: BYTE {R4}
;
; Algorithm:
;
;  Begin
;   If SrchCache(BlockNumber, Random)
;    Then
;     If Sech_Cache.Head<>Head
;      Then
;       SelectHead(SrchCache.Head)
;       Head:=SrchCache.Head
;     Sector:=SrchCache.Sector
;     Ack_Read
;    Else
;     Retry:=4
;     Temp:=false
;     Repeat
;      Retry:=Retry-1
;      Temp:=PositionHeads(NoWait, Dmt_Overlap, BlockNumber)
;     Until not(Recovery) Or Temp Or Retry=0
;     If not(Temp) Then Abort
;     BlockStatus:=PositionHeads.Status
;     TempCyl:=PosiitonHeads.Cylinder
;     TempHead:=PositionHeads.Head
;     TempSector:=PositionHeads.Sector
;     If CommandType=Write
;      Then Get_Write_Data(Response)
;      Else Ack_Read
;     Load_Cache
;     Seek(TempCyl, TempHead, TempSector)
;  End
;
;********************************************************************

OverLap		call SrchCache
		jr NZ, OvrLapSeek
		push R0			; save SrchCache result
		tm BlkStat, #CachHdChg	; check for a head change
		jr Z, OverLd_Sctr
;
		xor Head, #1		; complement the head value
		ld R14, Head
		ld R2, #SelectHead /256
		ld R3, #SelectHead #256
		call Bank_Call
		ld R2, #Load_Cache /256
		ld R3, #Load_Cache #256
		call Bank_Call
		ld R2, #27h
		ld R3, #0F7h
		call Bank_Call
;
OverLd_Sctr	pop R3			; retrieve SrchCache result
		and R3, #1Fh		; mask off all but sector info
		ld Sector, R3
		jr OvrLp_End
;
OvrLapSeek	ld R2, #CnvrtLogical /256
		ld R3, #CnvrtLogical #256
		call Bank_Call
		clr R8			; no wait
		ld R2, #PosHeads /256
		ld R3, #PosHeads #256
		call Bank_Call
		call ExtPush_Vector
		ld R2, #Load_Cache /256
		ld R3, #Load_Cache #256
		call Bank_Call
		ld R2, #CacheStat /256
		ld R3, #CacheStat #256
		lde R0, @RR2
		ld BlkStat, R0
		call ExtPop_Vector
		call New_Seek
OvrLp_End	jp Bank_Ret

		DB 02000h-$ dup(0FFh)	; pad with $FF's
;====================================================================
;====================================================================

;********************************************************************
; Module Bank1.Assem
;
;********************************************************************
	org 1000h
	phase 2000h		; EPROM will start at 4k so 4k 01000h offset
		DW Checksum1
		DB 1		; bank 1
		DW Passwrd1, Passwrd2



;********************************************************************
; Module RdHdr.B1.Assem
;
; This module contains all but the very most primitive of
; procedures that concern themselves with performing a read
; header operation.
;
;********************************************************************

;********************************************************************
;
; Function: ReadHdr
;
;  This function assumes that the heads are positioned over the
;  correct track and reads the block of data following the
;  next sector mark. This routine is used for two reasons: 1) to
;  verify the header that is laid out on a block, and 2) to try
;  to recover the data within a block if the header is munched.
;
; Inputs: Parent: BYTE {R8}
;
; Outputs: ReadHdr: BOOLEAN {zero flag, true if error in ReadHdr}
;          Status:    BYTE {R0}
;
; Global Variables Used: Cylinder, Head, Sector
;
; Local Variables Used: RdHError:  BOOLEAN {R9/bit 7}
;                       RdHExcept: BOOLEAN {R9/bit 6}
;
; Algorithm:
;
;  Begin
;   SetDeadManTimer(ReadBlock, Parent)
;   RdHError:=false
;   RdHExcept:=false
;   RdHSctrGap:=0
;   RdHHdrGap:=0
;   /-
; R | Set-up external RAM address counter for read header
; E | Msel0:1:=Disk<-->Mem
; S | While SectorMark Do Begin End
; I | StartL:=true
; D | While not(SectorDnL) Do Begin End
; E | Status:=StatusPort
; N | StartL:=false
; T | Msel0:1:=Z8<-->Mem
;   \-
;   If Status.State<>NormalEndState
;    Then
;     Reset_StateMachine
;     Abort
;   If Status.ServoErr or not(Status.ServoRdy)
;    Then
;     RdHError:=true
;     RdHExcept:=true
;   If Status.CrcErrL then RdHError:=true
;   ClearDeadManTimer
;   Status/bit7:=RdHError
;   Status/bit7:=RdHExcept
;  End
;
;********************************************************************

ReadHdr		and DiskStat, #0FFh-Wr_Op	; make sure we are reading
		clr R9			; clear booleans
		call RdHdr_Resident	; go internal to the Z8
		ld R1, R0		; If Status.State
		and R1, #YMask
		cp R1, #Norm_State
		jr Z, RdHdr_Norm
;
		call Reset_StMach
		and DiskStat, #0FFh-RdHdrRecal
		ld R11, R10
		ld R10, R0
		call Abort
;
RdHdr_Norm	ld R1, R0		; If ServoErr or not(ServoRdy)
		tm R1, #ServoErr
		jr NZ, RdHd_ServoErr
		tm R1, #ServoRdy
		jr NZ, RdHd_SrvoOk
;
RdHd_ServoErr	or R9, #RdHError+RdHSrvoErr	; Then RdHError and RdHServoErr
		jr RdH_End
;
RdHd_SrvoOk	tm R0, #CrcErrL		; If Status.CrcErr
		jr Z, RdH_CrcErr
;
RdH_ChkEcc	tm R0, #WrtNvldL	; or Status.EccErr
		jr NZ, RdH_End
		or R9, #RdHError+RdCrcErr
		or RdErrCnt, #EccStat
		jr RdH_End
;
RdH_CrcErr	or R9, #RdHError+RdCrcErr	; Else
		ld RdErrCnt, #CrcStat
		jr RdH_ChkEcc
;
RdH_End		ld R0, R9		; send status back to caller
		ld RdStat, R9
		tcm R0, #RdHError	; set zero flag if error
		jp Bank_Ret



;********************************************************************
; Module Utils.B1
;
; This module contains routines that are part of the main utility
; module and that must be kept in Bank 1.
;
;********************************************************************

;********************************************************************
;
; Procedures: Buf2_To_RBuf  {move Buffer2 to ReadBuffer}
;             WrBuf_To_Buf2 {move WriteBuffer to Buffer2}
;             Buf2_To_WrBuf {move Buffer2 to WriteBuffer}
;             Spr_To_RBuf   {move SpareArray to ReadBuffer}
;             RBuf_To_Spr   {move ReadBuffer to SpareArray}
;             WrBuf_ToSpr   {move WriteBuffer to SpareArray}
;             RBuf_To_Buf2  {move ReadBuffer to Buffer2}
;             Spr_To_WrBuf  {move SpareArray to WriteBuffer}
;             Zero_RdBuf    {move zeros to ReadBuffer}
;             Zero_Fmt      {move zeros to FormatBuffer}
;
; Inputs: none
;
; Outputs: none
;
;********************************************************************

Buf2_To_RBuf	ld R0, #Buf2Array /256
		ld R1, #Buf2Array #256
		ld R2, #RDummy /256
		ld R3, #RDummy #256
		jr B_Move
;
WrBuf_To_Buf2	cp Data_Type, #User_Type	; nop if sparetable data
		jr NZ, Return_Vector
		ld R0, #(WBuffer1-1) /256
		ld R1, #(WBuffer1-1) #256
		jr X_To_Buf2
;
Buf2_To_WrBuf	cp Data_Type, #User_Type
		jr NZ, Spr_To_WrBuf
		ld R0, #Buf2Array /256
		ld R1, #Buf2Array #256
		ld R2, #(WBuffer1-1) /256
		ld R3, #(WBuffer1-1) #256
		jr B_Move
;
Spr_To_RBuf	ld R0, #SpareArray /256
		ld R1, #SpareArray #256
		ld R2, #RBuffer1 /256
		ld R3, #RBuffer1 #256
		jr B_Move

RBuf_To_Spr	ld R0, #RBuffer1 /256
		ld R1, #RBuffer1 #256
X_To_Spr	ld R2, #SpareArray /256
		ld R3, #SpareArray #256
		jr B_Move

WrBuf_To_Spr 	ld R0, #WBuffer1 /256
		ld R1, #WBuffer1 #256
		jr X_To_Spr

RBuf_To_Buf2 	cp Data_Type, #2
		jr NZ, Return_Vector
		ld R0, #RDummy /256
		ld R1, #RDummy #256
X_To_Buf2 	ld R2, #Buf2Array /256
		ld R3, #Buf2Array #256
B_Move		call BlockMove
Return_Vector	jp Bank_Ret


Spr_To_WrBuf	ld R0, #SpareArray /256
		ld R1, #SpareArray #256
		ld R2, #WBuffer1 /256
		ld R3, #WBuffer1 #256
		jr B_Move
		jr Return_Vector

Zero_RdBuf	ld R2, #ReadArray /256
		ld R3, #ReadArray #256
		call ZeroBlock

Zero_Fmt	ld R2, #FBuffer1 /256
		ld R3, #FBuffer1 #256
		ld R14, #(FEndGap-FBuffer1) /256
		ld R15, #(FEndGap-FBuffer1) #256
		ld R0, #0C6h	; init block to pattern
Zero_Flp	lde @RR2, R0
		incw RR2
		decw RR14
		jr NZ, Zero_Flp
		jp Return_Vector


;********************************************************************
;
; 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 {R12:14}
;
; Outputs: HiCylinder: BYTE {R12}
;          LoCylinder: BYTE {R13}
;          Head:       BYTE {R14}
;          Sector:     BYTE {R15}
;
;********************************************************************

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 R15, R3		; Sector
		ld R14, R2		; Head
		pop R13			; LoCylinder
		pop R12			; HiCylinder
		ld PSector, R15	; update physical sector
		jp Bank_Ret


;********************************************************************
;
; 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
;
;********************************************************************

UpDate_Cur_Cyl	call GoodHdr
		jr Z, UD_C_C_End
		ld Cur_Cyl, R0
		ld Cur_Cyl+1, R1
		or R1, #1		; return non-zero status
UD_C_C_End	jp Bank_Ret


;********************************************************************
;
; Function: GoodHdr
;
;  This function is responsible for reading the 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 {RR0}
;          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
;
;********************************************************************

GoodHdr		ld R4, #8		; try hard to find a good header
GdHdr_1		call ReadHdr
		ld R2, #RHHeader /256
		ld R3, #RHHeader #256
		ld R0, #ScrReg0
		ld R1, #6		; load 6 bytes
GdHdr_Lp	ldei @R0, @RR2
		djnz R1, GdHdr_Lp
		srp #Wrk_Scr 		; context switch
	assume RP:Wrk_Scr
		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
	assume RP:Wrk_Sys
		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, #10 /256		; wait 100ms before retrying
		ld R3, #10 #256
		call MsWait
		djnz R4, GdHdr_1	; retry if bad header
		jr GdHdr_End
; found a good one
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, #3		; just leave head info in register
		ld R3, ScrReg2		; GoodHdr.Sectror:=ReadHdr.HdSctr/bits 5:0
		and R3, #3Fh
		ld ScrReg6, #1
;
GdHdr_End	or ScrReg6, ScrReg6	; set zero flag
		jp Bank_Ret


;********************************************************************
;
; 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 {RR2}
;         Source:      PTR {RR0}
;
; Outputs: none
;
;********************************************************************

BlockMove	call Ext_Push
		ld R4, #BlockLength /256
		ld R5, #BlockLength #256
Blk_Move	lde R6, @RR0
		lde @RR2, R6
		incw RR0
		incw RR2
		decw RR4
		jr NZ, Blk_Move
		call Ext_Pop
		ret


;********************************************************************
;
; 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 operation's 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
;
;********************************************************************

UpDate_Hdr	call Ext_Push
		and DiskStat, #0FFh-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 DiskStat, #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


;********************************************************************
;
; 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 {R12:14}
;
; 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
;
;********************************************************************

Get_Type	call Load_Logical
		ld R0, R8
		ld R8, #User_Type
		or R0, R0		; If DriverType=ProFile
		jr NZ, Get_Type_Check
		ld R0, #0FFh		; If BlockNumber=$-2...
		ld R1, #0FFh
		ld R2, #0FEh
		call Sub3		; compare
		or R0, R1
		or R0, R2
		jr NZ, G_T_ChkID
;
		ld R8, #SprTbl_Type
G_T_ProCnvrt	clr R12
		clr R13
		ld R14, #1
		jr Get_Type_Check
;
G_T_ChkID	ld R0, #0FFh		; If BlockNumber=$-1...
		ld R1, #0FFh
		ld R2, #0FFh
		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 NC, Get_Type_End
;
		ld R0, #2
		ld R1, #Illegal_Block
		call SetStatus
		call Abort
;
Get_Type_End	jp Bank_Ret



;********************************************************************
; Module Utils1.B1.Assem  {utilities, continued}
;
;********************************************************************

;********************************************************************
;
; Procedure: Move4
;
;  This procedure is used to move 4 bytes from the source to the
;  destination
;
; Inputs: Source:      PTR (RR2)
;         Destination: PTR (RR14)
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   For i:=0 To 3 Do
;    @Destination[i]:=Source[i]
;  End
;
;********************************************************************

Move4		ld R1, #4
Move4_Lp	lde R0, @RR2
		lde @RR14, R0
		incw RR2
		incw RR14
		djnz R1, Move4_Lp
		ret


;********************************************************************
;
; Procedure: Move4C  {move from program space to data space}
;
;  This procedure is used to move 4 bytes from the source to the
;  destination
;
; Inputs: Source:      PTR (RR2)
;         Destination: PTR (RR14)
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   For i:=0 To 3 Do
;    @Destination[i]:=Source[i]
;  End
;
;********************************************************************

Move4C		ld R1, #4
Move4C_Lp 	ldc R0, @RR2
		lde @RR14, R0
		incw RR2
		incw RR14
		djnz R1, Move4C_Lp
		ret


;********************************************************************
;
; Procedure: Init_ExtStack
;
;  This procedure initializes Widget's external stack. This
;  stack is a software implemented structire, and as such does not
;  use the Z8's external stack capabilities
;
; Inputs: none
;
; Outputs: none
;
; Global Variables Changed: StackPtr
;
; Algorithm:
;
;  Begin
;   StackPtr:=TopOfStack
;  End
;
;********************************************************************

Init_ExtStack	ld R2, #StackPtr /256
		ld R3, #StackPtr #256
		ld R0, #TopOfStack /256
		lde @RR2, R0
		incw RR2
		ld R0, #TopOfStack #256
		lde @RR2, R0
		jp Bank_Ret


;********************************************************************
;
; Procedure: Ext_Push
;
;  This procedure pushes the current working register set
;  onto the external stack.
;
; Inputs: none
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   For i:=0 To 15 Do
;    @StackPtr:=WorkingRegister[15-i]
;    StackPtr:=StackPtr-1
;  End
;
;********************************************************************

Ext_Push	ld ScrReg4, RP		; save context
		srp #Wrk_Scr
	assume RP:Wrk_Scr
		call Load_TOS
		ld R0, R4		; save context
		add R0, #15		; start at register 15
		ld R1, #16		; save 16 registers
Ext_Push_Lp	ld R15, @R0
		lde @RR2, R15
		dec R0
		decw RR2
		djnz R1, Ext_Push_Lp
		call Store_TOS
		ld RP, R4		; restore context
		jp Bank_Ret


;********************************************************************
;
; Procedure: Ext_Pop
;
;  This procedure pops 16 locations from the external stack
;  into the current working register set.
;
; Inputs: none
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   For i:=0 To 15 Do
;    StackPtr:=StackPtr+1
;    WorkingRegister[i]:=@StackPtr
;  End
;
;********************************************************************

Ext_Pop		ld >ScrReg4, RP
		srp #Wrk_Scr
	assume RP:Wrk_Scr
		call Load_TOS
		ld R0, R4		; save context
		ld R1, #16		; load 16 registers
		incw RR2		; StackPtr:=StackPtr+1
Ext_Pop_Lp	ldei @R0, @RR2
		djnz R1, Ext_Pop_Lp
		decw RR2		; point at TOS
		call Store_TOS
		ld RP, R4		; restore context
		jp Bank_Ret


;********************************************************************
;
; Function: Load_TOS
;
;  This function returns a ptr to the current top
;  of the external stack.
;
; Inputs: none
;
; Outputs: Load_TOS: PTR {RR2}
;
; Algorithm:
;
;  Begin
;   Load_TOS:=StackPtr
;  End
;
;********************************************************************

Load_TOS	ld	R0, #StackPtr /256
		ld	R1, #StackPtr #256
		lde	R2, @RR0
		incw	RR0
		lde	R3, @RR0
		ret


;********************************************************************
;
; Procedure: Store_TOS
;
;  This procedure stores the contents of Ptr into StackPtr.
;
; Inputs: Ptr: PTR {RR2}
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   StackPtr:=Ptr
;  End
;
;********************************************************************

Store_TOS	cp R3, #StackPtr #256	; check for stack underflow
		jr UGT, St_TOS
;
		srp #Wrk_Sys
	assume RP:Wrk_Sys
		pop R4			; save some history of stack frame
		pop R5
		pop R6
		pop R7
		call Abort
;
St_TOS		ld R0, #StackPtr /256
		ld R1, #StackPtr #256
		lde @RR0, R2
		incw RR0
		lde @RR0, R3
		ret


;********************************************************************
;
; Procedure: ZeroBlock
;
;  This procedure zeroes out the 532 bytes pointed to by the
;  input parameter.
;
; Inputs: BlockPtr: PTR {RR2}
;
; Outputs: none
;
;********************************************************************

ZeroBlock	ld R14, #532 /256
		ld R15, #532 #256
		clr R0
ZeroBlk_Lp	lde @RR2, R0
		incw RR2
		decw RR14
		jr NZ, ZeroBlk_Lp
		jp Bank_Ret


;********************************************************************
;
; Procedure: ZeroHeader
;
;  This procedure zeroes the sector gap, header sync gap,
;  header field, and data sync field
;
; Inputs: none
;
; Outputs: none
;
;********************************************************************

ZeroHeader	ld R1, #21h
		ld R2, #10h
		ld R3, #0
		clr R0
ZeroHdr_Lp	lde @RR2, R0
		incw RR2
		djnz R1, ZeroHdr_Lp
		and Excpt_Stat, #-29h
		jp Bank_Ret



;********************************************************************
; Module Spr1.B1.Assem
;
; This module contains those sparing routines that must
; be located in bank 1.
;
;********************************************************************

;********************************************************************
;
; Procedure: Init_SprTbl
;
;  This procedure initializes the spare table. It assumes
;  that there is NO spare table on disk.
;
; Inputs: FormatOffset:     BYTE (R4)
;         FormatInterLeave: BYTE (R5)
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   SparePw1:=$F0783C1E
;   SpareTmStmp:=0
;   For i:=0 To Length(SegPtrArray)-1 Do
;    SegPtrArray[i].Nil:=True
;    SegPtrArray[i].Ptr:=0
;   SpareCount:=0
;   BadCount:=0
;   For i:=0 To Length(SpareBitMap)-1 Do
;    SpareBitMap[i]:=0
;   For i:=0 To 1 Do
;    AddSpare(SprTbl_Type, Spare, GetNewSpare(Spr(i), Spr(i))
;   SparePw2:=$F0783C1E
;   UpDate_SprTbl
;  End
;
;********************************************************************

Init_SprTbl	ld R2, #SpareArray /256
		ld R3, #SpareArray #256
		call ZeroBlock
		ld R14, #SparePw1 /256
		ld R15, #SparePw1 #256
		call Load_PassWord
		ld R2, #FmtOffset /256
		ld R3, #FmtOffset #256
		lde @RR2, R4		; store offset value
		incw RR2
		lde @RR2, R5		; store interleave value
		incw RR2
; initialize SegPtrArray
		ld R0, #Nil
		ld R1, #SprCount-SegPtrArray
I_S_Tbl_Lp2	lde @RR2, R0
		incw RR2
		djnz R1, I_S_Tbl_Lp2
; initialize SpareBitMap
		ld R0, #0
		ld R4, #(SpareCheck-SprCount) /256
		ld R5, #(SpareCheck-SprCount) #256
I_S_Tbl_Lp4	lde @RR2, R0
		incw RR2
		decw RR4
		jr NZ, I_S_Tbl_Lp4
;
		ld Data_Type, #SprTbl_Type
		ld R5, #2		; create two tables
		clr R8
Create_Tbl	call Spr
		ld R2, #GetNewSpare /256
		ld R3, #GetNewSpare #256
		call Bank_Call
		push R8			; save counter
		ld R15, R0
		ld R8, #SprTbl_Type
		or R8, #Spare
		ld R2, #AddSpare /256
		ld R3, #AddSpare #256
		call Bank_Call
		pop R8
		inc R8			; go to next table
		djnz R5, Create_Tbl
; create interleave mapping table
		ld R2, #Map_Table /256
		ld R3, #Map_Table #256
		ld R1, #NbrSctrs
		ld R0, #0
I_Map_Lp	lde @RR2, R0
		incw RR2
		add R0, #Map_Dflt
		cp R0, #NbrSctrs
		jr LT, I_Map_Lp2
		sub R0, #NbrSctrs
I_Map_Lp2	djnz R1, I_Map_Lp
		ld R14, #SparePw2 /256
		ld R15, #SparePw2 #256
		call Load_PassWord
;
		call UpDate_SprTbl
		jp Bank_Ret


;********************************************************************
;
; Procedure: Load_SprTbl
;
;  This procedure loads the spare table from disk. The table is
;  found by a linear search of all spare blocks until a block
;  is found where both the Spare Table Identifier field is present
;  and the internal passwords and check byte are valid.
;
; Inputs: none
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   Seek_Type:=Access_Offset
;   InterLeaveFactor:=Find_InterLeave
;   Found:=false
;   Count:=0
;   i:=NumberOfSpareBlocks
;   While i>0 and Count<2 Do
;    Seek(CnvrtLogical(MulR0_m(i)))
;    j:=0
;    Sector:=0
;    While not(Found) And (j<NbrSctrs) Do
;     If ReadCommon
;      Then
;       If BlockID=SprTblID and
;          PassWord1=PassWord2=PassWord and
;          CheckByte is valid
;        Then
;         Found:=true
;         Count:=Count+1
;         If Count>1
;          Then
;           If SpareTable.RunNumber<ReadBuffer.RunNumber
;            Then MoveBlock(SpareTable, ReadBuffer)
;          Else MoveBlock(SpareTable, ReadBuffer)
;       Else
;        If RdErrCnt<10
;         Then
;          MoveBlock(ReadBuffer, Buffer2)
;          If BlockID=SprTblID and
;             PassWord1=PassWord2=PassWord and
;             CheckByte is valid
;           Then
;            Found:=true
;            Count:=Count+1
;            If Count>1
;             Then
;              If SpareTable.RunNumber<ReadBuffer.RunNumber
;               Then MoveBlock(SpareTable, ReadBuffer)
;             Else MoveBlock(SpareTable, ReadBuffer)
;     j:=j+1
;     Sector:=(Secotr+InterLeaveFactor) mod NbrSctrs
;    i:=i+1
;   If not(Found)
;    Then Abort
;    Else
;     UpDate_SprTbl
;     Park
;     SlfTst_Result.NoSpareTable:=False
;  End
;
;********************************************************************

Load_SprTbl	ld R5, #76		; i:= NumberOfSpareBlocks
		clr R4			; SprTbl_Found:=false
		ld Data_Type, #User_Type
L_SprTbl_Lp	call Ext_Push
		ld R0, R5
		call MulR0_m
		call Get_Cyl_H_S
		ld Seek_Type, #Access
		call Seek
		call Ext_Pop
		ld R6, #NbrSctrs	; check the entire track
		clr Sector		; starting with sector 0
;
L_Rd_Lp		push R4
		push R5
		push R6
		ld R2, #RdBlk_Vector /256
		ld R3, #RdBlk_Vector #256
		call Bank_Call
		pop R6
		pop R5
		pop R4
		jr NZ, Chk_SprTbl
;
		ld R0, RdErrCnt
		and R0, #0Fh		; mask unwanted status
		cp R0, #10		; check for any successful reads
		jr GE, L_SprTbl_More
		call Buf2_To_RBuf	; get good data back into read buffer
		jr Chk_SprTbl
;
L_SprTbl_More	inc Sector
		djnz R6, L_Rd_Lp
		djnz R5, L_SprTbl_Lp
		or R4, R4		; check if any spare table found
		jr NZ, L_Spr_End
		call Abort
;
Chk_SprTbl	ld ScrReg2, #(RBuffer1+BlockID) /256
		ld ScrReg3, #(RBuffer1+BlockID) #256
		call Chk_PassWord
		jr Z, L_SprTbl_More
;
		ld ScrReg2, #RBuffer1 /256
		ld ScrReg3, #RBuffer1 #256
		call Chk_PassWord
		jr Z, L_SprTbl_More
;
		ld R2, #(RBuffer1+SpareCheck-SpareArray) /256
		ld R3, #(RBuffer1+SpareCheck-SpareArray) #256
		lde R0, @RR2		; check possible check byte
		incw RR2
		lde R1, @RR2
		srp #Wrk_Scr
	assume RP:Wrk_Scr
		ld R12, #RBuffer1 /256
		ld R13, #RBuffer1 #256
		call SprChk2
		srp #Wrk_Sys
	assume RP:Wrk_Sys
		call Chk_Spr2
		jr Z, L_SprTbl_More
;
		or R4, R4		; check for a SpareTable already found
		jr Z, L_Spr_Move
		srp #Wrk_Scr
	assume RP:Wrk_Scr
		ld R6, #SpareTmStmp /256
		ld R7, #SpareTmStmp #256
		ld R4, #ScrReg0
		call Ld_TmStmp
		ld R6, #(RBuffer1+SpareTmStmp-SpareArray) /256
		ld R7, #(RBuffer1+SpareTmStmp-SpareArray) #256
		ld R4, #ScrRegC
		call Ld_TmStmp
		sub R3, R15
		sbc R2, R14
		sbc R1, R13
		sbc R0, R12
		srp #Wrk_Sys
	assume RP:Wrk_Sys
		jr GE, L_Spr_Inc
		dec R4			; account for old, bogus spare table
L_Spr_Move	call RBuf_To_Spr
L_Spr_Inc	inc R4			; note the arrval of a SpareTable
		cp R4, #2		; see if that's all there is
		jr NZ, L_SprTbl_More
;
L_Spr_End	call UpDate_SprTbl
		and SlfTst_Result, #0FFh-No_SprTbl
		ld R2, #Park_Heads /256
		ld R3, #Park_Heads #256
		call Bank_Call
		jp Bank_Ret
;********************************************************************
Ld_TmStmp	ld R5, #4		; load 4 bytes
Ld_Tm_Lp	ldei @R4, @RR6
		djnz R5, Ld_Tm_Lp
		ret


;********************************************************************
;
; Procedure: SprChkSum
;
;  This procedure calculates a 16-bit checksum over the contents
;  of the spare table, and stores the sum within the spare table.
;
; Inputs: none
;
; Outputs: none
;
; Side Effect: ScrReg1, ScrReg2 hold the calculated check byte on return
;
; Algorithm:
;
;  Begin
;   Sum:=0
;   SumPtr:=SpareArray
;   For i:=1 To Length(SpareArray) Do
;    Sum:=Sum+SpareArray[i-1]
;   SpareArray.ChkSum:=Sum
;  End
;
;********************************************************************

SprChkSum	ld R12, #SpareArray /256
		ld R13, #SpareArray #256
SprChk2		ld R14, #(SpareCheck-SpareArray) /256
		ld R15, #(SpareCheck-SpareArray) #256
		clr R1
		clr R2
SprChk_Lp	lde R0, @RR12
		add R2, R0
		adc R1,#0
		incw RR12
		decw RR14
		jr NZ, SprChk_Lp
		lde @RR12, R1		; store high byte of checksum
		incw RR12
		lde @RR12, R2		; store low byte of checksum
		jp Bank_Ret


;********************************************************************
;
; Function: Chk_SprChk
;
;  This function is responsible for verifying that the checksum
;  residing in the spare table is correct.
;
; Inputs: none
;
; Outputs: Chk_SprChk: BOOLEAN {zero flag}
;
; Algorithm:
;
;  Begin
;   TempSum:=SpareTable.CheckSum
;   SpareTable.CheckSum:=SprChkSum
;   If TempSum=SpareTable.CheckSum
;    Then Chk_SprChk:=true
;    Else Chk_SprChk:=false
;  End
;
;********************************************************************

Chk_SprChk	ld R2, #SpareCheck /256
		ld R3, #SpareCheck #256
		lde R0, @RR2
		incw RR2
		lde R1, @RR2
		srp #Wrk_Scr
	assume RP:Wrk_Scr
		call SprChkSum
		srp #Wrk_Sys
	assume RP:Wrk_Sys
Chk_Spr2	xor R0, ScrReg1		; side effect: ScrReg 1:2 hold new checkbyte
		xor R1, ScrReg2
		or R0, R1
		clr R0
		jr NZ, Chk_Spr_End
		ld R0, #1
Chk_Spr_End	or R0, R0		; set zero flag
		jp Bank_Ret


;********************************************************************
;
; Function: Spr
;
;  This function returns the logical block number associated
;  with the parameter passed in. Because there are only two spare
;  blocks containing the spare table, this function accepts
;  only ODD or EVEN input params.
;
; Inputs: SpareTableIndex: BYTE {R8}
;
; Outputs: Spr: 3 BYTES {R12:14}
;
; Algorithm:
;
;  Begin
;   If SpareTableIndex is EVEN
;    Then Spr:=SprBlk0
;    Else Spr:=SprBlk1
;  End
;
;********************************************************************

Spr		and R8, #1		; If SpareTableIndex is EVEN
		jr NZ, Spr_Odd
;
		ld R12, #HiSpr0
		ld R13, #MidSpr0
		ld R14, #LoSpr0
		jr Spr_End
;
Spr_Odd		ld R12, #HiSpr1
		ld R13, #MidSpr1
		ld R14, #LoSpr1
Spr_End		jp Bank_Ret


;********************************************************************
;
; Procedure: UpDate_SprTbl
;
;  This procedure is responsible for updating the spare table
;  to both of its locations on disk after a change has been made
;  to the table
;
; Inputs: none
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   SpareTmStamp:=SpareTmStamp+1
;   SprChkSum
;   ZeroBlock
;   WBuffer1.BlockID:=PassWord
;   For i:=0 To 1 Do
;    MoveBlock(WriteBuffer, SpareTable)
;    TempCyl, TempHead, TempSector:=
;                   Get_Cyl_H_S(SrchSpTabl(SpareTable(Spr(i)))
;    If table not found in SpareTable Then Abort
;    Seek(TemoCyl, TempHead, TempSector)
;    If nor(WriteVerify(Conservative))
;     Then
;      If Recovery And WriteVerify.ErrorCode=Ex_ReadErr
;       Then Exit UpDate_SprTbl
;       Else
;        ZeroBlock
;        WriteBlock
;        MoveBlock(Buffer2, SpareTable)
;        SpareBlock(True, SpareTable, Write_Op, Spr(i))
;  End
;
;********************************************************************

UpDate_SprTbl	ld R12, Cylinder
		ld R13, Cylinder+1
		ld R14, Head
		ld R15, Sector
		call Ext_Push
		ld R2, #SpareTmStmp /256
		ld R3, #SpareTmStmp #256
		ld R0, #Wrk_Sys+12
		ld R1, #4		; get 4 bytes
UpDate_1	ldei @R0, @RR2
		djnz R1, UpDate_1
		add R15, #1		; increment the count
		adc R14, #0
		adc R13, #0
		adc R12, #0
		ld R0, #Wrk_Sys+12
		ld R1, #4		; move 4 bytes
		ld R2, #SpareTmStmp /256
		ld R3, #SpareTmStmp #256
UpDate_2	ldei @RR2, @R0
		djnz R1, UpDate_2
		call SprChkSum
		ld R4, #2		; write the table to the disk twice
		clr R5			; spare table index
SprB_Lp		call Spr_To_WrBuf
		ld R14, #(WBuffer1+BlockID) /256
		ld R15, #(WBuffer1+BlockID) #256
		call Load_PassWord
		ld R8, R5
		call Spr
		ld Data_Type, #SprTbl_Type
		ld R2, #CnvrtLogical /256
		ld R3, #CnvrtLogical #256
		call Bank_Call
		jr NZ, SprB_Seek
		call Abort
;
SprB_Seek	ld Seek_Type, #Access_Offset
		call Seek
		ld BlkStat, #S_Block	; say that we've got a spare block
		ld Data_Type, #SprTbl_Type
		ld RdErrCnt, #10	; prime the counter for write error
		ld R2, #WrVer_Common /256
		ld R3, #WrVer_Common #256
		call Bank_Call
		jr NZ, Spr_Next
;
		tm Excpt_Stat, #Recovery	; If Recovery Then ...
		jr NZ, Rcvr_SprTbl
		call Abort
;
Rcvr_SprTbl	ld R1, RdErrCnt		; check for noisy read
		and R1, #0Fh
		cp R1, #SprThresh
		jr LE, Spr_Next
		ld R2, #WBuffer1 /256
		ld R3, #WBuffer1 #256
		call ZeroBlock
		ld R2, #Wr_Common /256
		ld R3, #Wr_Common #256
		call Bank_Call
		ld R8, R5
		call Spr
		ld R2, #Spr_Enter /256
		ld R3, #Spr_Enter #256
		call Bank_Call
		jr Z, Rcvr_SprTbl
;
Spr_Next	inc R5			; do next table
		djnz R4, SprB_Lp
		call Ext_Pop
		call Seek
		jp Bank_Ret


;********************************************************************
;
; Function: Chk_PassWord
;
;  This function is responsible for checking if the 32 bits
;  pointed to by the input parameter match with the controller's
;  32 bit password
;
; Inputs: PassWordPtr: PTR {ScrReg2:3}
;
; Outputs: Chk_PassWord: BOOLEAN {zero flag}
;
; Algorithm:
;
;  Begin
;   If @PassWordPtr=PassWord
;    Then Chk_PassWord:=true
;    Else Chk_PassWord:=false
;  End
;
;********************************************************************

Chk_PassWord	srp #Wrk_Scr
	assume RP:Wrk_Scr
		ld R4, #4		; check 4 bytes
		ld R14, #PassWord /256
		ld R15, #PassWord #256
Chk_P_Lp	ldc R0, @RR14		; get a byte of the password
		incw RR14
		lde R1, @RR2		; get a byte of test string
		incw RR2
		cp R0, R1
		clr R0			; assume failure
		jr NZ, Chk_P_End
		djnz R4, Chk_P_Lp
		ld R0, #1
Chk_P_End	or R0, R0		; set flags
		srp #Wrk_Sys
	assume RP:Wrk_Sys
		jp Bank_Ret


;********************************************************************
;
; Procedure: Load_PassWord
;
;  This procedure loads the 32-bit password into the memory
;  location pointed to by the input parameter
;
; Inputs: Destination: PTR {RR14}
;
; Outputs: none
;
;********************************************************************

Load_PassWord	ld R2, #PassWord /256
		ld R3, #PassWord #256
		ld R1, #4		; move 4 bytes
Lpw_Lp		ldc R0, @RR2
		lde @RR14, R0
		incw RR2
		incw RR14
		djnz R1, Lpw_Lp
		jp Bank_Ret


;********************************************************************
;
; Procedure: SpareCount
;
;  This procedure is responsible for incrementing/decrementing
;  the Spare/Bad Block count. If the total count exceeds
;  MaxSpares-5 then a status bit is set for this command only.
;
; Inputs: Command: 2 BITS [R0/Bits 1:0}
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   Case Command Of
;    0: Increment the spare count
;    1: Increment the bad block count
;    2: Decrement the bad block count
;    Otherwise Abort
;   If SpareCount+BadBlockCount>=MaxSpares-5
;    Then
;     Excpt_Stat.SprTbl_Warn:=true
;     SetStatus(SprBlk_Warn)
;  End
;
;********************************************************************

SpareCount	ld R1, R0		; get command
		and R1, #0FCh		; check for illegal command
		jr Z, SprCnt_1
		ld R9, R0
		call Abort
;
SprCnt_1	ld R2, #SprCount /256
		ld R3, #SprCount #256
		lde R1, @RR2		; assume spare count increment
		inc R1
		cp R0, #0		; check for Inc_Spare
		jr Z, S_C_Spare
		incw RR2
		lde R1, @RR2		; get bad block count
		cp R0, #1		; check for Inc_BadBlock
		jr Z, S_C_BadInc
		dec R1
		jr S_C_BadEnd
;
S_C_BadInc	inc R1
S_C_BadEnd	lde @RR2, R1		; store new count
		jr SprCnt_End
;
S_C_Spare	lde @RR2, R1		; store new count
SprCnt_End	call Chk_SprCnt
		jp Bank_Ret
;
Chk_SprCnt	ld R2, #SprCount /256
		ld R3, #SprCount #256
		lde R0, @RR2		; get spare count
		incw RR2
		lde R1, @RR2		; get bd block count
		add R0, R1
		cp R0, #76-5
		jr LT, Chk_Spr_Ret
		or Excpt_Stat, #SprTbl_Warn
		call SS_SprWarn
Chk_Spr_Ret	jp Bank_Ret



;********************************************************************
; Module Math.B1.Assem
;
; This module contains all the special purpose math routines
; used in the Widget firmware.
;
;********************************************************************

;********************************************************************
;
; Function: Div3_k, Div3_19, Div3_38, Div3_76
;
;  This function performs the following 24 bit arithmetic operations:
;  Div3_19: Result <-- A div 19
;  Div3_38: Result <-- A div 38
;  Div3_76: Result <-- A div 76
;
; Inputs:  A: 3 BYTES {R12:14}
;
; Outputs: Result: 3 BYTES {R0:2}
;
; Local Variables: Divisor:  3 BYTES {R12:14}
;                  Quotient: 3 BYTES {R7:9}
;                  Dividend: 3 BYTES {R4:6}
;
; Algorithm: (div 19)
;
;  Begin
;   Divisor:=$013000 {19*2^(13-1)}
;   Quotient:=0
;   For i:=1 To 13 Do
;   If Dividend>=Divisor
;    Then
;     Dividend:=Dividend-Divisor
;     Quotient:=Quotient*2 +1
;    Else Quotient:=Quotient*2
;   Divisor:=Divisor div 2
;  End
;
;********************************************************************

Div3_76 	ld ScrRegC, #004h	; Divisor:=$04C000
		ld ScrRegD, #0C0h
		ld ScrRegE, #000h
		jr Div3_x
;
Div3_38		ld ScrRegC, #002h	; Divisor:=$026000
		ld ScrRegD, #060h
		ld ScrRegE, #000h
		jr Div3_x
;
Div3_19		ld ScrRegC, #001h	; Divisor:=$013000
		ld ScrRegD, #030h
		ld ScrRegE, #000h
;
Div3_x		ld ScrReg4, R12		; pass dividend to routine
		ld ScrReg5, R13
		ld ScrReg6, R14
		push RP			; save context
		srp #Wrk_Scr
	save
	assume RP:Wrk_Scr
		clr R7			; Quotient:=0
		clr R8
		clr R9
		ld R10, #13		; For i:=1 To 13 Do
Div3_19_Lp	rcf			; clear carry
		rlc R9			; Quotient:=Quotient*2
		rlc R8
		rlc R7
		ld R0, R4		; If Dividend>=Divisor
		ld R1, R5
		ld R2, R6
		call Sub3
		jr C, Div3_19_Div2
;
		ld R4, R0		; Dividend:=Dividend-Divisor
		ld R5, R1
		ld R6, R2
		add R9, #1		; Quotient:=Quotient+1
		adc R8, #0
		adc R7, #0
Div3_19_Div2	rcf			; clear carry flag
		rrc R12			; Divisor:=Divisor div 2
		rrc R13
		rrc R14
		djnz R10, Div3_19_Lp
;
		pop RP			; context switch
	restore
		ld R0, >ScrReg7		; pass quotient to caller
		ld R1, >ScrReg8
		ld R2, >ScrReg9
		jp Bank_Ret


;********************************************************************
;
; Function: MulR0_m  {multiply R0 by m}
;
;  This function multiplies the byte in R0 by m where:
;  for 10MByte Widget: m=256
;  for 20MByte Widget: m=512
;  for 30MByte Widget: m=1024
;
; Inputs:  A: BYTES {R0}
;
; Outputs: Result: 3 BYTES {R12:14}
;
;********************************************************************

MulR0_m 	clr R14		; Result:=R0*256
		ld R13, R0
		clr R12
	if W_20MB || W_40MB
		rlc R12		; Result:=Result*2
		rlc R13
		rlc R14
	endif
	if W_40MB
		rlc R12		; Result:=Result*2
		rlc R13
		rlc R14
	endif
		jp Bank_Ret


;********************************************************************
;
; Function: DivHdsSctrs  {divide by Heads*Sectors}
;
;  This function takes the 24 bit word passed to it and returns
;  a 2 byte integer (representing the cylinder number) and
;  the remainder from the divide.
;
; Inputs:  A: 3 BYTES {R12:14}
;
; Outputs: Cylinder:  WORD {RR0}
;          Remainder: BYTE {R3}
;
;********************************************************************

DivHdsSctrs
	if W_10MB
		call Div3_38		; return with R0:2 = result
	endif
	if W_20MB || W_40MB
		call Div3_76		;   ScrReg4:6 = remainder
	endif
		ld R0, R1		; HiCyl
		ld R1, R2		; LoCyl
		ld R3, >ScrReg6		; Remainder
		jp Bank_Ret


;********************************************************************
;
; Function: DivSctrs  {divide by Sectors}
;
;  This function takes the 1 byte word passed to it and returns
;  a 1 byte integer (representing the head number) and
;  the remainder from the divide (the remainder is the sector #).
;
; Inputs:  A: BYTES {R3}
;
; Outputs: Sector:  BYTE {R2}
;          Remainder: BYTE {R3}
;
; Algorithm:
;
;  Note that there are only 2 heads on the Widget, therefore
;  if A>NumberOfSectors then Head:=1, else Head:=0 and
;  if A>NumberOfSectors then Sector:=A-NumberOfSectors
;   else Sector:=A.
;  Also, the sector is logically remapped to account for
;  different interleaved drives. The mapping table is located
;  in the SpareTable: when the physical sector is founf it's
;  value is used as an index into the Map_Table and the value
;  stored at that location of the table becomes the new sector.
;
;********************************************************************

DivSctrs	ld R0, #NbrSctrs	; load number of sectors into R0
		cp R3, R0		; check for A greater/equal
		jr LT, D_Sctrs1
		ld R2, #1		; select head 1
		sub R3, R0
		jr D_Sctrs2
D_Sctrs1	clr R2			; select head 0
D_Sctrs2	ret


ReMap_Sector	push R14		; save regs
		push R15
		ld R14, #Map_Table /256
		ld R15, #Map_Table #256
		add R15, R0		; index into table
		adc R14, #0
		lde R0, @RR14		; get remapped sector
		pop R15
		pop R14
		jp Bank_Ret



;********************************************************************
; Module Srvo1.B1.Assem
;
; This module contains all the specifications and source code for
; controlling the Widget Servo Board (i.e. communication protocol,
; seek and head positioning, spare table lookup, etc)
;
;********************************************************************

;********************************************************************
;
; Procedure: Seek
;
;  This procedure accepts cylinder, head, and sector values
;  and then calls PositionHeads.
;
; Inputs:  Cylinder: WORD {R12}
;          Head:     BYTE {R14}
;          Sector:   BYTE {R15}
;
; Outputs: none
;
; Global Variables Changed: Cylinder, Head, Sector
;
; Algorithm:
;
;  Begin
;   LastSeekAddress:=CurrentSeekAddress
;   DiskStat.Parked:=false
;   If DiskStat.SeekComplete
;    Then
;     i:=4
;     While i>0 and not(PositionHeads(Wait, Dmt_Seek, Cylinder,
;                                     Head, Sector) Do
;      If not(Recovery) Then Abort
;      i:=i+1
;      If i=0 Then Abort
;    Else wait up to 1 sec for ServoReady, if timeout Abort
;   LastCylinder:=GlobalCylinder
;   LastHeas:=GlobalHead
;   LastSector:=GlobalSector
;   GlobalCylinder:=Cylinder
;   GlobalHead:=Head
;   GlobalSector:=Sector
;   SectorCount:=SectorCount+1
;   DiskStat.SeekComplete:=true
;   DiskStat.OnTrack:=true
;  End
;
;********************************************************************

Seek		call Ext_Push
		ld Lst_HiCyl, Cylinder	; save last seek address
		ld Lst_LoCyl, Cylinder+1
		ld Lst_Head, Head
		ld Lst_Sector, Sector
		and DiskStat, #0FFh-Parked
		tm DiskStat, #SeekComplete	; check for head/arm motion
		jr Z, Seek_Wait
Seek_ReTry	ld R1, #4		; four attempts
;
Seek_Lp		ld R8, #Wait
		call PosHeads
		jr NZ, Seek_End
		and DiskStat, #0FFh-Offset_On
		ld Seek_Type, #Access_Offset
		tm Excpt_Stat, #Recovery
		jr Z, Seek_Fault
		push R1			; save counter
		ld R2, #20 /256
		ld R3, #20 #256
		call MsWait		; wait 200 ms before retrying
		pop R1			; get counter back
		djnz R1, Seek_Lp
;
Seek_Fault	ld R0, #0		; status byte 0
		ld R1, #Stat_Srvo
		call SetStatus
		or DiskStat, #SeekComplete
		call Abort
;
Seek_Wait	call Wait_xMs
		jr Z, Seek_Lp
;
Seek_End	or DiskStat, #On_Track+SeekComplete
		ld Cylinder, R12
		ld Cylinder+1, R13
		call SelectHead
		ld Sector, R15
		incw SeekCount
		call Chk_Offset
		jr Z, Seek_Lp
		call Ext_Pop
		jp Bank_Ret


;********************************************************************
;
; Procedure: Wait_xMs
;
;  This procedure performs a busy wait until SioRdy and ServoRdy.
;
; Inputs: x: BYTE {R1}
;
;********************************************************************

Wait_xMs	push R1
		ld R1, #100
Wait_xMs2	and IRQ, #0FFh-Timer1
Wait_xMs1	tm P2, #SioRdy		; servo finished?
		jr Z, Wait_xMs3
;
		call LoadStatus		; check status port
		tm R0, #ServoRdy
		jr NZ, Wait_xMs_End
;
Wait_xMs3	tm IRQ, #Timer1		; timer expired?
		jr Z, Wait_xMs1
		dec R1			; decrement counter
		jr NZ, Wait_xMs2	;  and start again
;
Wait_xMs_End	pop R1
		ret


;********************************************************************
;
; Function: PosHeads  {position heads}
;
;  This function is responsible for positioning the heads of
;  the drive. There are two ways of positioning the heads via this
;  routine: 1) Specify a logical block number (where all the
;  user accessible blocks are numbered from 0 to n-1) or
;  2) by specifying the Cylinder, and Head number of any track
;  on the disk surface.
;
;  The way that PositionHeads interprets the block number is
;  controlled by the input parameter Logical (i.e. if Logical
;  Then BlockNumber is a Logical BlockNumber, Else BlockNumber
;  is Cylinder and Head).
;
;  The Spare Table is searched ONLY if Logical is true, this is
;  consistent with allowing a diagnostic program to get to any
;  spot on the disk independent of the data stored there.
;
; Inputs:  Wait:     BOOLEAN {R8/bit 6}
;          Parent:   BYTE {R11}
;          Cylinder: WORD {R12}
;          Head:     BYTE {R14}
;          Sector:   BYTE {R15}
;
; Outputs: PositionHeads: BOOLEAN {zero flag is true
;                                  if PositionHeads is true}
;
; Global Variables Changed: Cylinder, Head, Sector, Cur_Cyl
;
; Local Variables: Seek, PositionDone: BOOLEAN {R4, R3}
;                  Cyl, Magnitude:     WORD {RR12, R5/R6}
;                  Head, Sector:       BYTE {R14, R15}
;                  PosRetry, SioRetry: BYTE {R7, R10}
;                  Direction:          BYTE {R9}
;
; Algorithm:
;
;  Begin
;   SetDeadManTimer(PositionHeads, Parent)
;   SelectHead(Head)
;   Seek:=CalcMagnitudeDirection(Cyl, Magnitude, Direction)
;   If GlobalHead<>Head And DiskStatus.Write Then Seek:=True
;   GlobalHead:=Head
;   GlobalSector:=Sector
;   PosRetry:=2
;   Repeat
;    If Seek And ServoOk(RecalcMagAndDir)  {boolean returned in R0}
;     Then
;      If RecalcMagAndDir Then Seek:=CalcMagnitudeDirection(Cyl,
;                                           Magnitude, Direction)
;      SioRetry:=2
;      While SioRetry>0 And not(ServoStore(AccessCar+Direction+
;                     Magnitude[1}, Magnitude[2], AutoOffset, 0) Do
;       If Recovery
;        Then
;         ResetServo
;         SioRetry:=SioRetry-1
;        Else
;         SioRetry:=0
;      If Wait And SioRetry>0
;       Then While not(ServoReady) Do Begin End
;    PositionDone:=not(ServoError)
;    PosRetry:=PosRetry-1
;   Until not(Recovery) Or PositionDone Or PosRetry=0
;   ClearDeadManTimer
;   If PositionDone Then CurCyl:=LocalCylinder
;   PositionHeads:=PositionDone
;  End
;
;********************************************************************
	assume RP:Wrk_Sys
PosHeads	call Ext_Push		; save callers variables
		ld R14, #1
		call SelectHead
		call ServoOk
		jr Z, PosHds_Done
;
		call CalcMagDir
		jr Z, PosHds_Done1
;
		and DiskStat, #0FFh-On_Track-Parked-SeekComplete-Offset_On
		cp Seek_Type, #Access_Offset
		jr Z, loc_26B6
		tm Excpt_Stat, #Recovery
		ld ScrReg2, #0
		jr Z, loc_26BE
		call loc_2807
		ld ScrReg2, #0
		jr LT, $+14
		ld ScrReg2, R0
		jr $+5
loc_26B6	ld ScrReg2, #40h
		ld ScrReg0, #90h
		jr loc_16C1
loc_26BE	ld ScrReg0, #80h
loc_16C1	srp #Wrk_Scr
	assume RP:Wrk_Scr
		or R0, Wrk_Sys+9	; plus direction
		or R0, Wrk_Sys+5	; plus MSB magnitude
		ld R1, Wrk_Sys+6	; LSB magnitude
		ld R3, #S_Rate_57_6
		srp #Wrk_Sys
	assume RP:Wrk_Sys
		call Set_Dmt
		call ServoCmnd
		di
		cp Seek_Type, #Access_Offset
		jr NZ, loc_16DE
		or DiskStat, #Offset_On
loc_16DE	ld R14, #61h
		ld R15, #0A8h
loc_16E2	call LoadStatus
		tm R0, #10h
		jr NZ, PosHds_Done1
		tm R8, #40h
		jr Z, PosHds_Done1
		decw RR14
		jr Z, PosHds_Done1
		tm R0, #20h
		jr Z, loc_16E2
		or DiskStat, #SeekComplete
PosHds_Done1	ld Cur_Cyl, R12
		ld Cur_Cyl+1, R13
;
PosHds_Done	call Ext_Pop
		call LoadStatus1
		jp Bank_Ret
;
LoadStatus1 	call LoadStatus
		ld R1, R0
		com R0
		rl R0
		and R0, R1
		and R0, #20h
		ret


;********************************************************************
;
; Procedure: SelectHead
;
; Inputs: Head: BYTE {R14}
;
;********************************************************************

SelectHead	or R14, R14		; test for zero
		jr Z, SlctHd_0
		or P3, #Hs0 		; set HeadSelect0
		jr SlctHd_End
SlctHd_0	and P3, #0FFh-Hs0 	; clear headSelect0
SlctHd_End	ld Head, R14
		jp Bank_Ret


;********************************************************************
;
; Function: CalcMagDir
;
; Inputs: Cylinder:  WORD {RR12}
;         Magnitude: WORD {R5/R6}
;         Direction: BYTE {R9}
;
;********************************************************************

CalcMagDir	ld R1, Cur_Cyl		; get current cylinder
		ld R2, Cur_Cyl+1
		sub R2, R13		; subtract target cylinder
		sbc R1, R12
		clr R4
		jr LT, CalcMD_back	; negative --> go backwards
;
		clr R5			; clear magnitude
		clr R6
		ld R9, #0		; clear direction
		clr R3
		or R3, R2
		or R3, R1
		jr Z, CalcMD_There	; distance is zero!
		jr CalcMD_End
;
CalcMD_back	com R2			; make magnitude positive
		com R1
		add R2, #1
		adc R1, #0
		ld R9, #4		; set direction
;
CalcMD_End	ld R5, R1		; set magnitude
		ld R6, R2
		or R4, #1
;
CalcMD_There	jp Bank_Ret


;********************************************************************
;
; Function: ServoOk
;
; Inputs: RecalMagDir: BYTE {R0}
;
;********************************************************************

ServoOk		tm Excpt_Stat, #Recovery
		jr Z, loc_1789
		call LoadStatus
		tm R0, #ServoErr
		jr Z, loc_1774
		call loc_278F
		ld R0, #40h
		call Restore
		call Buf2_To_WrBuf
		jr loc_1789
;
loc_1774	tm R0, #20h
		jr NZ, loc_1789
		call loc_278F
		call ResetServo
		ld R0, #40h
		call Restore
		call Buf2_To_WrBuf
		jr loc_1789
;
loc_1789	call LoadStatus1
		jp Bank_Ret
;
loc_278F	ld R0, #0		; status byte 0
		ld R1, #Stat_Srvo
		call SetStatus
		tm DiskStat, #Wr_Op
		jr Z, loc_279E
		call WrBuf_To_Buf2
loc_279E	ret


;********************************************************************
;
; Procedure: Auto_Offset
;
;********************************************************************

Auto_Offset	call Ext_Push
		push Head
		ld R14, #1
		call SelectHead
		call Set_Dmt
		srp #Wrk_Scr
	assume RP:Wrk_Scr
		ld R0, #90h
		ld R1, #0
		ld R2, #40h
		ld R3, #80h
		srp #Wrk_Sys
	assume RP:Wrk_Sys
		call ServoCmnd
		call LoadStatus
		srp #Wrk_Scr
	assume RP:Wrk_Scr
		ld R0, #0
		ld R1, #0
		ld R2, #0
		ld R3, #1
		srp #Wrk_Sys
	assume RP:Wrk_Sys
		call ServoStatus
		call Clr_Dmt
		ld R2, #(SStatus0+1) /256
		ld R3, #(SStatus0+1) #256
		lde R0, @RR2
		xor R0, #1Fh
		and R0, #3Fh
		push R0
		ld R12, Cylinder
		ld R13, Cylinder+1
		call loc_2807
		pop R0
		lde @RR2, R0
		pop R14
		call SelectHead
		call Ext_Pop
		or DiskStat, #1
		jp Bank_Ret


;********************************************************************
;
; Procedure: Chk_Offset
;
;********************************************************************

Chk_Offset	cp Seek_Type,#Access_Offset
		jr NZ, Chk_Offset_End
ChkOff_NoOff	tm DiskStat, #Offset_On
		jr NZ, Chk_Offset_End
		call Auto_Offset
Chk_Offset_End	jp Bank_Ret



loc_2807	ld R0, R12
		ld R1, R13
		ld R2, #5
		rcf
loc_180E	rrc R0
		rrc R1
		djnz R2, loc_180E
		ld R2, #SpareEnd /256
		ld R3, #SpareEnd #256
		add R3, R1
		adc R2, #0
		lde R0, @RR2
loc_181F	tm R0, #20h
		ld R1, #0
		jr NZ, loc_2828
		ld R1, #80h
loc_2828	and R0, #1Fh
		cp R0, #10
		push FLAGS
		or R0, R1
		pop FLAGS
		jp Bank_Ret



;********************************************************************
; Module Srvo2.B1.Assem
;
; This module holds those routines that make up most of the
; auxiliary servo commands.
;
;********************************************************************

;********************************************************************
;
; Procedure: SrvoCmnd  {send the servo a command}
;
; Inputs:  CommandString: 4 BYTES {R0:3}
;
; Outputs: none
;
; Global Variables Changed: SrvoCmndBuffer
;
; Local Variables: SioRetry: BYTE {R8}
;
; Algorithm:
;
;  Begin
;   SrvoCmndBuffer[CommandByte]:=R0
;   SrvoCmndBuffer[LoDiffByte]:=R1
;   SrvoCmndBuffer[OffsetByte]:=R2
;   SrvoCmndBuffer[StatusByte]:=R3
;   SioRetry:=4
;   While not(ServoStore(CommandString) And SioRetry>0 Do
;    SioRetry:=SioRetry-1
;   If SioRetry=0 Abort
;  End
;
;********************************************************************

ServoCmnd	srp #Wrk_Sys2
	assume RP:Wrk_Sys2
		call Load_SrvoCmnd
Srv_C_Lp1	ld R8, #4		; SioRetry:=4
		ld R4, ScrReg0		; save command
		ld R5, ScrReg1
		ld R10, ScrReg2
		ld R11, ScrReg3
;
Srv_C_Lp	call ServoStore
		jr NZ, Srv_C_End
		ld ScrReg0, R4		; restore command
		ld ScrReg1, R5
		ld ScrReg2, R10
		ld ScrReg3, R11
		djnz R8, Srv_C_Lp
		call Abort
Srv_C_End	srp #Wrk_Sys
	assume RP:Wrk_Sys
		jp Bank_Ret


;********************************************************************
;
; Procedure: ServoStatus  {read a status location from the servo}
;
; Inputs:  CommandString: 4 BYTES {R0:3}
;          BufferPtr:     PTR {RR14}
;
; Outputs: none
;
; Global Variables Changed: SrvoCmndBuffer
;                           @BufferPtr
;
; Local Variables: SioRetry: BYTE {R8}
;
; Algorithm:
;
;  Begin
;   ServoCmnd
;   If not(ServoLoad(BufferPtr)) Then Abort
;  End
;
;********************************************************************

ServoStatus	push R8			; save for caller
		ld R8, #4
Servo_StLp1	call ServoCmnd
		call ServoLoad
		jr Z, Srv_St_End
		ld ScrReg0, Wrk_Sys2+4
		ld ScrReg1, Wrk_Sys2+5
		ld ScrReg2, Wrk_Sys2+10
		ld ScrReg3, Wrk_Sys2+11
		djnz R8, Servo_StLp1
		call Abort
Srv_St_End	pop R8
		jp Bank_Ret


;********************************************************************
;
; Function: ServoStore
;
;  The function of this routine is to transmit a command string
;  to the Servo Processor. The command string is expected to
;  reside in the global variable SrvoCmndBuffer (thus providing a
;  record of the last command sent to the servo). ServoStore then
;  attempts to complete the transmission to the Servo, and returns
;  a boolean variable indicating whether the transmission was
;  completed or not.
;
; Inputs:  Parent: BYTE {R1}
;
; Outputs: ServoStore: BOOLEAN {zero flag, true if transmission failed}
;
; Local Variables: Retry: BYTE {R6}
;                  i:     BYTE {R4}
;                  j:     BYTE {R5}
;
; Algorithm:
;
;  Begin
;   SrvoCmndBuffer[CheckByte]:=GenerateCheckByte(PTR(SrvoCmndBuffer),
;                                                Length(SrvoCmndBuffer))
;   Retry:=10
;   Repeat
;    For i:=1 To 5 Do
;     While IRQ.Serial_Output=false Or not(SioReady) Do
;      Begin End
;     Sio.Data:=SrvoCmndBuffer[i]
;    Retry:=Retry-1
;    For j:=1 To (value for 250µs wait) Do Begin End
;   Until not(SioReady) Or Retry=0
;   For j:=1 To (value for 400µs wait) Do Begin End
;    If Retry=0
;     Then ServoStore:=false
;     Else ServoStore:=true
;  End
;
;********************************************************************

ServoStore	push RP			; save context
		srp #Wrk_Scr
	save
	assume RP:Wrk_Scr
		ld R14, #SrvoCmndBuf /256
		ld R15, #SrvoCmndBuf #256
		ld R8, #S_Cmnd_Len-1
		call Gen_Chk_Byte
		ld R6, #10		; Retry:=10
Srvo_St_Rpt	ld R14, #SrvoCmndBuf /256
		ld R15, #SrvoCmndBuf #256
		ld R4, #S_Cmnd_Len
;
Srvo_St_1	tm P2, #SioRdy		; While not(SioReady)
		jr Z, Srvo_St_1
;
		and IRQ, #0FFh-Serial_Out-Serial_In	; clear old interrupts
		lde R0, @RR14		; get a command byte
		ld sio, R0		; send it to servo
		incw RR14		; point to next command byte
;
Srvo_St_2	tm IRQ, #Serial_Out	; While IRQ.Serial_Out=false
		jr Z, Srvo_St_2
;
		djnz R4, Srvo_St_1	; loop till all command bytes are sent
		and IRQ, #0FFh-Serial_Out	; clear old interrupts
		ld R5, #80
Srvo_Wt_1	djnz R5, Srvo_Wt_1	; do a 250µs wait
		tm P2, #SioRdy		; if not(SioReady) then Servo
		jr Z, Srvo_St_Exit	;  took the bytes and is munchin' on 'em
		djnz R6, Srvo_St_Rpt	; Retry:=Retry-1
;
Srvo_St_Exit	or R6, R6		; test for Retry=0
		pop RP			; return to original context
	restore
		ret


;********************************************************************
;
; Function: ServoLoad
;
;  This function is responsible for reading a status back
;  from the servo. The bytes that it receives are stored into a
;  buffer that is passed into the routine in the form of a PTR.
;  ServoLoad passes back to the caller a boolean variable describing
;  whether the checkbyte was valid (i.e. the transmission was a
;  success).
;
; Inputs:  Parent:    BYTE {R1}
;          BufferPtr: PTR {R2:3}
;
; Outputs: ServoLoad: BOOLEAN {zero flag, true if transmission failed}
;
; Global Variables Changed: Buffer pointed to by RR2
;
; Local Variables: i:          BYTE {R4}
;                  TempBufPtr: PTR {RR10}
;
; Algorithm:
;
;  Begin
;   For i:=1 To 5 Do
;    While not(IRQ.Serial_Input) Do Begin End
;    Buffer[i]:=Sio.Data
;   ServoLoad:=Check_Check_Byte(Ptr(Buffer),5)
;  End
;
;********************************************************************

ServoLoad	push RP			; save context
		srp #Wrk_Scr 		; context switch
	save
	assume RP:Wrk_Scr
		ld R14, #SStatus0 /256
		ld R15, #SStatus0 #256
		ld R4, #5		; load i
;
Srvo_Ld_Wt	tm IRQ, #Serial_In
		jr Z, Srvo_Ld_Wt
		and IRQ, #0FFh-Serial_In	; clear old interrupts
		ld R0, SIO		; read SIO
		lde @RR14, R0		; and store in buffer
		incw RR14		; point to next location in buffer
		djnz R4, Srvo_Ld_Wt	; loop till all bytes are read
;
		ld R14, #SStatus0 /256	; get original buffer ptr
		ld R15, #SStatus0 #256
		ld R8, #4		; length of buffer
		call Chk_Chk_Byte
		pop RP			; context switch
	restore
		ret


;********************************************************************
;
; Function: Restore
;
;  This function performs a Recal operation on the servo.
;  Normally, a ReadHeader operation is to be performed after
;  the Servo comes ready, and the current cylinder is to be
;  subsequently updated. This mechanism (doing the ReadHeader)
;  can be turned off by clearing the RdHdrRecal bit in the
;  exception working register set.
;
; Inputs:  RecalType: BYTE {R0}
;
; Outputs: Restore: BOOLEAN {zero flag, true if not(ServoRdy)}
;
; Algorithm:
;
;  Begin
;   ServoCmnd(RecalType, 0, 0, 57.6kBaud)
;   While not(ServoRdy) And 2 seconds hasn't gone by Do Begin End
;   If DiskStat.RdHdrRecal and ServoRdy
;    Then If not(UpDate_Cur_Cyl) Then Abort
;    Else
;     If RecalType=DataRecal
;      Then CurCyl:=Init_Cyl
;      Else CurCyl:=Max_Cyl
;   Restore:=ServoRdy
;  End
;
;********************************************************************

Restore		push R0			; save Recal type for later
		call Ext_Push
		call Set_Dmt
		srp #Wrk_Scr
	assume RP:Wrk_Scr
		pop R0			; get back Recal type
		push R0
		clr R1
		clr R2
		ld R3, #S_Rate_57_6
		srp #Wrk_Sys
	assume RP:Wrk_Sys
		call ServoCmnd
		call Clr_Dmt
		and DiskStat, #0FFh-On_Track
		call Set_SeekNeeded	; invalidate cache contents
		ld Cur_Cyl, #HiMaxCyl	; assume FrmtRecal
		ld Cur_Cyl+1, #LoMaxCyl
		pop R6			; load Recal type from storage
		cp R6, #FrmtRecal
		jr Z, Rest_Up2
		ld Cur_Cyl, #Init_HiCyl	; otherwise DataRecal
		ld Cur_Cyl+1, #Init_LoCyl
Rest_Up2	clr R5
		ld R4, #0D5h		; max time to wait is two seconds
;
Restore_Lp	call LoadStatus
		tm R0, #ServoRdy
		jr NZ, Rest_UpDate
		decw RR4
		jr NZ, Restore_Lp
		clr R0			; pass error status to exit
		jr Restore_End
;
Rest_UpDate	tm DiskStat, #RdHdrRecal
		jr Z, Rest_Up1
		cp R6, #FrmtRecal	; don't try to read headers here!
		jr Z, Rest_Up1
		call UpDate_Cur_Cyl
		jr NZ, Rest_Up1		; leave if positioned correctly
;
		call SS_NoHdr		; header err here is bad news!
		call Abort
;
Rest_Up1	ld R0, #1
Restore_End	push R0			; save result
		call Ext_Pop
		and DiskStat, #0FFh-On_Track-Parked-Offset_On
		or DiskStat, #SeekComplete
		pop R0
		or R0, R0		; set zero flag
		jp Bank_Ret


;********************************************************************
;
; Procedure: SrvoRcvry  {servo recovery}
;
;  This procedure's responsibility is to do everything within reason
;  to make certain that the Servo Processor is Healthy, Wealthy, and
;  Wise; and if it can't then there is no point in using the Servo.
;
; Inputs:  none
;
; Outputs: none
;
; Local Variables: i:     BYTE {R8}
;                  Error: BOOLEAN {R9}
;
; Global Variabled Changed: On_Track, Cur_Cyl
;
; Global Variables Used: Cylinder, Head, Sector
;
; Algorithm:
;
;  Begin
;   i:=4
;   Repeat
;    ZeroHeader
;    Error:=false
;    If ServoError
;     Then Error:=not(ServoOk)
;     Else
;      Error:=not(ReadHdr)
;      If not(Error)
;       Then
;        If ReadHdr.Cylinder<>Cylinder
;         Then
;          Cur_Cyl:=ReadHdr.Cylinder
;          On_Track:=false
;     If not(On_Track) Then Seek(Cylinder, Head, Sector)
;   Until i=0 Or not(Error)
;   If i=0 Then Abort
;  End
;
;********************************************************************

SrvoRcvry	call Ext_Push		; save caller's stuff
		ld R8, #4		; i:=4
;
Srvo_R_Rpt	call ZeroHeader
		call LoadStatus1
		jr NZ, Srvo_R_Sok
		call ServoOk
;
Srvo_R_Sok	call UpDate_Hdr		; If ReadHdr.Cylinder<>Cylinder
		jr NZ, Srvo_R_Leave
		ld R0, #1		; status byte 1
		ld R1, #Stat_Seek
		call SetStatus
		call ReSeek
		djnz R8, Srvo_R_Rpt
;
Srvo_R_Leave	call Ext_Pop		; get user's stuff back
		jp Bank_Ret


;********************************************************************
;
; Procedure: Load_SrvoCmnd  {load servo command buffer}
;
;  This procedure loads the global array ServoCmndBuffer
;  with the information contained within R0:3
;
; Inputs: SrvoCmndBuffer[0]: BYTE {R0}
;         SrvoCmndBuffer[1]: BYTE {R1}
;         SrvoCmndBuffer[2]: BYTE {R2}
;         SrvoCmndBuffer[3]: BYTE {R3}
;
; Outputs: none
;
;********************************************************************

Load_SrvoCmnd	ld R2, #SrvoCmndBuf /256
		ld R3, #SrvoCmndBuf #256
		ld R1, #4		; load 4 bytes
		ld R0, #ScrReg0		; start with what's in ScrReg0
Ld_S_Cmnd_Lp	ldei @RR2, @R0
		djnz R1, Ld_S_Cmnd_Lp
		ret


;********************************************************************
;
; Procedure: Park_Heads
;
;  This procedure moves the heads in a closed-loop fashion
;  (access command vs Park command) to a location away from the
;  user data area. The attempt here is to provide some additional
;  protection from power failures, and the such, from accidentally
;  writing bogus data on the disk.
;
; Inputs: none
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   Seek(HiParkCylinder, LowParkCylinder, 0, 0)
;   Set_SeekNeeded
;  End
;
;********************************************************************

Park_Heads	ld Seek_Type, #Access
		ld R12, #ParkCyl /256
		ld R13, #ParkCyl #256
		clr R14
		clr R15
		call Seek
		or DiskStat, #Parked
		and DiskStat, #0FFh-On_Track
		call Set_SeekNeeded	; invalidate cache contents
		jp Bank_Ret


;********************************************************************
;
; Procedure: ResetServo
;
;  This procedure is responsible for performing all the
;  necessary tasks in resetting the servo. This includes
;  setting the correct Baud rates (the servo comes up
;  at 19.2k Baud, but normally runs at 57.6k Baud) and
;  positioning the heads over the data field and keeping
;  the world straight about it (cylinder is set to
;  MaxDataCylinder because a DataRecal positions the heads
;  at the closest data cylinder to the inside)
;
; Inputs: none
;
; Outputs: none
;
; Global Variables Changed: Cylinder
;
; Algorithm:
;
;  Begin
;   ServoRst:=true
;   wait for 50ms to make certain that Reset is valid
;   ServoRst:=false
;   wait for 1 sec to allow motor and servo to get ready
;   If not(ServoRdy) or ServoError Then Abort
;   Baud Rate:=19.2k
;   If not(ServoStore(ReadStatus, x, x, BaudRate57.6+NormalStatus)
;    Then Abort
;   If not(ServoLoad(NormalReadStatusBuffer)
;    Then Abort
;   Baud Rate:=57.6k
;   If not(Restore(DataRecal)) Then Abort
;   DiskStatus.On_Track:=false
;  End
;
;********************************************************************

ResetServo	and P0, #0FFh-Not_ServoRst	; set Servo Reset
		ld R2, #5 /256
		ld R3, #5 #256
		call MsWait		; busy wait for 50ms
		or P0, #Not_ServoRst	; clear Servo Reset
		ld R2, #100 /256
		ld R3, #100 #256
		call MsWait		; busy wait for 1s
S_Rst_1		call LoadStatus		; get servo status
		tm R0, #ServoErr
		jr Z, S_Rst_Bd1
;
S_Rst_Abt1	ld R10, R0
		call Abort
;
S_Rst_Bd1	and TMR, #0FFh-T0_CntEn	; halt T0
		ld PRE0, #00Dh		; PRE0:=Mod-64, continuous, go to 19.2k
		ld T0, #1		; interrupt after 1 byte
		or TMR, #T0_CntEn+T0_Load
		call Set_Dmt
		srp #Wrk_Scr
	assume RP:Wrk_Scr
		ld R0, #ReadStatus	; try talking to the Servo
		clr R1
		clr R2
		ld R3, #S_Norm_Status+S_Rate_57_6
		srp #Wrk_Sys
	assume RP:Wrk_Sys
		call Load_SrvoCmnd
		call ServoStore
		jr Z, S_Rst_Comm
		call LoadStatus
		tm R0, #ServoErr
		jr Z, S_Rst_Ld
;
S_Rst_Comm	ld R10, #S_Store
S_Rst_Abt2	ld R8, #S_Rst_Abort
		call Abort
;
S_Rst_Ld	call ServoLoad
		ld R7, #S_Load
		jr NZ, S_Rst_Abt2
		call Clr_Dmt
		and TMR, #0FFh-T0_CntEn	; halt T0
		ld PRE0, #5		; PRE0:=1, continuous run, go to 57.6k
		ld T0, #1
		or TMR, #T0_CntEn+T0_Load
		jp Bank_Ret


;********************************************************************
;
; Procedure: Set_SeekNeeded
;
;  This procedure acts in much the same fashion for the
;  LogicalBlockCache as DiskStat.On_Track does for simple
;  seeks. It keeps a legitimate seeks between two requests
;  for the same block number from returning a bogus result.
;
; Inputs: none
;
; Outputs: none
;
; Global Variables Changed: Cylinder
;
; Algorithm:
;
;  Begin
;   For i:=1 To CacheLength Do
;    CacheStat[i].SeekNeeded:=true
;  End
;
;********************************************************************

Set_SeekNeeded	ld R2, #CacheStat /256
		ld R3, #CacheStat #256
		ld R1, #CacheLength
S_SeekN_Lp	lde R0, @RR2		; get array value
		or R0, #CachSeek
		lde @RR2, R0
		incw RR2
		djnz R1, S_SeekN_Lp
		jp Bank_Ret



;********************************************************************
; Module SlfTst.B1.Assem   {Selftest}
;
;********************************************************************

;********************************************************************
;
; Function: Selftest
;
;  This function performs some check to confirm Widget's integrity.
;
;  Inputs: none
;
;  Outputs: SlfTst_Result: BYTE
;
;********************************************************************

SelfTest	or Excpt_Stat, #Recovery
		tm Excpt_Stat, #PwrRst	; check for power reset
		jr Z, ST_MtrSpd
;
		ld R0, Scr_Cntr		; otherwise finish time on disk speed
		or R0, Scr_Cntr+1
		jr Z, ST_MtrSpd
;
		ld R2, Scr_Cntr
		ld R3, Scr_Cntr+1
		call MsWait
;
ST_MtrSpd	call MtrSpd
		jr Z, Slf_Tracks	; ok, passed
		ld R2, #1500 /256
		ld R3, #1500 #256	; set timeout to 21.3 ms and try again
		call MtrSpd		;  (label should be MtrSpd_Lp1 !)
		jr NZ, Slf_Leave
;
Slf_Tracks	and SlfTst_Result, #0FFh-Disk_Speed
		call TrackCount
		and SlfTst_Result, #0FFh-Servo_Fail
		call SctrCount
		jr NZ, Slf_Leave
		and SlfTst_Result, #0FFh-Sector_Cnt
		call LoadStatus		; check state machine state
		and R0, #YMask
		jr NZ, Slf_Leave
		and SlfTst_Result, #0FFh-State_Fail
		call RwTest		; see if we can read and write
		jr Z, Slf_Leave
		and SlfTst_Result, #0FFh-Rw_Fail
;
Slf_Leave	jp Bank_Ret


;********************************************************************
;
; Function: MtrSpd  {motor speed}
;
;  This function is responsible for measuring the motor
;  speed. This is done by timing the interval between
;  consecutive index marks
;
; Inputs: none
;
; Outputs: MtrSpd: BOOLEAN {zero flag, NOT set if failure
;
;********************************************************************

MtrSpd		and IRQ, #0FFh-IRQ_Index	; clear old event
		ld R0, #1
		ld R4, #1600 /256	; set timeout values
		ld R5, #1600 #256	;  (22,7ms)
		ld R2, #11000 /256
		ld R3, #11000 #256	;  (156,2ms)
MtrSpd_Lp1	tm IRQ, #Irq_Index	; wait for index to come around
		jr NZ, MtrSpd_Idx	;  got him
		decw RR2		; decrement timeout
		jr NZ, MtrSpd_Lp1
		jr MtrSpd_Lv
;
MtrSpd_Idx	and IRQ, #0FFh-IRQ_Index-Irq_Sector	; clear events
		clr R0			; clear counter
		clr R1
MtrSpd_Lp2	tm IRQ, #Irq_Index	; wait for one rev
		jr NZ, MtrSpd_End	;  done
		incw RR0		; 0.0142 ms/lp or 70.4 cnts/ms
		decw RR4		; decrement timeout
		jr NZ, MtrSpd_Lp2
; nominal value is 1408 counts for 20 ms / 3000 rpm
MtrSpd_End	ld R14, R1
		ld R13, R0
		ld R12, #0
		call Div3_19		; get speed within tolerance in R2
	if W_10MB
		ld R0, #1		; assume failure
		cp R2, #044h		; accept speeds between 18.4 and 20.5 ms
		jr LT, MtrSpd_Lv
		cp R2, #4Ch
		jr GT, MtrSpd_Lv
		ld R0, #0		; otherwise pass
	endif
	if W_20MB || W_40MB
		ld R0, #1		; assume failure
		cp R2, #048h		; accept speeds between 19.5 and 20.5 ms
		jr LT, MtrSpd_Lv
		cp R2, #4Ch
		jr GT, MtrSpd_Lv
		ld R0, #0		; otherwise pass
	endif
;
MtrSpd_Lv	or R0, R0		; set flags
		ret


;********************************************************************
;
; Function: TrackCount
;
;  This function is responsible for counting the number of
;  tracks that are available on the servo.
;
; Inputs: none
;
; Outputs: TrackCount: BOOLEAN {zero flag, NOT set if failure
;
; Algorithm:
;
;  Begin
;   Restore(FrmtRecal)
;   Seek(Cyl=0)
;  End
;
;********************************************************************

TrackCount	call ResetServo		; try to get the servo in a nice state
		and DiskStat, #0FFh-RdHdrRecal	; don't read any headers!
		ld R0, #DataRecal
		call Restore
		ld R12, #0		; cylinder = 0
		ld R13, #69h
		ld R14, #0		; head = 0
		ld R15, #0		; sector = 0
		call Seek
		ret


;********************************************************************
;
; Function: SctrCount
;
;  This function counts the number of sector marks between
;  successive index marks and returns a true value if
;  the number of sectors counted equals NbrSctrs.
;
; Inputs: none
;
; Outputs: SctrCount: BOOLEAN {zero flag, NOT set if failure
;
;********************************************************************

SctrCount	clr R2			; count:=0
		and IRQ, #0FFh-Irq_Index	; clear old index marks
S_Cnt_1		tm IRQ, #Irq_Index	; wait for index mark
		jr Z, S_Cnt_1
		and IRQ, #0FFh-Irq_SecDn	; clear old events
S_Cnt_3		tm P3, #IndexMark	; While not(Index)
		jr NZ, S_Cnt_End
		tm IRQ, #Irq_Sector
		jr Z, S_Cnt_3
		inc R2			; bump the sector count
		and IRQ, #0FFh-Irq_Sector
		jr S_Cnt_3
S_Cnt_End	cp R2, #NbrSctrs
		ret


;********************************************************************
;
; Function: RwTest
;
;  This test moves the heads over a track away from the data
;  area (useable data area) and performs a write verify
;  on block zero of that track. If a failure occurs, the test
;  will continue on the next sequential sector on that track
;  until all sectors have been written to. If there are no
;  successful transfers on any of the sectors of this track then
;  the test is assumed to have failed.
;
; Inputs: none
;
; Outputs: RwTest: BOOLEAN {zero flag, NOT set if failure
;
; Algorithm:
;
;  Begin
;   Seek(RwTest track)
;   Repeat
;    If not(WrVerCommon) And RdErrCnt=10
;     Then
;      Sector:=Sector+1
;      If Sector>NbrSctrs
;       Then Done:=true
;     Else Done:=true
;   Until Done
;   If Sector>NbrSctrs
;    Then RwTest:=false
;    Else RwTest:=true
;  End
;
;********************************************************************

RwTest		or DiskStat, #RdHdrRecal
		ld R0, #DataRecal
		call Restore
		ld R5, #0
		jr Z, RwTest_End
;
RwTest1		ld R12, #Tst_HiCyl
		ld R13, #Tst_LoCyl
		ld R14, #Tst_Head
		ld R15, #Tst_Sctr
		call Seek
		ld R5, #NbrSctrs
;
RwTest_Lp	call ZeroHeader
		push R5
		or DiskStat, #Wr_Op
		ld R2, #WrBlk_Vector /256
		ld R3, #WrBlk_Vector #256
		call Bank_Call
		pop R5
		jr Z, Rw_Next
;
		ld RdErrCnt, #10	; assume a failure
		push R5
		and DiskStat, #0FFh-Wr_Op
		ld R2, #RdBlk_Vector /256
		ld R3, #RdBlk_Vector #256
		call Bank_Call
		pop R5
		jr NZ, RwTest_End
;
		tm R0, #RdNoHdrFnd
		jr NZ, Rw_Next
		ld R0, RdErrCnt
		and R0, #0Fh		; mask off unwanted status
		cp R0, #10
		jr NZ, RwTest_End
;
Rw_Next		inc Sector
		djnz R5, RwTest_Lp
RwTest_End	call Park_Heads
		or R5, R5		; set zero flag
		ret



;********************************************************************
; Module Cache.B1.Assem
;
; This module contains those Cache routines that must be located
; in Bank 1.
;
;********************************************************************

;********************************************************************
;
; Procedure: Load_Cache
;
;  This procedure loads the 'cache-table' with the next 7
;  sequential logical blocks (from the current logical block)
;  and status' associated with each block (i.e. if no seek is
;  required, if a head change is required, etc) and the
;  head and sector number of each logical block (assuming that
;  all the blocks are on the same track). It should be noted that
;  the status bytes are kept in a separate table.
;
; Inputs: none
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   Data_Type:=User_Type
;   For i:=1 To CacheLength Do
;    Cur_Block:=LogicalBlock+i
;    TempCyl, TempHead, TempSector,
;        SrchStat, SrchPtr:=CnvrtLogical(Cur_Block)
;    CacheStatus[i-1].Seek:=CalcMagDir(TempCyl)
;    If TempHead<>Head
;     Then CacheStatus[i.1].HeadChange:=True
;     Else CacheStatus[i-1].HeadChange:=false
;    CacheArray[i-1].LogicalBlock:=Cur_Block
;    CacheArray[i-1].Head:=TempHead
;    CacheArray[i-1].Sector:=TempSector
;   Cache_Index:=0
;  End
;
;********************************************************************

Load_Cache	ld R4, #CacheLength
		ld R5, #0		; clear SeekNeeded, HeadChange booleans
		ld Data_Type, #2
		ld R8, #CacheStat /256
		ld R9, #CacheStat #256
		ld R10, #CacheArray /256
		ld R11, #CacheArray #256
		call Load_Logical	; init logical block
		ld R2, #TLBlock /256
		ld R3, #TLBlock #256
		call Ld_C_PushR12R14	; store logical block into TLBlock
		call Ld_C_Srch
		push FLAGS		; save result of spare table search
		push R0
		ld R2, #PBlock /256
		ld R3, #PBlock #256
		call Ld_C_PushR12R14	; store physical block into PBlock
		call Cache_Cnvrt
		pop R0
		pop FLAGS
		clr R1			; SeekNeeded, HdChg:=false
		jr NZ, Ld_Cache_In
		jp Ld_C_Enter
;
Ld_Cache_Lp	ld R2, #PBlock /256
		ld R3, #PBlock #256
		call Ld_C_IncBlkNr	; inc physical block
		ld R2, #TLBlock /256
		ld R3, #TLBlock #256
		call Ld_C_IncBlkNr	; inc logical block
		call Ld_C_Srch
		jr Z, Cache_NoSpare
;
		ld R1, #CachSeek
		or R5, R1		; set SeekNeeded
Ld_Cache_In	call Enter_Cache
		jr Ld_Cache_More
;
Cache_NoSpare	srp #Wrk_Sys2
	assume RP:Wrk_Sys2
		ld R9, Wrk_Sys+12	; nondestructive subtract
		ld R10, Wrk_Sys+13
		ld R11, Wrk_Sys+14
		ld R2, #PBlock /256
		ld R3, #PBlock #256
		ld R0, #Wrk_Sys2+12
		call Ld_C_Pop3Byte	; get physical block
		sub R14, R11		; If TLBlock <>PBlock
		sbc R13, R10
		sbc R12, R9
		or R12, R13
		or R12, R14
		push FLAGS
		srp #Wrk_Sys
	assume RP:Wrk_Sys
		ld R2, #PBlock /256
		ld R3, #PBlock #256
		call Ld_C_PushR12R14
		pop FLAGS		; check for spare block
		jr NZ, Ld_Cach_Seek
		call Ld_C_GetCurTHS
Ld_Cach_NoSeek	inc R15			; bump the sector address
		cp R15, #NbrSctrs
		jr GE, Ld_Cach_HdChg
		call Ld_C_SetCurTHS
		clr R1			; CacheSeek, CacheHdChg:=false
		jr Ld_C_Enter

Ld_Cach_HdChg	ld R15, #0		; start at sector 0
		cp R14, #0		; check for head 0
		jr NZ, Ld_Cach_Seek
		ld R14, #1		; otherwise go to head 1
		call Ld_C_SetCurTHS
		ld R1, #CachHdChg
		jr Ld_C_Enter
;
Ld_Cach_Seek	ld R14, #0		; start at head 0
		ld R15, #0		; and sector 0
		add R13, #1		; bump the track count by 1
		adc R12, #0
		call Ld_C_SetCurTHS
		ld R1, #CachSeek
;
Ld_C_Enter	or R5, R1		; set SeekNeeded or HeadChange
		call Ld_BlkStat
Ld_Cache_More	djnz R4, Ld_Cache_Lp
		clr Cache_Index
		jp Bank_Ret
;
;********************************************************************
;
Enter_Cache	tm R0, #Spare		; check if block is a spare
		jr Z, Ld_Blk_BB
		or R1, #S_Block		; set Spare Block status
		jr Ld_BlkStat
Ld_Blk_BB	or R1, #B_Block		; otherwise it must be a Bad Block

Ld_BlkStat	or R1, R5		; merge in global SeekNeeded or HeadChange
		lde @RR8, R1
		ld R2, #TLBlock /256
		ld R3, #TLBlock #256
		call Ld_C_PopR12R14	; get logical block
		ld R0, #Wrk_Sys+12	; load into CacheArray.Logical
		ldei @RR10, @R0
		ldei @RR10, @R0
		ldei @RR10, @R0
		incw RR8		; set up for the next go 'round
		call Ld_C_GetCurTHS
;
		ld R0, R15
		call ReMap_Sector
		swap R14		; merge Head with Sector
		rcf
		rl R14
		rl R14
		or R14, R0
		lde @RR10, R14
		incw RR10		; point to next cache entry
		ret
;
Cache_Cnvrt	ld R2, #PBlock /256
		ld R3, #PBlock #256
		call Ld_C_PopR12R14	; get latest physical block
		call Get_Cyl_H_S
		call Ld_C_SetCurTHS	; store latest seek address
		ret
;
Ld_C_SetCurTHS	ld R2, #Cur_THS /256	;copy R12:15 into Cur_THS ff.
		ld R3, #Cur_THS #256
		ld R0, #Wrk_Sys+12
		ldei @RR2, @R0
Ld_C_Push3Byte	ldei @RR2, @R0
		ldei @RR2, @R0
		ldei @RR2, @R0
		ret
;
Ld_C_GetCurTHS	ld R2, #Cur_THS /256	; get 4 bytes from Cur_THS ff.
		ld R3, #Cur_THS #256	;  into R12:15
		ld R0, #Wrk_Sys+12
		ldei @R0, @RR2
Ld_C_Pop3Byte	ldei @R0, @RR2
		ldei @R0, @RR2
		ldei @R0, @RR2
		ret
;
Ld_C_IncBlkNr	ld R0, #Wrk_Sys+12	; get R12:14 from RAM
		ldei @R0, @RR2
		ldei @R0, @RR2
		ldei @R0, @RR2
		add R14, #1		; inc block number
		adc R13, #0
		adc R12, #0
		sub R3, #3		; restor RAM pointer to
		sbc R2, #0		;  original value
		ld R0, #Wrk_Sys+12	; and write back into RAM
		ldei @RR2, @R0
		ldei @RR2, @R0
		ldei @RR2, @R0
		ret
;
Ld_C_PushR12R14	ld R0, #Wrk_Sys+12	; copy R12:14 into
		jr Ld_C_Push3Byte	;  external memory @RR2
;
Ld_C_PopR12R14	ld R0, #Wrk_Sys+12	; retrieve R12:14 from
		jr Ld_C_Pop3Byte	;  external memory @RR2
;
;********************************************************************
;  Fast search of spare table
;********************************************************************
;
Ld_C_Srch	ld R1, R13		; check if head ptr is NIL
		ld R0, R12		; but first form a headptr structure
		rrc R0			; and index into HdPtr array
		rrc R1
		rcf
		rrc R1
		ld R2, #SegPtrArray /256
		ld R3, #SegPtrArray #256
		add R3, R1
		adc R2, #0
		lde R0, @RR2		; get HeadPtr and check for NIL
		tm R0, #NIL
		jr Z, Ld_C_Long		; do a real search if not NIL
	if W_10MB
		ld R0, R13		; save for possible rollover
		add R14, R13		; Physical Block:= LBlock+LBlock div 256
		adc R13, #0
		adc R12, #0
	endif
		cp R0, R13		; check for rollover
		jr Z, Ld_C_S_End
;
		add R14, #1		; otherwise bump Physical Block
		adc R13, #0
		adc R12, #0
;
Ld_C_S_End	xor R0, R0		; return zero status
Ld_C_S_Ret	ret
;
Ld_C_Long	ld R2, #SrchSpTabl /256
		ld R3, #SrchSpTabl #256
		call Bank_Call
		jr Ld_C_S_Ret



;********************************************************************
; Module Progs.B1.Assem
;
; This module contains those routines that are 'high-level
; programs
;
;********************************************************************

;********************************************************************
;
; Procedure: Scan
;
;  This procedure reads each logical block on the disk and
;  spares them if they are bad.
;
; Inputs: none
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   For i:=0 To MaxLogicalBlocks Do
;    Seek_Type:=Access_Offset
;    Data_Type:=User
;    DiskStat.Wr_Op:=false
;    If not(Srch_Cach(i) or Srch_Cach.SeekNeeded
;     Then
;      Seek(CnvrtLogical)
;      Load_Cache
;     Else
;      If Srch_Cach.HdChg
;       Then
;        SelectHead(complement(Head))
;        Load_Cache
;       Else
;        Sector:=Srch_Cach.Sector
;    If not(Read_Common)
;     Then Data_Ex_Handler(Read_Common.ErrorCode)
;  End
;
;********************************************************************

D_Scan		call ClrNormStat_1
		ld Wrk_lo+10, #D_Scan_Response
		call Ack_Read
;
Scan		tm SlfTst_Result, #No_SprTbl
		jr Z, Scan_1
		call Abort
;
Scan_1		ld R0, #0
		ld R1, #7
		ld R2, #Wrk_Sys+9	; init PBlock and Track-Head-Sector
Scan_Init	ld @R2, R0
		inc R2
		djnz R1, Scan_Init
		call loc_2E28
;
Scan_Lp		ld Seek_Type, #Access
		call Seek
Scan_Read	ld R2, #Read_Common /256
		ld R3, #Read_Common #256
		call Bank_Call
		jr NZ, Scan_More
		push R0			; save error code
		call Ext_Push
		ld R1, #0		; convert to logical interleave
		ld R2, #Map_Table /256
		ld R3, #Map_Table #256
		ld R4, #NbrSctrs
Scan_Map_Lp	lde R0, @RR2
		cp R0, Sector
		jr Z, Scan_Map_End
		incw RR2
		inc R1
		djnz R4, Scan_Map_Lp
		call Abort
;
Scan_Map_End	call loc_2E35
		add R11, R1		; add in the sector offset
		adc R10, #0
		adc R9, #0
		ld R0, #0		; check for logical block zero
		or R0, R11
		or R0, R10
		or R0, R9
		jr Z, Scan_L_Err
; convert the physcal block number to a logical block number
	if W_10MB
		or R11, R11		; check for spareblock
		jr Z, Scan_M_Pop
		sub R11, R10
		sbc R10, #0
		sbc R9, #0
	endif
	if W_20MB
		ld R0, R10		; check for spareblock
		and R0, #01h
		or R0, R11
		jr Z, Scan_M_Pop
		ld R1, R9
		ld R2, R10
		rcf
		rrc R1
		rrc R2
		sub R11, R2
		sbc R10, R1
		sbc R9, #0
	endif
	if W_40MB
		ld R0, R10		; check for spareblock
		and R0, #01h
		or R0, R11
		jr Z, Scan_M_Pop
		ld R1, R9
		ld R2, R10
		ld R0, #2
P_To_L		rcf
		rrc R1
		rrc R2
		djnz R0, P_To_L
		sub R11, R2
		sbc R10, R1
		sbc R9, #0
	endif
Scan_L_Err	call loc_2E1B
		or R0, #1
Scan_M_Pop	jr Z, Scan_M_Pop1
		ld Data_Type, #User_Type
		call Load_Cache
		call Load_Logical
		ld R2, #SC_Vector /256
		ld R3, #SC_Vector #256
		call Bank_Call
		pop R0			; retrieve error code
		tm BlkStat, #S_Block
		jr NZ, Scan_M_Pop1
Scan_Rd_Err	ld R2, #Data_Ex_Handler /256
		ld R3, #Data_Ex_Handler #256
		call Bank_Call
		call ZeroHeader
;
Scan_M_Pop1	call Ext_Pop
;
Scan_More	inc Sector		; read next sector
		cp Sector, #NbrSctrs	; check for overflow
		jr LT, Scan_Read
		call loc_2E35
		add R11, #NbrSctrs	; go to next set of blocks
		adc R10, #0
		adc R9, #0
		call loc_2E28
		ld Sector, #0		; otherwise start at sector 0
		inc Head		; and go to next head
		cp Head, #NbrHds
		jr GE, Scan_IncTrks
		ld R14, Head
		call SelectHead
		jp Scan_Read
;
Scan_IncTrks	ld R14, #0
		ld R15, #0
		ld R13, Cylinder+1
		ld R12, Cylinder
		add R13, #1		; otherwise seek to the next track
		adc R12, #0
		ld R0, #(NbrTracks-1) /256
		ld R1, #(NbrTracks-1) #256
		xor R1, R13		; check for last track
		xor R0, R12
		or R0, R1
		jp NZ, Scan_Lp
		ld R4, #0
loc_2D9D:	ld R2, #SegPtrArray /256
		ld R3, #SegPtrArray #256
		add R3, R4
loc_2DA3	adc R2, #0
		lde R0, @RR2
		tm R0, #80h
		jr NZ, loc_2E04
		and R0, #07Fh
		ld R2, #Get_Ptr /256
loc_2DB2	ld R3, #Get_Ptr #256
		call Bank_Call
		lde R0, @RR2
		tm R0, #02
		jr Z, loc_2DFA
		tm R0, #10h
		jr NZ, loc_2DFA
		call Ext_Push
		ld R9, #0
		rl R4
		rlc R4
		rlc R9
		incw RR2
		lde R10, @RR2
		or R10, R4
		incw RR2
		lde R11, @RR2
		call loc_2E1B
loc_2DDB	call Load_Logical
		ld R2, #CnvrtLogical /256
		ld R3, #CnvrtLogical #256
		call Bank_Call
		call Seek
		ld R0, #84h
		ld Data_Type, #User_Type
		ld BlkStat, #B_Block
		ld R2, #Data_Ex_Handler /256
		ld R3, #Data_Ex_Handler #256
		call Bank_Call
		call Ext_Pop
loc_2DFA	tm R0, #80h
		jr NZ, loc_2E04
		add R3, #3
		jr loc_2DA3
;
loc_2E04	inc R4
		cp R4, #20h
		jr NZ, loc_2D9D
		call Park_Heads
		call ClrNormStat_1
		jp Bank_Ret
;
ClrNormStat_1	ld R2, #ClrNormStat /256
		ld R3, #ClrNormStat #256
		call Bank_Call
		ret
;
loc_2E1B	ld R2, #CStatus3 /256
		ld R3, #CStatus3 #256
sub_2E1F	ld R1, #4
		ld R0, #19h
loc_2E23	ldei @RR2, @R0
		djnz R1, loc_2E23
		ret

loc_2E28 	ld R2, #17h
		ld R3, #58h
		ld R0, #19h
		ldei @RR2, @R0
		ldei @RR2, @R0
		ldei @RR2, @R0
		ret

loc_2E35 	ld R2, #17h
		ld R3, #58h
		ld R0, #19h
		ldei @R0, @RR2
		ldei @R0, @RR2
		ldei @R0, @RR2
		ret


;********************************************************************
;
; Procedure: Strt_FreeProcess
;
;  This procedure is used by the controller to start any process
;  (along the lines of internal bookkeeping) that it choses
;
; Inputs: none
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   If SeekCount>=2048
;    Then ArmSweep
;   Free_SlfTst:=0
;   While not(CMD) Do
;    Bsy_Wait for 2 seconds
;    If not(Parked) Then Park_Heads
;    Case Free_Slftst Of
;     0: Test Eprom
;     1: Test Sector Count
;     2: Test Motor Speed
;     3: Test Read/Write; Free_SlfTst:=0
;   Take care of Host
;  End
;
;********************************************************************

Strt_FreeProcess	di
		srp #Wrk_Sys		; get into a reasonable state
	assume RP:Wrk_Sys
		clr SPH
		ld SPL, #Stack_Top
		and P2, #0FFh-Bsy	; clear BSY
		tm SeekCount, #0F8h	; do arm sweep every 1k seeks or so
		jr Z, Chk_Park
;
		clr SeekCount
		clr SeekCount+1
		ld R12, #Init_HiCyl	; seek to data recal location
		ld R13, #Init_LoCyl
		ld R14, #0		; head zero
		ld R15, #0		; sector zero
		call New_Seek
		ld R12, #0
		ld R13, #40h
		ld R14, #0
		ld R15, #0
		call New_Seek
		and DiskStat, #0FFh-On_Track
		call Set_SeekNeeded	; invalidate cache contents
;
Chk_Park	clr Free_SlfTst		; get ready to do some self tests
		ld R2, #200 /256	; wait for two seconds
		ld R3, #200 #256
		jr Chk_Park8
;
Chk_Pk_Jp	ld R2, #3		; wait for eight seconds
		ld R3, #20h
Chk_Park8	call Chk_Park1		; do busy wait while monitoring CMD
;
Chk_Park3 	tm DiskStat, #Parked
		jr NZ, FreeP_NoPark
		call Park_Heads
		jr Chk_Pk_Jp
;
FreeP_NoPark	tm Free_SlfTst, #1	; check for even value
		jr Z, FreeP_SlfTst	; do tests only every other time
		inc Free_SlfTst		; otherwise make even for the next time
		jr Chk_Pk_Jp
;
FreeP_SlfTst	ld R2, #SlfTst_Table /256
		ld R3, #SlfTst_Table #256
		add R3, Free_SlfTst
		adc R2, #0
		inc Free_SlfTst		; make value odd
		ldc R14, @RR2
		incw RR2
		ldc R15, @RR2
		jp @RR14
;
SlfTst_Table 	DW Free_Ram
		DW Chk_Pk_Jp		; nop
		DW Free_Eprom
		DW Chk_Pk_Jp
		DW Free_MtrSpd
		DW Chk_Pk_Jp
		DW Free_SctrCnt
		DW Chk_Pk_Jp
		DW Free_Rw
;
Free_Ram 	ld R2, #Chk_SprChk /256	; verify spare table checksum
		ld R3, #Chk_SprChk #256
		call Bank_Call
		jr NZ, Chk_Pk_Jp
;
		or SlfTst_Result, #Ram_Fail
		jr NZ, Chk_Pk_Jp
;
Free_Eprom	ld R0, #Eprom2
		call EpromTest
		jr Z, Chk_Pk_Jp
;
		or SlfTst_Result, #Eprom_Fail
		jr Chk_Pk_Jp
;
Free_SctrCnt	ld R2, #SctrCount /256
		ld R3, #SctrCount #256
		call Bank_Call
		jr Z, Chk_Pk_Jp
;
		or SlfTst_Result, #Sector_Cnt
		jr Chk_Pk_Jp
;
Free_MtrSpd	ld R2, #MtrSpd /256
		ld R3, #MtrSpd #256
		call Bank_Call
		jr Z, Chk_Pk_Jp
;
		or SlfTst_Result, #Disk_Speed
		jp Chk_Pk_Jp
;
Free_RW		or SlfTst_Result, #Rw_Fail	; assume failure
		ld R2, #RwTest1 /256
		ld R3, #RwTest1 #256
		call Bank_Call
		jp Z, Chk_Pk_Jp
;
		and SlfTst_Result, #0FFh-Rw_Fail
		clr Free_SlfTst
		jp Strt_FreeProcess	; check for arm sweep



;********************************************************************
; Module ECC.B1.Assem   {Error Check and Correction}
;
; This module contains all the relevant files pertaining
; to the ECC method and algorithm used on Widget
;
;********************************************************************

;********************************************************************
;
; Function: Ecc
;
;  This function is responsible for 1) checking if the data in the
;  ReadBuffer is correctable and 2) correcting that data if it is
;  correctable.
;
;  The method used was prepared by:
;    Neil Glover
;    Daza Systems Technology Corp.
;    1801 Aspen St.
;    Broomfield, Co. 80020
;    (303) 466-5228
;
; Inputs:  none
;
; Outputs: Ecc: BOOLEAN {zero flag is set if not correctable}
;
; K1 = BitLength(DataField)+BitLength(CrcField)+BitLength(EccField)-41
;    = (1+532)*8 + 2*8 + 6*8 -41
;    = 4287
;
; Correction Span = 12 bits
;
;  R2Mask = $00
;  R3Mask = $0F
;
; Syndrome Bytes begin at ReadArray.RBuf1Ecc
;
; Local Variables: R1:          BYTE {R1}
;                  R2:          BYTE {R2}
;                  R3:          BYTE {R3}
;                  R4:          BYTE {R4}
;                  R5:          BYTE {R5}
;                  R6:          BYTE {R6}
;                  Correctable: BOOLEAN {R7/bit 7}
;                  Aligned:     BOOLEAN {R7/bit 6}
;                  Done:        BOOLEAN {R7/bit 5}
;                  J:           WORD {RR8}
;
; Algorithm:
;
;  Begin
;   R1:=SynfromeByte[1] {most significant byte}
;   R2:=SynfromeByte[2]
;   R3:=SynfromeByte[3]
;   R4:=SynfromeByte[4]
;   R5:=SynfromeByte[5]
;   R6:=SynfromeByte[6]
;   If (R1=R2=R3=R4=R5=R6=0)
;    Then Ecc:=false
;    Else
;     J:=K1
;     Aligned:=false
;     Done:=false
;     Correctable:=false
;     While R1=0 Do
;      ShiftRegsLeft 1 Byte {left-hand justify syndrome}
;      J:=J+8
;     While not(Done) Or not(Aligned) Do
;      ShiftAndXor
;      If R1=0
;       Then
;        If R4=R5=R6=0 And R2*R2Mask=0 And R3*R3Mask
;         Then
;          Aligned:=true
;          TestMod8
;      If not(Aligned) Then Test0
;      If not(Done) Then J:=J-1
;     While not(Done) Do
;      ShiftAndXor
;      If R=0
;       Then TestMod8
;       Else Test0
;      If not(Done) Then J:=J-1
;     If Correctable
;      Then
;       J:=J div 8
;       Buffer1[J]:=Buffer1[J] Xor R2
;       Buffer1[J+1]:=Buffer1[J+1] Xor R3
;       Buffer1[J+2]:=Buffer1[J+2] Xor R4
;       BlockMove(Buffer2, RBuffer
;     Ecc:=Correctable
;  End
;
;********************************************************************

K1		EQU 4287
R2Mask		EQU 0
R3Mask		EQU 00Fh
Ecc_Correctable	EQU 080h
Ecc_Aligned	EQU 040h
Ecc_Done	EQU 020h

ECC		clr R7			; clear booleans
		ld R8, #K1 /256
		ld R9, #K1 #256
		ld R0, #0		; get ready to check for all zero syndrome
		ld R11, #6		; load six bytes, R1..R6:=Syndrome Bytes
		ld R12, #RBuf1Ecc /256
		ld R13, #RBuf1Ecc #256
		ld R10, #Wrk_Sys+1	; load syndrome bytes into registers
;
Ecc_Ld_Lp	ldei @R10, @RR12
		or R0, @R10
		djnz R11, Ecc_Ld_Lp
		jp Z, Ecc_End
;
Ecc_LHJ_While	or R1, R1		; While R1=0 Do
		jr NZ, Ecc_Align
		ld R1, R2		; shift left 1 byte
		ld R2, R3
		ld R3, R4
		ld R4, R5
		ld R5, R6
		clr R6
		add R9, #8		; J:=J+8
		adc R8, #0
		jr Ecc_LHJ_While
;
Ecc_Align	call ShiftAndXor
		jr NZ, Ecc_Al_1
		ld R0, R4		; If (R4=R5=R6=0)
		or R0, R5
		or R0, R6
		ld R15, R3		; And (R3*R3Mask=0)
		and R15, #R3Mask
		or R0, R15
		jr NZ, Ecc_Al_1
;
		or R7, #Ecc_Aligned
		call TestMod8
;
Ecc_Al_1	tm R7, #Ecc_Aligned
		jr NZ, Ecc_Al_2
;
		call Test0
;
Ecc_Al_2	tm R7, #20h
		jr NZ, Ecc_Crct
;
		decw RR8		; J:=J-1
		tm R7, #Ecc_Done+Ecc_Aligned
		jr Z, Ecc_Align
;
Ecc_Shift	call ShiftAndXor
		jr NZ, Ecc_Shft_Else
		call TestMod8
		jr Ecc_Shft_2
;
Ecc_Shft_Else	call Test0
Ecc_Shft_2	tm R7, #Ecc_Done
		jr NZ, Ecc_Crct
		decw RR8		; J:=J-1
		jr Ecc_Shift
;
Ecc_Crct	tm R7, #Ecc_Correctable
		jr Z, Ecc_End
;
		ld R10, #3		; J:=J div 8
Ecc_Div8	rrc R8
		rrc R9
		djnz R10, Ecc_Div8
		and R8, #1Fh		; mask off unwanted carries
		ld R12, #RDummy /256
		ld R13, #RDummy #256
		add R13, R9
		adc R12, R8
		ld R11, #Wrk_Sys+2	; start with R2
		ld R10, #3		; correct 3 bytes
;
Ecc_Crct_Lp	lde R1, @RR12
		xor R1, @R11
		ld @R11, R1
		ldei @RR12, @R11
		djnz R10, Ecc_Crct_Lp
;
		ld R2, #RBuf_To_Buf2 /256
		ld R3, #RBuf_To_Buf2 #256
		call Bank_Call
		tm R7, #Ecc_Correctable	; set correctable flag
;
Ecc_End		jp Bank_Ret


;********************************************************************
;
; Procedure: ShiftAndXor
;
;  This procedure is used to shift the current syndrome bytes
;  (assumed to be located in R1:6) to the right 1 bit and then
;  Xor the syndromes with the reciprocal polynominal if needed.
;
; Inputs: R1: BYTE {R1}
;         R2: BYTE {R2}
;         R3: BYTE {R3}
;         R4: BYTE {R4}
;         R5: BYTE {R5}
;         R6: BYTE {R6}
;
; Outputs: R1: BYTE {R1}
;          R2: BYTE {R2}
;          R3: BYTE {R3}
;          R4: BYTE {R4}
;          R5: BYTE {R5}
;          R6: BYTE {R6}
;
; Algorithm:
;
;  Begin
;   Shift the syndromes right 1 bit with carry
;   If the LSB of R6 was a 1 {if carry}
;    Then
;     R1:=R1 Xor 140
;     R2:=R3 Xor 12
;     R3:=R3 Xor 10
;     R4:=R4 Xor 40
;     R5:=R5 Xor 24
;     R6:=R6 Xor 8
;  End
;
;********************************************************************

ShiftAndXor 	ld R12, #6		; shift 6 bytes
		ld R13, #Wrk_Sys+1	; start with R1
		rcf
;
S_A_Xor_Lp	rrc @R13
		inc R13
		djnz R12, S_A_Xor_Lp
;
		jr NC, S_A_Xor_End
		xor R1, #140
		xor R2, #12
		xor R3, #10
		xor R4, #40
		xor R5, #24
		xor R6, #8
S_A_Xor_End	or R1, R1		; If R1=0 ...
		ret


;********************************************************************
;
; Procedure: TestMod8
;
;  This procedure is used to test if J mod 8 = 0.
;
; Inputs: J: WORD {RR8}
;
; Outputs: none
;
; Global Variables Changed: Done:        BOOLEAN {R7/bit 5}
;                           Correctable: BOOLEAN {R7/bit 7}
;
; Algorithm:
;
;  Begin
;   If J mod 8 = 0
;    Then
;     Done:=true
;     Correctable:=true
;  End
;
;********************************************************************

TestMod8 	ld R0, R9
		and R0, #7		; get remainder from division
		jr NZ, TstMd8_Done
		or R7, #Ecc_Done+Ecc_Correctable
TstMd8_Done	ret


;********************************************************************
;
; Procedure: Test0
;
;  This procedure is used to test if J=0.
;
; Inputs: J: WORD {RR8}
;
; Outputs: none
;
; Global Variables Changed: Done:        BOOLEAN {R7/bit 5}
;                           Correctable: BOOLEAN {R7/bit 7}
;
; Algorithm:
;
;  Begin
;   If J=0
;    Then
;     Done:=true
;     Correctable:=true
;  End
;
;********************************************************************

Test0 		ld R0, R8
		or R0, R9		; If J=0 ...
		jr NZ, Test0_Done
		or R7, #Ecc_Done
		and R7, #0FFh-Ecc_Correctable
Test0_Done	ret


	dephase
	if $>ROMsize
		error "\aROM size exceeded !!!"
	elseif
		DB ROMsize-$ dup(0FFh)	; pad with $FF's
	endif
	end

