mirror of
https://github.com/elliotnunn/mac-rom.git
synced 2024-12-22 08:30:05 +00:00
4325cdcc78
Resource forks are included only for .rsrc files. These are DeRezzed into their data fork. 'ckid' resources, from the Projector VCS, are not included. The Tools directory, containing mostly junk, is also excluded.
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
|
|
|