*
* $Header: DH0:src/omti/dist/src/device/RCS/Omti_Cmd.a,v 1.1 92/11/25 02:14:01 Barnard Exp $
*

*
* omti_cmd.a contains the Device <-> Controller communication
* for the omti.device
*
*

				NOLIST
				include	"libraries/dos.i"
				LIST

* In dieser Datei findet sich der gesamte Code für die
* Device <-> Controller - Kommunikation

*<------------------- Kommandos für Device ------------------->*

* Alle Kommandos bekommen
* A2: Pointer auf Drive-Charakteristics
* A6: Pointer auf Device-Struktur

* Unbekannte Kommandos

cmdinvalid					; Invalid Command
cmdreset					; Reset Device
cmdstop						; Stoppe Kommando
cmdstart					; Starte Kommando wieder
cmdflush					; Flushe alle Buffer
cmdreadraw					; Lesen von rohen Tracks
cmdwriteraw					; Schreiben von rohen Tracks

				move.b	#IOERR_NOCMD,IO_ERROR(a1)	; Diese Kommandos kennt das Device nicht

* Kommandos, die immer korrekt sind

cmdclear					; Lösche alle internen Buffer
cmdupdate					; Update den Zustand des Controllers und der Platte
cmdremove					; für Diskwechsel
cmdgetdrivetyp				; hole Laufwerkstyp
cmdchangenum				; Anzahl der Diskwechsel (sic)

				moveq.l	#DOSTRUE,d0	; Befehl ist korrekt
				rts

* Write-Protect-Test
* Wenn wrprot == FF, dann Disk ist R/O

cmdprotstatus	moveq.l	#0,d0				; lösche D0
				move.b	dc_wrprot(a2),d0	; hole den Wrt-Protect-Status
				move.l	d0,IO_ACTUAL(a1)	; Nach io_actual
				moveq.l	#DOSTRUE,d0			; ok und Ende
				rts

* Führe ein Rekalibierungskommando aus

cmdchangestate						; Test, schaue, ob Disk im Drive
				lea		tdr(pc),a0	; Lade Tdr-Tabelle
				bra		sendomti	; Schicke sie zum Omti

* Hole die Maximale Tracknummer

cmdgetmaxtracks

				move.l	dc_maxoff(a2),d0		; Anzahl der Bytes (!) auf der Platte holen
				divu	#NOF_SECS*SECSIZE,d0	; Durch die Anzahl der Bytes pro Spur teilen
				move.w	d0,IO_ACTUAL+2(a1)		; Lade Ergebnis in obere Hälfte von IO_ACTUAL
cmdok			moveq.l	#DOSTRUE,d0				; Ergebnis ok
cmdfastend		rts

* Parke Kopf bei Motor aus, wenn dc_autopark == 00

cmdmotor		move.l	IO_LENGTH(a1),d0	; Hole IO_Length
				move.l	d0,IO_ACTUAL(a1)	; speichere nach IO_ACTUAL		; Der gewünschte Status ist auch der aktuelle
				bne.s	motstart			; nicht Motor STOP -> Ende

* MOTOR STOP

				bclr.b	#DCB_MOTOR,dc_flags+1(a2)	; lösche das Motor-Flag
				bsr.s	motvec

				move.b	dc_autopark(a2),d1		; Testen, ob geparkt werden soll
				beq.s	cmdok					; Nein, also Ende

				move.l	dc_parkpos(a2),d1		; Parkspur holen
				bsr		prepparkoff				; präpariere Kommando
				bra.s	sok						; und Köpfe seeken

* MOTOR START

motstart		bset.b	#DCB_MOTOR,dc_flags+1(a2)	; Motor läuft jetzt
				bsr.s	motvec
				bra.s	cmdok

* MotVec überprüft, ob in dem Vektor etwas anderes als 000000 steht
* und ruft diesen Vektor ev. auf

motvec			tst.l	dc_vector(a2)		; Vektor == 0 ?
				bne.s	callvec				; Nein...
				rts
callvec			move.l	a1,-(a7)			; A1 retten
				move.l	dc_vector(a2),a1	; Vektor holen
				jsr		(a1)
				move.l	(a7)+,a1			; A1 zurück
				rts							; und Ende


