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

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

* In dieser Datei findet sich der gesamte Code fuer die
* EXEC <-> Device Kommunikation


* Hier gehts los; Init wird unmittelbar nach dem Laden des Devices
* aufgerufen und initialisiert alle Strukturen

* Register: A0: Segmentdresse
*           D0: Device-Pointer
*           A6: ExecBase
*

	IFND	DEBUG

init			movem.l	a2/a5,-(a7)			; Rette A2 und A5 auf Stack
				move.l	a0,a5				; Rette Segmentadresse nach A5
				lea		omtibase,a0			; Lade Omtiadresse nach A0
				clr.b	oreset(a0)			; Resette Controller
				moveq.l	#-1,d1				; Lade Timeout-Counter
iscontrthere	btst.b	#oBSY,ostatus(a0)	; Ist Controller noch idle?
				dbeq	d1,iscontrthere		; Warte in Schleife
				bne.s	nocon				; Ja, also kein Controller da

				moveq.l	#-1,d1				; Lade Timeout-Counter
waitforselect	clr.b	oselect(a0)			; Starte Select-Phase
				btst.b	#oBSY,ostatus(a0)	; Geht Controller auf Select?
				dbne	d1,waitforselect	; Nein, warte
				bne.s	isthere				; Ja, Controller ist da.

* Kein Controller an der angegebenen Adresse -> Fehler

nocon			clr.l	d0					; D0 loeschen, also Fehler
				bra		retini				; und Schluss

* Controller ist da, also wird jetzt das Device installiert

isthere			clr.b	oreset(a0)					; Controller nochmal resetten
ok				exg.l	a5,d0						; Speicheradresse nach D0, Device-Adresse nach A5
				move.l	d0,d_segment(a5)			; Segmentadresse abspeichern

*
* Adresse etc, die reloziert werden (muessten)
*

				lea		name(pc),a1
				move.l	a1,LN_NAME(a5)
				move.l	a1,LN_NAME+d_int(a5)
				lea		idstr(pc),a1
				move.l	a1,LIB_IDSTRING(a5)

* Jetzt den Rest des Devices initialisieren

				lea		d_qlist(a5),a1		; Liste auf d_qlist aufbauen
				NEWLIST	a1					; mit einem Macro

* Interrupt initialisieren

				lea		d_int(a5),a1		; Interrupt-Struktur initialisieren

* Alle Statischen Daten werden schon von InitResident initialisiert

				move.l	a5,IS_DATA(a1)		; Datenpointer ist Pointer auf Device-Struktur
				lea		interrupt(pc),a0	; Interrupt-Server eintragen
				move.l	a0,IS_CODE(a1)
				moveq.l	#INTNUM,d0			; Treiber fuer Interrupt INTNUM
				jsr		AddIntServer(a6)	; Und als Server eintragen

				move.l	a5,d0				; Returncode: Pointer auf Device-Struktur

retini			movem.l	(a7)+,a2/a5			; Register von Stack holen
				rts							; Und Ende

	ENDC
	IFD		DEBUG

init			movem.l	a2/a5,-(a7)			; Rette A2 und A5 auf Stack
				move.l	a0,a5				; Rette Segmentadresse nach A5
				lea		omtibase,a0			; Lade Omtiadresse nach A0
ok				exg.l	a5,d0				; Speicheradresse nach D0, Device-Adresse nach A5
				move.l	d0,d_segment(a5)	; Segmentadresse abspeichern

*
* Adresse etc, die reloziert werden (muessten)
*

				lea		name(pc),a1
				move.l	a1,LN_NAME(a5)
				move.l	a1,LN_NAME+d_int(a5)
				lea		idstr(pc),a1
				move.l	a1,LIB_IDSTRING(a5)

				lea		d_qlist(a5),a1		; Liste auf d_qlist aufbauen
				NEWLIST	a1					; mit einem Macro

* Interrupt initialisieren

				lea		d_int(a5),a1		; Interrupt-Struktur initialisieren

* Alle Statischen Daten werden schon von InitResident initialisiert

				move.l	a5,IS_DATA(a1)		; Datenpointer ist Pointer auf Device-Struktur
				lea		interrupt(pc),a0	; Interrupt-Server eintragen
				move.l	a0,IS_CODE(a1)
				moveq.l	#INTNUM,d0			; Treiber fuer Interrupt INTNUM
				move.l	a5,d0				; Returncode: Pointer auf Device-Struktur
