CMD

.Page
;++
;
;			Cmd
;
; Cmd is the commmand interpreter; it looks at the Iob Gobyte and evaluates the
; command to be done.  If an interrupt is pending, '81' command will be
; aborted.  All commands are checked for the validity of their parameters by
; the "Validate" routine.
;
; REGISTERS
;	All =	Destroyed
;
; CALLS
;	Validate	Validates the parameters of the Iob
;	Rwts	Handles all commands related to the disk
;	Seek	Moves the heads to Drive/Side/Track
;	Call	Will call routine
;	ClrIst	Clears the Interrupt Status Bye
;	SetIMsk	Enables drives for interrupts using mask byte
;	ClrIMsk	Disables drives for interrupts using mask byte
;	WaitROM	Waits for particular GoByte sequence before Cold Restart
;	ESAD	Will only access ROM; loops forever on until reset
;++
.Page

;++
;	For x:=IobSize Downto 0 do		Transfer Iob into internal copy
;	   iiob,x := iob,x;
;	"A" reg will be left containing GoByte
;--
Cmd	.Equ *				; Entry point for Cmd
	Ldy	CmdX
	Ldx	#IobSize			; Move only the eight bytes that are passed
Cmd1	Lda	Iob,x			; Fetch
	Sta	IIob,x			; And deposit
	Sta	@SaveL,y			; for posterity
	Dey
	Dex				; Count down
	Bpl	Cmd1

	Sty	Cmdx
	Iny
	Bne	$32
	Ldy	#CmdLeng
	Sty	CmdX

;	If IIob.Gobyte = 81 then
;	   Goto Rwts

$32	Cmp	#RwtsCmd			; Rwts command?
Beq	Rwts				; Yes & let Rts from Rwts return to Loop

;	Else
;	Begin
;	   In not (IIob.GoByte in [Valid IIob.GoByte]) then
;	   begin
;	      Iob.GoByte := 001;		Invalid command
;               Carry := Clear		Command completed
;	      Return
;	   end;
;	Validate

	Cmp	#NullCmd			; If null command then exit
	Beq	CmdNoErr

	Sec
	Sbc	#LwCmdNo			; Subtract lowest command number
	Bcc	Cmd3			; Oooops!  Not a valid GoByte command
	Cmp	#CmdNumb+1		; Number of commands in range
	Bcc	Cmd5			; Skip if valid GoByte command
Cmd3	Lda	#GErrCmd			; Else signal invalid GoByte
	Bne	CmdClnUp			; Cleanup & exit

Cmd5	Tax				; Index to command already in A-reg!
	Sta	HoldInx			; Save index fro the moment
	Lda	TestGoB,x			; Fetch Parsing for GoByte Commands
	Jsr	Validate			; And validate it
	Bcs	CmdClnUp			; Not a valid command
	Lda	HoldInx			; Get back the index to the command

;	If Valid then
;	case GoByte of
;	83:	Seek			Seek to track
;	84:	Call			Call 6504 address
;	85:	ClrIst			Clear Ist (Interrupt Status) bits
;	86:	SetIMsk			Set (enable) bits in IMsk (Interrupt Mask)
;	87:	ClrIMsk			Clear (disable) bits in Imsk (Interrupt Mask)
;	88:	WaitRom			Wait in Rom (used by 68K ram test)
;	89:	ESAD			Jump to self in Rom and never return
;	End; {case}
;	End;

	Asl	A			; To index into word table
	Tax				; Use command number as index
	Jsr	Cmd6
	Lda	HoldInx			; Fetch the index (0..6)
	Beq	CmdRts			; Skip if comand was a "Seek" command
	Cmp	#01			; Also skip if it was a "Call" command
	Beq	CmdRts
CmdNoErr	.Equ *
	Lda	#0			; Signal no error

CmdClnUp	Sta	Iob+ErrStat		; Common error reporting code for Cmd and Rwts
	Lda	#00
	Sta	Iob+GoByte		; Allow host to process error (if any)

CmdRts	Rts				; And exit

;--
;
;	Cmd Jump Table Dispatcher
;

Cmd6	Lda	CmdJmp+1,x		; Fetch th ehigh byte
	Pha				; Put it on the stack
	Lda	CmdJmp,x			; Then the low byte
	Pha
	Rts				; and jump indirect top of stack

CmdJmp	.Word	Seek-1,Call-1,ClrIst-1,SetIMsk-1,ClrIMsk-1
	.Word	WaitRom-1,ESAD-1