* Seek-Kommando durchführen

cmdseek			bsr		prepoff					; präpariere Kommando
				bne.s	cmdfastend				; Fehler: Byte zu gross -> Ende
sok				move.b	#omti_c_seek,d_COM0(a6)	; Seek-Kommando eintragen
				bra		docom					; und Kommando ausführen

* Lesekommando durchführen

cmdread			bset.b	#DCB_MOTOR,dc_flags+1(a2)	; Motor läuft jetzt
				bsr.s	motvec
				bsr.s	preplen					; präpariere Kommando und Blockcount
				bne.s	cmdfastend				; Fehler: Ende
				move.b	#omti_c_read,d_COM0(a6)	; Kommando eintragen
				bra		docom					; und ausfüheren

* Schreibkommando durchführen
* Formatieren = Track schreiben

cmdwrite
cmdformat		bset.b	#DCB_MOTOR,dc_flags+1(a2)	; Motor läuft jetzt
				bsr.s	motvec					; Vektor?
				bsr.s	preplen					; präpariere Kommando und Blockcount
				bne.s	cmdfastend				; Fehler: Ende
				move.b	#omti_c_write,d_COM0(a6); Kommando eintragen
				bra		docom					; und ausführen

* PrepLen baut das Kommando für den Omti auf und

* A6: Zeiger auf Device
* A1: Zeiger auf IORequest
* A2: Zeiger auf Device Charakteristics

preplen			bsr.s	prepoff					; Bereite Omti-Kommando vor
				bne.s	erts					; Fehler: Ende
plok			move.l	IO_LENGTH(a1),d1		; Lade Länge nach D1
				sub.l	IO_ACTUAL(a1),d1		; aktueller Transfer gleich?
				beq.s	retmin					; ja, dann Ende
nonull			add.l	IO_OFFSET(a1),d1		; Offset aufaddieren
				bcs.s	cherr					; Overflow: Fehler
				bsr.s	check					; Ist Wert auf Platte?
				bne.s	erts					; Nein: Ende
				sub.l	IO_OFFSET(a1),d1		; Offset wieder weg
				lsr.l	#8,d1					;
				lsr.l	#1,d1					; in Blocks unrechnen
				move.l	d1,d_blocks(a6)			; in Device-Struktur speichern
				cmp.l	#256,d1					; Blockcount 256?
				bcs.s	one						; Nein...
				clr.b	d1						; Ja, dann Lade 0
one				move.b	d1,d_COM4(a6)			; Blockcount nach Com4 laden
cheok			clr.l	d0						; Alles ok
				rts

* Check prüft, ob die gesuchten Bytes auch auf der Platte liegen

* A1: Zeiger auf IORequest
* A2: Zeiger auf Device Charakteristics
* D1: Gesuchte Bytenummer

check			cmp.l	dc_maxoff(a2),d1				; Liegt Bereich auf Platte?
				bcs.s	cheok							; Ja, dann ok geben
cherr			move.b	#IOERR_BADLENGTH,IO_ERROR(a1)	; BADLENGTH in Fehlerfeld laden
retmin			moveq.l	#-1,d0							; Fehler und Ende
erts			rts

* prepoff präpariert eine Omti-Kommando aus einen IORequest
* A1: Pointer auf Request
* A2: Zeiger auf Device Char.
* A6: Zeiger auf Device Struktur

prepoff			move.l	IO_OFFSET(a1),d1	; Offset (in Bytes holen)
				add.l	IO_ACTUAL(a1),d1	; Anzahl der zu holenden Bytes holen
				bsr.s	check				; Testen
				bne.s	erts				; Fehler: Ende

* prepparkoff präpariert eine Omti-Kommando aus dem Wert in D1