retini			movem.l	(a7)+,a2/a5			; Register von Stack holen
				rts							; Und Ende

	ENDC


* DeviceOpen oeffnet das Device

* D0: Devicenummer
* A1: Pointer auf IORequest
* A6: Pointer auf Device-Struktur

DeviceOpen		moveq.l	#IOERR_OPENFAIL,d1	; Code fuer FAIL vorladen
				cmp.l	#2,d0				; uebergebene Device-Nummer > 1 ?
				bcc.s	OpenErr				; ja, Fehler
				lea		d_char0(a6),a0		; Lade Adresse fuer Drive 0 Characteristics
				tst.l	d0					; Device-Nummer 0 ?
				beq.s	StoreUnit			; ja....
				lea		d_char1(a6),a0		; sonst Device 1
StoreUnit		move.l	a0,IO_UNIT(a1)		; Store Pointer auf Unit (Drive_Char)
				addq.w	#1,LIB_OPENCNT(a6)	; Erhoehe OpenCount der Library
				clr.l	d1					; Kein Fehler
OpenErr			move.b	d1,IO_ERROR(a1)		; Im Error-Feld des Requestes speichern
				rts							; und Ende

* Close schliesst das Device

* A1: Pointer auf IORequest
* A6: Pointer auf Device-Struktur

DeviceClose		clr.b	IO_ERROR(a1)		; Kein Fehler moeglich
				tst.w	LIB_OPENCNT(a6)		; ist OpenCount schon Null?
				beq.s	CloseRet			; ja, Ende
				subq.w	#1,LIB_OPENCNT(a6)	; nein, verringere OpenCount um 1
				bne.s	CloseRet			; Nicht 0: Dann Ende
				clr.l	d_CurIoB(a6)		; Loesche den Aktuellen IORequest
				btst.b	#LIBB_DELEXP,LIB_FLAGS(a6)	; Ist DelExpunge schon in Arbeit?
				bne.s	DeviceExpunge		; Nein, dann starte es
CloseRet		clr.l	d0					; Ende mit D0 = 0
				rts

* DeviceExpunge schliesst alle Resourcen und entfernt das Device aus dem Speicher

* A6: Pointer auf Device-Struktur

DeviceExpunge	tst.w	LIB_OPENCNT(a6)				; Ist der Counter schon auf Null?
				beq.s	DoExpunge					; Ja, dann schmeiss sie raus...
				bset.b	#LIBB_DELEXP,LIB_FLAGS(a6)	; nein, setze nur das Bit
				clr.l	d0							; Ende mit D0=0
				rts

DoExpunge		move.l	a6,-(a7)					; Rette Adresse der Dev-Struktur

	IFND	DEBUG

				lea		d_int(a6),a1				; Lade Adresse der Interrupt-Struktur
				moveq.l	#INTNUM,d0					; Diesen Interrupt wollen wir abstellen
				move.l	execbase,a6					; ExecBase holen
				jsr		RemIntServer(a6)			; Interrupt-Server entfernen

	ENDC

				move.l	(a7)+,a6					; Dev-Struktur zurueckholen
				move.l	a6,a1						; nach A1 bringen
				bsr.s	RemoveElement				; Device-Struktur aus Device-Liste ausklinken

				move.l	d2,-(a7)
				move.l	d_segment(a6),d2
				clr.l	d0							; D0 loeschen
				move.w	LIB_NEGSIZE(a6),d0
				sub.w	d0,a1
				add.w	LIB_POSSIZE(a6),d0			; Groesse des zu befreienden Speichers berechnen
				move.l	execbase,a6					; ExecBase holen
				jsr		FreeMem(a6)					; Speicher freigeben
				move.l	d2,d0						; Segment-Pointer in D0 zurückgeben
				move.l	(a7)+,d2
				rts

* RemoveElement

* A1: Pointer auf Listenelement

RemoveElement	move.l	a1,-(a7)	; A1 wird von REMOVE benoetigt
				REMOVE				; Macro
				move.l	(a7)+,a1	; A1 zurueckholen
				rts

* DeviceNull ist reserviert von Commo

