supermario/base/SuperMarioProj.1994-02-09/OS/IoPrimitives/ClockPRAMPrimitives.a

1662 lines
60 KiB
Plaintext
Raw Permalink Normal View History

2019-06-29 15:17:50 +00:00
;
; File: ClockPRAMPrimitives.a
;
; Contains: low-level routines for accessing the realtime clock and parameter RAM
;
; Written by: Steve Christensen
;
; Copyright: © 1991-1993 by Apple Computer, Inc. All rights reserved.
;
; This file is used in these builds: ROM
;
; Change History (most recent first):
;
; <SM14> 12/13/93 PN Roll in KAOs and Horror changes to support Malcom and AJ
; machines.
; <SM13> 6/23/93 RC Go back to the Ludwig roll-in since PDM reallly needs this code
; from Cyclone - stay tuned for further fixes/changes from Kurt
; <SM12> 6/21/93 kc Roll out SM10 so that Quadras would wake up with the right time.
; <SM11> 6/14/93 kc Roll in Ludwig.
; <LW3> 1/14/93 GS (RBM) Fix the occassional hang at boot. When outputing from
; the VIA to Cuda, the interrupt will occur prior to the rising
; edge of CB1. Delay long enough for the edge to occur before
; acknowledging the shift register interrupt.
; <SM10> 5/20/93 RM EgretReadTime should not call _EgretDispatch. The lowmem global
; 'Time' is self correcting for Egret/Caboose/Cuda. The one second
; interrupt handler for Egret / Caboose / Cuda maintains the
; lowmem global 'Time' in a self correcting manner. This routine
; need only return the lowmem 'Time'. A call to _EgretDispatch
; will cause the AutoPoll timer within Egret / Caboose / Cuda to
; reset, resulting in deferring ADB auto polling. If an
; application calls ReadTime in a tight loop, ADB polling could be
; deferred indefinitely (making the mouse/keyboard appear to hang
; or respond intermittently). Since 'Time' is self correcting,
; the routine just returns 'Time' as the result placed into
; 'GetParam'.
; <SM9> 12/11/92 fau Backed out <SM8> cause it broke Cyclone.
; <SM8> 12/10/92 GS This patch bypasses the physical read of Egret Chip when reading
; the time. The time is automatically updated in the background,
; therefore the low mem global always reflects the accurate time.
; This patch pertains to all Egret Chip based firmware in
; Egret8/9, Caboose, and Cuda.
; <SM7> 11/30/92 SWC Rolled in the rest of the Horror changes, except for HcMac code.
; Added the build conditionals back in.
; <H6> 9/3/92 SWC Fixed a couple of bugs in the RTC-based routines (you mean no
; one had any problems until now?). Thanks, Helder.
; <SM6> 10/21/92 fau Cleaned up a MMCExists that was there from Pandora's timeframe:
; comment P5. Cuda works now, so we don't need to bypass stuff.
; <SM5> 6/26/92 GS (RBM) Rewrite of 'CudaRdXByte' & 'CudaWrXByte' to avoid looking
; for collisions. This is possible because a 'SyncAck' cycle
; occurs immediately after 'InitVias' and disables all
; asynchronous message sources within Cuda. These routines are
; being used after the Cuda manager is installed which will cause
; a loss of data from Cuda so I also made the routine dispatch to
; a trap based function using the Cuda manager once the Cuda
; manager is installed. This is not a particularly clean
; implementation and needs to be looked at further!!!
; <SM4> 5/28/92 kc Add ClkNoMem.
; <SM3> 5/25/92 RB Removed a forCyclone conditional. This WAS the last one left. A
; check for MMC is used instead to skip over RTCClkNoMem.
; <SM2> 5/21/92 RB Making changes for Cyclone. Original comments below: <P6>
; 2/10/92 GS Changed the PRAM IOPrimitives to use the full word of
; a register to set up the address to R/W. <P5> 1/30/92 RMP
; Temporarly disabled RTCClock read/write PRAM since Cuda isn't
; working (I know it doesn't make sense but it's 2:13 in the
; morning). <P4> 1/16/92 GS Updated the PRAMIO routine to use a
; full word as the address to Read/Write <P4> 1/16/92 GS Update
; VIA control bit var names. <P3> 1/16/92 GS Update to include the
; new file CudaMgr Clock and PRAM routines. <P2> 01/14/92 jmp
; (SWC,H5) Fixed PMgrWrXByte (registers were getting trashed).
; <1> 5/17/92 kc first checked in
; <SM0> 5/2/92 kc Roll in Horror. Comments follow:
; <H5> 1/13/92 SWC Fixed PMgrWrXByte (registers were getting trashed).
; <H4> 11/25/91 CCH Used standardized equates for VIA pins.
; <H3> 11/7/91 SWC Re-wrote the Egret code. It was working on an emulator but
; stopped working when the Mac was running stand-alone. Converted
; existing implementations to use a "standard" PramIO routine that
; calls implementation specific read/write PRAM byte routines so
; there's one less routine to write for new PRAM models that can
; use it.
; <H2> 11/6/91 SWC Re-wrote PMGRXPRamIO to fix problems too numerous to mention.
; <H1> 10/15/91 SWC Adding this file into the build.
PRINT OFF
LOAD 'StandardEqu.d'
INCLUDE 'HardwarePrivateEqu.a'
INCLUDE 'UniversalEqu.a'
IF hasPwrControls THEN
INCLUDE 'PowerPrivEqu.a'
ENDIF
IF hasEgret THEN
INCLUDE 'EgretEqu.a'
ENDIF
INCLUDE 'IOPrimitiveEqu.a'
PRINT ON
PRINT NOMDIR
MACHINE MC68020
ClockPRAMPrimitives PROC EXPORT
;————————————————————————————————————————————————————————————————————————————————————————
;
; Clock/PRAM primitives vector table
;
; Tables pointed to by the universal ProductInfo record (ClockPRAMPtr) for low-level
; clock and PRAM routines dependent on the hardware. There should be a table for each
; supported ProductInfo. Note that if a machine has a combination of the "standard"
; PRAM and clock models, it should have a machine-specific table added below
IF hasVIAClock THEN
EXPORT RTCClockPRAM
DC.W 0 ; flags
DC.W (RTCTableEnd-RTCClockPRAM)/4 ; number of entries
RTCClockPRAM
DC.L RTCInitHW-RTCClockPRAM ; initialize the hardware (RAM-less)
DC.L RTCWrProtOff-RTCClockPRAM ; write-enable PRAM (RAM-less)
DC.L RTCWrProtOn-RTCClockPRAM ; write-protect PRAM (RAM-less)
DC.L RTCRdXByte-RTCClockPRAM ; read PRAM byte (RAM-less)
DC.L RTCWrXByte-RTCClockPRAM ; write PRAM byte (RAM-less)
DC.L StandardXPramIO-RTCClockPRAM ; read/write PRAM byte[s] (no traps) <H3>
DC.L RTCXParam-RTCClockPRAM ; read/write PRAM byte[s] (trap-based)
DC.L RTCReadTime-RTCClockPRAM ; read the clock (uses RAM)
DC.L RTCWriteTime-RTCClockPRAM ; write to the clock (uses RAM)
RTCTableEnd
ENDIF
IF hasPwrControls THEN
EXPORT PMGRClockPRAM
DC.W 0 ; flags
DC.W (PMGRTableEnd-PMGRClockPRAM)/4 ; number of entries
PMGRClockPRAM
DC.L 0 ; not used
DC.L 0 ; not used
DC.L 0 ; not used
DC.L PMGRRdXByte-PMGRClockPRAM ; read PRAM byte (RAM-less)
DC.L PMGRWrXByte-PMGRClockPRAM ; write PRAM byte (RAM-less)
DC.L StandardXPramIO-PMGRClockPRAM ; read/write PRAM byte[s] (no traps) <H3>
DC.L PMGRXParam-PMGRClockPRAM ; read/write PRAM byte[s] (trap-based)
DC.L PMGRReadTime-PMGRClockPRAM ; read the clock (uses RAM)
DC.L PMGRWriteTime-PMGRClockPRAM ; write to the clock (uses RAM)
PMGRTableEnd
ENDIF
IF hasEgret THEN
EXPORT EgretClockPRAM
DC.W 0 ; flags
DC.W (EgretTableEnd-EgretClockPRAM)/4 ; number of entries
EgretClockPRAM
DC.L 0 ; not used
DC.L 0 ; not used
DC.L 0 ; not used
DC.L EgretRdXByte-EgretClockPRAM ; read PRAM byte (RAM-less)
DC.L EgretWrXByte-EgretClockPRAM ; write PRAM byte (RAM-less)
DC.L StandardXPramIO-EgretClockPRAM ; read/write PRAM byte[s] (no traps) <H3>
DC.L EgretXParam-EgretClockPRAM ; read/write PRAM byte[s] (trap-based)
DC.L EgretReadTime-EgretClockPRAM ; read the clock (uses RAM)
DC.L EgretWriteTime-EgretClockPRAM ; write to the clock (uses RAM)
EgretTableEnd
EXPORT CudaClockPRAM
DC.W 0 ; flags
DC.W (CudaTableEnd-CudaClockPRAM)/4 ; number of entries
CudaClockPRAM
DC.L 0 ; not used
DC.L 0 ; not used
DC.L 0 ; not used
DC.L CudaRdXByte-CudaClockPRAM ; read PRAM byte (RAM-less)
DC.L CudaWrXByte-CudaClockPRAM ; write PRAM byte (RAM-less)
DC.L StandardXPramIO-CudaClockPRAM ; read/write PRAM byte[s] (no traps) <H5>
DC.L EgretXParam-CudaClockPRAM ; read/write PRAM byte[s] (trap-based)
DC.L EgretReadTime-CudaClockPRAM ; read the clock (uses RAM)
DC.L EgretWriteTime-CudaClockPRAM ; write to the clock (uses RAM)
CudaTableEnd
ENDIF
IF hasProtectedPRAM THEN
EXPORT NoPRAMClockPRAM
DC.W 0 ; flags
DC.W (NoPRAMTableEnd-NoPRAMClockPRAM)/4 ; number of entries
NoPRAMClockPRAM
DC.L 0 ; not used
DC.L 0 ; not used
DC.L 0 ; not used
DC.L NoPRAMRdXByte-NoPRAMClockPRAM ; read PRAM byte (RAM-less)
DC.L NoPRAMWrXByte-NoPRAMClockPRAM ; write PRAM byte (RAM-less)
DC.L NoPRAMXPramIO-NoPRAMClockPRAM ; read/write PRAM byte[s] (no traps)
DC.L NoPRAMXParam-NoPRAMClockPRAM ; read/write PRAM byte[s] (trap-based)
DC.L NoPRAMReadTime-NoPRAMClockPRAM ; read the clock (uses RAM)
DC.L NoPRAMWriteTime-NoPRAMClockPRAM ; write to the clock (uses RAM)
NoPRAMTableEnd
ENDIF
IF hasVIAClock | hasPwrControls | hasEgret | hasPowerMgr THEN ; <H3>
;________________________________________________________________________________________
;
; Routine: StandardXPramIO
;
; Inputs: A0 - pointer to table of base addresses
; A1 - pointer to ProductInfo record for this machine
; A3 - pointer to PRAM I/O buffer
; D1 - flags indicating which external features are valid
; D3 - [r/w (bit 31)][number of PRAM bytes to read/write][starting PRAM address]
;
; Outputs: none
;
; Trashes: A0,A1,A2,A3,D0,D1,D2,D3
;
; Function: Reads/writes byte[s] from paramter RAM before traps are set up. This calls
; the implementation-specific RdXByte and WrXByte routines to read or write
; each PRAM byte.
;________________________________________________________________________________________
IMPORT ClkWPOff,RdXByte,WrXByte
StandardXPramIO
MOVEM.L D7/A4-A6,-(SP)
MOVEA.L DecoderInfo.VIA1Addr(A0),A2 ; get VIA 1's base address
BCLR #31,D3 ; read or write?
BEQ.S @StartRead ; -> read
;BigBSR6 ClkWPOff,A5 ; write-enable PRAM
BRA.S @StartWrite
@WriteLoop
MOVE SR,-(SP) ; save the status register
ORI #$0700,SR ; and disable all interrupts
BigBSR6 ClkWPOff,A5 ; write-enable PRAM
SWAP D3 ; get the PRAM address
MOVE.W D3,D1 ; move entire address into D1
MOVE.B (A3)+,D2 ; and a data byte from the caller's buffer
MOVE.L A3,D7 ; (save the buffer pointer)
BigBSR6 WrXByte,A5 ; write a byte to PRAM
MOVEA.L D7,A3 ; (restore the buffer pointer)
ADDQ.W #1,D3 ; next byte
MOVE (SP)+,SR ; restore the status register
bsr.l StdXPRAM2Patch ; call the patch
@StartWrite
SWAP D3 ; get the count
DBRA D3,@WriteLoop ; -> keep looping until done
MOVEA.L A1,A6 ; point to this machine's product info
ADDA.L ProductInfo.ClockPRAMPtr(A6),A6 ; and get the address of its clock/PRAM table
MOVE.L 4*cpWrProtOn(A6),D0 ; get the offset to the write-protect routine
BEQ.S @Done ; -> it's not supported for this implementation
;ADDA.L D0,A6 ; calculate the routine's address
;LEA @Done,A5 ; (simulate a BSR5)
;JMP (A6) ; and call it to turn on PRAM write-protect
bra.s @Done ; we're done
@ReadLoop SWAP D3 ; get the PRAM address
MOVE.W D3,D1 ; move entire address into D1
MOVE.L A3,D7 ; (save the buffer pointer)
BigBSR6 RdXByte,A5 ; read a byte from PRAM
MOVEA.L D7,A3 ; (restore the buffer pointer)
MOVE.B D1,(A3)+ ; and save it in the caller's buffer
ADDQ.W #1,D3 ; next byte
@StartRead SWAP D3 ; get the count
DBRA D3,@ReadLoop ; -> keep looping until done
@Done MOVEM.L (SP)+,D7/A4-A6
RTS
ENDIF
IF hasVIAClock THEN
;••••••••••••••••••••••••••••••••••••••••• RTC ••••••••••••••••••••••••••••••••••••••••••
IMPORT ReadTime
;________________________________________________________________________________________
;
; Routine: RTCReadTime
;
; Inputs: none
;
; Outputs: D0 - result code
; "GetParam" contains the 32-bit time value read from the clock
;
; Trashes: A0,A2,A3,D1,D2,D4,D5, 8 bytes at "GetParam"
;
; Function: reads the time from the realtime clock
;________________________________________________________________________________________
RTCReadTime MOVEM.L A5/A6,-(SP)
MOVEA.L VIA,A2 ; get the VIA 1 base address for RTCClkNoMem
MOVEQ #2-1,D4 ; try reading twice in case the time rolls over
@RetryLoop MOVEQ #$9D-256,D5 ; command to read the high byte of the time
LEA GetParam,A0 ; use clock utility storage
MOVEA.L A0,A3 ; and save a copy for later
@ReadLoop MOVE.B D5,D1 ; get the command into D1
BSR5 RTCClkNoMem ; read a byte of the time
MOVE.B D1,(A0)+ ; and save it
SUBQ.B #4,D5 ; next byte
BMI.S @ReadLoop ; -> keep looping until all 8 bytes are read
MOVE.L (A3)+,D0 ; do the two reads of the time match?
SUB.L (A3)+,D0
DBEQ D4,@RetryLoop ; -> keep looping if not, or for two tries
BEQ.S @Done ; -> no error
MOVEQ #ClkRdErr,D0 ; note the problem
@Done MOVEM.L (SP)+,A5/A6
RTS
;________________________________________________________________________________________
;
; Routine: RTCWriteTime
;
; Inputs: "Time" contains the 32-bit value to write to the clock
;
; Outputs: D0 - result code
;
; Trashes: A0,D1,D2,D3,D4,D5
;
; Function: writes a new time value to the realtime clock
;________________________________________________________________________________________
RTCWriteTime
MOVEM.L D6/A2/A5/A6,-(SP)
MOVEA.L VIA,A2 ; get the VIA 1 base address for RTCClkNoMem
BSR5 RTCWrProtOff ; write-enable the clock chip
MOVEQ #2-1,D5 ; give it two tries
MOVE.L Time,D3 ; get the time to write
@RetryLoop MOVEQ #$01,D6 ; command to write the low byte of the time
MOVEQ #4-1,D4
@WriteLoop MOVE.L D3,D1 ; put the byte to write into the upper word
SWAP D1
MOVE.B D6,D1 ; and the command in the lower word
BSR5 RTCClkNoMem ; write a byte of the time
ADDQ.B #4,D6 ; address the next register
ROR.L #8,D3 ; get the next byte
DBRA D4,@WriteLoop
BIGJSR ReadTime,A0 ; read the time to verify it was written correctly
BNE.S @Done
CMP.L Time,D3 ; do they match?
DBEQ D5,@RetryLoop ; -> keep looping if not, or for two tries
BEQ.S @Done ; -> they match so we're done
MOVEQ #ClkWrErr,D0 ; note the error
@Done MOVE.L D0,D3 ; save the result code
BSR5 RTCWrProtOn ; turn write-protect back on <H6>
MOVE.L D3,D0 ; restore the result code
MOVEM.L (SP)+,D6/A2/A5/A6
RTS
;________________________________________________________________________________________
;
; Routine: RTCXParam
;
; Inputs: A0 - pointer to user's buffer
; D0 - [number of bytes to transfer][starting PRAM address]
; D1 - bit 12: 1=read, 0=write
;
; Outputs: D0 - result code
;
; Trashes: A0,A1,A2,A3,D1,D2,D3
;
; Function: reads/writes byte[s] from RTC-based paramter RAM
;________________________________________________________________________________________
RTCXParam MOVEA.L A0,A3 ; get the pointer to the caller's buffer
MOVEA.L UnivInfoPtr,A1 ; point to the ProductInfo table <H3>
MOVEA.L A1,A0 ; and to the decoder table <H3>
ADDA.L ProductInfo.DecoderInfoPtr(A0),A0
MOVE.L D0,D3 ; get the starting PRAM address and count
BTST #12,D1 ; is it a read or write?
BNE.S @ItsRead
BSET #31,D3 ; mark it as a write
@ItsRead BSR.S StandardXPramIO ; go transfer the PRAM bytes <H3>
MOVEQ #0,D0 ; always return "no error"
RTS
;________________________________________________________________________________________
;
; Routine: RTCRdXByte
;
; Inputs: D1 - address of PRAM byte to read
; A1 - pointer to ProductInfo record for this machine
; A2 - VIA1 base address
; A3 - return address
;
; Outputs: D1 - byte read from PRAM
;
; Trashes: D0,D2,A5,A6
;
; Function: reads a byte from RTC-based PRAM without using RAM
;________________________________________________________________________________________
RTCRdXByte ANDI.W #$00FF,D1 ; [oooo oooo 7654 3210]
ASL.W #3,D1 ; [oooo o765 4321 0ooo]
ROR.B #1,D1 ; [oooo o765 o432 10oo]
ROR.W #8,D1 ; [o432 10oo oooo o765]
ORI.B #$B8,D1 ; [o432 10oo ioii i765]
BSR5 RTCClkNoMem ; read the PRAM byte
ANDI.W #$00FF,D1 ; zero-extend the byte
JMP (A3)
;________________________________________________________________________________________
;
; Routine: RTCWrXByte
;
; Inputs: D1 - address of PRAM byte to write
; D2 - byte to write to PRAM
; A1 - pointer to ProductInfo record for this machine
; A2 - VIA1 base address
; A3 - return address
;
; Outputs: none
;
; Trashes: D0,D1,D2,A5,A6
;
; Function: writes a byte to RTC-based PRAM without using RAM
;________________________________________________________________________________________
RTCWrXByte SWAP D1 ; copy the byte to write
MOVE.B D2,D1 ; to the upper word of D1
SWAP D1
ANDI.W #$00FF,D1 ; [oooo oooo 7654 3210]
ASL.W #3,D1 ; [oooo o765 4321 0ooo]
ROR.B #1,D1 ; [oooo o765 o432 10oo]
ROR.W #8,D1 ; [o432 10oo oooo o765]
ORI.B #$38,D1 ; [o432 10oo ooii i765]
MOVEA.L A3,A5 ; (fake out the return address)
BRA.S RTCClkNoMem ; write the PRAM byte
;________________________________________________________________________________________
;
; Routine: RTCInitHW
;
; Inputs: A1 - pointer to ProductInfo record for this machine
; A6 - return address (BSR6)
;
; Outputs: none
;
; Trashes: D0,D1,D2,A0,A2,A5,A6
;
; Function: initializes the RTC by taking it out of test mode
;________________________________________________________________________________________
RTCInitHW MOVEA.L A6,A0 ; save the return address
MOVEA.L A1,A2 ; point to the decoder info
ADDA.L ProductInfo.DecoderInfoPtr(A2),A2
MOVEA.L DecoderInfo.VIA1Addr(A2),A2 ; get VIA 1's base address <H6>
BSR5 RTCWrProtOff ; write-enable the clock chip
MOVEQ #(0<<16)+($31<<0),D1 ; write zero to test bit in clock chip
BSR5 RTCClkNoMem
MOVEA.L A0,A5 ; restore the return address
;________________________________________________________________________________________
;
; Routine: RTCWrProtOn [RTC utility routine]
;
; Inputs: A1 - pointer to ProductInfo record for this machine
; A2 - VIA1 base address
; A5 - return address (BSR5)
;
; Outputs: none
;
; Trashes: D0,D1,D2,A6
;
; Function: write-protects RTC-based PRAM when we're done modifying it
;________________________________________________________________________________________
RTCWrProtOn MOVE.L #($D5<<16)+($35<<0),D1 ; use 'set write protect' command
BRA.S RTCClkNoMem ; write it to the clock chip
;________________________________________________________________________________________
;
; Routine: RTCWrProtOff [RTC utility routine]
;
; Inputs: A1 - pointer to ProductInfo record for this machine
; A2 - VIA1 base address
; A5 - return address (BSR5)
;
; Outputs: none
;
; Trashes: D0,D1,D2,A6
;
; Function: write-enables RTC-based PRAM so we can write bytes to it
;________________________________________________________________________________________
RTCWrProtOff
MOVE.L #($55<<16)+($35<<0),D1 ; use 'reset write protect' command
* BRA.S RTCClkNoMem ; write it to the clock chip
;________________________________________________________________________________________
;
; Routine: RTCClkNoMem [RTC utility routine]
;
; Inputs: D1 - [x] [byte to write] [<extended command>] [command (bit 7: 1=read, 0=write)]
; A2 - VIA1 base address
; A5 - return address (BSR5)
;
; Outputs: D1 - data byte read
;
; Trashes: D0,D1,D2,A6
;
; Function: reads or writes one 8-bit clock register
;________________________________________________________________________________________
RTCClkNoMem MOVE SR,D2 ; save the SR
ORI #HiIntMask,SR ; and disable interrupts
SWAP D2
MOVE.B D1,D2 ; save a copy of the command
BCLR #vRTCEnb,vBufB(A2) ; enable the clock
MOVEQ #$78,D0 ; mask off the code bits
AND.B D1,D0
CMPI.B #$38,D0 ; is this an extended command?
BNE.S @NotExtended ; -> nope
BSR6 SendToClk ; send the first command byte
LSR.W #8,D1 ; move the second command byte into position
@NotExtended
TST.B D2 ; is this a read or write command?
BMI.S @ItsRead ; -> read
BSR6 SendToClk ; send the command byte
SWAP D1 ; get the data byte
BSR6 SendToClk ; and send it
BRA.S @Done
@ItsRead BSR6 SendToClk ; send the command byte
BSR6 ReadFrClk ; and read the data byte
@Done BSET #vRTCEnb,vBufB(A2) ; disable the clock
SWAP D2
MOVE D2,SR ; re-enable interrupts
RTS5
;________________________________________________________________________________________
;
; Routine: SendToClk [RTC utility routine]
;
; Inputs: D1 - command/data byte to write to RTC
; A2 - VIA1 base address
; A6 - return address (BSR6)
;
; Outputs: none
;
; Trashes: D0,D1
;
; Function: writes a command or data byte to the RTC (assumes interrupts masked to $2700)
;________________________________________________________________________________________
SendToClk MOVEQ #8-1,D0 ; shifting out 8 bits
@ShiftLoop SWAP D0 ; save the count
MOVE.B vBufB(A2),D0 ; get the VIA register's contents
ROR.B #1,D0 ; shift the bits to position the RTC data bit (bit 0)
ADDX.B D1,D1 ; shift the next data bit into the extend bit
ADDX.B D0,D0 ; shift the data bit from the extend bit to the RTC data bit
MOVE.B D0,VBufB(A2) ; stuff it back into the VIA register
BCLR #vRTCClk,vBufB(A2) ; and clock the bit out
BSET #vRTCClk,vBufB(A2)
SWAP D0 ; get the bit counter back
DBRA D0,@ShiftLoop ; -> keep looping until all bits are shifted out
RTS6
;________________________________________________________________________________________
;
; Routine: ReadFrClk [RTC utility routine]
;
; Inputs: A2 - VIA1 base address
; A6 - return address (BSR6)
;
; Outputs: D1 - data byte read from RTC
;
; Trashes: D0
;
; Function: reads a data byte from the RTC (assumes interrupts masked to $2700)
;________________________________________________________________________________________
ReadFrClk BCLR #vRTCData,vDirB(A2) ; set the RTC data bit's direction to input
MOVEQ #1,D1 ; initialize the result with a flag bit
@ShiftLoop BCLR #vRTCClk,vBufB(A2) ; clock the next bit in
BSET #vRTCClk,vBufB(A2)
MOVE.B vBufB(A2),D0 ; read the register
LSR.B #vRTCData+1,D0 ; shift the RTC data bit into the carry/X
ADDX.B D1,D1 ; shift in the new bit towards the msb
BCC.S @ShiftLoop ; -> keep looping until the flag bit shifts out
BSET #vRTCData,vDirB(A2) ; restore the RTC data bit's direction to output
RTS6
ENDIF ; {hasVIAClock}
IF hasPwrControls THEN
;••••••••••••••••••••••••••••••••••••• Power Manager ••••••••••••••••••••••••••••••••••••
WITH PMCommandRec
IMPORT USTPmgrSendCommand, USTPMgrSendByte, USTPMGRRecvByte
;________________________________________________________________________________________
;
; Routine: PMGRReadTime
;
; Inputs: none
;
; Outputs: D0 - result code
; "GetParam" contains the 32-bit time value read from the clock
;
; Trashes: A0
;
; Function: reads the time from the realtime clock
;________________________________________________________________________________________
PMGRReadTime
PEA GetParam ; pmRBuffer
MOVE.L (SP),-(SP) ; pmSBuffer (not used)
CLR.W -(SP) ; pmLength = 0
MOVE.W #TimeRead,-(SP) ; pmCommand = read time
MOVE.L SP,A0 ; point to the parameter block
_PmgrOp ; read the time
LEA pmRBuffer+4(SP),SP ; clean up the stack
RTS
;________________________________________________________________________________________
;
; Routine: PMGRWriteTime
;
; Inputs: "Time" contains 32-bit quantity to write to clock
;
; Outputs: D0 - result code
;
; Trashes: A0
;
; Function: writes a new time value to the realtime clock
;________________________________________________________________________________________
PMGRWriteTime
PEA Time ; pmRBuffer (not used)
MOVE.L (SP),-(SP) ; pmSBuffer
MOVE.W #4,-(SP) ; pmLength = 4 bytes of time
MOVE.W #TimeWrite,-(SP) ; pmCommand = write time
MOVE.L SP,A0 ; point to the parameter block
_PmgrOp ; read the time
LEA pmRBuffer+4(SP),SP ; clean up the stack
RTS
;________________________________________________________________________________________
;
; Routine: PMGRXParam
;
; Inputs: A0 - pointer to user's buffer
; D0 - [number of bytes to transfer][starting PRAM address]
; D1 - bit 12: 1=read, 0=write
;
; Outputs: D0 - result code
;
; Trashes: A0,A1,A2,D1,D2,D3
;
; Function: reads/writes byte[s] from PMGR-based parameter RAM
;________________________________________________________________________________________
PMGRXParam MOVEA.L SP,A2 ; point to the current top of stack
MOVE.L D0,D2 ;
SWAP D2 ; D2.L=[starting PRAM address][count]
ADDQ.W #2+1,D2 ; make space on the stack for a buffer and 2 parameter bytes
BCLR #0,D2 ; and round up to the next word
SUBA.W D2,SP
MOVEA.L SP,A1
MOVE.L A0,-(SP) ; pmRBuffer
MOVE.L A1,-(SP) ; pmSBuffer
MOVE.B D0,(A1)+ ; put the PRAM address
SWAP D0
MOVE.B D0,(A1)+ ; and count at the start of the send buffer
MOVEQ #xPramRead,D3 ; assume it's a read
MOVEQ #2,D2 ;
BTST #12,D1 ; is this a write command?
BNE.S @NoWrBuffer ; -> no
MOVEQ #xPramWrite,D3
ADD.W D0,D2 ; adjust the count to include the bytes to be written
BRA.S @StartCopy
@CopyLoop MOVE.B (A0)+,(A1)+ ; copy the write buffer into the stack buffer
@StartCopy DBRA D0,@CopyLoop
@NoWrBuffer MOVE.W D2,-(SP) ; pmLength
MOVE.W D3,-(SP) ; pmCommand
MOVEA.L SP,A0
_PMgrOp ; have the PMGR read/write PRAM
MOVEA.L A2,SP ; restore the stack pointer
RTS
;________________________________________________________________________________________
;
; Routine: PMGRWrXByte
;
; Inputs: D1 - address of PRAM byte to write
; D2 - byte to write to PRAM
; A1 - pointer to ProductInfo record for this machine
; A2 - VIA1 base address
; A3 - return address
;
; Outputs: none
;
; Trashes: A0,A3,A4,A5,A6,D0,D1,D2
;
; Function: writes a byte to PMGR-based PRAM without using RAM
;________________________________________________________________________________________
PMGRWrXByte MOVEA.L A3,A4 ; save the return address
EXG D3,D1 ; save D3 in D1, put PRAM address in the upper word of D3
SWAP D3
EXG D4,D2 ; save D4 in D2, put data byte in the upper word of D4
SWAP D4
MOVE.L D6,D0 ; save D6
MOVE.W #(xPramWrite<<0)|\ ; pmCommand
(-1<<8),D3 ; pmLength = -1 (just send command byte)
MOVEA.L A1,A0 ; point to the decoder table
ADDA.L ProductInfo.DecoderInfoPtr(A0),A0
BigBSR6 USTPmgrSendCommand,A5 ; send the command to the PMGR
BNE.S @PMgrDone ; -> error
MOVE.B #3,D3 ; send pmLength = 3 (address, count, data)
BigBSR5 USTPMgrSendByte,A6
BNE.S @PMgrDone ; -> error
SWAP D3 ; send the PRAM address
BigBSR5 USTPMgrSendByte,A6
BNE.S @PMgrDone ; -> error
MOVEQ #1,D3 ; send the PRAM byte count
BigBSR5 USTPMgrSendByte,A6
BNE.S @PMgrDone ; -> error
MOVE.L D4,D3 ; send the PRAM data byte
SWAP D3
BigBSR5 USTPMgrSendByte,A6
@PMgrDone MOVE.L D1,D3 ; restore registers
MOVE.L D2,D4
MOVE.L D0,D6
JMP (A4)
;________________________________________________________________________________________
;
; Routine: PMGRRdXByte
;
; Inputs: D1 - address of PRAM byte to read
; A1 - pointer to ProductInfo record for this machine
; A2 - VIA1 base address
; A3 - return address
;
; Outputs: D1 - byte read from PRAM
;
; Trashes: A0,A3,A4,A5,A6,D0,D2
;
; Function: reads a byte from PMGR-based PRAM without using RAM
;________________________________________________________________________________________
PMGRRdXByte MOVEA.L A3,A4 ; save the return address
EXG D3,D1 ; save D3 in D1, put command/data bytes in D3
MOVE.L D4,D0 ; save other registers
MOVE.L D6,D2
ANDI.L #$000000FF,D3 ; mask the PRAM address and build the command:
ORI.L #(xPramRead<<16)|\ ; pmCommand
(2<<24)|\ ; pmLength
(1<<8),D3 ; count = 1 byte
SWAP D3 ; D1.L=[count][addr][pmLength][pmCommand]
MOVEA.L A1,A0 ; point to the decoder table
ADDA.L ProductInfo.DecoderInfoPtr(A0),A0
BigBSR6 USTPmgrSendCommand,A5 ; send the command to the PMGR
BNE.S @PMgrError ; -> error
BigBSR5 USTPMGRRecvByte,A6 ; read a byte back
BNE.S @PMgrError ; -> error
CMPI.B #xPramRead,D3 ; is it the reply byte?
BNE.S @NoReplyByte ; -> no, new protocol
BigBSR5 USTPMGRRecvByte,A6 ; read the count byte
BNE.S @PMgrError ; -> error
@NoReplyByte
BigBSR5 USTPMGRRecvByte,A6 ; read the PRAM byte
BEQ.S @PMgrDone ; -> got it
@PMgrError MOVEQ #0,D3 ; return a zero if an error occurs
@PMgrDone EXG D1,D3 ; restore D3, return PRAM byte in D1
MOVE.L D0,D4 ; restore other registers
MOVE.L D2,D6
TST.B D1 ; set up the CCR if anybody cares
JMP (A4)
ENDWITH ; {PMgrRec}
ENDIF ; {hasPwrControls}
IF hasEgret THEN
;••••••••••••••••••••••••••••••••••••••••• Egret ••••••••••••••••••••••••••••••••••••••••
WITH EgretPB
;________________________________________________________________________________________
;
; Routine: EgretReadTime
;
; The one second interrupt handler for Egret/Caboose/Cuda maintains the lowmem global <sm10>
; 'Time' in a self correcting manner. This routine need only return the lowmem 'Time'.
; A call to _EgretDispatch will cause the AutoPoll timer within Egret/Caboose/Cuda to
; reset, resulting in deferring ADB auto polling. If an application calls ReadTime
; in a tight loop, ADB polling could be deferred indefinitely (making the mouse/keyboard
; appear to hang or respond intermittently). Since 'Time' is self correcting, the
; routine just returns 'Time' as the result placed into 'GetParam'.
;
; Inputs: none
;
; Outputs: D0 - result code
; "GetParam" contains the 32-bit time value read from the clock
;
; Trashes: A0
;
; Function: reads the time from the realtime clock
;________________________________________________________________________________________
EgretReadTime
clr.l d0 ; result code = noErr <sm10>
move.l Time,GetParam ; return current time <sm10>
; CLR.L -(SP) ; pbCompletion = nil <sm12>
; LEA pbCmdType-pbCompletion(SP),SP ; <sm12>
; MOVE.W #(pseudopkt<<8)+\ ; pbCmdType = pseudo type <sm12>
; (RdTime<<0),(SP) ; pbCmd = read time <sm12>
; MOVEA.L SP,A0 ; point to the parameter block <sm12>
; _EgretDispatch ; call Egret to read the time <sm12>
; MOVE.L pbParam(A0),GetParam ; move the time into a temporary buffer <sm12>
; LEA EgretPBSize(SP),SP ; clean up the stack <sm12>
RTS
;________________________________________________________________________________________
;
; Routine: EgretWriteTime
;
; Inputs: "Time" contains 32-bit quantity to write to clock
;
; Outputs: D0 - result code
;
; Trashes: A0
;
; Function: writes a new time value to the realtime clock
;________________________________________________________________________________________
EgretWriteTime
CLR.L -(SP) ; pbCompletion = nil
LEA pbParam-pbCompletion(SP),SP
MOVE.L Time,(SP) ; pbParam = current time
MOVE.W #(pseudopkt<<8)+\ ; pbCmdType = pseudo type
+(WrTime<<0),-(SP) ; pbCmd = write time
MOVEA.L SP,A0 ; point to the parameter block
_EgretDispatch ; call Egret to read the time
LEA EgretPBSize(SP),SP ; clean up the stack
RTS
;________________________________________________________________________________________
;
; Routine: EgretXParam
;
; Inputs: A0 - pointer to user's buffer
; D0 - [number of bytes to transfer][starting PRAM address]
; D1 - bit 12: 1=read, 0=write
;
; Outputs: D0 - result code
;
; Trashes: A0
;
; Function: reads/writes byte[s] from Egret-based parameter RAM
;________________________________________________________________________________________
EgretXParam CLR.L -(SP) ; pbCompletion = nil
SUBQ.W #pbCompletion-pbBufPtr,SP
MOVE.L A0,(SP) ; pbBufPtr = pointer to user's buffer
SWAP D0
MOVE.W D0,-(SP) ; pbByteCnt = number of PRAM bytes to read/write
SUBQ.W #pbByteCnt-pbParam,SP
SWAP D0
ADDI.W #$0100,D0 ;• convert PRAM address to 6805 address (temporary?)
MOVE.W D0,(SP) ; pbParam = starting PRAM address
MOVE.W #(pseudopkt<<8)+\ ; pbCmdType = pseudo type
(Rd6805Addr<<0),-(SP) ; pbCmd = read PRAM
BTST #12,D1 ; is this a write command?
BNE.S @NotWrite ; -> no
MOVE.W #(pseudopkt<<8)+\ ; pbCmdType = pseudo type
(Wr6805Addr<<0),(SP) ; pbCmd = write PRAM
@NotWrite MOVEA.L SP,A0 ; point to the parameter block
_EgretDispatch ; call Egret to read/write PRAM
LEA EgretPBSize(SP),SP ; clean up the stack
RTS
;________________________________________________________________________________________
;
; Routine: EgretWrXByte
;
; Inputs: D1 - address of PRAM byte to write
; D2 - byte to write to PRAM
; A1 - pointer to ProductInfo record for this machine
; A2 - VIA1 base address
; A3 - return address
;
; Outputs: none
;
; Trashes: D0,D1,D2,A5,A6
;
; Function: writes a byte to Egret-based PRAM without using RAM
;________________________________________________________________________________________
EgretWrXByte
SWAP D1
MOVE.B D2,D1 ; save the byte to send
SWAP D1
BSET #15,D1 ; mark this as a write
; fall into EgretRdXByte
;________________________________________________________________________________________
;
; Routine: EgretRdXByte
;
; Inputs: D1 - address of PRAM byte to read
; A1 - pointer to ProductInfo record for this machine
; A2 - VIA1 base address
; A3 - return address
;
; Outputs: D1 - byte read from PRAM
;
; Trashes: D0,D2,A5,A6
;
; Function: reads a byte from Egret-based PRAM without using RAM
;________________________________________________________________________________________
EgretRdXByte
BCLR #vSysSesbit,vBufB(A2) ; pending transaction?
BEQ.S @NotPending ; -> no
BSR5 EgretDelay ; wait a bit
BCLR #vViaFullBit,vBufB(A2) ; de-assert VIA full
BSR5 EgretDelay ; wait some more
BRA.S @NotPending
@Abort BSR6 EgretReadByte ; too many bytes being returned so
BEQ.S @Abort ; keep throwing them out until there are no more,
BRA.S @Reset ; then start over
@DisposePacket
BSR5 EgretDelay ; wait for the interrupt to occur
TST.B vSR(A2) ; then read the ATTN byte to clear the interrupt
@Reset BCLR #vSysSesbit,vBufB(A2) ; turn off SYS session
BCLR #vViaFullBit,vBufB(A2) ; and de-assert VIA full
BCLR #SRdir,vACR(A2) ; define shift direction = FROM Egret
BSR5 EgretDelay ; wait
BRA.S @CheckXCVR
@ThrowAway BSET #vSysSesbit,vBufB(A2) ; set SYS session
BSR6 EgretReadByte ; read a byte to throw away
@CheckXCVR BTST #vXcvrSesBit,vBufB(A2) ; is XCVR session asserted (active low)?
BEQ.S @ThrowAway ; -> yes, throw away the byte
BSR5 EgretDelay ; wait
@NotPending ORI.B #ACRMode++(1<<SRdir),vACR(A2) ; set VIA shift register to shift out with external clock
BTST #vXcvrSesBit,vBufB(A2) ; is XCVR session asserted (active low)?
BEQ.S @DisposePacket ; -> yes, dispose of the packet
BSET #vSysSesbit,vBufB(A2) ; assert sys session signal
MOVEQ #pseudopkt,D0 ; packet type
BSR6 EgretSendByte
BMI.S @Reset ; -> Egret's hung, so try to reset
BTST #vXcvrSesBit,vBufB(A2) ; is XCVR session asserted (active low)?
BEQ.S @Reset ; -> yes, throw out the packet
MOVEQ #rdPram,D0 ; assume we're doing a read
TST.W D1 ; are we?
BPL.S @SendCmd
MOVEQ #wrPram,D0 ; no, we're writing
@SendCmd BSR6 EgretSendByte ; send the command byte
MOVEQ #0,D0
BSR6 EgretSendByte ; send the MSB of the PRAM address
MOVE.B D1,D0
BSR6 EgretSendByte ; send the LSB of the PRAM address
TST.W D1 ; are we writing?
BPL.S @SendDone
MOVE.L D1,D0 ; yes, get the data byte
SWAP D0
BSR6 EgretSendByte ; and send it
@SendDone BCLR #SRdir,vACR(A2) ; define shift direction = FROM Egret
BCLR #vSysSesbit,vBufB(A2) ; turn off SYS session
@WaitForIRQ BTST #ifSR,vIFR(A2) ; wait for shift to complete
BEQ.S @WaitForIRQ
BTST #vXcvrSesBit,vBufB(A2) ; is XCVR session asserted (active low)?
BNE.S @WaitForIRQ ; -> no, keep polling until it is
TST.B vSR(A2) ; read the ATTN byte to clear the interrupt
BSR5 EgretDelay ; wait
BSET #vSysSesbit,vBufB(A2) ; assert sys session again
BSR6 EgretReadByte ; read the response packet type
BNE.S @Reset ; -> XCVR session turned off early, so try again
BSR6 EgretReadByte ; read the response packet flag
BNE.S @Reset ; -> XCVR session turned off early, so try again
TST.W D1 ; is this a read or write?
BPL.S @FinishRead ; -> read
BSR6 EgretReadByte ; read the "command was" byte
BEQ.S @Abort ; -> XCVR session is still on, so try again
CMPI.B #wrPram,D0 ; is this the correct packet?
BNE.S @Reset ; -> no, try again
BCLR #vSysSesbit,vBufB(A2) ; turn off SYS session
BCLR #vViaFullBit,vBufB(A2) ; and de-assert VIA full
BSR5 EgretDelay ; delay to let Egret see the lines change state
JMP (A3)
@FinishRead BSR6 EgretReadByte ; read the "command was" byte
BNE.S @Reset ; -> XCVR session turned off early, so try again
SUBQ.B #rdPram,D0 ; is this the correct packet?
BNE.S @Reset ; -> no, try again
BSR6 EgretReadByte ; read the PRAM data byte
BCLR #vSysSesbit,vBufB(A2) ; turn off SYS session
@Wait4XCVR BTST #vXcvrSesBit,vBufB(A2) ; wait for XCVR session to turn off
BEQ.S @Wait4XCVR
BCLR #vViaFullBit,vBufB(A2) ; de-assert VIA full
BSR5 EgretDelay ; delay to let Egret see the lines change state
MOVE.B D0,D1 ; return the byte in D1
JMP (A3)
;________________________________________________________________________________________
;
; Routine: EgretSendByte [Egret utility routine]
;
; Inputs: D0 - byte to send to Egret
; A2 - VIA1 base address
; A6 - return address (BSR6)
;
; Outputs: none
;
; Trashes: D2,A5
;
; Function: sends a byte to Egret
;________________________________________________________________________________________
EgretSendByte
MOVEA.L A6,A5 ; save the return address
MOVE.B D0,vSR(A2) ; load byte into shift register
BSET #vViaFullBit,vBufB(A2) ; indicate shift register is full
MOVE.W #32767,D0 ; set a 42msec timeout
@Wait4Shift BTST #ifSR,vIFR(A2) ; wait for shift to complete
DBNE D0,@Wait4Shift
BEQ.S EgretSendTO ; -> timed out
TST.B vSR(A2) ; clear the interrupt
BCLR #vViaFullBit,vBufB(A2) ; then negate VIA full bit
; delay a bit so Egret can keep up...
EgretDelay MOVEQ #104,D2 ; delay 125µs minimum (we actually delay 134µsec)
@wait TST.B (A2) ; sync to VIA clock (about 1.28µsec/access)
DBRA D2,@wait ; delay at least 100 us (very rough)
EgretSendTO TST.W D0 ; set CCR: BMI if send timeout, BPL if OK
RTS5
;________________________________________________________________________________________
;
; Routine: EgretReadByte [Egret utility routine]
;
; Inputs: A2 - VIA1 base address
; A6 - return address (BSR6)
;
; Outputs: D0 - byte read
;
; Trashes: D2,A5
;
; Function: reads a byte from Egret
;________________________________________________________________________________________
EgretReadByte
BCLR #vViaFullBit,vBufB(A2) ; de-assert VIA full
@Wait4Shift BTST #ifSR,vIFR(A2) ; wait for shift to complete
BEQ.S @Wait4Shift
BSET #vViaFullBit,vBufB(A2) ; acknowlege byte
MOVE.B vSR(A2),D0 ; read the byte
BSR5 EgretDelay ; give Egret time to see it
BTST #vXcvrSesBit,vBufB(A2) ; return with the state of XCVR session
RTS6
;________________________________________________________________________________________
;
; Routine: CudaRdXByte
;
; Inputs: D1 - address of PRAM byte to read
; A1 - pointer to ProductInfo record for this machine
; A2 - VIA1 base address
; A3 - return address
;
; Outputs: D1 - byte read from PRAM
;
; Trashes: D0,D2,A5,A6
;
; Function: reads a byte from Egret-based PRAM without using RAM
;
; This routine can only be executed after a SyncAck cycle. The SyncAck cycle,
; which is executed immediately after InitVia will disable all asynchronous
; message sources within Cuda. This allows the following routine to be written
; without regard to detecting collisions between command packets and response
; packets. AT NO TIME SHOULD AN ASYNCHRONOUS MESSAGE SOURCE BE ENABLED PRIOR
; TO USE OF THIS ROUTINE. ASYNCHRONOUS MESSAGE SOURCES SHOULD ONLY BE ENABLED
; AFTER THE CUDA MANAGER HAS BEEN INSTALLED.
;________________________________________________________________________________________
CudaRdXByte cmp.l #$8000,sp ; is there any stack yet?
ble.s @5 ; no, use register based
cmp.w #0,EgretBase
bne.s @5
movec VBR,d2 ; get the contents of VBR
tst.l d2 ; check for zero
bne.s @5
; This code allows the low level routines to be used after the Cuda manager is installed
; by constructing a parameter block on the stack and issuing a call to CudaDispatch. This
; is absolutely necessary because the low level routines do not detect collisions which can
; occur once the Cuda manager is installed. Further, it is almost impossible to determine
; how to to synchronize Cuda after an asynchronous re-boot has occurred.
movem.l a0-a6/d0/d2-d7,-(sp)
suba.l #EgretPBSize,sp ; make room on the stack for Cuda parameter block
move.b #pseudoPkt,pbCmdType(sp) ; set packet type
move.b #RdPram,pbCmd(sp) ; set packet command
move.w d1,pbParam(sp) ; set pram address
move.w #1,pbByteCnt(sp) ; 1 byte to send
move.l sp,pbBufPtr(sp)
add.l #pbParam+2,pbBufPtr(sp) ; point to our data buffer
move.b #0,pbFlags(sp)
move.w #0,pbResult(sp)
move.l #0,pbCompletion(sp) ; execute synchronously
move.l sp,a0 ; parameter block in a0
_EgretDispatch ; (this is actually the Cuda A-Trap)
move.w pbParam+2(sp),d1 ; get pram data
adda.l #EgretPBSize,sp ; remove parameter block from stack
movem.l (sp)+,a0-a6/d0/d2-d7
jmp (a3)
;
; The following code is executed only when the Cuda manager is not installed.
; AT NO TIME SHOULD AN ASYNCHRONOUS MESSAGE SOURCE BE ENABLED PRIOR TO USE OF THIS ROUTINE.
; ASYNCHRONOUS MESSAGE SOURCES SHOULD ONLY BE ENABLED AFTER THE CUDA MANAGER HAS BEEN INSTALLED.
;
@5
ori.b #ACRmode,vacr(a2) ; else initialize via mode
bset.b #SRdir,vACR(a2) ; set output mode
move.b #pseudopkt,vSR(a2) ; send packet type & clear any pending shift irq
bclr.b #TIP,vBufB(a2) ; start the transaction
@10
btst.b #ifSR,vIFR(a2) ; Wait for shift register IRQ to indicate byte was read
beq.s @10
;________________________________________________________________________________________________
; When outputing from the VIA to Cuda, the interrupt will occur prior to the rising edge
; of CB1. Delay long enough for the edge to occur before acknowledging the shift
; register interrupt. (R. Montagne 1/11/93)
;________________________________________________________________________________________________
moveq #10,d2 ; delay for 10µS min. <LW3><VIA rbm>
@m7dly ; <LW3><VIA rbm>
tst.b vBufB(a1) ; hardware access is 1.2µS <LW3><VIA rbm>
sub.b #1,d2 ; can only trash low byte <LW3><VIA rbm>
bne.s @m7dly ; wait long enough for CB1 to rise (10µS delay) <LW3><VIA rbm>
tst.b vSR(a2) ; Clear the shift register IRQ
moveq #rdpram,d2 ; Send the command byte
BSR6 CudaWriteByte
move.l d1,d2
lsr.l #8,d2 ; Send PRAM address MSB
BSR6 CudaWriteByte
move.l d1,d2 ; Send PRAM address LSB
BSR6 CudaWriteByte
bclr.b #SRdir,vACR(a2) ; Define direction FROM Cuda
btst.b #SRdir,vACR(a2)
@69up
bne.s @69up
ori.b #((1<<vCudaTIP)|\
(1<<vCudaByteAck)),vBufB(a2) ; terminate current command transaction
@Wait4Attn
btst.b #ifSR,vIFR(a2) ; Wait for attention IRQ
beq.s @Wait4Attn
tst.b vSR(a2) ; Read attn byte from VIA (clears IRQ)
bclr.b #vCudaTIP,vBufB(a2) ; assert TIP to begin response transaction
BSR6 CudaReadByte ; Read a byte - response packet type
BSR6 CudaReadByte ; next byte - response packet flag
BSR6 CudaReadByte ; next byte - "cmd was" byte
BSR6 CudaReadByte ; finally! PRAM data in d2
ori.b #((1<<vCudaTIP)|\
(1<<vCudaByteAck)),vBufB(a2) ; terminate current response transaction
@Wait4CudaDone
btst.b #vCudaTReq,vBufB(a2) ; Wait for TReq to negate
beq.s @Wait4CudaDone
@Wait4IdleIRQ
btst.b #ifSR,vIFR(a2) ; Wait for idle state IRQ
beq.s @Wait4IdleIRQ
tst.b vSR(a2) ; Read idle state byte (clears IRQ)
move.l d2,d1 ; return data in d1
jmp (a3) ; return to caller
;________________________________________________________________________________________
;
; Routine: CudaWrXByte
;
; Inputs: D1 - address of PRAM byte to write
; D2 - byte to write to PRAM
; A1 - pointer to ProductInfo record for this machine
; A2 - VIA1 base address
; A3 - return address
;
; Outputs: none
;
; Trashes: D0,D1,D2,A5,A6
;
; Function: writes a byte to Egret-based PRAM without using RAM
;
; This routine can only be executed after a SyncAck cycle. The SyncAck cycle,
; which is executed immediately after InitVia will disable all asynchronous
; message sources within Cuda. This allows the following routine to be written
; without regard to detecting collisions between command packets and response
; packets. AT NO TIME SHOULD AN ASYNCHRONOUS MESSAGE SOURCE BE ENABLED PRIOR
; TO USE OF THIS ROUTINE. ASYNCHRONOUS MESSAGE SOURCES SHOULD ONLY BE ENABLED
; AFTER THE CUDA MANAGER HAS BEEN INSTALLED.
;________________________________________________________________________________________
CudaWrXByte cmp.l #$8000,sp ; is there any stack yet? <SM5>(rbm)<2> begin {
ble.s @5 ; no, use register based
cmp.w #0,EgretBase
bne.s @5
movec vbr,d5 ; get a copy of VBR
tst.l d5 ; is it zero?
bne.s @5
; This code allows the low level routines to be used after the Cuda manager is installed
; by constructing a parameter block on the stack and issuing a call to CudaDispatch. This
; is absolutely necessary because the low level routines do not detect collisions which can
; occur once the Cuda manager is installed. Further, it is almost impossible to determine
; how to to synchronize Cuda after an asynchronous re-boot has occurred.
movem.l a0-a6/d0/d3-d7,-(sp)
suba.l #EgretPBSize,sp ; make room on the stack for Cuda parameter block
move.b #pseudoPkt,pbCmdType(sp) ; set packet type
move.b #WrPram,pbCmd(sp) ; set packet command
move.w d1,pbParam(sp) ; set pram address
move.w d2,pbParam+2(sp) ; set pram data
move.w #1,pbByteCnt(sp) ; 1 byte to send
move.l sp,pbBufPtr(sp)
add.l #pbParam+2,pbBufPtr(sp) ; point to our data buffer
move.b #0,pbFlags(sp)
move.w #0,pbResult(sp)
move.l #0,pbCompletion(sp) ; execute synchronously
move.l sp,a0 ; parameter block in a0
_EgretDispatch ; (this is actually the Cuda A-Trap)
adda.l #EgretPBSize,sp ; remove parameter block from stack
movem.l (sp)+,a0-a6/d0/d3-d7
jmp (a3)
; The following code is executed only when the Cuda manager is not installed.
; AT NO TIME SHOULD AN ASYNCHRONOUS MESSAGE SOURCE BE ENABLED PRIOR TO USE OF THIS ROUTINE.
; ASYNCHRONOUS MESSAGE SOURCES SHOULD ONLY BE ENABLED AFTER THE CUDA MANAGER HAS BEEN INSTALLED.
@5 move.l d2,d5 ; Save the write data
ori.b #ACRmode,vacr(a2) ; else initialize via mode
bset.b #SRdir,vACR(a2) ; set output mode
move.b #pseudopkt,vSR(a2) ; send packet type & clear any pending shift irq
bclr.b #TIP,vBufB(a2) ; start the transaction
@10
btst.b #ifSR,vIFR(a2) ; Wait for shift register IRQ to indicate byte was read
beq.s @10
;________________________________________________________________________________________________
; When outputing from the VIA to Cuda, the interrupt will occur prior to the rising edge
; of CB1. Delay long enough for the edge to occur before acknowledging the shift
; register interrupt. (R. Montagne 1/11/93)
;________________________________________________________________________________________________
move.b #10,d2 ; mode 7 interrupt occurs at falling edge CB1 <LW3><VIA rbm>
@m7dly ; <LW3><VIA rbm>
tst.b vBufB(a1) ; hardware access is 1.2µS <LW3><VIA rbm>
sub.b #1,d2 ; can only trash low byte <LW3><VIA rbm>
bne.s @m7dly ; wait long enough for CB1 to rise (10µS delay) <LW3><VIA rbm>
tst.b vSR(a2) ; Clear the shift register IRQ
moveq #wrpram,d2 ; Send the command byte
BSR6 CudaWriteByte
move.l d1,d2
lsr.l #8,d2 ; Send PRAM address MSB
BSR6 CudaWriteByte
move.l d1,d2 ; Send PRAM address LSB
BSR6 CudaWriteByte
move.l d5,d2 ; Send the write data
BSR6 CudaWriteByte
bclr.b #SRdir,vACR(a2) ; Define direction FROM Cuda
ori.b #((1<<vCudaTIP)|\
(1<<vCudaByteAck)),vBufB(a2) ; terminate current transaction
@40 btst.b #ifSR,vIFR(a2) ; Wait for attention byte shift register IRQ
beq.s @40
btst.b #vCudaTReq,vBufB(a2) ; Is TReq asserted? (active low)
bne.s @40 ; continue polling if TReq is not asserted
tst.b vSR(a2) ; Read attn byte from VIA (clears IRQ)
bclr.b #vCudaTIP,vBufB(a2) ; assert TIP to begin response transaction
@20
btst.b #ifSR,vIFR(a2) ; Wait for shift register IRQ to indicate byte was read
beq.s @20
tst.b vSR(a2) ; Clear the shift register IRQ
ori.b #((1<<vCudaTIP)|\
(1<<vCudaByteAck)),vBufB(a2) ; terminate the response transaction
@41 btst.b #ifSR,vIFR(a2) ; Wait for idle state IRQ
beq.s @41
tst.b vSR(a2) ; Read idle state byte (clears IRQ)
jmp (a3)
;---------------------------------------------------------------------------
EXPORT CudaWriteByte
CudaWriteByte
move.b d2,vSR(a2) ; Put data in VIA data reg
eori.b #1<<vCudaByteAck,vBufB(a2) ; indicate data moved into VIA
@10
btst.b #ifSR,vIFR(a2) ; Wait for shift register IRQ to indicate byte was read
beq.s @10
;________________________________________________________________________________________________
; When outputing from the VIA to Cuda, the interrupt will occur prior to the rising edge
; of CB1. Delay long enough for the edge to occur before acknowledging the shift
; register interrupt. (R. Montagne 1/11/93)
;________________________________________________________________________________________________
move.b #10,d2 ; mode 7 interrupt occurs at falling edge CB1 <LW3><VIA rbm>
@m7dly ; <LW3><VIA rbm>
tst.b vBufB(a1) ; hardware access is 1.2µS <LW3><VIA rbm>
sub.b #1,d2 ; can only trash low byte <LW3><VIA rbm>
bne.s @m7dly ; wait long enough for CB1 to rise (10µS delay) <LW3><VIA rbm>
tst.b vSR(a2) ; Clear the shift register IRQ
RTS6
;---------------------------------------------------------------------------
EXPORT CudaReadByte
CudaReadByte
btst.b #ifSR,vIFR(a2) ; Wait for IRQ
beq.s CudaReadByte
move.b vSR(a2),d2 ; Read data from VIA (clears IRQ)
and.w #$00FF,d2 ; isolate the byte
eori.b #1<<vCudaByteAck,vBufB(a2) ; handshake the byte
btst.b #vCudaTReq,vBufB(a2) ; Return with state of TReq Session <SM5>(rbm)<2> end }
RTS6
ENDWITH ; {EgretPB}
ENDIF ; {hasEgret}
IF hasProtectedPRAM THEN
; •••••••••••••••••••••••• PRAM in VRAM, clock is someplace else ••••••••••••••••••••••••
WITH ProductInfo, VideoInfo
;________________________________________________________________________________________
;
; Routine: NoPRAMReadTime
;
; Inputs: none
;
; Outputs: D0 - result code
; "GetParam" contains the 32-bit time value read from the clock
;
; Trashes: TBD
;
; Function: Would read the time from the clock if we knew what kind it was. This routine
; is provided as an aid to the person who has to code for this case.
;________________________________________________________________________________________
NoPRAMReadTime
MOVEQ #dsCoreErr,D0 ; just die so we know something didn't happen
_SysError
MOVEQ #clkRdErr,D0 ; and leave this as a clue to why
RTS
;________________________________________________________________________________________
;
; Routine: NoPRAMWriteTime
;
; Inputs: "Time" contains 32-bit quantity to write to clock
;
; Outputs: D0 - result code
;
; Trashes: TBD
;
; Function: Would write the time to the clock if we knew what kind it was. This routine
; is provided as an aid to the person who has to code for this case.
;________________________________________________________________________________________
NoPRAMWriteTime
MOVEQ #dsCoreErr,D0 ; just die so we know something didn't happen
_SysError
MOVEQ #clkWrErr,D0 ; and leave this as a clue to why
RTS
;________________________________________________________________________________________
;
; Routine: NoPRAMXParam
;
; Inputs: A0 - pointer to user's buffer
; D0 - [number of bytes to transfer][starting PRAM address]
; D1 - bit 12: 1=read, 0=write
;
; Outputs: D0 - result code
;
; Trashes: A0,A1,A2,D1,D2,D3
;
; Function: reads/writes byte[s] from parameter RAM that hides in unused video RAM
; (assumes that traps and the MMU are set up)
;________________________________________________________________________________________
NoPRAMXParam
MOVEA.L A0,A2 ; save the pointer to the user's buffer,
MOVE.L D0,D3 ; and the starting PRAM address and count
MOVEQ #true32b,D0 ; switch into 32-bit addressing mode
_SwapMMUMode
MOVEA.L UnivInfoPtr,A1 ; point to this machine's product info,
ADDA.L VideoInfoPtr(A1),A1 ; which leads to video info,
MOVEA.L VRAMPhysAddr(A1),A1 ; which leads to the base address of video,
LEA -256(A1),A1 ; which leads to PRAM in unused video memory
ADD.W D3,A1 ; index into start of desired PRAM
SWAP D3 ; get the number of bytes to transfer
BTST #12,D1 ; is it a write?
BNE.S @StartCopy ; -> no
EXG A1,A2 ; yes, exchange source and destination pointers
BRA.S @StartCopy
@CopyPRAM MOVE.B (A1)+,(A2)+ ; move the bytes
@StartCopy DBRA D3,@CopyPRAM
_SwapMMUMode ; restore the addressing mode
MOVEQ #0,D0 ; never an error
RTS
;________________________________________________________________________________________
;
; Routine: NoPRAMXPramIO
;
; Inputs: A0 -- pointer to table of base addresses
; A1 -- pointer to ProductInfo record for this machine
; A3 -- pointer to PRAM I/O buffer
; D1 -- flags indicating which external features are valid
; D3 -- [r/w (bit 31)][number of PRAM bytes to read/write][starting PRAM address]
;
; Outputs: none
;
; Trashes: A2,A3,D3
;
; Function: reads/writes byte[s] from parameter RAM that hides in unused video RAM
; before traps (or the MMU) are set up
;________________________________________________________________________________________
NoPRAMXPramIO
MOVEA.L A1,A2 ; point to this machine's product info,
ADDA.L VideoInfoPtr(A2),A2 ; which leads to video info,
MOVEA.L VRAMPhysAddr(A2),A2 ; which leads to the base address of video,
LEA -256(A2),A2 ; which leads to PRAM in unused video memory
ADD.W D3,A2 ; index into start of desired PRAM
SWAP D3 ; get the number of bytes to transfer
BCLR #15,D3 ; is it a write?
BEQ.S @StartCopy ; -> no
EXG A2,A3 ; yes, exchange source and destination pointers
BRA.S @StartCopy
@CopyPRAM MOVE.B (A2)+,(A3)+ ; move the bytes
@StartCopy DBRA D3,@CopyPRAM
RTS
;________________________________________________________________________________________
;
; Routine: NoPRAMRdXByte
;
; Inputs: D1 - address of PRAM byte to read
; A1 - pointer to ProductInfo record for this machine
; A2 - VIA1 base address
; A3 - return address
;
; Outputs: D1 - byte read from PRAM
;
; Trashes: A0,D0
;
; Function: reads a byte from parameter RAM that hides in unused video RAM without using RAM
;________________________________________________________________________________________
NoPRAMRdXByte
MOVEA.L A1,A0 ; point to this machine's product info,
ADDA.L VideoInfoPtr(A0),A0 ; which leads to video info,
MOVEA.L VRAMPhysAddr(A0),A0 ; which leads to the base address of video,
LEA -256(A0),A0 ; which leads to PRAM in unused video memory
MOVEQ #0,D0
MOVE.B D1,D0 ; zero-extend the byte number to make an index
MOVE.B 0(A0,D0),D1 ; and get the byte
JMP (A3)
;________________________________________________________________________________________
;
; Routine: NoPRAMWrXByte
;
; Inputs: D1 - address of PRAM byte to write
; D2 - byte to write to PRAM
; A1 - pointer to ProductInfo record for this machine
; A2 - VIA1 base address
; A3 - return address
;
; Outputs: none
;
; Trashes: A0,D0
;
; Function: writes a byte to parameter RAM that hides in unused video RAM without using RAM
;________________________________________________________________________________________
NoPRAMWrXByte
MOVEA.L A1,A0 ; point to this machine's product info,
ADDA.L VideoInfoPtr(A0),A0 ; which leads to video info,
MOVEA.L VRAMPhysAddr(A0),A0 ; which leads to the base address of video,
LEA -256(A0),A0 ; which leads to PRAM in unused video memory
MOVEQ #0,D0
MOVE.B D1,D0 ; zero-extend the byte number to make an index
MOVE.B D2,0(A0,D0) ; and write the byte
JMP (A3)
ENDWITH ; {ProductInfo, VideoInfo}
ENDIF ; {hasProtectedPRAM}
; ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
;________________________________________________________________________________________
;
; Routine: ClkNoMem
;
; Inputs: A1 - VIA 1 base address
; A5 - return address (BSR5)
; D1 - clock chip command byte (bit 7: 0=write, 1=read)
; D2 - data byte to write
;
; Outputs: D2 - data byte read
;
; Trashes: A0,A1,A5,A6,D1,D3
;
; Function: support for the jClkNoMem vector (do we still need this?)
;________________________________________________________________________________________
EXPORT ClkNoMem
ClkNoMem RTS5
;________________________________________________________________________________________ <H9>thru next <H9>
; These patches change the behavior of StandardXPramIO so that during a multi-byte
; write to PRAM, interrupts are disabled and re-enabled around the write of each byte,
; and the RTC is write-enabled and then write-protected again during the time that
; interrupts are off. This fixes a problem where the Reliability Manager's interrupt
; task, which calls this routine to write to PRAM every 5 minutes, interrupts during
; this loop, resulting in PRAM being left write-protected; when control returns to the
; foreground, remaining bytes to be written to PRAM don't get written.
;
; Monte Benaresh - 7/2/93
;________________________________________________________________________________________
StdXPRAM2Patch ; patch for StandardXPramIO
MOVEA.L A1,A6 ; point to this machine's product info
ADDA.L ProductInfo.ClockPRAMPtr(A6),A6 ; and get the address of its clock/PRAM table
MOVE.L 4*cpWrProtOn(A6),D0 ; get the offset to the write-protect routine
BEQ.S @Done ; -> it's not supported for this implementation
ADDA.L D0,A6 ; calculate the routine's address
movea.l (sp)+,A5 ; simulate a BSR5 which returns to our caller
JMP (A6) ; and call it to turn on PRAM write-protect
@Done
rts ; <H9>
END