prepparkoff		lsr.l	#8,d1				; Wert durch SECSIZE teilen
				lsr.l	#1,d1
				clr.w	d0					; lösche D0
				move.b	dc_step(a2),d_COM5(a6)	; lade Steprate nach COM5
				move.b	dc_heads(a2),d0		; lade Kopfzahl nach D0
				mulu	#NOF_SECS,d0		; multipliziere mit Sektoren (Sektoren pro Zylinder)
				divu	d0,d1				; Teile Block / Block pro Zylinder
				move.b	d1,d_COM3(a6)		; Ergebnis: 0-7 nach COM3

				lsr.w	#2,d1				; Bits 8 und 9 nach COM2
				andi.b	#%11000000,d1
				move.b	d1,d_COM2(a6)

				lsr.w	#1,d1				; Bit 10 nach COM1
				andi.b	#%10000000,d1
				move.b	d1,d_COM1(a6)

				clr.w	d1
				swap	d1
				divu	#NOF_SECS,d1
				or.b	d1,d_COM1(a6)		; Speichere Kopfnummer in COM1
				swap	d1
				or.b	d1,d_COM2(a6)		; Speichere Sektornummer in COM2
				clr.l	d0
				rts

* DoCom sendet ein Kommando aus COM0 ab
* A6 zeigt auf Device-Struktur
* A5 zeigt auf Omti
* A2 zeigt auf Drive Charakteristics
* A0 zeigt auf Kommando

docom			lea		d_COM0(a6),a0

* SendOmti schickt ein Standard-Kommando (6 Bytes zum Omti)

sendomti		moveq.l	#6-2-1,d1			; -2: - Command und 1. Datenbyte; -1 wg. dbf

* SendOmti2 schickt soviele Bytes zum Omti, wie in D1 steht

sendomti2		clr.b	omask(a5)			; Interrupts aus

WaitForSel		clr.b	oselect(a5)			; Selecte Controller
				btst.b	#oBSY,ostatus(a5)	; Warten, bis Controller selected ist
				beq.s	WaitForSel

				move.b	#%11,omask(a5)		; DMA und Interrupts freigeben
				bsr.s	waitomti			; warten, bis Omti Byte braucht
				move.b	(a0)+,odata(a5)		; Kommando zum Omti schicken
				bsr.s	waitomti			; warten
				move.b	dc_lun(a2),d0		; Select-Byte für Omti holen
				andi.b	#LUNMASK,d0			; Ausmaskieren
				or.b	(a0)+,d0			; LUN dazu oren
				move.b	d0,odata(a5)		; und zum Omti
SendOmtiLoop	bsr.s	waitomti			; Auf OMTI warten
				move.b	(a0)+,odata(a5)		; Kommando-Bytes schicken
				dbf		d1,SendOmtiLoop
				clr.l	d0					; alles ok
				rts

* WaitOmti wartet, bis der Controller ein Byte anfordert
* A5: Pointer auf Omti

waitomti		btst.b	#oREQ,ostatus(a5)	; REQ-Bit gesetzt?
				beq.s	waitomti			; nein: warten
				rts

* Interrupt ist das Kernstück des Interruptgetriebenen Devices:
* Hier werden die Interrupts vom Controller verarbeitet

* Dies ist ein Interrupt-Server, d.h.

* Rückkehr mit Z-Bit gesetzt -> Nächsten Server testen
* Rückkehr mit Z-Bit gelöscht -> War unser Interrupt

* Folgendes wird vom Amiga-Interrupt-Treiber gesetzt:

* A1: Zeiger auf Device-Struktur
* A5: Zeiger auf Interrupt

* Scratch-Register sind D0,D1,A0,A6

interrupt		lea		omtibase,a5			; Omtibase nach A5
				lea		statreq(pc),a0		; Lade Adresse von StatReq
				btst.b	#oIREQ,ostatus(a5)	; Ist der Interrupt, weil Kommando fertig?
				bne.s	gotreq				; Ja, wirf nächsten Request an
				lea		datareq(pc),a0		; Lade Adresse von DatReq
				btst.b	#oDREQ,ostatus(a5)	; Will der Omti Daten rüberpacken?
				bne.s	gotreq				; Ja, dann jetzt nach GotReq
				moveq.l	#0,d0				; Sonst setzt Z-Bit (nicht für uns) und Ende
				rts							; Amiga will RTS, nicht RTE!!!

* Verarbeitung der pending Requests

gotreq			move.l	a2,-(a7)			; rette A2 auf Stack
				move.l	a1,a6				; Wirf Pointer auf Device nach A6
				move.l	d_CurIoB(a6),d0		; packe aktuellen Request in D0
				bne.s	ReqStillThere		; Ja....

				lea		idle(pc),a0			; Nein, lade Adresse der Idle-Routine