DeviceNull			clr.l	d0		; Fehler & Ende
					rts

* BeginIO leitet eine IO-Operation ein

* A1: Pointer auf IORequest
* A6: Pointer auf Device-Struktur

DeviceBeginIO	move.l	a2,-(a7)				; Rette A2
				move.l	IO_UNIT(a1),a2			; Hole Pointer auf Driv Char.
				move.b	#NT_MESSAGE,LN_TYPE(a1)	; Setzt Typ der bekommenen Message auf NT_MESSAGE
				clr.b	IO_ERROR(a1)			; Loesche IO_Error-Feld
				clr.l	IO_ACTUAL(a1)			; NofBytes loeschen
				clr.l	d0						; Loesche D0
				move.w	IO_COMMAND(a1),d0		; Lade Kommando-Feld nach D0
				bclr.l	#15,d0					; Loesche das EXTCOMMAND-Bit
				cmp.w	#NOF_ODCMD,d0			; Gueltiges Kommando?
				bcs.s	CmdValid				; Ja, dann weiter
				move.b	#IOERR_NOCMD,IO_ERROR(a1)	; Nein; Fehler und Ende
				bra.s	return

CmdValid		move.l	#%11110011000111100011,d1
*											 ^- CMD_INVALID		x
*											^-- CMD_RESET		x
*										   ^--- CMD_READ
*										  ^---- CMD_WRITE
*										 ^----- CMD_UPDATE
*										^------ CMD_CLEAR		x
*									   ^------- CMD_STOP		x
*									  ^-------- CMD_START		x
*									 ^--------- CMD_FLUSH		x
*									^---------- OD_MOTOR
*								   ^----------- OD_SEEK
*								  ^------------ OD_FORMAT
*								 ^------------- OD_REMOVE		x
*								^-------------- OD_CHANGENUM	x
*							   ^--------------- OD_CHANGESTATE
*							  ^---------------- OD_PROTSTATUS
*							 ^----------------- OD_READRAW		x
*							^------------------ OD_WRITERAW		x
*						   ^------------------- OD_GETDRIVETYPE	x
*						  ^-------------------- OD_GETNUMTRACKS	x
*
* x bedeutet: Kann als FastIO durchgefuehrt werden


				btst.l	d0,d1					; Darf Kommando als FastIO ?
				beq.s	SlowIO					; 0: Nein
				tst.b	dc_resstat(a2)			; Ist Device initialisiert?
				bmi.s	FastIO					; Ja...
				move.l	#%10000000000000000000,d1	; GETNUMTRACKS-Kommando?
				btst.l	d0,d1
				bne.s	SlowIO					; Ja, beim ersten Mal slow..

FastIO			bsr		execute					; Fuehre Kommando aus
return			btst.b	#IOB_QUICK,IO_FLAGS(a1)	; Ist QUICKIO gesetzt?
				bne.s	BIoReturn				; Ja, dann kein ReplyMsg
				move.l	a6,-(a7)				; Save Device-Pointer
				move.l	execbase,a6				; ExecBase nach A6
				jsr	ReplyMsg(a6)				; Message beantworten
				move.l	(a7)+,a6				; A6 restoren
BIoReturn		move.l	(a7)+,a2				; A2 restoren
				rts

* SlowIO performt alle Aktionen, die auf den Controller warten muessen

SlowIO			bsr		disable					; Sperre die Interrupts
				bclr.b	#IOB_QUICK,IO_FLAGS(a1)	; Loesche das QUICK-Bit
				tst.w	 d_qcnt(a6)				; Sind noch Requests in der Schlange
				bne.s	QueueRequest			; Ja, queue den Request
				tst.l	d_CurIoB(a6)			; sind noch ein Current Request da?
				bne.s	QueueRequest			; Ja, queue den Request
				btst.b	#oBSY,omtistatus		; Wie gehts dem Omti?
				bne.s	QueueRequest			; ist selected -> queue den Request
				move.l	a5,-(a7)				; rette A5
				lea		omtibase,a5				; Omtibase nach A5 laden
				bsr		process					; Aktuellen Request direkt processen
				move.l	(a7)+,a5				; A5 restoren
				beq.s	vslow					; Befehl gab 0 zurueck -> nach vslow
				bsr.s	enable					; Interrupts wieder freigeben
				clr.l	d_CurIoB(a6)			; Aktuellen Request loeschen
				bra		return					; Ende von BeginIO durchziehen

