mirror of
https://github.com/elliotnunn/supermario.git
synced 2024-11-26 01:49:19 +00:00
1463 lines
56 KiB
Plaintext
1463 lines
56 KiB
Plaintext
|
;__________________________________________________________________________________________________
|
|||
|
;
|
|||
|
; File: IOPMgr.a
|
|||
|
;
|
|||
|
; Contains: This module provides the message passing interface between the 680XX and the
|
|||
|
; drivers running on the IOPs. It also provides the routines to download code
|
|||
|
; and to initialize the IOPs.
|
|||
|
;
|
|||
|
; NOTE: The IOP hardware is called a "Peripheral Interface Controller" or "PIC",
|
|||
|
; which can cause some confusion with other names already in the vocabulary
|
|||
|
; of Macintosh developers ("PICT" for example), so from the software side,
|
|||
|
; we will call them "Input / Output Processors" or "IOP" to avoid (or create)
|
|||
|
; confusion.
|
|||
|
;
|
|||
|
; Written by: Gary G. Davidian
|
|||
|
;
|
|||
|
; Copyright © 1987-1993 by Apple Computer, Inc. All rights reserved.
|
|||
|
;
|
|||
|
; Change History (most recent first):
|
|||
|
;
|
|||
|
; <SM9> 11/10/93 KW fix localtalk for iop based machines on STP card use forSTP601v1
|
|||
|
; <SM8> 11/9/93 KW BTU on the STP card doesn't handle bytereads very well so
|
|||
|
; changed IOP stuff. Changes are only made when forSTP601=TRUE
|
|||
|
; <SM7> 8/20/93 chp References to SCCIOPBypassInt have been resolved to the more
|
|||
|
; general and historically accepted SccDecode entry point in
|
|||
|
; InterruptHandlers.a. It wasn't necessary to have two names for
|
|||
|
; the same thing.
|
|||
|
; <SM6> 11/19/92 RB When looking for iop code, look in ROM first.
|
|||
|
; <SM5> 9/10/92 kc Fix booting problem on Eclipse. (The last change was trashing a
|
|||
|
; register in IOPInterrupt)
|
|||
|
; <SM4> 6/11/92 PN Roll in Cmd-Shift-Esc patch from PatchIIci.a
|
|||
|
; <SM3> 5/21/92 kc Append "Trap" to the names of Enqueue and Dequeue to avoid name
|
|||
|
; conflict with the glue.
|
|||
|
; <9> 4/27/92 JSM Get rid of conditionals: isUniversal, hasIOPSCC, and hasIOPSWIM
|
|||
|
; are always true, left in the padForOverpatch conditional for
|
|||
|
; now.
|
|||
|
; <8> 12/30/91 RB Made a beq instruction use the long format in order to link. The
|
|||
|
; SCCIOPByPassInt routine cannot be accessed with a 16 bit
|
|||
|
; reference from ROM.
|
|||
|
; <7> 10/1/91 JSM Don’t use eclipseDebug.
|
|||
|
; <6> 8/30/91 JSM Cleanup header.
|
|||
|
; <5> 6/12/91 LN removed #include 'HardwareEqu.a'
|
|||
|
; <4> 9/1/90 BG Added a check to leave Eclipses in Bypass Mode if we are running
|
|||
|
; from RAM so that ReAnimator doesnt have its SCC communications
|
|||
|
; path kicked out from under itself.
|
|||
|
; <3> 1/11/90 CCH Added include of “HardwarePrivateEqu.a”.
|
|||
|
; <2> 1/11/90 SWC Set the bypass mode bit in SCCIOPFlag if we can't put the SCC
|
|||
|
; IOP into enhanced mode so that the serial and AppleTalk drivers
|
|||
|
; will at least configure correctly. Changed the initialization of
|
|||
|
; the SCC IOP SccIOCtlReg from $44 to $23 (per Bob Hollyer).
|
|||
|
; <2.2> 12/1/89 GGD Cleaned up after BAD DOG (SWC), don't include Private.a twice,
|
|||
|
; It was already included by Inc.Sum.a.
|
|||
|
; <2.1> 12/1/89 SWC Oops! Forgot to INCLUDE Private.a since that's where SCCIOPFlag
|
|||
|
; is defined.
|
|||
|
; <2.0> 12/1/89 SWC Changed the polarity of the mode bit (0) in SCCIOPFlag so that a
|
|||
|
; zero means we use IOP mode and a 1 means we use bypass so that
|
|||
|
; if PRAM is reset, Zone 5 will use IOP mode as its default state.
|
|||
|
; <1.9> 11/29/89 SWC Copied the PRAM byte controlling SCC IOP state to new low mem
|
|||
|
; variable SCCIOPFlag ($BFE) so that we can change the PRAM value
|
|||
|
; without hosing current IOP clients.
|
|||
|
; <1.8> 11/2/89 GGD NEEDED FOR ZONE-5 Changed the rules pertaining to the usage of
|
|||
|
; the IOP Ram Address Pointer register, The old convention was
|
|||
|
; that interrupts could trash it, the new convention is that
|
|||
|
; interrupts must save/restore it. Modified all routines to
|
|||
|
; preserve it, and improved the cross processor data transfer
|
|||
|
; performance by not having to mask interrupts and save/restore
|
|||
|
; the addr reg within the xfer loop. Added Vectoring of the
|
|||
|
; interrupt handler, and change internal calls of IOPMgr trap
|
|||
|
; routines to jump through the OS Dispatch table, to make future
|
|||
|
; patching easier.
|
|||
|
; <1.7> 7/25/89 GGD Fixed interrupt masking bug in Deferred Task queueing which
|
|||
|
; caused a circular queue if the SCC IOP interrupted the SWIM IOP
|
|||
|
; interrupt processing at the wrong time. NEEDED FOR F19, Code
|
|||
|
; size doesn't change.
|
|||
|
; <1.6> 7/14/89 GGD Added conditionals around the overpatch padding so that it can
|
|||
|
; be easily located. Turned off the Debugging VBL task. Improved
|
|||
|
; the handling of interrupts from unknown IOPs to check for the
|
|||
|
; SCC IOP, and pass the interrupt on to SCCIOPBypassInt if it was
|
|||
|
; from the SCC.
|
|||
|
; <1.5> 6/30/89 GGD Modified SCCIOPByPass to not hang waiting for a reply if the
|
|||
|
; Send failed. Added padding in preparation for the F19 overpatch.
|
|||
|
; <1.4> 5/29/89 GGD Added new routine SCCIOPHwInit which resets the SCC IOP, puts it
|
|||
|
; into bypass mode, and initializes the IOControl register holdoff
|
|||
|
; and wait delays.
|
|||
|
; <1.3> 5/20/89 GGD Removed IOP register equates, and moved them into HardwareEqu,
|
|||
|
; converted to use new names for registers and bits. Made the VBL
|
|||
|
; debugging task support code conditionally assembly so that it
|
|||
|
; can be easily turned off in the future. Re-wrote initialization
|
|||
|
; code to make it universal.
|
|||
|
; <1.2> 2/8/89 SGS Added support for SCC IOP
|
|||
|
; <1.1> 11/10/88 CCH Fixed Header.
|
|||
|
; <1.0> 11/9/88 CCH Adding to EASE.
|
|||
|
; <1.1> 11/6/88 GGD Modified interrupt handler to allow for non-deferrable message
|
|||
|
; handling (needed for MacsBug/ADB support). Fixed a few
|
|||
|
; un-noticed bugs. Changed interrupt handler to service all
|
|||
|
; interrupt sources before returning because on IoMac the SWIM IOP
|
|||
|
; interrupt line is an edge-triggered VIA input and not a level
|
|||
|
; triggered interrupt. Changed install IOP code to use
|
|||
|
; _RGetResource to find the 'iopc' resource so that a system file
|
|||
|
; can override it.
|
|||
|
; <1.0> 10/7/88 rwh New to EASE today.
|
|||
|
; 6/6/88 GGD Re-Written to allow queued xmt requests, and more flexible data
|
|||
|
; structures to allow IOPs to be added dynamicaly.
|
|||
|
; 7/31/87 GGD Created today.
|
|||
|
;__________________________________________________________________________________________________
|
|||
|
|
|||
|
TITLE 'IOPmgr - Input / Output Processor Manager'
|
|||
|
|
|||
|
BLANKS ON
|
|||
|
STRING ASIS
|
|||
|
PRINT OFF
|
|||
|
LOAD 'StandardEqu.d'
|
|||
|
INCLUDE 'HardwarePrivateEqu.a'
|
|||
|
INCLUDE 'UniversalEqu.a'
|
|||
|
PRINT ON
|
|||
|
INCLUDE 'IOPequ.a'
|
|||
|
MACHINE MC68020
|
|||
|
PRINT NOMDIR
|
|||
|
|
|||
|
macro
|
|||
|
assert &boolExpr
|
|||
|
if not(&Eval(&boolExpr)) then
|
|||
|
aerror &concat('Assertion Failed - ',&boolExpr)
|
|||
|
endif
|
|||
|
endm
|
|||
|
|
|||
|
JIOPMoveData EQU ($88*4)+OSTable ; OS trap table entry for _IOPMoveData
|
|||
|
|
|||
|
Debugging equ 0 ; use debugging VBL task <1.3><1.6>
|
|||
|
|
|||
|
IOPPRAM equ $00000089 ; IOP PRAM index <1.2>
|
|||
|
Active equ $FF ; dummy value for active
|
|||
|
ReplyLen equ $01 ; bypass message reply length
|
|||
|
MsgLen equ $03 ; bypass message length
|
|||
|
KernMsg equ $01 ; kernel message box number
|
|||
|
ByPassCmd equ $04 ; bypass command
|
|||
|
|
|||
|
|
|||
|
MaxIOPRamAddr equ $7FFF ; IOP has 32KB RAM
|
|||
|
IOPMsgPage equ $02
|
|||
|
IOPXmtMsgBase equ $0200
|
|||
|
IOPRcvMsgBase equ $0300
|
|||
|
PatchReqAddr equ $021F ; Shared Loc to sync patching
|
|||
|
IOPAliveAddr equ $031F ; IOP sets to $FF when idle
|
|||
|
|
|||
|
AliveVBLrate equ 127 ; 127 ticks = 2.1166666 seconds
|
|||
|
|
|||
|
; Message State encodings
|
|||
|
MsgIdle equ 0 ; message buffer idle
|
|||
|
NewMsgSent equ 1 ; new message just sent
|
|||
|
MsgReceived equ 2 ; message received, and being processed
|
|||
|
MsgCompleted equ 3 ; message processing complete, reply available
|
|||
|
|
|||
|
|
|||
|
; Internal IOP Manager data structures (IOPmgrVars is the root pointer)
|
|||
|
|
|||
|
IOPMgrGlobals record {IOPInfoPtrs},increment ; miscellaneous globals used by the IOP manager
|
|||
|
if Debugging then ; <1.3>
|
|||
|
ds.w 1 ; force nice long word alignments
|
|||
|
VTask ds.b vblPhase+2 ; Virtical Retrace Task info
|
|||
|
endif ; <1.3>
|
|||
|
DTask ds.b dtQElSize ; Deferred task Manager task info
|
|||
|
ds.b 1 ; filler
|
|||
|
DTaskQueued ds.b 1 ; $FF if task queued or running, $00 when done
|
|||
|
CompleteQHdr ds.b qHeadSize ; queue of completed requests
|
|||
|
IntHandlerPtr ds.l 1 ; [long] ptr to IOP interrupt handler
|
|||
|
IOPInfoPtrs ds.l NumberOfIOPs ; [long] ptrs to IOP info for each IOP
|
|||
|
|
|||
|
IOPInfos ds.b 0 ; start of IOPInfo records for installed IOPs
|
|||
|
|
|||
|
GlobalsSize equ *-IOPMgrGlobals ; size of IOPMgrGlobals
|
|||
|
endr
|
|||
|
|
|||
|
|
|||
|
IOPmgr PROC EXPORT
|
|||
|
EXPORT InitIOPMgr
|
|||
|
EXPORT IOPInfoAccess
|
|||
|
EXPORT IOPMsgRequest
|
|||
|
EXPORT IOPMoveData
|
|||
|
EXPORT IOPInterrupt
|
|||
|
EXPORT MoveReqHandler
|
|||
|
EXPORT SCCIOPByPass ;<1.2>
|
|||
|
EXPORT SCCIOPHwInit ;<1.4>
|
|||
|
IMPORT SccDecode
|
|||
|
IMPORT EnqueueTrap
|
|||
|
IMPORT DequeueTrap
|
|||
|
|
|||
|
TITLE 'IOPmgr - IOP Info Access'
|
|||
|
|
|||
|
;_______________________________________________________________________
|
|||
|
;
|
|||
|
; Routine: IOPInfoAccess
|
|||
|
; Inputs: A0 - pointer to IOPAccessInfo paramater block
|
|||
|
; Outputs: D0 - Result Code (NoErr/paramErr)
|
|||
|
; Destroys: A0, A1, D0, D1, D2
|
|||
|
; Calls: none
|
|||
|
; Called by: OsTrap Dispatch Table
|
|||
|
;
|
|||
|
; Function: Performs various access operations on an IOP.
|
|||
|
;
|
|||
|
;_______________________________________________________________________
|
|||
|
|
|||
|
with IOPAccessInfo,IOPInfo,IOPRequestInfo,IOPMoveInfo
|
|||
|
IOPInfoAccess ; a0-a1/d1-d2 saved by OsTrap dispatch
|
|||
|
@SavedRegs reg a2-a4/d3-d4
|
|||
|
movem.l @SavedRegs,-(sp) ; save registers
|
|||
|
moveq.l #NoErr,d0 ; assume success
|
|||
|
moveq.l #0,d1 ; zero extend
|
|||
|
|
|||
|
move.b iaAccessKind(a0),d1 ; get the access kind
|
|||
|
cmpi.b #iaRemoveIOP,d1 ; range check it
|
|||
|
bhi.s @paramError ; if out of range
|
|||
|
move.w @JumpTable(d1.w*2),d2 ; get the routine offset
|
|||
|
|
|||
|
move.b iaIOPNumber(a0),d1 ; get the IOP number
|
|||
|
cmpi.b #MaxIopNum,d1 ; range check it
|
|||
|
bhi.s @paramError ; if out of range
|
|||
|
lea ([IOPmgrVars],d1.w*4,\ ; a1 := pointer into IOPInfoPtrs table
|
|||
|
IOPMgrGlobals.IOPInfoPtrs),a1
|
|||
|
|
|||
|
jmp @JumpTable(d2.w) ; process the request
|
|||
|
|
|||
|
@JumpTable assert iaInstallIOP=((*-@JumpTable)/2)
|
|||
|
dc.w @InstallIOP-@JumpTable
|
|||
|
|
|||
|
assert iaGetIOPInfo=((*-@JumpTable)/2)
|
|||
|
dc.w @GetIOPInfo-@JumpTable
|
|||
|
|
|||
|
assert iaRemoveIOP=((*-@JumpTable)/2)
|
|||
|
dc.w @RemoveIOP-@JumpTable
|
|||
|
|
|||
|
@paramError moveq.l #paramErr,d0 ; indicate failure
|
|||
|
bra.s @Done ; restore registers and return
|
|||
|
|
|||
|
|
|||
|
@GetIOPInfo move.l (a1),iaIOPInfoPtr(a0) ; return the pointer to IOP Info
|
|||
|
@Done movem.l (sp)+,@SavedRegs ; restore registers
|
|||
|
rts ; and return
|
|||
|
|
|||
|
|
|||
|
@RemoveIOP move.l (a1),iaIOPInfoPtr(a0) ; return the old pointer to IOP Info
|
|||
|
clr.l (a1) ; and remove it from the table
|
|||
|
bra.s @Done ; return with success
|
|||
|
|
|||
|
|
|||
|
@InstallIOP tst.l (a1) ; see if it is already installed
|
|||
|
bne.s @paramError ; if so, it's an error
|
|||
|
movea.l iaIOPInfoPtr(a0),a4 ; save IOPInfo pointer
|
|||
|
move.l d1,d3 ; save IOP number
|
|||
|
subq.l #4,sp ; allocate function result
|
|||
|
move.l #'iopc',-(sp) ; ResType = 'iopc' (IOP code)
|
|||
|
move.w d3,-(sp) ; theID = IOP number
|
|||
|
move.w #MapTrue,RomMapInsert ; look in ROM first <SM6> rb
|
|||
|
_GetResource ; top of stack <- IOP code resource handle <SM6> rb
|
|||
|
move.l (sp)+,d0 ; d0 <- resource handle
|
|||
|
beq.s @paramError ; error if neither found
|
|||
|
movea.l d0,a0 ; a0 <- resource handle
|
|||
|
movea.l (a0),a0 ; a0 <- resource pointer
|
|||
|
|
|||
|
; Note that this routine will destroy the old contents of the IopAddrReg, but
|
|||
|
; since we are also destroying all of it's RAM contents, it isn't worth the
|
|||
|
; trouble to save it.
|
|||
|
move.w sr,-(sp) ; save interrupt priority
|
|||
|
ori.w #HiIntMask,sr ; disable interrupts
|
|||
|
assert IopAddrRegPtr=0
|
|||
|
movea.l (a4)+,a2 ; a2 <- IOP Ram Address Reg pointer
|
|||
|
assert IopDataRegPtr=(IopAddrRegPtr+4)
|
|||
|
movea.l (a4)+,a3 ; a3 <- IOP Ram Data Reg pointer
|
|||
|
assert IopCtlRegPtr=(IopDataRegPtr+4)
|
|||
|
movea.l (a4),a1 ; a1 <- IOP Status/Ctl Reg pointer
|
|||
|
move.b #resetIopRun,(a1) ; init the Status/Ctl reg, hold IOP reset
|
|||
|
|
|||
|
; Fill all of IOP memory with $FF
|
|||
|
|
|||
|
move.w #MaxIOPRamAddr,d0 ; d0 <- fill addr / loop counter
|
|||
|
moveq.l #-1,d1 ; d1 <- fill data ($FF)
|
|||
|
@Fill_FF_Loop
|
|||
|
move.w d0,(a2) ; load the address register
|
|||
|
move.b d1,(a3) ; write the data to the data reg
|
|||
|
dbra d0,@Fill_FF_Loop ; fill all of IOP memory with $FF
|
|||
|
|
|||
|
; Complement all of IOP memory. $FF -> $00
|
|||
|
|
|||
|
move.w #MaxIOPRamAddr,d0 ; d0 <- Complement addr / loop counter
|
|||
|
@ComplementLoop move.w d0,(a2) ; load the address register
|
|||
|
move.b (a3),d1 ; read the data byte
|
|||
|
not.b d1 ; complement the data
|
|||
|
move.w d0,(a2) ; reload the address register
|
|||
|
move.b d1,(a3) ; write the data to the data reg
|
|||
|
dbne d0,@ComplementLoop ; Complement all of IOP memory
|
|||
|
bne.s @InitError ; if didn't read back $FF (HW error)
|
|||
|
|
|||
|
; Check all of IOP memory for $00
|
|||
|
|
|||
|
move.w #MaxIOPRamAddr,d0 ; d0 <- Check addr / loop counter
|
|||
|
@Check_00_Loop move.w d0,(a2) ; load the address register
|
|||
|
move.b (a3),d1 ; read the data byte
|
|||
|
dbne d0,@Check_00_Loop ; Check all of IOP memory for $00
|
|||
|
bne.s @InitError ; if didn't read back $00 (HW error)
|
|||
|
|
|||
|
; Download and Verify the IOP code
|
|||
|
|
|||
|
movea.l a0,a1 ; a1 <- copy of code start
|
|||
|
@SegLoop move.b (a0)+,d0 ; get the size of the segment
|
|||
|
beq.s @LoadDone ; size of zero terminates segment list
|
|||
|
move.w (a0)+,(a2) ; setup the code load address
|
|||
|
|
|||
|
@LoadLoop move.b (a0)+,(a3) ; download a byte
|
|||
|
subq.b #1,d0 ; decrement the loop count
|
|||
|
bne.s @LoadLoop ; load all of the bytes
|
|||
|
|
|||
|
move.b (a1)+,d0 ; get size of the segment
|
|||
|
move.w (a1)+,(a2) ; setup the code check address
|
|||
|
|
|||
|
@CheckCodeLoop move.b (a3),d1 ; read a byte from the IOP
|
|||
|
IF forSTP601v1 THEN
|
|||
|
subq.w #3, (a2) ;ZZZ
|
|||
|
ENDIF
|
|||
|
cmp.b (a1)+,d1 ; compare it to what was loaded
|
|||
|
bne.s @InitError ; if download verify error (HW error)
|
|||
|
subq.b #1,d0 ; decrement the loop count
|
|||
|
bne.s @CheckCodeLoop ; check all of the bytes
|
|||
|
|
|||
|
bra.s @SegLoop ; load the next segment
|
|||
|
@LoadDone
|
|||
|
|
|||
|
; Start IOP execution
|
|||
|
|
|||
|
move.w #IOPAliveAddr,(a2) ; Address the ALIVE flag
|
|||
|
clr.b (a3) ; clear ALIVE
|
|||
|
assert IopCtlRegPtr=(IopDataRegPtr+4)
|
|||
|
movea.l (a4),a1 ; a1 <- IOP Status/Ctl Reg pointer
|
|||
|
move.b #setIopRun,(a1) ; release IOP reset
|
|||
|
moveq.l #-1,d0 ; timeout loop counter
|
|||
|
@AliveWaitLoop
|
|||
|
move.w #IOPAliveAddr,(a2) ; Address the ALIVE flag
|
|||
|
cmpi.b #$FF,(a3) ; check for alive
|
|||
|
dbeq d0,@AliveWaitLoop ; loop until alive
|
|||
|
beq.s @Alive ; error if time out (HW error)
|
|||
|
@InitError move.w (sp)+,sr ; restore interrupt priority
|
|||
|
bra.w @paramError ; return with error
|
|||
|
@Alive
|
|||
|
|
|||
|
; Update the Max Xmt / Rcv messages supported
|
|||
|
|
|||
|
move.w #IOPXmtMsgBase,(a2) ; address Xmt MAX
|
|||
|
addq.l #MaxXmt-IopCtlRegPtr,a4 ; point to MaxXmt in IOPInfo
|
|||
|
move.b (a3),(a4)+ ; get the Xmt MAX
|
|||
|
|
|||
|
move.w #IOPRcvMsgBase,(a2) ; address Rcv MAX
|
|||
|
assert MaxRcv=(MaxXmt+1)
|
|||
|
move.b (a3),(a4) ; get the Rcv MAX
|
|||
|
|
|||
|
suba.w #MaxRcv,a4 ; point to start of IOPInfo record
|
|||
|
move.l a4,([IOPmgrVars],d3.w*4,\ ; install pointer into IOPInfoPtrs table
|
|||
|
IOPMgrGlobals.IOPInfoPtrs)
|
|||
|
|
|||
|
lea MoveReqInfo(a4),a0 ; a0 := pointer to IOPRequestInfo
|
|||
|
lea irIOPNumber(a0),a1 ; a1 := initialization pointer
|
|||
|
move.b d3,(a1)+ ; initialize the irIOPNumber field
|
|||
|
assert irRequestKind=(irIOPNumber+1)
|
|||
|
move.b #irWaitRcvMessage,(a1)+ ; initialize the irRequestKind field
|
|||
|
|
|||
|
assert irMsgNumber=(irRequestKind+1)
|
|||
|
assert irMessageLen=(irMsgNumber+1)
|
|||
|
assert irReplyLen=(irMessageLen+1)
|
|||
|
assert irReqActive=(irReplyLen+1)
|
|||
|
move.l #(1<<24)+\ ; irMsgNumber := 1
|
|||
|
(imMoveInfoSize<<16)+\ ; irMessageLen := imMoveInfoSize
|
|||
|
(imMoveInfoSize<<8)+\ ; irReplyLen := imMoveInfoSize
|
|||
|
(0),(a1)+ ; irReqActive := 0
|
|||
|
|
|||
|
lea MoveReqBuffer(a4),a2 ; a2 := pointer to IOPMoveInfo
|
|||
|
assert irMessagePtr=(irReqActive+1)
|
|||
|
move.l a2,(a1)+ ; irMessagePtr := MoveReqBuffer
|
|||
|
assert irReplyPtr=(irMessagePtr+4)
|
|||
|
move.l a2,(a1)+ ; irReplyPtr := MoveReqBuffer
|
|||
|
|
|||
|
lea MoveReqHandler,a2 ; a2 := completion routine address
|
|||
|
assert irHandler=(irReplyPtr+4)
|
|||
|
move.l a2,(a1)+ ; irHandler := MoveReqHandler
|
|||
|
_IOPMsgRequest ; install the message handler
|
|||
|
|
|||
|
move.w (sp)+,sr ; restore interrupt priority
|
|||
|
bra.w @Done ; return with success
|
|||
|
endwith
|
|||
|
TITLE 'IOPmgr - IOP Message Request'
|
|||
|
|
|||
|
;_______________________________________________________________________
|
|||
|
;
|
|||
|
; Routine: IOPMsgRequest
|
|||
|
; Inputs: A0 - pointer to IOPRequestInfo paramater block
|
|||
|
; Outputs: D0 - Result Code (NoErr/paramErr)
|
|||
|
; irReqActive - set to $FF, until request completed
|
|||
|
; Destroys: A0, A1, D0, D1, D2
|
|||
|
; Calls: CopyToIOP, Enqueue
|
|||
|
; Called by: OsTrap Dispatch Table
|
|||
|
;
|
|||
|
; Function:
|
|||
|
;
|
|||
|
;_______________________________________________________________________
|
|||
|
|
|||
|
with IOPRequestInfo
|
|||
|
IOPMsgRequest
|
|||
|
@SavedRegs reg a2/a3/d3/d4 ; a0-a1/d1-d2 saved by OsTrap dispatch
|
|||
|
movem.l @SavedRegs,-(sp) ; save registers
|
|||
|
|
|||
|
moveq.l #paramErr,d0 ; assume Parameter Error status
|
|||
|
moveq.l #0,d2 ; prepare to zero extend
|
|||
|
|
|||
|
lea irIOPNumber(a0),a1 ; a1 <- pointer to parameters
|
|||
|
move.b (a1)+,d2 ; d2 <- zero extended IOP number
|
|||
|
cmpi.w #MaxIopNum,d2 ; range check the iop number
|
|||
|
bhi.s @toParamError ; if out of range
|
|||
|
|
|||
|
movea.l IOPmgrVars,a2 ; get message table base
|
|||
|
movea.l IOPMgrGlobals.IOPInfoPtrs(a2,d2.w*4),a2 ; a2 <- iop info ptr
|
|||
|
move.l a2,d3 ; test for zero
|
|||
|
beq.s @toParamError ; IOP isn't initialized
|
|||
|
move.b IOPInfo.MaxXmt(a2),d3 ; d3 := max message number
|
|||
|
|
|||
|
assert irRequestKind=(irIOPNumber+1)
|
|||
|
move.b (a1)+,d2 ; d2 <- zero extended request Kind
|
|||
|
assert irSendXmtMessage=0
|
|||
|
beq.s @CheckReqKind ; if for Xmt Msg, max is correct
|
|||
|
move.b IOPInfo.MaxRcv(a2),d3 ; d3 := max message number
|
|||
|
@CheckReqKind
|
|||
|
cmpi.w #irRemoveRcvWaiter,d2 ; range check the request Kind
|
|||
|
bhi.s @toParamError ; if out of range
|
|||
|
move.w @JumpTable(d2.w*2),d1 ; d1 := offset to routine
|
|||
|
|
|||
|
assert irMsgNumber=(irRequestKind+1)
|
|||
|
move.b (a1)+,d2 ; d2 <- zero extended Msg number
|
|||
|
beq.s @paramError ; Message zero is reserved
|
|||
|
cmp.b d3,d2 ; compare to max
|
|||
|
bhi.s @paramError ; if out of range
|
|||
|
move.w d2,d3 ; d3.w := iop state address
|
|||
|
assert IOPMsgEntry.IOPMsgEntrySize=(1<<4)
|
|||
|
lsl.w #4,d2 ; d2 := index into MsgTable
|
|||
|
|
|||
|
moveq.l #MaxIopMsgLen,d4 ; prepare to check message lengths
|
|||
|
assert irMessageLen=(irMsgNumber+1)
|
|||
|
cmp.b (a1)+,d4 ; check the message length
|
|||
|
blo.s @paramError ; if out of range
|
|||
|
assert irReplyLen=(irMessageLen+1)
|
|||
|
cmp.b (a1)+,d4 ; check the reply length
|
|||
|
blo.s @paramError ; if out of range
|
|||
|
|
|||
|
lea IOPInfo.MsgTable+IOPMsgEntry.RcvMsgInfoPtr(a2,d2.w),a3
|
|||
|
jmp @JumpTable(d1.w) ; process the request
|
|||
|
@JumpTable assert irSendXmtMessage=((*-@JumpTable)/2)
|
|||
|
dc.w @SendXmtMessage-@JumpTable
|
|||
|
|
|||
|
assert irSendRcvReply=((*-@JumpTable)/2)
|
|||
|
dc.w @SendRcvReply-@JumpTable
|
|||
|
|
|||
|
assert irWaitRcvMessage=((*-@JumpTable)/2)
|
|||
|
dc.w @WaitRcvMessage-@JumpTable
|
|||
|
|
|||
|
assert irRemoveRcvWaiter=((*-@JumpTable)/2)
|
|||
|
dc.w @RemoveRcvWaiter-@JumpTable
|
|||
|
|
|||
|
@toParamError
|
|||
|
bra.s @paramError ; allow short branches
|
|||
|
|
|||
|
@SendXmtMessage
|
|||
|
assert irReqActive=(irReplyLen+1)
|
|||
|
st (a1) ; mark it as active now
|
|||
|
lea IOPMsgEntry.XmtMsgQHdr-IOPMsgEntry.RcvMsgInfoPtr(a3),a1
|
|||
|
move.w sr,d2 ; d2 := saved interrupt priority
|
|||
|
ori.w #HiIntMask,sr ; no interrupts while checking queue
|
|||
|
cmpa.l qHead(a1),a0 ; see if already the head of the queue
|
|||
|
beq.s @SkipEnqueue ; if so, don't enqueue it
|
|||
|
jsr EnqueueTrap ; enqueue the request
|
|||
|
cmpa.l qHead(a1),a0 ; see if at the head of the queue
|
|||
|
bne.s @notAtHead ; exit with request queued
|
|||
|
@SkipEnqueue
|
|||
|
move.w d2,sr ; restore interrupt priority
|
|||
|
moveq.l #0,d0 ; zero extend message length
|
|||
|
move.b irMessageLen(a0),d0 ; get the message buffer length
|
|||
|
movea.l irMessagePtr(a0),a1 ; get the message buffer pointer
|
|||
|
move.b #NewMsgSent,-(sp) ; save new message state
|
|||
|
ori.w #IOPXmtMsgBase,d3 ; d3 := iop message state address
|
|||
|
@SendAndInterrupt
|
|||
|
move.w d3,-(sp) ; save state address for later
|
|||
|
assert MaxIOPMsgLen=(1<<5)
|
|||
|
lsl.b #5,d3 ; d3 := iop message address
|
|||
|
move.l IOPInfo.IopCtlRegPtr(a2),-(sp) ; save ctl/status reg ptr for later
|
|||
|
movea.l IOPInfo.IopDataRegPtr(a2),a3 ; (a3) := IOPRamDataReg
|
|||
|
movea.l IOPInfo.IopAddrRegPtr(a2),a2 ; (a2) := IOPRamAddrReg
|
|||
|
move.w (a2),-(sp) ; save the IOPRamAddrReg
|
|||
|
move.w d3,(a2) ; setup iop ram address
|
|||
|
bsr.w CopyToIop ; copy the message to the IOP
|
|||
|
move.w (sp)+,d0 ; get the saved IOPRamAddrReg
|
|||
|
movea.l (sp)+,a1 ; (a1) := IopCtlReg
|
|||
|
move.w (sp)+,(a2) ; setup iop ram address of message state
|
|||
|
move.b (sp)+,(a3) ; set new state
|
|||
|
move.w d0,(a2) ; restore the IOPRamAddrReg
|
|||
|
move.b #setIopGenInt,(a1) ; interrupt the IOP
|
|||
|
@success moveq.l #noErr,d0 ; report success
|
|||
|
@paramError movem.l (sp)+,@SavedRegs
|
|||
|
rts ; all done
|
|||
|
@notAtHead move.w d2,sr ; restore interrupt priority
|
|||
|
bra.s @success ; return with success
|
|||
|
|
|||
|
@SendRcvReply
|
|||
|
cmpa.l (a3),a0 ; see if we are the waiter
|
|||
|
bne.s @paramError ; return with error if not
|
|||
|
assert irReqActive=(irReplyLen+1)
|
|||
|
tst.b (a1) ; see if the request is already active
|
|||
|
bne.s @paramError ; return with error if active
|
|||
|
st (a1) ; mark it as active now
|
|||
|
moveq.l #0,d0 ; zero extend reply length
|
|||
|
move.b irReplyLen(a0),d0 ; get the reply buffer length
|
|||
|
movea.l irReplyPtr(a0),a1 ; get the reply buffer pointer
|
|||
|
move.b #MsgCompleted,-(sp) ; save new message state
|
|||
|
ori.w #IOPRcvMsgBase,d3 ; d3 := iop message state address
|
|||
|
bra.s @SendAndInterrupt ; send the reply and interrupt the IOP
|
|||
|
|
|||
|
|
|||
|
@WaitRcvMessage
|
|||
|
tst.l (a3) ; see if waiter already exists
|
|||
|
bne.s @paramError ; return with error if so
|
|||
|
assert irReqActive=(irReplyLen+1)
|
|||
|
st (a1) ; mark the request as active
|
|||
|
move.l a0,(a3) ; setup waiter
|
|||
|
bra.s @success ; return with success
|
|||
|
|
|||
|
|
|||
|
@RemoveRcvWaiter
|
|||
|
cmpa.l (a3),a0 ; see if we are the waiter
|
|||
|
bne.s @paramError ; return with error if not
|
|||
|
assert irReqActive=(irReplyLen+1)
|
|||
|
clr.b (a1) ; mark the request as complete
|
|||
|
clr.l (a3) ; setup waiter
|
|||
|
bra.s @success ; return with success
|
|||
|
|
|||
|
endwith
|
|||
|
TITLE 'IOPmgr - IOP Move Data'
|
|||
|
|
|||
|
;_______________________________________________________________________
|
|||
|
;
|
|||
|
; Routine: IOPMoveData
|
|||
|
; Inputs: A0 - pointer to IOPMoveInfo paramater block
|
|||
|
; Outputs: D0 - Result Code (NoErr/paramErr)
|
|||
|
; Destroys: A0, A1, D0, D1, D2
|
|||
|
; Calls: CopyToIOP, CopyFromIOP, CompareWithIop
|
|||
|
; Called by: OsTrap Dispatch Table
|
|||
|
;
|
|||
|
; Function: Moves, or compares data between the IOP and the Host
|
|||
|
; memories, using the parameters inthe IOPMoveInfo parameter
|
|||
|
; block. Also has a special mode to apply patches to IOP
|
|||
|
; memory as an atomic operation.
|
|||
|
;
|
|||
|
;_______________________________________________________________________
|
|||
|
|
|||
|
with IOPMoveInfo
|
|||
|
IOPMoveData
|
|||
|
@SavedRegs reg a2/a3 ; a0-a1/d1-d2 saved by OsTrap dispatch
|
|||
|
movem.l @SavedRegs,-(sp) ; save registers
|
|||
|
moveq.l #paramErr,d0 ; assume Parameter Error status
|
|||
|
moveq.l #0,d2 ; prepare to zero extend
|
|||
|
|
|||
|
assert imCopyKind=0
|
|||
|
move.b (a0)+,d2 ; d2 <- zero extended Copy Kind
|
|||
|
cmpi.w #imPatchIop,d2 ; range check the copy kind
|
|||
|
bhi.s @paramError ; if out of range
|
|||
|
move.w @JumpTable(d2.w*2),d1 ; d1 := offset to copy routine
|
|||
|
|
|||
|
assert imIOPNumber=(imCopyKind+1)
|
|||
|
move.b (a0)+,d2 ; d2 <- zero extended IOP number
|
|||
|
cmpi.w #MaxIopNum,d2 ; range check the iop number
|
|||
|
bhi.s @paramError ; if out of range
|
|||
|
|
|||
|
movea.l IOPmgrVars,a1 ; get message table base
|
|||
|
movea.l IOPMgrGlobals.IOPInfoPtrs(a1,d2.w*4),a2 ; get iop info base address
|
|||
|
move.l a2,d2 ; test for zero
|
|||
|
beq.s @paramError ; IOP isn't initialized
|
|||
|
|
|||
|
movea.l IOPInfo.IopDataRegPtr(a2),a3 ; (a3) := IOPRamDataReg
|
|||
|
movea.l IOPInfo.IopAddrRegPtr(a2),a2 ; (a2) := IOPRamAddrReg
|
|||
|
|
|||
|
moveq.l #0,d0 ; zero extend to long
|
|||
|
assert imByteCount=(imIOPNumber+1)
|
|||
|
move.w (a0)+,d0 ; d0 := byte count
|
|||
|
|
|||
|
assert imHostAddr=(imByteCount+2)
|
|||
|
movea.l (a0)+,a1 ; a1 := host buffer address
|
|||
|
move.w (a2),-(sp) ; save the IOPRamAddrReg
|
|||
|
assert imIopAddr=(imHostAddr+4)
|
|||
|
move.w (a0)+,(a2) ; setup iop ram address
|
|||
|
assert imCompRel=(imIopAddr+2) ; a0 pointer to imCompRel for CompareWithIop
|
|||
|
jsr @JumpTable(d1.w) ; do the copy
|
|||
|
move.w (sp)+,(a2) ; restore the IOPRamAddrReg
|
|||
|
moveq.l #noErr,d0 ; return No Error status
|
|||
|
|
|||
|
@paramError
|
|||
|
@return movem.l (sp)+,@SavedRegs ; restore registers
|
|||
|
rts
|
|||
|
|
|||
|
@JumpTable assert imIopToHost=((*-@JumpTable)/2)
|
|||
|
dc.w CopyFromIop-@JumpTable
|
|||
|
|
|||
|
assert imHostToIop=((*-@JumpTable)/2)
|
|||
|
dc.w CopyToIop-@JumpTable
|
|||
|
|
|||
|
assert imCompare=((*-@JumpTable)/2)
|
|||
|
dc.w CompareWithIop-@JumpTable
|
|||
|
|
|||
|
assert imPatchIop=((*-@JumpTable)/2)
|
|||
|
dc.w @Patch-@JumpTable
|
|||
|
|
|||
|
|
|||
|
@Patch move.w sr,d2 ; d2 := saved interrupt priority
|
|||
|
ori.w #HiIntMask,sr ; disable interrupts
|
|||
|
move.w #PatchReqAddr,d1 ; address of Patch Request Byte
|
|||
|
move.w d1,(a2) ; setup the IOP address
|
|||
|
moveq.l #NewMsgSent,d0 ; patch request state byte
|
|||
|
move.b d0,(a3) ; issue the patch request
|
|||
|
|
|||
|
@PatchSync move.w d1,(a2) ; setup the IOP address
|
|||
|
cmp.b (a3),d0 ; see if patch request was accepted
|
|||
|
beq.s @PatchSync ; wait until it is accepted
|
|||
|
|
|||
|
@PatchSegLoop
|
|||
|
move.b (a1)+,d0 ; get the size of the segment
|
|||
|
beq.s @PatchDone ; size of zero terminates segment list
|
|||
|
move.w (a1)+,(a2) ; setup the patch load address
|
|||
|
|
|||
|
@PatchLoadLoop
|
|||
|
move.b (a1)+,(a3) ; patch a byte
|
|||
|
subq.b #1,d0 ; decrement the loop count
|
|||
|
bne.s @PatchLoadLoop ; load all of the bytes
|
|||
|
bra.s @PatchSegLoop ; load the next segment
|
|||
|
|
|||
|
@PatchDone move.w d1,(a2) ; setup the IOP address
|
|||
|
move.b #MsgIdle,(a3) ; tell the IOP that patching is done
|
|||
|
move.w d2,sr ; restore interrupt priority
|
|||
|
rts ; patching is complete
|
|||
|
|
|||
|
endwith
|
|||
|
TITLE 'IOPmgr - Copy From IOP'
|
|||
|
|
|||
|
;_______________________________________________________________________
|
|||
|
;
|
|||
|
; Routine: CopyFromIop
|
|||
|
; Inputs: A1 - pointer to Host RAM buffer (dest addr)
|
|||
|
; A2 - pointer to IOPRamAddrReg
|
|||
|
; (A2) - IOP data address (source addr)
|
|||
|
; A3 - pointer to IOPRamDataReg
|
|||
|
; D0 - (word) transfer byte count (zero extended to long)
|
|||
|
; Outputs: none
|
|||
|
; Destroys: A1, D0, D1
|
|||
|
; Calls: none
|
|||
|
; Called by: IOPInterrupt, IOPMoveData
|
|||
|
;
|
|||
|
; Function: Moves data from the IOP to the Host memory.
|
|||
|
;
|
|||
|
;_______________________________________________________________________
|
|||
|
|
|||
|
CopyFromIop cmpi.w #3,d0 ; check for very short copy
|
|||
|
bls.s @veryShort ; skip alignment if very short
|
|||
|
move.l a1,d1 ; get the Host Address
|
|||
|
andi.w #$0003,d1 ; check for long word alignment
|
|||
|
beq.s @Aligned ; if no alignment needed
|
|||
|
subq.w #4,d1 ; get byte count bias
|
|||
|
add.w d1,d0 ; adjust the byte count
|
|||
|
IF forSTP601v1 THEN
|
|||
|
jmp @Aligned(d1.w*4) ; ZZZdo the alignment
|
|||
|
ELSE
|
|||
|
jmp @Aligned(d1.w*2) ; do the alignment
|
|||
|
ENDIF
|
|||
|
move.b (a3),(a1)+ ; move a byte
|
|||
|
IF forSTP601v1 THEN
|
|||
|
subq.w #3, (a2) ;ZZZ
|
|||
|
move.b (a3),(a1)+ ; move a byte
|
|||
|
subq.w #3, (a2) ;ZZZ
|
|||
|
move.b (a3),(a1)+ ; move a byte
|
|||
|
subq.w #3, (a2) ;ZZZ
|
|||
|
ELSE
|
|||
|
move.b (a3),(a1)+ ; move a byte
|
|||
|
move.b (a3),(a1)+ ; move a byte
|
|||
|
ENDIF
|
|||
|
@Aligned ror.l #2,d0 ; save tail byte count in high 2 bits
|
|||
|
moveq.l #7,d1 ; mask for starting index
|
|||
|
and.w d0,d1 ; number of long words to move first
|
|||
|
neg.w d1 ; negate to index backwards
|
|||
|
lsr.w #3,d0 ; number of 32 byte blocks to move
|
|||
|
jmp @CopyStart(d1.w*2) ; jump into the loop
|
|||
|
|
|||
|
@CopyLoop move.l (a3),(a1)+ ; move a 32 byte block of data....
|
|||
|
move.l (a3),(a1)+ ; ... 4 bytes at a time
|
|||
|
move.l (a3),(a1)+
|
|||
|
move.l (a3),(a1)+
|
|||
|
move.l (a3),(a1)+
|
|||
|
move.l (a3),(a1)+
|
|||
|
move.l (a3),(a1)+
|
|||
|
move.l (a3),(a1)+
|
|||
|
@CopyStart dbra d0,@CopyLoop ; loop through all of the blocks
|
|||
|
|
|||
|
add.l d0,d0 ; load c and n with remaining 2 bits
|
|||
|
bcs.s @Copy2or3bytes
|
|||
|
@Copy0or1byte
|
|||
|
bpl.s @exit ; if no bytes left
|
|||
|
move.b (a3),(a1) ; copy the last byte
|
|||
|
IF forSTP601v1 THEN
|
|||
|
subq.w #3, (a2) ;ZZZ
|
|||
|
ENDIF
|
|||
|
@exit rts ; all done
|
|||
|
|
|||
|
@veryShort roxr.l #2,d0 ; load c and n with remaining 2 bits
|
|||
|
bcc.s @Copy0or1byte
|
|||
|
@Copy2or3bytes
|
|||
|
bpl.s @Copy2bytes ; if only 2 bytes left
|
|||
|
move.b (a3),(a1)+ ; copy 1 byte, 2 still remaining
|
|||
|
IF forSTP601v1 THEN
|
|||
|
subq.w #3, (a2) ;ZZZ
|
|||
|
ENDIF
|
|||
|
@Copy2bytes move.w (a3),(a1) ; copy the last 2 bytes
|
|||
|
IF forSTP601v1 THEN
|
|||
|
subq.w #2, (a2) ;ZZZ
|
|||
|
ENDIF
|
|||
|
rts ; all done
|
|||
|
TITLE 'IOPmgr - Copy To IOP'
|
|||
|
|
|||
|
;_______________________________________________________________________
|
|||
|
;
|
|||
|
; Routine: CopyToIop
|
|||
|
; Inputs: A1 - pointer to Host RAM buffer (source addr)
|
|||
|
; A2 - pointer to IOPRamAddrReg
|
|||
|
; (A2) - IOP data address (dest addr)
|
|||
|
; A3 - pointer to IOPRamDataReg
|
|||
|
; D0 - (word) transfer byte count (zero extended to long)
|
|||
|
; Outputs: none
|
|||
|
; Destroys: A1, D0, D1
|
|||
|
; Calls: none
|
|||
|
; Called by: IOPMsgAccess, IOPMoveData
|
|||
|
;
|
|||
|
; Function: Moves data from the Host to the IOP memory.
|
|||
|
;
|
|||
|
;_______________________________________________________________________
|
|||
|
|
|||
|
CopyToIop cmpi.w #3,d0 ; check for very short copy
|
|||
|
bls.s @veryShort ; skip alignment if very short
|
|||
|
move.l a1,d1 ; get the Host Address
|
|||
|
andi.w #$0003,d1 ; check for long word alignment
|
|||
|
beq.s @Aligned ; if no alignment needed
|
|||
|
subq.w #4,d1 ; get byte count bias
|
|||
|
add.w d1,d0 ; adjust the byte count
|
|||
|
jmp @Aligned(d1.w*2) ; do the alignment
|
|||
|
move.b (a1)+,(a3) ; move a byte
|
|||
|
move.b (a1)+,(a3) ; move a byte
|
|||
|
move.b (a1)+,(a3) ; move a byte
|
|||
|
@Aligned ror.l #2,d0 ; save tail byte count in high 2 bits
|
|||
|
moveq.l #7,d1 ; mask for starting index
|
|||
|
and.w d0,d1 ; number of long words to move first
|
|||
|
neg.w d1 ; negate to index backwards
|
|||
|
lsr.w #3,d0 ; number of 32 byte blocks to move
|
|||
|
jmp @CopyStart(d1.w*2) ; jump into the loop
|
|||
|
|
|||
|
@CopyLoop move.l (a1)+,(a3) ; move a 32 byte block of data....
|
|||
|
move.l (a1)+,(a3) ; ... 4 bytes at a time
|
|||
|
move.l (a1)+,(a3)
|
|||
|
move.l (a1)+,(a3)
|
|||
|
move.l (a1)+,(a3)
|
|||
|
move.l (a1)+,(a3)
|
|||
|
move.l (a1)+,(a3)
|
|||
|
move.l (a1)+,(a3)
|
|||
|
@CopyStart dbra d0,@CopyLoop ; loop through all of the blocks
|
|||
|
|
|||
|
add.l d0,d0 ; load c and n with remaining 2 bits
|
|||
|
bcs.s @Copy2or3bytes
|
|||
|
@Copy0or1byte
|
|||
|
bpl.s @exit ; if no bytes left
|
|||
|
move.b (a1),(a3) ; copy the last byte
|
|||
|
@exit rts ; all done
|
|||
|
|
|||
|
@veryShort roxr.l #2,d0 ; load c and n with remaining 2 bits
|
|||
|
bcc.s @Copy0or1byte
|
|||
|
@Copy2or3bytes
|
|||
|
bpl.s @Copy2bytes ; if only 2 bytes left
|
|||
|
move.b (a1)+,(a3) ; copy 1 byte, 2 still remaining
|
|||
|
@Copy2bytes move.w (a1),(a3) ; copy the last 2 bytes
|
|||
|
rts ; all done
|
|||
|
TITLE 'IOPmgr - Compare With IOP'
|
|||
|
|
|||
|
;_______________________________________________________________________
|
|||
|
;
|
|||
|
; Routine: CompareWithIop
|
|||
|
; Inputs: A0 - pointer to imCompRel byte (result addr)
|
|||
|
; A1 - pointer to Host RAM buffer (dest addr)
|
|||
|
; A2 - pointer to IOPRamAddrReg
|
|||
|
; (A2) - IOP data address (source addr)
|
|||
|
; A3 - pointer to IOPRamDataReg
|
|||
|
; D0 - (word) transfer byte count (zero extended to long)
|
|||
|
; Outputs: none
|
|||
|
; Destroys: A1, D0, D1, D2
|
|||
|
; Calls: none
|
|||
|
; Called by: IOPMoveData
|
|||
|
;
|
|||
|
; Function: Compares data from the IOP to the Host memory.
|
|||
|
;
|
|||
|
;_______________________________________________________________________
|
|||
|
|
|||
|
CompareWithIop
|
|||
|
cmpi.w #3,d0 ; check for very short compare
|
|||
|
bls.s @Aligned ; skip alignment if very short
|
|||
|
move.l a1,d1 ; get the Host Address
|
|||
|
andi.w #$0003,d1 ; check for long word alignment
|
|||
|
beq.s @Aligned ; if no alignment needed
|
|||
|
subq.w #4,d1 ; get byte count bias
|
|||
|
add.w d1,d0 ; adjust the byte count
|
|||
|
not.w d1 ; setup count for DBNE
|
|||
|
@AlignLoop move.b (a3),d2 ; get byte of data from IOP
|
|||
|
IF forSTP601v1 THEN
|
|||
|
subq.w #3, (a2) ;ZZZ
|
|||
|
ENDIF
|
|||
|
cmp.b (a1)+,d2 ; compare to HOST data
|
|||
|
dbne d1,@AlignLoop ; loop until longword aligned
|
|||
|
bne.s @NotEqual ; exit if didn't compare
|
|||
|
|
|||
|
@Aligned moveq.l #3,d1 ; mask for byte remainder
|
|||
|
and.w d0,d1 ; number of tail bytes to compare
|
|||
|
lsr.w #2,d0 ; number of long words to compare
|
|||
|
beq.s @TailStart ; if no longs, start with tail
|
|||
|
subq.w #1,d0 ; adjust loop count for DBNE
|
|||
|
@CmpLoop move.l (a3),d2 ; get long word of data from IOP
|
|||
|
cmp.l (a1)+,d2 ; compare to HOST data
|
|||
|
dbne d0,@CmpLoop ; loop through all long words
|
|||
|
bra.s @TailStart ; then compare the remaining bytes
|
|||
|
|
|||
|
@TailLoop move.b (a3),d2 ; get byte of data from IOP
|
|||
|
IF forSTP601v1 THEN
|
|||
|
subq.w #3, (a2) ;ZZZ
|
|||
|
ENDIF
|
|||
|
cmp.b (a1)+,d2 ; compare to HOST data
|
|||
|
@TailStart dbne d1,@TailLoop ; loop through all remaining bytes
|
|||
|
|
|||
|
@NotEqual sne.b (a0) ; return -1 if IOP < HOST
|
|||
|
bls.s @Exit ; return 0 if IOP = HOST
|
|||
|
neg.b (a0) ; return +1 if IOP > HOST
|
|||
|
@Exit rts
|
|||
|
TITLE 'IOPmgr - IOP Interrupt'
|
|||
|
|
|||
|
;_______________________________________________________________________
|
|||
|
;
|
|||
|
; Routine: IOPInterrupt
|
|||
|
; Inputs: D0{31…24} - flags indicating non-deferrable XMT messages
|
|||
|
; D0{23…16} - flags indicating non-deferrable RCV messages
|
|||
|
; D0{15…8} - must be zero
|
|||
|
; D0{7…0} - IOP number of IOP requesting the interrupt
|
|||
|
; Outputs: none
|
|||
|
; Destroys: A0, A1, A2, A3, D0, D1, D2, D3
|
|||
|
; Calls: DTInstall, IOP message Handler
|
|||
|
; Called by: System interrupt handler
|
|||
|
;
|
|||
|
; Function: Services interrupts from the IOPs. Acknowledges the messages,
|
|||
|
; and calls the message handlers directly, or schedules a
|
|||
|
; deferred task manager task to call them, if they were
|
|||
|
; deferrable handlers. If the interrupt was a Bypass Interrupt
|
|||
|
; Request, it will reset the interrupt and JMP to the external
|
|||
|
; label IOPBypassInterrupt passing the IOP number that generated
|
|||
|
; the interrupt in register D0.
|
|||
|
;
|
|||
|
;_______________________________________________________________________
|
|||
|
|
|||
|
with IOPMgrGlobals,IOPInfo
|
|||
|
|
|||
|
IOPInterrupt ; a0-a3/d0-d3 saved by int handler
|
|||
|
movea.l IOPmgrVars,a0 ; point to globals
|
|||
|
jmp ([IntHandlerPtr,a0]) ; jump through the patch vector
|
|||
|
vIOPInterrupt
|
|||
|
move.l IOPInfoPtrs(a0,d0.w*4),d1
|
|||
|
beq.s @noIop ; ignore if IOP doesn't exist
|
|||
|
movea.l d1,a0 ; a0 := pointer to IOPInfo
|
|||
|
assert IopAddrRegPtr=0
|
|||
|
movea.l (a0)+,a2 ; a2 := pointer to IOP Ram addr reg
|
|||
|
assert IopDataRegPtr=(IopAddrRegPtr+4)
|
|||
|
movea.l (a0)+,a3 ; a3 := pointer to IOP Ram data reg
|
|||
|
move.w sr,d2 ; save old interrupt level
|
|||
|
move.b d0,d2 ; save the IOP number, overwrite saved ccr
|
|||
|
move.l a0,-(sp) ; save reg for msg handler to restore
|
|||
|
@checkAgain
|
|||
|
assert IopCtlRegPtr=(IopDataRegPtr+4)
|
|||
|
movea.l (a0)+,a1 ; a1 := pointer to IOP control reg
|
|||
|
move.b (a1),d1 ; get the control register
|
|||
|
lsr.b #iopInt0Active+1,d1 ; test INT0 (xmt MessageCompleted)
|
|||
|
bcs.s @xmtComp ; handle INT0
|
|||
|
lsr.b #iopInt1Active-iopInt0Active,d1 ; test INT1 (rcv NewMessageSent)
|
|||
|
bcs.s @rcvNew ; handle INT1
|
|||
|
lsr.b #iopBypassIntReq-iopInt1Active,d1 ; test BypassIntReq
|
|||
|
bcs.s @bypassInt ; all done if not bypass interrupt
|
|||
|
@IntHandled move.w d2,sr ; restore interrupt level
|
|||
|
addq.l #4,sp ; pop saved a0
|
|||
|
lsr.l #8,d0 ; test the flags
|
|||
|
bne.w @RunNonDeferrable ; if non-deferrables, go check for and run them
|
|||
|
rts ; return from the interrupt <1.6>
|
|||
|
|
|||
|
@noIop cmpi.w #SccIopNum,d0 ; see if it was from the SCC <1.6>
|
|||
|
beq.l SccDecode ; if so, pass it on to the Bypass handler <1.6> <8> rb
|
|||
|
rts ; return from the interrupt <1.6>
|
|||
|
|
|||
|
|
|||
|
@bypassInt
|
|||
|
assert BypassHandler=(IopCtlRegPtr+4)
|
|||
|
move.l (a0),d1 ; get address of bypass int handler
|
|||
|
beq.s @IntHandled ; no bypass handler, not much we can do
|
|||
|
move.w d2,sr ; restore interrupt level
|
|||
|
move.l d0,(sp) ; save the flags/iop number
|
|||
|
movea.l d1,a0 ; setup handler address
|
|||
|
jsr (a0) ; call the ByPass interrupt handler
|
|||
|
move.l (sp)+,d0 ; restore flags/IOP number
|
|||
|
bra.s IOPInterrupt ; check again for other interrupts
|
|||
|
|
|||
|
|
|||
|
@rcvNew move.b #clrIopInt1,(a1) ; clear the interrupt
|
|||
|
move.w #(MsgReceived<<8)+\ ; new state
|
|||
|
(NewMsgSent<<0),d1 ; state to search for
|
|||
|
move.w #IOPRcvMsgBase+1,d3 ; IOP address to start search at
|
|||
|
lea @rcvFound,a1 ; a1 := rcv found routine
|
|||
|
assert BypassHandler=(IopCtlRegPtr+4)
|
|||
|
move.b MaxRcv-BypassHandler(a0),d0 ; get message count
|
|||
|
adda.w #MsgTable-BypassHandler,a0 ; point before the first message
|
|||
|
move.w (a2),-(sp) ; save the IOPRamAddrReg
|
|||
|
bra.s @SearchStart ; start searching the message states
|
|||
|
|
|||
|
@xmtComp move.b #clrIopInt0,(a1) ; clear the interrupt
|
|||
|
moveq.l #(MsgIdle<<8)+\ ; new state
|
|||
|
(MsgCompleted<<0),d1 ; state to search for
|
|||
|
move.w #IOPXmtMsgBase+1,d3 ; IOP address to start search at
|
|||
|
lea @xmtFound,a1 ; a1 := xmt done routine
|
|||
|
assert BypassHandler=(IopCtlRegPtr+4)
|
|||
|
move.b MaxXmt-BypassHandler(a0),d0 ; get message count
|
|||
|
adda.w #MsgTable-BypassHandler,a0 ; point before the first message
|
|||
|
move.w (a2),-(sp) ; save the IOPRamAddrReg
|
|||
|
bra.s @SearchStart ; start searching the message states
|
|||
|
|
|||
|
@searchLoop adda.w #IOPMsgEntry.IOPMsgEntrySize,a0 ; point to next message info
|
|||
|
cmp.b (a3),d1 ; check for state match
|
|||
|
IF forSTP601v1 THEN
|
|||
|
move ccr, -(sp) ;ZZZ
|
|||
|
subq.w #3, (a2) ;ZZZ
|
|||
|
move (sp)+, ccr ;ZZZ
|
|||
|
ENDIF
|
|||
|
dbeq d0,@searchLoop ; loop until match or end of list
|
|||
|
bne.s @searchDone ; if all message states checked
|
|||
|
|
|||
|
move.w (a2),d3 ; get the address pointer
|
|||
|
@MsgRegs reg a0/a1/d0/d1/d2/d3 ; registers to save when servicing message
|
|||
|
movem.l @MsgRegs,-(sp) ; save the registers
|
|||
|
subq.w #1,d3 ; point to the message state that was found
|
|||
|
move.w d3,(a2) ; re-load ram address pointer
|
|||
|
lsr.w #8,d1 ; get the new message state
|
|||
|
move.b d1,(a3) ; set the new message state
|
|||
|
move.w d2,sr ; give higher priority ints a chance
|
|||
|
jsr (a1) ; call the xmt/rcv found routine
|
|||
|
movem.l (sp)+,@MsgRegs ; restore the registers
|
|||
|
@SearchStart
|
|||
|
ori.w #HiIntMask,sr ; disable all interrupts
|
|||
|
move.w d3,(a2) ; restore the address pointer
|
|||
|
dbra d0,@searchLoop ; loop until end of list
|
|||
|
|
|||
|
@searchDone move.w (sp)+,(a2) ; restore the IOPRamAddrReg
|
|||
|
clr.w d0 ; setup for zero extended IOP number
|
|||
|
move.b d2,d0 ; flags in high word, IOP number in low word
|
|||
|
movea.l IOPmgrVars,a1
|
|||
|
tst.b DTaskQueued(a1)
|
|||
|
bne.s @toCkAgain ; if already queued, just return
|
|||
|
tst.l CompleteQHdr+qHead(a1)
|
|||
|
beq.s @toCkAgain ; if nothing queued, just return
|
|||
|
st DTaskQueued(a1)
|
|||
|
lea DTask(a1),a0
|
|||
|
@DTaskRegs reg a2/d0/d2 ; regs to save when queueing a deferred task
|
|||
|
movem.l @DTaskRegs,-(sp) ; save regs
|
|||
|
jsr ([jDTInstall]) ; queue the deferred task
|
|||
|
movem.l (sp)+,@DTaskRegs ; restore regs
|
|||
|
@toCkAgain move.w d2,sr ; give higher priority ints a chance <1.7>
|
|||
|
movea.l (sp),a0 ; setup a0 for entry at @checkAgain
|
|||
|
bra.w @checkAgain ; return from the interrupt <1.7>
|
|||
|
|
|||
|
@XmtFound lea IOPMsgEntry.XmtMsgQHdr+qHead(a0),a1 ; point to request info
|
|||
|
move.l (a1),d1 ; get request info
|
|||
|
beq.s @empty ; if no handler installed
|
|||
|
move.l a1,-(sp) ; save message entry pointer
|
|||
|
movea.l d1,a0 ; setup request info address
|
|||
|
subq.l #qHead,a1 ; point to queue header
|
|||
|
jsr DequeueTrap ; remove it from the msg queue
|
|||
|
movea.l IOPRequestInfo.irReplyPtr(a0),a1 ; get the buffer address
|
|||
|
moveq.l #0,d0 ; zero extend the reply byte count
|
|||
|
move.b IOPRequestInfo.irReplyLen(a0),d0 ; get the byte count
|
|||
|
bsr.s @CopyAndQueue ; read the reply and queue it
|
|||
|
movea.l (sp)+,a1 ; point to message queue head
|
|||
|
move.l (a1),d1 ; get head of queue
|
|||
|
beq.s @empty ; if empty, do nothing
|
|||
|
movea.l d1,a0 ; setup to send the queued message
|
|||
|
jmp ([JIOPMsgRequest]) ; send the next message and return
|
|||
|
@empty rts
|
|||
|
|
|||
|
@RcvFound move.l IOPMsgEntry.RcvMsgInfoPtr(a0),d1 ; get request info
|
|||
|
beq.s @empty ; if no handler installed
|
|||
|
movea.l d1,a0 ; setup request info address
|
|||
|
movea.l IOPRequestInfo.irMessagePtr(a0),a1 ; get the buffer address
|
|||
|
moveq.l #0,d0 ; zero extend the message byte count
|
|||
|
move.b IOPRequestInfo.irMessageLen(a0),d0 ; get the byte count
|
|||
|
@CopyAndQueue
|
|||
|
beq.s @CopyDone ; if no message data to copy
|
|||
|
move.w d3,d1 ; get copy of message state address
|
|||
|
assert MaxIOPMsgLen=(1<<5)
|
|||
|
lsl.b #5,d1 ; convert to message data address
|
|||
|
move.w d1,(a2) ; point to the message
|
|||
|
bsr.w CopyFromIop ; read the message
|
|||
|
@CopyDone movea.l IOPmgrVars,a1 ; point to globals
|
|||
|
adda.w #CompleteQHdr,a1 ; point to the completion queue
|
|||
|
tst.l IOPRequestInfo.irHandler(a0) ; check the handler address
|
|||
|
beq.s @NoHandler ; if no handler, don't enqueue it
|
|||
|
jmp EnqueueTrap ; enqueue the completed request, and return
|
|||
|
|
|||
|
@NoHandler clr.b IOPRequestInfo.irReqActive(a0) ; indicate that it is done
|
|||
|
rts ; return
|
|||
|
|
|||
|
|
|||
|
@RunNonDeferrable
|
|||
|
move.l d0,d3 ; save the flags
|
|||
|
lsr.l #8,d3 ; get flags into low 2 bytes
|
|||
|
movea.l IOPmgrVars,a1 ; get the globals
|
|||
|
lea CompleteQHdr(a1),a1 ; point to the completion queue
|
|||
|
lea qHead-qLink(a1),a0 ; point to ptr to first element
|
|||
|
ori.w #HiIntMask,sr ; disable all ints while touching queue
|
|||
|
@RunLoop move.l qLink(a0),d0 ; get ptr to next element
|
|||
|
beq.s @RunDone ; if end of queue, we're done
|
|||
|
movea.l d0,a0 ; setup queue element pointer
|
|||
|
cmp.b IOPRequestInfo.irIOPNumber(a0),d2 ; is it for this IOP
|
|||
|
bne.s @RunLoop ; if not, go on to next one
|
|||
|
move.b IOPRequestInfo.irMsgNumber(a0),d0 ; get the message number
|
|||
|
assert IOPRequestInfo.irSendXmtMessage=0
|
|||
|
tst.b IOPRequestInfo.irRequestKind(a0) ; test for xmt/rcv
|
|||
|
bne.s @ChkMsgNum ; if receive, bit number is correct
|
|||
|
addq.b #8,d0 ; point to next byte for xmt
|
|||
|
@ChkMsgNum btst.l d0,d3 ; see if this msg is non-deferrable
|
|||
|
beq.s @RunLoop ; if not, go on to next one
|
|||
|
jsr DequeueTrap ; remove it from the queue
|
|||
|
move.l a0,-(sp) ; remember the address
|
|||
|
bsr.s @RunLoop ; check the next one (using recursion)
|
|||
|
|
|||
|
movea.l (sp)+,a0 ; get the queue element address
|
|||
|
clr.b IOPRequestInfo.irReqActive(a0) ; indicate that it is done
|
|||
|
move.l IOPRequestInfo.irHandler(a0),d0 ; get the handler address
|
|||
|
beq.s @RunRTS ; if no handler, don't try to call it
|
|||
|
movea.l d0,a1 ; get handler address
|
|||
|
jmp (a1) ; run the handler (may trash a0-a3/d0-d3)
|
|||
|
|
|||
|
@RunDone move.w d2,sr ; restore interrupt level
|
|||
|
@RunRTS rts ; search done, now let them run
|
|||
|
|
|||
|
endwith
|
|||
|
TITLE 'IOPmgr - IOP Deferred Task'
|
|||
|
|
|||
|
;_______________________________________________________________________
|
|||
|
;
|
|||
|
; Routine: IOPDefTask
|
|||
|
; Inputs: none
|
|||
|
; Outputs: none
|
|||
|
; Destroys: A0, A1, D0
|
|||
|
; Calls: IOP message Handler
|
|||
|
; Called by: Deferred task manager
|
|||
|
;
|
|||
|
; Function: Calls the message handlers for the deferrable messages
|
|||
|
; that were marked and acknowledged by the IOP interrupt
|
|||
|
; handler.
|
|||
|
;
|
|||
|
;_______________________________________________________________________
|
|||
|
|
|||
|
CallCompHandler
|
|||
|
move.w (sp)+,sr ; restore interrupts
|
|||
|
movea.l d0,a0 ; setup element to dequeue
|
|||
|
jsr DequeueTrap ; dequeue the element
|
|||
|
clr.b IOPRequestInfo.irReqActive(a0) ; indicate that it is done
|
|||
|
move.l IOPRequestInfo.irHandler(a0),d0 ; get the handler address
|
|||
|
beq.s IOPDefTask ; if no handler, don't try to call it
|
|||
|
|
|||
|
move.l a1,-(sp) ; save queue pointer across call to handler
|
|||
|
movea.l d0,a1 ; get handler address
|
|||
|
jsr (a1) ; call the handler (may trash a0-a3/d0-d3)
|
|||
|
movea.l (sp)+,a1 ; restore queue pointer
|
|||
|
|
|||
|
IOPDefTask ; Enters HERE ; a0-a3/d0-d3 saved by int handler
|
|||
|
; a1 := pointer to CompleteQHdr
|
|||
|
move.w sr,-(sp) ; save interrupt priority
|
|||
|
ori.w #HiIntMask,sr ; disable interrupts
|
|||
|
move.l qHead(a1),d0 ; read the head of the queue
|
|||
|
bne.s CallCompHandler ; if not empty, dequeue and call it
|
|||
|
|
|||
|
assert IOPMgrGlobals.DTaskQueued=(IOPMgrGlobals.CompleteQHdr-1)
|
|||
|
clr.b -(a1) ; indicate that the task is done
|
|||
|
move.w (sp)+,sr ; restore interrupts
|
|||
|
rts ; all done
|
|||
|
if Debugging then ; <1.3>
|
|||
|
TITLE 'IOPmgr - IOP VBL Task'
|
|||
|
|
|||
|
;_______________________________________________________________________
|
|||
|
;
|
|||
|
; Routine: IOPVblTask
|
|||
|
; Inputs: none
|
|||
|
; Outputs: none
|
|||
|
; Destroys: A0, A1, A2, D0
|
|||
|
; Calls: none
|
|||
|
; Called by: Vertical Retrace Manager
|
|||
|
;
|
|||
|
; Function: Tests and clears the Alive flag on each IOP, and crashes
|
|||
|
; if any IOP was not Alive
|
|||
|
;
|
|||
|
;_______________________________________________________________________
|
|||
|
|
|||
|
with IOPMgrGlobals,IOPInfo
|
|||
|
IOPVblTask ; a0-a3/d0-d3 saved by int handler
|
|||
|
movea.l IOPmgrVars,a0 ; get globals
|
|||
|
move.w #AliveVBLrate,VTask+vblCount(a0) ; re-initialize the count
|
|||
|
move.w sr,-(sp) ; save interrupt priority
|
|||
|
ori.w #HiIntMask,sr ; disable interrupts
|
|||
|
moveq.l #NumberOfIOPs-1,d0 ; loop counter
|
|||
|
@PollLoop movea.l (a0)+,a1 ; a1 := pointer to IOPInfo
|
|||
|
move.l a1,d1 ; test for zero
|
|||
|
beq.s @Next ; skip if IOP doesn't exist
|
|||
|
assert IopAddrRegPtr=0
|
|||
|
movea.l (a1)+,a2 ; a2 := IopAddrRegPtr
|
|||
|
assert IopDataRegPtr=(IopAddrRegPtr+4)
|
|||
|
movea.l (a1)+,a1 ; a1 := IopDataRegPtr
|
|||
|
move.w (a2),-(sp) ; save the IOPRamAddrReg
|
|||
|
move.w #IOPAliveAddr,(a2) ; setup the IOP Ram Address
|
|||
|
cmpi.b #$FF,(a1) ; IOP sets it to $FF when Idle
|
|||
|
beq.s @Alive ; if alive and well
|
|||
|
bsr.s IOPisDead ; crash if dead
|
|||
|
@Alive move.w #IOPAliveAddr,(a2) ; setup the IOP Ram Address
|
|||
|
clr.b (a1) ; we set it to $00 after testing it
|
|||
|
move.w (sp)+,(a2) ; restore the IOPRamAddrReg
|
|||
|
@Next dbra d0,@PollLoop ; poll the next IOP
|
|||
|
move.w (sp)+,sr ; restore interrupt priority
|
|||
|
rts ; return
|
|||
|
|
|||
|
|
|||
|
IOPisDead move.w #dsSysErr,d0 ; Sorry, a system error occurred
|
|||
|
_SysError ; crash
|
|||
|
bra.s IOPisDead ; IOP failure, crash and burn
|
|||
|
|
|||
|
endwith
|
|||
|
endif ; <1.3>
|
|||
|
TITLE 'IOPmgr - Move Request Handler'
|
|||
|
|
|||
|
;_______________________________________________________________________
|
|||
|
;
|
|||
|
; Routine: MoveReqHandler
|
|||
|
; Inputs: A0 - pointer to IOPRequestInfo
|
|||
|
; Outputs: none
|
|||
|
; Destroys: A0-A2/D0-D2
|
|||
|
; Calls: IOPMsgRequest, IOPMoveData
|
|||
|
; Called by: IOP interrupt handler
|
|||
|
;
|
|||
|
; Function: Handles Data Movement requests issued by drivers on the IOPs.
|
|||
|
;
|
|||
|
;_______________________________________________________________________
|
|||
|
|
|||
|
with IOPRequestInfo,IOPMoveInfo
|
|||
|
MoveReqHandler ; a0-a3/d0-d3 saved by Interrupt Handler
|
|||
|
|
|||
|
; The message that the IOP sent looks very much like a IOPMoveInfo parameter
|
|||
|
; block, the message from the IOP was read directly into the IOPMoveInfo block.
|
|||
|
|
|||
|
movea.l a0,a2 ; save pointer to IOPRequestInfo for later
|
|||
|
movea.l irMessagePtr(a0),a0 ; message buffer is IOPMoveInfo request block
|
|||
|
|
|||
|
; The IOP number field is the only field that was not in the message from
|
|||
|
; the IOP, so just copy it from the IOPAccessInfo, and then issue the Move.
|
|||
|
|
|||
|
move.b irIOPNumber(a2),imIOPNumber(a0) ; fill in the IOP number
|
|||
|
jsr ([JIOPMoveData]) ; Perform the Move Request
|
|||
|
|
|||
|
; Send the result from IOPMoveData back as the reply.
|
|||
|
; Send the Message Completed interrupt to the IOP, and return.
|
|||
|
|
|||
|
movea.l a2,a0 ; a0 <- IOPRequestInfo
|
|||
|
move.b #irSendRcvReply,irRequestKind(a0)
|
|||
|
jmp ([JIOPMsgRequest]) ; tell IOP that Message Completed and return
|
|||
|
|
|||
|
endwith
|
|||
|
TITLE 'IOPmgr - Initialize IOP Manager'
|
|||
|
|
|||
|
;_______________________________________________________________________
|
|||
|
;
|
|||
|
; Routine: jsr InitIOPMgr
|
|||
|
; Inputs: none
|
|||
|
; Outputs: none
|
|||
|
; Destroys: none
|
|||
|
; Calls: none
|
|||
|
; Called by: Start Manager
|
|||
|
;
|
|||
|
; Function: Initializes the IOPs, downloads their code, and starts them
|
|||
|
; running. Allocates and initializes the IOPMgr global data
|
|||
|
; structures.
|
|||
|
;
|
|||
|
;_______________________________________________________________________
|
|||
|
|
|||
|
with IOPMgrGlobals,IOPAccessInfo,IOPInfo
|
|||
|
InitIOPMgr
|
|||
|
@SavedRegs reg a0-a3/d0
|
|||
|
movem.l @SavedRegs,-(sp) ; save registers
|
|||
|
|
|||
|
move.w sr,-(sp) ; save interrupt priority
|
|||
|
ori.w #HiIntMask,sr ; disable interrupts
|
|||
|
|
|||
|
moveq.l #0,d0 ; amount of space for IOPInfos
|
|||
|
|
|||
|
TestFor SCCIOPExists ; see if we have an SCC IOP
|
|||
|
beq.s @noSCCIOP ; if not, don't allocate space for it
|
|||
|
|
|||
|
addi.w #IOPInfoSize,d0 ; allocate space for it
|
|||
|
@noSCCIOP
|
|||
|
|
|||
|
TestFor SWIMIOPExists ; see if we have an SWIM IOP
|
|||
|
beq.s @noSWIMIOP ; if not, don't allocate space for it
|
|||
|
|
|||
|
addi.w #IOPInfoSize,d0 ; allocate space for it
|
|||
|
@noSWIMIOP
|
|||
|
|
|||
|
tst.w d0 ; are there any IOPs
|
|||
|
beq.s @noIOPs ; if not, exit
|
|||
|
|
|||
|
addi.w #GlobalsSize,d0 ; d0 <- data structure size
|
|||
|
_NewPtr ,SYS,CLEAR ; allocate and clear the structure
|
|||
|
|
|||
|
lea -IOPMgrGlobals(a0),a2 ; Globals are negative to the pointer
|
|||
|
move.l a2,IOPmgrVars ; initialize the Pointer
|
|||
|
if Debugging then ; <1.3>
|
|||
|
|
|||
|
; Install the VBL task to look for dead IOPs
|
|||
|
|
|||
|
lea VTask(a2),a0 ; a0 <- VBL task
|
|||
|
move.w #vType,qType(a0) ; initialize qType field
|
|||
|
move.w #AliveVBLrate,vblCount(a0) ; initialize the count
|
|||
|
lea IOPVblTask,a1 ; get the task address
|
|||
|
move.l a1,vblAddr(a0) ; initialize vblAddr field
|
|||
|
_VInstall ; install the VBL task
|
|||
|
endif ; <1.3>
|
|||
|
|
|||
|
; Setup the deferred task manager data structure
|
|||
|
|
|||
|
move.w #dtQType,DTask+qType(a2) ; Initialize DTask.qType
|
|||
|
lea IOPDefTask,a0 ; a0 <- addr of IOPDefTask
|
|||
|
move.l a0,DTask+dtAddr(a2) ; Initialize DTask.dtAddr
|
|||
|
lea CompleteQHdr(a2),a0 ; a0 <- completion queue header
|
|||
|
move.l a0,DTask+dtParm(a2) ; DTask.dtParm (loaded into A1 for DTask)
|
|||
|
|
|||
|
; Setup the Interrupt Handler Pointer
|
|||
|
|
|||
|
lea vIOPInterrupt,a0 ; point to the handler
|
|||
|
move.l a0,IntHandlerPtr(a2) ; initialize the data structure
|
|||
|
|
|||
|
; Install and initialize the IOPs
|
|||
|
|
|||
|
lea IOPInfos(a2),a2 ; point to start of IOPInfo records
|
|||
|
|
|||
|
TestFor SCCIOPExists ; see if we have an SCC IOP
|
|||
|
beq.s @SCCIOPdone ; if not, don't install it
|
|||
|
|
|||
|
moveq.l #(iaInstallIOP<<8)|\
|
|||
|
SccIopNum,d0 ; get the IOP number and access kind
|
|||
|
movea.l SCCrd,a1 ; get the base address
|
|||
|
lea SccDecode,a3 ; get the bypass interrupt handler address
|
|||
|
bsr.s @InstallIOP ; install the SCC IOP
|
|||
|
@SCCIOPdone
|
|||
|
|
|||
|
TestFor SWIMIOPExists ; see if we have an SWIM IOP
|
|||
|
beq.s @SWIMIOPdone ; if not, don't install it
|
|||
|
|
|||
|
moveq.l #(iaInstallIOP<<8)|\
|
|||
|
SwimIopNum,d0 ; get the IOP number and access kind
|
|||
|
movea.l IWM,a1 ; get the base address
|
|||
|
suba.l a3,a3 ; no bypass interrupt handler
|
|||
|
bsr.s @InstallIOP ; install the SWIM IOP
|
|||
|
@SWIMIOPdone
|
|||
|
|
|||
|
@Done move.w (sp)+,sr ; restore interrupts
|
|||
|
movem.l (sp)+,@SavedRegs ; restore registers
|
|||
|
rts ; all done
|
|||
|
|
|||
|
@noIOPs lea @traps,a1 ; point to the list
|
|||
|
move.w (a1)+,d0 ; get the _Unimplemented trap
|
|||
|
_GetTrapAddress ,newTool ; get address of unimplemented trap
|
|||
|
@trapLoop move.w (a1)+,d0 ; get next trap to un-implement
|
|||
|
beq.s @Done ; traps disabled, exit
|
|||
|
_SetTrapAddress ,newOS ; un-implement the trap
|
|||
|
bra.s @trapLoop ; loop through the table
|
|||
|
|
|||
|
@traps _Unimplemented
|
|||
|
_IOPInfoAccess
|
|||
|
_IOPMsgRequest
|
|||
|
_IOPMoveData
|
|||
|
dc.w 0 ; end of list
|
|||
|
|
|||
|
@InstallIOP movea.l a2,a0 ; a0 <- IOPInfoRecord
|
|||
|
assert iaIOPInfoPtr=(iaAccessInfoSize-4)
|
|||
|
move.l a0,-(sp) ; setup IOP info pointer
|
|||
|
clr.w -(sp) ; clear reserved field
|
|||
|
assert iaIOPNumber=(iaIOPInfoPtr-3)
|
|||
|
assert iaAccessKind=(iaIOPInfoPtr-4)
|
|||
|
move.w d0,-(sp) ; setup the IOP number and access kind
|
|||
|
|
|||
|
assert IopAddrRegPtr=0
|
|||
|
lea iopRamAddr(a1),a1 ; point to the address register
|
|||
|
move.l a1,(a0)+ ; setup IopAddrRegPtr
|
|||
|
|
|||
|
assert IopDataRegPtr=(IopAddrRegPtr+4)
|
|||
|
addq.w #iopRamData-iopRamAddr,a1 ; point to the data register
|
|||
|
move.l a1,(a0)+ ; setup IopDataRegPtr
|
|||
|
|
|||
|
assert IopCtlRegPtr=(IopDataRegPtr+4)
|
|||
|
subq.w #iopRamData-iopStatCtl,a1 ; point to the status/control register
|
|||
|
move.l a1,(a0)+ ; setup IopCtlRegPtr
|
|||
|
|
|||
|
assert BypassHandler=(IopCtlRegPtr+4)
|
|||
|
move.l a3,(a0)+ ; setup BypassHandler
|
|||
|
|
|||
|
movea.l sp,a0 ; a0 <- IOPAccessInfo record
|
|||
|
_IOPInfoAccess ; install the IOP
|
|||
|
addq.l #iaAccessInfoSize,sp ; de-allocate the IOPAccessInfo record
|
|||
|
adda.w #IOPInfoSize,a2 ; point to next IOPInfo record
|
|||
|
rts ; go on to the next IOP
|
|||
|
|
|||
|
endwith
|
|||
|
TITLE 'IOPmgr - Set SCC IOP ByPass mode'
|
|||
|
|
|||
|
;_______________________________________________________________________
|
|||
|
;
|
|||
|
; Routine: jsr SCCIOPByPass <1.2>
|
|||
|
; Inputs: none
|
|||
|
; Outputs: none
|
|||
|
; Destroys: none
|
|||
|
; Calls: none
|
|||
|
; Called by: InitIO
|
|||
|
;
|
|||
|
; Function: Checks to see if the SCC IOP should be placed in bypass
|
|||
|
; and set it accordingly.
|
|||
|
;
|
|||
|
;_______________________________________________________________________
|
|||
|
|
|||
|
with IOPMgrGlobals,IOPAccessInfo,IOPInfo,IOPRequestInfo
|
|||
|
|
|||
|
SCCIOPByPass
|
|||
|
|
|||
|
;
|
|||
|
; Check PRAM to see if we should put the SCC IOP into enhanced communications mode.
|
|||
|
;
|
|||
|
; Bit 0 = 1 -> leave in bypass <2>
|
|||
|
; = 0 -> put in enhanced communications mode <2>
|
|||
|
;
|
|||
|
|
|||
|
@SavedRegs reg a0-a2/d0
|
|||
|
movem.l @SavedRegs,-(sp) ; save registers
|
|||
|
move.l #((01<<16)|IOPPRAM), d0 ; index and number of bytes to get
|
|||
|
clr.b -(sp) ; make room for data
|
|||
|
movea.l sp, a0 ; a0 points to data buffer
|
|||
|
_ReadXPRAM
|
|||
|
|
|||
|
MOVE.B (SP),SCCIOPFlag ; save the PRAM byte in low mem <1.9>
|
|||
|
|
|||
|
moveq.l #$01,d0 ; check the bypass bit
|
|||
|
and.b (sp)+, d0 ; get the PRAM value
|
|||
|
bne.s @Done ; branch if SET, stay in bypass mode <2.0>
|
|||
|
|
|||
|
clr.l -(sp) ; reply buffer
|
|||
|
clr.l -(sp) ; [long] -> Completion Routine Address (Nil)
|
|||
|
pea 4(sp) ; [long] -> Reply Buffer Address
|
|||
|
pea @ByPassMsg ; [long] -> Message Buffer Address
|
|||
|
move.l #((Active<<0)+\ ; [byte] -> Request active
|
|||
|
(ReplyLen<<8)+\ ; [byte] -> Reply Length
|
|||
|
(MsgLen<<16)+\ ; [byte] -> Message Length
|
|||
|
(KernMsg<<24)), -(sp) ; [byte] -> Message Number
|
|||
|
move.l #((irSendXmtMessage<<0)+\ ; [byte] -> kind of request to perform
|
|||
|
(SccIopNum<<8)), -(sp) ; [byte] -> IOP Number for SCC
|
|||
|
; [word] -> queue type
|
|||
|
clr.l -(sp) ; [long] -> link to next queue element
|
|||
|
movea.l sp, a0
|
|||
|
_IOPMsgRequest
|
|||
|
bne.s @FlagBypass ; if send failed, don't wait <1.5><2>
|
|||
|
|
|||
|
@Sync_Loop tst.b irReqActive(sp) ; make this a sync call
|
|||
|
bne.s @Sync_Loop
|
|||
|
|
|||
|
@waitDone adda.w #(irReqInfoSize+4), sp ; adjust stack <1.5>
|
|||
|
|
|||
|
@Done movem.l (sp)+,@SavedRegs ; restore registers
|
|||
|
rts
|
|||
|
|
|||
|
@FlagBypass BSET #0,SCCIOPFlag ;set bit 0=1 to flag bypass mode <2>
|
|||
|
BRA.S @waitDone ; <2>
|
|||
|
|
|||
|
@ByPassMsg DC.B ByPassCmd, 0, $ff ; Turn bypass on or off
|
|||
|
align 2
|
|||
|
endwith
|
|||
|
TITLE 'IOPmgr - Initialize SCC IOP Hardware'
|
|||
|
|
|||
|
;_______________________________________________________________________ <1.4>
|
|||
|
;
|
|||
|
; Routine: jsr SCCIOPHwInit
|
|||
|
; Inputs: none
|
|||
|
; Outputs: none
|
|||
|
; Destroys: A0, A1, D0
|
|||
|
; Calls: none
|
|||
|
; Called by: Start Manager
|
|||
|
;
|
|||
|
; Function: Initializes the SCC IOP, so that the SCC can be used in ByPass
|
|||
|
; mode and has the correct timing to that when TimeSCCDB is
|
|||
|
; computed it will be correct.
|
|||
|
;
|
|||
|
;_______________________________________________________________________
|
|||
|
|
|||
|
|
|||
|
SCCIOPHwInit
|
|||
|
movea.l SCCRd,a1 ; a1 <- IOP Base
|
|||
|
move.b #resetIopRun,iopStatCtl(a1) ; init the Status/Ctl reg, hold IOP reset
|
|||
|
|
|||
|
; Download the IOP code
|
|||
|
|
|||
|
lea SCCIOPInitCode,a0 ; a0 <- start of IOP code (in this ROM)
|
|||
|
move.w (a0)+,d0 ; get the size code (-1 for DBRA)
|
|||
|
move.w (a0)+,iopRamAddr(a1); setup the code load address
|
|||
|
|
|||
|
@LoadLoop move.b (a0)+,iopRamData(a1); download a byte
|
|||
|
dbra d0,@LoadLoop ; load all of the bytes
|
|||
|
|
|||
|
; Start IOP execution
|
|||
|
|
|||
|
move.b #setIopRun,iopStatCtl(a1) ; release IOP reset (let it rip!)
|
|||
|
lsr.w #6,d0 ; $FFFF -> $03FF -> loop 1024 times
|
|||
|
@wait dbra d0,@wait ; delay a bit while IOP initializes
|
|||
|
rts ; all done
|
|||
|
|
|||
|
; 6502 code to throw SCC IOP into bypass mode
|
|||
|
SCCIOPInitCode
|
|||
|
dc.w (@end-@Start)-1 ; size of the code (in bytes), -1 for DBRA
|
|||
|
dc.w $8000-(@end-@Start) ; (word) load address
|
|||
|
@start ; code starts here
|
|||
|
dc.b $A9,$81 ; 7FEE: lda #1*DEBUG+0*SCCISM+1*BYPASS
|
|||
|
dc.b $8D,$30,$F0 ; 7FF0: sta SCCControlReg
|
|||
|
dc.b $A9,$23 ; 7FF3: lda #SccIOCtlReg <2>
|
|||
|
dc.b $8D,$31,$F0 ; 7FF5: sta IOControlReg
|
|||
|
dc.b $80,$FE ; 7FF8: bra $7FF8
|
|||
|
|
|||
|
dc.b $EE,$7F ; 7FFA 7FEE Non-Maskable Interrupt vector
|
|||
|
dc.b $EE,$7F ; 7FFC 7FEE Processor reset vector
|
|||
|
dc.b $EE,$7F ; 7FFE 7FEE Interrupt Request vector
|
|||
|
@end ; code ends here
|
|||
|
|
|||
|
|
|||
|
if PadForOverpatch then ; <1.6>
|
|||
|
align 4 ; <1.5>
|
|||
|
string asis ; <1.5>
|
|||
|
dcb.l ($770-(*-IOPmgr))/4,'Gary' ; padding for overpatch <1.5>
|
|||
|
endif ; <1.6>
|
|||
|
|
|||
|
endproc
|
|||
|
END
|
|||
|
|