abortomti		btst.b	#oBSY,ostatus(a5)	; Ist das Busy-Bit gesetzt?
				beq.s	doreq				; Nein, gehe in Idle-Zustand
				btst.b	#oIO,ostatus(a5)	; Will Controller Daten schicken?
				bne.s	dummywrite			; Nein, dann Dummy-Write
				tst.b	odata(a5)			; Dummy-Read
				bra.s	abortomti			; und weiter, bis alle Bytes da
dummywrite		clr.b	odata(a5)			; Dummy-Write
				bra.s	abortomti			; und weiter, bis Controller idle

ReqStillThere	move.l	d0,a1				; Adresse des aktuellen Requests nach A1
				move.l	IO_UNIT(a1),a2		; hole gesuchte Unit nach A2
doreq			jsr		(a0)				; Starte den Request
				move.l	(a7)+,a2			; Hole A2 wieder zurück
				moveq.l	#1,d0				; war für uns, Z-Bit löschen und Ende
				rts							; und Ende

* StatReq wird vom Interrupt aufgerufen, wenn der Controller ein Kommando
* komplettiert hat

* A6: Zeiger auf Device-Struktur
* A5: Zeiger auf Omti
* A2: Zeiger auf Unit
* A1: Zeiger auf aktuellen IORequest

statreq			move.b	odata(a5),d0		; Hole das Kommando-Completed Byte nach D0
											; und lösche IREQ
				move.b	dc_resstat(a2),d1	; Test, ob Device noch im Reset-Status ist
				ble.s	notinres			; Nein.....
				btst.l	#sCS,d0				; ist das Error-Bit gesetzt?
				bne.s	reserr				; Ja...
				addq.b	#1,dc_resstat(a2)	; erhöhe Reset-Status
				cmp.b	#1,d1				; Status == 1? (Dann ist Initdrive schon abgesetzt)
				bne.s	makeright			;
				lea		readfirst(pc),a0	; Ja, dann hole ersten Block von der Platte
				bra		sendomti			; und nach SendOmti

makeright		cmp.b	#2,d1				; ist der Status == 2 (Dann ist der Block schon eingelesen)
				bne.s	endinit				; nein, dann Ende
											; sonst die eingestellten Init-Parameter zum Drive schicken
				lea		d_init0(a6),a0		; Lade Zeiger auf die Initfelder nach A0
				bra		sendinit			; und die initialisierten Datenfelder zum Controller schicken

endinit			st.b	dc_resstat(a2)		; Setze A2 auf -1
				bra.s	startproc			; und processe Request

reserr			sf.b	dc_resstat(a2)		; Setzt A2 auf 0; Device muss neu initialisiert werden
				bra.s	serr				; und gib Read Error zurück

notinres		btst.l	#sCS,d0				; ist Fehler aufgetreten?
				bne.s	serr				; Ja, also Fehler
				tst.l	d_blocks(a6)		; sind noch Blocks zu übertrage?
				beq.s	noerr				; nein, also Ende
				bsr	execute					; führe das nächste Kommando aus
				bra.s	icont				; und Ende

serr			clr.l	d_blocks(a6)		; keine Blocks mehr übertragen
				not.b	d_error(a6)			; invertiere Errorflag
				beq.s	noerr				; nur wenns der erste Fehler war, sonst noerr
				move.b	#ODERR_NoSecHdr,IO_ERROR(a1)	; Lade Fehlernummer in IO_Error
				lea		sense(pc),a0		; Lade Sense-Kommando nach A0
				bra		sendomti			; und hole die Sense-Bytes vom Omti

noerr			clr.b	d_error(a6)			; Lösche das Error-Flag
				move.l	a6,-(a7)			; rette A6
				move.l	execbase,a6			; hole die ExecBase
				jsr		ReplyMsg(a6)		; beantworte die Message
				move.l	(a7)+,a6			; hole A6 zurück

idle			tst.w	d_qcnt(a6)			; sind noch Requests in der Queue?
				bne.s	job					; ja, dann starte den nächsten
				clr.l	d_CurIoB(a6)		; nein, lösche den aktuellen Request
				move.b	#%00,omask(a5)		; sperre die Interrupts
				rts							; und Ende