QueueRequest	bset.b	#IOFB_QUEUED,IO_FLAGS(a1)	; Der Request ist gequeued worden
				lea		d_qlist(a6),a0				; Request in Liste hinten einbinden
				ADDTAIL								; mit Macro
				addq.w	#1,d_qcnt(a6)				; 1 Request mehr in der Schlange
vslow			bsr.s	enable						; Gib Interrupts frei
				bra		BIoReturn					; und zurueck

* DeviceAbortIO bricht den Aktuellen IORequest ab

* A1: abzubrechender Request

DeviceAbortIO	move.l	a2,-(a7)					; rette das A2
				move.l	IO_UNIT(a1),a2				; hole die Drive Char. in A2
				bsr.s	disable						; sperre Interrupts
				cmp.l	d_CurIoB(a6),a1				; ist der aktuelle Request der gesuchte?
				bne.s	RemoveIO					; Nein, also in Liste schaun
				tst.b	dc_resstat(a2)				; Ist Device initialisiert?
				bmi.s	NoInit						; Ja, also kein Init
				clr.b	dc_resstat(a2)				; Loesche dc_resstat (Device neu initialisieren)
NoInit			clr.l	d_CurIoB(a6)				; Loesche aktuellen Request
				bra.s	NoRemove

RemoveIO		btst.b	#IOFB_QUEUED,IO_FLAGS(a1)	; ist ein Request gequeued?
				beq.s	vslow						; nein, dann Interrupts frei und return
				bsr		RemoveElement				; Element aus Liste ausketten
				subq.w	#1,d_qcnt(a6)				; Ein Element weniger in Liste

NoRemove 		move.b	#IOERR_ABORTED,IO_ERROR(a1)	; Request wurde aborted
				bsr.s	enable						; Interrupts freigeben
				bra		return						; und Ende

* 'Inline'-Code fuer Enable() und Disable()

enable			move.l	a6,-(a7)					; Rette A6
				move.l	execbase,a6					; ExecBase holen
				subq.b	#1,IDNestCnt(a6)			; IDNestCnt verringern
				bge.s	enabende					; Wenn noch >0: Ende
				move.w	#INTF_INTEN+INTF_SETCLR,custom+intena	; Setze das Master-Interrupt Bit
enabende		move.l	(a7)+,a6					; A6 zurueck
				rts

disable			move.l	a6,-(a7)					; Rette A6
				move.w	#INTF_INTEN,custom+intena	; Loesche das Master-Interrupt Bit
				move.l	execbase,a6					; Lade die ExecBase
				addq.b	#1,IDNestCnt(a6)			; Erhoehe IDNestCnt um eins
				move.l	(a7)+,a6					; A6 zurueck
				rts

* Process fuehrt einen Request durch

* A6: Pointer auf Device-Struktur
* A2: Pointer auf Drive-Charakteristics

process			move.l	a1,d_CurIoB(a6)			; Lade A1 mit aktuellem Request
				move.l	IO_DATA(a1),d_data(a6)	; Datenpointer umtragen
				clr.l	d_blocks(a6)			; Blockscounter loeschen
				tst.b	dc_resstat(a2)			; ist Device initialisiert?
				bmi.s	execute					; Ja, dann fuehre Request durch
				move.b	#$01,dc_resstat(a2)		; Device wird jetzt initialisiert

				lea		InitDrive(pc),a0		; Init-Drive Kommando fuer Drive 0
sendinit		moveq.l	#14-2-1,d1				; 14 Bytes fuer Request nach D1; -2/-1 siehe sendomti2
				bra		sendomti2				; Und Befehl zum Omti schicken

execute			move.w	IO_COMMAND(a1),d0		; Hole das Kommando aus Request
				lsl.w	#1,d0					; schiebe nach links fuer Tabelle
				lea		jump(pc),a0				; Lade die Adresse der Jump-Tabelle
				add.w	0(a0,d0.w),a0			; Addiere und lade die Adresse
				jmp		(a0)					; Starte Kommando

*<------------------- Ende des Device-Codes ------------------->*