.Page
;++
;
;		Rwts
;
; Rwts (Read Write Track Sector) is the routine that parses and dispatches
; all commands that affect the disk ( except Seek).  If no interrupts are
; pending and all parameters are valid, then the memory will be disabled to
; access by the 68K and the proper routine will be called.
;
; REGISTER
;  IN
;	A =	IIob+GoByte
;
;  OUT
;	All ==	Destroyed
;
; CALLS
;	Validate	Validates parameters for Cmd, driven by TestTbl
;	Init	Set up Global conditions used by most commands
;	Read	Reads Drive/Side/Track/Sector
;	Write	Writes Drive/Side/Track/Sector
;	Unclamp	Unclamps the disk in Drive
;	Format	Formats the disk in Drive starting at Side/Track
;	Verify	Reads the disk in Drive starting at Side/Track
;	FormTrack	Formats single Track on Disk in Drive
;	VerTrack	Read single Track on Disk in Drive
;	ReadBF	Read Drive/Side/Track/Sector w/o checksum verify
;	WriteBF	Writes Drive/Side/Track/Sector w/o checksum creation
;	ClpEnty	Will clamp disk in drive
;
;++
.Page

Rwts	.Equ *				; Entry point for Rwts

;	Validate IIob.Command;
;	if not valid then
;	begin
;	   Iob.GoByte := Command error
;	   Carry := Clear
;	   return from Rwts
;	end
;	Validate;
;	if not valid then return from rwts
;	else
;	begin
;	   lock memory
;	   init
;	   call command
;	   Ist := R/W command done
;	   UpdInt
;	end
;	end
;	return from rwts
;++

	Lda	OkToGo			; If <> 0 then interrupt is pending
	Beq	AOk
	Lda	#GeErrIntr		; Pending interrupt error
NotAOk	Jsr	CmdClnUp			; Report error
	Beq	Rw400			; Send an interrupt back to host

AOk	Ldx	IIob+Command		; Fetch the command number
	Cpx	#MaxCmd+1			; compare against maximum command
	Bcc	Rwts4			; Skip if valid
	Lda	#GeErrCmd			; Signal a command error
	Bne	NotAOk			; Local error exit handler

Rwts4	Lda	TestTbl,X			; Fetch bit mask of parameters to check
	Jsr	Validate			; Validate return carry clear if OK
	Jsr	CmdClnUp			; Will clear GoByte and return error code
	Bcs	Rw400			; If error the return FDirH

Rwts40	Jsr	Init			; Set up global conditions used by most commands
	Lda	IIob+Command		; Load with command byte
	Asl	A			; Mult by two to address word entry
	Tax				; Put into index register
	Jsr	Rwts7			; Push return address on stack and execute cmd

	Sta	DisL			; Unlock memory
	Sta	BootH			; Tell 68K that memory is available
	Bcc	Rwts5			; If carry clear then no error
	Sta	Iob+ErrStat		; If error then report this to host

Rw400	.Equ *
	Ldy	CmdX			; And save for posterity
	Cpy	#CmdLeng
	Bne	$10
	Ldy	#6
	Bne	$20
$10	Tya
	Clc
	Adc	#7			; Point at "Speed" byte
	Tay
$20	Lda	Iob+ErrStat
	Sta	@SaveL,y

Rwts5	Lda	#040			; Use drive 80
	Ora	Ist			; Set bit in interrupt status
	Sta	Ist
	Jmp	UpdInt

;--
;
;		Rwts command dispatcher

Rwts7	Lda	RwtsJmp+1,x		; Fetch the high byte of the routine
	Pha				; Push it
	Lda	RwtsJmp,x			; Fetch the low byte of the address
	Pha				; Push it to the stack
	Sta	BootL			; Tell the 68K that memory is disabled
	Sta	DisH			; Disable address al last possible moment
	Rts				; Pop address and jump to it

RwtsJmp	.Word	Read-1,Write-1,UnClamp-1
	.Word	Format-1,Verify-1,FormTrak-1,VerTrack-1
	.Word	ReadBF-1,WriteBf-1,ClpEnty-1

.Page
;++
;
;			Validate
;
; Validates the Iob parameters passed from the 68K depending on the
; bit pattern in the A-reg.
;
; REGISTERS
;  IN
;	A =	Parameters in the Iob to be tested:
;
;	Bit	Contents
;	---	--------
;	0 (LSb)	Drive and a clamped disk
;	1	Side #
;	2	Sector #
;	3	Track #
;	4	Mask
;	5	Confirmation byte
;	6	Write Protection
;	7 (MSb)	Format/Verify parameters
;
;	X =	Any value
;	Y =	Any value
;
;  OUT
;	A =	Destroyed
;	X =	Destroyed
;	Y =	Destroyed
;
; CALLS
;	EnblTest	Test for drive being enabled for interrupts
;	TrkClss	Returns w/ "Y" = class of Track
;	ReadDWP	Tests for write protected disk
;---
.Page

