	cpu Z8601
	page 0
	include stddefZ8.inc

;********************************************************************
;
;              Apple Widget Controller - Z8 firmware
;                         Version 341-0288-A
;            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-10 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

HiRevNumber	SET 01Ah		; not used here but good to know
LoRevNumber	SET 045h
ROMsize		SET 2048


; this source contains Init.Assem (0000), Resident.Assem (025F), Bs.Assem,
; (04AB), Int.Asssem (050D), Except.Assem (051F), and Res.Misc.Assem (05CA)

	include defs.inc		; Defs1.Assem, Defs2.Assem
	include vectors0289.inc		; addresses referred to in external EPROM

;********************************************************************
; Module Init.Assem
;
; This module contains the routines that are responsible for
; initializing widget
;
;********************************************************************
	org 0000h
Int_Vec0	DW Vector0		; illegal
Int_Vec1	DW Vector0		; illegal
Int_Vec2	DW Vector0		; illegal
Int_Vec3	DW Vector0		; illegal
Int_Vec4	DW Vector0		; illegal
Int_Vec5	DW Vector5		; T1 / Dead Man Timer

Start 		di
		srp #Wrk_lo		; select I/O working register set
	assume rp:Wrk_lo
 		clr SPH			; initialize the stack
 		ld SPL, #Stack_Top
 		ld P01M, #P0_03_Adr+P0_47_Out+Stack_In+P1_Adr+Mem_Ext
 		ld P0, #Not_FmenL+Not_ZTestL+Not_RdHdrH+Not_ServoRst
 		ld P2M, #P21_In+P22_In+P26_In
 		ld P2, #Not_StartL+Bsy+Z8_Mem+DrwL_Read
		ld P3M, #B0_7_Ser+B1_6_Io+B3_4_Idm+B2_5_Io+Totem_Pol+Par_Off
		ld R14, #RamBank0 / 256	; select RAM bank 1
		ld R15, #01
		lde @RR14, R14
		inc R14			; next RAM adr bit
		ld R15, #00
		lde @RR14, R14
		ld R14, #1000h /256	; save PwrFlg regs temporarely
		ld R15, #1000h #256
		ld R13, #4		; move 4 registers
		ld R12, #PwrFlg0
Start_Loop	ldei @RR14, @R12
		djnz R13, Start_Loop