job				lea		d_qlist(a6),a0		; Lade den Listenzeiger nach A0
				REMHEAD						; hole oberstes Element
				subq.w	#1,d_qcnt(a6)		; verringere Counter
				move.l	d0,a1				; Lade den Zeiger nach A1
				bclr.b	#IOFB_QUEUED,IO_FLAGS(a1)	; lösche das QUEUED-Flag
				move.l	IO_UNIT(a1),a2		; Lade den Unit-Poiner nach A2
startproc		bsr		process				; führe Request aus
icont			bne.s	noerr				; alles ok, dann replye Message
				rts							; sonst nur Ende

* DatReq wird vom Interrupt aufgerufen, wenn der Controller Daten übertragen will

* A6: Zeiger auf Device-Struktur
* A5: Zeiger auf Omti
* A2: Zeiger auf Unit
* A1: Zeiger auf aktuellen IORequest

datareq			cmp.b	#2,dc_resstat(a2)	; Ist der Resetstatus = 2
											; (ankommender Datenblock ist die Device-Initialisierung)
				bne		notinres2			; Nein, dann normaler Request

* Aufbau des Parameter-Datenblocks auf der Festplatte siehe parablock.i

				move.l	#drvpara_beg-1,d0	; überlese die ersten Bytes
SkipAufbau		bsr		waitomti
				tst.b	odata(a5)			; Dummy-Read
				dbf		d0,SkipAufbau

				lea		dc_hicyl(a2),a0		; A0 zeigt jetzt auf Init-Block

				move.l	#boot_beg-drvpara_beg-1,d0	; Soviele Bytes holen
GetDrvPara		bsr		waitomti
				move.b	odata(a5),(a0)+				; Datenbyte in Struktur speichern
				dbf		d0,GetDrvPara				; und Schleife binden

				move.l	#SECSIZE-boot_beg-1,d0		; und den Rest einlesen
SkipRest		bsr		waitomti
				tst.b	odata(a5)					; Dummy-Read
				dbf		d0,SkipRest

* Jetzt stehen die Drive-Parameter in der Drivechar.

* Aufbau des Initialisierungs-Kommandos

				lea		d_init0(a6),a0				; Pointer auf Command laden
				move.b	#omti_c_idc,(a0)+			; Kommando laden
				moveq.l	#6-1-1,d0					; Rest des Kommandos nullen
NullCommand		move.b	#0,(a0)+
				dbf		d0,NullCommand

				move.l	a1,-(a7)
				lea		dc_hicyl(a2),a1				; Pointer auf Block

				move.b	(a1)+,(a0)+
				move.b	(a1)+,(a0)+					; HiCyl übertragen

				move.b	(a1)+,(a0)+					; Köpfe übertragen
				addq.l	#1,a1						; A1 um 1 weiter

				move.b	(a1)+,(a0)+					; Reduced Write Current
				move.b	(a1)+,(a0)+

				move.b	(a1)+,(a0)+					; Write Precompensation
				move.b	(a1)+,(a0)+

				move.b	#0,(a0)						; Letztes Byte löschen

				move.l	(a7)+,a1

* Jetzt steht der fertige Init-Drive-Request in d_COM0 - d_COM5 / d_init0 - d_init7

				lea		dc_maxoff(a2),a0	; Zeiger für Bytes
				move.w	dc_wrcyl(a2),d0		; Höchste lese-/schreibbare Spur holen
				addq.w	#1,d0				; Anzahl = Höchste Spur +1
				bsr.s	makeoff				; Anzahl der Bytes auf dem Drive berechnen
				move.w	dc_parkcyl(a2),d0	; Parkspur holen
				bsr.s	makeoff				; Byte berechnen, dass Parkspur darstellt
				bra		dataend

* Aus Tracks, Köpfen und Sektoren eine Bytezahl berechnen

* A2: Zeiger auf Unit-Struktur
* A0: Zeiger auf Maxoff
* D0: Spurnummer