Validate	.Equ *				; Entry point for validate
	Sta	Temp1			; Save "A" here during validation
	Beq	VldNoTst			; If zero then no testing required
	Ldx	#0E			; Test bits 7-0, use 14. to index word array
Valid1	Lsr	Temp1			; Shift and test the carry bit
	Bcc	Valid20			; Bit not set; skip this test
	Stx	Temp2			; Save the "X" couinter value
	Lda	ValJmp+1,x		; Pick up the high byte
	Pha				; Put it on the stack
	Lda	ValJmp,x			; Pick up the low byte
	Pha				; Put it too on the stack
	Rts				; Jmp to the test

; This is the return point after a successful test

Valid2	Ldx	Temp2			; Restore "X" counter value
Valid20	Dex				; Loop count -2 = next test address index
	Dex
	Bpl	Valid1			; Will loop until index is negative
VldNoTst	Lda	#0
	Clc				; Signal no errors
	Rts

; This the return point after a failed test

Valid3	Sec				; Something went wrong
	Rts

;--
;
;		Test drive number, if the drive is enabled and for a clamped disk

ValidDr	Lda	IIob+Drive
	Cmp	#80			; Must use 'Lower' drive value
	Bne	$28
	Lda	Clamped
	Bne	$57			; Yes, there is a clamped disk
$28	Lda	#GeErrClm			; No disk in drive error
	Bne	$57			; Always taken

$57	Jsr	EnblTest			; Test if the drive is enabled
	Bcs	Valid2			; Yes, the drive is enabled
	Lda	#GeErrNA			; Drive not enabled
	Bne	Valid3			; Always taken

;--
;
;		Test side number

ValidSI	Lda	#GeErrSid			; Assume error
	Ldy	IIob+Side
	Beq	Valid2			; side 0 is always good
	Cpy	#01			; now see if side 1
	Bne	Valid3			; Not side 1 either
	Ldy	#02
	Cpy	Iob+NoSides		; Will range from 0 to 2 -- must be 2 for dual sided
	Bne	Valid3			; Not a dual sided drive
	Beq	Valid2

;--
;
;		Test sector number

ValidSe	Jsr	TrkClss			; Find track class
	Lda	IIob+Sector		; New sector number
	Cmp	SecPrTrk,y		; Compare against max sectors per track class
	Bcc	Valid2			; Ok, within limits
	Lda	#GeErrSec			; Oops, to many
	Bne	Valid3			; Always taken

;--
;
;		Test track number

ValidTr	Lda	IIob+Track		; Fetch the track
	Cmp	#MaxTrack+1		; And test against the maximum track number
	Bcc	Valid2			; Ok, nothing wrong so far
	Lda	#GeErrTrk			; Track number i too high, signal error
	Bne	Valid3			; Always taken

;--
;
;		Test mask

ValidCF	Inc	Iob+FmtCnfM		; If correct byte, this will inc to zero
	Beq	Valid2			; Ok, byte was = FF
	Lda	#GeErrFmPr		; Wrong byte
	Bne	Valid3			; Always taken

;--
;
;		Test for write protection

ValidWP	Jsr	ReadWP			; Test for a write protected disk
	Bcs	Valid2			; Carry = 1 ==> not protected
	Lda	#SErrProt			; Write protection error constant
	Bne	Valid3			; Always taken

;--
;
;		Test for format and verify parameters

ValidFV	Lda	IIob+Track
	Ora	IIob+Side			; Must have Trk/Side = 0
	Beq	Valid2			; Params are Ok-continue
	Lda	#GeErrFmPr		; Track/Side <> 0 error
	Bne	Valid3			; Always taken

;--
;
; Validation jump table
;	Table must be in this order -- Drive must be validated and selected
;	prior to checking for write protection

ValJmp	.Word	ValidFV-1,ValidWP-1,ValidCF-1,ValidMA-1,ValidTr-1
	.Word	ValidSe-1,ValidSi-1,ValidDr-1

.Page
;++
;
;		EnblTest
;
; Test IMsk against X-reg to find out if the drive indexed by x is enabled.
;
;--
;
; REGISTERS
;  IN
;	A =	Any Value
;	X =	=0, Drive 0; =2, Drive 80
;	Y =	Any value
;  OUT
;	A=	Destroyed
;	X =	Unchanged
;	Y =	Unchanged
;	Carry =	=Clear, drive not enabled; =Set drive enabled
;
;--

