;
;	File:		SlotMgr.a
;
;	Contains:	Macintosh Interface to the Slot Manager
;
;	Written by:	George Norman, June 1, 1986; (rewritten) David Wong, March 12, 1989
;
;	Copyright:	© 1986-1993 by Apple Computer, Inc., all rights reserved.
;
;	Change History (most recent first):
;
;	  <SM14>	11-07-93	jmp		Fixed two more bugs in RemoveCard:  1) Made sure that only the
;									fAll bit is set when searching for sResources to delete, and 2)
;									made sure that all resouces belonging to a particular slot would
;									in fact be found (and now not skipped).
;	  <SM13>	  2/8/93	rab		Sync up with Horror. Comments follow: <H14> 8/24/92 SWC Fixed a
;									bug in RemoveCard: the wrong register was being used.
;	  <SM12>	 1/20/93	PN		Re-roll in PatchGetCString so that d0 get zeroed out correctly.
;	  <SM11>	11/18/92	kc		Import GetDriverPatch to fix problem introduced by last checkin.
;	  <SM10>	11/17/92	kc		Add "IF NOT LC930 THEN" around GetDriverPatch to facilitate dead
;									code stripping for the LC930 build.
;	   <SM9>	10/22/92	CSS		Change some branch short instructions to branches.
;	   <SM8>	09-24-92	jmp		Fixed a bug in the GetDevIndex utility where a register was
;									being trashed.
;	   <SM7>	 6/30/92	kc		Roll in Horror, comments follow:
;ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊ					¥From SlotManagerPatch.a:
;	   <H12>	 6/10/92	djw		Modify AddCard to execute SecondaryInit's.
;	   <SM6>	 6/20/92	PN		Roll in patchIIciROM.a. Fix a bug in pInitEntry to restore the
;									spID before calling _sFindDevBasse
;	   <SM5>	 5/16/92	kc		Roll in Horror. Comments follow:
;									¥From SlotManager.a:
;		<H3>	 3/18/92	djw		Add new calls to support docking code - AddCard, RemoveCard,
;									CheckSlot, and GetSRTEntry.
;		<H2>	  3/5/92	djw		Replaced InitSlotMgr, InitsRsrcTable, InitPRAMRecs, and
;									Primary_Init entries in the table with new routines.
;		<T3>	 11/5/90	CCH		Fixed out of range error due to SlotMgrPatch.a moving to second
;									half-meg.
;		<T2>	 9/17/90	CCH		Patched Slot interrupt jump table in RAM for Eclipse.
;									¥From smspecial.a:
;		<H3>	  1/7/91	jmp		Because of a misplaced label, the sRsrcFlag was only being read
;									for Slot $0 devices.
;		<H2>	12/11/90	HJR		Fix a bug in special casing for slot zero: TestForRBV is not
;									appropriate since not all machines that need special casing for
;									slot zero have an RBV.
;									¥From smadvanced.a
;		<T5>	 4/23/91	jmp		Cleaned up StatLoadPatch comments.
;		<T4>	 4/22/91	CCH		Added a patch to StatLoadDrvr to flush the cache after loading
;									in slot resources.  Also slightly modified the way GetSlotDrvr
;									was being patched.
;		<T3>	 3/29/91	CCH		Added in a patch to SlotExec to flush the caches before
;									executing code read from a NuBus card.
;		<T2>	11/19/90	jmp		Added in patch for GetDriver.
;									¥From SlotMgrPatches.a
;		<H9>	 4/24/92	HJR		Added NiagraVIA2SlotInt and necessary install code for VSC video
;									interrupts.
;		<H8>	04/16/92	jmp		Added a routine so that Slot $0 framebuffers can actually use
;									the Minor/Major base address offset sResources like other slots
;									can.
;		<H7>	 3/18/92	djw		Complete CheckSlot call for DBLite docking code. Add AddCard and
;									RemoveCard calls
;		<H5>	02/05/92	jmp		Unfortunately, I put some tables in the middle of this file
;									that, in Cyclone, will get bigger.  So, I moved them from the
;									middle of the file into the pad space.  Also, I generalized the
;									routine that decides whether a DeclROMÕs PrimaryInit/Driver
;									should be patched by not stopping once I found the version I was
;									looking for (e.g., in case we need to patch out versions 1, 2,
;									and 3 of the 8¥24 card).
;		<H3>	10/29/91	jmp		Added the VerifySlotPatch routine for supporting super sRsrc
;									directories in Slot $0.
;		<H2>	10/22/91	SAM/jmp	Discontinued patching out the JMFB PrimaryInit & Driver on the
;									Bungee (1.1) version of the 4¥8/8¥24 Card ROM.  From Zydeco-TERROR ROM.
;									¥From SlotInfo.a
;		<H5>	 3/18/92	djw		Add stub entry points for new docking support routines -
;									StubAddCard and StubRemoveCard
;		<H4>	  3/6/92	SWC		We thank Dave for <H3> (getting it working in under 25 triesÉ),
;									and move the Docking Manager initialization to StartSDeclMgr
;									just before running the primary inits so that we can decided
;									whether or not to install the built-in LCD driver when DBLite
;									(etc.) is inside a docking station and thus the LCD screen is
;									not useable.
;		<H3>	  3/5/92	djw		Rewrote slot manager initialization code (major routines from
;									beginning of file to SecondaryInit) to facilitate new code for
;									DBLite docking station.  The major change was to modify the
;									routines to execute their functions for a single slot instead of
;									for every slot.
;		<H2>	10/29/91	jmp		Added a patch to the VerifySlot routine for supporting super
;									sRsrc directories in Slot $0.
;	   <SM4>	  5/5/92	JSM		Roll-in changes from Reality:
;									<19>	  5/4/92	JSM		The one check for isUniversal in this file should just be
;																forROM. (Dean really didnÕt have to do all that work in <17>).
;	   <SM3>	 3/18/92	RB		Added a WITH statement to use video information record.
;	   <SM2>	 2/20/92	RB		Need to switch to 24 bit mode before calling DisposPtr for
;									srtRecords in the routine DeleteSRTRec. Made a second pass on
;									Terror changes. The Terror code is diferent files and so that
;									makes it hard to compare sources. Changed InitSlotPRAM,
;									PtrToSlot, pInitEntry, GetBoard, and added MapUnit.
;		<18>	  1/7/92	RB		Rolled in Terror changes. Mainly cache flushing.
;		<17>	 8/30/91	DTY		Define isUniversal here since itÕs no longer defined in
;									BBSStartup.  isUniversal is defined to be 0 for System builds
;									because thatÕs the way it is.  It should already be defined for
;									the ROM build (in Build, in a {DefOverride}), but define it
;									again just to be on the safe side.  (ItÕs inside a check to see
;									whether or not itÕs defined, so itÕs ok to define it again.)
;		<16>	 6/12/91	LN		removed #include 'HardwareEqu.a'
;		<15>	  3/8/91	djw		<dnf>(BRC #83516) Fix GetCString where in 32 bit mode reg d0 was
;									set to 1 and caused an off-by-one count to be returned.
;		<14>	12/10/90	djw		Modify SlotExec to fix a problem with the 4¥8/8¥24/GC card's
;									secondaryInit code. Add FixTrident routine to do the fix.
;		<13>	 9/19/90	BG		Removed EclipseNOPs from <9>, <10>. 040s are behaving more
;									reliably now.
;		<12>	 7/30/90	BG		Removed IF CPU = 040 conditional in preparation for moving from
;									Mac040 to Mac32 build.
;		<11>	 7/20/90	gbm		get rid of slot warnings
;		<10>	 7/19/90	BG		Added EclipseNOPs for flakey 040s.
;		 <9>	  7/5/90	CCH		Fixed aeStackFrame record for 68040.  Also added EclipseNOPs for
;									flakey 040s.
;		 <8>	 6/29/90	djw		Fix bug in FindDevBase and in SlotPRAM where hasRBV must be true
;									or no slot zero. Deleted MapUnit routine (overkill). Modified
;									FindDevBase to return a base addr for slot zero even if there is
;									no internal video (addr is not mapped). Corrected conditionals
;									for ROM and A/UX. Modified bus exception handler to be VM
;									friendly (not pop stack frame and jump). Moved SecondaryInit
;									code back to SlotMgrInit.a. Added universal support to SlotPRAM
;									routines. Modified pGetBoard to accept spId as a parameter.
;		 <7>	  4/2/90	djw		Simplify the install and remove routines for bus exception
;									handler
;		 <6>	 2/20/90	BG		Changed the definition of aeXFrameSize in the 040 bus exception
;									handler to reflect the real size of the exception frame.
;		 <5>	 1/21/90	BG		Removed 040 AERROR from pBusException.  Added routine
;									p040BusException to handle 040 bus error exceptions.
;		 <4>	 1/17/90	djw		Modifications to build slot mgr with 32bit QD INIT using BBS
;									sources. Added Secondary_Init and GetBoard routines from
;									SlotMgrInit.a
;		 <3>	 1/11/90	CCH		Added include of ÒHardwarePrivateEqu.aÓ.
;		 <2>	  1/3/90	BG		Changing the quoting on the 040-related AERROR messages to use
;									the correct quotes (single, not double). Will fix problem
;									related to the AERROR shortly.
;	   <1.8>	 9/18/89	djw		Reorganized sources to combine into two source files, to allow
;									system 7.0 patch to share the same sources.
;	   <1.7>	 7/14/89	djw		NEEDED FOR AURORA: removed sNewPtr. Set findbigdevbase to
;									finddevbase because findbigdevbase. Update internal version
;									number. macro is in mpw equate files (so just can't get rid of
;									the call).
;	   <1.6>	 6/30/89	djw		removed pSlotStatus. Modified InitJmpTbl to use relative
;									addressed table. Changed sdmjmptbl to use relative addresses for
;									the vectors
;	   <1.5>	 6/12/89	djw		Removed findbigdevbase
;	   <1.4>	 3/28/89	djw		Cleaned up IMPORT statements. Rewrote InitJmpTbl,SlotManager.
;									Added dummy routine for unimplemented selectors. Deleted old
;									modification history. Added patch support, added old bsr routine
;									to jmp tbl now using slotjsr macro. Rewrote slotmgr dispatcher
;	   <1.3>	 3/12/89	djw		Added InitJmpTbl from smadvanced, to make the 1.0 patch easier
;									to manage. Moved things around a little. Added GetsRsrcPtr.
;	   <1.2>	 2/20/89	djw		Added sVersion, SetsRsrcState, InsertSRTRec, delete code to
;									switch slotmanager 24 bit mode - make it 32 bit clean. Added
;									GetsRsrc, GetTypesRsrc and FindSRTRec.
;	   <1.1>	11/10/88	CCH		Fixed Header.
;	   <1.0>	 11/9/88	CCH		Adding to EASE.
;	   <1.5>	 11/7/88	djw		Added InitSlotPRAM
;	  <¥1.4>	10/24/88	djw		NEW SLOTMANAGER VERSION. Deleted all register names. Rewrote to
;									save mmu mode on stack - no longer need to save any registers in
;									called slot manager routines. All new IMPORT routine names.
;	  <¥1.3>	 9/23/88	CCH		Got rid of inc.sum.d and empty nFiles
;	   <1.2>	 2/15/88	BBM		added inc.sum.d as a second load file
;	   <1.1>	 2/15/88	BBM		modified for mpw 3.0 equates
;	   <1.0>	 2/10/88	BBM		Adding file for the first time into EASEÉ
;

			Machine	MC68020
			String	Asis
			Print	Off
			LOAD	'StandardEqu.d'
			Include	'HardwarePrivateEqu.a'
			Include	'RomEqu.a'
			Include	'UniversalEqu.a'
			Include	'SlotMgrEqu.a'
			Include	'ComVideoEqu.a'						; <18> rb
			Print	On

  			If (&TYPE('forAUX') = 'UNDEFINED') Then		; equ indicating whether we are		<8>
forAUX	   	Equ 		0								; ... building for an A/UX patch
  			Endif

;_________________________________________________________________________________________	<1.4>
;	SlotManager  -  slot manager secondary dispatch routine
;
;	Vector through the secondary dispatch table for the slot manager to the requested
;	routine.
;
;	Input	: reg D0 = routine selector (word)
;

SlotMgr		Proc
			Export	SlotManager

;  Jump to the selected slot manager routine.

			DC.B	'slot version 2.3'				; slot manager internal version number	<4>
SlotManager
			CMP.W	#LastSDMSelt,D0					; check selector range
			BHI.S	SlotEmpty						; selector(unsigned) > max - error		<djw>
			move.l	([SDMJmpTblPtr],d0.w*4),a1		; get vector from table					<1.7>
			jmp		(a1)							;										<1.7>


;_________________________________________________________________________________________	<1.4>
;	SlotEmpty  -  unimplemented slot manager trap
;
			Export	SlotEmpty

SlotEmpty
			move.w	#smSelOOBErr,d0			; error - selector out of bounds
			rts


;=========================================================================================
;	smPrinciple
;=========================================================================================

**********************************************************************
*
* FUNCTION ReadSlotByte : Read Byte.
*	Return an 8-bit value from the list pointed to by sPointer and 
*	identified by Id. This value is placed in the LSB of the Result
*	field.
*
* Parameter block:
*	 ->	spsPointer	- Points to the list the Byte is in.
*	 ->	spId		- The Id of the Byte.
*	<-	spResult	- The desired byte.
*
**********************************************************************
ReadSlotByte	FUNC	EXPORT

;VAR
spBlkPtr$a0		EQU		A0			;Pointer to the SDM parameter block
Status$d0		EQU		D0			;Status of ReadSlotByte

;----------------------------------------------------------------------
; ReadSlotByte
;----------------------------------------------------------------------
;BEGIN 
				WITH	spBlock
				
;  Initialize
				MOVE.L	spsPointer(spBlkPtr$a0),-(SP)			;Save sPointer.
				CLR.B	spResult+3(spBlkPtr$a0)					;ReadSlotByte <- 0


;  Read the Offset/Data field and assign the Data to ReadSlotByte.
				_sOffsetData									;Read the Offset/Data field.
				BNE.S	End										;IF Error THEN
																;  GOTO End

				MOVE.B	spOffsetData+3(spBlkPtr$a0),spResult+3(spBlkPtr$a0)		;ReadSlotByte <- Data[4]
				MOVEQ	#0,Status$d0							;Status <- NoError


;  Exit
End				MOVE.L	(SP)+,spsPointer(spBlkPtr$a0)			;Restore sPointer.
				RTS												;Return.
				
				
				ENDWITH
;END ReadSlotByte
				ENDF

				
				EJECT
**********************************************************************
*
* FUNCTION ReadSlotWord : Read Word.
*	Return a 16-bit value from the list pointed to by sPointer and 
*	identified by Id. This value is placed in the two LSB fields of 
*	the Result field.
*
* Parameter block:
*	->	spsPointer	- List pointer. Points to the list the Word is in.
*	->	spId		- Id. The Id of the Word in the list.
*	<-	spResult	- The desired word.
*
**********************************************************************	
ReadSlotWord	FUNC	EXPORT

;VAR
spBlkPtr$a0		EQU		A0			;Pointer to the SDM block
Status$d0		EQU		D0			;Status of ReadSlotWord

;----------------------------------------------------------------------
; ReadSlotWord
;----------------------------------------------------------------------
;BEGIN 
				WITH	spBlock
				
;  Initialize 
				MOVE.L	spsPointer(spBlkPtr$a0),-(SP)		;Save sPointer
				CLR.W	spResult+2(spBlkPtr$a0)				;ReadSlotWord <- 0


;  Read the Offset/Data field and Assign the Data to ReadSlotWord.
				_sOffsetData								;Read the Offset/Data field.
				BNE.S	End									;IF Error THEN
															;  GOTO End

				MOVE.W	spOffsetData+2(spBlkPtr$a0),spResult+2(spBlkPtr$a0)	;ReadSlotWord <- Data[3..4]
				MOVEQ	#0,Status$d0						;Status <- NoError


;  Exit
End				MOVE.L	(SP)+,spsPointer(spBlkPtr$a0)		;Restore sPointer.
				RTS											;Return.

				
				ENDWITH
;END ReadSlotWord
				ENDF


				EJECT
**********************************************************************
*
* FUNCTION ReadSlotLong : Read Long
*	Return a 32-bit value from the list pointed to by sPointer and 
*	identified by Id. This value is placed in the Result field.
*
* Parameter block:
*	->	spsPointer	- List pointer. Points to the list the Long is in.
*	->	spId		- Id. The Id of the Long in the list.
*	<-	spResult	- The desired long.
*
**********************************************************************
ReadSlotLong	FUNC	EXPORT

;VAR
spBlkPtr$a0		EQU		A0			;Pointer to the SDM parameter block
Status$d0		EQU		D0			;Status of ReadSlotLong

;----------------------------------------------------------------------
; ReadSlotLong
;----------------------------------------------------------------------
;BEGIN 
				WITH	spBlock
				
;  Initialize 
				MOVE.L	spsPointer(spBlkPtr$a0),-(SP)				;Save sPointer.
				CLR.L	spResult(spBlkPtr$a0)						;ReadSlotLong <- 0


;  Use sReadPBSize to read the Long and move PhyBlkSize to Result.
				BCLR.B	#fCkReserved,spFlags(spBlkPtr$a0)			;Do not check reserved.
				_sReadPBSize										;Read the long.
				BNE.S	End											;IF Error THEN
																	;  GOTO End

				MOVE.L	spSize(spBlkPtr$a0),spResult(spBlkPtr$a0)	;ReadSlotLong <- PhyBlkSize
				MOVEQ	#0,Status$d0								;Status <- NoError


;  Exit
End				MOVE.L	(SP)+,spsPointer(spBlkPtr$a0)				;Restore sPointer.
				RTS													;Return

				
				ENDWITH
;END ReadSlotLong
				ENDF


				EJECT
;_______________________________________________________________________________________	<1.5>
;	GetcString  -  get a C string
;
;	Find the cString identified by the id in the given sList.  Allocate a pointer
;	block on the system heap and copy the string to it.
;
;	Input	: reg A0 = ptr to spBlock
;	Output	: none
;
;	spBlock	:	 -> spId				id of the cString in the list
;				 -> spsPointer			ptr to list
;				<-	spResult			ptr to the cString in RAM
;
;	Called	: trap dispatcher
;

GetcString	Proc	Export
			With	spBlock
				
			movem.l	a2-a3,-(sp)
			move.l	spsPointer(a0),-(sp)			; save ptr to the sList
			move.b	spSlot(a0),-(sp)				; save the slot

			_sFindStruct							; get the ptr to the string
			bne 	@Done							; <SM9> CSS
			movea.l	spsPointer(a0),a3				; a3 = ptr to the string

;  Calculate the step register

			bset.b	#fConsecBytes,spFlags(a0)		; calc for consecutive bytes
			_sCalcStep
			bne 	@Done							; <SM9> CSS 
			move.l	spResult(a0),d1					; d1 = step register

;	Loop and count all the chars in the string so we know how big a block to allocate. 

			moveq	#0,d2							; clear temp reg for NextStep macro
			movea.l	a3,a1							; a1 = ptr to the string in the decl rom
			lea		@Done,a2						; set address to jump to if bus error
			slotjsr	InstallBus						; replace sys bus excptn and 32 bit mode
			moveq	#0,d0							; d0 = length cntr <SM12> PN
@Loop
			addq.l	#1,d0							; inc the length
			move.b	(a1),d2							; NUBUS READ - read a char
			beq.s	@EndLoop						; found the end of the string
			sNextStep	d1,d2,a1					; inc to the next byte in the decl rom
			bra.s	@Loop							;Continue looping

@EndLoop	move.l	d0,spSize(a0)					; set size of string
			slotjsr	RestoreBus						; restore mmu mode and sys bus excptn

;	Allocate a non-relocatable block on the current heap to copy the string to

			movea.l	a0,a2							; save ptr to spBlock
			_NewPtr									; allocate the memory					<1.6>
			bne.s	@Done

			move.l	a0,spResult(a2)					; pass ptr to buf in result field
			movea.l	a2,a0							; restore a0 = ptr to spBlock
			move.l	a3,spsPointer(a0)				; restore ptr to the string
			_sReadStruct							; copy the string to the buffer
			beq.s	@Done							; a good return
			move.l	d0,d1							; an error - save the error code
			movea.l	a0,a1							; save ptr to spBlock
			movea.l	spsPointer(a1),a0				; free the allocated pointer
			_DisposPtr
			movea.l	a1,a0							; restore a0 = ptr to spBlock
			move.l	d1,d0							; restore the error code

@Done
			move.b	(sp)+,spSlot(a0)				; restore the slot
			move.l	(sp)+,spsPointer(a0)			; restore the ptr to the sList
			movem.l	(sp)+,a2-a3
			rts

  
				EJECT
;_______________________________________________________________________________________	<1.5>
;	GetSlotBlock  -  get an sBlock on the system heap
;
;	Given a pointer to an sList and the id of an sBlock, find and copy the block to
;	a non-relocatable block allocated on either the system or current heap.  GetSSBlock
;	must allocate on the system heap, because video drivers assumes that it does.  The
;	old slot manager allocated on the current heap.
;
;	Input	: reg A0 = ptr to spBlock
;	Output	: none
;
;	spBlock	:	 -> spId				id of the sBlock in the list
;				 -> spsPointer			ptr to list
;				<-	spResult			ptr to the copy block in ram
;
;	Called	: trap dispatcher
;

GetSlotBlock	Proc	Export
			With	spBlock

			move.l	spsPointer(a0),-(sp)			; save the spsPointer field

			bset.b	#fCkReserved,spFlags(a0)		; check the reserved byte for zero
			_sReadPBSize							; read the physical block size
			bne.s	@Done
			move.l	spSize(a0),d0					; d0 = sBlock size (including length field)
			subq.l	#4,d0							; size-length field > 0 ?
			bgt.s	@Alloc							; positive block size					<1.7>
			move.w	#smNilsBlockErr,d0				; a bad block size
			bra.s	@Done
				
@Alloc
			move.l	d0,spSize(a0)					; set size of sBlock (minus length field)
			movea.l	a0,a1							; save ptr to spBlock
			_NewPtr	,Sys							; on sys heap for video dependency	<1.6>
			bne.s	@Done
			move.l	a0,spResult(a1)					; pass ptr to buffer in spResult
			
			movea.l	a1,a0							; restore a0 = ptr to spBlock
			_sReadStruct							; copy the sBlock to memory
			beq.s	@Done							; a good return
			move.l	d0,d1							; an error - save the error code
			movea.l	a0,a1							; save ptr to spBlock
			movea.l	spsPointer(a1),a0				; free the allocated pointer
			_DisposPtr
			movea.l	a1,a0							; restore a0 = ptr to spBlock
			move.l	d1,d0							; restore the error code

@Done
			move.l	(sp)+,spsPointer(a0)			;restore ptr to the sList
			rts


;_______________________________________________________________________________________	<1.5>
;	FindSlotStruct  -  find a structure
;
;	Given a pointer to an sList, find the offset field associated with the given id.
;	Calculate a pointer from the offset and return the pointer.
;
;	Input	: reg A0 = ptr to spBlock
;	Output	: none
;
;	spBlock	:	 -> spId				id of the sBlock in the list
;				<-> spsPointer			ptr to list
;										returns ptr to structure
;				<-	spByteLanes			by product of offsetdata call
;
;	Called	: trap dispatcher, jsr
;

FindSlotStruct	Proc Export
			With	spBlock

			_sOffsetData					; find and read the offset/data field
			bne.s	@Done
			
			_sCalcsPointer					; convert offset to a pointer
@Done
			rts

  
;_______________________________________________________________________________________	<1.5>
;	ReadSlotStruct  -  read a structure
;
;	Given a pointer to a structure, copy the structure into the given buffer,
;
;	Input	: reg A0 = ptr to spBlock
;	Output	: none
;
;	spBlock	:	 -> spsPointer			ptr to list
;				 -> spSize				number of bytes to read
;				<->	spResult			ptr to buffer to copy structure to
;
;	Called	: trap dispatcher, jsr
;

ReadSlotStruct Proc	Export
			With	spBlock, sInfoRecord

			movem.l	d1-d3/a1-a3,-(sp)
			move.b	spSlot(a0),-(sp)				; save spSlot field
			movea.l	spsPointer(a0),a3				; a3 = ptr to struct to read
			move.l	a3,-(sp)						; save spsPointer field

;	Strip the buffer pointer to make sure it's 32 bit clean

			move.l	spResult(a0),d0					; d0 = buffer ptr
			_StripAddress
			move.l	d0,-(sp)						; save a copy of ptr to buffer
			movea.l	d0,a1							; a1 = ptr to the buffer

;	Convert the pointer to a slot number

			_sPtrToSlot								; return a slot number
			bne.s	@Done
			_sFindsInfoRecPtr						; given the slot, get ptr to sInfoRecord
			bne.s	@Done
			movea.l	spResult(a0),a2					; reg a2 = ptr to sInfo record

;	Calculate the step register for the slot


			move.b	siCPUByteLanes(a2),spByteLanes(a0)	; get byte lane field from sInfo rec
			bset.b	#fConsecBytes,spFlags(a0)		; calc for consecutive bytes
			_sCalcStep
			bne.s	@Done
			move.l	spResult(a0),d1					; d1 = step register

;	Loop and read the struct into the buffer
;	a1 = ptr to buffer, a3 = ptr to struct

			slotjsr	InstallBus						; 32 bit mode and swap bus exception vector
			moveq	#0,d3							; clear temp register for NextStep macro
			move.l	spSize(a0),d2					; d2 = number of bytes to read
			subq.l	#1,d2							; adjust size for dbra loop
			lea		@EndLoop,a2						; address to jump to if bus error
			move.w	#smUnExBusErr,d0				; set error return (just in case)

@Loop
			move.b	(a3),(a1)+						; NUBUS READ - copy a byte
			sNextStep	d1,d3,a3					; inc rom ptr
			dbra	d2,@Loop						; continue
			moveq	#noErr,d0						; completed ok - set return status
@EndLoop
			slotjsr	RestoreBus						; switch back to save mmu mode and bus exception

@Done
			move.l	(sp)+,spResult(a0)				; restore ptr to buffer
			move.l	(sp)+,spsPointer(a0)			; restore ptr to structure
			move.b	(sp)+,spSlot(a0)				; restore slot number field
			movem.l	(sp)+,d1-d3/a1-a3
			tst.w	d0								; set ccr
			rts

;=========================================================================================
;	smSpecial
;=========================================================================================


**********************************************************************
*
* FUNCTION  ReadInfo : Read  Slot Information.
*	Read the SlotInfo record identified by Slot.
*
* Parameter block:
*	 ->	spSlot		- Which slot.
*	<->	spResult	- Pointer to the buffer.
*
**********************************************************************
ReadSlotInfo	FUNC	EXPORT

;VAR
spBlkPtr$a0		EQU		A0			;Pointer to the SDM parameter block.
sInfoRec$a		EQU		A1			;sInfoArray pointer.
BufferPtr$a		EQU		A2			;Pointer to the buffer.

Status$d0		EQU		D0			;Status
Slot$d			EQU		D1			;Slot
LoopIndex$d		EQU		D2			;Loop Index.
				
				
;----------------------------------------------------------------------
; ReadSlotInfo
;----------------------------------------------------------------------
;BEGIN 
				WITH	spBlock,sInfoRecord
				
;  Allocate local vars.
				MOVEM.L	A2,-(SP)									;Save registers
				MOVE.L	spResult(spBlkPtr$a0),BufferPtr$a			;BuferPtr$a <- Pointer to the buffer.
				MOVE.L	BufferPtr$a,-(SP)							;Save spResult.


;  Get a pointer to the sInfo Array for the given slot.
C10				_sFindsInfoRecPtr									;Find the pointer to the sInfo record.		<C520><C726>
				BNE		End											;Branch if error.							<C520>
				
				MOVE.L	spResult(spBlkPtr$a0),sInfoRec$a			;sInfoRec$a <- pointer to the sInfo record.	<C520>


;  Move the sInfo record to the block.
				MOVEQ	#sInfoRecSize-1,LoopIndex$d					;LoopIndex$d <- sInfoRecSize-1
																	;REPEAT
Repeat			MOVE.B	(sInfoRec$a)+,(BufferPtr$a)+				;  sInfoRec(i)+ <- sInfoArray[Slot].(i)+
Until			DBF		LoopIndex$d,Repeat							;UNTIL LoopIndex < 0
				MOVEQ	#0,Status$d0								;Status <- NoError


;  Restore locals & return		
End				MOVE.L	(SP)+,spResult(spBlkPtr$a0)					;Restore the buffer pointer.
				MOVEM.L	(SP)+,A2									;Restore registers
				RTS													;Return
					
				ENDWITH
;END ReadSlotInfo
				ENDF



**********************************************************************
*
* PROCEDURE SlotDisposPtr : Dispose Pointer
*	Dispose of the pointer.
*
* Parameter block:
*	->	spsPointer	- The pointer to be disposed.
*
**********************************************************************
SlotDisposPtr	PROC	EXPORT

;VAR
spBlkPtr$a0		EQU		A0			;Pointer to the SDM parameter block.
p$a0			EQU		A0			;Pointer to be disposed by Memory Manager.

Status$d0		EQU		D0			;Status

		
;----------------------------------------------------------------------
; SlotDisposPtr
;----------------------------------------------------------------------
;BEGIN 
				WITH	spBlock
				
;  Allocate local vars.
				move.l	a0,-(sp)						;								<1.7>

;  Dispose of the block.
				MOVE.L	spsPointer(a0),a0	;p <- sPointer
				_DisposPtr								;DisposPtr(p)
				BEQ.S	End								;IF Good THEN
														;  GOTO End
														;ELSE
				MOVE.L	#smDisposePErr,d0		;  Status <- smDisposeHErr

;  Restore spBlkPtr and Return
End				movea.l	(sp)+,a0						;								<1.7>
				RTS
				
				ENDWITH
;END SlotDisposPtr
				ENDP
				
				
				EJECT
**********************************************************************
*
*  FUNCTION ReadSlotDrvrName : Read the driver name.
*	Read the sResource name, Append the '.' prefix to this name,
*	giving the sDriver name. The result is a pointer to a Pascal type
*	string useable by iocore (_Open).
*
* Parameter block:
*     -> spSlot		: Slot number.
*	  -> spId		: sResource id.
*	 <-  spResult	: Pointer to the driver name.
*
**********************************************************************
ReadSlotDrvrName	FUNC	EXPORT

;VAR
spBlkPtr$a0		EQU		A0			;Pointer to the SDM parameter block.
SourcePtr$a0	EQU		A0			;**Used by _BlockMove.
DestPtr$a1		EQU		A1			;**Used by _BlockMove.
sRsrcNamePtr$a	EQU		A2			;Pointer to sResource name.
BufferPtr$a		EQU		A3			;Pointer to the buffer.

Status$d0		EQU		D0			;Status.
ByteCnt$d0		EQU		D0			;**Used by _BlockMove.
LensRsrcName$d	EQU		D1			;Length of sResource name.
IdSave$d		EQU		D2			;Save the sResource Id.
				
;----------------------------------------------------------------------
; ReadSlotDrvrName
;----------------------------------------------------------------------
;BEGIN sGetDrvrName
				WITH 	spBlock,sInfoRecord
				
;  Save
				MOVEM.L	A2-A3,-(SP)										;Save A2-A3
				MOVE.B	spId(a0),d2						;Save Id
				MOVE.L	spResult(a0),a3				;BuferPtr$a <- Pointer to the buffer.
				MOVE.L	a3,-(SP)								;Save spResult.

				
;  Get sResource Name
				CLR.B	spExtDev(A0)					; was not required for ReadDrvrName	<1.4>
				CLR.L	spParamData(A0)					; only look at enabled guys			<1.4>
				_GetsRsrcPtr							; find the ptr to the sRsrc			<1.4>
				BNE.S	Err2
				MOVE.B	#sRsrcName,spId(a0)
				_sGetcString											;Get the name.
				BNE.S	Err2
				MOVE.L	spResult(a0),a2
				MOVE.L	spSize(a0),d1
				SUBQ.B	#1,d1								;Adjust length to ignore the nil EOS marker.
				CMP.L	#254,d1								;IF d1 > 254 THEN
				BHI.S	Err1


;  Set prefix {Prefix = '<Len>.', where Len is the length of the string.
				MOVE.B	spSize+3(a0),(a3)+			;Str -> '<Len>'
				MOVE.B	#'.',(a3)+								;Str -> '<Len>.'
				

;  Append the sResource name to the prefix, giving: '<Len>.Name'
				MOVEM.L	A0-A1,-(SP)										;Preserve A0-A1, destroyed by _BlockMove.
				MOVE.L	a2,a0
				MOVE.L	a3,a1
				MOVE.L	d1,d0
				_BlockMove
				MOVEM.L	(SP)+,A0-A1										;Restore A0-A1, destroyed by _BlockMove.
				BNE.S	Err1
				
				
;  sDriver name build was successful. Dispose of sRsrc name string and exit.

				exg.l	a2,a0							; a0 = ptr to string to dispose		<1.7>
				_DisposPtr								;									<1.7>
				movea.l	a2,a0							; restore a0 = ptr to spBlock		<1.7>
				MOVEQ	#0,d0
				BRA.S	EndGetDrvrName
				

;  Error, Dispose sRsrc name string and exit.

Err1			exg.l	a2,a0							; a0 = ptr to string to dispose		<1.7>
				_DisposPtr								;									<1.7>
				movea.l	a2,a0							; restore a0 = ptr to spBlock		<1.7>


Err2			MOVE.L	#smGetDrvrNamErr,d0
			

;  Exit sGetDrvrName.
EndGetDrvrName 	MOVE.L	(SP)+,spResult(a0)						;Restore the buffer pointer.
				MOVE.B	d2,spId(a0)						;Restore Id
				MOVEM.L	(SP)+,A2-A3										;Restore A2-A3
				RTS

;END ReadSlotDrvrName
				ENDWITH
				ENDF
			

;_______________________________________________________________________________________	<1.6>
;	FindDevBase  -  find the slot device base address
;
;	Given a slot and id, find the base address.  There are 3 possible base addresses,
;	each within a different address space.  They are the 1Mb, 16Mb, and 256Mb spaces.
;	The address returned is determined by a flag in sRsrcFlags, and by whether a
;	minor or major base offset record is found.  The following permutations are
;	possible, and result in the address returned as indicated:
;
;						minorbase		majorbase		address returned
;						_________		_________		________________
;
;	sRsrcFlags missing		x								Fssxxxxx
;	sRsrcFlags missing						x				sxxxxxxx
;	24 bit					x								Fssxxxxx
;	24 bit									x				sxxxxxxx
;	32 bit					x								Fsxxxxxx
;	32 bit									x				sxxxxxxx
;
;
;	Input	: reg A0 = ptr to spBlock
;
;	Output	: reg D0 = status, 0=ok		CCR reflects status
;
;	spBlock	:	 -> spSlot
;				 -> spId				Id in list to search for
;				 <- spResult			base address for the given slot
;
;	Called	: secondary dispatch table
;

FindDevBase	Proc	Export
			Import	FindDevBaseSlot0

			With	spBlock,sInfoRecord

			move.l	a1,-(sp)
			move.b	spId(a0),-(sp)					; save the id of the sRsrc
			move.l	spParamData(a0),-(sp)			; save ParamData field

;	Get the pointer to the sResource

			clr.l	spParamData(a0)					; clear flags field
			bset.b	#fOneSlot,spParamData+3(a0)		; search only one slot
			slotjsr	sGetsRsrc						; only look at enabled sRsrc's
			bne		@Done							; some error						<djw>

			moveq.l	#0,d2
			move.b	spSlot(a0),d2					; d2 = slot number

;	Try to get the minor base offset

			move.b	#MinorBaseOS,spId(a0)
			_sReadLong
			bne.s	@Major							; minor base offset not found
			move.l	spResult(a0),d1					; d1 = minor base offset
			and.l	#$00ffffff,d1					; max offset in minor slot space
			tst.b	spSlot(a0)						; getting base addr for slot zero ?				<H2>
			beq.s	@SlotZero
			bra.s	@FormSmall						; form a 1Mb or 16Mb ptr

;	No minor base - try to get the major base offset

@Major		move.b	#MajorBaseOS,spId(a0)
			_sReadLong
			bne.s	@Done							; major base offset not found - error
			move.l	spResult(a0),d1					; d1 = major base offset
			and.l	#$0fffffff,d1					; max offset in major slot space
			tst.b	spSlot(a0)						; getting base addr for slot zero ?				<H2>
			beq.s	@SlotZero

;	Form a major base pointer (256Mb space) : d2 = slot number

			ror.l	#4,d2							; d2 = s0000000
			or.l	d1,d2							; d2 = sxxxxxxx
			bra.s	@RtnBase

;	Form a small base pointer (1Mb or 16Mb) - get the sRsrcFlag word to determine which
;	d2 = slot number

@FormSmall	move.l	#$f0000000,d0					; d0 = F0000000
			or.l	d1,d0							; d0 = F0xxxxxx
			bfins	d2,d0{4:4}						; d0 = Fsxxxxxx
			bfins	d2,d0{8:4}						; d0 = Fssxxxxx
			move.l	d0,d2							; d2 = a 24 bit pointer

		If forROM and Not forAUX then				;										<8>
;	If slot zero, then map the base address - only needed for minor space.  Only video
;	currently has a base address for slot zero.  If there is no internal video, then
;	do not map the base address.

			tst.b	spSlot(a0)						; getting base addr for slot zero ?
			bne.s	@NonZero

			movea.l	UnivInfoPtr,a1					; point to the ProductInfo record
			adda.l	ProductInfo.VideoInfoPtr(a1),a1	; point to the VideoInfo record
			move.l	VideoInfo.VRAMLogAddr24(a1),d2	; 24bit dev base to alias addr for slot zero
			move.l	VideoInfo.VRAMLogAddr32(a1),d1	; set d1 to 32bit offset if 32bit ptr
@NonZero
		endif										;										<8>

			move.b	#sRsrcFlags,spId(a0)			; get flags to determine whether the pointer
			_sReadWord								; - should be 24 or 32 bit
			bne.s	@RtnBase						; no flag field - default to 24 bit space
			move.w	spResult+2(a0),d0				; get sRsrc flag word					<1.8>
			btst.l	#f32BitMode,d0					; 32 bit mode flag set?					<1.8>
			beq.s	@RtnBase						; not set - use 24 bit address form		<1.8>
			and.l	#$ff000000,d2					; d2 = Fs000000
			or.l	d1,d2							; d2 = Fsxxxxxx

@RtnBase	move.l	d2,spResult(a0)					; return the base pointer
			moveq.l	#noErr,d0						; set a good return

@Done
			move.l	(sp)+,spParamData(a0)			; restore spBlock fields
			move.b	(sp)+,spId(a0)
			movea.l	(sp)+,a1						;										<1.8>
			rts

@SlotZero	Bsr.l	FindDevBaseSlot0				; Jump to our SlotZero utility.					<H2>
			Bra.s	@RtnBase						; And exit.


;=========================================================================================
;	smAdvanced
;=========================================================================================


;_______________________________________________________________________________________	<1.6>
;	CardChanged  -  return true if card changed
;
;	Return true if a card's PRAM has been initialized (i.e. a card in the slot
;	is now different from the previous one).
;
;	Input	: reg A0 = ptr to spBlock
;
;	Output	: reg D0 = status, 0=ok		CCR reflects status
;				  A0 = ptr to spBlock
;
;	spBlock	:	 -> spSlot					slot number
;				<-	spResult				0=false (card same), $ff if card changed
;

CardChanged		Proc	Export
				with	spBlock,sInfoRecord
				
				clr.b	spResult+3(a0)						;CardChanged <- Flags	{True if > 0}

				_sFindsInfoRecPtr							;Find the pointer to the sInfo record.	<C520>
				bne.s	@Done								;								<1.8>
				movea.l	spResult(a0),a1						; a1 = pointer to the sInfo record.<C520>

				btst.b	#fCardIsChanged,siStatusFlags(a1)
				sne.b	spResult+3(a0)						;spResult <- True if > 0, else false.
				
				MOVEQ	#0,d0								;Status <- NoError (No error is possible)
@Done			rts											;								<1.8>
				
				Endp

				EJECT
**********************************************************************
*
* PROCEDURE SlotExec : Execute
*	Download the code block, from the list pointed to by sPointer
*	and identified by Id, to the current heap zone, check revision
*	level, check CPU field and execute the code. 
*
* Parameter block:
*	->	spsPointer	- Struct pointer. Points to the list the code block is in.
*	->	spId		- The Id of the code block in the list.
*	->	spsExecPBlk	- Pointer to the se block.
*
**********************************************************************
SlotExec		PROC	EXPORT

;VAR
spBlkPtr$a0		EQU		A0			;Pointer to the SDM parameter block.
sePBlkPtr$a0	EQU		A0			;se parameter block pointer. ## warning A0 ##
Temp$a			EQU		A1			;Temporary address register.
CodePtr$a		EQU		A2			;Pointer to code.
CodePtrCopy$a	EQU		A3			;Copy of CodePtr (use to dispose ptr).

Status$d0		EQU		D0			;Status
Revision$d		EQU		D1
Temp$d			EQU		D2			;Temporary data register.
Slot$d			EQU		D3			;Slot
TimeOut$d		EQU		D4			;Time-Out value.

				BRA.S	BeginSlotExec	;												<C783>

;====================================================================
;
; PROCEDURE Revision2 : Revision-2
;	Handle a header with a revision level of 2.
;
;  Vars imported from SlotExec:
;   <->	a2		: Pointer to the Header of the code record.
;	<->	Status$d0		: Status of this call.
;	<-	TimeOut$d		: Time-out register.
;
;====================================================================	

;BEGIN 
				WITH	spBlock,seHeader2
				
;  Check Revision level.
Revision2		CMP.B	#sExec2,seRevision(a2)		;IF (CodePtr.seRevision = sExec2) THEN
				BEQ.S	@10									;  GOTO @10

				MOVE.L	#smCodeRevErr,d0				;Status <- smCodeRevErr
				BRA.S	EndRev2


;  Check CPU type
@10				CMP.B	#sCPU68020,seCPUId(a2)		;IF CodePtr.seCPUId = (sCPU68000 OR sCPU68020) THEN	<C807>
				BLS.S	@20									;  GOTO @20 {CPU type is good, Continue}				<C807>
				
				MOVE.L	#smCPUErr,d0					;Status <- smCPUErr
				BRA.S	EndRev2


;  Get pointer to code
@20				ADDQ.L	#seCodeOffset,a2				;CodePtr <- CodePtr + CodePtr.seCodeOffset {Skip to pointer to code}
				MOVE.L	(a2),a1					;Temp <- CodePtr^  {Offset to code}
				ADD.L	a1,a2					;CodePtr <- CodePtr + Temp {Now points to top of code}


;  Code header is good, clear status and exit.
Good2			MOVEQ	#0,d0
				
																
;  Return.
EndRev2			RTS											;Return
					
				ENDWITH
;END Revision2


;----------------------------------------------------------------------
; SlotExec
;----------------------------------------------------------------------
;BEGIN 
				WITH 	spBlock

;  Allocate local vars.
BeginSlotExec
				MOVEM.L	A2-A3/D3-D4,-(SP)				;Save registers			<C783>
				MOVE.L	spsPointer(a0),-(SP)			;Save sPointer


;  Read the sBlock from the ROM into RAM.  Warning - GetsBlock now always allocates
;	on the system heap.

				_sGetBlock								;CodePtr <- Pointer to the code block.
				BNE.S	End								;IF Error THEN
														;  GOTO End {exit with error}
				MOVE.L	spResult(a0),a2
				MOVE.L	a2,a3							;Save a copy for dispose ptr

;	WARNING...WARNING...WARNING...
;	The routine FixTrident depends on spSize being unchanged from _sGetBlock

				bsr		FixTrident						; kludge to fix a bug in Trident video card
				
;  Decode the revision level of the header and retrieve the necessary data.				
				BSR.S	Revision2						;IF (Error in Rev-2 header) THEN
				BNE.S	DispCode						;  GOTO DispCode {Dispose of the code and exit with error}

;  Header ok, execute the code.
ExecCode		MOVEM.L	A0-A6/D0-D7,-(SP)				;Save registers
				JSR		([jCacheFlush])					;flush the caches		<18> rb
				MOVE.L	spsExecPBlk(a0),a0				;Set pointer to se parameters.
				JSR		(a2)							;Parameters: ->Slot,->Id, plus any extensions.
				MOVEM.L	(SP)+,A0-A6/D0-D7				;Restore registers

				MOVEQ	#0,d0							;SlotExec Good, now exit.				

;  Release memory (created by sGetBlock).
DispCode		EXG.L	d0,d2							;Save the Status (May be an error)		<C520>
				exg.l	a3,a0							; a0 = ptr to dispose					<1.7>
				_DisposPtr								; dispose of the code block				<1.7>
				movea.l	a3,a0							; restore a0 = ptr to spBlock			<1.7>
				EXG.L	d2,d0							;Restore the Status 					<C520>


;  Release memory, Clean up stack & return
End				MOVE.L	(SP)+,spsPointer(a0)			;Restore sPointer.		<C468/25Nov86>	<C783>
				MOVEM.L	(SP)+,A2-A3/D3-D4						;Restore registers
				
				RTS												;Return
				
;END SlotExec


;_______________________________________________________________________________________	<14>
;	FixTrident  -  patch 8¥24 card problem
;
;	This routine is called from SlotExec (_Exec) to detect a code problem in the
;	Trident/4¥8/8¥24/GC video card's secondaryInit code.  The Trident card's
;	secondaryInit code has a call to _sNextTypesRsrc.  Instead of setting "clr.b tbMask(a0)",
;	the error is using "clr.b sptbMask", which clears byte $30 in low mem (fortunately benign).
;	This fix looks at every sExec block being executed.  If it sees a "clr.b $30" next to
;	a _sNextTypesRsrc, then it changes the instruction to a "clr.b $30(a0)".
;
;	Input	:	a2	=	ptr to sExec sBlock (including an 8 byte header in this case)
;				a0	=	ptr to spBlock
;
;	spBlock	:	-> spSize	size of code block read in
;


TridentCodeSize	Equ		$18e					; size of secondaryInit sBlock
TheBadCode		Equ		8+$7e					; header+code offset to bad code to patch

FixTrident
			movem.l	a0-a1,-(sp)
			movea.l	a2,a1						; a1 = ptr to beginning of code

			cmpi.l	#TridentCodeSize,spSize(a0)	; same size code block?
			bne.s	@Done						; no - do nothing

			lea.l	CompareTbl,a0				; get addr of table of values to compare
			adda.l	(a0)+,a1					; add in offset to code to compare
			move.w	(a0)+,d0					; get the compare count
@Loop		cmp.w	(a0)+,(a1)+					; compare the code
			dbne	d0,@Loop					; continue comparing code

			tst.w	d0							; did we finish the count?
			bpl.s	@Done						; nope - positive count
			move.w	#$4228,TheBadCode(a2)		; change "clr.b $30" to "clr.b $30(a0)"

@Done
			movem.l	(sp)+,a0-a1
			rts

;_______________________________________________________________________________________	<14>
;	Code Comparision Table For Trident Card Fix
;

CompareTbl
			dc.l	8+$7e						; header+code offset into the code to start
			dc.w	6-1							; compare 6 words (adjusted for dbra)

			dc.w	$4238						; "clr.b	spTBMask" <--- the bad one
			dc.w	$0030
			dc.w	$4228						; "clr.b	spExtDev(a0)"
			dc.w	$0033
			dc.w	$7015						; "moveq	#15,d0"
			dc.w	$a06e						; "_SlotManager"
			
			Endwith
			Endp


				EJECT
**********************************************************************				<C225/16Oct86>
*
* PROCEDURE CalcsPointer : Calculate sPointer
*	Calculate a pointer to a byte in the declaration ROM, given the
*	pointer to the current byte and an offset in bytes (to the new
*	byte if the memory was contiguous).
*
* Parameter block:
*	<-> spsPointer		- A pointer to the current byte in ROM.
*	 ->	spOffsetData	- Offset to the desired location.
*	 ->	spByteLanes		- The ByteLanes value for this slot.
*	
**********************************************************************
CalcsPointer	PROC	EXPORT

;VAR
spBlkPtr$a0		EQU		A0			;Pointer to the SDM parameter block.
ROMPtr$a		EQU		A1			;Pointer to ROM.						<C783>


Status$d0		EQU		D0			;Status of sNextsRsrc.
TheByteLane$d	EQU		D1			;The Byte lane you are in.
NumByteLanes$d	EQU		D2			;The number of byte-lanes used.
Index$d			EQU		D3			;Index
ROMOffset$d		EQU		D4			;Number of bytes offset relative to ROM.
PCOffset$d		EQU		D5			;Number of bytes offset relative to the CPU.
Remainder$d		EQU		D6			;Number of bytes to do after the coarse calculation.
				
;----------------------------------------------------------------------
; CalcsPointer
;----------------------------------------------------------------------
;BEGIN 
				WITH	spBlock,srrBlock,sInfoRecord
								
;  Allocate and Initialize some vars.
				MOVEM.L	D3-D6,-(SP)									;Save registers  <C438/19Nov86><C783>

				MOVE.L	spsPointer(spBlkPtr$a0),ROMPtr$a			;ROMPtr$a <- Pointer to the present location in ROM.


;  Verify ROMPtr$a points to a valid byte-lane (This test could be deleted, but now it helps debug).
				MOVEQ	#0,TheByteLane$d							;TheByteLane$d <- Bits 0 and 1 of ROMPtr$a.
				MOVE.W	ROMPtr$a,TheByteLane$d
				AND.B	#$03,TheByteLane$d
				BTST.B	TheByteLane$d,spByteLanes(spBlkPtr$a0)		;IF (the bit in spByteLanes corresponds to the actual byte lane) THEN
				BNE.S	C20											;  GOTO C20  {Good}
																	;ELSE
				MOVE.L	#smBadsPtrErr,Status$d0						;  Status$d0 <- smBadsPtrErr
				BRA.S	End											;  GOTO End {Exit with error}
				

;  Calculate the number of byte-lanes used per long.
C20				MOVEQ	#0,NumByteLanes$d

				MOVEQ	#3,Index$d									;FOR Index <- 3 DOWNTO 0 DO
For				BTST.B	Index$d,spByteLanes(spBlkPtr$a0)			;  IF (There is a ROM in this byte-lane) THEN
				BEQ.S	EndFor
				ADDQ	#1,NumByteLanes$d							;     NumByteLanes$d <- NumByteLanes$d + 1
				
EndFor			DBF		Index$d,For									;ENDFOR

				TST.B	NumByteLanes$d								;IF NumByteLanes$d = 0 THEN
				BNE.S	C30
				MOVE.L	#smByteLanesErr,Status$d0					;   Status$d0 <- smByteLanesErr
				BRA.S	End											;   GOTO End {Exit with error}				


;  Calculate the offset (Coarse calculation, determines offset of ROMOffset$d >= 4 bytes).
C30				MOVE.L	spOffsetData(spBlkPtr$a0),ROMOffset$d		;ROMOffset$d <- The number of ROM bytes to the desired new position.
				
				BTST.L	#23,ROMOffset$d								;ROMOffset$d <- ROMOffset$d sign extended (from 24-bits to 32).
				BEQ.S	C35 
				OR.L	#$FF000000,ROMOffset$d
				
C35				MOVE.L	ROMOffset$d,PCOffset$d						;PCOffset$d <- ROMOffset$d DIV NumByteLanes$d
				TDIVS.L	NumByteLanes$d,Remainder$d:PCOffset$d
				LSL.L	#2,PCOffset$d								;PCOffset$d <- PCOffset$d * 4
				

;  Calculate the Offset (fine calculation, adjust offset for remainning offset of < 4 bytes)
C40				TST.B	Remainder$d									;IF Remainder = 0 THEN
				BEQ.S	C50											;  GOTO C50 {done}
				BPL.S	PlRemainder									;IF Remainder < 0 THEN
																	;  GOTO PlRemainder {Offset is positive}
;    Offset is negative.
MiRemainder		NEG.W	Remainder$d									;Remainder$d <- poitive remainder.	<C361>
				SUBQ.W	#1,Remainder$d								;Adjust Remainder$d for DBF			<C361>
																	;REPEAT  
Repeat_MI		SUBQ.B	#1,TheByteLane$d							;  TheByteLane$d <- the next byte lane (Check all byte lanes 0..3)	<C361><C726>
				AND.B	#$03,TheByteLane$d
				
				SUBQ.L	#1,PCOffset$d								;  PCOffset$d <- PCOffset$d + 1
				BTST.B	TheByteLane$d,spByteLanes(spBlkPtr$a0)		;  IF (no ROM in this byte lane) THEN
				BEQ.S	Repeat_MI									;     GOTO Repeat_MI
				
Until_MI		DBF		Remainder$d,Repeat_MI						;UNTIL (there have been four rotates)
				BRA.S	C50											;GOTO C50  {Calculation complete}
				
				
;    Offset is positive.
PlRemainder		SUBQ.W	#1,Remainder$d								;Adjust Remainder$d for DBF								<C361>
																	;REPEAT  
Repeat_PL		ADDQ.B	#1,TheByteLane$d							;  TheByteLane$d <- the next byte lane (Check all byte lanes 0..3)	<C361><C726
				AND.B	#$03,TheByteLane$d							;														<C726>
				
@10				ADDQ.L	#1,PCOffset$d								;  PCOffset$d <- PCOffset$d + 1
				BTST.B	TheByteLane$d,spByteLanes(spBlkPtr$a0)		;  IF (no ROM in this byte lane) THEN
				BEQ.S	Repeat_PL									;     GOTO Repeat_PL
				
Until_PL		DBF		Remainder$d,Repeat_PL						;UNTIL (there have been four rotates)


;  Calculations complete, offset was calculated successfully, now add it to the current pointer.
C50				ADD.L	PCOffset$d,spsPointer(spBlkPtr$a0)			;spsPointer <- spsPointer + PCOffset$d
				MOVEQ	#0,Status$d0								;Status$d0 <- good.
		

;  Restore globals & return		
End 			MOVEM.L	(SP)+,D3-D6									;Restore registers	<C438/19Nov86><C783>
				RTS													;Return

				ENDWITH
;END CalcsPointer
				ENDP

				EJECT
**********************************************************************
*
* FUNCTION GetSlotDrvr : Get Driver.
*	Load a Mac-OS driver into a relocatable block on the system heap.
*	
* Parameter block:
*	 ->	spIOFileName	- The file name (probably not useful).
*	 ->	spSlot			- The slot the driver is in.
*	 ->	spId			- Id of the sResource which contains the driver.
*	 ->	spExtDev		- Id of the external device (not useful to drivers in Decl ROM).
*	 ->	spsExecPBlk		- Pointer to any extra parameters useful to the driver 
*							and/or the sLoad record.
*	<-	spResult		- Handle/Ptr to the driver.
* *** New feature, add if necessary *** see Rich C. 	<-	spFlags			- If fDRAMBased is set then is Handle to the driver.
*
**********************************************************************
GetSlotDrvr		FUNC	EXPORT

;VAR
ParamBlk$a0		EQU		A0				;Parameter block pointer (Used by SDM & _NewHandle)	<C600>
h$a0			EQU		A0				;Pointer to the handle to be disposed of.			<C600>
NewHandRes$a0	EQU		A0				;***Used by _NewHandle								<C600>
spBlkPtr$a		EQU		A1				;SDM parameter block.								<C600>
seBlkPtr$a		EQU		A2				;SDM parameter block.								<C600>
DrvrHand$a		EQU		A3				;Handle to the driver.								<C600>

Status$d0		EQU		D0				;Status
Size$d0			EQU		D0				;***Used by _NewHandle
StripAddr$d0	EQU		D0				;Strip Address.								<C887>
Temp$d			EQU		D1				;Temporary data register
SavedId$d		EQU		D2				;Saved Id
				

				BRA		BeginGetSlotDrvr	;	<C783>
				EJECT
*====================================================================
* FUNCTION DynLoadDrvr : Dynamically Load Driver.
*	Execute a given block of code to load the driver. This code must
*	set spResult with a handle to the driver.
*
*  Vars imported from GetSlotDrvr:
*	Status$d0
*	Status$d0
*	Temp$d
*	ParamBlk$a0
*
* Parameter block:
*	 ->	spSlot			- The slot the driver is in.
*	 ->	spId			- Id of the sResource which contains the driver.
*	 ->	spExtDev		- Id of the external device (not useful to drivers in Decl ROM).
*	 ->	spsExecPBlk		- Pointer to any extra parameters useful to the driver 
*							and/or the sLoad record.
*	<-	spResult		- Handle to the driver (Set by the sLoad record).
* *** New feature, add if necessary *** see Rich C. 	<-	spFlags			- If fDRAMBased is set then is Handle to the driver.
*
*====================================================================	

;----------------------------------------------------------------------
; DynLoadDrvr
;----------------------------------------------------------------------
;BEGIN 
				WITH	spBlock,FHeaderRec,seBlock

;  Initialize.
DynLoadDrvr		CLR.L	spParamData(A0)						; only look at enabled guys			<1.4>
				_GetsRsrcPtr								; find the ptr to the sRsrc			<1.4>
				BNE.S	DError									;Branch if error.


;  Execute the code to load the driver.
				MOVE.B	#sRsrcLoadRec,spId(ParamBlk$a0)		;Load the driver.
				_sExec
				BNE.S	DError									;IF SDM error THEN GOTO DError
				MOVE.L	spsExecPBlk(ParamBlk$a0),seBlkPtr$a		;seBlkPtr$a <- pointer to the seBlock	<C614>
				TST.W	seStatus(seBlkPtr$a)					;Test the status of the sLoad code.		<C614>
				BNE.S	DError									;Branch if error.						<C614>

				MOVE.L	seResult(seBlkPtr$a),spResult(ParamBlk$a0)	;Return the handle to the caller.
				MOVE.B	seFlags(seBlkPtr$a),spFlags(ParamBlk$a0)	;Handle or Ptr? See fRAMBased.
				MOVEQ	#0,Status$d0
				BRA.S	EndDynLoad								;GOTO EndDynLoad

				
;  Error.
DError			MOVE.L	#smsGetDrvrErr,Status$d0				;Status <- smsGetDrvrErr


EndDynLoad														;							<1.8>
				RTS				
				
				ENDWITH
;END  DynLoadDrvr


				EJECT
*====================================================================
* FUNCTION StatLoadDrvr : Statically Load Driver.
*	Load the driver from the driver directory.
*
*  Vars imported from GetSlotDrvr:
*	Status$d0
*	Temp$d
*	spBlkPtr$a
*	ParamBlk$a0
*	NewHandRes$a0
*	h$a0
*	DrvrHand$a
*
*	spsPointer
*
* Parameter block:
*	 ->	spSlot		- The slot the driver is in.
*	 ->	spId		- Id of the sResource which contains the driver.
*	<-	spResult	- Handle to the driver (Set by the sLoad record).
* *** New feature, add if necessary *** see Rich C. 	<-	spFlags			- If fDRAMBased is set then is Handle to the driver.
*
*====================================================================	

;----------------------------------------------------------------------
; StatLoadDrvr
;----------------------------------------------------------------------
;BEGIN 
				WITH	spBlock,FHeaderRec
				
;  Get a pointer to the sResource list and find the driver directory.
StatLoadDrvr	_sRsrcInfo										;Get a pointer to the sResource list.	<C468>
				BNE 	SError2								; <SM9> CSS 
				MOVE.B	#sRsrcDrvrDir,spId(ParamBlk$a0)		;Find the driver directory.
				_sFindStruct
				BNE.S	SError2
				

;  Load the driver into the system heap zone.
LoadDrvr		MOVE.B	#sMacOS68020,spId(ParamBlk$a0)			;Load a MacOS 68020 driver.					<C807>
				BSET.B	#fCkReserved,spFlags(ParamBlk$a0)		;Check reserved byte of the PBS.		<C807>
				_sReadPBSize									;Read the physical block size.			<C807>
				BEQ.S	@10										;IF ok THEN								<C807>
																;  GOTO @10								<C807>
				MOVE.B	#sMacOS68000,spId(ParamBlk$a0)			;Load a MacOS 68000 driver.
				BSET.B	#fCkReserved,spFlags(ParamBlk$a0)		;Check reserved byte of the PBS.		<C600>
				_sReadPBSize									;Read the physical block size.			<C600>
				BNE.S	SError2									;IF Error THEN							<C600>
																;  GOTO End								<C600>
@10				SUBQ.L	#4,spSize(ParamBlk$a0)					;Size <- PhyBlkSize - 4 {By pass the physical block size}.<C600>
				MOVE.L	spSize(ParamBlk$a0),Size$d0
				_ResrvMem	,SYS								;Compact the system heap.				<C600><C783>
				BNE.S	SError2									;Branch if error						<C600><C783>

				MOVE.L	spSize(ParamBlk$a0),Size$d0				;											  <C783>
				_NewHandle	,SYS								;Allocate the memory on the system heap.<C600>
				BNE.S	SError2									;  GOTO SError2							<C600>

				MOVE.L	NewHandRes$a0,DrvrHand$a				;Put the result of _NewHandle into DrvrHand$a.	<C600>
				MOVE.L	(NewHandRes$a0),StripAddr$d0			;Dereference. <C887>
				_StripAddress									;Strip the address. <C887>
				MOVE.L	StripAddr$d0,spResult(spBlkPtr$a)		;Put the pointer to the block into spResult.	<C600><C887>
				MOVE.L	spBlkPtr$a,ParamBlk$a0					;Restore the SDM parameter block pointer.		<C600>

				_sReadStruct									;Read the sBlock into the buffer.		<C600>
				BNE.S	SError1									;IF error THEN							<C600>
																;  GOTO SError1.						<C600>
				MOVE.L	DrvrHand$a,spResult(ParamBlk$a0)		;Put handle to the driver in spResult.	<C600>
				JSR		([jCacheFlush])							; flush the caches  <18> rb
				BRA.S	EndStatLoad								;GOTO end.								<C600>
				
;  ERRORS
;    Error(1): Dispose of the sBlock and restore the zone.
SError1			MOVE.L	DrvrHand$a,h$a0
				_DisposHandle									;Dispose of the handle.				


;    Error(2): Set Status$d, spResult and exit with error.
SError2			MOVE.L	spBlkPtr$a,ParamBlk$a0					;Restore spBlkPtr
				CLR.L	spResult(ParamBlk$a0)					;Result <- nil
				MOVE.L	#smsGetDrvrErr,Status$d0				;Status <- smsGetDrvrErr
																

EndStatLoad		RTS				
				
				ENDWITH
;END  StatLoadDrvr

				EJECT

;----------------------------------------------------------------------
; GetSlotDrvr
;----------------------------------------------------------------------
				WITH 	spBlock

;	Patch out Drivers for any Apple Cards that need it.

BeginGetSlotDrvr
			IF NOT LC930 THEN
				Import	GetDriverPatch
				Bsr		GetDriverPatch						; If GetDriverPatch returns a 
				Tst.b	D0									;	result >= 0, donÕt execute
				Bge.s	ExitGetSlotDrvr						; Patched, so just return.
			ENDIF
;	This is the original entry-point. It is called by GetDevIndex
				Export	OrigGetSlotDrvr
OrigGetSlotDrvr
				MOVEM.L	A2-A3,-(SP)								;Save registers		<C783>
				MOVE.L	ParamBlk$a0,spBlkPtr$a					;Save spBlkPtr
				MOVE.B	spId(ParamBlk$a0),SavedId$d				;Save Id.

;  Try to load the driver by executing the code from proper sLoad list in the sRsrc_LoadDir.

				BSR		DynLoadDrvr								;spResult <- DynLoadDrvr(->spSlot,->spId,<-Status)
				BEQ.S	@Done									;Driver was successfully loaded.
				
;  Try to load the driver from the sRsrc_DrvrDir.

				MOVE.B	SavedId$d,spId(ParamBlk$a0)
				BSR		StatLoadDrvr							;spResult <- StatLoadDrvr(->spSlot,->spId,<-Status)
				
;  Clean up and exit		

@Done			MOVE.L	spBlkPtr$a,ParamBlk$a0					;Restore spBlkPtr
				MOVE.B	SavedId$d,spId(ParamBlk$a0)				;Restore Id.
				MOVEM.L	(SP)+,A2-A3								;Restore registers	<C783>
				

ExitGetSlotDrvr	RTS
				ENDWith

				EJECT
				
;____________________________________________________________________________
;
; GetDriverPatch and support (called from _SGetDriver).
;
;	Entry/Exit:	A0 points to spBlock.
;
;	Purpose:	The purpose of GetDriverPatch is to identify Apple
;				Cards whose Driver code is faulty, and execute fixed
;				versions from the CPU ROM. ItÕs job is to
;				call GetDevIndex to determine whether the card/device
;				pointed to by the incoming spBlock is one that needs
;				patching.  If so, it attempts to allocate enough space
;				for the driver in the system heap, copy it there, and
;				then return a handle to the driver in spBlock.spResult.
;				If the driver canÕt be loaded (e.g., not enough memory),
;				the appropriate Slot Manager error is returned.
;____________________________________________________________________________

GetDriverPatch		Proc	Export
			With	spBlock,LoadTblRec

@saveRegs	Reg		A0/A3-A5/D3-D4						; Define registers for work/saving, and
			Movem.l	@saveRegs,-(Sp)						;	store them on the stack.
			Link	A6,#-spBlockSize					; Get a stack frame.
			
			Move.l	A0,A3								; Save spBlockPtr.

; First, look to see if we even need to be attempting to patch the driver on this
; 	card/device.
;
			Bsr		GetDevIndex							; Get the card/device index into D0.
			Move.l	D0,D3								; If the device index is negative, then
			Bmi 	@done								; driver doesnÕt need patching.<SM9> CSS	

; Next, check to see that weÕre pointing to the sResource that has the driver or
;	driver loader in it (i.e., we donÕt want to be opening the driver multiple
;	times).
;
			Lea		LoadTbl,A1							; Point to base of LoadTbl.
			Mulu	#LTEntrySize,D3						; Adjust index.
			
			Move.l	A1,A0								; Point to base of LoadTbl.
			Move.b	ltSpID(A0,D3),D0					; Get spID.
			Move.b	ltSpExtDev(A0,D3),D1				; Get spExtDev.

			Moveq	#0,D4								; Clear value holder.
			Move.b	D0,D4								; Save spID
			Swap	D4									; 	and
			Move.b	D1,D4								; Save spExtDev.
			
			Move.l	Sp,A0								; Point to local spBlock.

			Move.b	spSlot(A3),spSlot(A0)				; Get pointer to right sResource list:
			Move.b	spID(A3),spID(A0)					; 	Need spSlot, spID, and 
			Move.b	D4,spExtDev(A0)						; 	spExtDev.
			_SRsrcInfo
			Bne.s	@errRtn
			
			Swap	D4									; Get spID.									
			
			Move.b	D4,spID(A0)							; Make sure this sResource is the one
			_SFindStruct								; 	with the driver in it.
			Bne.s	@errRtn

; Finally, now that we know that we on the right card/device and are looking at the right
;	sResource, allocate relocatable space in the System heap and tell the caller where that
;	space is.
;
			Move.l	A1,A4								; Point to base of LoadTbl.
			Add.l	ltAddrOff(A1,D3),A4					; Add offset to driver and save pointer.
			
			Move.l	A1,A0								; Point to base of LoadTbl.
			Add.l	ltSizeOff(A1,D3),A0					; Add offset to driver size and get pointer.
			Move.l	(A0),D3								; Save driver size.
			
			Move.l	D3,D0								; Compact/Reserve some space for ourselves
			_ResrvMem	,SYS							;	in the System heap.
			Bne.s	@errRtn								; Get out if we couldnÕt reserve the space.
			
			Move.l	D3,D0								; Attempt to allocate some space in the
			_NewHandle	,SYS							;	System heap.
			Bne.s	@errRtn								; Get out if we couldnÕt get the space.
			
			Move.l	A0,A5								; Remember allocated handle, and prevent it
			_HNoPurge									;	from being removed.
			
			Move.l	A4,A0								; Get address of driver code.
			Move.l	(A5),A1								; Get address of allocated space.
			Move.l	D3,D0								; Get size.
			_BlockMove									; Move driver into allocated space.
			
			Move.l	A3,A0								; Restore spBlockPtr.
			Move.l	A5,spResult(A0)						; Return driver handle in spResult.
			
			Moveq	#0,D0								; Return noErr.
			Bra.s	@done

@errRtn		Move.l	#smsGetDrvrErr,D0					; CouldnÕt load driver error.			
@done		Unlk	A6									; Restore stack frame.
			Movem.l	(Sp)+,@saveRegs						; Restore registers.
			Rts
			
;____________________________________________________________________________
;
;	GetDevIndex
;
;	Entry:		A0	-- points to spBlock.
;
;	Exit:		D0	-- contains index (0..n-1) or error value (<0).
;
;	Purpose:  The purpose of GetDevIndex is to cycle through a table of 
;			  card/device identifiers.  If the card/device passed in
;			  is in the device table, itÕs entry is returned.  Otherwise,
;			  a negative number (-1) is returned.  This is a utility routine
;			  called by SlotGetDriverPatch and SlotPrimaryInitPatch.
;
;	Note:	  This routine JsrÕs (Bsr.lÕs) to GetSlotDrvr directly due to
;			  the fact that we might be coming from our patched out version
;			  of GetSlotDrvr!  We need to do this so that we can compare
;			  the version of the driver on the card with the one we know
;			  about.  This is so that we donÕt patch out newer (and
;			  presumably compatible) versions of the PrimaryInit and Driver.
;
;____________________________________________________________________________

			Macro										; Macro for jumping directly int
			_GetSlotDriver								;	GetSlotDrvr (acting like trap dispatcher).								
			Movem.l	A1/D1-D2,-(Sp)						; Save registers (A0 i/o, D0 is result).
			Bsr.l	OrigGetSlotDrvr						; Make call.					<T8>
			Tst.w	D0									; Act like Slot Manager for result.
			Movem.l	(Sp)+,A1/D1-D2						; Restore registers.
			EndMacro									;
			
			With	DevTblRec,spBlock,seBlock
			Export	GetDevIndex 
GetDevIndex 

@saveRegs	Reg		A3-A5/D3-D4							; Define registers for work/saving, and
			Movem.l	@saveRegs,-(Sp)						; 	store them on the stack.
			Link	A6,#-spBlockSize					; Get a stack frame.
			
			Move.l	A0,A3								; Save current spBlock to simulate sExec.
			Move.l	Sp,A0								; Point to stack spBlock.

			Lea		DevTbl,A4							; Point to table of device identifiers.
			Moveq	#0,D3								; Set entry index to 0.
			
@repeat		Move.b	spSlot(A3),spSlot(A0)				; Reset spSlot to the one we want.
			Clr.w	spID(A0)							; Start search from top, no external devices.		<11>
			Clr.b	spTBMask(A0)						; No mask (i.e., specific search).
			Move.w	category(A4),spCategory(A0)			; Look for:  Category,
			Move.w	cType(A4),spCType(A0)				;			 CType,
			Move.w	drvrSW(A4),spDrvrSW(A0)				;			 DrvrSW,
			Move.w	drvrHW(A4),spDrvrHW(A0)				;			 DrvrHW.
			Clr.l	spParamData(A0)						; Look only for enabled sRsrcs.						<11>
			Bset	#foneslot,spParamData+3(A0)			; Limit search to this slot only by
			_GetTypeSRsrc								; 	using the new & improved routine.
			
			Bne.s	@until								; If this isnÕt the device were looking for,
														;	then keep going.				
			
			_GetSlotDriver								; Otherwise, attempt to load driver (for version #).
			Bne.s	@dontPatch							; If it couldnÕt be loaded, punt.
			
			Move.l	spResult(A0),A5						; Save driver handle (to dispose).
			Move.l	(A5),A0								; Get driver pointer.
			Lea		drvrName(A0),A0						; Get driver name.
			
			Clr.w	D0									; Clear D0.w (for use as index).
			Move.b	(A0),D0								; Get the length of the driver name.
			Addq	#2,D0								; Adjust offset to version field.
			Bclr	#0,D0								; Adjust offset for word alignment.
			Move.w	(A0,D0.w),D4						; Save the version number.
			Move.l	A5,A0								; Get driver handle, and
			_DisposHandle								;	dispose of it.
			
			Cmp.w	drvrVers(A4),D4						; If the cardÕs version ­ to ours,					<11>
			Bne.s	@until								;	then keep going.								<11><H5>
			
			Move.l	D3,D0								; Otherwise, copy the index into D0,
			Bra.s	@done								;	and return.
			
@until		Adda	#DTEntrySize,A4						; Point to next entry in table, and
			Addq	#1,D3								;	increment entry index.
			Tst.w	(A4)								; If this isnÕt the final entry, then							
			Bne.s	@repeat								;	keep checking.
			
@dontPatch	Moveq	#-1,D0								; Flag that we donÕt want to patch.
@done		Unlk	A6									; Put stack frame back.
			Movem.l	(Sp)+,@saveRegs						; Restore registers.
			Rts		
			
			EndWith

			
;____________________________________________________________________________
;
;	Tables for PrimaryInit & GetDriver patches.
;
;	Note:  Keep these here and donÕt export these labels!
;____________________________________________________________________________

			Import	TFBDrvr,TFBDrvrSize
			Import	JMFBDrvr,JMFBDrvrSize
			
			Import	TFBPrimaryInit
			Import	JMFBPrimaryInit
			
			Export	JsrTbl

DevTbl		Dc.w	CatDisplay,TypVideo,DrSwApple,DrHwTFB,0	 ; TFB  Entry									<11>
			Dc.w	CatDisplay,TypVideo,DrSwApple,DrHwJMFB,0 ; JMFB Entry									<11>
			Dc.w	0									
					
			Align	4
			
LoadTbl		Dc.l	TFBDrvr-LoadTbl						; Start of TFB driver code.	
			Dc.l	TFBDrvrSize-LoadTbl					; Length.
			Dc.b	sRsrcDrvrDir						; TFBÕs have an sRsrcDrvrDir.
			Dc.b	0									; TFBÕs are singly deviced.
			Dc.b	0,0									; Padding.											<11>
			
			Dc.l	JMFBDrvr-LoadTbl					; Start of 4¥8/8¥24 driver code.
			Dc.l	JMFBDrvrSize-LoadTbl				; Length.
			Dc.b	sRsrcDrvrDir						; 4¥8/8¥24Õs have an sRsrcDrvrDir.
			Dc.b	0									; 4¥8/8¥24Õs are singly deviced.
			Dc.b	0,0									; Padding.
			
			Align	4
			
JsrTbl		Dc.l	TFBPrimaryInit-JsrTbl				; TFBPrimaryInit
			Dc.l	JMFBPrimaryInit-JsrTbl				; 4¥8/8¥24 PrimaryInit


;=========================================================================================
;	slotinfo
;=========================================================================================

;_______________________________________________________________________________________	<v1.2>
;	ReadSlotPRAM  -  read PRAM associated with a slot
;	PutSlotPRAM	  -  write PRAM associated with a slot
;	InitSlotPRAM  -  write PRAM including board and record id's
;	
;	Read or write the parameter RAM associated with the given slot number.  These
;	routines handle both internal and external slot PRAM.
;
;
;	Input	: reg A0 = ptr to spBlock
;	Output	: reg D0 = status, 0=ok		CCR reflects status
;				  A0 = ptr to spBlock
;
;	For ReadSlotPRAM :
;			spBlock	:	-> spSlot			slot whose PRAM to read
;						-> spResult			pointer to 8 byte buffer to place PRAM data
;
;	For PutSlotPRAM and InitSlotPRAM :
;			spBlock	:	-> spSlot			slot whose PRAM to read
;						-> spsPointer		pointer to 8 byte buffer to place PRAM data
;

SlotPRAM	PROC
			EXPORT	ReadSlotPRAM
			EXPORT	PutSlotPRAM
			EXPORT	InitSlotPRAM
			WITH	spBlock,slotGlobals

ReadSlotPRAM
			MOVEQ	#0,D0							; clear reg D0 to indicate a read
			MOVE.L	spResult(A0),spParamData(A0)	; temp set = ptr to buffer to read to
			BRA.S	CommonPRAM					; goto shared routine for read and write
			
PutSlotPRAM
			MOVEQ	#1,D0						; set reg D0 to indicate a write
			MOVE.L	spsPointer(A0),spParamData(A0)	; temp set = ptr to bufr to write from
			BRA.S	CommonPRAM					; goto shared routine for read and write

InitSlotPRAM
			MOVEQ	#2,D0						; set reg D0 to indicate executive write
			MOVE.L	spsPointer(A0),spParamData(A0)	; temp set = ptr to buf to write from

CommonPRAM
@savedregs	Reg		d1/d3/a0-a1					;												<8>
			movem.l	@savedregs,-(sp)			; save regs
			CLR.W	D3
			MOVE.L	D0,D1						; D1 = indicates which routine executing
			MOVE.B	spSlot(A0),D3				; D3 = slot number with high byte clear
			BNE.S	@CheckRange					; not slot zero

; <SM2> rb, Start
;	Check for slot zero and convert slot number to relative slot to index into PRAM.  On		<1.2>
;	RBV machines, slot zero steals the PRAM from one of the 9-$E slots.  The slot mgr			<1.2>
;	is only assigned enough PRAM for 6 slots.													<1.2>
		
			WITH 	VideoInfo					; <SM3> rb
			movea.l	UnivInfoPtr,a1					; point to the ProductInfo record			<1.9>
			adda.l	ProductInfo.VideoInfoPtr(a1),a1	; point to the VideoInfo record				<1.9>
			move.b	VideoInfo.SlotNumberAlias(a1),d3; get the alias for slot zero PRAM			<1.9>

@CheckRange										; not slot zero
			MOVEA.L	spParamData(A0),A1			; A1 = ptr to buf to read to or write from		<1.9>

;	Check range for internal slots with PRAM on the motherboard

			MOVE.W	#smSlotOOBErr,D0			; assume error
			CMP.B	#sLastSlot,D3
			BHI.S	@Done						; slot number out of range
			CMP.B	#FirstPRAMSlot,D3			; a motherboard slot?
			bge.s	@InternalPRAM				; good slot - get PRAM, <SM2> rb, End

;	Slot number is in the expansion chassis range.  If an expansion chassis is present,		<1.5>
;	call a vendor routine, to handle PRAM requests.

			TST.W	([sInfoPtr],expnSlot)		; is there an expansion chassis ?
			BEQ.S	@Done						; nope - can't handle this slot's PRAM request
;	*****	Add code to look for and call PRAM handling code - use sExec ?	*****
			BRA.S	@Done						; should never get to this point

;	Setup parameters to read or write PRAM from motherboard

@InternalPRAM
			SUB.B	#FirstPRAMSlot,D3			; convert slot to relative index
			MULU.W	#SizesPRAMRec,D3			; index to PRAM record for slot
			ADD.W	#SMPRAMTop,D3				; add offset to slot manager PRAM 

			MOVE.L	#SizesPRAMRec<<16,D0		; move number of bytes to read into high word
			MOVE.W	D3,D0						; move offset in PRAM into low word
			movea.l	a1,a0						; setup A0 = ptr to buffer or data			<2.1>
			TST.B	D1							; is this a read or write?
			BNE.S	@WritePRAM					; non-zero - must be a write
			
			_ReadXPRAM
			BRA.S	@Done
			
@WritePRAM
			subq.l	#2,d1						; regular write or executive write?			<2.1>
			BEQ.S	@DoWrite					; executive write
			ADDQ.L	#2,A0						; skip over board and record id
			sub.l	#$20000-2,d0				; sub two from number of bytes to write (in high word)	<2.1>
												; add two to offset into PRAM, pass board and record id's
@DoWrite	_WriteXPRAM

@Done		movem.l	(sp)+,@savedregs			; restore ptr to spBlock
			RTS									; done


;_______________________________________________________________________________________	<1.5>
;	FindsInfoRecPtr - find sInfo record ptr
;
;	Given the slot number in an spBlock, return a pointer to the sInfo record in the
;	spResult field.  The pointer is retrieved from the sInfo vector table.
;
;	Input	: reg A0 = ptr to spBlock
;	Output	: reg D0 = status, 0=ok		CCR reflects status
;				  A0 = ptr to spBlock
;
;	spBlock	:	-> spSlot		slot number
;				<- spResult		ptr to sInfo record
;

FindsInfoRecPtr		PROC	EXPORT
			WITH	spBlock
				
;  Verify slot is good
			
			moveq.l	#0,d0							; clear register						<2.0>
			MOVE.B	spSlot(A0),D0					; D0 = slot number
			CMPI.B	#sLastSlot,D0
			bhi.S	@Error							; error - slot number out of range
							
;  Calculate the pointer to the desired record

			MOVE.L	([sInfoPtr],d0.w*4),spResult(A0)	; get ptr to sInfo record
			MOVEQ	#0,D0							; sInfo record present - return good status
			BRA.S	@Done
			
@Error		MOVE.W	#smSlotOOBErr,D0				; set error return
@Done		RTS										; end - FindsInfoRecPtr
			ENDP


;_______________________________________________________________________________________	<1.4>
;	GetsRsrcPtr  - find the pointer to an sResource list
;	FindsRsrcPtr - find the pointer to an enabled sResource List
;
;	Given the <slot><id><extdev>, return the pointer to the sResource list.  This routine
;	differs from the original slot manager in that the sResource list pointer is fetched
;	from the SRT instead of the sResource directory on the card.  This change allows
;	fetching pointers to replaced or RAM sResources.
;
;	Input	: reg A0 = ptr to spBlock
;	Output	: reg D0 = status, 0=ok		CCR reflects status
;				  A0 = ptr to spBlock
;
;	spBlock	:	-> spSlot			slot number
;				-> spId				sResource id
;				<- spsPointer		ptr to sResource list
;
;	GetsRsrcPtr Only :
;				-> spExtDev			external device id
;				->	spParamData		input flag filed
;									bit 0 set = include disabled sResources in search
;										  clr = ignore disabled sResources
;									bit 1 set = restrict search to single given slot
;										  clr = search all slots
;									bit 2 set = search for next <slot><id><extdev>
;										  clr = search for given <slot><id><extdev>
;

FindsRsrcPtr	PROC	EXPORT
			EXPORT	GetsRsrcPtr

			WITH	spBlock,srrBlock

			CLR.B	spExtDev(A0)					; not required by old slot mgr
			CLR.L	spParamData(A0)					; clear flags field
GetsRsrcPtr
			MOVE.L	A1,-(SP)						; save reg
			_FindSRTRec								; get ptr to SRT entry
			BNE.S	@Done							; not found - error
			MOVEA.L	spsPointer(A0),A1				; A1 = ptr to SRT entry
			MOVE.L	srrsRsrcPtr(A1),spsPointer(A0)	; return ptr to sRsrc
@Done		MOVEA.L	(SP)+,A1
			RTS
			


;_______________________________________________________________________________________	<2.3>
;	PtrToSlot  -  map sPointer to slot number
;
;	Given a valid pointer to a slot, map it to the slot number.  Both major and minor
;	NuBus address space pointers are mapped.  The minor slot number must be a 32bit
;	NuBus pointer.  This routine map change to map into an array of valid slots to
;	take into account bus expansion chassis's.  A pointer within the size of
;	ROMBase (assumed in ROM) is translated into slot 0.
;
;	Input	: reg A0 = ptr to spBlock
;	Output	: reg D0 = status, 0=ok		CCR reflects status
;				  A0 = ptr to spBlock
;
;	spBlock	:	 -> spsPointer			the sPointer
;				<-	spSlot				slot number
;
PtrToSlot	Proc	Export
			With	spBlock

			movem.l	d1-d2,-(sp)						; <SM2> rb

;  Verify a good sPointer
													; <SM2> rb, Start
			MOVE	#smsPointerNil,D0				; assume nil ptr - set error status
			MOVE.L	spsPointer(A0),D1				; D1 = sPointer
			BEQ.S	@Done							; nil pointer - error

;  Determine the slot

			clr.b	d1								; clear low byte of pointer				<2.1>
			ROL.L	#4,D1							; determine if major or minor space
			CMP.B	#$0F,D1							; minor space?
			BEQ.S	@minor							; minor slot space

;	Super slot space - if the high nibble is the same as ROMBase, then this is an access to
;	the host ROM's declaration data (or ram).  Translate it into slot 0.

			MOVE.B	ROMBase,D2						; D2 = high byte of ROMBase
			lsr.b	#4,d2							; shift high nib to low nibble			<2.1>
			CMP.B	D2,D1							; same?
			BHI.S	@FoundSlot						; must be other super slot space
			
;	Pointer is to ram or rom.  Translate it to slot 0.
			
			MOVEQ	#0,D1							; access to host ROM - translate to slot 0
			BRA.S	@FoundSlot

@minor		clr.b	d1								; sxxxxx0F to sxxxxx00					<2.1>
			ROL.L	#4,D1							; minor space - reg D1 = xxxxx00s

@FoundSlot	MOVE.W	#smSlotOOBErr,D0				; assume out of bounds
			CMP.B	#sLastSlot,D1					; good slot number?
			BHI.S	@Done							; must be slot $0F which is bad

			MOVE.B	D1,spSlot(A0)					; return slot number
			MOVEQ	#0,D0							; set good return
			
@Done		TST		D0								; set condition codes
			movem.l	(sp)+,d1-d2
			RTS										; done - PtrToSlot
			ENDP									; <SM2> rb, End

;_______________________________________________________________________________________
;	ReadFHeader  -  copy a declaration ROM's format header block
;
;	Copy a declaration ROM's format header block into a buffer.  The pointer to the
;	buffer is an argument.
;
;	Input	: reg A0 = ptr to spBlock
;	Output	: reg D0 = status, 0=ok		CCR reflects status
;				  A0 = ptr to spBlock
;
;	spBlock	:	 -> spSlot			slot to read
;				<->	spResult		pointer to buffer
;
ReadFHeader	PROC	EXPORT
			WITH	spBlock,sInfoRecord,FHeaderRec

			movem.l	a1-a2,-(sp)
			MOVE.L	spResult(A0),A2						; A2 = save ptr to buffer
			
			_sFindsInfoRecPtr							; ret - spResult(A0) = ptr to sInfo record	
			BNE.S	@Done								; error - bad slot

			MOVE.L	spResult(A0),A1						; A1 = ptr to sInfo record
			MOVE.W	siInitStatusA(A1),D0				; is there a card installed ?		<1.5>
			BMI.S	@Done								; bad status - error				<1.5>

;	Offset the ROM ptr to the bottom of the format block

			MOVE.L	siROMAddr(A1),spsPointer(A0)		; set ptr to top of ROM
			MOVE.L	#-fhBlockSize+1,spOffsetData(A0)	; set offset to move ptr
			MOVE.B	siCPUByteLanes(A1),spByteLanes(A0)	; set byte lanes field
			_sCalcsPointer
			BNE.s	@Done								; some error

;	Copy format block into RAM

			MOVE.L	A2,spResult(A0)						; set buffer pointer
			MOVE.L	#fhBlockSize,spSize(A0)				; number of bytes to copy
			_sReadStruct

@Done		movem.l	(sp)+,a1-a2
			RTS											; done - ReadFHeader
			ENDP


;_______________________________________________________________________________________	<1.4>
;	CkCardStat  -  check a card's status
;
;	Check a card's status in the sInfo array.  If there is no entry for the slot, then
;	the card is not installed.  If the initStatusA field of the sInfo record is negative,
;	then the card status is also bad.
;
;	Input	: reg A0 = ptr to spBlock
;	Output	: reg D0 = status, 0=ok		CCR reflects status
;				  A0 = ptr to spBlock
;
;	spBlock	:	 -> spSlot			slot to get status
;
CkCardStat	PROC	EXPORT
			WITH	spBlock,sInfoRecord

			MOVE.L	A2,-(SP)
			_sFindsInfoRecPtr						; get ptr to sInfo record
			BNE.S	@CardBad						; error - card not installed
			MOVEA.L	spResult(A0),A2					; A2 = ptr to sinfo record
			TST.W	siInitStatusA(A2)				; check card's status field
			BMI.S	@CardBad						; error - card status is bad
			MOVEQ	#0,D0							; card is ok
			BRA.S	@Done
				
@CardBad	MOVE.L	#smCkStatusErr,D0				; set bad card status
@Done		MOVE.L	(SP)+,A2
			RTS										; done - CkCardStat


;_______________________________________________________________________________________	<1.3>
;	SlotVersion  -  return slot manager version number
;
;	Return the slot manager version number.  The Mac II 1.0 and 1.2 ROM's are considered
;	version 1.  The version in the Cobra II is version 2.
;
;	Input		: reg A0 = ptr to spBlock
;	Output		: reg A0 = ptr to spBlock
;
;	spBlock		:	<- spResult		version number (long)
;					<- spPointer	nil (in case of future use)
;
SlotVersion	PROC	EXPORT
			WITH	spBlock

			if forROM then
			MOVE.L	#slotVNum,spResult(A0)
			else
			move.l	#1,spResult(A0)
			endif
			CLR.L	spsPointer(A0)
			MOVEQ	#0,D0						; set a good return
			RTS

;_______________________________________________________________________________________
;	GetsRsrc  -  get sResource by id
;	NextsRsrc -  get next visible sResource by id
;
;	Return information on the next sResource starting from the given slot number and
;	sResource id.  Unused fields in the spBlock are set to zero.  The difference between
;	GetsRsrc and NextsRsrc, is that NextsRsrc only includes enabled (visible) entries
;	in the search. GetsRsrc uses spParamData as a flags field to indicate the type of
;	enabled and disabled entries in the search.
;
;	Input	: reg A0 = ptr to spBlock
;	Output	: reg D0 = status, 0=ok		CCR reflects status
;				  A0 = ptr to spBlock
;
;	spBlock	:	<-> spSlot			slot to start looking in
;				<->	spId			id to start looking from
;				<->	spExtDev		external device identifier
;				<-	spsPointer		pointer to sResource record
;				<-	spRefNum		driver reference number (if applicable)
;				<-	spIOReserved	
;				<-	spCategory		sType fields
;				<-	spCType
;				<-	spDrvrSW
;				<-	spDrvrHW
;				<-	spHWDev			hardware device id (if applicable)
;
;	For GetsRsrc only :
;				 ->	spParamData		input flag filed
;									bit 0 set = include disabled sResources in search
;										  clr = ignore disabled sResources
;									bit 1 set = restrict search to single given slot
;										  clr = search all slots
;									bit 2 set = search for next <slot><id><extdev>
;										  clr = search for given <slot><id><extdev>
;
;				 <- spParamData		state of sResource: 0=enabled, 1=disabled
;
NextsRsrc	PROC	EXPORT
			EXPORT	GetsRsrc
			WITH	sInfoRecord,srrBlock,spBlock,SlotRsrcType,srtLink,slotGlobals
			
			CLR.L	spParamData(A0)					; clear optional flags
			BSET.B	#fnext,spParamData+3(A0)		; search for next SRT entry	

GetsRsrc
			MOVE.L	A1,-(SP)

			_FindSRTRec								; search table
			BNE.S	@Error							; some error or not found

;	sResource entry found - set parameters and exit

			MOVEA.L	spsPointer(A0),A1				; pass A1 = ptr to SRT entry
			slotjsr	SrToSpBlock						; fill in spBlock with srrBlock values
			BRA.S	@Done

;	sResource was not found

@Error		MOVE.W	#smNoMoresRsrcs,D0				; set error return
@Done		MOVE.L	(SP)+,A1
			TST.W	D0
			RTS										; done  -  NextsRsrc


;_______________________________________________________________________________________	<1.5>
;	GetTypesRsrc  -  get sResource by type
;	NextTypesRsrc -  get next visible sResource by type
;
;	Search for an sResource by its type fields - <category><type><drvrSW><drvrHW>.  Mask
;	the type fields appropriately by the mask given in the type mask field of the spBlock.
;	Return information on the next sResource starting from the given slot number and
;	sResource id.  Unused fields in the spBlock are set to zero.  The difference between
;	GetTypesRsrc and NextTypesRsrc, is that NextTypesRsrc only includes enabled (visible)
;	entries in the search. GetTypesRsrc uses spParamData as a flags field to indicate the type
;	of enabled and disabled entries in the search.
;
;	Input	: reg A0 = ptr to spBlock
;	Output	: reg D0 = status, 0=ok		CCR reflects status
;				  A0 = ptr to spBlock
;
;	spBlock	:	 -> spTBMask		sType mask (bit 0-3) - bit set means mask off this field
;				<-> spSlot			slot to start looking in
;				<->	spId			id to start looking from
;				<->	spExtDev		external device identifier
;				<-	spHWDev			hardware device id (if applicable)
;				<-	spsPointer		pointer sResource list (*****different than old slot manager)
;				<-	spRefNum		driver reference number (if applicable)
;				<-	spIOReserved	
;				<-	spCategory		sType fields
;				<-	spCType
;				<-	spDrvrSW
;				<-	spDrvrHW
;
;	For GetTypesRsrc only :
;				 ->	spParamData		input flag filed
;									bit 0 set = include disabled sResources in search
;										  clr = ignore disabled sResources
;									bit 1 set = restrict search to single given slot
;										  clr = search all slots
;
;				<-  spParamData		state of sResource: 0=enabled, 1=disabled
;
			EXPORT	NextTypesRsrc,GetTypesRsrc

NextTypesRsrc
			CLR.L	spParamData(A0)					; clear optional flags

GetTypesRsrc
			MOVEM.L	D1-D6/A1,-(SP)
			
;	Use the type mask field in the spBlock as an index into a table to get the
;	bit mask for the sType fields <category><type><drvrsw><drvrhw>

			MOVEQ	#0,D0							; clear for use as index
			MOVE.B	spTBMask(A0),D0					; D0 = index (mask value)
			LEA		MaskTbl,A1						; addr of table in reg A1
			LSR.B	#2,D0							; use bits <3,2> as index
			MOVE.L	(A1,D0.W*4),D1					; D1 = mask for <category><type>
			MOVE.B	spTBMask(A0),D0					; D0 = index (mask value)
			AND.B	#3,D0							; use low 2 bits
			MOVE.L	(A1,D0.W*4),D2					; D2 = mask for <drvrsw><drvrhw>
			MOVE.L	spCategory(A0),D3				; get LONG, D3 = <category><type> to match
			MOVE.L	spDrvrSW(A0),D4					; get LONG, D4 = <drvrsw><drvrhw> to match
			AND.L	D1,D3							; mask target <category><type>
			AND.L	D2,D4							; mask target <drvrsw><drvrhw>

;	Search SRT comparing the type fields of the entries to the type we are looking
;	for.  Mask the entry's type field before doing the compare.
;	reg D1 = <cat><type> mask, D2 = <drvrsw><drvrhw> mask
;	reg D3 = <cat><type> to match, D4 = <drvrsw><drvrhw> to match

@Loop		BSET.B	#fnext,spParamData+3(A0)	; search for next SRT entry
			_FindSRTRec							; search table
			BNE.S	@Done						; no more good entries in the SRT - failed
			
			MOVE.L	spsPointer(A0),A1			; get A1 = ptr to SRT entry
			MOVE.L	srrSlot(A1),spSlot(A0)		; update spSlot,spId,spExtDev,spHwDev
			MOVE.L	D1,D5						; get D5 = copy of <category><type> mask
			AND.L	srrCategory(A1),D5			; mask <cat><type> into D5
			CMP.L	D3,D5						; <does <category><type> match ?
			BNE.S	@Loop						; does not match - get next entry
			MOVE.L	D2,D6						; get D6 = copy of <drvrsw><drvrhw> mask
			AND.L	srrDrvrSw(A1),D6			; mask <drvrsw><drvrhw> into D6
			CMP.L	D4,D6						; does <drvrsw><drvrhw> fields match ?
			BNE.S	@Loop						; no match - get next entry

;	Found the entry - return the sResource information

			slotjsr	SrToSpBlock					; fill in spBlock with srrBlock values
			MOVEQ	#0,D0						; set good return
			
@Done		MOVEM.L	(SP)+,D1-D6/A1
			RTS


;_______________________________________________________________________________________	<1.3>
;	Mask table for NextTypesRsrc
;
;	This is a table of bit masks for the <category><type><drvrSW><drvrHW> fields of
;	an sResource.  The table is indexed by the spTBMask field of the spBlock.  Bits
;	<1,0> are used as an index for the <drvrSW><drvrHW> mask. Bits <3,2> are used as
;	an index for <category><type> field.
;

MaskTbl		DC.W	$FFFF,$FFFF						; index 00
			DC.W	$FFFF,$0000						;		01
			DC.W	$0000,$FFFF						;		10
			DC.W	$0000,$0000						;		11
			

;_______________________________________________________________________________________	<1.5>
;	UpdateSRT  -  add or update an entry in the slot resource table
;	InsertSRTRec  -  add a ROM or RAM sResource to the slot resource table
;
;	UpdateSRT adds or updates an entry in the SRT based on spSlot, spId, and spExtDev.  If
;	the slot-id-extdev are found in the SRT, then the RefNum and IOReserved fields are
;	updated.  If the entry is not found, then a new record is added to the table with
;	spSlot, spId, spExtDev, spRefNum, and spIOReserved.  In addition to these fields,
;	srrsRsrcPtr, srrType, and srrHWDev are updated by reading their values from the
;	appropriate declaration ROM.  Updates may be made to enabled sResources only.
;
;	InsertSRTRec is similar to UpdateSRT except it uses the spsPointer field as an argument.
;	If the spsPointer field is nil, then an sResource is added to the SRT if it exist
;	in the sResource directory (based on slot-id-extdev) in the declaration ROM.  If
;	spsPointer is not nil, then assume we are adding a RAM sResource (not implemented yet).
;	Also accept flags to indicate whether the sResource should be enabled or disabled.
;
;	There are 5 cases to consider in the shared code :
;		UpdateSRT :
;			(1)  Add a new sRsrc dir entry to SRT
;			(5)  Modify an existing SRT entry
;
;		InsertSRT :
;			(1)  Add a new sRsrc dir entry to SRT							(spsPointer == nil)
;			(2)  Add a new RAM sRsrc (not in sRsrc dir) to SRT				(spsPointer == RAM addr)
;			(3)  Replace an SRT entry with a new RAM sRsrc					(spsPointer == RAM addr)
;			(4)  Add a new RAM sRsrc (replacing one in sRsrc dir) to SRT	(spsPointer == RAM addr)
;
;	In case (3), when replacing an sResource, if there is a reference number
;	(i.e. a driver) associated with the sResource we are replacing, then recalculate
;	the dCtlDevBase field in the driver's DCE
;
;
;	Input	: reg A0 = ptr to spBlock
;	Output	: reg D0 = status, 0=ok		CCR reflects status
;				  A0 = ptr to spBlock
;
;	For UpdateSRT and InsertSRT :
;			spBlock :	-> spSlot			slot number
;						-> spId				sResource id
;						-> spExtDev			sResource external device id
;						-> spRefNum			reference number of driver for sResource
;						-> spIOReserved		reserved field
;
;	In addition for InsertSRT only :
;						-> spsPointer		if nil then ROM sResource, else RAM sResource
;						-> spParamData		(formerly spStackPtr) 1 = disable, 0 = enable sResource
;
			EXPORT	UpdateSRT,InsertSRT
			
UpdateSRT
			MOVEQ	#1,D0					; reg D0 = 1 indicates UpdateSRT
			CLR.L	spsPointer(A0)			; nil to simulate AddSRTRec call for add-new case
			CLR.L	spParamData(A0)			; do not disable the sResource
			BRA.S	ModifySRT
InsertSRT
			MOVEQ	#0,D0					; reg D0 = 0 indicates InsertSRTRec

;	Begin shared code for both UpdateSRT and InsertSRTRec

ModifySRT
			MOVEM.L	D1-D2/A1-A2,-(SP)		; save registers
			MOVE.L	D0,D1					; D1 = flag indicating Update or Insert
			MOVEA.L	spsPointer(A0),A2		; save reg A2 = spsPointer (ptr to RAM sRsrc or nil)
			MOVE.L	spParamData(A0),D2		; save enable/disable flag to set

;	Search the SRT for the <spSlot><spId><spExtDev> to see if the sResource already exists.
			
			CLR.L	spParamData(A0)			; clear flags to FindSRTRec
			BSET.B	#fall,spParamData+3(A0)	; set flag to look for all sRsrc's
			_FindSRTRec	
			BEQ.S	@Found					; sRsrc found - must be an update or a replacement
											; <case 3 or 5>

;	At this point, the <spSlot><spId><spExtDev> is not in the SRT   Add the sRsrc to the SRT.
;	<case 1,2, or 4>

			MOVE.L	A2,spsPointer(A0)		; restore ptr to RAM sRsrc or nil
			MOVE.L	D2,spParamData(A0)		; restore spParamData = enable/disable flag
			slotjsr	NewSRTEntry
			BRA.S	@Done

;	Doing update or replacement

@Found		MOVEA.L	spsPointer(A0),A1		; A1 = ptr to SRT entry, A2 = ptr to RAM sRsrc
			TST.W	D1						; are we executing Insert or Update SRT?
			BEQ.S	@Replace				; doing Insert - replace SRT entry with a RAM sRsrc

;	Doing an update on an existing entry <case 5>.  Check that the entry is enabled

			MOVE.W	#smNoMoresRsrcs,D0					; set error return in case
			BTST.B	#srdisable,srrFlags+3(A1)			; is this an enabled or disabled entry ?
			BNE.S	@Done								; error - trying to update a disabled entry
			MOVE.W	spIOReserved(A0),srrIOReserved(A1)	; do update of existing entry  <case 5>
			MOVE.W	spRefNum(A0),srrRefNum(A1)
			MOVEQ	#0,D0								; set good return status
			BRA.S	@Done
			
;	Replace an existing sRsrc entry in the SRT with a new RAM sRsrc.
;	<case 3>

@Replace	move.l	a2,d0					; is the ptr to the ram sRsrc nil ?				<1.7>
			beq.s	@Done					; yes - ignore this case - leave existing entry	<1.7>
			MOVE.L	A2,spsPointer(A0)		; pass ptr to RAM sResource
			MOVE.L	D2,spParamData(A0)		; restore spParamData = enable/disable flag
			slotjsr	InitEntry				; pass reg A1 = ptr SRT entry to replace
;			BNE.S	@Done					; some error

@Done		MOVEM.L	(SP)+,D1-D2/A1-A2
			RTS

			

;_______________________________________________________________________________________	<1.5>
;	FindSRTRec	-  search the slot resource table for an sResource
;	SearchSRT   -  search the SRT for visible sResource entries
;
;	Search the slot resource table (SRT) for an sResource identified by the <slot number>
;	<sResource id> and <ExtDev id> fields.  Optionally return a pointer either to the
;	found SRT entry, or to the entry immediately following the found entry.
;
;	FindSRTRec accepts a flag indicating whether the search sould include disabled
;	(invisible) sResource entries in the SRT.  SearchSRT is the original slot manager
;	_sSearchSRT documented in Inside Mac vol 5, and it only searches visible entries.
;
;	Input	: reg A0 = ptr to spBlock
;
;	Output	: reg D0 = status, 0=ok		CCR reflects status
;
;	spBlock	:	-> spSlot				slot number of sResource
;				-> spId					id of sResource
;				-> spExtDev				external device id of sResource
;				-> spFlags				bit 0 clear means return ptr for the given sResource
;											  set means return ptr for the following sResource
;			   <-  spsPointer			returns pointer to found or next entry in SRT.
;
;	FindSRTRec only	:
;				 ->	spParamData		input flag filed
;									bit 0 set = include disabled sResources in search
;										  clr = ignore disabled sResources
;									bit 1 set = restrict search to single given slot
;										  clr = search all slots
;									bit 2 set = search for next <slot><id><extdev>
;										  clr = search for given <slot><id><extdev>
;

			EXPORT	SearchSRT,FindSRTRec

SearchSRT
			CLR.L	spParamData(A0)				; clear flag field for FindSRTRec
			BTST.B	#fCkForNext,spFlags(A0)		; test for next search flag
			BEQ.S	FindSRTRec					; not set - go to shared code
			BSET.B	#fnext,spParamData+3(A0)	; transfer flag to new flag field

FindSRTRec
			MOVEM.L	D2-D4/A1-A3,-(SP)

			MOVEA.L	sRsrcTblPtr,A1				; get ptr to beginning of SRT
			MOVE.L	spSlot(A0),D2				; get <slot><id><extdev><hwdev> to search for
			CLR.B	D2							; zero <hwdev> - not included in search key
			MOVEA.L	A1,A2						; init ptr to "found" entry
			MOVEA.L	A1,A3						; init ptr to a "next" entry
			MOVE.L	MinusOne,D3					; init a large "next" <slot><id><extdev> entry

;	Loop and search each entry in the SRT blocks for the given <slot><id><extdev>.
;	Reg A1 = ptr to current SRT entry
;		A2 = ptr to "found" entry, D2 = "found" <slot><id><extdev>
;		A3 = ptr to "next" SRT entry, D3 = "next" <slot><id><extdev>

@Loop		BSR		GetSRTEntry					; return A1 = ptr to good entry
			BNE.S	@NoMore						; no more good entries in the SRT - done
			MOVE.L	srrSlot(A1),D4				; get <slot><id><extdev><hwdev>
			CLR.B	D4							; clear <hwdev>
			CMP.L	D2,D4						; are we looking for this one ?
			BGT.S	@ChkNext					; no match - try "next" entry
			BLT.S	@EndLoop					; no match and does not qualify for a "next" entry
			
			MOVEA.L	A1,A2						; matches - set ptr to "found" entry
			BTST.B	#fnext,spParamData+3(A0)	; are we looking for the "next" entry ?
			BEQ.S	@Done						; not looking for "next" - we are done
			BRA.S	@EndLoop					; continue looking for the "next" one

@ChkNext	CMP.L	D3,D4						; check against current "next" entry
			BHI.S	@EndLoop					; nope - greater than existing "next"
			MOVEA.L	A1,A3						; found new "next entry - update registers
			MOVE.L	D4,D3
			
@EndLoop	ADDA.W	#SRTRecSize,A1				; inc SRT entry ptr to next entry
			BRA.S	@Loop						; continue looking

;	Did not find the entry. Reg A3 = ptr to "next" valid SRT entry in the table.  If we
;	are set to search for the next sResource, then we are ok.

@NoMore
			TST.L	D3							; did we ever update D3 ("next" <slot><id><extdev> ?
			BMI.S	@Error						; nope - there is no "next" entry
			MOVEQ	#0,D0						; setup a good return
			MOVEA.L	A3,A2						; return ptr to "next" entry
			BTST.B	#fnext,spParamData+3(A0)	; check "for next" bit
			BEQ.S	@Error						; error - could not find specific entry

;	A1 = valid ptr to NEXT entry.  If the oneslot bit is set, then check if it is
;	the same as the given slot number.

@CheckSlot
			BTST.B	#foneslot,spParamData+3(A0)	; restrict search to one slot ?
			BEQ.S	@Done						; no - we are done
			MOVE.B	spSlot(A0),D1				; get D1 = given slot
			CMP.B	srrSlot(A2),D1				; still in same slot ?
			BEQ.S	@Done						; yes - we are done
			
@Error		MOVE.W	#smNoMoresRsrcs,D0			; error - could not find entry in slot

@Done		MOVE.L	A2,spsPointer(A0)			; return ptr to SRT entry
			MOVEM.L	(SP)+,D2-D4/A1-A3
			TST.W	D0
			RTS
			

;_______________________________________________________________________________________	<1.6>
;	DeleteSRTRec  -  delete a given sResource entry from the SRT
;
;	Search the slot resource table (SRT) for an sResource identified by the <slot number>
;	<sResource id> and <ExtDev id> fields.  If found, then remove it from the table.
;	This routine will delete an enabled or disabled entry from the SRT.
;
;	Entries are removed by moving the last entry in the table to the deleted location.
;	This keeps the SRT as a compacted (unordered) list.  There is a pointer to the
;	last entry stored in the slot manager globals.  If after moving the last entry, the
;	last block becomes free, then the block is freed.  Because there is no back link
;	pointer, the SRT must be travered from the beginning to the new last block, so that
;	it's link pointer field may be nil-ed.
;
;	Input	: reg A0 = ptr to spBlock
;
;	Output	: reg D0 = status, 0=ok		CCR reflects status
;
;	spBlock	:	-> spSlot				slot number of sResource
;				-> spId					id of sResource
;				-> spExtDev				external device id of sResource
;

			EXPORT	DeleteSRTRec

DeleteSRTRec
			MOVEM.L	A0-A2,-(SP)

;	Find the sResource entry in the SRT
			
			CLR.L	spParamData(A0)				; clear flags to FindSRTRec
			BSET.B	#fall,spParamData+3(A0)		; set flag to look for all sRsrc's
			_FindSRTRec
			BNE		@Error						; sRsrc not found - done

;	Mark the entry as being free (just in case)

			MOVEA.L	spsPointer(A0),A1			; A1 = ptr to found entry
			MOVE.W	#srtfree,srrSlot(A1)		; place free tag at beginning of srrblock

;	Compact the SRT by copying the last entry to the free space

			MOVEA.L	([sInfoPtr],lastSRTPtr),A2	; A2 = ptr to last entry
			MOVE.W	#SRTRecSize-1,D0			; entry size (adjusted for dbra)
@Loop		MOVE.B	(A2)+,(A1)+					; copy last entry to freed entry
			DBRA	D0,@Loop

;	Determine whether the last block in the SRT chain is empty and can be freed.

			MOVEA.L	([sInfoPtr],lastSRTPtr),A2	; get ptr to last entry again
			MOVE.W	#srtfree,srrSlot(A2)		; place free tag at beginning of srrblock
			SUBQ.W	#1,([sInfoPtr],srtCnt)		; dec total srt cnt
			MOVE.W	([sInfoPtr],srtCnt),D0		; get new cnt
			DIVU.W	#srtMaxEntries,D0			; divide total cnt by num entries per blk
			AND.L	#$FFFF0000,D0				; D0 = <remainder><quotient>, any remainder ?
			BNE.S	@Good						; still entries left in last blk - done

;	Last SRT block is empty - free the block.  Traverse SRT to end to nil the link ptr.

@Free		MOVE.B	mmu32Bit,-(SP)				; save current mmu state <SM2> rb
			MOVE.B	#false32b,D0				; <SM2> rb
			_SwapMMUMode						; switch to 24 bit mode <SM2> rb
			MOVEA.L	A2,A0						; A0 = ptr to last SRT blk
			_DisposPtr							; free the block
			MOVE.B	(SP)+,D0					; recover status <SM2> rb
			_SwapMMUMode						; restore mmu state <SM2> rb
			MOVEA.L	sRsrcTblPtr,A1				; get ptr to beginning of SRT
@Loop1		ADDA.W	#srtBlkSize,A1				; inc to end of blk
			CMPA.L	srtNext(A1),A2				; found end of link ?
			BEQ.S	@Found						; found new last blk
			MOVEA.L	srtNext(A1),A1				; advance to next blk
			BRA.S	@Loop1						; continue

@Found		CLR.L	srtNext(A1)					; nil the link field
			MOVEA.L	A1,A2						; get ptr into A2 for the following shared code

@Good		SUB.W	#SRTRecSize,A2				; dec ptr to previous entry
			MOVE.L	A2,([sInfoPtr],lastSRTPtr)	; update new last ptr
			MOVEQ	#0,D0						; a good return
			BRA.S	@Done

@Error		MOVE.W	#smRecNotFnd,D0				; same error code as old slot manager

@Done		MOVEM.L	(SP)+,A0-A2
			RTS


;_______________________________________________________________________________________	<1.7>
;	GetsRsrcInfo  -  get sResource information
;	SlotRsrcInfo  -  get visible sResource information
;
;	Given an sResource's <slot><id><extdev>, find the sResource's SRT entry and return
;	some of the information stored in the SRT entry.
;
;	Input	: reg A0 = ptr to spBlock
;	Output	: reg D0 = status, 0=ok		CCR reflects status
;				  A0 = ptr to spBlock
;
;	spBlock	:	 -> spSlot			slot number
;				 ->	spId			sResource id
;				 ->	spExtDev		external device id
;				<-	spHWDev			hardware device id (if applicable)
;				<-	spsPointer		pointer sResource list (*****different than old slot manager)
;				<-	spRefNum		driver reference number (if applicable)
;				<-	spIOReserved	
;				<-	spCategory		sType fields
;				<-	spCType
;				<-	spDrvrSW
;				<-	spDrvrHW
;
;	For GetsRsrcInfo only :
;				 ->	spParamData		input flag filed
;									bit 0 set = include disabled sResources in search
;										  clr = ignore disabled sResources
;
;				<-  spParamData		state of sResource: 0=enabled, 1=disabled
;
			EXPORT	SlotRsrcInfo,GetsRsrcInfo

SlotRsrcInfo
			CLR.L	spParamData(A0)				; clear flag field

GetsRsrcInfo
			MOVE.L	A1,-(SP)
				
			_FindSRTRec							; search for the SRT entry
			BNE.S	@Done						; error - sRsrc not found
			
			MOVEA.L	spsPointer(A0),A1			; pass A1 = ptr to SRT entry
			slotjsr	SrToSpBlock					; fill in spBlock with srrBlock values
			bra.s	@Done

@Error		move.w	#smRecNotFnd,d0				; set compatible error return
@Done		MOVEA.L	(SP)+,A1
			RTS

			

;_______________________________________________________________________________________
;	SetsRsrcState  -  enable or disable an sResource
;
;	Search the slot resource table (SRT) for a given sResource identified by <slot number>
;	<sResource id> and <ExtDev id> fields.  Set the flag in the SRT record to enable or
;	disable the sResource.
;
;	Input	: reg A0 = ptr to spBlock
;
;	Output	: reg D0 = status, 0=ok		CCR reflects status
;
;	spBlock	:	-> spSlot				slot number of sResource
;				-> spId					id of sResource
;				-> spExtDev				external device id of sResource
;				-> spParamData			0 = enable, 1 = disable
;

			EXPORT	SetsRsrcState				;											<1.5>

SetsRsrcState
			MOVEM.L	D1/A1,-(SP)
			MOVE.L	spParamData(A0),D1			; save enable/disable state to set
			CLR.L	spParamData(A0)				; clear flag field
			BSET.B	#fall,spParamData+3(A0)		; search all sResources
			_FindSRTRec	
			BNE.S	@Done						; sRsrc not found			
			MOVEA.L	spsPointer(A0),A1			; get A1 = ptr to SRT entry
			BCLR.B	#srDisable,srrFlags+3(A1)		; assume enabling the sResource
			TST.B	D1							; enabling or disabling ?
			BEQ.S	@Done						; enabling - we already did, so done
			BSET.B	#srDisable,srrFlags+3(A1)		; disable the sResource
			MOVEQ	#0,D0						; set good return
			
@Done		MOVE.L	D1,spParamData(A0)			; restore param data field
			MOVEM.L	(SP)+,D1/A1
			TST.W	D0
			RTS


;_______________________________________________________________________________________	<1.5>
;	pNewSRTEntry  -  Add a new record to the SRT
;
;	Add a new record entry to the slot resource table (SRT).  Insert the entry to the
;	proper place in the table.
;
;	Input	: reg A0 = ptr to spBlock
;
;	Output	: reg D0 = status, 0=ok		CCR reflects status
;
;	spBlock	:	-> spsPointer			ptr to RAM sRsrc, or nil
;				-> spParamData			enable/disable flag
;
			EXPORT	pNewSRTEntry

pNewSRTEntry
			MOVEM.L	D1/A1-A2,-(SP)

			MOVE.L	spsPointer(A0),D0		; test if there is a ptr to a RAM sRsrc
			BNE.S	@FindFree				; yes - add the SRT entry

;	Find the sResource in the ROM sResource directory, based on the <spSlot><spId><spExtDev>.
;	***** Under the old slot mgr, if the sRsrc could not be found, it was not an error.

			MOVE.L	spParamData(A0),D1			; save enable/disable flags
			_sFindsInfoRecPtr					; get ptr to sinfo record
			MOVEA.L	spResult(A0),A1				; A1 = ptr to sInfo record
			MOVE.L	siDirPtr(A1),spsPointer(A0)	; set ptr to ROM directory
			_sFindStruct						; search the ROM directory
			BNE.S	@Done						; can't find it - error (***** different from old slot mgr)
			MOVE.L	D1,spParamData(A0)			; restore flag

;	Find a free entry in the SRT.  If there is no free space left, then allocate and
;	link a new SRT block.

@FindFree	MOVEA.L	([sInfoPtr],lastSRTPtr),A1	; A1 = ptr to last entry in SRT				<1.6>

@Loop		CMPI.W	#srtfree,srrSlot(A1)		; free block ?
			BEQ.S	@AddEntry					; found a free SRT entry
			CMPI.W	#srtend,srrSlot(A1)			; end of current block ?
			BEQ.S	@NewBlock					; need to alloc a new blk					<1.6>
			ADDA.W	#SRTRecSize,A1
			BRA.S	@Loop

;	Allocate a new SRT block and link it in

@NewBlock	MOVEA.L	A1,A2						; save ptr to current blk
			slotjsr	AllocSRTBlk					; return A1 = ptr to new blk
			BNE.S	@Done						; memory error
			MOVE.L	A1,srtNext(A2)				; update link field

;	Initialize the new SRT entry  -  A1 = ptr to free SRT entry

@AddEntry	slotjsr	InitEntry
			BNE.S	@Done						; some error								<1.6>
			ADDQ.W	#1,([sInfoPtr],srtCnt)		; inc SRT count								<1.6>
			MOVE.L	A1,([sInfoPtr],lastSRTPtr)	; save ptr to last entry in SRT				<1.6>

@Done		MOVEM.L	(SP)+,D1/A1-A2				; restore regs
			RTS


;_______________________________________________________________________________________
;	pInitEntry  -  init an SRT entry
;
;	Initialize and fill in the fields of a slot resource table entry.
;
;	Input	: reg A0 = ptr to spBlock
;				  A1 = ptr to SRT entry to initialize
;
;	Output	: reg D0 = status, 0=ok		CCR reflects status
;
;	spBlock	:	-> spSlot				slot number
;				-> spId					sResource id
;				-> spExtDev				external device id
;				-> spRefNum				reference number or nil
;				-> spIOReserved			reserved value, or nil
;				-> spsPointer			pointer to sResource 
;				-> spParamData			enable/disable flag
;
			EXPORT	pInitEntry

pInitEntry
			MOVEM.L	D1/A2,-(SP)						; save regs
			SUBA.W	#SRSize,SP						; alloc size to read sRsrc sType field
			MOVEA.L	SP,A2							; save A2 = ptr to buffer

;	Fill in the fields of a new SRT entry

			MOVE.B	spSlot(A0),srrSlot(A1)			; slot number
			MOVE.B	spId(A0),srrId(A1)				; sRsrc id
			MOVE.B	spExtDev(A0),srrExtDev(A1)		; external device id
			CLR.B	srrHWDev(A1)					; clear hardware device id
			MOVE.W	spRefNum(A0),srrRefNum(A1)		; driver reference number
			MOVE.W	spIOReserved(A0),srrIOReserved(A1)	; IO reserved field (good for nothin?)
			MOVE.L	spsPointer(A0),srrsRsrcPtr(A1)	; set the ptr to the RAM or ROM sRsrc
			CLR.L	srrFlags(A1)					; clear flag field
			MOVE.L	spParamData(A0),D0				; test state of enable/disable value
			BEQ.S	@ReadStuff						; zero - sRsrc is marked as enabled
			BSET.B	#srDisable,srrFlags+3(A1)		; set flag to indicate disabled

;	Read the hardware sRsrc id

@ReadStuff	MOVE.B	#sRsrcHWDevId,spId(A0)			; get hardware id (if present)
			_sReadByte								; no error if not present
			BNE.S	@Continue
			MOVE.B	spResult+3(A0),srrHWDev(A1)		; only a byte value

;	Find and read the sRsrc type field <category><type><drvrSW><drveHW>

@Continue	MOVE.B	#sRsrcType,spId(A0)				; get ptr to sRsrc type
			_sFindStruct
			BNE.S	@Error							; error - sRsrc type field is required
			MOVE.L	#SRSize,spSize(A0)				; set size to read
			MOVE.L	A2,spResult(A0)					; set buffer ptr
			_sReadStruct
			BNE.S	@Error							; error - found but can't read ?
			MOVE.L	sCategory(A2),srrCategory(A1)	; copy <cat><type>
			MOVE.L	sDrvrSw(A2),srrDrvrSw(A1)		; copy <drvrSW><drvrHW>

;	If there is a valid reference number, then find the DCE and calculate the dCtlDevBase	<1.5>

			MOVEQ	#0,D0							; set a good return					<SM2> rb
			MOVE.W	spRefNum(A0),D1					; get ref num						<SM2> rb
			BEQ.S	@Done							; no ref num - done					<SM2> rb
			BSR.S	MapUnit							; return reg A2 = ptr to DCE		<SM2> rb
			BNE.S	@Error							; some error with ref num or DCE	<SM2> rb

;	Read a 24 or 32 bit base address from the sResource										<2.0>

;	Fixes a bug when calling _InsertSRTRec, inserting a ROM sRsrc with a valid
;	refnum, pInitEntry is called.  pInitEntry calls _sFindDevBase to calc a base
;	addr to place in the DCE.  The bug is _sFindDevBase is called with a bad
;	sRsrc id.  The fix is one line to restore the spId before calling
;	_sFindDevBase.

			move.b	srrId(a1),spId(a0)				; set spId of sRsrc to get base addr (the fix) <SM6>
			_sFindDevBase							; get base address
			BNE.S	@Error
			MOVE.L	spResult(A0),dCtlDevBase(A2)	; set the dCtlDevBase
			MOVE.B	srrId(A1),dCtlSlotId(A2)		; update sResource id					<1.6>
			MOVE.B	srrExtDev(A1),dCtlExtDev(A2)	; update the external device id			<1.6>
			BRA.S	@Done

;	Some error occurred - mark the SRT entry free again

@Error		MOVE.W	#srtfree,srrSlot(A1)

@Done		MOVE.L	srrsRsrcPtr(A1),spsPointer(A0)	; restore ptr to sRsrc
			ADDA.W	#SRSize,SP						; free sRsrc type buffer
			MOVEM.L	(SP)+,D1/A2						; restore regs
			TST.W	D0								; set CCR
			RTS
; <SM2> rb, from Zydeco...I give you MapUnit !
;_______________________________________________________________________________________	<1.5>
;	MapUnit  -  map a reference number to a pointer to a DCE
;
;	Given a driver reference number, get the pointer to it's DCE
;
;	Input	: reg D1 = reference number
;
;	Output	: reg A2 = ptr to DCE
;				  D0 = status, 0=ok		CCR reflects status
;

MapUnit
			MOVE.W	#rfNumErr,D0		; setup bad ref num error return
		 	NOT.W	D1					; bitwise complement to get unitnum
			BMI.S	@Done
			CMP.W	UnitNtryCnt,D1		; is it in range ?
			BGE.S	@Done	 			; skip if it's not

			ASL.W	#2,D1				; multiply by four
			MOVE.L	UTableBase,A2		; get address of unit table
			MOVE.L	0(A2,D1.W),D1		; add in the offset
			BEQ.S	@Done 				; branch if there is no driver installed

			MOVE.L	D1,A2				; DCE handle
			MOVE.L	(A2),D1 			; dereference handle
			BEQ.S	@SysErr				; DCE should never be purged - call sys error
			MOVE.L	D1,A2				; pointer to Device Control Entry
			MOVEQ	#0,D0				; init good return

@Done		TST.W	D0
			RTS

@SysErr		MOVEQ	#dsIOCoreErr,D0 	; deep shit IOCore error
			_SysError					; invoke deep shit

;_______________________________________________________________________________________	<2.0>
;	pAllocSRTBlk  -  allocate and initialize an SRT block
;
;	Allocate an SRT block.  Initialize all the SRT entries to free.
;
;	Input	: none
;
;	Output	: reg A1 = ptr to new SRT block
;				  D0 = status, 0=ok		CCR reflects status
;
			EXPORT	pAllocSRTBlk

pAllocSRTBlk
			move.l	a0,-(sp)
			move.l	#srtBlkSize+srtLinkSize,d0		; d0 = size to allocate
			_NewPtr	,Sys,Clear						; allocate block
			bne.s	@Done
			movea.l	a0,a1							; save copy of ptr to SRT block for the return
			move.w	#srtMaxEntries-1,d0				; number of entries in SRT (adjusted for dbra)
@Loop		move.w	#srtfree,srrSlot(a0)			; mark entries as free
			adda.w	#SRTRecSize,a0					; inc to next entry
			dbra	d0,@Loop
			move.w	#srtend,srtMarker(a0)			; indicate link and end of block
			moveq	#noErr,d0						; good return status
@Done
			movea.l	(sp)+,a0
			rts

			

;_______________________________________________________________________________________
;	GetSRTEntry  -  return a pointer to the next visible SRT entry
;
;	Given a pointer to an entry in an SRT block, return that pointer if the SRT entry
;	is visible.  If it is not visible, or if the pointer is to the end of the SRT
;	block, increment and return a pointer to the next visible SRT entry.  Walk down
;	the linked SRT blocks if necessary.
;
;	Input	: reg A0 = ptr to spBlock
;				  A1 = ptr to SRT entry to get or start search from
;
;	Output	: reg A1 = ptr to visible SRT entry
;				  D0 = status, 0=ok		CCR reflects status
;
;	spBlock	:	->	spParamData		input flag filled
;									bit 0 set = include disabled sResources in search
;										  clr = ignore disabled sResources
;
			Export	GetSRTEntry
GetSRTEntry
			MOVEM.L	D1-D2/A0,-(SP)
			
@Loop		MOVE.W	srrSlot(A1),D1				; get <slot><id>
			BMI.S	@ChkEnd						; escape flag - check for end of block
			BTST.B	#fall,spParamData+3(A0)		; search includes disabled sResource entries ?
			BNE.S	@Good						; yes - return whatever entry we have
			BTST.B	#srdisable,srrFlags+3(A1)	; is this entry enabled ?
			BEQ.S	@Good						; yes - return ptr to good entry
			BRA.S	@EndLoop					; otherwise inc to next entry and continue

;	Entry has escape flag (srrSlot == $FF).  Check for end of block

@ChkEnd		CMP.W	#srtEnd,D1					; end of list ?
			BNE.S	@EndLoop
			TST.L	srtNext(A1)					; end of block - any more srt blks ?
			BEQ.S	@NoMore						; end or SRT
			MOVEA.L	srtNext(A1),A1				; point to top of next block
			BRA.S	@Loop						; continue looking

@EndLoop	ADDA.W	#SRTRecSize,A1				; inc to next entry
			BRA.S	@Loop

@NoMore		MOVE.W	#smRecNotFnd,D0				; no good entries
			BRA.S	@Done
@Good		MOVEQ	#0,D0						; set good return
@Done		MOVEM.L	(SP)+,D1-D2/A0
			RTS
			

;_______________________________________________________________________________________	<1.3>
;	pSrToSpBlock  -  copy srBlock info to spBlock
;
;	
;
;	Input	: reg A0 = ptr to spBlock
;				  A1 = ptr to srBlock
;
;	Output	: none
;
;	spBlock	:	<-	spSlot			slot to start looking in
;				<-	spId			id to start looking from
;				<-	spExtDev		external device identifier
;				<-	spHWDev			hardware device id (if applicable)
;				<-	spsPointer		pointer sResource list
;				<-	spRefNum		driver reference number (if applicable)
;				<-	spIOReserved	
;				<-	spCategory		sType fields
;				<-	spCType
;				<-	spDrvrSW
;				<-	spDrvrHW
;				<-	spParamData		1 = sRsrc is disabled, 0 = enabled (*****different from old slotmgr)
;

			EXPORT	pSrToSpBlock

pSrToSpBlock
			MOVE.B	srrSlot(A1),spSlot(A0)
			MOVE.B	srrId(A1),spId(A0)	
			MOVE.B	srrExtDev(A1),spExtDev(A0)	
			MOVE.B	srrHWDev(A1),spHWDev(A0)	
			MOVE.W	srrRefNum(A1),spRefNum(A0)	
			MOVE.W	srrIOReserved(A1),spIOReserved(A0)	
			MOVE.W	srrCategory(A1),spCategory(A0)	
			MOVE.W	srrCType(A1),spCType(A0)
			MOVE.W	srrDrvrSw(A1),spDrvrSW(A0)
			MOVE.W	srrDrvrHw(A1),spDrvrHW(A0)
			MOVE.L	srrsRsrcPtr(A1),spsPointer(A0)		; return ptr to sRsrc list
			CLR.L	spParamData(A0)						; clear return flags field
			BTST.B	#srdisable,srrFlags+3(A1)			; test disable flag
			BEQ.S	@Done								; not disabled - return zero
			MOVE.L	#1,spParamData(A0)					; disabled - return 1
@Done		RTS



;_______________________________________________________________________________________
;	CalcStep  -  calculate the byte lane's step register value
;
;	The step register contains the value to add to a pointer to a declaration ROM, to
;	increment to the next consecutive byte or id field.  NuBus cards are not required to
;	support all 4 byte lanes, so the address increment value between consecutive bytes
;	may be greater than 1 (see Designing Cards and Driver's manual for more detail on
;	bytelanes).
;
;	The step register is a long, having the format of <incr 3><incr 2><incr 1><incr 0>,
;	where each increment value is a byte.  The increment values indicate from the first
;	byte lane supported, how much to increment to the next consecutive byte or id field.
;	The step register is used as a rotating shift register, so the increment values may
;	repeat.  For example, if only 1 byte lane where supported, then the step register
;	value would be <04><04><04><04>.  If all 4 byte lanes are supported, then the step
;	register would be <01><01><01><01>.  For 2 or 3 byte lanes, the step register value
;	depends on the byte lanes supported.
;
;	When used for accessing consecutive bytes, the step register the low byte is added
;	to the address to get the next byte.  Then the step register is rotated left on byte
;	in preparation for the next byte access.  When accessing consecutive id's, only the
;	low byte is needed as the increment value.  No rotation is needed.
;
;	Input	: reg A0 = ptr to spBlock
;
;	Output	: reg D0 = status, 0=ok		CCR reflects status
;
;	spBlock	:	-> spsPointer			NuBus address to calc step value for
;				-> spByteLanes			byte lane for slot
;				-> spFlags				if bit 5 is set, then calc for consecutive bytes, else id's
;				<- spResult				step value
;

CalcStep	PROC	EXPORT
			WITH	spBlock

			MOVEM.L	D1-D3/A1,-(SP)
			
			MOVE.W	#smBadsPtrErr,D0				; assume an error
			CLR.W	D1								; clear for later
			CLR.W	D2								; clear for later
			MOVE.B	spsPointer+3(A0),D1				; get low byte of NuBus address
			AND.L	#$03,D1							; get low 2 bits = using which byte lane 
			MOVE.B	spByteLanes(A0),D2				; reg D2 = byte lanes supported for card
			BTST.L	D1,D2							; using a valid byte lane ?
			BEQ.S	@Error							; a bad pointer for this slot

			MOVE.L	D2,D3							; get copy D3 = byte lanes for slot
			ASL.W	#2,D3							; adjust index to long entries in table

			BTST.B	#fConsecBytes,spFlags(A0)		; calculating bytes or id's ?
			BEQ.S	@Ids							; do id's

;	Do table lookup to get step register value for consecutive bytes
;	reg D3 = byte lanes for slot * 4, D1 = byte lane spsPointer is occupying (used to rotate step value)

			LEA		ByteTable,A1					; reg A1 = ptr to byte access table
			MOVE.L	0(A1,D3.W),D2					; reg D2 = step value
			MULU.W	#8,D1							; calc which step value current byte lane is on
			ROR.L	D1,D2							; D2 = step value corrected to current byte lane
			MOVE.L	D2,spResult(A0)
			BRA.S	@Done

;	Do table lookup to get step register value for consecutive id's
;	reg D2 = byte lanes for slot, D1 = byte lane spsPointer is occupying

@Ids		LEA		IdTable,A1						; reg A1 = addr of id access table
			MOVE.L	0(A1,D3.W),spResult(A0)			; return step value - no rotation needed
			BPL.S	@Done							; if negative, then calc for 3 byte lanes

;	Card supports three byte lanes.  Must index into an auxilary table.  There are 4 valid
;	and 1 invalid entry in the table (total of 5).  Each entry has the step value for the
;	slot's byte lane configuration, a different step value depending on which byte lane
;	the spsPointer occupies.

			LEA		ThreeByteTbl,A1					; get addr of auxilary table for 3 byte lanes
			AND.B	#7,D2							; use low 3 bits for indexing into table
			SUB.B	#3,D2							; adjust index for table
			ASL.L	#4,D2							; multiply by 16 bytes (size of each entry)
			ADDA.L	D1,A1							; add in current byte lane offset
			MOVE.L	0(A1,D2.W),spResult(A0)			; get step value for 3 byte lane support

@Done		CLR.W	D0								; set good result
@Error		MOVEM.L	(SP)+,D1-D3/A1
			RTS
			
;_______________________________________________________________________________________
;	Byte lane translation tables
;
;	ByteTable, corresponds to consecutive bytes.  IdTable, corresponds to consecutive
;	id fields.  ThreeByteTbl, corresponds to consecutive id fields on cards which support
;	three byte lanes.  Each entry is 4 longs.  These byte lanes have their entries as -1
;	in IdTable.
;
;					step values						byte lanes
;					-----------						----------
ByteTable	DC.L	$0								; no byte lane indexed at 0
			DC.L	$04040404						; 0
			DC.L	$04040404						; 1
			DC.L	$03010301						; 0,1
			DC.L	$04040404						; 2
			DC.L	$02020202						; 0,2
			DC.L	$03010301						; 1,2
			DC.L	$00020101						; 0,1,2
			DC.L	$04040404						; 3
			DC.L	$01030103						; 0,3
			DC.L	$02020202						; 1,3
			DC.L	$00010201						; 0,1,3
			DC.L	$03010301						; 2,3
			DC.L	$00010102						; 0,2,3
			DC.L	$00020101						; 1,2,3
			DC.L	$01010101						; 0,1,2,3

IdTable		DC.L	$0								; no byte lane indexed at 0
			DC.L	$10101010						; 0
			DC.L	$10101010						; 1
			DC.L	$08080808						; 0,1
			DC.L	$10101010						; 2
			DC.L	$08080808						; 0,2
			DC.L	$08080808						; 1,2
			DC.L	$FFFFFFFF						; 0,1,2
			DC.L	$10101010						; 3
			DC.L	$08080808						; 0,3
			DC.L	$08080808						; 1,3
			DC.L	$FFFFFFFF						; 0,1,3
			DC.L	$08080808						; 2,3
			DC.L	$FFFFFFFF						; 0,2,3
			DC.L	$FFFFFFFF						; 1,2,3
			DC.L	$04040404						; 0,1,2,3

ThreeByteTbl
			DC.L	$05050505						; 0,1,3
			DC.L	$06060606						;
			DC.L	$0								;
			DC.L	$05050505						;
			DC.L	$0								; empty
			DC.L	$0								;
			DC.L	$0								;
			DC.L	$0								;
			DC.L	$06060606						; 0,2,3
			DC.L	$0								;
			DC.L	$05050505						;
			DC.L	$05050505						;
			DC.L	$0								; 1,2,3
			DC.L	$05050505						;
			DC.L	$05050505						;
			DC.L	$06060606						;
			DC.L	$05050505						; 0,1,2
			DC.L	$05050505						;
			DC.L	$06060606						;
			DC.L	$0								;

			

;_______________________________________________________________________________________
;	OffsetData  -  read an offset/data field
;
;	Read the offset/data field identified by spId, in the list pointed to by spsPointer.
;	The offset/data field is the 3 byte field following the id in a list entry :
;	<id><offset/data>.  This routine may be used for general data access of a list.
;
;	Input	: reg A0 = ptr to spBlock
;
;	Output	: reg D0 = status, 0=ok		CCR reflects status
;
;	spBlock	:	<-> spsPointer			On entry is ptr to list to search, on exit is ptr
;										to id in list.  This can be used to calculate a new
;										pointer offset by the value of the offset/data field.
;				 -> spId				Id in list to search for
;				 <- spOffsetData		the value of the offset data field
;				 <- spByteLanes			byte lane for slot
;

OffsetData	PROC	EXPORT
			WITH	spBlock,sInfoRecord

			MOVEM.L	D1-D2/A1,-(SP)
			MOVE.B	spSlot(A0),-(SP)				; save slot field (if any)

			MOVE.W	#smBadRefId,D0					; asume an error
			CMPI.B	#$FF,spId(A0)					; is id less than $FF ?
			beq		@Done							; no - id out of range

;	Get the sInfo record for the slot.  Derive the slot number from the spsPointer.
;	If the spsPointer is to a RAM list, then slot number gets translated to slot 0.

			_sPtrToSlot								; convert to slot number
			bne		@Done
			_sFindsInfoRecPtr						; get ptr to sInfo record and status	<1.6>
			MOVE.L	spResult(A0),A1					; reg A1 = ptr to sInfo record
			move.w	siInitStatusA(a1),d0			; test slot
			bmi.s	@Done							; bad slot - skip it

;	Calculate the step value to consecutive bytes

			MOVE.B	siCPUByteLanes(A1),spByteLanes(A0)	; get byte lane field from sInfo rec
			BSET.B	#fConsecBytes,spFlags(A0)			; set flag for consecutive bytes
			_sCalcStep									; calc step value for spsPointer
			bne.s	@Done

;	Loop and read the <id><offset/data> fields (a long) from the list.

			MOVE.L	spResult(A0),spParamData(A0)		; step value in spParamData
			MOVEQ	#0,D1								; clear for last id read

@Loop		MOVE.B	D1,D2								; save D2 = last id read
			MOVE.L	spsPointer(A0),A1					; save reg A1 = ptr to current id
			slotjsr	Read4Bytes							; read <id><offset/data>
			bne.s	@Done								; some error
			MOVE.B	spResult(A0),D1						; reg D1 = <id>
			MOVE.W	#smBadsList,D0						; assume out of order error
			CMP.B	D2,D1								; check for ascending order
			bcs.s	@Done								; out of order
			MOVE.W	#smBadRefId,D0						; assume not found
			CMP.B	#$FF,D1								; end of list ?
			beq.s	@Done								; id not found
			CMP.B	spId(A0),D1							; found id ?
			BNE.S	@Loop								; not found - continue

;	Found id - return offset/data value

			MOVE.L	spResult(A0),D0						; get <id><offset/data>
			AND.L	#$00FFFFFF,D0						; mask off id
			MOVE.L	D0,spOffsetData(A0)					; set return offset/data field
			MOVE.L	A1,spsPointer(A0)					; set ptr to id in list
			moveq	#noErr,d0							; set a good return

@Done		MOVE.B	(SP)+,spSlot(A0)					; restore save slot number
			MOVEM.L	(SP)+,D1-D2/A1
			TST.W	D0
			RTS

			

;_______________________________________________________________________________________	<2.0>
;	ReadPBSize  -  read the physical block size field of an sBlock
;
;	Search a list pointed to by spsPointer for the id spId.  Use the offset/data field
;	of the id to calculate a pointer to an sBlock.  Return the physical block size of
;	the sBlock.  Also return the pointer to the sBlock after the size field.  Optionally
;	return a long instead of the physical block size (PBSize is masked to 3 bytes).
;
;	Input	: reg A0 = ptr to spBlock
;
;	Output	: reg D0 = status, 0=ok		CCR reflects status
;
;	spBlock	:	<-> spsPointer			On entry is ptr to list to search, on exit is ptr
;										to sBlock.
;				 -> spId				Id in list to search for
;				 -> spFlags				bit 1 set means check high byte of size for zero
;				 <- spSize				physical block size field of sBlock
;				 <- spByteLanes			byte lane for slot
;
;	Called	: trap dispatcher, jsr
;

ReadPBSize	Proc	Export
			With	spBlock,sInfoRecord

			movem.l	d1/a1,-(sp)
			move.b	spSlot(a0),-(sp)				; save slot field (if any)

;	Search the list pointed to by spsPointer for the spId.  Use the offset/data
;	field to calculate a pointer to the sBlock.

			_sFindStruct							; return new spsPointer
			bne.s	@Done

;	Read the PBSize field pointed to by spsPointer.  Increment spsPointer to after the
;	size field.

			_sPtrToSlot								; get slot number
			bne.s	@Done
			_sFindsInfoRecPtr						; get ptr to sInfo record and status	<1.6>
			movea.l	spResult(a0),a1					; reg a1 = ptr to sInfo record
			move.w	siInitStatusA(a1),d0			; test slot
			bmi.s	@Done							; bad slot - skip it

;	Calculate the step value to consecutive bytes

			move.b	siCPUByteLanes(a1),spByteLanes(a0)	; get byte lane field from sInfo rec
			bset.b	#fConsecBytes,spFlags(a0)			; set flag for consecutive bytes
			_sCalcStep									; calc step value for spsPointer
			bne.s	@Done

			move.l	spResult(a0),spParamData(a0)		; step value in spParamData
			slotjsr	Read4Bytes							; read PBSize field
			bne.s	@Done

;	Optionally check the PBSize field to make sure that the high byte (a reserved field),
;	is zero.

			move.l	spResult(a0),spSize(a0)			; move pbsize to size field
			moveq	#noErr,d0						; assume a good return
			btst.b	#fCkReserved,spFlags(a0)		; check reserved field ?
			beq.s	@Done							; no - we are done
			move.b	spResult(a0),d1					; d1 = high byte of PBSize field
			beq.s	@Done							; it's ok
			move.w	#smReservedErr,d0				; not zero - log an error

@Done
			move.b	(sp)+,spSlot(a0)				; restore save slot number
			movem.l	(sp)+,d1/a1
			tst.w	d0
			rts


;_______________________________________________________________________________________	<2.0>
;	pRead4Bytes  -  read 4 bytes from NuBus
;
;	Utility routine to read 4 bytes from NuBus.  This differs from ReadLong(), in that
;	it doesn't do a lot of the endless slot manager checking.
;
;	Input	: reg A0 = ptr to spBlock
;
;	Output	: reg D0 = status, 0=ok		CCR reflects status
;
;	spBlock	:	<-> spPointer			address of 4 bytes to read - on exit, ptr to next long
;				-> spParamData			consecutive byte step value
;				-> spSlot				slot number
;				<- spResult				4 bytes read
;
pRead4Bytes	PROC	EXPORT

			WITH	spBlock,sInfoRecord

			movem.l	d1-d4/a1-a2,-(sp)

;	Setup for accessing NuBus

			MOVE.L	spParamData(A0),D2				; get step value
			MOVEA.L	spsPointer(A0),A1				; reg A1 = ptr to read bytes from

			MOVEQ	#3,D3							; D3 = loop cntr
			MOVEQ	#0,D4							; clear for temp reg for macro
			slotjsr	InstallBus						; switch to 32bit mode and new bus exception
			lea		@Done,a2						; set addr to jump to if bus error
			move.w	#smUnExBusErr,d0				; assume unexpected bus error
@Loop
			asl.l	#8,d1							; shift previously read value
			move.b	(a1),d1							; NUBUS READ - read byte
			sNextStep	d2,d4,a1					; macro to inc ptr to next byte
			dbra	d3,@Loop						; continue

			move.l	d1,spResult(a0)					; set result to 4 bytes read
			MOVE.L	A1,spsPointer(A0)				; update incremented ptr
			MOVEQ	#noErr,D0						; done - set good return
			
@Done		
			slotjsr	RestoreBus						; restore mmu mode and bus exception
			movem.l	(sp)+,d1-d4/a1-a2
			RTS



;_______________________________________________________________________________________	<4>
;	GetBoard  -  get ptr to board sResource
;
;	Given a slot number - return a pointer to the board sResource and sInfo record
;
;	Input	: reg A0 = ptr to spBlock
;
;	Output	: reg D0 = status, 0=ok		CCR reflects status
;				  A0 = ptr to spBlock
;
;	spBlock	:	 -> spSlot					slot number
;				 ->	spId					sRsrc id to search from							<8>
;				<-	spsPointer				ptr to board sResource
;				<-	spResult				ptr to sInfo record
;

			Export	pGetBoard

pGetBoard
			move.l	a1,-(sp)

			_sFindsInfoRecPtr						; get the sInfo record ptr
			movea.l	spResult(a0),a1					; save a1 = ptr to sInfo record

;	Get the board sResource

			move.l	#((CatBoard<<16)++TypBoard),spCategory(a0)	; set category and type fields
			clr.l	spDrvrSW(a0)					; no drvrSW and drvrHW fields
			clr.b	spTBMask(a0)					; no <cat><typ><SW><HW> masking
			clr.b	spExtDev(a0)					; clear external device id field (not a device)
			clr.l	spParamData(a0)					; clear flag field
	IF NOT forROM THEN								; <SM2> rb
			bset.b	#fall,spParamData+3(a0)			; search all sResources
			bset.b	#foneslot,spParamData+3(a0)		; search in only one slot
	ELSE											; <SM2> rb
			ori.b	#(1<<fall)+(1<<foneslot),spParamData+3(a0)	; <SM2> rb search all sRsrc's in one slot	<4>
	ENDIF											; <SM2> rb
			_GetTypesRsrc							; return ptr to sResource
			beq.s	@Done
			move.w	#smNoBoardsRsrc,d0				; no board sResource

@Done		move.l	a1,spResult(a0)					; restore spResult to sInfo ptr
			movea.l	(sp)+,a1
			tst.w	d0
			rts
			

;_______________________________________________________________________________________
;	pBusException  -  nubus bus error exception handler
;
;	This is the slot manager's replacement bus error exception handler.  It is designed
;	to detect faulted Nubus address data read bus cycles.  It is NOT designed to handle
;	data write or read-modify-write cycles, or instruction fetch faults.  These exceptions
;	are transfered to the old bus exception handler.
;
;	Upon an exception, the stack frame type is verified for as being one this routine is
;	able to handle (in this case, a long bus cycle stack frame only).  The SSW is check
;	for a data read cycle fault ONLY.  If the fault address is a NuBus address (32 bit
;	address), then the exception stack frame is pop-ed and the exception return address
;	in reg a2 is jumped to.
;
;	Input	: reg a2				address to return from exception when a bus error occurs
;	Output	: ccr set to value in reg d0
;

pBusException Proc	Export
					Export pInstallBus,pRestoreBus

stackframe	RECORD	0
savereg		DS.L	1								; saved register on stack
statusreg	DS.W	1
programCnt	DS.L	1
type		DS.B	1								; format frame type
fill		DS.B	3								; filler
ssw			DS.W	1								; special status register
fill2		DS.L	1								; filler
DFAddr		DS.L	1								; data cycle fault address
remainder	DS.B	72-8							; remainder of stack frame minus the short frame
shortSR		ds.w	1								; beginning of short stack frame definition
shortPC		ds.l	1								; new pc for short frame
shortvers	ds.w	1								; version and vector offset
			ENDR


			WITH	stackframe,slotGlobals
			

;	Verify that this is a faulted NuBus read data cycle

			move.l	d0,-(sp)						; save working register
			move.w	ssw(sp),d0						; get special status register
			and.w	#$F1C0,d0						; mask FC FB RC RB DF RM RW
			cmp.w	#$0140,d0						; DF and RW bit set only ?
			bne.s	@RealBusEx						; can't handle this case - pass it on
			move.b	type(sp),d0						; get format of stack frame for long frame
			lsr.b	#4,d0							; look at high nibble
			cmp.b	#$0B,d0							; long bus exception frame ?
			bne.s	@RealBusEx						; transfer to real bus exception handler
			move.b	DFAddr(sp),d0					; get high byte of data fault cycle address
			cmp.b	#majorSpace,d0					; in super slot space ($60-$E0) ?
			blo.s	@RealBusEx						; not in slot address space
			cmp.b	#$FF,d0							; in minor slot space range ?
			beq.s	@RealBusEx						; not in minor slot space
			cmp.b	#$F0,d0							; in minor slot space range ?
			beq.s	@RealBusEx						; not in minor space $F1 - $FE

;	Have verified that a NuBus read data access caused the bus error.  Need to modify the
;	stack frame to return to the address in register a2.  Accomplish this by creating a new
;	short stack frame and setting a new PC.  The long bus exception PC is popped off leaving
;	a short exception frame.  Cannot just modify the PC in the long frame to return to a new
;	address (this doesn't work).

			move.l	(sp),d0							; restore reg d0
			move.l	a2,shortPC(sp)					; set new return address in short frame
			move.w	statusreg(sp),shortSR(sp)		; move the SR
			clr.w	shortvers(sp)					; clear frame type and vector offset
			adda.w	#shortSR,sp						; pop long frame leaving a short frame
			rte

;	The bus exception was not caused by a read to NuBus - pass the exception to the
;	real bus exception handler.

@RealBusEx	move.l	(sp)+,d0						; restore reg D0
			jmp		([sInfoPtr],sysBusExcptn)		; jump to bus exception vector


;_______________________________________________________________________________________	<7>
;	pInstallBus  -  install the slot manager's bus exception vector
;
;	Switch to 32 bit mode and replace the system bus exception handler with the slot
;	manager's exception handler.  Return the saved mmu state in reg D0.  Installing
;	the slot manager's vector is based on a use count in the slot mgr globals.  If the
;	count is zero, then save the system vector and install the new vector.  If the count
;	is not zero, then assume some embedded slot mgr routine has already replaced the
;	vector.  In that case, just increment the count.  This routine may be called at
;	interrupt level.
;
;	Input	: none
;	Output	: none
;
;	Destroys: d0
;

pInstallBus			
			move.w	sr,-(sp)								; Save current interrupt level.
			ori.w	#HiIntMask,sr							; disable interrupts
			moveq	#True32B,d0
			_SwapMMUMode									; change to 32 bit mode
			move.b	d0,([sInfoPtr],entryMMUMode)			; save old mmu state
			move.l	BusErrVct,([sInfoPtr],sysBusExcptn)		; save system bus vector in globals
			move.l	([SDMJmpTblPtr],BusException*4),BusErrVct	; replace with slot mgr vector
			move.w	(SP)+,SR								; restore interrupt state
			rts


;_______________________________________________________________________________________	<7>
;	pRestoreBus  -  restore the system bus exception vector
;
;	Restore the mmu state and the system's bus exception vector.  If the bus exception
;	use count is incremented to zero, then there is no slot mgr routine still using
;	the exception vector, so restore the system's vector (saved in the slot mgr globals).
;	If the count is non-zero, then do nothing.  This routine may be called at interrupt
;	level.
;
;	Input	: none
;	Output	: none
;
;	Preserves all registers
;

pRestoreBus
			move.l	d0,-(sp)						; save reg
			move.w	sr,-(sp)						; Save current interrupt level.
			ori.w	#HiIntMask,sr					; disable interrupts
			move.b	([sInfoPtr],entryMMUMode),d0		; get saved mmu mode		
			_SwapMMUMode								; restore mmu mode
			move.l	([sInfoPtr],sysBusExcptn),BusErrVct	; restore system exception vector
			move.w	(sp)+,sr							; restore interrupt state
			move.l	(sp)+,d0							; restore reg
			rts

			ENDWITH									; { stackframe,slotGlobals }			<5>


;____________________________________________________________________________
;
;  Routine:		p040BusException
;
;  Inputs:		a2	-	address to return to from exception when a bus error occurs
;
;  Outputs:		ccr set to value in reg d0
;
;  Function:	nubus bus error exception handler for 68040-based machines
;
;____________________________________________________________________________
				Export	p040BusException
p040BusException						;									

;	The 040 Special Status Word (SSW) is different than the 020/030 one.  It looks like	
;																						
;		+---------------------------------------------------------------------+			
;		| CP | CU | CT | CM | MA | ATC | LK | RW | X |  SIZE  |  TT  |   TM   |			
;		+---------------------------------------------------------------------+			
;		  15   14   13   12   11   10    9    8    7   6    5  4   3  2      0			
;																						
;																						
;			CP		=	Continuation - Floating Pt. Post Exception Pending				
;			CU		=	Continuation - Unimplemented Floating Pt. Instruction Exception	
;			CT		=	Continuation - Trace Exception Pending							
;			CM		=	Continuation - MOVEM Instruction Execution Pending				
;			MA		=	Misaligned Access												
;			ATC		=	ATC Fault														
;			LK		=	Locked Transfer													
;			RW		=	Read/Write (1=Read)												
;			X		=	Undefined														
;			SIZE	=	Transfer Size													
;			TT		=	Transfer Type													
;			TM		=	Transfer Modifier												

aeStackFrame	RECORD		0						; 040 Access Stack Frame				
savereg			DS.L		1						; space to save working register (D0)
aeSR			DS.W		1						; status register					
aePC			DS.L		1						; program counter					
aeType			DS.B		1						; type of exception stack frame		
less			DS.B		1						; filling (vector offset)			
aeEffAddr		DS.L		1						; effective address					
aeSSW			DS.W		1						; special status word				
aeWBnS			DS.W		3						; WriteBack #n Status				
aeFA			DS.L		1						; fault address						
aeWB3Addr		DS.L		1						; WriteBack 3 Address				
aeWB3Data		DS.L		1						; WriteBack 3 Data					
aeWB2Addr		DS.L		1						; WriteBack 2 Address				
aeWB2Data		DS.L		1						; WriteBack 2 Data					
aeWB1Addr		DS.L		1						; WriteBack 1 Address				
aeWB1Data		DS.L		1						; WriteBack 1 Data/Push Data LW0	
aePD1			DS.L		1						; Push Data LW 1					
aePD2			DS.L		1						; Push Data LW 2					
aePD3			DS.L		1						; Push Data LW 3					
				ENDR

aeXFrameSize	EQU		$3C							; size of an Access Error stack frame	
aeXFrameType	EQU		$07							; access error exception frame type		


			WITH	aeStackFrame,slotGlobals		;									

;	Verify that this is a faulted NuBus read data cycle									

			move.l	d0,-(SP)						; save working register				
			move.w	aeSSW(SP),d0					; retrieve SSW						
			andi.w	#$0518,d0						; mask off only ATC, RW, TT			
			cmp.w	#$0100,d0						; should be ATC=0, RW=1, TT=0		
			bne.s	@RealBusEx						; can't handle these case - pass it on
			move.b	aeType(SP),d0					; get format of stack frame			
			lsr.b	#4,d0							; look at high nibble				
			cmp.b	#aeXFrameType,d0				; access error exception frame?		
			bne.s	@RealBusEx						; NO  ... use sys. access error handler
			move.b	aeFA(SP),d0						; get high byte of fault address	
			cmp.b	#majorSpace,d0					; in super slot space ($60-$E0)?	
			blo.s	@RealBusEx						; not in slot address space			
			cmp.b	#$FF,d0							; in minor slot space range?		
			beq.s	@RealBusEx						; not in minor slot space			
			cmp.b	#$F0,d0							; in minor slot space range?		
			beq.s	@RealBusEx						; not in minor space $F1 - $FE		

;	Have verified that a NuBus read data access caused the bus error.  Pop the exception
;	stack frame and jump to the error return address in register a2.
;
;	***	I don't think this will work correctly.  Given that you could have other exceptions
;	***	of a lesser priority pending, and could possibly have pending writebacks as well,
;	***	does this code need to look to complete those writebacks, stuff A2 into aePC(SP)
;	*** and do an RTE?  Or can we legally/safely stuff A2 into aePC(SP)?				

;			move.l	(sp)+,d0						; restore reg d0					
;			adda.w	#aeXFrameSize,sp				; pop the exception stack frame		
;			jmp		(a2)							; take the error return address		

			move.l	a2,aePC(SP)						; (re-)set the PC to where we want to go
			move.l	(sp)+,d0						; restore reg d0						
			rte										; return, catching any pending exceptions

;	The bus exception was not caused by a read to NuBus - pass the exception to the
;	real bus exception handler.

@RealBusEx	MOVE.L	(SP)+,D0						; restore reg D0
			MOVE.L	([sInfoPtr],sysBusExcptn),-(SP)	; put saved bus exception vector on stack
			RTS										; jump to bus exception vector

			ENDWITH									; { aeStackFrame,slotGlobals }		

;_______________________________________________________________________________________	<h7> djw
;	AddCard	-	add a card to the slot manager data structures
;
;	There is a stub entrypoint for this routine in the file slotInfo.a which does a long
;	branch here.  The stub routine is needed for the slot manager's jump table offset
;	limitation.
;
;	Add a card to the slot manager's data structures and run it's primaryInit.  If the
;	card's current status is not empty, then the slot is rejected as not valid.  Memory
;	is allocated, so this routine should not be run at interrupt time.
;
;	Input	:	a0 = ptr to spBlock
;	Output	:	none
;
;	spBlock	:	-> spSlot			slot number (slot 0 is not valid)
;
			Export	AddCard
			with	spBlock,sInfoRecord,seBlock
AddCard
@regs		reg		d1/a0/a2
			movem.l	@regs,-(sp)

;	If the current slot status is not empty, then the slot may not be re-initialized

			_sFindsInfoRecPtr					; get ptr to sInfoRecord
			bne		@Done						; slot not valid - done
			movea.l	spResult(a0),a2				; a2 = ptr to sInfoRecord
			move.w	#smSlotOOBErr,d0			; assume bad slot
			cmp.w	#smEmptySlot,		\		; check for empty slot
					siInitStatusA(a2)
			bne.s	@Done						; only empty slots are valid

;	Execute the slot manager initialization again on this slot

			slotjsr	initSDeclMgr				; initialize a slot's sInfoRecord
			slotjsr	sInitSRsrcTable				; initialize the slot resource table (SRT)
			slotjsr	sInitPRAMRecs				; initialize a slot's PRAM

			clr.b	spFlags(a0)					; clear flag bit to assume cold start
			cmp.l	#WmStConst,WarmStart		; check low mem for warm start value
			bne.s	@continue					; cold start
			bset.b	#fWarmStart,spFlags(a0)		; warm start

@continue
			move.w	sr,-(sp)
			ori.w	#HiIntMask,sr				; disable ints for primary init
			slotjsr	sPrimaryInit				; execute the primaryInit
			move.w	(sp)+,sr					; restore ints

;	Execute the secondaryInit record														<h12> thru next <h12>

			lea		-seBlockSize(sp),sp			; alloc seBlock
			movea.l	sp,a2						; a2 = ptr to seBlock
			move.l	a2,spsExecPBlk(a0)			; set ptr in spblock to sExec blk
			move.b	#BoardId,sesRsrcId(a2)		; indicates exec code is from board sResource
			slotjsr	sDoSecondary				; jump thru the vector
			lea		seBlockSize(sp),sp			; dealloc seBlock							<h12> from last <h12>

@Done
			movem.l	(sp)+,@regs
			rts
			endwith


;_______________________________________________________________________________________	<h7> djw
;	RemoveCard	-	Remove a card from the slot manager
;
;	This routine will remove a card from the system by:
;
;		1.	Delete all sRsrc's from the SRT for that slot
;		2.	Delete sInfoRecord and set to empty status
;		3.	If there is a driver attached to the slot, attempt to close the driver
;		4.	Remove any slot interrupt queue elements for the slot
;
;	Input	:	a0 = ptr to spBlock
;	Output	:	none
;
;	spBlock	:	-> spSlot			slot number (slot 0 is not valid)
;
			Export	RemoveCard
			with	spBlock,SlotIntQElement,slotIntGlobals,srrBlock,sInfoRecord

RemoveCard
@regs		reg		d1-d3/a1-a2
			movem.l	@regs,-(sp)
			lea		-ioQElSize(sp),sp					; alloc iopb
			movea.l	sp,a2								;									<H14><SM13>
			clr.l	ioCompletion(a2)					; no completion routine
			moveq.l	#0,d1
			move.b	spSlot(a0),d1						; d1 = slot number

;	Loop through SRT deleting all sResources for this slot.

@LoopAgain
			movea.l	sRsrcTblPtr,a1						; get ptr to beginning of SRT
			suba.w	#SRTRecSize,a1						; pre-decrement pointer
@Loop
			adda.w	#SRTRecSize,a1						; inc to next srrBlock
			clr.l	spParamData(a0)						; ignore input flags becauseÉ
			bset.b	#fall,spParamData+3(a0)				; Éwe need to find both enabled and disabled sRsrc's
			slotjsr	sGetSRTEntry						; get next entry - rtn in ptr in a1
			bne.s	@noMore								; found all sRsrc's
			slotjsr	SrToSpBlock							; fill spBlock with srrBlock info
			cmp.b	spSlot(a0),d1						; is this the slot we are looking at?
			bne.s	@Loop								; not right slot - continue looking

;	If there is a driver associated with this slot, attempt to close it

			tst.w	spRefNum(a0)						; is there a valid refnum?
			bpl.s	@remove								; not a refnum
			move.w	spRefnum(a0),ioRefnum(a2)			; set refnum to close
			move.l	a0,-(sp)							; save ptr to spBlock
			movea.l	a2,a0								; a0 = ptr to iopb
			_Close										; close driver
			movea.l	(sp)+,a0							; restore a0 = ptr to spBlock
			
;	Delete the sRsrc from SRT

@remove
			slotjsr	sDeleteSRTRec						; delete the found sRsrc
			bra.s	@LoopAgain							; find and delete all sRsrc's

;	Delete the sInfoRecord

@noMore
			lea		([sInfoPtr],(4*TotalSlots)	\
					+(sInfoNewSize*2)),a1				; get ptr to empty sInfo record
			move.l	a1,([sInfoPtr],d1.w*4)				; set slot to point to empty record

;	Clear the slot interrupt queue for this slot
;	****	WARNING		****	WARNING		****
;	This code is very implementation specific.  If slot interrupt table structure
;	changes, this code will not work.  Also, this routine leaks memory because
;	it does not free the slot int queue elements

			lea		([SlotQDT],d1.w*4,slotIntQHeads-SQLink),a1	; get address of queue header
			clr.l	SQLink(a1)									; zero queue link

;	Done

@Done
			lea		ioQElSize(sp),sp					; de-alloc iopb
			movem.l	(sp)+,@regs
			rts

			endwith

;_______________________________________________________________________________________	<h7> djw
;	CheckSlot	-	check if a card has changed
;
;	Check whether a slot's status has changed.  There are 3 slot state changes to
;	consider:
;
;		1.	empty		-->		occupied
;		2.	occupied	-->		empty
;		3.	occupied	-->		occupied
;
;	If a slot goes from empty to occupied, or occupied to empty, return status that
;	the slot's state has changed.  If a slot was previously occupied and is still
;	occupied, then read the board id from the card and match it to the board id in
;	PRAM.  If they are not the same, then the previous card was removed, and a new
;	card is now in it's place.
;
;	Input	:	a0 = ptr to spBlock
;
;	Output	:	d0 = 0 if no change, non-zero if change
;
;	spBlock	:	-> spSlot			slot number (slot 0 is not valid)
;
			Export	CheckSlot
			Import	VerifySlot
			with	spBlock,sInfoRecord

CheckSlot
@regs		reg		d1-d3/a1-a4
			movem.l	@regs,-(sp)
			moveq.l	#0,d3							; set d3 = change status flag

;	get ptr to slot's sInfoRecord so we can check its previous state

			_sFindsInfoRecPtr						; get ptr to sInfoRecord
			bne		@Return							; slot not valid - no change
			movea.l	spResult(a0),a4					; a4 = ptr to sInfoRecord

;	If a slot is disabled or reserved, then it is invalid

			cmp.w	#smDisabledSlot,		\		; check for disabled slot
					siInitStatusA(a4)
			beq		@Return							; disabled slots not valid
			cmp.w	#smReservedSlot,		\		; check for reserved slot
					siInitStatusA(a4)
			beq		@Return							; reserved slots not valid

			moveq.l	#0,d1							; zero reg
			move.b	spSlot(a0),d1					; d1 = slot number

;	Save the current sInfoRecord for the slot

			lea		-sInfoNewSize(sp),sp			; alloc temp sInfoRecord
			move.w	d3,siInitStatusA(sp)			; clear status field
			move.b	d3,siState(sp)					; clear state variable
			move.w	siTOConst(a4),siTOConst(sp)		; set bus error retry cnt (not used)
			move.b	d1,siSlot(sp)					; set slot number
			move.l	d3,siROMAddr(sp)				; clear ROM address
			movea.l	sp,a1							; a1 = ptr to temp sInfoRecord
			move.l	a1,([sInfoPtr],d1.w*4)			; set new temp sInfoRecord ptr for slot

;	Determine whether the slot is empty or occupied

			moveq.l	#-1,d0
			bfins	d1,d0{4,4}						; inset slot number to get $fsffffff
			movea.l	d0,a3							; a3 = minor space nubus addr for slot
			lea		@Empty,a2						; a2 = addr to go if bus error occurs
			slotjsr	InstallBus						; replace bus excptn, 32 bit mode
			move.b	(a3),d0							; NUBUS READ - read byte from minor space
			slotjsr	RestoreBus						; restore mmu state and bus exception vector
			move.l	a3,siROMAddr(sp)				; set ROM address
			bra.s	@Verify							; slot is occupied - verify the decl rom

;	If the slot is still empty and was previously empty, then we are done

@Empty
			slotjsr	RestoreBus						; get here if empty slot
			move.w	#smEmptySlot,siInitStatusA(a1)	; set empty status
			bra.s	@CheckError						; check if same as previous status

;	Verify the format header  -  a0 = ptr to spblock, a1 = ptr to sInfoRecord

@Verify
			bsr.l	VerifySlot						; verify the config rom format
			tst.w	siInitStatusA(a1)				; is the slot status ok?
			bne.s	@CheckError						; bad or empty slot - check previous status

;	Config ROM verifies ok - walk through all the sResources looking for the board sResource

			bsr		GetBoardID						; find the board sRsrc and board id
			beq.s	@Changed						; no board id - a bad card

;	Compare with pram board id.  If sRsrc board id is different, then there is
;	a different card in the slot.

			move.w	d0,d2							; d2 = board id for new card
			lea		-8(sp),sp						; alloc pram buffer
			move.l	sp,spResult(a0)					; pass ptr to pram buffer
			slotjsr	sReadPRAMRec					; read the current slot pram
			bne.s	@Changed						; something wrong - change the card

			cmp.w	(sp),d2							; are board id's the same?
			lea		8(sp),sp						; de-alloc buffer
			beq.s	@Done							; same board id's - return no change
			bra.s	@Changed

;	Some error - if same as previously, then mark as unchanged

@CheckError
			move.w	siInitStatusA(a4),d0				; get previous status
			cmp.w	siInitStatusA(a1),d0				; same error?
			beq.s	@Done								; same board id's - no change

;	The card has changed

@Changed
			moveq.l	#-1,d3							; return error to indicate card changed

;	Done

@Done
			lea		sInfoNewSize(sp),sp				; free temp sInfoRecord
			move.l	a4,([sInfoPtr],d1.w*4)			; restore original sInfoRecord

@Return
			move.l	d3,d0							; return change status
			movem.l	(sp)+,@regs
			rts


;_______________________________________________________________________________________	<h7> djw
;	GetBoardID	-	Find and return board id
;
;	Find the board sResource and return the board id for the given slot.  This
;	routine walks through the sResource data structures on the card, not using
;	the SRT.
;
;	Input	:	a0 = ptr to spBlock
;				a1 = ptr to sInfoRecord
;	Output	:	d0 = board id or zero		ccr reflects status
;
;	spBlock	:	-> spSlot			slot number (slot 0 is not valid)
;

GetBoardID
@regs		reg		d1-d4/a1-a2
			movem.l	@regs,-(sp)
			moveq.l	#0,d4							; d4 = board id value to return

;	Calculate the step value for this slot so we can step through the sRsrc dir

			move.l	siDirPtr(a1),spsPointer(a0)			; set ptr field to get step value for
			move.b	siCPUByteLanes(a1),spByteLanes(a0)	; set byte lanes field
			bset.b	#fConsecBytes,spFlags(a0)			; set flag for step value for bytes
			_sCalcStep
			bne		@Done

;	For every entry in the sResource directory, read the sResource type to find the
;	board sResource.  Also check for ascending order for the sResources in case there
;	is no $ff at the end.

			move.l	spResult(a0),spParamData(a0)	; set step value for reading bytes
			move.l	siDirPtr(a1),d2					; set d2 = ptr to current dir entry
			moveq.l	#0,d1							; zero last id read

@Loop		move.l	d2,spsPointer(a0)				; set spsPointer to addr of id to read in dir
			slotjsr	Read4Bytes						; read <id><offset> - inc spsPointer to next long
			bne.s	@Done							; an error - stop searching
			move.l	spsPointer(a0),d2				; get d2 = ptr to next id to read

;	Check for acsending order in sResource directory id's

			move.b	spResult(a0),d3					; get high byte to get <id> field
			cmp.b	#$ff,d3							; end of list ?
			beq.s	@Done							; done - board sResource is not found
			cmp.b	d1,d3							; is new id greater than last id ?
			bls.s	@Done							; id's out of order - not found
			move.b	d3,d1							; set reg d1 = last id read

;	Get a pointer to the sResource id found

			move.l	siDirPtr(a1),spsPointer(a0)		; point to directory
			move.b	d1,spId(a0)						; find the sRsrc id just read from the dir
			_sFindStruct
			bne.s	@Done							; some error

;	With the spsPointer field now pointing to the sRsrc, read the type field

			movea.l	spsPointer(a0),a2				; save ptr to sRsrc

			move.b	#sRsrcType,spId(a0)				; get ptr to type field
			_sFindStruct
			bne.s	@Done							; error - no sRsrc type field found
			lea		-8(sp),sp						; alloc sRsrc_type buffer
			move.l	sp,spResult(a0)					; set buffer ptr
			move.l	#8,spSize(a0)					; set size to read
			_sReadStruct
			bne.s	@Done							; @@@@Remember to dealloc stack@@@@can't read it - error

			move.l	(sp)+,d0						; get category and type fields
			move.l	(sp)+,d3						; get drvrSW and drvrHW fields
			cmp.l	#(catBoard<<16)+typBoard,d0		; board type?
			bne.s	@Loop							; not found - continue search
			tst.l	d3								; drvrSW and drvrHW fields should be zero
			bne.s	@Loop							; not found - continue search

;	At this point, we have found the board sResource.  Read the board id

			move.l	a2,spsPointer(a0)				; restore ptr to sRsrc
			move.b	#Boardid,spId(a0)				; read board id field from board sRsrc
			_sReadWord
			bne.s	@Done							; some error - no board id
			move.w	spResult+2(a0),d4				; d4 = board id for new card

;	Done

@Done
			move.w	d4,d0							; return board id or zero
			movem.l	(sp)+,@regs
			rts

			endwith

;____________________________________________________________________________								<H8>
;
; FindDevBaseSlot0 (called from FindDevBase).
;
;	Entry:		a0 points to spBlock.
;	 			d1 is the major/minorbase offset.
;
;	Exit:		a0 points to spBlock.
;				d2 is the DevBase.
;
;	Trashes:	A1/D0-D1.
;____________________________________________________________________________

			Export	FindDevBaseSlot0

			With	SpBlock

FindDevBaseSlot0
			
			movea.l	UnivInfoPtr,a1					; point to the ProductInfo record
			adda.l	ProductInfo.VideoInfoPtr(a1),a1	; point to the VideoInfo record
			
			move.b	#sRsrcFlags,spId(a0)			; get flags to determine whether the pointer
			_sReadWord								; - should be 24 or 32 bit
			bne.s	@Do24							; no flag field - default to 24 bit space
			
			move.w	spResult+2(a0),d0				; get value of spFlags
			btst.l	#f32BitMode,D0					; see if 32- or 24-bit base address
			beq.s	@Do24							; branch if 24-bit base address	
				
			Add.l	VideoInfo.VRAMLogAddr32(a1),d1	; 32bit dev base to alias addr for slot zero
			Bra.s	@Done

@Do24		Add.l	VideoInfo.VRAMLogAddr24(a1),d1	; 24bit dev base to alias addr for slot zero
@Done		Move.l	d1,d2							; d2 has what we want
			Rts		
			
			EndWith

			Endp									; must have endp for RAM builds			<djw>

			End