makeoff			clr.l	d1				; Lösche D0
				move.b	dc_heads(a2),d1	; Hole Kopfzahl nach D1
				mulu	#NOF_SECS,d1	; mal Sektoren
				mulu	d1,d0			; Mal Spuren
				lsl.l	#8,d0
				lsl.l	#1,d0			; mal 512 (Bytes im Sektor)
				move.l	d0,(a0)+		; in dc_maxoff abspeichern
				rts

* Datentransfer (Device nicht im Reset-Status)

notinres2		tst.b	d_error(a6)		; Ist das Errorbit gesetzt?
				beq.s	nosense			; Nein... Also Data-Transfer

* Ankommende Datenbytes sind die Sense-Bytes

				bsr		waitomti		; Warte, bis Omti Bytes hat
				move.b	odata(a5),d1			; Hole Sense Byte 1
				tst.b	odata(a5)
				tst.b	odata(a5)
				tst.b	odata(a5)				; wirf die nächsten 3 Bytes weg
				lsl.b	#1,d1					; *2 ( AV Bit entfernen)
				moveq.l	#ODERR_DiskChanged,d0	; Fehler: Disk gewechselt :)
				cmp.b	#omti_err_dnosel*2,d1	; NoSelect-Fehler?
				bne.s	OtherErrs				; Nein
				cmp.w	#OD_CHANGESTATE,IO_COMMAND(a1)	; War das Kommando ein OD_CHANGESTAT
				bne.s	senseerr				; Nein, dann speichere Fehler und Ende
				clr.l	d0						; Lösche D0
				not.l	IO_ACTUAL(a1)			; invertiere IO_ACTUAL
				bra.s	senseerr

OtherErrs		moveq.l	#ODERR_WriteProt,d0		; Write-Protect-Fehler?
				cmp.b	#omti_err_wrtpro*2,d1
				beq.s	senseerr				; Ja, Fehler speichern und Ende
				moveq.l	#ODERR_SeekError,d0		; Seek-Fehler?
				cmp.b	#omti_err_noseek*2,d1
				beq.s	senseerr
				cmp.b	#omti_err_seeker*2,d1
				beq.s	senseerr				; Ja, dann speichere Fehler ab
				moveq.l	#ODERR_NoSecHdr,d0		; Sonst ist ein ein NoSecHdr

senseerr		move.b	d0,IO_ERROR(a1)			; Lade D0 nach IO_ERROR
				bra		dataend

* Hier findet der eigentliche Data-Transfer statt

nosense			move.l	d_data(a6),a0			; Pointer auf Daten nach A0
				btst.b	#oIO,ostatus(a5)		; Schreiben oder Lesen?
				beq.s	wloop					; 0: Schreiben

rloop			bsr		waitomti				; Warten auf den Controller
				btst.b	#oCD,ostatus(a5)		; Command oder Status?
				bne		statreq					: Status: gehe nach StatReq

				move.l	#SECSIZE-1,d0			; -1 wg dbf
drl				move.b	odata(a5),(a0)+			; 1 Transfer wg. 68010 Tight-Loop
				dbf		d0,drl
				add.l	#SECSIZE,IO_ACTUAL(a1)	; Wieder ein Sektor übertragen
				move.l	a0,d_data(a6)			; A0 wieder abspeichern
				subq.l	#1,d_blocks(a6)			; ein Block übertragen
				bne		rloop					; Mehr? Dann weiter
				bra.s	dataend					; und Ende

wloop			bsr		waitomti				; Auf Omti warten
				btst.b	#oCD,ostatus(a5)		; Statusbytes?
				bne		statreq					; Ja? Dann statreq
				move.l	#SECSIZE-1,d0			; s.o.
dwl				move.b	(a0)+,odata(a5)			; 1 Transfer wg. 68010 Tight-Loop
				dbf		d0,dwl
				add.l	#SECSIZE,IO_ACTUAL(a1)	; ein Block übertragen
				move.l	a0,d_data(a6)			; Pointer abspeichern
				subq.l	#1,d_blocks(a6)			; Ein Block weniger
				bne		wloop					; Mehr? Dann mehr...

dataend			btst.b	#oCD,ostatus(a5)		; Hat Controller Data-Bytes?
				bne		statreq					; Nein, dann StatReq aufrufen
				move.b	#%10,omask(a5)			; Nur Data Transfer completion Interrupt
				rts