;********************************************************************
; Diagnostic: Internal RAM / Register Test
;
;   This test is used as both a diagnostic and a selftest routine
;   in the Widget controller. It is intended to check the internal
;   functions of hte Z8 before allowing the controller to execute
;   any code that could be potentially dangerous to the system.
;
;   The test begins by checking working register set 0, registers 4,
;   5, 6, and 7, then uses those registers to test the rest of the
;   others.
;
; Test: All Zeros / All Ones
;
;   k:=0 {load value}
;   For i:=1 To 2 Do
;    Begin
;     i:=120 {128 regs: -4 I/O ports, -4 regs used in test}
;     j:=127 {highest register address}
;     While (i>0) Do
;      Begin
;       RamReg[j]:=k
;       j:=j-1
;       i:=i-1
;      End {While}
;     For i:=1 To 120 Do
;      Begin
;       If RamReg[i]<>k Then HALT
;       j:=j+1
;      End
;     k:=$FF {do all one's test next}
;    End {For}
;
;********************************************************************

Start_RegTest	clr R4
		or R4, R4		; quick check
		jr NZ, $		; loop here if can't clear R4
;
		ld R5, R4		; bootstrap a few registers
		ld R6, R4		;     /|\   .
		ld R7, R4		;      |
		ld R8, R4		;      |
		add R5, R4		;      | (all zeros)
		add R6, R5		;      |
		add R7, R6		;      |
		add R8, R7		;     \|/   .
		jr NZ, $		; loop here if can't bootstrap
;
		ld R4, #0FFh		; quick check
		cp R4, #0FFh
		jr NZ, $		; loop here if can't set R4
;
		ld R5, R4		; bootstrap a few registers
		ld R6, R4		;     /|\   .
		ld R7, R4		;      |
		ld R8, R4		;      |
		add R5, R4		;      | (all ones)
		add R6, R5		;      |
		add R7, R6		;      |
		add R8, R7		;     \|/   .
		add R8, #05		; 5* (-1) + 5 = 0
		jr NZ, $		; loop here if can't bootstrap
;
; test the rest of the registers
;
		clr R4			; load value
		ld R5, #RegLpTimes
RegLp1		ld R8, R4		; remember load value
		ld R6, #RegCount-RegUsed-I_ORegUsed
		ld R7, #HiRegAdr
RegLp2		ld @R7, R4		; begin loading RAM
		dec R7			; point to new reg
		djnz R6, RegLp2
		ld R6, #RegCount-RegUsed-I_ORegUsed
RegLp3		inc R7
		ld R4, @R7		; read regs
		cp R4, R8
		jr NZ, $		; loop here if failure
		djnz R6, RegLp3
		ld R4, #0FFh		; set up for all ones test
		djnz R5, RegLp1


;********************************************************************
; Diagnostic: Stack Test
;
;   This test is used to test hte Z8's push and pop capabilities
;   as well as its ability to perform calls and returns.
;
;   The stack is set to internal, and the stack top is set to
;   location $80. The ensuing PUSH instruction first decrements the
;   stack pointer to $7F, and then stores the contents of the
;   register being pushed in that location. A POP instruction
;   should first load the register with the contents pointed
;   to by the stack pointer and then increment the pointer.
;
;   Registers used: R4, R5
;
;********************************************************************

		clr SPH
		ld SPL, #80h
		ld R4, SPH
		jr NZ, $		; check the loading of Stack Pointer
		cp SPL, #80h
		jr NZ, $
;
		ld R4, #0AAh		; PUSH / POP $AA
		push R4
		ld R4, SPH
		jr NZ, $		; check for decrement of pointer
		cp SPL, #7Fh
		jr NZ, $
		pop R4
		cp R4, #0AAh
		jr NZ, $
		ld R4, SPH
		jr NZ, $		; check for increment of pointer
		cp SPL, #80h
		jr NZ, $
;
		call Stk_Test		; check call capability
Stk_Test	ld R4, SPL
Stk_Test1 	cp R4, #7Eh		; check for double dec on call
		jr NZ, $
;
		inc R4			; point to low address byte
		ld R5, @R4		; load low address byte
		cp R5, #Stk_Test	; should be next instruction after call
		jr NZ, $
;
		ld @R4, #Stk_Ret	; check return capability
		ret
Stk_Halt	jr Stk_Halt		; Halt if pc just got incremented
;
Stk_Ret 	clr SPH			; initialize the stack
		ld SPL, #Stack_Top


;********************************************************************
;
; Initialize the I/O Port Control Registers
;
;   P01M -->	select Port 0, Bits 0:3 =Adr Bits 8:11
;		select Port 0, Bits 4:7 = Output Ports
; 		select Port 1= Adr Bits 0:7
; 		select Extended Memory Timing
; 		select Internal Stack
;
;   P2M  -->	select Port 2, Bit 1 = Input
; 		select Port 2, Bit 2 = Input
; 		select Port 2, Bit 6 = Input
; 		all other Port 2 bits are outputs
;
;   P3M  -->	select Port 3, Bit 0 = SIO Data In
;		select Port 3, Bit 1 = Tin
;		select Port 3, Bit 2 = Input
;		select Port 3, Bit 3 = Input
;		select Port 3, Bit 4 = Dm-
;		select Port 3, Bit 5 = Output
;		select Port 3, Bit 6 = Tout
;		select Port 3, Bit 7 = SIO Data Out
;		select SIO Parity Off
;		select Totem-Pole outputs for Port 2
;
;********************************************************************

		ld P01M_Image, #P0_03_Adr+P0_47_Out+Stack_In+P1_Adr
		ld P01M_StMach, #P0_03_Out+P0_47_Out+Stack_In+P1_Tri
		ld P01M, P01M_Image
		ld P2M, #P21_in+P22_In+P26_In
		ld P3M_Image, #B0_7_Ser+B1_6_Io+B3_4_Idm+B2_5_Io+Totem_Pol+Par_Off
		ld P3M_StMach, #B0_7_Ser+B1_6_Io+B3_4_Io+B2_5_Io+Totem_Pol+Par_Off
		ld P3M, P3M_Image
;
		ld Dm_Mask, #0FFh-Dm
		ld Start_Mask, #0FFh-Not_StartL
;
		ld IPR, #08		; Group A := 0, A > B > C
		clr IMR			; disallow interrupts
		clr IRQ			; clear any pending interrupts
;
		clr DiskStat
		clr Excpt_Stat		; recovery off
		clr BlkStat
		clr RdStat
		clr RdErrCnt
		clr WrStat
		clr WrErrCnt
		clr Cache_Index
		ld Seek_Type, #Access_Offset
		ld Data_Type, #User_Type
		clr SeekCount
		clr SeekCount+1
;
		ld R14, #Rwi_Value /256	; load RWI/PC cylinder value
		ld R15, #Rwi_Value #256
		ld R12, #Hi_Rwi_Reg
		ldci @R12, @RR14
		ldci @R12, @RR14
		clr TMR			; initialize timers
		ld PRE1, #3		; mod 64, continuous run
		ld T1, #143		; interrupt every 10ms
		or TMR, #T1_CntEn+T1_Load
		ei			; kludge for Z8 to do polling
		di
		ld IMR, #Timer1		; allow only Timer 1 interrupts


;********************************************************************
;
; Initial Port Assignments
;
;   Port 0 -->	Bits 0:3 are set to Adr 8:11 via P01M
;		Bit 4 = Servo Reset active
; 		Bit 5 = Format Enable inactive
; 		Bit 6 = Z8 Test inactive
; 		Bit 7 = Read Header inactive
;
;   Port 1 -->	Bits 0:7 are set to Adr 0:7 via P01M
;
;   Port 2 -->	Bit 0 = Start inactive
; 		Bit 1 = ECC Error inactive
; 		Bit 2 = don't care / input
; 		Bit 3 = Busy active
; 		Bit 4:5 = Msel0,1: Z8 <--> Mem
; 		Bit 6 = don't care / input
; 		Bit 7 = Disk Read/Write: Read
;
;   Port 3  -->	don't care
;
;********************************************************************

		ld P0, #Not_ServoRst+Not_FmenL+Not_ZTestL+Not_RdHdrH
		ld P2, #Not_StartL+Bsy+Z8_Mem+DrwL_Read	; -67h
;
		srp #Wrk_Sys		; context switch
	assume RP:Wrk_Sys
;
		call Clr_BankSwitch
;
		or Excpt_Stat, #PwrRst	; assume a power reset
		ld R2, #1000h /256	; check saved power flags
		ld R3, #1000h #256
		ld R14, #PassWord /256
		ld R15, #PassWord #256
		ld R4, #4		; check 4 bytes
;
PwrRst_Lp	lde R0, @RR2		; get saved byte
		ldc R1, @RR14		; get a byte of password
		incw RR2
		incw RR14
		cp R0, R1
		jr NZ, Power_On
		djnz R4, PwrRst_Lp
;
		and Excpt_Stat, #0FFh-PwrRst
;
Power_On	ld Scr_Cntr, #2000 /256
		ld Scr_Cntr+1, #2000 #256
		ld PwrFlg0, #0F0h	; initialize power-on flags
		ld PwrFlg1, #078h
		ld PwrFlg2, #03Ch
		ld PwrFlg3, #01Eh
		jp Main			; go to main routine


;********************************************************************
;
; Function: External Ram Test
;
;   This test is used as both a diagnostic and a selftest routine
;   in the Widget controller. It is intended to check the external RAM
;   of the controller before allowing the controller to execute
;   any code that could be potentially dangerous to the system.
;
; Inputs: none
;
; Outputs: EcternalRamTest : BOOLEAN {zero flag false if error}
;
; Test: All Zeros / All Ones
;
;  k:=$FF  {load value}
;  For j := 1 To 2 Do
;   Begin
;    Dec_Scr_Ctnr
;    i:=RAMSize
;    RamPtr:=RamSize-1
;    While i<>0 Do
;     Begin
;      Ram[RamPtr]:=k
;      RamPtr:=RamPtr-1
;      i:=i-1
;     End
;     i:=RamSize
;     While i<>0 Do
;      Begin
;       If (Ram[RamPtr]<>k) Then HALT
;       RamPtr:=RamPtr+1
;       i:=i-1
;      End
;     k:=00 {do all zero's test next}
;    End
;
;  RamSize = 2048
;  k = R5
;  j = R8
;  i = R6, R7
;  RamPtr = R10, R11
;  scratch reg = R4
;
;********************************************************************

RamTest		ld R4, #0FFh		; load value
		ld R8, #RamLpTimes
;
RamLp1		call Dec_Scr_Cnt
		ld R5, R4		; remember load value
		ld R7, #RamSize #256
		ld R6, #RamSize /256
		ld R11, #HiRamAdr #256	; Initialize RAM ptr
		ld R10, #(HiRamAdr+RamOffset) /256
RamLp2		lde @RR10, R4		; begin loading RAM
		decw RR10		; point to next RAM location
		djnz R7, RamLp2
		djnz R6, RamLp2
		ld R6, #RamSize /256
;
RamLp3		incw RR10		; point to next RAM location to check
		lde R4, @RR10		; read regs
		cp R4, R5
		jr NZ, RamTestExit	; exit here if failure
		djnz R7, RamLp3
		djnz R6, RamLp3
;
		ld R4, #0		; set up for all zero test
		djnz R8, RamLp1
RamTestExit	ret


;********************************************************************
;
; Function: EPROM Test
;
;  This test is used as both as diagnostic for the external EPROM
;  for the Widget Controller and as a selftest routine in the same
;  unit's system firmware.
;
;  The first location in the external EPROM holds one byte that
;  is the check byte for that EPROM. The check byte is calculated by
;  adding then shifting each byte in the EPROM:
;
; Inputs: TopBank: BYTE {R0}
;
; Outputs: EpromTest: BOOLEAN {zero flag false if error}
;
; Algorithm:
;
;  Begin
;   For both halves of the EPROM Do
;    Sum:=0
;    Sum:=Sum+256*Eprom[0]
;    For i:=1 To MaxEpromAdress Do
;     Dec_Scr_Cnt
;     Sum:=Sum+Eprom[i]
;     If (Sum=0)
;      Then EpromTest:=True
;      Else EpromTest:=False
;
; {note that the check byte is stored in the first two locations
;  of the EPROM}
;
;********************************************************************

EpromTest	ld R4, R0		; make a loop counter out of highaddress
;
E_Lp		ld R0, R4		; get bank to test
		swap R0			; turn loop count back into highaddress
		call LookUp_Rom		; select that bank
		clr R6			; sum := 0
		clr R7
		ld R8, #EpromSize /256
		ld R9, #EpromSize #256
		ld R12, #EpromOffset /256
		ld R13, #EpromOffset #256
		ldc R0, @RR12		; get Eprom[0]
		com R0
		add R6, R0		; sum := sum + 256 * Eprom[0]
		incw RR12		; point to Eprom[1]
		dec R9			; account for Eprom[0]
		ldc R0, @RR12		; get Eprom[1]
		com R0
		add R7, R0		; sum := Sum + Eprom[1]
		incw RR6		; dp two's complement arithmetic
		incw RR12		; point to Eprom[2]
		dec R9			; account for Eprom[1]
;
Eprom_Lp	call Dec_Scr_Cnt
		ldc R0, @RR12		; get Eprom[i]
		incw RR12
		add R7, R0		; sum := sum + Eprom[i]
		adc R6, #0
		djnz R9, Eprom_Lp
		djnz R8, Eprom_Lp
		jr NZ, Eprom_End
;
		djnz R4, E_Lp

Eprom_End	jp Bank_Ret


;********************************************************************
;
; Procedure: MsWait
;
;   This procedure is used as a software timing loop, where
;   the busy wait length is equal to .01 * InputValue in seconds
;   (one unit wait = 10ms)
;
; Inputs: WaitLength: WORD (RR2)
;
; Outputs: none
;
;********************************************************************

MsWait		ld R0, #10		; change LED every 100ms
MsWait_1	and IRQ, #0FFh-Timer1	; clear any pending times
MsWait_Lp	tm IRQ, #Timer1		; wait for timer int
		jr Z, MsWait_Lp
		dec R0
		jr NZ, WsWait_Dec
		push R2			; save counter
		push R3
		call Invert_Led
		pop R3
		pop R2
		ld R0, #10
WsWait_Dec	decw RR2		; count down a unit
		jr NZ, MsWait_1
		ret


Dec_Scr_Cnt	tm IRQ, #Timer1		; wait for timer interrupt
		jr Z, Dec_Scr_End
		call Invert_Led
		and IRQ, #0FFh-Timer1	; get rid of old interrupt
		ld R0, Scr_Cntr		; check for already zero count
		or R0, Scr_Cntr+1
		jr Z, Dec_Scr_End
		decw Scr_Cntr
Dec_Scr_End	ret


Clr_BankSwitch	ld R2, #BankReg /256
		ld R3, #BankReg #256
		ld R1, #7		; clear 7 bits
Clr_B_Lp	lde @RR2, R2
		inc R2
		djnz R1, Clr_B_Lp
		ld R0, Excpt_Stat	; clear all but LED state
		and R0, #0FFh-Led_Mask
		call Set_Led
		ld R0, #Ram1
		call Set_RamBank
		ret


;********************************************************************
;
; Start_Procedure: Main
;
; Inputs: none
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   SlfTst_Result:={assume all tests fail}
;   SlfTst_Result.RamTest:=RamTest
;   SlfTst_Result.EpromTest:=EpromTest
;   Init_ExtStack
;   ZeroBlock(ReadArray)
;   ClearStatus
;   Load_PassWord(WrBlkFence)
;   SelfTest
;   If system passed self test
;    Then
;     Recovery is turned on
;     Load_SprTbl
;     Load_Cache
;     If SystemCode Then Scan
;    Else Recovery is turned off
;   Clr_Bsy(Init_Response, Not(Cmnd_Pending))
;  End
;
;********************************************************************

Main		ld SlfTst_Result, #0FFh	; assume all failures at first
		call RamTest		; test external RAM
		jr NZ, Main_Eprom
		and SlfTst_Result, #0FFh-Ram_Fail
;
Main_Eprom	ld R0, #Eprom2
		call EpromTest
		jr NZ, Main_SelfTst
		and SlfTst_Result, #0FFh-Eprom_Fail
;
Main_SelfTst	call ExtStk_Vector	; init external stack
		ld R14, #WrBlkFence /256
		ld R15, #WrBlkFence #256
		call LdPw_Vector	; set write buffer fence
		call ClrStat_Vector	; clear all status'
		call SlfTst_Vector	; perform selftest
;
Main_LdSpr	tm SlfTst_Result, #0FFh-No_SprTbl
		jr Z, Main_Set_R
		and Excpt_Stat, #0FFh-Recovery	; then recovery off
		jr Main_Lp1
;
Main_Set_R	or Excpt_Stat, #Recovery	; else it is on
		call SprTbl_Vector
Main_Cache	call LC_Vector
		call IScan_Vector
Main_Lp1	call Clr_BankSwitch
		call Set_SeekNeeded
		ld Wrk_lo+10, #Init_Response
		clr Wrk_lo+11		; Cmnd_Pending; IBsy:=false



;********************************************************************
; Module Resident.Assem
;
; This module contains all the routines (besides the Z8 initialization
; procedures) that must be resident within the Z8
;
;********************************************************************

;********************************************************************
;
; Procedure: Clr_Bsy
;
; Inputs: Response:     BYTE (Wrk_lo+10)
;         Cmnd_Pending: BIT (Wrk_lo+11/Bit 7)
;         IBsy:         BIT (Wrk_lo+11/Bit 6)
;         MemAdr:       PTR (RR12)
;
; Outputs: none
;
; Begin
;  While CMD do Begin End {busy wait on CMD}
;  Set up memory select: Apple <--> Mem
;  Memory_Address_Register:=MemAdr
;  Reset BSY
;  While not(CMD) Do Begin End {busy wait on CMD-}
;  Jump(Start_Command) {begin a new command}
; End
;
;********************************************************************

Clr_Bsy		srp #Wrk_lo
	assume RP:Wrk_lo
		ld P2, #Not_StartL+Disk_Mem+DrwL_Read+Bsy
		lde @RR12, R12
		ld P3M, P3m_StMach
		and P3, Dm_Mask		; set DM/IoPort to low
		ld P01M, P01m_StMach
		ld P2, #Not_StartL+Disk_Mem+DrwL_Read+Bsy	; toggle AOE
		tm R11, #MultiWr	; check for multi-write command
		jr Z, Clr_Bsy_Rd
;
		ld P2, #Not_StartL+Apple_Mem
		jr Clr_Bsy1
;
Clr_Bsy_Rd	ld P2, #Not_StartL+Apple_Mem+DrwL_Read
Clr_Bsy1	ld R15, #Cmd		; load mask
		ld R12, #Cmnd_Ptr /256	; MemAdr := Command Buffer
		ld R13, #Cmnd_Ptr #256
Bsy_Lp2		tm P2, R15		; test for CMD active high
		jr Z, Bsy_Lp2


;********************************************************************
;
; Procedure: Wait_Cmd
;
; Inputs: Cmnd_Pending: BOOLEAN (R11/bit 7)
;         Response:     BYTE (R10)
;         IBsy:         BOOLEAN (R11/bit 6)
;         MemAdr:       PTR (R12)
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   Set up memory select: Apple <--> Z8
;   While not(CMD) Do Begin End {busy wait on CMD-}
;   Write(Response) to Apple
;   Set BSY
;   While CMD Do Begin End {busy wait on CMD}
;   Read(Apple_Response) from Apple
;   If (Apple_Response=$55)
;    Then
;     If not(IBsy)
;      Then
;       Memory_Address_Register:=MemAdr
;       reset BSY
;       While not(CMD) Do Begin End (busy wait on CMD-)
;     Set up memory select: Z8 <--> Mem
;     If Cmnd_Pending
;      Then Return
;      Else Jump(Start_Command) {begin a new command}
;    Else Abort(Apple_Interface_Exception, Cmnd_Pending,
;                 Response, Apple_Response)
;  End
;
;********************************************************************

Wait_Cmd	ld P01M, P01m_Image	; restore port to load adr
		ld P2, #Not_StartL+Disk_Mem+DrwL_Read
		lde @RR12, R12		; set external memory address
Wait_Cmd1	ld P2, #Not_StartL+Z8_Apple+DrwL_Read
		ld P01M, #P0_03_Adr+P0_47_Out+Stack_in+P1_Out
		ld P1, R10		; Apple gets response
		ld P2, #Not_StartL+Z8_Apple+DrwL_Read+Bsy
		ld R15, #Cmd
		ld R14, #Apl_Ack	; get ready for response
		ld R13, #IBsy
Cmd_Lp2		tm P2, R15		; test for CMD inactive low
		jr NZ, Cmd_Lp2
; make Port 1 an input
		ld P01M, #P0_03_Adr+P0_47_Out+Stack_In+P1_In
		ld P2, #Not_StartL+Disk_Mem+DrwL_Read+Bsy	; strobe AOE
		ld P2, #Not_StartL+Z8_Apple+DrwL_Read+Bsy	; strobe AOE
		cp R1, R14
		jr NZ, Cmd_NotAck
;
Cmd_TstBsy	tm R11, R13		; test IBsy
		jr Z, No_IBsy
; get back to system access
Cmd_Leave	ld P01M, P01m_Image
		ld P2, #Not_StartL+Bsy+Z8_Mem+DrwL_Read
		ld P3M, P3m_Image
		srp #Wrk_Sys
	assume RP:Wrk_Sys
		ret			; resume command
;
; Mem <--> Host(wr)
; change DM to output
	assume RP:Wrk_Lo
No_IBsy		ld P3M, P3m_StMach
		and P3, Dm_Mask		; set DM/IoPort low
		ld P2, #Not_StartL+Apple_Mem
		ld R15, #Cmd
;
Cmd_Lp3		tm P2, R15		; test for CMD high
		jr Z, Cmd_Lp3
		ld P01M, P01m_Image
		ld P2, #Not_StartL+Z8_Mem+DrwL_Read
		ld P3M, P3m_Image
		tm R11, #Cmnd_Pending
		jp Z, Start_Vector
		ret
;
Cmd_NotAck	ld R15, P1
		ld P01M, P01m_Image
		ld P3M, P3m_Image
		ld P2, #Not_StartL+Bsy+Z8_Mem+DrwL_Read
		srp #Wrk_Sys
	assume RP:Wrk_Sys
		cp Wrk_lo+15, #Free_Proc	; check for Freeing up bus
		jp Z, Free_Vector
;
Wt_Cmd_Nak	push Wrk_lo+15
		call ClrNormStat
		clr R0			; status byte 0
		ld R1, #Bad_55
		call SetStatus
		pop R9
		call Abort


;********************************************************************
;
; Procedure: Get_Wr_Data {get write data from host}
;
; Inputs: Response: BYTE (Wrk_lo+10)
;
; Begin
;  Wait_Cmd(Command_Pending, Response, NotIBsy, WBuffer1)
;  Wait_Cmd(Command_Pending, EndWriteResponse, IBsy, WBuffer1)
; End
;
;********************************************************************

Get_Wr_Data	srp #Wrk_lo
	assume RP:Wrk_lo
		ld R11, #Cmnd_Pending	; Cmnd_Pending:=true, IBsy:=false
		ld R12, #WBuffer1 /256
		ld R13, #Wbuffer1 #256
		call Wait_Cmd
		ld R10, #End_Wr_Response
		ld R11, #Cmnd_Pending+IBsy	; Cmnd_Pending:=true
		call Wait_Cmd
		srp #Wrk_Sys
	assume RP:Wrk_Sys
		ret


;********************************************************************
;
; Procedure: Ack_Read {acknowledge read command, set BSY}
;
; Inputs: Response: BYTE (Wrk_lo+10)
;
; Outputs: none
;
; Begin
;  Wait_Cmd(Command_Pending, Response, Bsy, x)
; End
;
;********************************************************************

Ack_Read	srp #Wrk_lo
	assume RP:Wrk_lo
		ld R11, #Cmnd_Pending+IBsy
		call Wait_Cmd1
		srp #Wrk_Sys
	assume RP:Wrk_Sys
		ret


;********************************************************************
;
; Function: Wr_Resident
;
;  This function exists primarily because the architecture of the
;  Widget Controller prevents the Z8 from executing instructions
;  from external memory while the state machine is running.
;
; Inputs: none
;
; Outputs: Status: BYTE (R0)
;
; Algorithm:
;
; Begin
;  Msel0:1 := Disk <--> Mem
;  Set-up external RAM address counter for Write
;  DiskRW:=Write
;  RdHdrH:=false
;  If (Cylinder>RWI_Cylinder)
;   Then
;    PC:=true
;    RWI:=true
;   Else
;    PC:=false
;    RWI:=false
;  StartStateMachine
; End
;
;********************************************************************

Wr_Resident	srp #Wrk_lo
	assume RP:Wrk_lo
		ld P2, #Not_StartL+Bsy+Disk_Mem
		ld R14, Hi_Rwi_Reg
		ld R15, Lo_Rwi_Reg
		sub R15, Cylinder+1	; check for >RWI_Cylinder
		sbc R14, Cylinder
		jr LT, WPC_Else
		ld R10, #7Fh		; bits inactive
		jr WPC_End
WPC_Else	ld R10, #7Fh-RWI-PC
WPC_End		jr Rd_Res2


;********************************************************************
;
; Function: Fmt_Resident
;
;  This function exists primarily because the architecture of the
;  Widget Controller prevents the Z8 from executing instructions
;  from external program memory while the state machine is running
;
; Inputs: none
;
; Outputs: Status: BYTE (R0)
;
; Algorithm:
;
;  Begin
;   Msel0:1 := Disk <--> Mem
;   Set-up external RAM address counter for Format
;   DiskRW:=Write
;   RdHdrH:=True
;   If (Cylinder>RWI_Cylinder)
;    Then
;     PC:=true
;     RWI:=true
;    Else
;     PC:=false
;     RWI:=false
;   StartStateMachine
;  End
;
;********************************************************************

Fmt_Resident	srp #Wrk_lo
	assume RP:Wrk_lo
		ld P2, #Not_StartL+Bsy+Disk_Mem
		ld R14, Hi_Rwi_Reg
		ld R15, Lo_Rwi_Reg
		sub R15, Cylinder+1	; check for >RWI_Cylinder
		sbc R14, Cylinder
		jr LT, FPC_Else
		ld R10, #0FFh		; bits inactive
		jr FPC_End
FPC_Else	ld R10, #0FFh-RWI-PC
FPC_End		ld R14, #FormatArray /256
		ld R15, #FormatArray #256
		jr Start_StMach


;********************************************************************
;
; Function: RdHdr_Resident
;
;  This function exists primarily because the architecture of the
;  Widget Controller prevents the Z8 from executing instructions
;  from external program memory while the state machine is running
;
; Inputs: none
;
; Outputs: Status: BYTE (R0)
;
; Algorithm:
;
;  Begin
;   Msel0:1 := Disk <--> Mem
;   Set-up external RAM address counter for Read
;   DiskRW:=Read
;   RdHdrH:=True
;   RWI:=false
;   PC:=false
;   StartStateMachine
;  End
;
;********************************************************************

RdHdr_Resident	srp #Wrk_lo
	assume RP:Wrk_lo
		ld R10, #0FFh		; bits inactive
		jr Rd_Res1


;********************************************************************
;
; Function: Rd_Resident
;
;  This function exists primarily because the architecture of the
;  Widget Controller prevents the Z8 from executing instructions
;  from external program memory while the state machine is running
;
; Inputs: none
;
; Outputs: Status: BYTE (R0)
;
; Algorithm:
;
;  Begin
;   Msel0:1 := Disk <--> Mem
;   Set-up external RAM address counter for Read
;   DiskRW:=Read
;   RdHdrH:=false
;   RWI:=false
;   PC:=false
;   StartStateMachine
;  End
;
;********************************************************************

Rd_Resident	srp #Wrk_lo
	assume RP:Wrk_lo
		ld R10, #7Fh		; bits inactive
Rd_Res1		ld P2, #Not_StartL+Bsy+Disk_Mem+DrwL_Read
Rd_Res2		ld R14, #ReadArray /256
		ld R15, #ReadArray #256


;********************************************************************
;
; SubFunction: Start_StMach
;
;  This is a routine that is shared by all the resident state machine
;  routines and exists primarily for the purose of saving space. It's
;  function is to complete the set-up for the state machine and then
;  start it up and wait for it to finish.
;
; Begin
;  Z8 Port 3, Bit 4 := 0 {instead of DM it is an I/O port}
;  Z8 Port 1 := Input {keep Z8 from conflicting with Disk data}
;  SectorsRead := 2* NumberOfSectors
;  While not(SectorDnL) and (SectorsRead<>0) Do
;   Begin
;    If SectorMark
;     Then
;      SectorsRead:=SectorsRead-1
;      While SectorMark Do Begin End
;   End
;  Z8 Port 3, Bit 4 := DM
;  Z8 Port 1 := Adress/Data
;  Msel0:1 := Z8 <--> Mem
;  Status := StatusPort
;  If EccErr
;   Then Status.CrcError:=true
;  StartL:=false
; End
;
;********************************************************************

Start_StMach	lde @RR14, R14		; set buffer address
		ld P3M, P3m_StMach
		and P3, Dm_Mask		; set DM/IoPort low
		ld P01M, P01m_StMach
		ld P0, R10
;
St_Res_1	tm P3, #SectorMark	; test for sector mark
		jr NZ, St_Res_1
		and P2, Start_Mask	; start state machine
		ld R10, #Not_StartL
		ld R11, #Not_EccError
		ld R14, #SectDnL	; load mask
		ld R15, #SectorMark
		ld Wrk_Sys+10, #(NbrSctrs+2)	; timeout after 21 sectors
		call Set_Dmt
;
St_Res_2	tm P3, R15		; count sector marks
		jr Z, St_Res_3
		dec Wrk_Sys+10
		jr Z, St_Res_4
;
St_Res_25	tm P3, R15		; wait for mark to go away
		jr NZ, St_Res_25

St_Res_3	tcm P3, R14		; wait for state machine to finish
		jr Z, St_Res_2
		tcm P3, R14		; sample it twice
		jr Z, St_Res_2
St_Res_4	ld P01M, P01m_Image
		ld R2, #Bsy+Z8_Mem+DrwL_Read
		ld P3M, P3m_Image
		ld R12, #StatusPort /256
		ld R13, #StatusPort  #256
		lde R15, @RR12
		tm P2, R11
		jr Z, Res_Ecc_Err
;
Res_StMach	or P2, R10		; reset state machine
		di			; clear the dead man timer
		srp #Wrk_Sys
	assume RP:Wrk_Sys
		ld R0, Wrk_lo+15	; return StMach status
		ret
;
Res_Ecc_Err	tm DiskStat, #Wr_Op
		jr NZ, Res_StMach
		and R15, #0FFh-WrtNvldL	; if ECC error
		jr Res_StMach


;********************************************************************
;
; Function: Sub3, Add3
;
; These functiond perform the following 24bit arithmetic operations:
;
;   Sub3: A <-- A - B
;   Add3: A <-- A + B
;
; Inputs: A: 3 BYTES (R0, R1, R2)
;         B: 3 BYTES (R12, R13, R14)
;
; Outputs: A: 3 BYTES (R0, R1, R2)
;
;********************************************************************

Sub3		sub R2, R14
		sbc R1, R13
		sbc R0, R12
		ret

Add3		add R2, R14
		adc R1, R13
		adc R0, R12
		ret


;********************************************************************
;
; Procedure: Set_RamBank
;
;  This procedure activated the 2k bank of RAM that is indicated by
;  the input parameter
;
; Inputs: Ram_Bank: BYTE (R0)
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   If (Ram_Bank>3) Then Abort
;   deselect all RAM bits
;   select the RAM bit pointed to by Ram_Bank
;  End
;
;********************************************************************

Set_RamBank	cp R0, #4
		jr LT, Set_RB_Start
		call Abort
Set_RB_Start	ld R2, #Ram_Table /256
		ld R3, #Ram_Table #256
		rl R0			; multiply index by 2
		add R3, R0		; index into table
		adc R2, #0
		ldc R1, @RR2
		incw RR2
		ldc R0, @RR2
		ld R2, #RamBank0 /256	; set the adr bits
		ld R3, R0
		lde @RR2, R3
		inc R2
		ld R3, R1
		lde @RR2, R3
		ret

Ram_Table	DB 0, 0			; adr 13 := 0, adr 12 := 0
		DB 0, 1			; adr 13 := 0, adr 12 := 1
		DB 1, 0			; adr 13 := 1, adr 12 := 0
		DB 1, 1			; adr 13 := 1, adr 12 := 1


;********************************************************************
;
; Procedure: Set_Led
;
;  This procedure changes the state of the controller LED. The
;  state is determined by the value of State (if State is odd
;  then the Led is lit, otherwise it is turned off).
;
; Inputs: State: BIT (R0/Bit 0)
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   Led[State]:=State
;   Bank_Image.Led:=State
;  End
;
;********************************************************************

Set_Led		and Excpt_Stat, #0FFh-LedStat
		or Excpt_Stat, R0
		ld R2, #Led / 256
		ld R3, R0
		lde @RR2, R3
		ret
;
Invert_Led	ld R0, Excpt_Stat
		xor R0, #0FFh-Led_Mask	; invert only the Led bit
		jr Set_Led
;
Led_Wait	ld R2, #0		; 500 ms
		ld R3, #50
		call MsWait
		ret


;********************************************************************
;
; Function: LoadStatus
;
;
; Inputs: none
;
; Outputs: LoadStatus: BYTE (R0)
;
;********************************************************************

LoadStatus	ld R2, #StatusPort /256
		ld R3, #0
		lde R0, @RR2
		ret


;********************************************************************
;
; Procedure: SetStatus
;
;  SetStatus is used to set a particular bit or bits
;  within a specific byte of standard status
;
; Inputs: StatusByte: BYTE (R0)
;
; Outputs: none
;
;********************************************************************

SetStatus	ld R2, #CStatus0 /256
		ld R3, #CStatus0 #256
		add R3, R0
		adc R2, #0
		lde R0, @RR2
		or R0, R1
		lde @RR2, R0
		ret


;********************************************************************
;
; Procedure: Set_Dmt (dead man timer = watchdog)
;
;  This procedure sets the DeadManTimer.
;  The Set_Dmt routine also initializes the DeadManCounter
;  as well as storing away information concerning the process
;  that started the Dmt
;
; Inputs: none
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   Disable Interrupts
;   DeadManCounter:=5s (assume timer interrupts every 10ms)
;   Enable Interrupts
;  End
;
;********************************************************************

Set_Dmt		di
		ld Dmt_Counter, #Dmt_Val /256
		ld Dmt_Counter+1, #Dmt_Val #256
		and IRQ, #0FFh-Timer1	; clear old events
		ei
		ret


;********************************************************************
;
; Procedure: Clr_Dmt (dead man timer = watchdog)
;
;  This procedure clears the DeadManTimer.
;  Theis routine also enables the normal timer
;
; Inputs: none
;
; Outputs: none
;
;********************************************************************

Clr_Dmt		di
		ret


;********************************************************************
;
; Procedure: Chk_Park1
;
;  This procedure performs a busy wait for RR2 x 10ms while
;  monitoring the CMD line. In case of a host request, Wait_Cmd
;  is entered.
;
; Inputs: WaitTime: WORD {RR2}
;
; Outputs: none
;
;********************************************************************

Chk_Park1	and P2, #0FFh-Bsy	; clr BSY
Chk_Pk_Lp2	and IRQ, #0FFh-Timer1	; clear old events
Chk_Pk_Lp1	tm P2, #Cmd		; check for command
		jr NZ, Chk_Pk_HostCmd
		tm IRQ, #Timer1		; nothing to do, wait for timer int
		jr Z, Chk_Pk_Lp1
		decw RR2
		jr NZ, Chk_Pk_Lp2
		ret
;
Chk_Pk_HostCmd	call Clr_BankSwitch
		srp #Wrk_lo
	assume RP:Wrk_lo
		ld R10, #1		; response on bus
		ld R11, #0		; Cmnd_Pending & IBsy cleared
		ld R12, #Cmnd_Ptr /256	; store command bytes here
		ld R13, #Cmnd_Ptr #256
		jp Wait_Cmd


;********************************************************************
;
; Procedure: FormatBlock
;
;********************************************************************

FormatBlock	clr R9
		ld R2, #FmtDelay /256
		ld R3, #FmtDelay #256
		ldc R1, @RR2		; get delay value
		ld R2, #FormatArray /256
		ld R3, #FormatArray #256
		clr R0
FmtBlk_Lp	lde @RR2, R0		; clear format array
		incw RR2
		djnz R1, FmtBlk_Lp
		ld R2, #FHdrSync /256
		ld R3, #FHdrSync #256
		ld R0, #1
		lde @RR2, R0		; store a 1
		incw RR2
		clr R0
		lde @RR2, R0		; then a zero
		incw RR2
		call LH_Vector		; then the header
		ld R2, #FDataSync /256
		ld R3, #FDataSync #256
		ld R0, #1
		lde @RR2, R0		; store a 1
		call Fmt_Resident
		ld R1, R0		; get machine status
		and R1, #Ymask
		cp R1, #NormFmt_State
		jr Z, FmtBlk_End
		call Reset_StMach
		ld R10, R0
		call Abort
;
FmtBlk_End	ld R1, R0
		tm R1, #ServoErr	; servo ok?
		jr NZ, FmtBlk_Srvo
		tm R1, #ServoRdy
		jr NZ, FmtBlk_Srvo1
;
FmtBlk_Srvo	or R9, #FmtError+FmtSrvoErr
		jr FmtBlk_Done
;
FmtBlk_Srvo1	tm R0, #WrtNvldL	; ECC error?
		jr NZ, FmtBlk_Done
		or R9, #FmtError
FmtBlk_Done	ld R0, R9
		ld WrStat, R9
		tcm R0, #FmtError
		ret



;********************************************************************
; Module BS.Assem
;
; This module contains the routines that are used in bankswitching
;
;********************************************************************

;********************************************************************
;
; Procedure BankCall
;
;  This procedure manages subroutine calls from one bank of
;  the controllers externel program space to the other. If
;  one routine wants to call another routine in a different
;  memory bank it must do indirectly through this routine.
;
; Inputs: Address: PRT (RR2)
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   LookUp_Rom(Address div 256)
;   Goto @Address
;  End
;
;********************************************************************

Bank_Call	push RP
		ld Wrk_Sys2+4, RP	; sace RP for reference later
		srp #Wrk_Sys2
	save				; let as remember RP value
	assume RP:Wrk_Sys2
		add R4, #2		; get reg 2 in original RP
		ld R0, @R4		; get hibyte of called adr
		call LookUp_Rom
		pop RP
	restore
		jp @RR2


;********************************************************************
;
; Procedure: LookUp_Rom
;
;  This procedure is used to determine which of five 4k
;  banks of program space the user wishes to execute within.
;
; Inputs: HighAddress: BYTE (R0)
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   Case HighAddress Of
;    0 : Adr13:=0, Adr12:=0
;    1 : Adr13:=0, Adr12:=0
;    2 : Adr13:=0, Adr12:=1
;    3 : Adr13:=1, Adr12:=0
;    4 : Adr13:=1, Adr12:=1
;   Otherwise Abort
;  End
;
;********************************************************************

LookUp_Rom	tm R0, #80h		; check for adr out of range
		jr NZ, LU_Abort
		and R0, #70h		; mask off unnecessary stuff from address
		cp R0, #40h
		jr LE, BC_1
LU_Abort	call Abort
;
BC_1		ld R2, #RomTable /256
		ld R3, #RomTable #256
		swap R0			; turn highaddress into index value
		rl R0			; multiply index by 2 (2 byte/element table)
		add R3, R0		; index into table
		adc R2, #0
		ldc R1, @RR2		; get ROM address values
		incw RR2
		ldc R0, @RR2
		ld R2, #RomBank0 /256
		ld R3, R0
		lde @RR2, R3		; set EpromBank0, 1
		ld R2, #RomBank2 /256
		ld R3, R1
		lde @RR2, R3		; set EpromBank2, 3
		ret

RomTable	DB 0, 0
		DB 0, 0
		DB 0, 1
		DB 1, 0
		DB 1, 1


;********************************************************************
;
; Procedure: Bank_Ret
;
;  This procedure is used as the return linkage when another
;  procedure wishes to return to a location that is not
;  in the same external program bank.
;
; Inputs: none
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   Address.HiByte:=Stack[StackPtr]
;   Address.LoByte:=Stack[StackPtr-1]
;   LookUp_Rom(Address div 256)
;   Goto @Address
;  End
;
;********************************************************************

Bank_Ret	push FLAGS
		push RP
	save				; let as remember RP value
		srp #Wrk_Sys2
	assume RP:Wrk_Sys2
		ld R1, SPL		; get location of return address
		add R1, #2		; account for the two pushes at
		ld R0, @R1		;  entry of routine
		call LookUp_Rom
		pop RP
	restore
		pop FLAGS
		ret



;********************************************************************
; Module Int.Assem
;
; This module contains the routines associated with
; the various interrupts that the Widget controller deals with
;
;********************************************************************

;********************************************************************
;
; Procedure: Vector0:4 {illegal interrupts}
;
;********************************************************************

Vector0
Vector1
Vector2
Vector3
Vector4		pop R9			; pop FLAGS
		pop R10			; pop return address
		pop R11
		call Abort


;********************************************************************
;
; Procedure: Vector5 {IRQ5 interrupt}
;
;  This procedure decrements the DeadMan Timer Counter. If the
;  result is zero then Abort
;
;********************************************************************

Vector5	decw Dmt_Counter
		jr Z, Vector0		; abort if zero
		and IRQ, #0FFh-Timer1	; clear old interrupt
		ei
		iret



;********************************************************************
; Module Except.Assem
;
;********************************************************************

;********************************************************************
;
; Procedure: Abort
;
;  This procedure is the 'garbage collector' for this set of
;  firmware. Abort is the place where all routines 'goto' if
;  things get so screwed up that they can no longer cope.
;
; Inputs: none, address of caller on stack
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   Except_Stat.Recovery:=false
;   DiskStat.Parked:=false
;   DiskStat.On_Track:=false
;   Clr_BankSwitch
;   Set_RamBank(Ram0)
;   Registers R14 and R15 get address of caller
;   For i:=0 To 15 Do
;    Abort_Stat[i]:=Register[Rp+i]
;   Initialize Write_BufferFence
;   SetStatus(Abort)
;   Goto RdLeave
;  End
;
;********************************************************************

Abort		di
		pop R14			; remember who called us
		pop R15
		push RP			; save context
		srp #Wrk_Scr
	assume RP:Wrk_Scr
		call Clr_BankSwitch
		pop R0			; get caller's context from stack
		ld R1, #16		; load 16 locations
		ld R14, #Abort_Stat /256
		ld R15, #Abort_Stat #256
Abort_Lp1	ldei @RR14, @R0
		djnz R1, Abort_Lp1
		srp #Wrk_Sys
	assume RP:Wrk_Sys
		clr SPH			; clean up stack
		ld SPL, #Stack_Top
		call ExtStk_Vector
		call ZrRd_Vector
		ld R14, #WrBlkFence /256	; re-write the write block fence
		ld R15, #WrBlkFence #256
		call LdPw_Vector
		tm DiskStat, #MultiBlk
		jr Z, Abt_Stat_Ld
		call UpDate_Logical
Abt_Stat_Ld	call SS_Abort
		tm P2, #Bsy		; check if BSY is set
		jp NZ, RdL_Vector
		srp #Wrk_lo
	assume RP:Wrk_lo
		ld R10, #Init_Response
		ld R11, #0
		jp Clr_Bsy1
;
UpDate_Logical	call LL_Vector
		add R14, R5
		adc R13, #0
		adc R12, #0
		ld R0, #Wrk_Sys+12
		ld R1, #3
		ld R2, #LogicalBlock /256
		ld R3, #LogicalBlock #256
UpD_Lgcl_Lp	ldei @RR2, @R0
		djnz R1, UpD_Lgcl_Lp
		and DiskStat, #0FFh-MultiBlk
		ret


;********************************************************************
;
; ProcedureSet: SetStatus_Calls
;
;  These are a few of the more commonly used SetStatus routines:
;
;   SS_Abort: SetStatus(Abort)
;             SetStatus(OperationFailed)
;
;   SS_OpFail: SetStatus(OperationFailed)
;
;   SS_ReadErr: SetStatus(ReadErrCount)
;
;   SS_NoHdr: SetStatus(NoHeaderFound)
;
;   SS_SprWarn: SetStatus(SprBlk_Warn)
;
;********************************************************************

SS_Abort	ld R0, #1		; status byte 1
		ld R1, #Stat_Abort
		call SetStatus
SS_OpFail	ld R0, #0		; status byte 0
		ld R1, #Op_Failed
SS_Set		call SetStatus
		ret

SS_RdCnt	ld R0, #3		; status byte 3
		ld R1, RdErrCnt
		jr SS_Set

SS_ReadErr	ld R0, #0		; status byte 0
		ld R1, #8
		call SetStatus
		ld R0, #3		; status byte 3
		ld R1, RdErrCnt
		jr SS_Set

SS_NoHdr	ld R0, #0		; status byte 0
		ld R1, #Stat_No_Hdr
		jr SS_Set

SS_SprWarn	ld R0, #1		; status byte 1
		ld R1, #SprBlk_Warn
		jr SS_Set


;********************************************************************
;
; Procedure: Reset_StMach
;
;  This routine is used in the case where the state machine
;  ended abnormally (i.e. it ended in a state other than its
;  normal 'end-state'). The assumption is made that the drive
;  clock is not available and that the controller must supply
;  a signal in its place in order for the state machine to
;  reset.
;
; Inputs: none
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   ZTestL:=true
;   For i:=16 Downto 0 Do
;    Zrwck:=true
;    Zrwck:=false
;   ZTestL:=false
;  End
;
;********************************************************************

Reset_StMach	push RP			; save context
		srp #Wrk_lo
	save				; let as remember RP value
	assume RP:Wrk_lo
		and P0, #0FFh-Not_ZTestL	; assert ZTestL
		ld R14, #16		; i:=16
Res_StM_Lp	or P3, #Zrwck		; set clock
		and P3, #0FFh-Zrwck	; clear clock
		djnz R14, Res_StM_Lp
		or P0, #ZTestL		; deassert ZTestL
		or P0, #Zrwck		; set clock
		pop RP
	restore
		ret

Int_Version	DB 00, 00



;********************************************************************
; Module Res.Misc.Assem
;
;********************************************************************

ReSeek		ld Seek_Type, #Access_Offset
		decw SeekCount		; account for zero track seek
		ld R12, Cylinder
		ld R13, Cylinder+1
		ld R14, Head
		ld R15, Sector

New_Seek	ld R2, #Seek_Vector /256
		ld R3, #Seek_Vector #256
		call Bank_Call
		jp Bank_Ret


;********************************************************************
;
; Procedure: Load_Header
;
;  The procedure is responsible for generating (and loading
;  into the disk buffer) the header that will be used to identify
;  the correct block to operate on.
;
; Inputs: BufferPtr: PTR (RR2)
;
; Outputs: BufferPtr+5: PTR (RR2)
;
; Algorithm:
;
;  Begin
;   @BufferPtr(0):=HiCylinder
;   @BufferPtr(1):=LoCylinder
;   @BufferPtr(2)/bits 7:6:=Head
;   @BufferPtr(2)/bits 5:0:=Sector
;   @BufferPtr(3):=Invert(@BufferPtr(0))
;   @BufferPtr(4):=Invert(@BufferPtr(1))
;   @BufferPtr(4):=Invert(@BufferPtr(2))
;  End
;
;********************************************************************

Load_Header	ld R0, #Cylinder	; load header: hi cylinder
		ldei @RR2, @R0
		ldei @RR2, @R0		; load lo cylinder
		ld R1, @R0		; head
		swap R1
		rl R1
		rl R1
		inc R0
		or R1, @R0		; merge in sector
		lde @RR2, R1		; load head/sector
		incw RR2
		ld R0, Cylinder		; Not(hi cylinder)
		com R0
		lde @RR2, R0
		incw RR2
		ld R0, Cylinder+1	; Not(lo cylinder)
		com R0
		lde @RR2, R0
		incw RR2
		com R1			; Not(head/sector)
		lde @RR2, R1
		incw RR2
		ret


;********************************************************************
;
; Function: LoadLogical
;
;  This function returns the last logical block number
;  requested by the host.
;
; Inputs: none
;
; Outputs: LogicalBlockNumber: 3 BYTES (R12:14)
;
;********************************************************************

Load_Logical	ld R2, #LogicalBlock /256
		ld R3, #LogicalBlock #256
		ld R1, #3
		ld R0, RP
		or R0, #0Ch
Log_Log_Lp	ldei @R0, @RR2
		djnz R1, Log_Log_Lp
		ret


;********************************************************************
;
; Function: Chk_Chk_Byte
;
; Inputs: SourcePtr: PTR (R14)
;         SourceLength: BYTE (R8)
;
; Outputs: If CheckBytes match
;           Then Zero_Flag:=true
;           Else Zero_Flag:=false
;
; Algorithm:
;
;  Begin
;   Temp:=0
;   For i:=1 To SourceLength Do
;    Temp:=Temp+@SourcePtr
;    SourcePtr:=SourcePtr+1
;   Compare(Temp, @SourcePtr)
;  End
;
; Temp: R0, Scratch Register: R2
;
;********************************************************************

Chk_Chk_Byte	clr R0
ChkB_Lp1	lde R2, @RR14
		add R0, R2
		incw RR14
		djnz R8, ChkB_Lp1
		com R0
		lde R1, @RR14
		cp R0, R1		; set/clear zero flag
		ret


;********************************************************************
;
; Procedure: Gen_Chk_Byte
;
; Inputs: SourcePtr: PTR (R14)
;         SourceLength: BYTE (R8)
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   Temp:=0
;   For i:=1 To SourceLength Do
;    Temp:=Temp+@SourcePtr
;    SourcePtr:=SourcePtr+1
;   Temp:=not(Temp)
;   @SourcePtr:=Temp
;  End
;
; Temp: R0, Scratch Register: R2
;
;********************************************************************

Gen_Chk_Byte	clr R0
Gen_ChkB_Lp1	lde R2, @RR14
		add R0, R2
		incw RR14
		djnz R8, Gen_ChkB_Lp1
		com R0
		lde @RR14, R0
		ret


;********************************************************************
;
; Procedure: ClearStatus
;
;  This procedure clears all the bytes within the status array
;
; Inputs: none
;
; Outputs: none
;
; Algorithm:
;
;  Begin
;   For i:=1 To Length(StatusArray)
;    StatusArray[i]:=0
;  End
;
;********************************************************************

ClearStatus	ld R2, #CStatus0 /256
		ld R3, #CStatus0 #256
		clr R0			; clear all status bytes
		ld R1, #(End_CStatus-CStatus0)
Clr_Stat	lde	@RR2, R0
		incw	RR2
		djnz	R1, Clr_Stat
		jp	Bank_Ret


;********************************************************************
;
; Procedure: Ld_LgclBlk {load logical block}
;
;  This procedure is used to extract the LogicalBlockNumber
;  information from a command string (sent by the host) and
;  move it into the global variable LogicalBlock.
;
; Inputs: Offset: BYTE (R0)
;
; Outputs: none
;
; Global Variables Changed: LogicalBlock
;
; Algorithm:
;
;  Begin
;   For i:=1 To Length (LogicalBlock) Do
;    LogicalBlock[i]:=CmdArray.KLogicalBlock[i]
;  End
;
;********************************************************************

Ld_LgclBlk	ld ScrReg0, R0		; pass parameter
		push RP			; save context
		srp #Wrk_Scr
	save
	assume RP:Wrk_Scr
		ld R2, #CStatus4 /256
		ld R3, #CStatus4 #256
		add R3, R0		; add offset to BlockNumber
		adc R2, #0
		ld R14, #LogicalBlock /256
		ld R15, #LogicalBlock #256
		ld R1, #4		; move 4 bytes
Ld_Lgcl_Lp	lde R0, @RR2
		lde @RR14, R0
		incw RR2
		incw RR14
		djnz R1, Ld_Lgcl_Lp
		pop RP			; context switch
	restore
		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
;
; Algorithm:
;
;  Begin
;   For i:=1 To CacheLength Do
;    CachStat[i].Seek.Needed:=true
;  End
;
;********************************************************************

Set_SeekNeeded	ld R2, #CacheStat /256
		ld R3, #CacheStat #256
		ld R1, #CacheLength-1
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


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