EnblTest	.Equ *				; Entry point for EnblTest

	Lda	#080			; Drive 80
EnblTst1	And	IMsk			; Test against interrupt mask
	Adc	#0F8			; Cause overflow if 008 or larger
	Rts

.Page
;++
;
;		Init
;
; Set up global conditions used by most Rwts commands
;
; REGISTERS
;  IN
;	A =	Any value
;	X =	Any value
;	Y =	Any value
;
;  OUT
;	A =	Destroyed
;	X =	Destroyed
;	Y =	Unchanged
;
;++
;
;	RetryCnt := MaxRetry;
;	RecalCnt := MaxRecal;
;	Error counters := 0;

Init	.Equ *				; Entry point for Init
	Lda	MaxRetry
	Sta	RetryCnt
	Lda	MaxRecal
	Sta	RecalCnt
	Lda	MaxDDly			; Motor off delay time
	Sta	WthHih
	Lda	IIob+Side
	Beq	$09
	Lda	#20			; If side 1 then set only bit 5
	Sta	IIob+Side
$09	Lda	#00
	Sta	WtLow			; Reset 3 byte counter
	Sta	WtMid
	Sta	RtyFlg			; When = 2 then abort read or write operation
	Ldx	#ErrLen			; Number of error counters
$12	Sta	StSlp,x			; Zero the counter
	Dex
	Bpl	$12			; Zero based index
	Rts

.Page
;++
;
;		Call
;
; Calls a routine in the Rom or Ram as specified by the address in AdrL, AdrH.
; This is mostly used by diagnostics.  You can always return from the call by
; Executing an Rts.
;
;
; REGISTERS
;  IN
;	A =	Any value
;	X =	Any value
;	Y =	Any value
;
;  OUT
;	A =	Destroyed
;	X =	Destroyed
;	Y =	Destroyed
;
;++

; First make sure that the motors are off
;  then jump off into the blue

Call	.Equ *				; Entry point for call
	Jsr	PrkClr0			; Park heads, turn off motors & clear GoByte
	Jmp	@IIob+Adrl		; Jump indirect on the address in the IIob

.Page

;++
;
;		ClrIst
;
; Clears selected bits in the Ist (Interrupt Status) and then checks if FDir
; should still be high. Mask should contain a 1 for the bits in Ist to
; be switched off
;
; REGISTERS
;  IN
;	A =	Any value
;	X =	Any value
;	Y =	Any value
;  OUT
;	A =	Destroyed
;	X =	Unchanged
;	Y =	Unchanged
;
; CALLS
;	UpdInt	Updates FDir depending in Ist
;
;++

ClrIst	.Equ *				; Entry point for ClrIst
	Lda	IIob+Mask			; Fetch the mask
	Eor	#0FF			; Ones complement
	And	Ist			; And with Ist to switch off selected bits
	Sta	Ist			; Save new Ist
ClrExit	Jmp	UpdInt			; Calculate new status of FDir

.Page

;++
;
;		SetIMsk
;
; Sets (Enables) the interrupt mask fro drive 0 and drive 80 and computes the
; new status if FDir.
;
; REGISTERS
;  IN
;	A =	Any value
;	X =	Any value
;	Y =	Any value
;  OUT
;	A =	Destroyed
;	X =	Unchanged
;	Y =	Unchanged
;
; CALLS
;	UpdInt	Update FDir status
;
;	Imsk := $88
;	Call UpdInt -- will return from UpdInt to caller
;++

SetImsk	.Equ *				; Entry point for SetIMsk
	Lda	IIob+Mask
	Ora	IMsk
	Sta	IMsk			; Save the new IMsk value
	Jmp	UpdInt			; Calculate the new status of FDir

.Page
;++
;
;		ClrImsk
;
; Clears (Disables) then IMsk (Interrupt Status) selectivly.  Computes new status
; of the FDir.
;
; REGISTERS
;  IN
;	A =	Any value
;	X =	Any value
;	Y =	Any value
;  OUT
;	A =	Destroyed
;	X =	Unchanged
;	Y =	Unchanged
;
; CALLS
;	UpdInt	Update FDir
;
;	Call UpdInt -- Will return from UpdInt to caller
;++

ClrIMsk	.Equ *				; Entry point for ClrIMsk
	Lda	IIob+Mask
	Eor	#0FF
	And	IMsk
	Sta	IMsk			; Save new IMsk value
	Jmp	UpdInt			; Calculate new status of FDir