; ; File: PowerMgrPrimitives.a ; ; Contains: low-level routines for managing Power Management tasks ; ; Written by: Helder J. Ramalho ; ; Copyright: © 1992-1993 by Apple Computer, Inc. All rights reserved. ; ; This file is used in these builds: ROM ; ; Change History (most recent first): ; ; 12/13/93 PN Roll in KAOs and Horror changes to support Malcom and AJ ; machines. ; 9/17/93 KH I'm not Eva (EH), but this BBS installation is apparently ; screwed up. ; 9/17/93 SKH Fix a bug with sleep. Don't use A3 to acces PMgr globals, use ; A2. (SaveSleepInfo) ; 8/4/93 JDR Private sound defines were moved to SoundPrivate.a. ; 01-12-93 jmp Changed name from drHwDBLite to the more appropriate drHwGSC. ; 11/20/92 SWC Rolled in the rest of the Horror changes. ; 8/13/92 SWC Changed GetExtLevel to change warning levels at the 1/3 and 2/3 ; levels instead of at the x/4 levels so we get better spaced ; warnings. ; 8/4/92 SWC Save and restore the current GrafPort around the call to ; PaintBehind since PaintBehind will clobber it. This was causing ; the screen to be incorrectly updated (portions remained gray). ; 7/27/92 SWC Point to the correct SendSleep routine for DBLite (we ; distinguish between a normal and low power sleep). ; 7/23/92 SWC Fixed a bug in GetExtLevel that was keeping us from displaying ; low power messages. ; 7/22/92 SWC In SaveMSC, turn on IOClk before we go to sleep in case a bar is ; added during sleep so that the clock will be running as soon as ; power is turned back on. If it turns out no bar is attached, the ; Docking Manager will turn this back off. ; 7/14/92 ag Change the LeadScaledBattery routine to use globals and avoid ; hitting the power manager. Instead of current battery level, ; use averaged battery level. ; 7/14/92 djw (HJR) Remove NiagraChkIdle since it is not used. ; 7/14/92 SWC Hopefully I finally got the scaled battery stuff working (those ; Dart guys will be mighty pissed if we need to do a patch...). ; 7/14/92 HJR Modify dimming so that a screen update will only occur if some ; device supported the LowPwrSelect control call. ; 7/13/92 HJR Fix a bug in Dimming where if External Monitor is startup ; device, internal display does not unblank itself after dimming. ; Problem was that A0=IOPB was being trashed by PaletteDispatch. ; 7/13/92 HJR Rewrote External monitor alerts so that if an External Monitor ; Alert is displayed, the charger state will not remove alert. ; Added ChkDimming so that DBlite may use it more readily. ; 7/13/92 ag Added constants to the info tables for delay time before shorted ; battery dialog and delta battery warn level if external monitor ; is being used. ; 7/11/92 HJR Rewrote VidLowPwrSelect so that it runs throught the device list ; and powers down all available screens. Cleaned up things here ; and there. ; 7/10/92 ag added modem primitive tables and routines. added vectorize ; shutdown with retry. ; 7/1/92 ag Clear sound power control before turning on power amps. ; 7/1/92 ag Add code to turn off power to sound circuits controlled by ponti ; asic during sleep. Added power amplifiers controls to niagra ; sleep and wake routines. ; 6/30/92 HJR In SaveGSC and RestoreGSC, call the driver to Blank the screen ; on going to sleep and grey the screen on wakeup. ; 6/25/92 SWC Fixed the clamshell logic (was crashing if a docking bar ; prevented sleep). We no longer shutdown on clamshell close since ; that had a lot of problems. Upped the padding size to 8K since ; we're getting full (and more stuff is expected. :) ; 6/11/92 djw Modify PostUserNmMsg to accept completion routine parameter. ; Add code to check for posting a warning msg based on a flag set ; by the external video's primaryInit (if no charger attached) in ; NiagraRunIdleRoutineProc. ; 6/10/92 HJR Disable GDevice when dimming and fix bug where if external ; device is main device, bus error occurs after dimming. ; 6/9/92 ag change the batman save and restore code the the one used on ; terror. The order which things are done is very critical. if you ; must change the code, do so carefully. ; 6/5/92 SWC Finished up the scaled battery info routines. ; 6/2/92 djw Added check for external video without a charger in ; NiagraRunIdleRoutineProc. If condition found, post alert to the ; user. Added PostUserNmMsg utility routine. ; 6/1/92 HJR Disable GDevice if we are powering down the device. ; 5/19/92 HJR Generalized IdleMindTable with RunIdleRoutines so that various ; miscellaneous routines can be localized. Add ; NiagraRunIdleRoutineProc which now check whether to power down ; screens. ; 5/15/92 SWC Added the battery and sound monitoring VBL tasks to the list of ; primitives for each machine, and moved SndWatch and ; SndWatchPonti here from PowerMgr.a. ; 5/8/92 SWC Fixed a copy/paste typo in the MSC table that could cause a ; crash. Removed CPUSpeedNiagraRead since it's no longer used. ; 5/7/92 HJR Added primitives for WakeScreenRefresh and necessary code for ; VSCPowerSelect. Rewrote CPUSpeedNiagra to that it matches ; hardware. Blank GSC during wakeup. ; 5/7/92 ag Added softshutdown to Dartanian timeout checks. ; 5/7/92 SWC Added primitives to return a scaled battery level so the Battery ; DA, etc., won't have to be rev'd each time we do a new portable. ; Added a check to ChkHDSpinDown to prevent the hard disk from ; being spun down if we're connected to a docking station (AC ; power is assumed). ; 11/19/92 SWC Exported the primitives tables so they can be used in ; UniversalTables.a. Changed ShutdownEqu.a->Shutdown.a. ; <1> 5/17/92 kc first checked in ; 5/2/92 kc Roll in Horror. Comments follow: ; 4/24/92 HJR Updated NiagraChkIdle for current state of hardware. Changed ; PRAM base in NiagraPrimInfo to $46 since slot E is now needed ; for VSC. ; 4/17/92 SWC Added a check to GetExtLevel to see if the charger is connected ; since the returned battery voltage will be zero if we're running ; without a battery (causing the system to go to sleep all the ; time). ; 4/13/92 SWC Modified GetExtLevel to actually check for the existance of a ; battery. Zeroed the MSC default battery warning level values ; since they'll change depending on what kind of battery is ; connected. The PMGR will determine what these should be each ; time a new battery type is installed. ; 4/8/92 SWC Fixed a bug in the clamshell switch check. ; 3/20/92 SWC Added calls to DockingSleep and DockingWakeup to DBLite's sleep ; and wakeup tables so everything will be kept up-to-date. ; 3/16/92 SWC Modified CPUSpeedMSC to support 16, 20, 25, and 33MHz machines. ; We're only planning to do 25 and 33, but if marketing wants to ; do the slower ones, at least the support's done for free. ; 3/13/92 SWC Fixed a couple of bugs in CPUSpeedMSC. ; 3/11/92 SWC Added an entry to the info tables for the address of the power ; cycling register. Added a routine entry for a clamshell switch ; monitoring VBL task. Added an IdleMind routine for DBLite to ; check if the clamshell has been closed. ;
3/6/92 SWC Added a routine to reset the internal SCC's channel B on wakeup ; since it seems to be getting into a weird state (this may go ; away later once I figure out what's happening, but it will help ; for now). ;
2/21/92 HJR Added Power cycling to the PrimsRec. Added WakeLvl hysteresis to ; the PrimInfos. ;

2/7/92 SWC Added default hysteresis and low/dead battery warning levels to ; the primitives info tables. ;

2/7/92 SWC Added support for determining battery level so we can handle ; different hardware implementations. Made the table offsets in ; PmgrPrimLookup self-relative. ;

2/4/92 SWC Added support for sleep and wakeup tables. Fixed a bug in ; CPUSpeedMSC. Added assembly conditionals (by decoder) just to be ; thorough. Cleaned up the code here and there. Added padding to ; make the code fixed-size. ;

2/3/92 HJR first checked in BLANKS ON STRING ASIS PRINT OFF LOAD 'StandardEqu.d' INCLUDE 'HardwarePrivateEqu.a' INCLUDE 'SoundPrivate.a' INCLUDE 'UniversalEqu.a' INCLUDE 'PowerPrivEqu.a' INCLUDE 'PowerMgrDispatchEqu.a' INCLUDE 'MMUEqu.a' INCLUDE 'IOPrimitiveEqu.a' INCLUDE 'DockingEqu.a' INCLUDE 'ShutDown.a' INCLUDE 'ROMEqu.a' INCLUDE 'Video.a' INCLUDE 'Notification.a' INCLUDE 'GestaltEqu.a' INCLUDE 'Processes.a' INCLUDE 'SlotMgrEqu.a' INCLUDE 'DepVideoEqu.a' INCLUDE 'Palettes.a' INCLUDE 'LayerEqu.a' INCLUDE 'ENETEqu.a' PRINT ON MACHINE MC68030 MC68881 PowerMgrPrimitives PROC EXPORT IF hasPwrControls THEN IMPORT AbsoluteBattery ; PowerMgr.a IMPORT BatWatch ; PowerMgr.a IMPORT DockingSleep ; DockingMgr.a IMPORT DockingWakeup ; DockingMgr.a IMPORT GracefulShutdown ; PowerMgr.a IMPORT PMGRrecv ; PowerMgr.a IMPORT PMGRsend ; PowerMgr.a IMPORT PrivateFeatures ; PowerMgr.a IMPORT SetSupervisorMode ; PowerMgr.a EXPORT GetLevel WITH PmgrRec,PowerDispRec,PmgrPramRec WITH PmgrPrimitivesRec,PmgrRoutineRec,PrimInfoTbleRec,IdleMindTblRec WITH pmCommandRec,nmRec,PmgrPramRec,ProcessInfoRec WITH DecoderInfo,DecoderKinds,ProductInfo,VideoInfo IF hasJAWS THEN ;••••••••••••••••••••••••••••••••••••••••• JAWS •••••••••••••••••••••••••••••••••••••••••• EXPORT JawsPmgrPrims ALIGN 4 DC.L PrimsTypeTable ; flags DC.L (JawsPmgrPrimsEnd-JawsPmgrPrims) ; size of table JawsPmgrPrims ; Table of Primitive Tables DC.L JawsRoutines-JawsPmgrPrims ; offset to table of Jaws routines DC.L JawsPrimInfo-JawsPmgrPrims ; offset to table decoder-specific constants DC.L JawsIdleMindTable-JawsPmgrPrims ; offset to table of Idlemind routines DC.L JawsSleepTable-JawsPmgrPrims ; offset to table of sleep routines DC.L JawsWakeTable-JawsPmgrPrims ; offset to table of wakeup routines DC.L 0 ; no PMgrOp exception table DC.L ModemTable-JawsPmgrPrims ; offset to table modem routines DC.L PwrDispatchVects-JawsPmgrPrims ; offset to power dispatch table DC.L 0 ; no PMgr hook table DC.L CommsPowerTable-JawsPmgrPrims ; offset to table of comms power routines DC.L PMgrOpTbl-JawsPmgrPrims ; offset to routine for PmgrOp DC.L 0 ; offset to backlight tables JawsPmgrPrimsEnd DC.L PrimsTypePtr ; flags DC.L (JawsRoutinesEnd-JawsRoutines) ; size of table JawsRoutines ; machine-specific routines DC.L PowerCycle030-JawsRoutines ; offset to routine for power cycling
DC.L Restore030-JawsRoutines ; offset to routine for power cycling restore
DC.L BatWatch-JawsRoutines ; offset to VBL task to check the battery level DC.L GetLevel-JawsRoutines ; offset to routine for determining battery level DC.L LeadScaledBatteryInfo-JawsRoutines ; offset to routine to return scaled battery level DC.L 0 ; no enviromental int DC.L 0 ; no clamshell DC.L CPUSpeedJaws-JawsRoutines ; offset to routine for determining CPU speed DC.L SndWatch-JawsRoutines ; offset to VBL task to check for sound usage DC.L RedrawScrn-JawsRoutines ; offset to routine for refreshing the screen DC.L LeadAbsoluteBatteryVoltage-JawsRoutines; routine to return absolute battery level DC.L 0 ; no routine to return info about battery times DC.L 0 ; dynamic CPU speed change is not supported JawsRoutinesEnd DC.L PrimsTypeInfo ; flags DC.L (JawsPrimInfoEnd-JawsPrimInfo) ; size of table JawsPrimInfo ; machine-specific constants: DC.B $70 ; PRAM base address DC.B DefHysteresis ; default hysteresis DC.B PMGRWARNLEVEL-STDOFFSET ; default low battery warning level
DC.B PMGRCUTOFF-STDOFFSET ; default dead battery warning level
DC.B PGMRWAKELEVEL-STDOFFSET ; hysteresis setting for pmgr wake level
DC.B (1-1) ; display shorted battery at second interrupt DC.B 0 ; no external video correction needed DC.B 0 ; no charger features DC.L $50FA0000 ; address of power cycling register DC.L 0 |\ ; bitmap of public Power Manager features (0< (0< DC.B 0 ; value for sleep register DC.B 0 ; padding for now DC.B 0 ; padding for now JawsPrimInfoEnd DC.L PrimsTypePtr ; flags DC.L (JawsIdleMindTableEnd-JawsIdleMindTable); size of table JawsIdleMindTable ; machine specific IdleMind Routines DC.L CheckCountDownTimer-JawsIdleMindTable ; offset to count down timer DC.L RunIdleRoutinesProc-JawsIdleMindTable ; offset to run idle procs DC.L ChkSleepTimeOut-JawsIdleMindTable ; offset to sleep time out DC.L ChkIdle-JawsIdleMindTable ; offset to check idle DC.L CalcProgressive-JawsIdleMindTable ; offset to calc progressive DC.L GoPowerCycle-JawsIdleMindTable ; offset to go power cycle JawsIdleMindTableEnd DC.L PrimsTypePtr ; flags DC.L (JawsSleepTableEnd-JawsSleepTable) ; size of table JawsSleepTable ; list of routines to execute when going to sleep: DC.L SleepHD-JawsSleepTable ; sleep the hard drive if currently running DC.L SaveVIA1-JawsSleepTable ; save VIA1 registers DC.L SaveASCBatman-JawsSleepTable ; save ASC/Batman registers DC.L SaveFPU-JawsSleepTable ; save FPU registers DC.L SaveVIA2-JawsSleepTable ; save VIA2 registers DC.L SendSleep-JawsSleepTable ; send a sleep command DC.L SaveSlp030-JawsSleepTable ; save MMU registers DC.L SaveSleepInfo-JawsSleepTable ; save sleep info in video RAM DC.L 0 ; (end of table) JawsSleepTableEnd DC.L PrimsTypePtr ; flags DC.L (JawsWakeTableEnd-JawsWakeTable) ; size of table JawsWakeTable ; list of routines to execute when waking up: DC.L RestoreSlp030-JawsWakeTable ; restore MMU, SPs, cache registers DC.L RestoreVIA2-JawsWakeTable ; restore VIA2 registers DC.L RestoreFPU-JawsWakeTable ; restore FPU registers DC.L RestoreASCBatman-JawsWakeTable ; restore ASC and Batman registers DC.L RestoreVIA1-JawsWakeTable ; restore VIA1 registers DC.L WakeClrInts-JawsWakeTable ; clear any pending PmgrInterrupts DC.L WakeSoundSetJaws-JawsWakeTable ; set state for sound out of sleep DC.L 0 ; (end of table) JawsWakeTableEnd ALIGN 4 ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: CPUSpeedJaws ; ; Inputs: none ; ; Outputs: D0 - [maximum CPU speed][current CPU speed] ; ; Trashes: A0 ; ; Function: returns the maximum and current (econo-mode or full) CPU speeds for ; JAWS-based machines ;———————————————————————————————————————————————————————————————————————————————————————— CPUSpeedJaws MOVEA.L UnivInfoPtr,A0 ; point to the ProductInfo table, ADDA.L DecoderInfoPtr(A0),A0 ; then to the DecoderInfo table, MOVEA.L JawsAddr(A0),A0 ; then to the JAWS base address, ADDA.L #JAWSGetCPUClock,A0 ; and finally to the CPU clock register BTST #0,(A0) ; are we a 25MHz or 16MHz machine? ADDA.L #JAWSEconoMode-JAWSGetCPUClock,A0 ; (point to the econo-mode register) BEQ.S @16MHz ; -> 16MHz MOVE.L #(CPUSpeed25MHz<<16)|\ ; assume we're running full-speed at 25MHz (CPUSpeed25MHz<<0),D0 BTST #1,(A0) ; are we in econo-mode? BEQ.S @NoEcono25 ; -> nope, we're done MOVE.W #CPUSpeed16MHz,D0 ; yes, we're currently running at 16MHz @NoEcono25 RTS @16MHz MOVE.L #(CPUSpeed16MHz<<16)|\ ; assume we're running full-speed at 16MHz (CPUSpeed16MHz<<0),D0 BTST #1,(A0) ; are we in econo-mode? BEQ.S @NoEcono16 ; -> nope, we're done MOVE.W #CPUSpeed8MHz,D0 ; yes, we're currently running at 8MHz @NoEcono16 RTS ENDIF ;———————————————————————————————————————————————————————————————————————————————————————— ; Wakeup routine for setting sound (A1 = return address) ;———————————————————————————————————————————————————————————————————————————————————————— WakeSoundSetNiagra WakeSoundSetJaws MOVEM.L A0-A1,-(SP) ; save special registers SUBQ.W #4,SP ; Create stack frame MOVE.L SP,A0 MOVE.B #sndClrLtch,(A0) ; Clear the Sound latch MOVEQ #1,D1 ; One long to send MOVE.W #SoundSet,D0 ; PMGR command, Set Sound Control MOVE.L A2,-(SP) BigJSR PMGRsend,A2 ; have the PMGR clear the sound latch MOVE.L (SP)+,A2 ADDQ.W #4,SP ; Remove stack frame With ExpandmemRec,SoundIOHeader MOVEA.L ([Expandmem],\ emSndPrimitives),A0 ; Get pointer to Expandmem MOVE.B DFAClast(A0),D0 ; get last byte sent to DFAC jsrTBL sndDFACSend,A0 ; send byte to DFAC Endwith MOVEM.L (SP)+,A0-A1 ; restore special registers JMP (A1) ; go home IF hasMSC THEN ;••••••••••••••••••••••••••••••••••••••••• MBT ••••••••••••••••••••••••••••••••••••••••••• EXPORT MBTPmgrPrims ALIGN 4 DC.L PrimsTypeTable ; flags DC.L (MBTPmgrPrimsEnd-MBTPmgrPrims) ; size of table MBTPmgrPrims ; Table of Primitives tables DC.L MBTRoutines-MBTPmgrPrims ; offset to table of MSC routines DC.L MBTPrimInfo-MBTPmgrPrims ; offset to decoder-specific constants DC.L MBTIdleMindTable-MBTPmgrPrims ; offset to table of Idlemind routines DC.L MBTSleepTable-MBTPmgrPrims ; offset to table of sleep routines DC.L MBTWakeTable-MBTPmgrPrims ; offset to table of wakeup routines DC.L MBTModemTable-MBTPmgrPrims ; offset to table of modem routines DC.L MBTPMgrOpExceptions-MBTPmgrPrims ; offset to PMgrOp exception table DC.L PwrDispatchVects-MBTPmgrPrims ; offset to table of PwrDispatchVects DC.L PMgrHookVects-MBTPmgrPrims ; offset to table of PmgrMgr Hooks DC.L 0 ; no comms power routines DC.L PMgrOpTbl-MBTPmgrPrims ; offset to routine for PmgrOp DC.L 0 ; offset to backlight tables MBTPmgrPrimsEnd DC.L PrimsTypePtr ; flags DC.L (MBTRoutinesEnd-MBTRoutines) ; size of table MBTRoutines ; machine-specific routines DC.L PowerCycle040-MBTRoutines ; offset to routine for power cycling
DC.L Restore040-MBTRoutines ; offset to routine for power cycling restore
DC.L BatWatch-MBTRoutines ; offset to VBL task to check the battery level DC.L GetExtLevel-MBTRoutines ; offset to routine for determining battery level DC.L MultiScaledBatteryInfo-MBTRoutines ; offset to routine to return scaled battery level DC.L 0 ; no environment interrupts DC.L ClamshellVBL-MBTRoutines ; offset to VBL task to check for clamshell closed DC.L CPUSpeedMSC-MBTRoutines ; offset to routine for determining CPU speed DC.L SndWatchMSC-MBTRoutines ; offset to VBL task to check for sound usage DC.L RedrawScrn-MBTRoutines ; offset to routine for refreshing the screen DC.L PGEAbsoluteBatteryVoltage-MBTRoutines ; routine to return absolute battery level DC.L 0 ; no routine to return info about battery times DC.L 0 ; dynamic CPU speed change is not supported MBTRoutinesEnd DC.L PrimsTypeInfo ; flags DC.L (MBTPrimInfoEnd-MBTPrimInfo) ; size of table MBTPrimInfo ; machine-specific constants: DC.B $46 ; PRAM base address DC.B DefHysteresis ; default hysteresis DC.B 0 ; default low battery warning level (none) DC.B 0 ; default dead battery warning level (none) DC.B 0 ; hysteresis setting for wake level (none) DC.B 0 ; shorted battery at first interrupt (none) DC.B 0 ; no external video correction needed DC.B 0 |\ ; bitmap of charger attributes (1< DC.L 0 |\ ; bitmap of Power Manager features (1< (1< DC.B 0 ; padding for now DC.L 0 ; no extended charging DC.B $5A ; value for power cycling register DC.B 0 ; value for sleep register DC.B 0 ; padding for now DC.B 0 ; padding for now MBTPrimInfoEnd DC.L PrimsTypePtr ; flags DC.L (MBTIdleMindTableEnd-MBTIdleMindTable); size of table MBTIdleMindTable ; machine specific IdleMind Routines DC.L CheckCountDownTimer-MBTIdleMindTable; offset to count down timer DC.L CheckClamshell-MBTIdleMindTable ; offset to clam shell DC.L ChkSleepTimeOut-MBTIdleMindTable ; offset to sleep time out DC.L ChkIdle-MBTIdleMindTable ; offset to check idle DC.L CalcProgressive-MBTIdleMindTable ; offset to calc progressive DC.L GoPowerCycle-MBTIdleMindTable ; offset to go power cycle MBTIdleMindTableEnd DC.L PrimsTypePtr ; flags DC.L (MBTSleepTableEnd-MBTSleepTable) ; size of table MBTSleepTable ; list of routines to execute when going to sleep: DC.L SleepHD-MBTSleepTable ; sleep the hard drive if currently running DC.L DockingSleep-MBTSleepTable ; save state of currently connected bar DC.L SaveVIA1-MBTSleepTable ; save VIA1 registers DC.L SaveASCBatman-MBTSleepTable ; save ASC/Batman registers DC.L SaveFPU-MBTSleepTable ; save FPU registers DC.L SaveLCD-MBTSleepTable ; save GSC (built-in LCD video) registers DC.L SaveMSC-MBTSleepTable ; save MSC registers DC.L SendSleepLP-MBTSleepTable ; send a sleep command DC.L SaveSlp040-MBTSleepTable ; save MMU registers DC.L SaveSleepInfo-MBTSleepTable ; save sleep info in Power Manager globals DC.L MSCKillPower-MBTSleepTable ; have MSC pull the plug as well DC.L 0 ; (end of table) MBTSleepTableEnd DC.L PrimsTypePtr ; flags DC.L (MBTWakeTableEnd-MBTWakeTable) ; size of table MBTWakeTable ; list of routines to execute when waking up: DC.L RestoreSlp040-MBTWakeTable ; restore MMU, SPs, cache registers DC.L RestoreMSC-MBTWakeTable ; restore volatile MSC registers DC.L RestoreLCD-MBTWakeTable ; restore GSC (built-in LCD video) registers DC.L RestoreFPU-MBTWakeTable ; restore FPU registers DC.L RestoreASCBatman-MBTWakeTable ; restore ASC and Batman registers DC.L RestoreVIA1-MBTWakeTable ; restore VIA1 registers DC.L MSCInitSCC-MBTWakeTable ; reset channel B on internal SCC
DC.L DockingWakeup-MBTWakeTable ; restore state of currently connected bar DC.L WakeClrInts-MBTWakeTable ; clear any pending PmgrInterrupts DC.L WakeSoundSet-MBTWakeTable ; set state for sound out of sleep DC.L 0 ; (end of table) MBTWakeTableEnd DC.L PrimsTypePMgrEx ; flags DC.L (MBTPMgrOpExceptionsEnd-MBTPMgrOpExceptions) ; size of table MBTPMgrOpExceptions ; PMgrOp exceptions: DC.B $FF,powerCntl ; $10 - power control DC.L MSCPowerControl-* DC.B $FF,powerRead ; $18 - power status DC.L MSCPowerStatus-* DC.B $FF,$1F ; $1F - fake power status (for MSC/PG&E) DC.L MSCFakePowerStatus-* DC.B $FE,batteryRead ; $68, $69 - read battery status DC.L MSCReadBattery-* DC.B $FF,soundSet ; $90 - clear sound latch, turn sound power on/off DC.L MSCSetSound-* DC.B $FF,soundRead ; $98 - return sound latch, sound power status DC.L MSCReadSound-* DC.W 0 ; (end of table) MBTPMgrOpExceptionsEnd DC.L PrimsTypePtr ; flags DC.L (MBTModemTableEnd-MBTModemTable) ; size of table MBTModemTable DC.L 0 ; offset to routine to turn on modem DC.L 0 ; offset to routine to turn off modem DC.L DBLiteModemType-MBTModemTable ; offset to routine to get modem type MBTModemTableEnd ALIGN 4 ;••••••••••••••••••••••••••••••••••••••••• MSC2 ••••••••••••••••••••••••••••••••••••••••••• EXPORT MSC2PmgrPrims ALIGN 4 DC.L PrimsTypeTable ; flags DC.L (MSC2PmgrPrimsEnd-MSC2PmgrPrims) ; size of table MSC2PmgrPrims ; Table of Primitives tables DC.L MSC2Routines-MSC2PmgrPrims ; offset to table of MSC routines DC.L MSC2PrimInfo-MSC2PmgrPrims ; offset to decoder-specific constants DC.L MSC2IdleMindTable-MSC2PmgrPrims ; offset to table of Idlemind routines DC.L MSC2SleepTable-MSC2PmgrPrims ; offset to table of sleep routines DC.L MSC2WakeTable-MSC2PmgrPrims ; offset to table of wakeup routines DC.L MSC2ModemTable-MSC2PmgrPrims ; offset to table of modem routines DC.L MSC2PMgrOpExceptions-MSC2PmgrPrims ; offset to PMgrOp exception table DC.L PwrDispatchVects-MSC2PmgrPrims ; offset to table of PwrDispatchVects DC.L PMgrHookVects-MSC2PmgrPrims ; offset to table of PmgrMgr Hooks DC.L 0 ; no comms power routines DC.L PMgrOpTbl-MSC2PmgrPrims ; offset to routine for PmgrOp DC.L 0 ; offset to backlight tables MSC2PmgrPrimsEnd DC.L PrimsTypePtr ; flags DC.L (MSC2RoutinesEnd-MSC2Routines) ; size of table MSC2Routines ; machine-specific routines DC.L PowerCycle040-MSC2Routines ; offset to routine for power cycling
DC.L Restore040-MSC2Routines ; offset to routine for power cycling restore
DC.L BatWatch-MSC2Routines ; offset to VBL task to check the battery level DC.L GetExtLevel-MSC2Routines ; offset to routine for determining battery level DC.L MultiScaledBatteryInfo-MSC2Routines ; offset to routine to return scaled battery level DC.L 0 ; no environment interrupts DC.L ClamshellVBL-MSC2Routines ; offset to VBL task to check for clamshell closed DC.L CPUSpeedMSC-MSC2Routines ; offset to routine for determining CPU speed DC.L SndWatchMSC-MSC2Routines ; offset to VBL task to check for sound usage DC.L RedrawScrn-MSC2Routines ; offset to routine for refreshing the screen DC.L PGEAbsoluteBatteryVoltage-MSC2Routines ; routine to return absolute battery level DC.L 0 ; no routine to return info about battery times DC.L 0 ; dynamic CPU speed change is not supported MSC2RoutinesEnd DC.L PrimsTypeInfo ; flags DC.L (MSC2PrimInfoEnd-MSC2PrimInfo) ; size of table MSC2PrimInfo ; machine-specific constants: DC.B $46 ; PRAM base address DC.B DefHysteresis ; default hysteresis DC.B 0 ; default low battery warning level (none) DC.B 0 ; default dead battery warning level (none) DC.B 0 ; hysteresis setting for wake level (none) DC.B 0 ; shorted battery at first interrupt (none) DC.B 0 ; no external video correction needed DC.B 0 |\ ; bitmap of charger attributes (1< DC.L 0 |\ ; bitmap of Power Manager features (1< (1< DC.B 0 ; padding for now DC.L 0 ; no extended charging DC.B $5A ; value for power cycling register DC.B 0 ; value for sleep register DC.B 0 ; padding for now DC.B 0 ; padding for now MSC2PrimInfoEnd DC.L PrimsTypePtr ; flags DC.L (MSC2IdleMindTableEnd-MSC2IdleMindTable); size of table MSC2IdleMindTable ; machine specific IdleMind Routines DC.L CheckCountDownTimer-MSC2IdleMindTable; offset to count down timer DC.L CheckClamshell-MSC2IdleMindTable ; offset to clam shell DC.L ChkSleepTimeOut-MSC2IdleMindTable ; offset to sleep time out DC.L ChkIdle-MSC2IdleMindTable ; offset to check idle DC.L CalcProgressive-MSC2IdleMindTable ; offset to calc progressive DC.L GoPowerCycle-MSC2IdleMindTable ; offset to go power cycle MSC2IdleMindTableEnd DC.L PrimsTypePtr ; flags DC.L (MSC2SleepTableEnd-MSC2SleepTable) ; size of table MSC2SleepTable ; list of routines to execute when going to sleep: DC.L SleepHD-MSC2SleepTable ; sleep the hard drive if currently running DC.L DockingSleep-MSC2SleepTable ; save state of currently connected bar DC.L SaveVIA1-MSC2SleepTable ; save VIA1 registers DC.L SaveASCBatman-MSC2SleepTable ; save ASC/Batman registers DC.L SaveFPU-MSC2SleepTable ; save FPU registers DC.L SaveLCD-MSC2SleepTable ; save GSC (built-in LCD video) registers DC.L SaveMSC-MSC2SleepTable ; save MSC registers DC.L SendSleepLP-MSC2SleepTable ; send a sleep command DC.L SaveSlp040-MSC2SleepTable ; save MMU registers DC.L SaveSleepInfo-MSC2SleepTable ; save sleep info in Power Manager globals DC.L MSCKillPower-MSC2SleepTable ; have MSC pull the plug as well DC.L 0 ; (end of table) MSC2SleepTableEnd DC.L PrimsTypePtr ; flags DC.L (MSC2WakeTableEnd-MSC2WakeTable) ; size of table MSC2WakeTable ; list of routines to execute when waking up: DC.L RestoreSlp040-MSC2WakeTable ; restore MMU, SPs, cache registers DC.L RestoreMSC-MSC2WakeTable ; restore volatile MSC registers DC.L RestoreLCD-MSC2WakeTable ; restore GSC (built-in LCD video) registers DC.L RestoreFPU-MSC2WakeTable ; restore FPU registers DC.L RestoreASCBatman-MSC2WakeTable ; restore ASC and Batman registers DC.L RestoreVIA1-MSC2WakeTable ; restore VIA1 registers DC.L MSCInitSCC-MSC2WakeTable ; reset channel B on internal SCC
DC.L DockingWakeup-MSC2WakeTable ; restore state of currently connected bar DC.L WakeClrInts-MSC2WakeTable ; clear any pending PmgrInterrupts DC.L WakeSoundSet-MSC2WakeTable ; set state for sound out of sleep DC.L 0 ; (end of table) MSC2WakeTableEnd DC.L PrimsTypePMgrEx ; flags DC.L (MSC2PMgrOpExceptionsEnd-MSC2PMgrOpExceptions) ; size of table MSC2PMgrOpExceptions ; PMgrOp exceptions: DC.B $FF,powerCntl ; $10 - power control DC.L MSCPowerControl-* DC.B $FF,powerRead ; $18 - power status DC.L MSCPowerStatus-* DC.B $FF,$1F ; $1F - fake power status (for MSC/PG&E) DC.L MSCFakePowerStatus-* DC.B $FE,batteryRead ; $68, $69 - read battery status DC.L MSCReadBattery-* DC.B $FF,soundSet ; $90 - clear sound latch, turn sound power on/off DC.L MSCSetSound-* DC.B $FF,soundRead ; $98 - return sound latch, sound power status DC.L MSCReadSound-* DC.W 0 ; (end of table) MSC2PMgrOpExceptionsEnd DC.L PrimsTypePtr ; flags DC.L (MSC2ModemTableEnd-MSC2ModemTable) ; size of table MSC2ModemTable DC.L 0 ; offset to routine to turn on modem DC.L 0 ; offset to routine to turn off modem DC.L DBLiteModemType-MSC2ModemTable ; offset to routine to get modem type MSC2ModemTableEnd ALIGN 4 ;••••••••••••••••••••••••••••••••••••••••• MSC ••••••••••••••••••••••••••••••••••••••••••• EXPORT MSCPmgrPrims ALIGN 4 DC.L PrimsTypeTable ; flags DC.L (MSCPmgrPrimsEnd-MSCPmgrPrims) ; size of table MSCPmgrPrims ; Table of Primitives tables DC.L MSCRoutines-MSCPmgrPrims ; offset to table of MSC routines DC.L MSCPrimInfo-MSCPmgrPrims ; offset to decoder-specific constants DC.L MSCIdleMindTable-MSCPmgrPrims ; offset to table of Idlemind routines DC.L MSCSleepTable-MSCPmgrPrims ; offset to table of sleep routines DC.L MSCWakeTable-MSCPmgrPrims ; offset to table of wakeup routines DC.L MSCModemTable-MSCPmgrPrims ; offset to table of modem routines DC.L MSCPMgrOpExceptions-MSCPmgrPrims ; offset to PMgrOp exception table DC.L PwrDispatchVects-MSCPmgrPrims ; offset to table of PwrDispatchVects DC.L PMgrHookVects-MSCPmgrPrims ; offset to table of PmgrMgr Hooks DC.L 0 ; no comms power routines DC.L PMgrOpTbl-MSCPmgrPrims ; offset to routine for PmgrOp DC.L 0 ; offset to backlight tables MSCPmgrPrimsEnd DC.L PrimsTypePtr ; flags DC.L (MSCRoutinesEnd-MSCRoutines) ; size of table MSCRoutines ; machine-specific routines DC.L PowerCycle030-MSCRoutines ; offset to routine for power cycling
DC.L Restore030-MSCRoutines ; offset to routine for power cycling restore
DC.L BatWatch-MSCRoutines ; offset to VBL task to check the battery level DC.L GetExtLevel-MSCRoutines ; offset to routine for determining battery level DC.L MultiScaledBatteryInfo-MSCRoutines ; offset to routine to return scaled battery level DC.L 0 ; no environment interrupts DC.L ClamshellVBL-MSCRoutines ; offset to VBL task to check for clamshell closed DC.L CPUSpeedMSC-MSCRoutines ; offset to routine for determining CPU speed DC.L SndWatchMSC-MSCRoutines ; offset to VBL task to check for sound usage DC.L RedrawScrn-MSCRoutines ; offset to routine for refreshing the screen DC.L PGEAbsoluteBatteryVoltage-MSCPmgrPrims ; routine to return absolute battery level DC.L 0 ; no routine to return info about battery times DC.L ChangeCPUSpeedMSC-MSCPmgrPrims ; routine to do a dynamic CPU speed change MSCRoutinesEnd DC.L PrimsTypeInfo ; flags DC.L (MSCPrimInfoEnd-MSCPrimInfo) ; size of table MSCPrimInfo ; machine-specific constants: DC.B $46 ; PRAM base address DC.B DefHysteresis ; default hysteresis DC.B 0 ; default low battery warning level (none) DC.B 0 ; default dead battery warning level (none) DC.B 0 ; hysteresis setting for wake level (none) DC.B 0 ; shorted battery at first interrupt (none) DC.B 0 ; no external video correction needed DC.B 0 ; no charger features DC.L $50FA0000 ; address of power cycling register DC.L 0 |\ ; bitmap of Power Manager features (1< (1< DC.B 0 ; padding for now DC.L 0 ; no extended charging DC.B $5A ; value for power cycling register DC.B 0 ; value for sleep register DC.B 0 ; padding for now DC.B 0 ; padding for now MSCPrimInfoEnd DC.L PrimsTypePtr ; flags DC.L (MSCIdleMindTableEnd-MSCIdleMindTable); size of table MSCIdleMindTable ; machine specific IdleMind Routines DC.L CheckCountDownTimer-MSCIdleMindTable; offset to count down timer DC.L CheckClamshell-MSCIdleMindTable ; offset to clam shell DC.L ChkSleepTimeOut-MSCIdleMindTable ; offset to sleep time out DC.L ChkIdle-MSCIdleMindTable ; offset to check idle DC.L CalcProgressive-MSCIdleMindTable ; offset to calc progressive DC.L GoPowerCycle-MSCIdleMindTable ; offset to go power cycle MSCIdleMindTableEnd DC.L PrimsTypePtr ; flags DC.L (MSCSleepTableEnd-MSCSleepTable) ; size of table MSCSleepTable ; list of routines to execute when going to sleep: DC.L SleepHD-MSCSleepTable ; sleep the hard drive if currently running DC.L DockingSleep-MSCSleepTable ; save state of currently connected bar DC.L SaveVIA1-MSCSleepTable ; save VIA1 registers DC.L SaveASCBatman-MSCSleepTable ; save ASC/Batman registers DC.L SaveFPU-MSCSleepTable ; save FPU registers DC.L SaveLCD-MSCSleepTable ; save GSC (built-in LCD video) registers DC.L SaveMSC-MSCSleepTable ; save MSC registers DC.L SendSleepLP-MSCSleepTable ; send a sleep command DC.L SaveSlp030-MSCSleepTable ; save MMU registers DC.L SaveSleepInfo-MSCSleepTable ; save sleep info in Power Manager globals DC.L MSCKillPower-MSCSleepTable ; have MSC pull the plug as well DC.L 0 ; (end of table) MSCSleepTableEnd DC.L PrimsTypePtr ; flags DC.L (MSCWakeTableEnd-MSCWakeTable) ; size of table MSCWakeTable ; list of routines to execute when waking up: DC.L RestoreSlp030-MSCWakeTable ; restore MMU, SPs, cache registers DC.L RestoreMSC-MSCWakeTable ; restore volatile MSC registers DC.L RestoreLCD-MSCWakeTable ; restore GSC (built-in LCD video) registers DC.L RestoreFPU-MSCWakeTable ; restore FPU registers DC.L RestoreASCBatman-MSCWakeTable ; restore ASC and Batman registers DC.L RestoreVIA1-MSCWakeTable ; restore VIA1 registers DC.L MSCInitSCC-MSCWakeTable ; reset channel B on internal SCC
DC.L DockingWakeup-MSCWakeTable ; restore state of currently connected bar DC.L WakeClrInts-MSCWakeTable ; clear any pending PmgrInterrupts DC.L WakeSoundSet-MSCWakeTable ; set state for sound out of sleep DC.L 0 ; (end of table) MSCWakeTableEnd DC.L PrimsTypePMgrEx ; flags DC.L (MSCPMgrOpExceptionsEnd-MSCPMgrOpExceptions) ; size of table MSCPMgrOpExceptions ; PMgrOp exceptions: DC.B $FF,powerCntl ; $10 - power control DC.L MSCPowerControl-* DC.B $FF,powerRead ; $18 - power status DC.L MSCPowerStatus-* DC.B $FF,$1F ; $1F - fake power status (for MSC/PG&E) DC.L MSCFakePowerStatus-* DC.B $FE,batteryRead ; $68, $69 - read battery status DC.L MSCReadBattery-* DC.B $FF,soundSet ; $90 - clear sound latch, turn sound power on/off DC.L MSCSetSound-* DC.B $FF,soundRead ; $98 - return sound latch, sound power status DC.L MSCReadSound-* DC.W 0 ; (end of table) MSCPMgrOpExceptionsEnd DC.L PrimsTypePtr ; flags DC.L (MSCModemTableEnd-MSCModemTable) ; size of table MSCModemTable DC.L 0 ; offset to routine to turn on modem DC.L 0 ; offset to routine to turn off modem DC.L DBLiteModemType-MSCModemTable ; offset to routine to get modem type MSCModemTableEnd ALIGN 4 ;_______________________________________________________________________________________ ; ; Routine: MSCReadBattery ; ; Inputs: A0 -- pointer to caller's parameter block ; ; Outputs: D0 -- result code ; CCR-- always BEQ ; ; Trashes: D1,A0-A1 ; ; Function: Emulates the read battery status ($68) and instantaneous battery status ($69) ; calls since they're not supported by PG&E. The contents of the receive buffer ; are mapped as follows: ; ; extended status ($6B) read status ($68 or $69) ; +0 flags (bits) flags (bits) ; 7: chargeable battery 7: 0 ; 6: "energy used" count valid 6: 0 ; 5: 0 5: charger state change ; 4: battery termperature valid 4: low battery ; 3: dead battery 3: dead battery (always 0) ; 2: battery connected 2: hi-charge counter overflow ; 1: hi-charge enabled 1: hi-charge enabled ; 0: charger installed 0: charger installed ; +1 voltage (H) 0-511 -> 7-21 volts power ; +2 voltage (L) ambient temperature ; +3 ambient temperature (°C) - ; +4 battery temperature (°C) - ; +5 power usage rate (* 66.9 = mW) - ; +6 energy used (H) - ; +7 energy used (L) - ;_______________________________________________________________________________________ MSCReadBattery MOVEA.L A0,A1 ; save the parameter block pointer SUBQ.W #8,SP ; make space for a buffer MOVE.L SP,-(SP) ; pmRBuffer MOVE.L (SP),-(SP) ; pmSBuffer CLR.W -(SP) ; pmLength = 0 (no bytes sent) MOVE.W #readExtBatt,-(SP) ; pmCommand = read extended battery status MOVE.L SP,A0 _PMgrOp ; get extended battery status LEA pmRBuffer+4(SP),SP ; (get rid of the parameter block) BNE.S @CantRead ; -> error MOVEA.L SP,A0 ; point to our reply buffer MOVE.W #3,pmLength(A1) ; stuff the reply count MOVEA.L pmRBuffer(A1),A1 ; point to the reply buffer MOVEQ #%00001011,D1 ; mask off the relevant flag bits AND.B (A0),D1 MOVE.B D1,(A1)+ ; and stuff them into the buffer MOVEQ #0,D1 ; assume no battery BTST #2,(A0)+ ; is there one? BEQ.S @NoBattery ; -> no, return zero volts MOVE.W (A0),D1 ; get the battery voltage LSR.W #1,D1 ; for now, just scale it into 0-255 @NoBattery MOVE.B D1,(A1)+ ADDQ.W #2,A0 ; (skip over voltage) MOVE.B (A0)+,(A1)+ ; •••for now, just copy the temperature across @CantRead ADDQ.W #8,SP ; get rid of the buffer MOVEQ #0,D1 ; set CCR for BEQ RTS ;_______________________________________________________________________________________ ; ; Routine: MSCPowerControl ; ; Inputs: A0 -- pointer to caller's parameter block ; ; Outputs: D0 -- result code ; CCR-- BEQ if the call was emulated, else BNE ; ; Trashes: D1-D2, A1 ; ; Function: Emulates all or part of the powerCntl call ($10) for systems where not all ; power planes are controlled by the PMGR. The send buffer will contain a ; byte with the following bits used: ; ; bit 0: 1=IWM power ; 1: 1=SCC power ; 2: 1=hard disk power ; 3: 1=internal modem power ; 4: 1=serial output driver power ; 5: 1=sound power ; 6: 1=minus 5 volt power ; 7: 1=turn on, 0=turn off devices corresponding to 1's in bits 0-6 ;_______________________________________________________________________________________ Unimplement EQU $A89F ; _Unimplemented trap MSCPowerControl MOVEA.L pmSBuffer(A0),A1 ; point to the transmit buffer MOVEQ #0,D1 MOVE.B (A1),D1 ; get the data byte MOVEM.L D1/A0,-(SP) MOVE.W #Unimplement,D0 ; does the _DockingDispatch trap exist? _GetTrapAddress ,NEWTOOL MOVEA.L A0,A1 MOVE.W @DockTrap,D0 _GetTrapAddress ,NEWTOOL CMPA.L A0,A1 BEQ.S @NoDocking ; -> no, just turn internal power planes on/off SUBQ.W #4,SP ; result PEA dockPowerControl ; docking selector = power control MOVE.L D1,-(SP) ; params = which power planes to affect @DockTrap _DockingDispatch ; call the handler ADDQ.W #4,SP ; toss the result @NoDocking MOVEM.L (SP)+,D1/A0 MOVEA.L VIA2,A1 ; point to the base of the MSC MOVEQ #(1< nope EOR.B D0,D1 ; turn them both off TST.B D1 ; turn power on or off? BMI.S @TurnOnSCC BCLR #MSCSCCClk,MSCClkCntl(A1) ; turn off SCC PCLK, RTXC BRA.S @NoSCC @TurnOnSCC BSET #MSCSCCClk,MSCClkCntl(A1) ; turn on SCC PCLK, RTXC @NoSCC BTST #pHD,D1 ; is hard disk power being turned on/off? BEQ.S @NoHD ; -> no TST.B D1 ; turn power on or off? BMI.S @TurnOnSCSI BCLR #MSCSCSIReset,MSCClkCntl(A1) ; drive SCSI chip reset low to stop clocks BRA.S @NoHD @TurnOnSCSI BSET #MSCSCSIReset,MSCClkCntl(A1) ; drive SCSI chip reset high to start it up @NoHD BCLR #pASC,D1 ; is sound power being turned on/off? BEQ.S @NoSound ; -> nope TST.B D1 ; turn power on or off? BMI.S @TurnOnSnd BCLR #MSCSndPower,MSCSndCntl(A1) ; turn off sound power BRA.S @NoSound @TurnOnSnd BSET #MSCSndPower,MSCSndCntl(A1) ; turn off sound power @NoSound BCLR #pIWM,D1 ; clear the SWIM power bit @NoSWIM MOVEQ #$7F,D0 ; return BNE if any bits are still set AND.B D1,D0 ; so we can pass it on to the PMGR BNE.S @MorePower ; -> more for the PMGR to do CLR.W pmLength(A0) ; fix the returned length since we're done @MorePower RTS ;_______________________________________________________________________________________ ; ; Routine: MSCPowerStatus ; ; Inputs: A0 -- pointer to caller's parameter block ; ; Outputs: D0 -- result code ; CCR-- BEQ if the call was emulated, else BNE ; ; Trashes: D1-D2, A0-A1 ; ; Function: Emulates all or part of the powerRead call ($18) for systems where not all ; power planes are controlled by the PMGR. On exit, the receive buffer will ; contain a byte with the following bits used: ; ; bit 0: 1=IWM power ; 1: 1=SCC power ; 2: 1=hard disk power ; 3: 1=internal modem power ; 4: 1=serial output driver power ; 5: 1=sound power ; 6: 1=minus 5 volt power ; 7: 1=turn on, 0=turn off devices corresponding to 1's in bits 0-6 ;_______________________________________________________________________________________ MSCPowerStatus MOVEQ #0,D1 MOVE.L A0,-(SP) MOVE.W #Unimplement,D0 ; does the _DockingDispatch trap exist? _GetTrapAddress ,NEWTOOL MOVEA.L A0,A1 MOVE.W @DockTrap,D0 _GetTrapAddress ,NEWTOOL CMPA.L A0,A1 BEQ.S @NoDocking ; -> no, just turn internal power planes on/off SUBQ.W #4,SP ; result PEA dockPowerStatus ; docking selector = power status CLR.L -(SP) ; params = nil @DockTrap _DockingDispatch ; call the handler ADDQ.W #4,SP ; toss the result @NoDocking MOVEM.L (SP)+,D1/A0 MOVEA.L VIA2,A1 ; point to the base of the MSC MOVEQ #(1< nope EOR.B D0,D1 ; turn them both off TST.B D1 ; turn power on or off? BMI.S @TurnOnSCC BCLR #MSCSCCClk,MSCClkCntl(A1) ; turn off SCC PCLK, RTXC BRA.S @NoSCC @TurnOnSCC BSET #MSCSCCClk,MSCClkCntl(A1) ; turn on SCC PCLK, RTXC @NoSCC BTST #pHD,D1 ; is hard disk power being turned on/off? BEQ.S @NoHD ; -> no TST.B D1 ; turn power on or off? BMI.S @TurnOnSCSI BCLR #MSCSCSIReset,MSCClkCntl(A1) ; drive SCSI chip reset low to stop clocks BRA.S @NoHD @TurnOnSCSI BSET #MSCSCSIReset,MSCClkCntl(A1) ; drive SCSI chip reset high to start it up @NoHD BCLR #pASC,D1 ; is sound power being turned on/off? BEQ.S @NoSound ; -> nope TST.B D1 ; turn power on or off? BMI.S @TurnOnSnd BCLR #MSCSndPower,MSCSndCntl(A1) ; turn off sound power BRA.S @NoSound @TurnOnSnd BSET #MSCSndPower,MSCSndCntl(A1) ; turn off sound power @NoSound BCLR #pIWM,D1 ; clear the SWIM power bit @NoSWIM MOVEQ #$7F,D0 ; return BNE if any bits are still set AND.B D1,D0 ; so we can pass it on to the PMGR BNE.S @MorePower ; -> more for the PMGR to do CLR.W pmLength(A0) ; fix the returned length since we're done @MorePower RTS ;_______________________________________________________________________________________ ; ; Routine: MSCFakePowerStatus ; ; Inputs: A0 -- pointer to caller's parameter block ; ; Outputs: CCR-- always BNE ; ; Trashes: none ; ; Function: This is a skanky way of letting us call the Power Manager and getting control ; back to do further processing. This converts the powerRead+1 command sent ; by the PowerStatus routine above into a powerRead command so it can get the ; status PG&E knows about before filling in the rest of the bits that are now ; handled by the MSC. What can I say? The price of extensibility? ;_______________________________________________________________________________________ MSCFakePowerStatus SUBQ.W #$1F-powerRead,pmCommand(A0) ; convert this to a powerRead command ($18) RTS ; get here with BNE to send the command to the PMGR ;_______________________________________________________________________________________ ; ; Routine: MSCSetSound ; ; Inputs: A0 -- pointer to caller's parameter block ; ; Outputs: D0 -- result code ; CCR-- BEQ if the call was emulated, else BNE ; ; Trashes: D1,A1 ; ; Function: Emulates the soundSet call ($90) for systems that don't control sound ; power and latch with the PMGR micro. The send buffer will contain a ; byte with the following bits used: ; ; bit 0: 1=sound power enabled ; 1: 1=clear sound latch ;_______________________________________________________________________________________ MSCSetSound CLR.W pmLength(A0) ; no bytes returned MOVEA.L pmSBuffer(A0),A1 ; point to the transmit buffer MOVEQ #3,D1 ; mask off valid bits AND.B (A1),D1 MOVEA.L VIA2,A1 ; get the MSC's base address MOVE.B @MSCSound(D1),MSCSndCntl(A1) ; stuff the right value into the sound control register MOVEQ #0,D0 ; return "no error" RTS ; on MSC, the sound latch is cleared simply by accessing the sound control register @MSCSound DC.B (0< CMPI.W #CPUSpeed16MHz,D0 ; is this a 16MHz machine? BLE.S @NoMSCEcono ; -> yes, econo-mode isn't supported ADDA.L DecoderInfoPtr(A0),A0 ; point to our decoder table MOVEA.L RBVAddr(A0),A0 ; then to the base of the MSC BTST #MSCEconoBit,MSCConfig(A0) ; are we in econo-mode? BEQ.S @NoMSCEcono ; -> nope MOVE.W #CPUSpeed16MHz,D0 ; current speed is 16MHz @NoMSCEcono RTS @speedTable DC.W CPUSpeed33MHz, CPUSpeed33MHz ; ID=0 (boxYeager) DC.W $484A , $5221 ; ID=1 (reserved HJR!) DC.W CPUSpeed33MHz, CPUSpeed33MHz ; ID=2 (boxEscher) DC.W CPUSpeed20MHz, CPUSpeed20MHz ; ID=3 (reserved-wasPenLite) DC.W CPUSpeed25MHz, CPUSpeed25MHz ; ID=4 (boxDuo210) DC.W CPUSpeed33MHz, CPUSpeed33MHz ; ID=5 (boxDuo230) DC.W CPUSpeed16MHz, CPUSpeed16MHz ; ID=6 (boxDBLite16) DC.W CPUSpeed16MHz, CPUSpeed16MHz ; ID=7 (reserved 16MHz) ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: ChangeCPUSpeedMSC ; ; Inputs: D1 - ≠0: switch to reduced speed, =0: switch to full speed ; ; Outputs: D0 - =1: speed was changed, =0: speed was not changed ; ; Trashes: D1, A0 ; ; Function: switches the CPU speed based on the passed parameter, and returns 1 if the ; speed change was actually done ;———————————————————————————————————————————————————————————————————————————————————————— ChangeCPUSpeedMSC MOVEA.L VIA2,A1 ; point to the MSC's configuration register LEA MSCConfig(A1),A1 MOVEQ #~(1< no MOVE.B D1,(A1) ; yes, write out the new setting MOVEQ #CPUSpeedDisp,D0 ; get the current CPU speed _PowerDispatch BCLR #MSC25MHz,(A1) ; assume 33MHz CMPI.W #CPUSpeed25MHz,D0 ; are we running at 25MHz or less? BGT.S @SpeedChanged ; -> no, we're done BSET #MSC25MHz,(A1) ; yes, optimize the state machines for slower speed @SpeedChanged MOVEQ #1,D0 RTS @NoChange MOVEQ #0,D0 RTS ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: ClamshellVBL ; ; Inputs: A0 - pointer to VBL task queue element ; ; Outputs: none ; ; Trashes: D0,A0,A1 ; ; Function: checks to see if the clamshell is closed, and if so, sets a flag so we can ; handle it at a _IdleMind time ;———————————————————————————————————————————————————————————————————————————————————————— ClamshellVBL MOVE.W #ClamshellVBLFreq,vblCount(A0) ; reset the counter MOVEA.L PMgrBase,A1 ; point to Power Manager variables BTST #ignoreClamshell,PmgrFlags3(A1) ; what do we want to do? BNE.S @InStation ; -> nothing BTST #dockingStation,dockFlags(A1) ; are we in a docking station? BNE.S @InStation ; -> yes, clamshell closed is OK BTST #ClamshellClosed,PmgrFlags(A1) ; have we already detected the close? BNE.S @Done ; -> yes, we're done this time CLR.W -(SP) MOVE.L SP,-(SP) ; pmRBuffer MOVE.L (SP),-(SP) ; pmSBuffer CLR.W -(SP) ; pmLength = 0 MOVE.W #readExtSwitches,-(SP) ; pmCommand MOVEA.L SP,A0 ; point to the parameter block _PMgrOp ; get the clamshell info LEA pmRBuffer+4(SP),SP ; toss the parameter block BTST #clamshell,(SP)+ ; is the clamshell closed? BEQ.S @Done ; -> nope, all done BSET #ClamshellClosed,PmgrFlags(A1) ; flag that the clamshell is closed @Done RTS @InStation BCLR #ClamshellClosed,PmgrFlags(A1) ; flag that the clamshell is open RTS ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: CheckClamshell (IdleMind) ; ; Inputs: A2 - pointer to Power Manager variables ; ; Outputs: none ; ; Trashes: D0, A0 ; ; Function: does the appropriate thing if the clamshell was closed (sleep or nothing), ; and then runs the "standard" countdown checking code ;———————————————————————————————————————————————————————————————————————————————————————— CheckClamshell BTST #InSleep,PmgrFlags(A2) ; IF !InSleep THEN BNE @Exit ; CLR.W -(SP) ; Setup PB for PmgrCommand MOVE.L SP,-(SP) ; pmRBuffer MOVE.L (SP),-(SP) ; pmSBuffer CLR.W -(SP) ; pmLength = 0 MOVE.W #readExtSwitches,-(SP) ; pmCommand = ReadSwitch MOVEA.L SP,A0 ; point to the parameter block _PMgrOp ; get the clamshell info LEA pmRBuffer+4(SP),SP ; toss the parameter block BTST #clamshell,(SP)+ ; IF ClamShell Open THEN BNE.S @doClamshellClosed ; BCLR.B #ClamshellClosed,PmgrFlags(A2) ; flag that the clamshell is open BRA RunIdleRoutinesProc ; ELSE @doClamshellClosed BTST.B #ignoreClamshell,PmgrFlags3(A2) ; IF ignore Clamshell THEN BNE.S @Exit ; bail BTST.B #dockingStation,dockFlags(A2) ; ELSE IF in a dockingstation THEN BNE.S @Exit ; bail BSR CurFrontProcEqual ; ELSE IF Processes <> THEN BEQ.S @Exit ; bail BSET.B #ClamshellClosed,PmgrFlags(A2) ; ELSE IF Clamshell = closed THEN BNE.S @Exit ; bail MOVE.B PmgrFlags(A2),-(SP) ; ELSE save PmgrFlags BSET #AvoidNetDiag,PmgrFlags(A2) ; and set the ‘no AppleTalk dialogs’ flag MOVEQ #SleepNow,D0 ; force a sleep _Sleep BTST #AvoidNetDiag,(SP)+ ; was the ‘no AppleTalk dialogs’ flag set before? BEQ.S @NoDialog ; -> no, leave it alone BSET #AvoidNetDiag,PmgrFlags(A2) ; yes, restore it @NoDialog MOVE.L Ticks,LastAct(A2) ; waking updates last activity @Exit BRA RunIdleRoutinesProc ; ENDIF ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: SndWatchMSC ; ; Inputs: A0 - pointer to VBL task queue element ; ; Outputs: none ; ; Trashes: D0, A0, A2 ; ; Function: A VBL task called once every ten seconds that checks the sound latch for ; ASC new use. If so then sound power is turned on. If power is already on ; then then the latch is cleared. If the latch stays clear for two VBLs then ; sound power is turned off. ;———————————————————————————————————————————————————————————————————————————————————————— SndWatchMSC MOVE.W #SndWFreq,vblCount(A0) ; reset the counter MOVEA.L VIA2,A0 ; point to the MSC's sound control register LEA MSCSndCntl(A0),A0 BTST #MSCSndBusy,(A0) ; was sound used in the last 10 sec BNE.S @SoundOn ; -> yes MOVEA.L PMgrBase,A2 ; no, sound's been on but now it's not TST.B SysTaskFlag(A2) ; are the sound input primitives set up? BEQ.S @NoSoundAct ; -> no, there can't be a sound input source selected jsrTBL sndInputSource ; is a sound source selected? TST.B D0 BNE.S @NoSound ; -> yes, sound input is active so don't power off sound @NoSoundAct BCLR #MSCSndPower,(A0) ; turn off sound power RTS @SoundOn BSET #MSCSndPower,(A0) ; turn on sound power (latch is cleared by any register access) _IdleUpdateDispatch @NoSound RTS ;———————————————————————————————————————————————————————————————————————————————————————— ; _Sleep routine to save the volatile MSC chip registers (A1 = return address) ;———————————————————————————————————————————————————————————————————————————————————————— SaveMSC MOVEA.L VIA2,A2 ; point to the base of the MSC MOVEQ #$80-256,D0 ; save slot IER with bit 7 set so interrupts OR.B MSCSlotIER(A2),D0 ; will be re-enabled upon wakeup MOVE.B D0,-(SP) MOVEQ #$80-256,D0 ; do the same with VIA2 IER OR.B MSCVIA2IER(A2),D0 MOVE.B D0,-(SP) BSET #MSCIOClk,MSCClkCntl(A2) ; make sure IOClk is on in case a bar's attached MOVE.B MSCClkCntl(A2),-(SP) ; save the clock control register JMP (A1) ; and return ;———————————————————————————————————————————————————————————————————————————————————————— ; _Sleep routine to have MSC turn off system power (A1 = return address) ;———————————————————————————————————————————————————————————————————————————————————————— MSCKillPower MOVEA.L VIA2,A2 ; point to the power cycle/sleep register ADDA.L #MSCPowerCycle,A2 MOVEQ #-1,D0 MOVE.L D0,(A2) ; write -1 to the register to kill the power JMP (A1) ; we might optimistically get here ;———————————————————————————————————————————————————————————————————————————————————————— ; Wakeup routine to restore the volatile MSC chip registers (A1 = return address) ;———————————————————————————————————————————————————————————————————————————————————————— RestoreMSC MOVEA.L VIA2,A2 ; point to the base of the MSC MOVE.B (SP)+,MSCClkCntl(A2) ; restore volatile registers MOVE.B (SP)+,MSCVIA2IER(A2) MOVE.B (SP)+,MSCSlotIER(A2) JMP (A1) ; and return ;————————————————————————————————————————————————————————————————————————————————————————
; Wakeup routine to put the SCC into a known state (A1 = return address) ;———————————————————————————————————————————————————————————————————————————————————————— MSCInitSCC MOVEA.L VIA2,A2 ; turn on the internal SCC clock BSET #MSCSCCClk,MSCClkCntl(A2) MOVEA.L UnivInfoPtr,A2 ; point to the ProductInfo table ADDA.L DecoderInfoPtr(A2),A2 ; then to the DecoderInfo table MOVEM.L SCCRdAddr(A2),A2-A3 ; get the SCC base read/write addresses (channel B) MOVEA.L VIA,A4 ; point to the VIA for delays MOVE.B (A2),D0 ; do a read to make sure the SCC is synched up TST.B (A4) ; delay a bit MOVE.B #9,(A3) ; select register 9 TST.B (A4) ; delay some more MOVE.B #$40,(A3) ; reset channel B TST.B (A4) ; delay a little more MOVEA.L VIA2,A2 ; turn off the internal SCC clock BCLR #MSCSCCClk,MSCClkCntl(A2) JMP (A1) ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: DBLiteModemType ; ; Input: ; ; Outputs: d0.l has the modem Type ; ; Trashes: d0 ; ; Function: Returns type of modem installed in DBLite Modem ; ;———————————————————————————————————————————————————————————————————————————————————————— DBLiteModemType moveq.l #ModemTypeDUO,d0 ; dbmodem rts ENDIF ; {hasMSC} IF hasNiagra THEN ;•••••••••••••••••••••••••••••••••••••••• Niagra ••••••••••••••••••••••••••••••••••••••••• EXPORT NiagraPmgrPrims ALIGN 4 DC.L PrimsTypeTable ; padding, for now DC.L (NiagraPmgrPrimsEnd-NiagraPmgrPrims); size of table NiagraPmgrPrims ; Table of Niagra Primitives Tables DC.L NiagraRoutines-NiagraPmgrPrims ; offset to table of NiagraRoutines DC.L NiagraPrimInfo-NiagraPmgrPrims ; offset to decoder-specific constants DC.L NiagraIdleMindTable-NiagraPmgrPrims ; offset to table of Idlemind routines DC.L NiagraSleepTable-NiagraPmgrPrims ; offset to table of sleep routines DC.L NiagraWakeTable-NiagraPmgrPrims ; offset to table of wakeup routines DC.L NiagraPMgrOpExceptions-NiagraPmgrPrims; offset to PMgrOp exception table DC.L NiagraModemTable-NiagraPmgrPrims ; offset to Modem Table DC.L PwrDispatchVects-NiagraPmgrPrims ; offset to power dispatch table DC.L PMgrHookVects-NiagraPmgrPrims ; offset to PMgr hook table DC.L CommsPowerTable-NiagraPmgrPrims ; offset to table of comms power routines DC.L PMgrOpTbl-NiagraPmgrPrims ; offset to routine for PmgrOp DC.L 0 ; offset to backlight tables NiagraPmgrPrimsEnd DC.L PrimsTypePtr ; flags DC.L (NiagraRoutinesEnd-NiagraRoutines) ; size of table NiagraRoutines ; Table of Niagra Specific Routines DC.L PowerCycle030-NiagraRoutines ; offset to routine for power cycling down
DC.L Restore030-NiagraRoutines ; offset to routine for power cycling restore
DC.L BatWatch-NiagraRoutines ; offset to VBL task to check the battery level DC.L GetLevel-NiagraRoutines ; offset to routine for determining battery level DC.L LeadScaledBatteryInfo-NiagraRoutines; offset to routine to return scaled battery level DC.L NiagraEnvInt-NiagraRoutines ; offset to environment interrupt handler DC.L 0 ; no clamshellvbl DC.L CPUSpeedNiagra-NiagraRoutines ; offset to routine for determining CPU speed DC.L SndWatchPonti-NiagraRoutines ; offset to VBL task to check for sound usage DC.L RedrawScrn-NiagraRoutines ; offset to routine for refreshing the screen DC.L LeadAbsoluteBatteryVoltage-NiagraPmgrPrims; routine to return absolute battery level DC.L 0 ; no routine to return info about battery times DC.L 0 ; dynamic CPU speed change is not supported NiagraRoutinesEnd DC.L PrimsTypeInfo ; flags DC.L (NiagraPrimInfoEnd-NiagraPrimInfo) ; size of table NiagraPrimInfo ; machine-specific constants: DC.B $46 ; PRAM base address DC.B DefHysteresis ; default hysteresis DC.B PMGRWARNLEVEL-STDOFFSET ; default low battery warning level
DC.B PMGRCUTOFF-STDOFFSET ; default dead battery warning level
DC.B PGMRWAKELEVEL-STDOFFSET ; hysteresis setting for pmgr wake level
DC.B (2-1) ; display shorted battery at second interrupt DC.B 10 ; add 100mv to low bat warn if external video DC.B 0 ; no charger features DC.L $50FA0000 ; address of power cycling register DC.L 0 |\ ; bitmap of Power Manager features (0< (0< DC.B 0 ; value for sleep register DC.B 0 ; padding for now DC.B 0 ; padding for now NiagraPrimInfoEnd DC.L PrimsTypePtr ; flags DC.L (NiagraIdleMindTableEnd-NiagraIdleMindTable) ; size of table NiagraIdleMindTable ; Machine specific Idlemind routines DC.L CheckCountDownTimer-NiagraIdleMindTable ; offset to countdown timer DC.L NiagraRunIdleRoutinesProc-NiagraIdleMindTable ; offset to run idle procs DC.L ChkSleepTimeOut-NiagraIdleMindTable ; offset to check sleep DC.L ChkIdle-NiagraIdleMindTable ; offset to check idle DC.L CalcProgressive-NiagraIdleMindTable ; offset to calc progressive DC.L GoPowerCycle-NiagraIdleMindTable ; offset to power cycle NiagraIdleMindTableEnd DC.L PrimsTypePtr ; flags DC.L (NiagraSleepTableEnd-NiagraSleepTable) ; size of table NiagraSleepTable ; list of routines to execute when going to sleep: DC.L SleepHD-NiagraSleepTable ; sleep the hard drive if currently running DC.L SaveVIA1-NiagraSleepTable ; save VIA1 registers DC.L SaveASCBatmanDART-NiagraSleepTable ; save ASC/Batman registers DC.L SoundPowerDownNiagra-NiagraSleepTable; power off sound DC.L SaveFPU-NiagraSleepTable ; save FPU registers DC.L SaveLCD-NiagraSleepTable ; save built-in LCD video registers DC.L SaveVIA2-NiagraSleepTable ; save VIA2 registers DC.L SendSleep-NiagraSleepTable ; send a sleep command DC.L SaveSlp030-NiagraSleepTable ; save MMU registers DC.L SaveSleepInfo-NiagraSleepTable ; save sleep info in video RAM DC.L 0 ; (end of table) NiagraSleepTableEnd DC.L PrimsTypePtr ; flags DC.L (NiagraWakeTableEnd-NiagraWakeTable) ; size of table NiagraWakeTable ; list of routines to execute when waking up: DC.L RestoreSlp030-NiagraWakeTable ; restore MMU, SPs, cache registers DC.L RestoreVIA2-NiagraWakeTable ; restore VIA2 registers DC.L RestoreLCD-NiagraWakeTable ; restore GSC (built-in LCD video) registers DC.L RestoreFPU-NiagraWakeTable ; restore FPU registers DC.L RestoreASCBatman-NiagraWakeTable ; restore ASC and Batman registers DC.L RestoreDartAmp-NiagraWakeTable ; turn on amps DC.L RestoreVIA1-NiagraWakeTable ; restore VIA1 registers DC.L RestorePmgrMisc-NiagraWakeTable ; restore Misc PmgrVars DC.L DockingWakeup-NiagraWakeTable ; restore state of currently connected bar DC.L WakeClrInts-NiagraWakeTable ; clear any pending PmgrInterrupts DC.L WakeSoundSetNiagra-NiagraWakeTable ; set state for sound out of sleep DC.L 0 ; (end of table) NiagraWakeTableEnd DC.L PrimsTypePMgrEx ; flags DC.L (NiagraPMgrOpExceptionsEnd-NiagraPMgrOpExceptions) ; size of table NiagraPMgrOpExceptions ; PMgrOp exceptions: DC.B $F0,$50 ; $5x - modem commands DC.L DartModemCmds-* ; DC.B $F7,$71 ; $71,$79 - modem commands DC.L DartSPI-* ; DC.B $F0,$A0 ; $Ax - modem commands DC.L DartSPI-* ; DC.W 0 ; (end of table) NiagraPMgrOpExceptionsEnd DC.L PrimsTypePtr ; flags DC.L (NiagraModemTableEnd-NiagraModemTable) ; size of table NiagraModemTable DC.L DartModemOn-NiagraModemTable ; offset to routine to turn on modem DC.L DartModemOff-NiagraModemTable ; offset to routine to turn off modem DC.L DartModemType-NiagraModemTable ; offset to routine to get modem type NiagraModemTableEnd ALIGN 4 ;———————————————————————————————————————————————————————————————————————————————— ; Routine: DartModemCmds ; ; Input: A0 - points the pmgrop PB ; ; Destroys: d1 ; ; Called by: Bra from NewPmgrOp ; ; Function: filter the modem $5X commands, $50 should still go the power manager ;———————————————————————————————————————————————————————————————————————————————— DartModemCmds cmp.w #modemSet,pmCommand(A0) bne.s DartSPI ; Handle call through SPI moveq #1,d1 ; set CC to not handled here rts ; return ;———————————————————————————————————————————————————————————————————————————————— ; Routine: DartSPI ; ; Input: A0 - points the pmgrop PB ; ; Destroys: d1 ; ; Called by: Bra from NewPmgrOp ; ; Function: transfer data through new SPI port ;———————————————————————————————————————————————————————————————————————————————— DartSPI @savedRegs REG D1-D4/A0-A3 ; ----- dynamic test for SPI ---- movea.l PMgrBase,a3 ; point to the Power Manager's variables btst.b #PmgrDartSPI,PmgrFlags1(a3) ; test if SPI modem installed bne.s @SPIStart ; Handle call through SPI moveq #1,d1 ; set CC to not handled here rts ; return @SPIStart movem.l @savedRegs,-(sp) MOVEA.L UnivInfoPtr,a2 ; point to the ProductInfo table, ADDA.L DecoderInfoPtr(a2),a2 ; then to the DecoderInfo table, MOVEA.L JAWSAddr(a2),a2 ; then to the Niagra base address, ADDA.L #NiagraGUR,a2 ; point to the base of the GUR space ; ----- send command and count ---- move.w pmCommand(a0),d3 move.w d3,d1 bsr SendSPI ; send command byte bne.s @PMgrOpExit ; exit if error returned MOVEA.L PMgrBase,A1 ; point to the Power Manager's variables MOVEA.L vSendCountTbl(A1),A1 ; and get the send count table move.w pmLength(a0),d2 ; pop the count into d2 tst.b (a1,d3) bpl.s @noCount ; if positive, no count move.w d2,d1 bsr SendSPI ; send count byte bne.s @PMgrOpExit ; exit if error returned ; ----- send data ---- @noCount movea.l pmSBuffer(a0),a3 ; get the pointer to the command's data bytes moveq #0,d1 ; (set CCR for BEQ so DBNE below won't fall thru) bra.s @StartSend @SendData MOVE.B (a3)+,D1 ; get the next data byte BSR SendSPI ; and send it @StartSend DBNE d2,@SendData ; -> more bytes to send BNE.S @PMgrOpExit ; -> error ; ----- receive data ----- MOVEA.L PMgrBase,A1 ; point to the Power Manager's variables MOVEA.L vRecvCountTbl(A1),A1 ; and get the receive count table clr.l d4 ; clear count register move.b (a1,d3),d4 ; initialize to count bmi.s @readReplyCount ; (<0) cmp.b #1,d4 ; test against 1 ble.s @readData ; if ( =0 or =1 ) go to read subq #1,d4 ; (>1) correct count bra.s @readData ; if 0 or 1 go to read @readReplyCount bsr ReceiveSPI ; read first byte for receive count bne.s @PMgrOpExit ; exit if error returned move.w d1,d4 ; move count into d4 @readData ; d4 has receive byte count movea.l pmRBuffer(a0),a3 ; a3 new points move.w d4,pmLength(a0) ; bra.s @StartReceive ; start receiving data @ReceiveByte bsr.s ReceiveSPI ; read a byte into d1 bne.s @PMgrOpExit ; -> error move.b d1,(a3)+ ; move data byte into buffer @StartReceive dbra d4,@ReceiveByte ; -> more bytes to send @PMgrOpExit moveq #0,d1 ; indicate we handled call @exit movem.l (sp)+,@savedRegs ; restore working registers rts ;———————————————————————————————————————————————————————————————————————————————— ; Routine: WaitSPIAckHi ; ; Input: a2.l - pointer to GUR space ; ; Returns CCR - BNE if ACK went hi, BEQ if timed out ; ; Function: wait for SPI ack high ;———————————————————————————————————————————————————————————————————————————————— WaitSPIAckHi @workRegs reg D1-d2 movem.l @workRegs,-(sp) ; save working set moveq #32,d2 ; loop to max 32 msec @nextmsec move.w timedbra,d1 ; 1 msec count @waitAckhi btst.b #PontiSPIAck,PontiSPIMdmCtl(a2) ; test ack dbne d1,@waitAckhi ; loop for upto 1 msec dbne d2,@nextmsec ; loop for d2 msec movem.l (sp)+,@workRegs rts ;———————————————————————————————————————————————————————————————————————————————— ; Routine: WaitSPIAckLo ; ; Input: a2 - pointer to GUR space ; ; Returns CCR - BEQ if ACK went lo, BNE if timed out ; ; Function: wait for SPI ack lo ;———————————————————————————————————————————————————————————————————————————————— WaitSPIAckLo @workRegs reg D1-d2 movem.l @workRegs,-(sp) ; save working set moveq #32,d2 ; loop to max 32 msec @nextmsec move.w timedbra,d1 ; 1 msec count @waitAcklo btst.b #PontiSPIAck,PontiSPIMdmCtl(a2) ; test ack dbeq d1,@waitAcklo ; loop for upto 1 msec dbeq d2,@nextmsec ; loop for d2 msec movem.l (sp)+,@workRegs rts ;———————————————————————————————————————————————————————————————————————————————— ; Routine: SendSPI ; ; Input: a2.l - pointer to GUR space ; d1.b - byte to send ; ; Returns d0.w - 0 = ok, non-zero = error ; ; Function: send a byte thru the SPI ;———————————————————————————————————————————————————————————————————————————————— SendSPI MOVE.W #pmSendStartErr,D0 ; assume a send error bsr.s WaitSPIAckHi ; (1) wait for pmgr idle beq.s @error ; if bit low, error ;begin transaction bset.b #PontiLmpSPIDir,PontiLmpSftCtl(a2) ; (2) set direction to output move.b d1,PontiSPISftReg(a2) ; (3) write data bclr.b #PontiSPIReq,PontiSPIMdmCtl(a2) ; (4) assert data valid bsr.s WaitSPIAckLo ; (5) --> modem shift data ; (6) wait for data accepted bne.s @error ; if bit low, error bset.b #PontiSPIReq,PontiSPIMdmCtl(a2) ; (7) clear data valid MOVEQ #noErr,D0 ; report no error @error tst.w d0 rts ;———————————————————————————————————————————————————————————————————————————————— ; Routine: ReceiveSPI ; ; Input: a2.l - pointer to GUR space ; d1.b - byte to send ; ; Returns d0.w - 0 = ok, non-zero = error ; ; Function: read a byte from the spi ;———————————————————————————————————————————————————————————————————————————————— ReceiveSPI MOVE.W #pmRecvStartErr,D0 ; assume a receive error bsr.s WaitSPIAckHi ; (1) wait for pmgr idle beq.s @error ; if bit low, error ;begin transaction bclr.b #PontiLmpSPIDir,PontiLmpSftCtl(a2) ; (2) set direction to input bclr.b #PontiSPIReq,PontiSPIMdmCtl(a2) ; (3) (RFD) ready for data bsr.s WaitSPIAckLo ; (4) acknowledge req ; (5) <-- modem shifting bne.s @error ; if bit low, error bset.b #PontiSPIReq,PontiSPIMdmCtl(a2) ; (6) acknowledge ack bsr.s WaitSPIAckHi ; (7) wait (DAV) beq.s @error ; if bit low, error move.b PontiSPISftReg(a2),d1 ; (8) read data MOVEQ #noErr,D0 ; report no error @error tst.w d0 rts ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: CPUSpeedNiagra ; ; Inputs: none ; ; Outputs: D0 - [maximum CPU speed][current CPU speed] ; ; Trashes: D1, A0 ; ; Function: returns the maximum and current (econo-mode or full) CPU speeds for ; Niagra-based machines ;———————————————————————————————————————————————————————————————————————————————————————— CPUSpeedNiagra MOVEA.L UnivInfoPtr,A0 ; point to the ProductInfo table, ADDA.L DecoderInfoPtr(A0),A0 ; then to the DecoderInfo table, MOVEA.L JawsAddr(A0),A0 ; then to the JAWS base address, ADDA.L #NiagraSpeedReg,A0 ; and finally to the CPU clock register MOVEQ #%00000011,D1 ; only care about last two bits AND.B (A0),D1 ; LSL.B #2,D1 ; convert to long words MOVE.L @CPUSpeedNiagraTable(D1.W),D0 ; get our table entry ADDA.L #JAWSEconoMode-NiagraSpeedReg,A0; (point to the econo-mode register) BTST #1,(A0) ; IF inEconoMode THEN BNE.S @InEcono ; Exit we're done MOVE.W @CPUSpeedNiagraTable(D1.W),D0 ; Else we are running full speed @InEcono RTS @CPUSpeedNiagraTable DC.W CPUSpeed16MHz,CPUSpeed16MHz DC.W CPUSpeed20MHz,CPUSpeed16MHz DC.W CPUSpeed25MHz,CPUSpeed16MHz DC.W CPUSpeed33MHz,CPUSpeed16MHz ALIGN 4 ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: NiagraRunIdleRoutinesProc (IdleMind) ; ; Inputs: A2 - pointer to Power Manager variables ; ; Outputs: none ; ; Trashes: D0, A0 ; ; Function: does the appropriate thing if the shutdown request bit is set ;———————————————————————————————————————————————————————————————————————————————————————— NiagraRunIdleRoutinesProc btst.b #PmgrShutdownReq,PmgrFlags1(A2) ; test request BEQ.S @exitSoftReset ; exit if no request movea.l vSoftShutdown(a2),a0 ; jsr (a0) ; call shutdown vector bne.s @exitSoftReset ; if error, don't clear flag bclr.b #PmgrShutdownReq,PmgrFlags1(A2) ; clear request @exitSoftReset ; Convienient place to check whether a notification message must be posted regarding ; the use of external video and the charger. If the user boots or wakes the machine | ; with an external monitor but no charger, then external video will be disabled by the v ; primaryInit code. PrimaryInit will also set the flag "PmgrExtVidAlrt" indicating ; the condition for a warning to be posted. If while external video is on, and the ; charger is disconnected, a different warning should be posted about the hardware ; becoming unstable unless the charger is attached. BCLR #PmgrExtVidAlrt,PmgrFlags1(A2) ; IF AlertUser THEN BEQ.S @NoVidAlrt ; . MOVE.L NoVidSTRPtr(a2),a0 ; Load Appropriate String MOVEQ #1,d0 ; Tell Message Handler to use completion routine BRA.S @postMsg ; Post Message @NoVidAlrt BTST #PmgrExtVidOn,PmgrFlags1(a2) ; IF External Video THEN BEQ.S @Cont ; . BTST #HasCharger,Charger(a2) ; IF ChargerNotConnected THEN BNE.S @clearFlag ; . MOVE.L NoChrgrSTRPtr(a2),a0 ; Get handle to alert string MOVEQ #0,d0 ; Set empty completion routine @postMsg BSR PostUserNmMsg ; Post the notification mgr message BRA.S @Cont @clearFlag ; ELSE BTST #PmgrAvoidUsrMsg,PmgrFlags1(a2) ; IF warning messages enabled THEN BEQ.S @Cont ; . LEA UNmQEntry(a2),a0 ; Get address of notification record _NMRemove ; Remove Message BCLR #PmgrAvoidUsrMsg,PmgrFlags1(a2) ; after doing NMRemove then clr flag (steve) @Cont BRA RunIdleRoutinesProc ; Continue with RunIdle ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: DartModemOn ; ; Input: a0.l = ptr to powermanager pb + 4 byte general data buffer ; d1.b = modem status bits from power manager ; ; Outputs: condition codes ; ; Trashes: d0 ; ; Function: turn Power on to the internal dartanian modem ; ;———————————————————————————————————————————————————————————————————————————————————————— DartModemOn bsr StdModemOn ; turn on the modem the standard way ; test for spi move.l a2,-(sp) ; save a2 movea.l UnivInfoPtr,a2 ; point to the ProductInfo table, adda.l DecoderInfoPtr(a2),a2 ; then to the DecoderInfo table, movea.l JAWSAddr(a2),a2 ; then to the Niagra base address, adda.l #NiagraGUR,a2 ; and finally general utility register space ; bclr.b #PmgrDartSPI, \ ; ([PmgrBase],PmgrFlags1) ; preset - disable spi interface btst.b #PontiSPIMdmId,PontiSPIMdmCtl(a2); check the new modem interface bit (spi) beq.s @exitSPI ; if not set, old modem, exit CCR set bset.b #PmgrDartSPI,\ ([PmgrBase],PmgrFlags1) ; enable spi interface bclr.b #PontiSndSPIIrqMsk,PontiSndCtl(a2); enable spi interrupts moveq.l #1,d0 ; set CCR, don't turn on power to SCC @exitSPI move.l (sp)+,a2 ; restore a2 RTS ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: DartModemOff ; ; Input: a0.l = ptr to powermanager pb + 4 byte general data buffer ; d1.b = modem status bits from power manager ; ; Outputs: none ; ; Trashes: d0 ; ; Function: turn Power off to the internal dartanian modem ; ;———————————————————————————————————————————————————————————————————————————————————————— DartModemOff ; move.l a2,-(sp) ; save a2 movea.l UnivInfoPtr,a2 ; point to the ProductInfo table, adda.l DecoderInfoPtr(a2),a2 ; then to the DecoderInfo table, movea.l JAWSAddr(a2),a2 ; then to the Niagra base address, adda.l #NiagraGUR+PontiSndCtl,a2 ; and finally to the modem register bclr.b #PmgrDartSPI,\ ([PmgrBase],PmgrFlags1) ; disable spi interface bset.b #PontiSndSPIIrqMsk,(a2) ; disable spi interrupts bsr StdModemOff ; power off modem the standard way move.l (sp)+,a2 ; restore a2 RTS ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: DartModemType ; ; Input: ; ; Outputs: d0.l has the modem Type ; ; Trashes: d0 ; ; Function: Returns type of modem installed in Dartanian Modem ; ;———————————————————————————————————————————————————————————————————————————————————————— DartModemType move.l a0,-(sp) moveq.l #ModemTypeSerial,d0 ; assume serialmodem movea.l UnivInfoPtr, a0 ; point to product info table, movea.l DecoderInfoPtr(a0), a0 ; then to the DecoderInfo table, movea.l JAWSAddr(a0),a0 ; then to the Niagra base address, adda.l #NiagraGUR+PontiSPIMdmCtl, a0 ; and finally to the modem ctl register btst.b #PontiSPIMdmId,(a0) ; test the modem bit beq.s @exit moveq.l #ModemTypeDUOPlus,d0 ; load new modem id @exit movem.l (sp)+,a0 rts ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: SndWatchPonti ; ; Inputs: A0 - pointer to VBL task queue element ; ; Outputs: none ; ; Trashes: D0, A0, A2 ; ; Function: A VBL task called once every ten seconds that checks the sound latch for ; ASC new use. If so then sound power is turned on. If power is already on ; then then the latch is cleared. If the latch stays clear for two VBLs then ; sound power is turned off. ;———————————————————————————————————————————————————————————————————————————————————————— SndWatchPonti MOVE.W #SndWFreq,vblCount(A0) ; reset the counter MOVEA.L UnivInfoPtr,A0 ; point to the ProductInfo table, ADDA.L DecoderInfoPtr(A0),A0 ; then to the DecoderInfo table, MOVEA.L JAWSAddr(A0),A0 ; then to the Niagra base address, ADDA.L #NiagraGUR+PontiSndCtl,A0 ; and finally to the sound register BTST #PontiSndLatchData,(A0) ; was sound used in the last 10 sec BNE.S @SoundOn ; -> yes MOVEA.L PMgrBase,A2 ; no, sound's been on but now it's not TST.B SysTaskFlag(A2) ; are the sound input primitives set up? BEQ.S @NoSoundAct ; -> no, there can't be a sound input source selected jsrTBL sndInputSource ; is a sound source selected? TST.B D0 BNE.S @NoSound ; -> yes, sound input is active so don't power off sound @NoSoundAct BCLR #PontiSndPwrOn,(A0) ; turn off sound power RTS @SoundOn BSET #PontiSndPwrOn,(A0) ; turn on sound power BSET #PontiSndLatchClr,(A0) ; pulse (positive) latch clear bit BCLR #PontiSndLatchClr,(A0) _IdleUpdateDispatch @NoSound RTS ;________________________________________________________________________________________ ; ; Routine: NiagraEnvInt ; ; Inputs: A0 - pointer to PMGR interrupt data: [flags][battery][gas][therm] ; A1 - pointer to VIA1 base ; A2 - pointer to Power Manager's variables ; ; Outputs: none ; ; Trashes: none (D0-D3 and A0-A3 are preserved by the interrupt dispatcher) ; ; Function: Handles PMGR environment interrupts by attempting a soft shutdown, if allowed. ;________________________________________________________________________________________ NiagraEnvInt _IdleUpdateDispatch btst.b #ScsiDiskModeOn,PmgrFlags(a2) ; check for scsi disk mode beq.s @normalRequest ; if not, continue _PowerOff ; ... else power off bra.s * ; ... wait for the plug @normalRequest btst.b #PmgrShutdownEnb,PmgrFlags1(a2) ; is shutdown allowed beq.s @exit ; if not just exit bset.b #PmgrShutdownReq,PmgrFlags1(a2) ; set shutdown request @exit rts ;———————————————————————————————————————————————————————————————————————————————————————— ; _Sleep routine to poweroff Sound (A1 = return address) ;———————————————————————————————————————————————————————————————————————————————————————— | SoundPowerDownNiagra ; V MOVEA.L UnivInfoPtr,A0 ; point to the ProductInfo table, ADDA.L DecoderInfoPtr(A0),A0 ; then to the DecoderInfo table, MOVEA.L JAWSAddr(A0),A0 ; then to the Niagra base address, ADDA.L #NiagraGUR+PontiSndCtl,A0 ; and finally to the sound register ; BSET #PontiSndLatchClr,(A0) ; pulse (positive) latch clear bit BCLR #PontiSndLatchClr,(A0) ; ; BCLR #PontiSndPwrOn,(A0) ; turn off sound power ; JMP (A1) ; and return ENDIF ; {hasNiagra} IF hasNiagra THEN IF hasPratt THEN ;•••••••••••••••••••••••••••••••••••••••• Pratt ••••••••••••••••••••••••••••••••••••••••• EXPORT PrattPmgrPrims import PrattBkltPrims ALIGN 4 DC.L PrimsTypeTable ; flags DC.L (PrattPmgrPrimsEnd-PrattPmgrPrims) ; size of table PrattPmgrPrims ; Table of Primitives tables DC.L PrattRoutines-PrattPmgrPrims ; offset to table of Pratt routines DC.L PrattPrimInfo-PrattPmgrPrims ; offset to decoder-specific constants DC.L PrattIdleMindTable-PrattPmgrPrims ; offset to table of Idlemind routines DC.L PrattSleepTable-PrattPmgrPrims ; offset to table of sleep routines DC.L PrattWakeTable-PrattPmgrPrims ; offset to table of wakeup routines DC.L PrattPMgrOpExceptions-PrattPmgrPrims; offset to PMgrOp exception table DC.L PrattModemTable-PrattPmgrPrims ; offset to table of modem routines DC.L PwrDispatchVects-PrattPmgrPrims ; offset to table of PwrDispatchVects DC.L PMgrHookVects-PrattPmgrPrims ; offset to table of PmgrMgr Hooks DC.L PrattCommsPowerTable-PrattPmgrPrims ; offset to table of comms power routines DC.L PMgrOpTbl-PrattPmgrPrims ; offset to table for PmgrOp DC.L PrattBkltPrims-PrattPmgrPrims ; offset to backlight tables PrattPmgrPrimsEnd DC.L PrimsTypePtr ; flags DC.L (PrattRoutinesEnd-PrattRoutines) ; size of table PrattRoutines ; machine-specific routines DC.L PowerCycle040-PrattRoutines ; offset to routine for power cycling DC.L Restore040-PrattRoutines ; offset to routine for power cycling restore DC.L BatWatch-PrattRoutines ; offset to VBL task to check the battery level DC.L GetExtLevelPratt-PrattRoutines ; offset to routine for determining battery level DC.L MultiScaledBatteryInfo-PrattRoutines; offset to routine to return scaled battery level DC.L 0 ; no environment interrupts DC.L CPUSpeedMSC-PrattRoutines ; offset to routine for determining CPU speed DC.L SndWatchPratt-PrattRoutines ; offset to VBL task to check for sound usage DC.L RedrawScrn-PrattRoutines ; offset to routine for refreshing the screen DC.L PGEAbsoluteBatteryVoltage-PrattRoutines ; routine to return absolute battery level DC.L PrattGetBatteryTimes-PrattRoutines ; routine to return info about battery times DC.L 0 ; dynamic CPU speed change is not supported PrattRoutinesEnd DC.L PrimsTypeInfo ; flags DC.L (PrattPrimInfoEnd-PrattPrimInfo) ; size of table PrattPrimInfo ; machine-specific constants: DC.B $46 ; PRAM base address DC.B DefHysteresis ; default hysteresis DC.B 40 ; default low battery warning level (20%) DC.B 10 ; default dead battery warning level (5%) DC.B 0 ; hysteresis setting for wake level (none) DC.B 0 ; shorted battery at first interrupt (none) DC.B 0 ; no external video correction needed DC.B 0 ; no charger features DC.L $5008001B ; address of power cycling register DC.L 0 |\ ; bitmap of Power Manager features (0< (1< DC.B 0 ; value for sleep register DC.B 0 ; padding for now DC.B 0 ; padding for now PrattPrimInfoEnd DC.L PrimsTypePtr ; flags DC.L (PrattIdleMindTableEnd-PrattIdleMindTable) ; size of table PrattIdleMindTable ; machine specific IdleMind Routines DC.L CheckCountDownTimer-PrattIdleMindTable; offset to count down timer DC.L CheckClamshell-PrattIdleMindTable ; offset to clam shell DC.L ChkSleepTimeOut-PrattIdleMindTable ; offset to sleep time out DC.L ChkIdle-PrattIdleMindTable ; offset to check idle DC.L CalcProgressive-PrattIdleMindTable ; offset to calc progressive DC.L GoPowerCycle-PrattIdleMindTable ; offset to go power cycle PrattIdleMindTableEnd DC.L PrimsTypePtr ; flags DC.L (PrattSleepTableEnd-PrattSleepTable); size of table PrattSleepTable ; list of routines to execute when going to sleep: DC.L SleepHD-PrattSleepTable ; sleep the hard drive if currently running DC.L DockingSleep-PrattSleepTable ; save state of currently connected bar DC.L EnetSleep-PrattSleepTable ; sleep the onboard Enet DC.L SaveLCD-PrattSleepTable ; save GSC (built-in LCD video) registers DC.L SaveFPU-PrattSleepTable ; save FPU registers DC.L SaveVIA1-PrattSleepTable ; save VIA1 registers DC.L SaveVIA2-PrattSleepTable ; save VIA2 registers DC.L SaveWhitney-PrattSleepTable ; save Whitney registers DC.L SaveSlp040-PrattSleepTable ; save MMU registers DC.L SaveSleepInfo-PrattSleepTable ; save sleep info in Power Manager globals DC.L SendSleep-PrattSleepTable ; send a sleep command DC.L 0 ; (end of table) PrattSleepTableEnd DC.L PrimsTypePtr ; flags DC.L (PrattWakeTableEnd-PrattWakeTable) ; size of table PrattWakeTable ; list of routines to execute when waking up: DC.L RestoreSlp040-PrattWakeTable ; restore MMU, SPs, cache registers DC.L RestoreWhitney-PrattWakeTable ; restore Whitney registers DC.L RestoreVIA2-PrattWakeTable ; restore VIA2 registers DC.L RestoreVIA1-PrattWakeTable ; restore VIA1 registers DC.L RestoreFPU-PrattWakeTable ; restore FPU registers DC.L RestoreLCD-PrattWakeTable ; restore CSC (built-in LCD video) registers DC.L EnetWake-PrattWakeTable ; wake the onboard Enet DC.L DockingWakeup-PrattWakeTable ; restore state of currently connected bar DC.L WakeClrInts-PrattWakeTable ; clear any pending PmgrInterrupts DC.L 0 ; (end of table) PrattWakeTableEnd DC.L PrimsTypePMgrEx ; flags DC.L (PrattPMgrOpExceptionsEnd-PrattPMgrOpExceptions) ; size of table PrattPMgrOpExceptions ; PMgrOp exceptions: DC.B $F7,SetModemInts ; $71,$79 - modem commands DC.L PrattModemOp-* ; DC.W 0 ; none for the moment PrattPMgrOpExceptionsEnd DC.L PrimsTypePtr ; flags DC.L (PrattModemTableEnd-PrattModemTable); size of table PrattModemTable DC.L 0 ; offset to routine to turn on modem DC.L 0 ; offset to routine to turn off modem DC.L BlackBirdModemType-PrattModemTable ; offset to routine to get modem type PrattModemTableEnd DC.L PrimsTypePtr ; flags DC.L (PrattCommsPowerTableEnd-PrattCommsPowerTable); number of entries PrattCommsPowerTable @PwrOn DC.L 0 ; no Port B serial DC.L PrattPortAOn-PrattCommsPowerTable ; A serial DC.L PortCOn-PrattCommsPowerTable ; C serial (internal modem) DC.L PrattSonicOn-PrattCommsPowerTable ; onboard ethernet @PwrOff DC.L 0 ; no Port B serial DC.L PrattPortAOff-PrattCommsPowerTable ; A serial DC.L PortCOff-PrattCommsPowerTable ; C serial (internal modem) DC.L PrattSonicOff-PrattCommsPowerTable ; onboard ethernet PrattCommsPowerTableEnd ALIGN 4 ENDIF ;———————————————————————————————————————————————————————————————————————————————————————— thru ; Routine: GetExtLevelPratt | ; v ; Inputs: A2 - pointer to Power Manager globals ; ; Outputs: D0 - normalized battery level (-1 to 4) ; CCR - BNE if a valid battery level is being returned ; ; Trashes: D0-D3, A0 ; ; Function: Reads the extended battery power level from the Power Manager (which is ; pre-averaged by the PMGR). Next, the value is compared with the warning ; and cutoff values in the globals. Warning being the first alert, midway from ; warning to cutoff being the second, and the cutoff being the 10 second ; countdown. ; ; Note: The extended battery status command supports a word-sized battery ; level. Currently the PMGR on Blackbird returns a value in half ; percentages, that is from 0-200. ;———————————————————————————————————————————————————————————————————————————————————————— GetExtLevelPratt CLR.L -(SP) CLR.L -(SP) MOVE.L SP,-(SP) ; pmRBuffer MOVE.L (SP),-(SP) ; pmSBuffer CLR.W -(SP) ; pmLength = 0 MOVE.W #readExtBatt,-(SP) ; pmCommand = get extended battery data MOVEA.L SP,A0 ; point to the parameter block _PMgrOp ; send the command LEA pmRBuffer+4(SP),SP ; toss the parameter block MOVEA.L SP,A0 ; point to the buffer MOVEQ #%00001011,D0 ; mask off the bits we want in the flags byte, AND.B (A0),D0 MOVE.B Charger(A2),D1 ; get the old charger state, MOVE.B D0,Charger(A2) ; and save the new charger state MOVE.B (A0)+,D0 ; get the unmodified flags EOR.B D0,D1 ; has the charger state changed since last time? BTST #HasCharger,D1 ; BEQ.S @ChargeSame ; -> no ST TOdirtyFlag(A2) ; yes, flag that timeouts need updating @ChargeSame MOVE.W (A0),D3 MOVE.B D3,BatAvg(A2) ; save the 8-bit battery voltage ADDQ.W #8,SP ; toss the buffer BTST #HasCharger,D0 ; is a charger connected? BNE.S @HaveCharger ; -> yes, we don't care about the battery level BTST #2,D0 ; is a battery connected? BEQ.S @HaveCharger ; -> no, don't bother with battery level MOVEQ #0,D0 ; level 0 CMP.B LowWarn(A2),D3 ; BHI.S @foundlevel MOVEQ #1,D0 ; level 1 MOVEQ #0,D1 ; clr register MOVE.B LowWarn(A2),D1 ; get low-warning level ADD.B CutOff(A2),D1 ; add cutoff LSR.B #1,D1 ; average the two CMP.B D1,D3 ; are we there yet? BHI.S @foundlevel ; MOVEQ #3,D0 ; level 3 CMP.B CutOff(A2),D3 ; are we at cutoff yet? BHI.S @foundlevel ; MOVEQ #4,D0 ; level 4 BRA.S @foundlevel ; 10 second countdown @HaveCharger MOVEQ #-1,D0 ; return a "charged" battery level in case someone cares @foundlevel MOVEQ #1,D1 ; always return BNE for data valid RTS ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: SndWatchPratt ; ; Inputs: A0 - pointer to VBL task queue element ; ; Outputs: none ; ; Trashes: D0, A0, A2 ; ; Function: A VBL task called once every ten seconds that checks the sound latch for ; ASC new use. If so then sound power is turned on. If power is already on ; then then the latch is cleared. If the latch stays clear for two VBLs then ; sound power is turned off. ;———————————————————————————————————————————————————————————————————————————————————————— SndWatchPratt MOVE.W #SndWFreq,vblCount(A0) ; reset the counter MOVEA.L ASCBase,A0 ; point to the ProductInfo table, LEA wSndControl(A0),A0 ; A0 = Ptr to Sound Control Register BTST #wSingerSNDFlag,(A0) ; IF wSingerSNDFlag THEN BNE.S @SoundOn ; there was a sound in the last 10 sec MOVEA.L PMgrBase,A2 ; no, sound's been on but now it's not TST.B SysTaskFlag(A2) ; are the sound input primitives set up? BEQ.S @NoSoundAct ; -> no, there can't be a sound input source selected jsrTBL sndInputSource ; is a sound source selected? TST.B D0 BNE.S @NoSound ; -> yes, sound input is active so don't power off sound @NoSoundAct BCLR #wSingerPower,(A0) ; turn off sound power RTS @SoundOn BSET #wSingerPower,(A0) ; turn on sound power BSET #wSingerSNDFlag,(A0) ; Clear the latch _IdleUpdateDispatch ; @NoSound RTS ; ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: PrattGetBatteryTimes ; ; Inputs: - pointer to BatteryTimeRec record: ; expectedBatteryTime DS.L 1 ; estimated battery time remaining ; minimumBatteryTime DS.L 1 ; minimum battery time remaining ; maximumBatteryTime DS.L 1 ; maximum battery time remaining ; timeUntilCharged DS.L 1 ; time remaining until the battery is fully charged ; D0 - high word: system total (0) - battery number (1-n) ; ; Outputs: none ; ; Trashes: A0-A1, D0-D2 ; ; Function: fills in a record containing fields that describe battery times. ;________________________________________________________________________________________ PrattGetBatteryTimes @NoBattery CLR.L (A0)+ ; zero out the fields CLR.L (A0)+ CLR.L (A0)+ CLR.L (A0)+ MOVEQ #0,D0 RTS ;———————————————————————————————————————————————————————————————————————————————— ; Routine: PrattModemOp ; ; Input: A0 - points the pmgrop PB ; ; Destroys: d1 ; ; Called by: PmgrOp ; ; Function: transfer data through modem ;———————————————————————————————————————————————————————————————————————————————— PrattModemOp moveq #1,d1 ; set CC to not handled here rts ; return ;———————————————————————————————————————————————————————————————————————————————————————— ; _Sleep routine to save the Whitney registers (A1 = return address) ;———————————————————————————————————————————————————————————————————————————————————————— SaveWhitney MOVEA.L ASCBase,A2 ; point to the base of the ASC/Batman MOVE.B ascMode(A2),-(SP) ; save the chip mode MOVE.B ascVolControl(A2),-(SP) ; save the external sound volume control MOVE.B ascPlayRecA(A2),-(SP) ; save channel A play/record register MOVE.B bmIntControlA(A2),-(SP) ; save sound input control register a MOVE.B bmIntControlB(A2),-(SP) ; save sound input control register b MOVE.B wSndControl(A2),-(SP) ; save sound control regiser MOVE.L wSndFIFOBase(A2),-(SP) ; save sound FIF0 Base address MOVE.L wSndSingerCtl1(A2),-(SP) ; save Singer Control Register 1 & 2 MOVE.B WhitneyPwrCtl,-(SP) ; save Power Control Register JMP (A1) ; and return ;———————————————————————————————————————————————————————————————————————————————————————— ; _Sleep routine to Restore the Whitney registers (A1 = return address) ;———————————————————————————————————————————————————————————————————————————————————————— RestoreWhitney MOVE.B (SP)+,WhitneyPwrCtl ; restore Power Control Register MOVEA.L ASCBase,A2 ; point to the base of the ASC/Batman MOVE.L (SP)+,wSndSingerCtl1(A2) ; restore Singer Control Register 1 & 2 MOVE.L (SP)+,wSndFIFOBase(A2) ; restore sound FIF0 Base address MOVE.B (SP)+,wSndControl(A2) ; restore sound control regiser MOVE.B (SP)+,bmIntControlB(A2) ; restore sound input control register b MOVE.B (SP)+,bmIntControlA(A2) ; restore sound input control register a MOVE.B (SP)+,ascPlayRecA(A2) ; restore channel A play/record register MOVE.B (SP)+,ascVolControl(A2) ; restore the external sound volume control MOVE.B (SP)+,ascMode(A2) ; restore the chip mode JMP (A1) ; and return ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: BlackbirdModemType ; ; Input: ; ; Outputs: d0.l has the modem Type ; ; Trashes: d0 ; ; Function: Returns type of modem installed in TIM Modem ; ;———————————————————————————————————————————————————————————————————————————————————————— BlackbirdModemType move d0, d0 ; do something stupid for now so I can test this rts ;_______________________________________________________________________________________ ; PrattPortAOn -- turns on serial port A ; ; Inputs: ; Outputs: ; Trashes: ; Notes: No need to do A -> C mapping. No need to Port B check (we don't have one). ;_______________________________________________________________________________________ PrattPortAOn bset.b #SerPortAPwr,\ ([PMgrBase],PmgrFlags2) ; log power state bclr.b #WhitneySCCpwr,WhitneyPwrCtl ; turn on SCC driver chip bclr.b #WhitneySCCclk,WhitneyPwrCtl ; start clock to SCC rts ;_______________________________________________________________________________________ ; PrattPortAOff -- turns off serial port A ; ; Inputs: ; Outputs: ; Trashes: ; ; Notes: No need to do A -> C mapping. No need to Port B check (we don't have one). ; Disable interrupts so we don't get any by mistake on wake. ;_______________________________________________________________________________________ PrattPortAOff bclr.b #SerPortAPwr,\ ([PMgrBase],PmgrFlags2) ; log power state move.l SCCRd, a0 ; Point to SCC move.l SCCWr, a1 addq.l #2, a0 ; point to port A addq.l #2, a1 move.b (a0), d0 ; sync up move.b #9,(a1) ; write register 9 move.b #2,(a1) ; disable all SCC interrupts bset.b #WhitneySCCpwr,WhitneyPwrCtl ; turn off SCC driver chip bset.b #WhitneySCCclk,WhitneyPwrCtl ; stop clock to SCC rts ;_______________________________________________________________________________________ ; PrattSonicOn -- turns on Sonic ethernet power ; ; Inputs: ; Outputs: ; Trashes: d2 ; ;_______________________________________________________________________________________ PrattSonicOn bset.b #EnetPwr,\ ([PMgrBase],PmgrFlags3) ; log power state bclr.b #WhitneyEnetPwr,WhitneyPwrCtl ; turn on power to Sonic ; wait for crystal to stabilize move.w TimeVIAdb,d2 ; 1 ms delay lsl.w #6,d2 ; 64 ms delay @loop tst.b ([VIA]) dbra d2,@loop bset.b #WhitneyEnetReset,WhitneyPwrCtl ; deassert _sleep pin rts ;_______________________________________________________________________________________ ; PrattSonicOff -- turns off Sonic ethernet power ; ; Inputs: ; Outputs: ; Trashes: ; ;_______________________________________________________________________________________ PrattSonicOff bclr.b #EnetPwr,\ ([PMgrBase],PmgrFlags3) ; log power state bclr.b #WhitneyEnetReset, WhitneyPwrCtl ; assert _sleep pin bset.b #WhitneyEnetPwr,WhitneyPwrCtl ; turn off power to Sonic rts ;_______________________________________________________________________________________ ; IsDriverInstalled -- looks to see if given driver is installed in the unit table ; ; Inputs: a0.l points to input driver name ; d1.l is the input driver name length ; Outputs: CCR has Z=0 for found, Z=1 for not found ; d0 = pointer to the DCE ; Trashes: d0 ; Called by: Sleep Table code in Power Mgr ; Calls: .ENET driver _Open and _Control ; ;_______________________________________________________________________________________ IsDriverInstalled @workregs REG a0-a3/d1-d4 movem.l @workregs, -(sp) ; save our working regs MOVE.L UTableBase, A3 ; get address of the unit I/O table MOVE.W UnitNtryCnt, D2 ; number of units to check @CkInstLoop MOVE.L (A3)+,D0 ; get next Device Control Entry handle BEQ.S @nextEntry ; branch if no entry installed MOVE.L D0,A2 ; DCE handle MOVE.L (A2),D0 ; dereference handle BEQ.S @nextEntry ; branch if null MOVE.L D0,A1 ; A1 -> Device Control Entry move.l d0, d4 ; save a pointer to the DCE BTST #DRAMBased,DCtlFlags+1(A1) ; ROM-based? MOVE.L DCtlDriver(A1), A1 ; driver ptr/handle BEQ.S @1 ; br if ROM-based (A1 is a pointer) MOVE.L A1, D0 ; be clean and check for 0 handle BEQ.S @nextEntry ; skip if so MOVE.L (A1), A1 ; deref handle @1 MOVE.L A1, D0 ; is driver ptr nil? BEQ.S @nextEntry ; br if so LEA DrvrName(A1),A1 ; point A1 to driver name MOVE.L D1, D0 ; SWAP D0 MOVE.B (A1)+, D0 ; string 2 length CMP.B D0, D1 ; are lengths the same? BNE.S @nextEntry ; br if not (names shouldn't match if not) _CmpString BEQ @found ; found the driver! @nextEntry SUBQ.W #1,D2 ; next unit table entry BGT.S @CkInstLoop ; continue searching through all drivers @found move.l d4, d0 ; return pointer to the DCE bra.s @out ; @notfound moveq #0, d0 ; driver not found @out movem.l (sp)+, @workregs ; restore our working regs rts ;_______________________________________________________________________________________ ; IsDriverOpen -- looks at driver flags in DCE to see if driver is open ; ; Inputs: d0 points to driver ; Outputs: CCR has Z=0 for open, Z=1 for not open ; d0 = pointer to the DCE ; Trashes: d0 ; Called by: Sleep Table code in Power Mgr ; Calls: .ENET driver _Open and _Control ; ;_______________________________________________________________________________________ IsDriverOpen @workregs REG a0-a3/d1-d4 movem.l @workregs, -(sp) ; save our working regs movea.l d0, a0 ; point to the driver move.w dCtlFlags(a0), d0 ; get driver flags btst #5, d0 ; is the driver Open? beq.s @notopen ; nope @open moveq #1, d0 ; driver open bra.s @out @notopen moveq #0, d0 ; driver not open @out movem.l (sp)+, @workregs ; restore our working regs rts ;_______________________________________________________________________________________ ; EnetSleep/EnetWake -- Calls .ENET driver to sleep or wake SONIC ; ; Inputs: a1 points to return address ; Outputs: none ; Trashes: none ; Called by: Sleep Table code in Power Mgr ; Calls: .ENET driver _Open and _Control ; ;_______________________________________________________________________________________ ESleep EQU 254 ; Sleep the SONIC EWake EQU 255 ; Wake the SONIC EDetachPH EQU 248 ; Detach the protocol handler EnetSleep move.l #ESleep, d0 ; We're sleeping now bra.s EnetWakeSleep EnetWake move.l #Ewake, d0 ; We're waking now EnetWakeSleep @workregs REG a0-a2/d1-d2 MOVEM.L @workregs,-(SP) ; save some reg's move.l d0, d2 ; save our request TestFor SONICExists ; do we have a SONIC? beq.s @out ; no SONIC lea EnetName, a0 ; point to driver name moveq.l #0,d1 move.b (a0)+, d1 ; get the driver name length bsr.w IsDriverInstalled ; is the .ENET driver installed? beq.s @out ; driver not installed bsr.w IsDriverOpen ; is the .ENET driver open? beq.s @out ; driver not open moveq #ioQElSize, d0 ; size the io stack frame sub.w d0,sp ; Allocate IO stack frame asr.w #1, d0 ; number of words in frame subi.w #1, d0 ; our friend dbra move.l sp, a2 ; point to start of frame @clr clr.w (a2)+ ; clear frame dbra d0, @clr MOVE.L SP,A0 ; Save this place LEA EnetName,A2 ; Get Ethernet refnum MOVE.L A2,ioVNPtr(A0) MOVE.B #fsCurPerm,ioPermssn(A0) _Open MOVE.w d2,csCode(A0) ; Sleep/Wake the Sonic _Control ; (synchronously) ; MOVE #EDetachPH,CSCode(A0); Set code for detach PH ; MOVE #0,EProtType(A0) ; Set for 802.2 ; _Control ; (synchronously) add.w #ioQElSize,SP ; Allocate IO stack frame @out movem.l (sp)+, @workregs ; restore the registers JMP (A1) ; and return EnetName dc.b 5 ; length of '.ENET' dc.b '.ENET' ; driver name ;••••••••••••••••••••••••••••••••••••• Dispatch Tables •••••••••••••••••••••••••••••••••••••••• IMPORT BasePRAM;•• IMPORT CPUSpeed;•• IMPORT ExternaVideoOnproc;•• IMPORT IdleDelay;•• IMPORT IdleDisable;•• IMPORT IdleEnable;•• IMPORT IdleMind IMPORT IdleRead IMPORT IdleState IMPORT IdleUpdate;•• IMPORT IdleUpdateTrap;•• IMPORT ModemStatusRT ;•• IMPORT ModemTypeProc ;•• IMPORT PmgrTrap IMPORT PowerMgrHook IMPORT PDimScreens IMPORT ScaledBattery IMPORT ScsiDiskModeproc;•• IMPORT SecondaryInitproc IMPORT SerialPower;•• ALIGN 4 DC.L PrimsTypePtr ; flags DC.L (PwrDispatchEnd-PwrDispatchVects) ; number of table entries PwrDispatchVects DC.L PmgrTrap-PwrDispatchVects ; Selector #0 DC.L IdleUpdate-PwrDispatchVects ; Selector #1 DC.L IdleDelay-PwrDispatchVects ; Selector #2 DC.L IdleMind-PwrDispatchVects ; Selector #3 DC.L IdleRead-PwrDispatchVects ; Selector #4 DC.L IdleEnable-PwrDispatchVects ; Selector #5 DC.L IdleDisable-PwrDispatchVects ; Selector #6 DC.L CPUSpeed-PwrDispatchVects ; Selector #7 DC.L BasePRAM-PwrDispatchVects ; Selector #8 DC.L ScaledBattery-PwrDispatchVects ; Selector #9 DC.L PowerMgrHook-PwrDispatchVects ; Selector #10 DC.L PDimScreens-PwrDispatchVects ; Selector #11 DC.L 0 ; Selector #12 (FactoryDisp patched on disk) DC.L PrivateFeatures-PwrDispatchVects ; Selector #13 PwrDispatchEnd DC.L PrimsTypePtr ; flags DC.L (PMgrHookEnd-PMgrHookVects) ; number of table entries PMgrHookVects DC.L SecondaryInitproc-PMgrHookVects ; Selector #0 DC.L ScsiDiskModeproc-PMgrHookVects ; Selector #1 DC.L ExternaVideoOnproc-PMgrHookVects ; Selector #2 DC.L ModemTypeProc-PMgrHookVects ; Selector #3 PMgrHookEnd DC.L PrimsTypePtr ; flags DC.L (PMgrOpTblEnd-PMgrOpTbl) ; number of table entries PMgrOpTbl DC.L PmgrTrap-PMgrOpTbl ; Pmgr Trap #A085 DC.L IdleUpdateTrap-PMgrOpTbl ; Pmgr Trap #A285 DC.L IdleState-PMgrOpTbl ; Pmgr Trap #A485 DC.L SerialPower-PMgrOpTbl ; Pmgr Trap #A685 PMgrOpTblEnd ;••••••••••••••••••••••••••••••••••••••• Sound VBL ••••••••••••••••••••••••••••••••••••••• ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: SndWatch ; ; Inputs: A0 - pointer to VBL task queue element ; ; Outputs: none ; ; Trashes: D0-D1, A0 ; ; Function: A VBL task called once every ten seconds that checks the sound latch for ; ASC new use. If so then sound power is turned on. If power is already on ; then then the latch is cleared. If the latch stays clear for two VBLs then ; sound power is turned off. ;———————————————————————————————————————————————————————————————————————————————————————— SndWatch ;MOVE.L PmgrBase,A2 ;LEA SwVBLTask(A2),A0 ; Get pointer to vbl task MOVE.W #SndWFreq,vblCount(A0) ; reset the counter CLR.W -(SP) ; allocate a buffer on the stack MOVE.L SP,-(SP) ; pmRBuffer MOVE.L (SP),-(SP) ; pmSBuffer CLR.W -(SP) ; pmLength = 0 MOVE.W #SoundRead,-(SP) ; pmCommand = get sound latch state MOVEA.L SP,A0 ; point to the parameter block _PMgrOp ; send the command LEA pmRBuffer+4(SP),SP ; toss the parameter block MOVE.B (SP)+,D0 ; get the sound state BEQ.S @NoSound ; -> no sound activity MOVEQ #sndOnclrLtch,D1 ; assume the latch is set (turn sound on, clear latch) BTST #1,D0 ; is the latch set? BNE.S @SndActive ; -> yes MOVEA.L PMgrBase,A0 ; no, sound's been on but now it's not TST.B SysTaskFlag(A0) ; are the sound input primitives set up? BEQ.S @NoInputs ; -> no, there can't be a sound input source selected jsrTBL sndInputSource ; is a sound source selected? TST.B D0 BNE.S @NoSound ; -> yes, sound input is active so don't power off sound @NoInputs MOVEQ #soundOff,D1 ; nothing's on so turn sound off @SndActive MOVE.B D1,-(SP) ; put the new sound state into a buffer MOVE.L SP,-(SP) ; pmRBuffer MOVE.L (SP),-(SP) ; pmSBuffer MOVE.W #1,-(SP) ; pmLength = 1 MOVE.W #SoundSet,-(SP) ; pmCommand = set sound state MOVEA.L SP,A0 ; point to the parameter block _PMgrOp ; send the command LEA pmRBuffer+4+2(SP),SP ; toss the parameter block and buffer TST.B D1 ; is sound turned off? BEQ.S @NoSound ; -> yes _IdleUpdateDispatch @NoSound RTS ; all done ;••••••••••••••••••••••••••••••••••••••• IdleMind •••••••••••••••••••••••••••••••••••••••• ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: RunIdleRoutinesProc (IdleMind) ; ; Inputs: A2 - pointer to Power Manager variables ; ; Outputs: none ; ; Trashes: D0-D2, A0 (must preserve A1) ; ; Function: Runs IdleMind routines which are not based on LastAct. These routines ; have priority over the LastAct based ones with the exception of the ; countdown timers. ;———————————————————————————————————————————————————————————————————————————————————————— RunIdleRoutinesProc BSR.S ChkDimming ; is it time to Power Down the Screen BSR.S ChkHdSpinDown ; Is it time to Power Down the HD RTS ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: ChkDimming ; ; Inputs: A2 - pointer to Power Manager variables ; ; Outputs: none ; ; Trashes: D0-D2, A0 (must preserve A1) ; ; Function: Runs IdleMind routines which are not based on LastAct. These routines ; have priority over the LastAct based ones with the exception of the ; countdown timers. ;———————————————————————————————————————————————————————————————————————————————————————— ChkDimming TST.B DimmingDisable(A2) ; IF counting semaphor = 0 THEN BNE.S @Exit ; BSR CurFrontProcEqual ; IF Process are the same THEN BEQ.S @Exit ; MOVE.L Ticks,D0 ; D0 = Current time SUB.L LastUsrAct(A2),D0 ; Calc elapsed time since last user activity BMI.S @hadActivityNoDim ; IF LastUsrAct > Tick THEN exit MOVEQ #0,D1 ; Clear register CMP.L DimmingWaitTime(A2),D0 ; IF TimeToWait <= TimeSinceLastAct THEN BLO.S @hadActivityNoDim ; BSET #PmgrDimStatus,PmgrFlags2(A2) ; Set the flag that we are dimmed BNE.S @Exit ; IF not already dimmed THEN MOVEQ #1,D0 ; Set Video for Power Down BRA VidLowPwrSelect ; Set Video State @hadActivityNoDim ; ELSE BCLR #PmgrDimStatus,PmgrFlags2(A2) ; Clear the flag that we are dimmed BEQ.S @Exit ; IF dimmed THEN MOVEQ #0,D0 ; Set Video for Power Up BRA VidLowPwrSelect ; Set Video State @Exit ; ENDIF RTS ; ENDIF ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: CheckCountDownTimer (IdleMind) ; ; Inputs: A2 - pointer to Power Manager variables ; ; Outputs: CCR - BEQ = not in countdown mode, BNE = go to sleep ; ; Trashes: D0 ; ; Function: returns whether or not we're in a countdown condition ;———————————————————————————————————————————————————————————————————————————————————————— CheckCountDownTimer CMP.B #4,LastLevel(A2) ; Test for level 4 sleep BNE.S @NotCountingDown ; IF LastLevel != 4 THEN exit CMP.B #SleepWait,Level4Cnt(A2) ; Test for standing 10 count BLS.S @NotCountingDown ; IF Level4Cnt != 10 THEN exit BSET #LowPowerSleep,PMgrFlags2(A2) ; set the low power sleep flag MOVEQ #-1,D0 ; Set to force sleep RTS ; Goodnight @NotCountingDown MOVEQ #0,D0 ; Set condition codes RTS ; exit ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: ChkHdSpinDown (IdleMind) ; ; Inputs: A2 - pointer to Power Manager variables ; ; Outputs: none ; ; Trashes: D0-D2, A0 (must preserve A1) ; ; Function: determines whether to spin down the hard drive. If so, then jumps through ; a vector to spin down the hard drive ;———————————————————————————————————————————————————————————————————————————————————————— ChkHdSpinDown MOVE.L LastHd(A2),D2 ; Get last HD activity BEQ.S @Done ; IF DriveSpunDown THEN exit MOVE.L HDvector(A2),D0 ; IF HDvector = null THEN BEQ.S @Done ; exit BTST #dockingStation,dockFlags(A2) ; are we in a docking station? BNE.S @Done ; -> yes, never spin down the hard disk BTST.B #HasCharger,Charger(A2) ; Check for Battery BEQ.S @OnBattery ; IF !HasCharger THEN On Battery BTST.B #ChargeSleep,SleepFlags(A2) ; Check if in ChargeSleep since no spin down while charging BNE.S @Done ; IF ChargeSleep THEN exit @OnBattery MOVEA.L D0,A0 ; get the spin down routine vector MOVEQ #0,D0 MOVE.B HDTime(A2),D0 ; Get current HD timeout BNE.S @hdTOok ; IF HDTime = null THEN MOVE.B #DfltHDTime,D0 ; Set Default @hdTOok MULU.W #pram2ticks,D0 ; Convert HD time out to ticks MOVE.L Ticks,D1 ; Get current time SUB.L D2,D1 ; D1 = elapsed time since last HD activity CMP.L D1,D0 ; Check for HD timeout BGT.S @Done ; IF elapsedTime > TimeOut THEN JSR (A0) ; Call spin down routine @Done RTS ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: ChkSleepTimeOut (IdleMind) ; ; Inputs: A2 - pointer to Power Manager variables ; ; Outputs: CCR - BEQ = continue, BNE = request sleep ; ; Trashes: D0 ; ; Function: returns whether to issue a sleep request ;———————————————————————————————————————————————————————————————————————————————————————— ChkSleepTimeOut BTST.B #HasCharger,Charger(A2) ; Check for Battery BEQ.S @OnBattery ; IF !HasCharger THEN On Battery BTST.B #ChargeSleep,SleepFlags(A2) ; Check if in ChargeSleep since no spin down while charging BNE.S @Done ; IF ChargeSleep THEN exit @OnBattery MOVEQ #0,D0 MOVE.B SleepTime(A2),D0 ; Get current Sleep timeout BNE.S @slpTOok ; IF SleepTime = null THEN MOVE.B #DfltSlpTime,D0 ; Set Default @slpTOok MULU.W #pram2ticks,D0 ; Convert Sleep timeout to ticks ADD.L LastAct(A2),D0 ; Calc time to go to sleep CMP.L Ticks,D0 ; IF TimeToSleep >= CurrentTime THEN BGE.S @Done ; exit TST.B AutoSlpDisable(A2) ; IF counting semaphor > 0 THEN BGT.S @Done ; exit BSR CurFrontProcEqual ; IF Processes <> THEN BEQ.S @Done ; exit MOVEQ #-1,D0 ; Set a sleep request RTS ; Try to Sleep @Done MOVEQ #0,D0 ; Set condition codes RTS ; Return ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: ChkIdle (IdleMind) ; ; Inputs: A2 - pointer to Power Manager variables ; ; Outputs: CCR - BEQ = continue, BNE = go idle ; ; Trashes: D0-D1 ; ; Function: determines whether or not we're in idle ;———————————————————————————————————————————————————————————————————————————————————————— ChkIdle BTST #dockNoPowerCycle,dockFlags(A2) ; is power cycling allowed?
BNE.S @hadActivity ; -> no, bail
TST.B MBState ; IF mouse down THEN BEQ.S @UpdateOut ; Update Activity and Exit TST.B watchCrsr(A2) ; IF watchCrsr THEN BNE.S @UpdateOut ; Update Activity and Exit BTST.B #IdleBit,SleepFlags(A2) ; IF idleDisable THEN BNE.S @hadActivity ; Exit and set Activity TST.B IdleFlagCnt(A2) ; IF IdleFlagCnt THEN BGT.S @hadActivity ; Exit and set Activity MOVE.L Ticks,D0 ; Mouse activity woke us up SUB.L LastAct(A2),D0 ; Calc elapsed time since last activity BMI.S @hadActivity ; IF LastAct > Tick THEN exit MOVEQ #0,D1 ; Clear register MOVE.W PwrCycWaitTime(A2),D1 ; Get the time to wait CMP.L D0,D1 ; IF TimeToWait >= TimeSinceLastAct THEN BGE.S @hadActivity ; Exit @noActivity MOVEQ #-1,D0 ; Set condition codes RTS @UpdateOut MOVE.L Ticks,LastAct(A2) ; Update activity @hadActivity MOVEQ #0,D0 ; Set condition codes RTS ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: CalcProgressive (IdleMind) ; ; Inputs: A2 - pointer to Power Manager variables ; ; Outputs: D1 - power cycling count ; ; Trashes: D0 ; ; Function: calculates the number of power cycles we will follow through ;———————————————————————————————————————————————————————————————————————————————————————— CalcProgressive MOVE.W PwrCycCounter(A2),D1 ; Power cycle a limited number of times MOVE.L LastAct(A2),D0 ; D0 = LastAct CMP.L PMgrScratch.ProgLastAct(A2),D0 ; Check for no activity BNE.S @SomeAct ; Some Activity. Branch. MOVE.W PMgrScratch.ProgCounter(A2),D1 ; Get current power cycle counter ADD.W PwrCycProgGrow(A2),D1 ; Increment counter CMP.W PwrCycProgMax(A2),D1 ; Set power cycle number to new value BHS.S @SetMax ; Have we reached maximum value MOVE.W D1,PMgrScratch.ProgCounter(A2) ; Save it for next time RTS @SetMax MOVE.W PwrCycProgMax(A2),D1 ; Set power cycling count to maximum level RTS @SomeAct MOVE.L D0,PMgrScratch.ProgLastAct(A2) ; Reset Activity for next check MOVE.W D1,PMgrScratch.ProgCounter(A2) ; Reset counter RTS ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: GoPowerCycle (IdleMind) ; ; Inputs: A2 - pointer to Power Manager variables ; ; Outputs: CCR - BEQ = continue, BNE = go idle ; ; Trashes: D0-D1 ; ; Function: high-level power cycling call ;———————————————————————————————————————————————————————————————————————————————————————— GoPowerCycle BSR.L SetSupervisorMode ; Set the machine to supervisor mode MOVE.W D0,SaveCPUState.CPUSRsave(A2) ; Save the the SR MOVE.L AutoInt7,SaveCPUState.CPUNMIsave(A2); Save the NMI Vector MOVE.L ResetSP,-(SP) ; save current ResetSP MOVE.L ResetPC,-(SP) ; save current ResetPC BTST.B #hwCbFPU,HWCfgFlags ; Do we have an FPU? BEQ.S @docycle ; IF hasFPU THEN FMOVEM.L FPCR/FPSR/FPIAR,-(SP) ; Save them regs FMOVEM.X FP0-FP7,-(SP) ; Save more them regs @docycle MOVE.L LastAct(A2),-(SP) ; Save lastact as an activity flag MOVE.L PwrCycProc(A2),A0 ; Get pointer to power cycling routine JSR (A0) ; Go power cycle MOVE.L (SP)+,D0 ; Get the previous lastact CMP.L LastAct(A2),D0 ; Test for any activity DBNE D1,@docycle ; Go through another loop if none BTST.B #hwCbFPU,HWCfgFlags ; Do we have an FPU? BEQ.S @noFPU ; IF hasFPU THEN FMOVEM.X (SP)+,FP0-FP7 ; Restore more them regs FMOVEM.L (SP)+,FPCR/FPSR/FPIAR ; Restore them regs @noFPU MOVE.L (SP)+,ResetPC ; Restore the ResetPC MOVE.L (SP)+,ResetSP ; Restore the ResetSP MOVE.W SaveCPUState.CPUSRsave(A2),SR ; Restore the Status Register from power cycle MOVE.L SaveCPUState.CPUNMIsave(A2),AutoInt7; Restore the NMI Vector RTS ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: CurFrontProcEqual ; ; Inputs: none ; ; Outputs: CCR - BEQ = Processes Not Equal, BNE = Processes Equal ; ; Trashes: D0 ; ; Function: returns whether the process managers front and current process are the same. ; This is important since we can neither sleep nor dim if they are different. ;———————————————————————————————————————————————————————————————————————————————————————— CurFrontEqFrame RECORD 0,DECR FrontSerialNum DS.B ProcessSerialNumber CurSerialNum DS.B ProcessSerialNumber Result DS.W 1 CurFrontEqFrameSz EQU * ENDR CurFrontProcEqual CurFrontProcRegs REG A0-A1 WITH CurFrontEqFrame LINK A6,#CurFrontEqFrameSz ; Build StackFrame MOVEM.L CurFrontProcRegs,-(SP) ; Save them regs SUBQ.W #2,SP ; Make space for OSErr PEA CurSerialNum(A6) ; Set pointer to CurSerialNum _GetCurrentProcess ; Get Process Managers Current Process PEA FrontSerialNum(A6) ; Set pointer to FrontSerialNum _GetFrontProcess ; Get Process Managers Front Process TST.W (SP) ; IF Error THEN BNE.S @ProcessEqual ; NoFrontProcesses Exit, Assume ProcessEqual PEA CurSerialNum(A6) ; Set pointer to CurSerialNum PEA FrontSerialNum(A6) ; Set pointer to FrontSerialNum PEA Result(A6) ; Set pointer to result _SameProcess ; IF CurrentProcess = FrontProcess THEN TST.W (SP) ; IF Err THEN BNE.S @ProcessNotEqual ; ProcessNotEqual TST.B Result(A6) ; ELSE BEQ.S @ProcessNotEqual ; ProcessEqual @ProcessEqual MOVEQ #1,D0 ; Set condition codes for Processes Equal BRA.S @Exit ; That's all folks @ProcessNotEqual MOVEQ #0,D0 ; Set condition codes for no sleep @Exit ADDQ.W #2,SP ; Clean up the stack MOVEM.L (SP)+,CurFrontProcRegs ; Restore them regs UNLK A6 ; Release stack fram TST.L D0 ; Set Condition codes RTS ; <5> ;••••••••••••••••••••••••••••••••••••••• PowerCycling ••••••••••••••••••••••••••••••••••••••• ;———————————————————————————————————————————————————————————————————————————————————————— ; ; PowerCycle ; ;———————————————————————————————————————————————————————————————————————————————————————— PowerCycleRegs REG D0-D7/A0-A6 WITH CPUStateRec ;——————————————————————————————————————————————————————————————————————— MACHINE MC68040 PowerCycle040 @SaveSetRegs REG D0-D7/A3-A6 MOVE.W SR,-(SP) ; Save status register ORI.W #HiIntMask,SR ; No more interrupts MOVEM.L PowerCycleRegs,-(SP) ; save them registers MOVE.L PMgrBase,A2 ; get pointer to globals LEA SaveCPUState(A2),A1 ; get pointer to SaveCPUState area MOVEC CACR,D7 ; get current Cache Register MOVEQ #0,D0 ; clear register MOVEC D0,CACR ; disable caches CPUSHA bc ; flush both caches entirely ; Get CPU Internal State Registers MOVEC URP,D0 ; Save User Root Pointer MOVEC SRP,D1 ; Save Supervisor Root Pointer MOVEC TC,D2 ; Save TC MOVEC DTT0,D3 ; Save Data TT0 MOVEC DTT1,D4 ; Save Data TT1 MOVEC ITT0,D5 ; Save Instruction TT0 MOVEC ITT1,D6 ; Save Instruction TT1 MOVEC VBR,A3 ; Save vector base reg MOVE.L AutoInt7(A3),-(SP) ; Save the NMI Vector MOVEC USP,A4 ; Save User Stack Pointer MOVEC MSP,A5 ; Save Master Stack Pointer MOVEC ISP,A6 ; Save Interrupt Stack Pointer MOVEM.L @SaveSetRegs,(A1) ; Save all them special regs ; Last Chance for SCC MOVEQ #(1< BRA.S * ; Wait for the drugs to take effect @DontCycle MOVEC D0,CACR ; restore them caches ADDQ.W #4,SP ; clear the NMI from the stack MOVEM.L (SP)+,PowerCycleRegs ; Restore them regs MOVE.W (SP)+,SR RTS ;———————————————————————————————————————————————————————————————————————————————————————— MACHINE MC68030 PowerCycle030 MOVE.W SR,-(SP) OR.W #HiIntMask,SR ; No more interrupts MOVEM.L PowerCycleRegs,-(SP) ; save all for now, Save only the important registers MOVEC CACR,D0 ; Now save cache regs OR.W #(1<<11)+(1<<3),D0 ; Flush caches upon return MOVEC CAAR,D1 ; MOVEC VBR,A0 ; Also save vector base MOVE.L USP,A1 ; and alternats stack pointers MOVEC MSP,A2 ; . MOVEM.L D0/D1/A0-A2,-(SP) ; Save some cool registers MOVEQ #0,D0 MOVEC D0,VBR ; need to put return addr right at the start MOVE.L PmgrBase,A1 ; get pmgr locals MOVEA.L PowerCycleReg(A1),A2 ; Get powercycling register in A2 MOVE.L PwrCycRestore(A1),ResetPC; Set the PC to restore code when coming up MOVE.L SP,SaveCPUState.CPUISPsave(A1) ; save old SP PMOVE TC,SaveCPUState.CPUMMUTCsave(A1); Save TC PMOVE CRP,SaveCPUState.CPUMMUCRPsave(A1) ; Save CRP PMOVE TT1,SaveCPUState.CPUMMUTT1save(A1) ; Save TT1 PMOVE TT0,SaveCPUState.CPUMMUTT0save(A1) ; Save TT2 MOVE.L PmgrVarPhysPtr(A1),A3 ; Get the physical address of the PMgr Globasl <25> HJR LEA SaveCPUState(A3),A3 ; PwrCycGlobals-4 Byte below must be free for NMIStkFrm <25> HJR MOVE.L A3,ResetSP ; Save it in ResetSP for powering up <25> HJR MOVE.L PwrCycRestore(A1),AutoInt7 ; Set NMIVec to our PwrCycling NMI handler <24> HJR MOVE.L SccRd,A3 ; SCC may have data to get BTST #0,(A3) ; Is there any SCC Data BNE.S @DontCycle ; Yes.. Forget killing the power MOVE.L D0,([RomBase]) ; Clear the Bus. Yes I Know it's in ROM... <6> HJR MOVE.L D0,(A2) ; Pull the pin <6> HJR BRA.S * ; Wait for the drugs to take effect @DontCycle MOVEC A0,VBR ; Restore the VBR <37> HJR MOVE.L SaveCPUState.\ CPUNMIsave(A1),AutoInt7 ; Restore the NMI Vector <33> HJR ADDA.L #5*4,SP ; Restore stack state MOVEM.L (SP)+,PowerCycleRegs ; Restore them regs MOVE.W (SP)+,SR RTS ;——————————————————————————————————————————————————————————————————————— PowerCycle020 MOVE.W SR,-(SP) OR.W #HiIntMask,SR ; No more interrupts MOVEM.L PowerCycleRegs,-(SP) ; save all for now, Save only the important registers MOVEC CACR,D0 ; Now save cache regs OR.W #(1<<11)+(1<<3),D0 ; Flush caches upon return MOVEC CAAR,D1 ; MOVEC VBR,A0 ; Also save vector base MOVE.L USP,A1 ; and alternats stack pointers MOVEC MSP,A2 ; . MOVEM.L D0/D1/A0-A2,-(SP) ; Save some cool registers MOVEQ #0,D0 MOVEC D0,VBR ; need to put return addr right at the start MOVE.L PmgrBase,A1 ; get pmgr locals MOVEA.L PowerCycleReg(A1),A2 ; Get powercycling register in A1 MOVE.L PwrCycRestore(A1),ResetPC; Set the PC to restore code when coming up MOVE.L SP,ResetSP ; Save the stack pointer in ResetSP for power up MOVE.L PwrCycRestore(A1),AutoInt7; Set NMIVec to our PwrCycling NMI handler <24> HJR MOVE.L SccRd,A3 ; SCC may have data to get BTST #0,(A3) ; Is there any SCC Data BNE.S @DontCycle ; Yes.. Forget killing the power MOVE.L D0,([RomBase]) ; Clear the Bus. Yes I Know it's in ROM... <6> HJR MOVE.L D0,(A2) ; Pull the pin <6> HJR BRA.S * ; Wait for the drugs to take effect @DontCycle MOVEC A0,VBR ; Restore the VBR <37> HJR MOVE.L SaveCPUState.\ CPUNMIsave(A1),AutoInt7 ; Restore the NMI Vector <33> HJR ADDA.L #5*4,SP ; Restore stack state MOVEM.L (SP)+,PowerCycleRegs ; Restore them regs MOVE.W (SP)+,SR RTS ;——————————————————————————————————————————————————————————————————————— MACHINE MC68040 Restore040 @SaveSetRegs REG D0-D7/A3-A6 MOVEM.L (SP),@SaveSetRegs ; get some saved special regs MOVEC D0,URP ; restore User Root Pointer MOVEC D1,SRP ; restore Supervisor Root Pointer MOVEC D3,DTT0 ; restore Data TT0 MOVEC D4,DTT1 ; restore Data TT1 MOVEC D5,ITT0 ; restore Instruction TT0 MOVEC D6,ITT1 ; restore Instruction TT1 MOVEC A3,VBR ; restore VBR MOVEC A4,USP ; restore User Stack pointer MOVEC A5,MSP ; restore Master Stack pointer MOVEC A6,ISP ; restore Interrupt Stack pointer PFLUSHA ; flush ATC MOVEC D2,TC ; restore TC CINVA BC ; invalidate both caches MOVEC D7,CACR ; Restore Vital CPU registers MOVE.L (SP)+,AutoInt7(A3) ; Restore the NMI Vector MOVEM.L (SP)+,PowerCycleRegs ; Restore them regs MOVE.W (SP)+,SR RTS ;——————————————————————————————————————————————————————————————————————— MACHINE MC68030 Restore030 MOVE.L ResetSP,A0 ; Save the Power Manager globals in the stack <25> HJR MOVE.L CPUISPsave(A0),SP ; Restore the stack to the previous life PMOVE CPUMMUTT0save(A0),TT0 ; "Pop" the MMU regs of the video stack PMOVE CPUMMUTT1save(A0),TT1 PMOVE CPUMMUCRPsave(A0),CRP PMOVE CPUMMUTCsave(A0),TC ; MMU is now restored, we have memory MOVEM.L (SP)+,D0/D1/A0-A2 MOVEC D0,CACR ; Restore Vital CPU registers MOVEC D1,CAAR MOVEC A0,VBR MOVEC A1,USP MOVEC A2,MSP MOVE.L PMgrBase,A0 ; Get the Globals <24> HJR MOVE.L SaveCPUState.\ CPUNMIsave(A0),AutoInt7 ; Restore the NMI Vector <24> HJR MOVEM.L (SP)+,PowerCycleRegs ; Restore them regs MOVE.W (SP)+,SR RTS ;——————————————————————————————————————————————————————————————————————— Restore020 MOVEM.L (SP)+,D0/D1/A0-A2 ; restore cool registers MOVEC D0,CACR ; Restore Vital CPU registers MOVEC D1,CAAR MOVEC A0,VBR MOVE.L A1,USP MOVEC A2,MSP MOVE.L PMgrBase,A0 ; Get the Globals <25> HJR MOVE.L SaveCPUState.\ CPUNMIsave(A0),AutoInt7 ; Restore the NMI Vector <25> HJR MOVEM.L (SP)+,PowerCycleRegs ; Restore them regs MOVE.W (SP)+,SR RTS ENDWITH ;•••••••••••••••••••••••••••••••••••••••• Sleep •••••••••••••••••••••••••••••••••••••••••• ; ; The following are a set of possible routines that are used to save the state, or ; set necessary conditions in order for the machine to sleep. For each machine there ; is a corresponding table of entries which point to the necessary routines. The ; register convention is that A0/A1 must be PRESERVED throughout all routines. All ; other register can be trashed and should not be relied upon. ; ;———————————————————————————————————————————————————————————————————————————————————————— ;———————————————————————————————————————————————————————————————————————————————————————— ; _Sleep routine to power down hard drive (A1 = return address) ;———————————————————————————————————————————————————————————————————————————————————————— SleepHD MOVEA.L PMgrBase,A2 ; get pointer to globals BTST #HDPowerOn,PmgrFlags(A2); Clear the Hard Disk semaphor BEQ.S @SkipHDSpinDown ; If set, spin down the hard disk MOVE.L HDvector(A2),D0 ; Does the vector exist BEQ.S @SkipHDSpinDown ; Nope... Go on MOVEA.L D0,A2 ; Get vector to hard disk spin down task JSR (A2) ; Spin down hard disk @SkipHDSpinDown JMP (A1) ; and return ;———————————————————————————————————————————————————————————————————————————————————————— ; _Sleep routine to save the VIA1 registers (A1 = return address) ;———————————————————————————————————————————————————————————————————————————————————————— SaveVIA1 MOVEA.L VIA,A2 MOVE.B vBufB(A2),D0 ; load up all the VIA registers MOVE.B vDIRB(A2),D1 MOVE.B vBufA(A2),D2 MOVE.B vDIRA(A2),D3 MOVE.B vIER(A2),D4 ORI.B #(1<<7),D4 ; set the Via Set bit so that value will stick MOVE.B vACR(A2),D5 MOVE.B vPCR(A2),D6 MOVEM.W D0-D6,-(SP) ; save the VIA registers JMP (A1) ; and return ;———————————————————————————————————————————————————————————————————————————————————————— ; _Sleep routine to save the VIA2 registers (A1 = return address) ;———————————————————————————————————————————————————————————————————————————————————————— SaveVIA2 MOVEA.L VIA2,A2 MOVE.B vBufB(A2),D0 ; load up all the VIA registers MOVE.B vDIRB(A2),D1 MOVE.B vBufA(A2),D2 MOVE.B vDIRA(A2),D3 MOVE.B vIER(A2),D4 ORI.B #(1<<7),D4 ; set the Via Set bit so that value will stick MOVE.B vACR(A2),D5 MOVE.B vPCR(A2),D6 MOVEM.W D0-D6,-(SP) ; save the VIA registers BCLR.B #v2CDis2,vBufB(A2) ; lower the cache disable line to save power JMP (A1) ; and return IF hasNiagra THEN ;———————————————————————————————————————————————————————————————————————————————————————— ; Dartanian ASC/Batman save code (A1 = return address) ;———————————————————————————————————————————————————————————————————————————————————————— SaveASCBatmanDart ; MOVEA.L UnivInfoPtr,A2 ; point to the ProductInfo table, ADDA.L DecoderInfoPtr(A2),A2 ; then to the DecoderInfo table, MOVEA.L JAWSAddr(A2),A2 ; then to the Niagra base address, ADDA.L #NiagraGUR+PontiSndCtl,A2 ; and finally to the sound register ; BSET #PontiSndPWMOff,(A2) ; turn off power amps ENDIF ;———————————————————————————————————————————————————————————————————————————————————————— ; _Sleep routine to save the ASC and Batman registers (A1 = return address) ;———————————————————————————————————————————————————————————————————————————————————————— batmanSnd EQU $B0 ; version number for Batman Chip SaveASCBatman MOVEA.L ASCBase,A2 ; point to the base of the ASC/Batman MOVE.B ascMode(A2),-(SP) ; save the chip mode MOVE.B ascVolControl(A2),-(SP) ; save the external sound volume control MOVE.B $80A(A2),-(SP) ; save channel A play/record register CMPI.B #batmanSnd,ascVersion(A2) ; is this a Batman? BNE.S @NotBatman ; -> nope, that's all we save MOVE.B ascTestReg(A2),-(SP) ; save test control register MOVE.L A1,D2 ; save the return address MOVEQ.L #0,D1 ; reg D1 = channel offset (0=A,$20=B) @saveBatReg LEA $F09+1(A2),a1 ; save Batman channel control regs ($F00-$F09) ADDA.L D1,a1 ; add channel offset for A or B MOVEQ.L #10-1,D0 ; @saveBatC MOVE.B -(a1),-(SP) ; DBRA D0,@saveBatC ; LEA $F17+1(A2),a1 ; save CDXA coefficient regs ($F10-$F17) ADDA.L D1,a1 ; add channel offset for A or B MOVEQ.L #8-1,D0 ; @saveCDXA MOVE.B -(a1),-(SP) ; DBRA D0,@saveCDXA ; TST.L D1 ; channel A or B BNE.S @BatmanDone ; already saved both channels A and B MOVE.L #$20,D1 ; set channel offset to channel B BRA.S @saveBatReg ; save channel B registers @BatmanDone MOVEA.L D2,A1 ; restore the return address @NotBatman JMP (A1) ; and return ;———————————————————————————————————————————————————————————————————————————————————————— ; _Sleep routine to poweroff Sound (A1 = return address) ;———————————————————————————————————————————————————————————————————————————————————————— SoundPowerDown ; MOVEA.L UnivInfoPtr,A2 ; point to the ProductInfo table, ADDA.L DecoderInfoPtr(A2),A2 ; then to the DecoderInfo table, MOVEA.L JAWSAddr(A2),A2 ; then to the Niagra base address, ADDA.L #NiagraGUR+PontiSndCtl,A2 ; and finally to the sound register ; BSET #PontiSndLatchClr,(A2) ; pulse (positive) latch clear bit BCLR #PontiSndLatchClr,(A2) ; ; BCLR #PontiSndPwrOn,(A2) ; turn off sound power ; JMP (A1) ; and return ;———————————————————————————————————————————————————————————————————————————————————————— ; _Sleep routine to save the video registers (A1 = return address) ;———————————————————————————————————————————————————————————————————————————————————————— SaveLCD Moveq #0,D1 ; Say we’re about to sleep. Bsr SaveRestoreLCD ; Go save the world. Jmp (A1) ; Vamoose. ;———————————————————————————————————————————————————————————————————————————————————————— ; _Sleep routine to save the FPU registers (A1 = return address) ;———————————————————————————————————————————————————————————————————————————————————————— SaveFPU BTST #hwCbFPU,HWCfgFlags ; do we have an FPU? BEQ.S @NoFPU ; -> no FMOVEM.X FP0-FP7,-(SP) ; save general purpose registers FMOVEM.L FPCR/FPSR/FPIAR,-(SP) ; save FPU condition registers @NoFPU JMP (A1) ; and return ;———————————————————————————————————————————————————————————————————————————————————————— ; _Sleep routine to save the 68040 registers (A1 = return address) ;———————————————————————————————————————————————————————————————————————————————————————— SaveSlp040 @SaveSetRegs REG D0-D7/A3-A6 MACHINE MC68040 MOVEA.L PmgrBase,A2 ; get pointer to globals MOVEC CACR,D7 ; get current Cache Register MOVEQ #0,D0 ; clear register MOVEC D0,CACR ; disable caches CPUSHA BC ; flush both caches entirely ; Get CPU Internal State Registers MOVEC URP,D0 ; Save User Root Pointer MOVEC SRP,D1 ; Save Supervisor Root Pointer MOVEC TC,D2 ; Save TC MOVEC DTT0,D3 ; Save Data TT0 MOVEC DTT1,D4 ; Save Data TT1 MOVEC ITT0,D5 ; Save Instruction TT0 MOVEC ITT1,D6 ; Save Instruction TT1 MOVEC VBR,A3 ; Save vector base reg MOVEC USP,A4 ; Save User Stack Pointer MOVEC MSP,A5 ; Save Master Stack Pointer MOVEC ISP,A6 ; Save Interrupt Stack Pointer MOVEM.L @SaveSetRegs,SaveCPUState(A2) ; Save all them special regs JMP (A1) ; and return ;———————————————————————————————————————————————————————————————————————————————————————— ; _Sleep routine to save the 68030 registers (A1 = return address) ;———————————————————————————————————————————————————————————————————————————————————————— SaveSlp030 @SaveSetRegs REG D0-D5 MACHINE MC68030 MOVEA.L PmgrBase,A2 ; Get pointer to our globals PMOVE CRP,SaveCPUState.CPUMMUCRPsave(A2); Save CRP PMOVE TC,SaveCPUState.CPUMMUTCsave(A2) ; Save TC PMOVE TT0,SaveCPUState.CPUMMUTT0save(A2); Save TT2 PMOVE TT1,SaveCPUState.CPUMMUTT1save(A2); Save TT1 MOVEC VBR,D0 ; Also save vector base MOVEC USP,D1 ; and alternats stack pointers MOVEC MSP,D2 ; . MOVEC ISP,D3 ; and old sp MOVEC CACR,D4 ; Now save cache regs OR.W #(1<<11)+(1<<3),D4 ; Flush caches upon return MOVEC CAAR,D5 ; MOVEM.L @SaveSetRegs,\ SaveCPUState.CPUVBRsave(A2) ; Save some cool registers JMP (A1) ; and return ;———————————————————————————————————————————————————————————————————————————————————————— ; _Sleep routine to setup state info before going to sleep (A1 = return address) ;———————————————————————————————————————————————————————————————————————————————————————— SaveSleepInfo MOVEA.L PMgrBase,A2 ; Get pointer to our globals MOVE.L #SleepConst,SleepSaveFlag(A2) ; set a flag that sleep has been entered JMP (A1) ;———————————————————————————————————————————————————————————————————————————————————————— ; _Sleep routine to send a sleep command to the PMGR (A1 = return address) ;———————————————————————————————————————————————————————————————————————————————————————— IF hasMSC THEN SendSleepLP PEA 'BORG' ; low power sleep signature BCLR #LowPowerSleep,PMgrFlags2(A2) ; are we sleeping because of a low power condition? BNE.S HaveSig ; -> yes ADDQ.W #4,SP ; no, toss the low power signature ENDIF SendSleep MOVE.L A0,-(SP) ; save this dam thing PEA SleepSig ; put the sleep signature into a buffer HaveSig MOVE.L SP,-(SP) ; pmRBuffer MOVE.L (SP),-(SP) ; pmSBuffer MOVE.W #4,-(SP) ; pmLength = 4 MOVE.W #SleepReq,-(SP) ; pmCommand = sleep… MOVEA.L SP,A0 ; point to the parameter block _PMgrOp ; send the sleep command LEA pmRBuffer+4+4(SP),SP ; clean up the stack MOVEA.L (SP)+,A0 ; restore it JMP (A1) ; and return ;•••••••••••••••••••••••••••••••••••••••• Wakeup ••••••••••••••••••••••••••••••••••••••••• ; ; The following are a set of possible routines that are used to restore the state, or ; set necessary conditions in order for the machine to wake from sleep. For each machine ; there is a corresponding table of entries which point to the necessary routines. The ; register convention is that A0/A1 must be PRESERVED throughout all routines. All ; other register can be trashed and should not be relied upon. ; ;———————————————————————————————————————————————————————————————————————————————————————— ;———————————————————————————————————————————————————————————————————————————————————————— ; Wakeup routine to restore the MMU registers for '040 (A1 = return address) ;———————————————————————————————————————————————————————————————————————————————————————— RestoreSlp040 @RestoreSetRegs REG D0-D7/A3-A6 MACHINE MC68040 MOVEA.L PmgrBase,A2 ; get pointer to our Globals MOVEM.L SaveCPUState(A2),@RestoreSetRegs; get some saved special regs MOVEC D0,URP ; restore User Root Pointer MOVEC D1,SRP ; restore Supervisor Root Pointer MOVEC D3,DTT0 ; restore Data TT0 MOVEC D4,DTT1 ; restore Data TT1 MOVEC D5,ITT0 ; restore Instruction TT0 MOVEC D6,ITT1 ; restore Instruction TT1 MOVEC A3,VBR ; restore VBR MOVEC A4,USP ; restore User Stack pointer MOVEC A5,MSP ; restore Master Stack pointer MOVEC A6,ISP ; restore Interrupt Stack pointer PFLUSHA ; flush ATC MOVEC D2,TC ; restore TC CINVA BC ; invalidate both caches MOVEC D7,CACR ; Restore Vital CPU registers MOVEQ #0,D7 ; Clear register for WakeRoutine to work JMP (A1) ; return ;———————————————————————————————————————————————————————————————————————————————————————— ; Wakeup routine to restore the MMU registers for '030 (A1 = return address) ;———————————————————————————————————————————————————————————————————————————————————————— RestoreSlp030 @RestoreSetRegs REG D0-D5 MACHINE MC68030 MOVEA.L PmgrBase,A2 ; get pointer to our Globals MOVEM.L SaveCPUState.CPUVBRsave(A2),\ @RestoreSetRegs ; get some special registers MOVEC D0,VBR ; restore the Vector Base Register MOVEC D1,USP ; restore the User Stack Pointer MOVEC D2,MSP ; restore the Master Stack Pointer MOVEC D3,ISP ; restore the Interrupt Stack Pointer PMOVE SaveCPUState.CPUMMUCRPsave(A2),\; CRP ; restore CRP PMOVE SaveCPUState.CPUMMUTT1save(A2),\; TT1 ; restore TT1 PMOVE SaveCPUState.CPUMMUTT0save(A2),\; TT0 ; restore TT2 PFLUSHA ; flush ATC PMOVE SaveCPUState.CPUMMUTCsave(A2),\ ; TC ; restore TC and turn on MMU MOVEC D4,CACR ; restore the Cache register MOVEC D5,CAAR ; restore the Cache Address register JMP (A1) ; and return ;———————————————————————————————————————————————————————————————————————————————————————— ; Wakeup routine to restore the FPU registers (A1 = return address) ;———————————————————————————————————————————————————————————————————————————————————————— RestoreFPU BTST #hwCbFPU,HWCfgFlags ; do we have an FPU? BEQ.S @NoFPU ; -> no FMOVEM.L (SP)+,FPCR/FPSR/FPIAR ; restore FPU condition registers FMOVEM.X (SP)+,FP0-FP7 ; restore general purpose registers @NoFPU JMP (A1) ; and return ;———————————————————————————————————————————————————————————————————————————————————————— ; Wakeup routine to save the video registers (A1 = return address) ;———————————————————————————————————————————————————————————————————————————————————————— RestoreLCD MOVEQ #1,D1 ; Say we’re about to sleep. BSR SaveRestoreLCD ; Go save the world. JMP (A1) ; Vamoose. ;———————————————————————————————————————————————————————————————————————————————————————— ; Wakeup routine to restore the ASC and Batman registers (A1 = return address) ; ; These registers must be restored in the given order. If the CDXA registers ; are restored in the incorrect order Batman will be confused and not function ; appropriately. Also if the FIFO control register is set prior to the SRC ; Time Increment it will not function properly. ;———————————————————————————————————————————————————————————————————————————————————————— RestoreASCBatman MOVE.L ASCBase,A2 CMPI.B #batmanSnd,ascVersion(A2) ; is this a Batman? BNE.S @NotBatman ; -> nope, that's all we save MOVE.L A1,D2 ; save the return address MOVEQ #$20,D1 ; reg D1 = channel offset (0=A,$20=B) @restBatReg LEA $F10(a2),a1 ; restore CDXA coefficient regs ($F10-$F17) ADDA.L D1,a1 ; add channel offset for A or B MOVEQ #8-1,D0 ; @restCDXA MOVE.B (SP)+,(a1)+ ; DBRA D0,@restCDXA ; LEA $F00(a2),a1 ; restore Batman channel control regs ($F00-$F09) ADDA.L D1,a1 ; add channel offset for A or B MOVEQ #10-1,D0 ; @restBatC MOVE.B (SP)+,(a1)+ ; DBRA D0,@restBatC ; TST.L D1 ; Are we done with both channels BEQ.S @restCont ; Yes??? Go on... MOVEQ #0,D1 ; Set Channel A Offset BRA.S @restBatReg ; restore channel B registers @restCont MOVE.B (SP)+,ascTestReg(a2) ; restore test control register MOVEA.L D2,A1 ; restore return address @NotBatman MOVE.B (SP)+,$80A(A2) ; restore Channel A play/record register MOVE.B (SP)+,ascVolControl(A2) ; restore external sound volume control MOVE.B (SP)+,ascMode(A2) ; restore chip mode JMP (A1) ;———————————————————————————————————————————————————————————————————————————————————————— ; Reenable Sound amps (A1 = return address) ;———————————————————————————————————————————————————————————————————————————————————————— | RestoreDartAmp ; V MOVEA.L UnivInfoPtr,A0 ; point to the ProductInfo table, ADDA.L DecoderInfoPtr(A0),A0 ; then to the DecoderInfo table, MOVEA.L JAWSAddr(A0),A0 ; then to the Niagra base address, ADDA.L #NiagraGUR+PontiSndCtl,A0 ; and finally to the sound register ; | BSET #PontiSndLatchClr,(A0) ; pulse (positive) latch clear bit BCLR #PontiSndLatchClr,(A0) ; ; BCLR #PontiSndPwrOn,(A0) ; turn off sound power ; | BCLR #PontiSndPWMOff,(A0) ; turn on power amps V JMP (A1) ; done ;———————————————————————————————————————————————————————————————————————————————————————— ; Wakeup routine to restore misc PmgrVars (A1 = return address) ;———————————————————————————————————————————————————————————————————————————————————————— RestorePmgrMisc MOVE.L A1,-(SP) ; push return address on stack CLR.L -(SP) ; zero the buffer MOVE.L SP,-(SP) ; pmRBuffer MOVE.L (SP),-(SP) ; pmSBuffer CLR.W -(SP) ; pmLength = 0 MOVE.W #batteryRead,-(SP) ; pmCommand = batteryRead MOVEA.L SP,A0 ; point to the parameter block _PMgrOp ; send the command MOVEA.L pmRBuffer(A0),A2 ; get pointer to receive buffer MOVEA.L PmgrBase,a0 ; get pointer to globals MOVE.B (A2),Charger(A0) ; Initialize charger state LEA pmBlkSize(SP),SP ; Remove stack frame RTS ; that's all folks ;———————————————————————————————————————————————————————————————————————————————————————— ; Wakeup routine to restore the VIA1 registers (A1 = return address) ;———————————————————————————————————————————————————————————————————————————————————————— RestoreVIA1 MOVEA.L VIA,A2 MOVEM.W (SP)+,D0-D6 ; get the saved registers off the stack MOVE.B D0,vBufB(A2) ; and stuff them back into the VIA MOVE.B D1,vDIRB(A2) MOVE.B D2,vBufA(A2) MOVE.B D3,vDIRA(A2) MOVE.B D4,vIER(A2) MOVE.B D5,vACR(A2) MOVE.B D6,vPCR(A2) MOVE.B vT2CH(A2),vT2CH(A2) ; restart the timer MOVE.B vT1CH(A2),vT1CH(A2) JMP (A1) ;———————————————————————————————————————————————————————————————————————————————————————— ; Wakeup routine to restore the VIA2 registers (A1 = return address) ;———————————————————————————————————————————————————————————————————————————————————————— RestoreVIA2 MOVEA.L VIA2,A2 MOVEM.W (SP)+,D0-D6 ; get the saved registers off the stack MOVE.B D0,vBufB(A2) ; and stuff them back into the VIA MOVE.B D1,vDIRB(A2) MOVE.B D2,vBufA(A2) MOVE.B D3,vDIRA(A2) MOVE.B D4,vIER(A2) MOVE.B D5,vACR(A2) MOVE.B D6,vPCR(A2) JMP (A1) ;———————————————————————————————————————————————————————————————————————————————————————— ; Wakeup routine for clearing interrupts (A1 = return address) ;———————————————————————————————————————————————————————————————————————————————————————— WakeClrInts MOVEM.L A0-A1,-(SP) ; save very important registers MOVEA.L VIA,A0 ; get VIA base address MOVE.B #(1< TST.B WWExist ; is the Window Manager initialized? BNE.S @NoWW ; -> no, just leave the screen alone SUBQ.W #4,SP ; Get some space on the stack <13> MOVE.L SP,-(SP) ; Push pointer for GrafPort <13> _GetPort ; Get our current port <13> _RedrawAll ; CheckUpdate on all layers _SetPort ; Restore port to original <13> @NoWW RTS ; That's all folks ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: SaveRestoreLCD (SleepLCD/RestoreLCD) ; ; Inputs: D1 - 0 -> SaveLCD, non-zero -> RestoreLCD. ; ; Outputs: None ; ; Trashes: D0 ; ; Function: This routine uses the VideoInfo record to tell it the DrHwID of the driver ; it is supposed to use in order to save the state of the LCD controller/panel. ; ; Notes: The SleepWake call in the video driver is required to do all that is necessary ; to save the state of LCD video controller so that it may be restored correctly ; upon wake. Also, the SleepWake call should paint the screen gray itself (because ; VRAM will have been non-refreshed during sleep) due to the fact that our having ; to make an addition control call from here could potentially be quite ; aesthetically unpleasing. ;———————————————————————————————————————————————————————————————————————————————————————— With SpBlock SaveRestoreLCD Move.l A0,-(SP) ; Save that register Movea.l UnivInfoPtr,A0 ; Point to the ProductInfo record. Adda.l VideoInfoPtr(A0),A0 ; Point to the VideoInfo record. Move.w DrvrHwID(A0),D0 ; If the DrvrHwID is zero, Beq.s @Done ; then just leave. Suba.w #SpBlockSize,Sp ; Allocate an SpBlock on the stack. Movea.l Sp,A0 ; Point to it with A0. Clr.b spSlot(A0) ; We’re only looking in Slot $0 here. Clr.b spID(A0) ; Begin at sRsrcID 0. Clr.b spExtDev(A0) ; No external device. Clr.b spTBMask(A0) ; No mask in search. Move.w #catDisplay,spCategory(A0) ; Look for: Display, Move.w #typVideo,spCType(A0) ; Video, Move.w #drSwApple,spDrvrSW(A0) ; Apple, Move.w D0,spDrvrHw(A0) ; . Clr.l spParamData(A0) ; Look only for enabled sRsrcs. Bset #foneslot,spParamData+3(A0) ; Limit search to this slot only. _GetTypeSRsrc ; If we didn’t find the target, Bne.s @Cleanup ; then just leave. Move.w spRefNum(A0),D0 ; If there’s no driver, then Beq.s @Cleanup ; just go on. Suba.w #ioQElSize,Sp ; Allocate ioCore pBlock. Movea.l Sp,A0 ; Set up for the control call: Move.w D0,ioRefNum(A0) ; ioRefNum, Move.w #cscSleepWake,csCode(A0) ; csCode, Move.b D1,-(Sp) ; csParam, Move.l Sp,csParam(A0) ; csParamPtr, _Control ,Immed ; SleepWake. Tst.b (Sp)+ ; Remove param from stack. Adda.w #ioQElSize,Sp ; Deallocate ioCore pBlock. @Cleanup Adda.w #SpBlockSize,Sp ; Deallocate SpBlock. @Done Movea.l (SP)+,A0 ; restore that register Rts Endwith ;_PaletteDispatch OPWORD $AAA2 ; PaletteDispatch is NOT defined in this ROM. ;selectSetDepth EQU $0A13 ; SetDepth -> $0A is paramsize, $13 is the selector. SetScreenBrightness EQU $4301 GetScreenBrightness EQU $5301 ; DimLevel EQU 1 ; VidStkFrame RECORD 0, DECR VidStkCntBlk DS.B IOVQElSize ; control call parm block VidStkVDPageInfo DS.B VDPageInfo ; params VidStkFrameSize EQU * ; size of frame ENDR ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: ScreenWakeInit | ; v ; Inputs: none ; ; Outputs: none ; ; Trashes: D0 ; ; Function: SetDepth in order to set the driver in appropriate state and to tell Quickdraw ; to rebuild the world. ;———————————————————————————————————————————————————————————————————————————————————————— ScreenWakeInit ScreenWakeInitRegs REG D1-D2/A0-A4 WITH VidStkFrame,VDPageInfo LINK A6,#VidStkFrameSize ; allocate variable space MOVEM.L ScreenWakeInitRegs,-(SP) ; save them regs LEA VidStkCntBlk(A6),A3 ; get pointer to IOPB LEA VidStkVDPageInfo(A6),A4 ; get pointer to csParams MOVE.L A4,csParam(A3) ; set csParams MOVE.W #cscPowerSelect,csCode(A3) ; cscPowerSelect MOVE.L DeviceList,D1 ; If the DeviceList is empty, BEQ.S @ScreenWakeInitDone ; then we can’t do anything! MOVEQ #0,D2 ; Initialize PowerSelect supported flag @ScreenWakeInitLp ; DO { MOVEA.L D1,A2 ; Get gDevice Handle MOVEA.L (A2),A2 ; Dereference the handle MOVEQ #0,D0 ; Clear both halves of D0.l. MOVE.W gdRefNum(A2),D0 ; Get driver refnum. BEQ.S @NoSetDepth ; If not non-zero, skip. MOVEA.L A3,A0 ; A0 := IOPB ORI.W #-1,D2 ; Set Flag MOVE.L D1,-(SP) ; Push gdHandle MOVE.W gdMode+2(A2),-(SP) ; Push the depth CLR.L gdMode(A2) ; Clear it so stupid trap doesn't freak out CLR.L -(SP) ; Clear all flags MOVE.W #selectSetDepth,D0 ; Set selector to set depth _PaletteDispatch ; Call it @NoSetDepth MOVE.L gdNextGD(A2),D1 ; Device := NextDevice BNE.S @ScreenWakeInitLp ; } While !EndOfDeviceLoop @ScreenWakeInitDone MOVEM.L (SP)+,ScreenWakeInitRegs ; restore reg UNLK A6 ; release stack frame RTS ; that's all folks ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: VidLowPwrSelect | ; v ; Inputs: D0 0 = PowerOn , nonzero = PowerDown ; ; Outputs: none ; ; Trashes: D0 ; ; Function: Call the video driver to set the power state to the appropriate position. ; In the case of power up, power up the hardware and call SetDepth in order to ; set the driver in appropriate state and to tell Quickdraw to rebuild the ; world. ;———————————————————————————————————————————————————————————————————————————————————————— VidLowPwrSelect VidLowPwrSelectRegs REG D1-D2/A0-A4 WITH VidStkFrame,VDPageInfo LINK A6,#VidStkFrameSize ; allocate variable space MOVEM.L VidLowPwrSelectRegs,-(SP) ; save them regs LEA VidStkCntBlk(A6),A3 ; get pointer to IOPB LEA VidStkVDPageInfo(A6),A4 ; get pointer to csParams MOVE.L A4,csParam(A3) ; set csParams Move.l #powerSelSig,csParam+4(A3) ; (For Radius, csParams is not a pointer in some cases.) MOVE.W #cscPowerSelect,csCode(A3) ; cscPowerSelect MOVE.L DeviceList,D1 ; If the DeviceList is empty, Beq.s @VidLowPwrSelectDone ; then we can’t do anything! MOVEQ #0,D2 ; Initialize PowerSelect supported flag TST.L D0 ; IF PowerDown THEN BNE @VidPowerDown ; branch… @PwrUpgDevLoop ; DO { MOVEA.L D1,A2 ; Get gDevice Handle MOVEA.L (A2),A2 ; Dereference the handle Moveq #0,D0 ; Clear both halves of D0.l. MOVE.W gdRefNum(A2),D0 ; Get driver refnum. Beq.s @NoSetDepth ; If not non-zero, skip. MOVEA.L A3,A0 ; A0 := IOPB Bsr PowerSelectSupported ; IF cscPowerSelect NOT supported THEN Beq.s @NoSetDepth ; just go on. Move.w D0,ioRefNum(A0) ; Set driver refnum CLR.W csMODE(A4) ; Set PowerUpMode in csMode _Control ,IMMED ; Power Up Device BNE.S @NoSetDepth ; IF cscPowerSelect supported THEN ORI.W #-1,D2 ; Set Flag MOVE.L D1,-(SP) ; Push gdHandle MOVE.W gdMode+2(A2),-(SP) ; Push the depth CLR.L gdMode(A2) ; Clear it so stupid trap doesn't freak out CLR.L -(SP) ; Clear all flags MOVE.W #selectSetDepth,D0 ; Set selector to set depth _PaletteDispatch ; Call it @NoSetDepth MOVE.L gdNextGD(A2),D1 ; Device := NextDevice BNE.S @PwrUpgDevLoop ; } While !EndOfDeviceLoop TST.W D2 ; IF SetDepthCalled THEN BEQ.S @NoRestore ; TST.B WWExist ; is the Window Manager initialized? BNE.S @NoRestore ; -> no, just leave the screen alone SUBQ.W #4,SP ; Get some space on the stack MOVE.L SP,-(SP) ; Push pointer for GrafPort _GetPort ; Get our current port _RedrawAll ; CheckUpdate on all layers _SetPort ; Restore port to original @NoRestore _ShowCursor ; Show Cursor @VidLowPwrSelectDone MOVEM.L (SP)+,VidLowPwrSelectRegs ; restore reg UNLK A6 ; release stack frame RTS ; that's all folks ; Power-Down Video Devices ; Set up IOPB for control call @VidPowerDown Moveq #0,D2 ; Assume that we’re not hiding the cursor. MOVEA.L A3,A0 ; A0 := IOPB @PwrDowngDevLoop ; DO { MOVEA.L D1,A2 ; Get gDevice Handle MOVEA.L (A2),A2 ; Dereference the handle Moveq #0,D0 ; Clear both halve of D0.l. Move.w gdRefNum(A2),D0 ; Get driver refNum Beq.s @NextGD ; If not non-zero, skip. Bsr PowerSelectSupported ; If cscPowerSelect NOT supported THEN Beq.s @NextGD ; just go on Move.w D0,ioRefNum(A0) ; Set driver refnum MOVE.W #-1,csMODE(A4) ; Set PowerDown in csMode _Control ,IMMED ; Power Down Device Bne.s @NextGD ; If failed, keep going. Moveq #-1,D2 ; Otherwise, hiding the cursor is okay. @NextGD MOVE.L gdNextGD(A2),D1 ; Device := NextDevice BNE.S @PwrDowngDevLoop ; } While !EndOfDeviceLoop ; Kill the BackLight. LEA DBLiteBackLite,A2 ; Get pointer to Backlight driver name. | MOVE.L A2,ioFileName(A0) ; Load it. v _Open ; Open driver to get the refNum. BNE.S @NoDriver ; IF Open==Successful THEN MOVE.W #GetScreenBrightness,csCode(A0) ; get current brightness level _Status ,IMMED ; CMP.W #DimLevel,csParam(a0) ; only dim if level is greater than dim value BLE.S @NoDriver ; level is already lower than dimming value MOVE.W #SetScreenBrightness,csCode(A0) ; turn off screen brightness MOVE.W #DimLevel,csParam(A0) ; set brightness level _Control ,IMMED ; do it Bne.s @NoDriver ; don't hide cursor if failed Ori.w #-1,D2 ; otherwise, flag that we can hide the cursor @NoDriver Tst.l D2 ; If we shouldn’t hide the cursor, Beq.s @VidLowPwrSelectDone ; then just go on. _HideCursor ; Otherwise, hide it. BRA.S @VidLowPwrSelectDone ; And go on. ENDWITH STRING Pascal DBLiteBackLite DC.B '.Backlight' ; Name of Backlight Driver for DBLite. ALIGN 2 VSCDrvrName Dc.b '.VSC_Video' ; Name of Mini/Duo Dock Video Driver. Align 2 KeyStoneDrvrName Dc.b '.Display_Video_Apple_ViSC' ; Name of Dart-class version of the VSC Video Driver. Align 2 STRING Asis ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: PowerSelectSupported ; ; Inputs: D0.l - refNum of a video driver ; A0.l - pointer to video driver control/status IOPB for cscPowerSelect ; ; Outputs: D0.w - if non-zero, is video driver’s refNum and PowerSelect is ; supported by the video driver. ; ; Trashes: D0 ; ; Function: Returns whether or not a particular video driver supports the cscPowerSelect ; call. Note that for the original two Apple video drivers that supported ; this call, we just do a name check. From now on, though, we call the ; corresponding status call. If we get back an error, we assume that the ; call is not supported. Otherwise, we check the csData field for the ; appropriate signature to be sure that cscPowerSelect is really supported. ;———————————————————————————————————————————————————————————————————————————————————————— PowerSelectSupported Move.w D0,-(Sp) ; Save the refNum for later. Beq @ExitNow ; If nil, then just leave. ; Get the video driver’s name… ; Movem.l A0-A1/D7,-(Sp) ; Save some work registers. Not.w D0 ; Convert the refNum into… Lsl.w #2,D0 ; …a UTable index. Add.l UTableBase,D0 ; Get a pointer to the AuxDCEHandle. Movea.l D0,A0 ; Get it into A0. Movea.l (A0),A0 ; Get the AuxDCEHandle. Movea.l (A0),A0 ; Get the AuxDCEPtr. Move.w dCtlFlags(A0),D0 ; Get the device flags. Movea.l dCtlDriver(A0),A0 ; Get the driver. Btst #dRAMBased,D0 ; If it’s not in RAM, then Beq.s @GetName ; it’s already a pointer. Movea.l (A0),A0 ; Otherwise, get pointer to driver. @GetName Lea drvrName(A0),A0 ; Point to the driver name. Move.l A0,D7 ; Save it for later. ; Check for the Mini/Duo Dock Video Driver… ; Moveq #0,D0 ; Clear both halves of D0.l. Move.b (A0)+,D0 ; Get the length of the driver name. Swap D0 ; Save it. Lea VSCDrvrName,A1 ; Point to the Mini/Duo Dock Video Driver’s name. Move.b (A1)+,D0 ; Get the length of the driver name. _CmpString ; Compare the two names. Tst.w D0 ; If they match, Beq.s @Supported ; then just go on. ; Check for the Dart-class version of the VSC Video Driver. ; Movea.l D7,A0 ; Re-point to the driver we’re checking. Moveq #0,D0 ; Clear both halves of D0. Move.b (A0)+,D0 ; Get the length of the driver name. Swap D0 ; Save it. Lea KeyStoneDrvrName,A1 ; Point to the Dart-class version of the VSC Video Driver. Move.b (A1)+,D0 ; Get the length of the driver name. _CmpString ; Compare the two names. Tst.w D0 ; If they match, Beq.s @Supported ; then just go on. ; Now, generically check the driver by making the cscPowerSelect status call… ; With VDPageInfo Movem.l (Sp)+,A0-A1/D7 ; Restore work registers. Move.l A0,D0 ; Save A0 (IOPB ptr) for now. Movea.l csParam(A0),A0 ; Point to csParams data. Clr.l csData(A0) ; Clear the verification field. Movea.l D0,A0 ; Restore A0. Move.w (Sp),ioRefNum(A0) ; Get the driver refNum. _Status ,Immed ; If cscPowerSelect is not supported, Bne.s @NotThere ; then just say so. Move.l A0,-(Sp) ; Save A0. Movea.l csParam(A0),A0 ; Point to the csParams data. Cmpi.l #powerSelSig,csData(A0) ; Check for cscPowerSelect. Movea.l (Sp)+,A0 ; Restore A0. Beq.s @Supported1 ; If cscPowerSelect is supported, then just go on. @NotThere Addq #2,Sp ; Strip refNum off the stack. Moveq #0,D0 ; Say that cscPowerSelect is not supported. Rts ; And leave. Endwith ; Clean up and go home… ; @Supported Movem.l (Sp)+,A0-A1/D7 ; Restore the work registers. @Supported1 Move.w (Sp)+,D0 ; Get the refNum back into D0. @ExitNow Rts ; Return to caller. ;•••••••••••••••••••••••••••••••••••• Battery Stuff ••••••••••••••••••••••••••••••••••••• ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: GetLevel ; ; Inputs: A2 - pointer to Power Manager globals ; ; Outputs: D0 - normalized battery level (-1 to 4) ; CCR - BNE if a valid battery level is being returned ; ; Trashes: D0-D3, A0 ; ; Function: Reads the battery power level from the Power Manager and adds this value into ; an eight value circular queue. If the queue is full then an average of these ; values is calculated. Next, the averaged value is compared with the warning ; and cutoff values in PRAM to determine if the average is greater then reserve ; or what fractional part of reserve (1, 3/4, 1/2, or 1/4). ;———————————————————————————————————————————————————————————————————————————————————————— GetLevel CLR.L -(SP) ; make space for a buffer MOVE.L SP,-(SP) ; pmRBuffer MOVE.L (SP),-(SP) ; pmSBuffer CLR.W -(SP) ; pmLength = 0 MOVE.W #batteryNow,-(SP) ; pmCommand = get battery data MOVEA.L SP,A0 ; point to the parameter block _PMgrOp ; send the command LEA pmRBuffer+4(SP),SP ; toss the parameter block MOVEA.L SP,A0 ; point to the buffer MOVEQ #0,D3 MOVE.B (A0)+,Charger(A2) ; get the charger state MOVE.B (A0),D3 ; and the battery level ADDQ.W #4,SP ; toss the stack frame BTST #ChrgState,Charger(A2) ; get the charger state (connected/disconnected) BEQ.S @ChargeSame ; -> no change in charger state ST TOdirtyFlag(A2) ; Flag TOs need updating @ChargeSame MOVEQ #0,D0 MOVE.B BatQIndex(A2),D0 ; get the next index into the circular queue SUBQ.B #1,D0 BPL.S @IndexOK ; -> index is still valid MOVEQ #7,D0 ; it wrapped, so reset it @IndexOK MOVE.B D0,BatQIndex(A2) ; save the new index MOVE.B D3,BatQ(A2,D0.W) ; put the latest battery level into queue TST.B BatQ(A2) ; wait for full queue before averaging BEQ.S GetLevel MOVEQ #0,D3 MOVE.B BatQ(A2),D0 ; calculate the average of the last 8 samples ADD.W D0,D3 MOVE.B BatQ+1(A2),D0 ADD.W D0,D3 MOVE.B BatQ+2(A2),D0 ADD.W D0,D3 MOVE.B BatQ+3(A2),D0 ADD.W D0,D3 MOVE.B BatQ+4(A2),D0 ADD.W D0,D3 MOVE.B BatQ+5(A2),D0 ADD.W D0,D3 MOVE.B BatQ+6(A2),D0 ADD.W D0,D3 MOVE.B BatQ+7(A2),D0 ADD.W D0,D3 LSR.W #1,D3 ; D3 = 4*avg MOVEQ #0,D2 MOVE.B LowWarn(A2),D2 ; D2 = low warning level MOVEQ #0,D1 MOVE.B CutOff(A2),D1 ; D1 = cutoff level SUB.W D1,D2 ; D2 = warning - cutoff ASL.W #2,D1 ; D1 = 4*cutoff MOVEQ #4,D0 ; level 4 ADD.W D2,D1 ; D1 = 4*cutoff + 1(warning - cutoff) CMP.W D1,D3 ; power ≤ 1/4 reserve BLS.S @foundlevel MOVEQ #3,D0 ; level 3 ADD.W D2,D1 ; D1 = 4*cutoff + 2(warning - cutoff) CMP.W D1,D3 ; power ≤ 1/2 reserve BLS.S @foundlevel MOVEQ #2,D0 ; level 2 ADD.W D2,D1 ; D1 = 4*cutoff + 3(warning - cutoff) CMP.W D1,D3 ; power ≤ 3/4 reserve BLS.S @foundlevel MOVEQ #1,D0 ; level 1 ADD.W D2,D1 ; D1 = 4*cutoff + 4(warning - cutoff) CMP.W D1,D3 ; power ≤ reserve BLS.S @foundlevel MOVEQ #0,D0 ; level 0 MOVE.W Hysteresis(A2),D2 ; D2 = hysteresis ASL.W #2,D2 ; D2 = 4 * hysteresis ADD.W D2,D1 ; D1 = 4*cutoff + 4(warning - cutoff) + 4*hysteresis CMP.W D1,D3 ; power ≤ reserve + hysteresis BLS.S @foundlevel MOVEQ #-1,D0 ; power > reserve + hysteresis @foundlevel LSR.W #2,D3 ; D3 = average MOVE.B D3,BatAvg(A2) ; save it TST.B BatQ(A2) ; set data valid condition RTS ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: GetExtLevel ; ; Inputs: A2 - pointer to Power Manager globals ; ; Outputs: D0 - normalized battery level (-1 to 4) ; CCR - BNE if a valid battery level is being returned ; ; Trashes: D0-D3, A0 ; ; Function: Reads the extended battery power level from the Power Manager (which is ; pre-averaged by the PMGR). Next, the value is compared with the warning ; and cutoff values in PRAM to determine if the average is greater then ; reserve or what fractional part of reserve (1, 2/3, or 1/3). ; ; Note: The extended battery status command supports a word-sized battery ; level. Currently the PMGR on DBLite uses a 9-bit A/D to sample ; the battery voltage. ; ; The battery warning levels returned will be zero if no battery is ; currently connected. ;———————————————————————————————————————————————————————————————————————————————————————— GetExtLevel CLR.W -(SP) ; setup a buffer for the result MOVE.L SP,-(SP) ; pmRBuffer MOVE.L (SP),-(SP) ; pmSBuffer CLR.W -(SP) ; pmLength = 0 MOVE.W #readBattWarning,-(SP) ; pmCommand = get warning/cutoff levels MOVEA.L SP,A0 ; point to the parameter block _PMgrOp ; go get them LEA pmRBuffer+4(SP),SP ; toss the parameter block MOVE.W (SP)+,LowWarn(A2) ; save the new warning/cutoff levels CLR.L -(SP) CLR.L -(SP) MOVE.L SP,-(SP) ; pmRBuffer MOVE.L (SP),-(SP) ; pmSBuffer CLR.W -(SP) ; pmLength = 0 MOVE.W #readExtBatt,-(SP) ; pmCommand = get extended battery data MOVEA.L SP,A0 ; point to the parameter block _PMgrOp ; send the command LEA pmRBuffer+4(SP),SP ; toss the parameter block MOVEA.L SP,A0 ; point to the buffer MOVEQ #%00001011,D0 ; mask off the bits we want in the flags byte, AND.B (A0),D0 MOVE.B Charger(A2),D1 ; get the old charger state, MOVE.B D0,Charger(A2) ; and save the new charger state MOVE.B (A0)+,D0 ; get the unmodified flags EOR.B D0,D1 ; has the charger state changed since last time? BTST #HasCharger,D1 ; BEQ.S @ChargeSame ; -> no ST TOdirtyFlag(A2) ; yes, flag that timeouts need updating @ChargeSame MOVE.W (A0),D3 LSR.W #1,D3 MOVE.B D3,BatAvg(A2) ; save the 8-bit battery voltage ADD.W (A0)+,D3 ; D3 = 3*(8 bit battery voltage) ADDQ.W #8,SP ; toss the buffer BTST #HasCharger,D0 ; is a charger connected? BNE.S @HaveCharger ; -> yes, we don't care about the battery level BTST #2,D0 ; is a battery connected? BEQ.S @HaveCharger ; -> no, don't bother with battery level MOVEQ #0,D2 ; MOVE.B LowWarn(A2),D2 ; D2 = low warning level | MOVEQ #0,D1 ; V MOVE.B CutOff(A2),D1 ; D1 = cutoff level SUB.W D1,D2 ; D2 = warning - cutoff MOVE.W D1,D0 ADD.W D0,D1 ADD.W D0,D1 ; D1 = 3*cutoff MOVEQ #4,D0 ; level 4 ADD.W D2,D1 ; D1 = 3*cutoff + 1(warning - cutoff) CMP.W D1,D3 ; power ≤ 1/3 reserve BLS.S @foundlevel MOVEQ #3,D0 ; level 3 ADD.W D2,D1 ; D1 = 3*cutoff + 2(warning - cutoff) CMP.W D1,D3 ; power ≤ 2/3 reserve BLS.S @foundlevel ; (level 2 is not used, so we don't return it) MOVEQ #1,D0 ; level 1 ADD.W D2,D1 ; D1 = 3*cutoff + 3(warning - cutoff) CMP.W D1,D3 ; power ≤ reserve BLS.S @foundlevel MOVEQ #0,D0 ; level 0 MOVE.W Hysteresis(A2),D2 ; D2 = hysteresis ADD.W D2,D1 ADD.W D2,D1 ADD.W D2,D1 ; D1 = 3*cutoff + 3(warning - cutoff) + 3*hysteresis CMP.W D1,D3 ; power ≤ 1.0 reserve + hysteresis BLS.S @foundlevel @HaveCharger MOVEQ #-1,D0 ; return a "charged" battery level in case someone cares @foundlevel MOVEQ #1,D1 ; always return BNE for data valid RTS ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: LeadScaledBatteryInfo (PowerDispatch selector #9) ; ; Inputs: A2 - pointer to Power Manager globals ; ; Outputs: D0 - bits 31: 1=battery installed ; 30: 1=battery is charging ; 29: 1=charger connected ; 23-16: warning level (0-255) ; 15: don't use this one (needs to be set if the selector isn't implemented) ; 7- 0: battery level (0-255) ; ; Trashes: D1, D2, A0 ; ; Function: Returns scaled warning and battery levels, plus a couple of flags for ; portables that support sealed lead acid batteries. ;———————————————————————————————————————————————————————————————————————————————————————— LeadScaledBatteryInfo MOVEQ #3,D0 ; mask off the battery charging and charger installed bits AND.B charger(a2),D0 ; MOVEQ #0,D2 ; get the maximum battery voltage MOVE.B @MaxVolts(D0),D2 SUB.B CutOff(A2),D2 ; ADDQ.B #1<<2,D0 ; set the battery installed bit ROR.W #3,D0 ; put the battery flags into bits 15-13 (temporarily) MOVEQ #0,D1 ; scale the warning level (256*warning/max) MOVE.B LowWarn(A2),D1 SUB.B CutOff(A2),D1 ; BSR.S ScaleLevel ; pin the level and save it SWAP D0 ; put it all in the upper word MOVEQ #0,D1 ; scale the battery level (256*batt/max) move.b BatAvg(A2),d1 ; get the current averaged battery SUB.B CutOff(A2),D1 ; BSR.S ScaleLevel ; pin the level and save it RTS @MaxVolts DC.B 123 ; [6.35v] not charging, no charger DC.B 188 ; [7.00v] not charging, has charger DC.B 123 ; [6.35v] is charging, no charger (can't get here) DC.B 208 ; [7.20v] is charging, has charger ScaleLevel ASL.L #8,D1 ; multiply by 256 DIVU D2,D1 ; divide a level by the maximum CMPI.W #255,D1 ; higher than max (well, it _might_ happen)? BLS.S @SaveLevel ; -> no, use it as is MOVE.B #255,D1 ; yes, pin it at maximum @SaveLevel MOVE.B D1,D0 ; save the scaled level RTS ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: MultiScaledBatteryInfo (PowerDispatch selector #9) ; ; Inputs: A2 - pointer to Power Manager globals ; ; Outputs: D0 - bits 31: 1=battery installed ; 30: 1=battery is charging ; 29: 1=charger connected ; 23-16: warning level (0-255) ; 15: don't use this one (needs to be set if the selector isn't implemented) ; 7- 0: battery level (0-255) ; ; Trashes: D1, D2, A0 ; ; Function: Returns scaled warning and battery levels, plus a couple of flags for ; portables that support multiple battery types. If no battery is currently ; connected, it will return zero for both the warning and battery levels. ; It uses the extended battery status command which returns the following info: ; ; +0 flags (bits) ; 7: chargeable battery ; 6: "energy used" count valid ; 5: 0 ; 4: battery termperature valid ; 3: dead battery ; 2: battery connected ; 1: hi-charge enabled ; 0: charger installed ; +1 voltage (H) ; +2 voltage (L) ; +3 ambient temperature (°C) ; +4 battery temperature (°C) ; +5 power usage rate ; +6 energy used (H) ; +7 energy used (L) ; ; and the battery info command, which returns the following info: ; ; +0 max energy used count (H) ; +1 max energy used count (L) ; +2 min voltage--charging (H) ; +3 min voltage--charging (L) ; +4 max voltage--charging (H) ; +5 max voltage--charging (L) ; +6 min voltage--discharging (H) ; +7 min voltage--discharging (L) ; +8 max voltage--discharging (H) ; +9 max voltage--discharging (L) ;———————————————————————————————————————————————————————————————————————————————————————— MultiScaledBatteryInfo LINK A6,#0 ; get parameters associated with the current type of battery... CLR.W -(SP) ; put a buffer on the stack CLR.L -(SP) CLR.L -(SP) MOVE.L SP,-(SP) ; pmRBuffer MOVE.L (SP),-(SP) ; pmSBuffer CLR.W -(SP) ; pmLength = 0 MOVE.W #readBatteryInfo,-(SP) ; pmCommand = return battery info MOVEA.L SP,A0 ; point to the parameter block _PMgrOp ; send the command LEA pmRBuffer+4(SP),SP ; toss the parameter block ; get the current battery state and battery level... CLR.L -(SP) CLR.L -(SP) MOVE.L SP,-(SP) ; pmRBuffer MOVE.L (SP),-(SP) ; pmSBuffer CLR.W -(SP) ; pmLength = 0 MOVE.W #readExtBatt,-(SP) ; pmCommand = get extended battery data MOVEA.L SP,A0 ; point to the parameter block _PMgrOp ; send the command LEA pmRBuffer+4(SP),SP ; toss the parameter block MOVEA.L SP,A0 ; point to the buffer MOVEQ #%01000111,D0 ; mask off the "energy used" count valid, AND.B (A0)+,D0 ; battery installed, charging, and charger connected bits ROR.W #3,D0 ; shift them into bits 15-13 and 3 (temporarily) MOVE.L (A0)+,D2 ; get the battery voltage into the upper word (NOTE: unaligned read) ADDQ.W #1,A0 ; skip over the power usage rate MOVE.W (A0)+,D2 ; get the "energy used" count into the lower word MOVE.L A0,SP ; toss the buffer TST.W D0 ; is a battery installed? BPL.S @NoBattery ; -> no, just swap the flags and exit BCLR #6-3,D0 ; is the "energy used" count valid? BEQ.S @UseVolts ; -> no, use the voltages ; calculate the current level based on the "energy used" count... @UseCount SWAP D0 ; put the flags into the upper word, set warning level=0 MOVEQ #0,D1 ; get the maximum energy count (zero-extended) MOVE.W (SP),D1 SUB.W D2,D1 ; calculate how much energy is left (max-used) BLE.S @Done ; -> max≤used, so pin at zero MOVE.W (SP),D2 ; get the maximum BSR.S ScaleLevel ; scale the level and save it BRA.S @Done ; calculate the current level based on voltages... @UseVolts LEA 4(SP),A0 ; point to the charge parameters BTST #14,D0 ; are we charging? BNE.S @Charging ; -> yes, we're pointing to the right place ADDQ.W #4,A0 ; no, point to the discharge parameters @Charging MOVE.W (A0),D2 ; calculate max-min SUB.W -(A0),D2 BLE.S @NoBattery ; -> max≤min (strange…), so bail BTST #14,D0 ; are we charging? BNE.S @NoWarning ; -> yes, there's no warning level MOVEQ #0,D1 MOVE.B LowWarn(A2),D1 ; calculate warning-min ADD.B D1,D1 SUB.W (A0),D1 BLE.S @NoWarning ; -> warning≤min, so pin at zero BSR.S ScaleLevel ; scale the level and save it @NoWarning SWAP D0 ; put it all in the upper word MOVE.L D2,D1 ; get the battery voltage CLR.W D1 ; and zero extend it SWAP D1 SUB.W (A0),D1 ; calculate battery-min BLE.S @Done ; -> battery≤min, so pin at zero BSR.S ScaleLevel ; scale the level and save it @Done UNLK A6 ; get rid of any lingering buffers RTS @NoBattery CLR.B D0 ; make sure the battery level is zero SWAP D0 ; put the flags in the upper word BRA.S @Done ; and exit ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: LeadAbsoluteBatteryVoltage ; ; Inputs: A2 - pointer to Power Manager globals ; ; Outputs: D0 - fixed-point representation of the absolute battery voltage ; ; Trashes: none ; ; Function: Calculates the absolute battery voltage for lead acid batteries. The result ; is returned as a fixed point number (16 bits integer + 16 bits fraction), ; giving a range from 0 to 32767.999984741 volts. This should be enough to ; handle the next few batteries we do. ; ; For lead acid batteries, the voltage is calculated as: ; ; voltage = ((power/100) + 5.12) volts ; ; For a ‘power’ range of 0 to 255, we get a voltage range of 5.12 to 7.67 volts. ;———————————————————————————————————————————————————————————————————————————————————————— LeadAbsoluteBatteryVoltage MOVEQ #0,D0 ; zero-extend the battery level MOVE.B BatAvg(A2),D0 SWAP D0 ; convert it to fixed-point DIVU #100,D0 ; divide by 100 to convert to relative volts SWAP D0 ; get the result back in the right order ADDI.L #(5<<16)+(65536*12/100),D0 ; add 5.12v to convert to absolute RTS ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: PGEAbsoluteBatteryVoltage ; ; Inputs: A2 - pointer to Power Manager globals ; ; Outputs: D0 - fixed-point representation of the absolute battery voltage ; ; Trashes: A0, D1 ; ; Function: Calculates the absolute battery voltage for PG&E-based machines. The result ; is returned as a fixed point number (16 bits integer + 16 bits fraction), ; giving a range from 0 to 32767.999984741 volts. This should be enough to ; handle the next few batteries we do. ; ; PG&E essentially uses a 9-bit A/D for a voltage range of 7 to 21 volts. ; So the voltage is calculated as: ; ; voltage = ((power*14/512) + 7) volts ;———————————————————————————————————————————————————————————————————————————————————————— PGEAbsoluteBatteryVoltage CLR.L -(SP) ; allocate space for the extended battery info CLR.L -(SP) MOVE.L SP,-(SP) ; pmRBuffer MOVE.L (SP),-(SP) ; pmSBuffer CLR.W -(SP) ; pmLength = 0 MOVE.W #readExtBatt,-(SP) ; pmCommand = get extended battery data MOVEA.L SP,A0 ; point to the parameter block _PMgrOp ; send the command LEA pmRBuffer+4(SP),SP ; toss the parameter block MOVEQ #0,D0 MOVEA.L SP,A0 ; point to the buffer BTST #2,(A0)+ ; is a battery connected? BEQ.S @NoBattery ; -> no, we're done MOVEQ #14,D0 ; battery level * 14 MULU (A0),D0 ; (NOTE: unaligned read) SWAP D0 ; convert it to fixed point MOVEQ #9,D1 LSR.L D1,D0 ; divide by 512 to convert to relative volts ADDI.L #7<<16,D0 ; add 7v to convert to absolute @NoBattery ADDQ.W #8,SP ; toss the buffer RTS ;•••••••••••••••••••••••••••••••••••••• Utilities ••••••••••••••••••••••••••••••••••••••• ;———————————————————————————————————————————————————————————————————————————————————————— ; PostUserNmMsg - post user warning notification msg ; ; General routine which will post a user warning notification mgr message using the ; "UNmQEntry" PmgrRec notification mgr record. ; ; Input: a0.l = ptr to notification string ; d0.w = indicates which completion routine ; 0 -> does nothing - leaves flashing icon (default) ; 1 -> removes message and icon ; ; Output: none ; Export PostUserNmMsg PostUserNmMsg @regs reg d1/a0/a2 movem.l @regs,-(sp) movea.l PmgrBase,a2 ; a2 = ptr to pmgr globals bset.b #PmgrAvoidUsrMsg,PmgrFlags1(a2) ; are warning messages enabled? bne.s @skip ; not enabled - skip it move.l a0,d1 ; is there a string? beq.s @skip ; no message to post lea UNmQEntry(a2),a0 ; addr of notification record move.l d1,nmStr(a0) ; set the string move.w #8,qType(a0) move.l #-1,nmSound(a0) ; use default sound clr.w nmMark(a0) ; No mark move.l lpSICNHndl(a2),nmIcon(a0) ; Use the icon lea @nmproc,a2 ; no completion routine subq.w #1,d0 ; the "other" completion routine? bne.s @postmsg ; no - use default lea @oneShot,a2 ; use routine which removes icon @postMsg move.l a2,nmResp(a0) ; set completion routine _NMInstall ; post message @skip movem.l (sp)+,@regs @nmproc rts ; empty completion routine @oneShot ; movea.l (sp)+,a1 ; save return addr movea.l (sp)+,a0 ; get ptr to BNmQEntry _NMRemove ; remove the bad battery notification movea.l PmgrBase,a0 ; get ptr to pmgr globals bclr.b #PmgrAvoidUsrMsg,PmgrFlags1(a0) ; allow user messages again jmp (a1) ; return to caller ;••••••••••••••••••••••••••••••••••••••• ModemTables •••••••••••••••••••••••••••••••••••••••• ;———————————————————————————————————————————————————————————————————————————————————————— ; Modem Table ; ; Tables of offsets to low level hardware dependent routines which are used for ; internal modem control ;———————————————————————————————————————————————————————————————————————————————————————— ALIGN 4 DC.L PrimsTypePtr ; flags DC.L (ModemTableEnd-ModemTable) ; size of table ModemTable DC.L StdModemOn-ModemTable ; standard routine to turn on modem DC.L StdModemOff-ModemTable ; standard routine to turn off modem DC.L StdModemType-ModemTable ; standard routine to get modem type ModemTableEnd ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: TurnModemPowerOn/Off ; ; Input: a0.l = ptr to powermanager pb + 4 byte general data buffer(pmData) ; - pre-initialized so pmSend/pmReceive points to pmData ; d1.b = modem status bits from power manager ; ; Outputs: none ; ; Trashes: none ; ; Function: Power control for the internal modem ; ;———————————————————————————————————————————————————————————————————————————————————————— TurnModemPowerOff bclr.l #ModemPwr,d1 ; clear bit in saved modem status bits bra.s SetData TurnModemPowerOn and.b #7,d1 ; mask only bset.l #ModemPwr,d1 ; set bit in saved modem status bits SetData move.b d1,pmData(a0) ; turn on +5V and -5V to modem bit move.w #1,pmLength(a0) ; xmit one byte move.w #modemSet,pmCommand(a0) ; modem set command _PmgrOp ; turn on +5V and -5V to modem rts ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: Enable/DisableModem ; ; Input: a0.l = ptr to powermanager pb + 4 byte general data buffer ; ; Outputs: none ; ; Trashes: none ; ; Function: Controls the modem enable line ; ;———————————————————————————————————————————————————————————————————————————————————————— DisableModem move.b #ModemOff,pmData(a0) ; disable modem - sense of bit is reversed bra.s SetCount EnableModem move.b #ModemOn,pmData(a0) ; enable modem - sense of bit is reversed SetCount move.w #1,pmLength(a0) ; xmit one byte move.w #powerCntl,pmCommand(a0) ; power control command _PmgrOp ; call the power manager rts ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: TurnOffModemSoundSelect ; ; Input: none ; ; Outputs: none ; ; Trashes: d0 ; ; Function: turns off input source if modem selected ; ;———————————————————————————————————————————————————————————————————————————————————————— TurnOffModemSoundSelect jsrTBL sndInputSource ; is a sound source selected? cmp.B #2,D0 ; is the modem input selected ? bne.s @exitSoundInput ; continue with code move.b #0,d0 ; get current volume jsrTBL sndPlayThruVol ; set volume for playthrough moveq.l #sndInputOff,d0 jsrTbl sndInputSelect ; select aux source @exitSoundInput rts ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: DelayNMsec ; ; Input: d0.l = msec to delay ; ; Outputs: none ; ; Trashes: none ; ; Function: Spin wait for N milliseconds ; ;———————————————————————————————————————————————————————————————————————————————————————— EXPORT DelayNMsec DelayNMsec @savedregs reg d0-d1 ; working set of register movem.l @savedregs,-(sp) ; save working set move.w d0,d1 ; number of msec subq #1,d1 ; subtract 1 for dbra loop @msec move.w TimeDBRA,d0 ; load 1ms delay in d0 @Delay dbra d0,@Delay ; wait 1 ms dbra d1,@msec ; repeat 1ms d1 times movem.l (sp)+,@savedregs ; restore working set rts ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: StdModemOn ; ; Input: a0.l = ptr to powermanager pb + 4 byte general data buffer ; d1.b = modem status bits from power manager ; ; Outputs: condition codes ; ; Trashes: d0 ; ; Function: Power the internal dartanian modem ; ; The following is a summary of the devices and which power manager commands to use ; to control them. ; ; device Pmgr cmd port0 bit signal name ; ----- --------- ------- ----------- ; SCC powerCntl P01/sccOn *SCC_CNTL ; external port powerCntl P04/serOn *SERIAL_POWER ; internal modem powerCntl P03/ModemOn MODEM_PWR ; modemSet P06 *MODEM_PWROUT ; ; Additionally for the internal modem, MODEM_RESET (a Orca/via2 signal) must be ; manipulated by changing it from an input to and output to de-assert reset. ; ; Powering on the SCC and the external ports only involves sending the correct power ; manager commands. Powering on the internal modem involves some timing delays and a ; specific sequence of commands. ; ; To power the SCC and external ports: ; turn on SCC ; turn on external line drivers ; ; To power the internal modem: ; clear MODEM_RESET ; turn on +5V and -5V to modem (MODEM_PWROUT) ; ... wait >2ms to allow +5V and -5V to settle ; set MODEM_RESET ; enable the modem (*MODEM_PWR) ; wait >5ms to allow reset time ; clear MODEM_RESET ; ;———————————————————————————————————————————————————————————————————————————————————————— StdModemOn bclr.b #7,([VIA2],vBufB) ; clear modem-reset before turning on power bsr.s TurnModemPowerOn ; turn on power moveq.l #3,d0 ; delay 3 msec (50% margin) bsr.s DelayNMsec ; ... wait for +5v and -5V to ramp bset.b #7,([VIA2],vBufB) ; set reset before enabling modem bsr EnableModem ; enable modem moveq.l #8,d0 ; delay 8 msec bsr.s DelayNMsec ; ... for reset pulse bclr.b #7,([VIA2],vBufB) ; clear modem-reset ; enable modem sound interrupt move.l ([PmgrBase],MdmSndVect),jModemSnd; install in Level 1 VIA1 dispatch table move.b #((1< bsr.s TurnOffModemSoundSelect ; turn off modem sound if needed bsr.s DisableModem ; disable modem move.l #512,d0 ; Delay 512ms for modem to save some of its state bsr.s DelayNMsec ; ... wait for +5v and -5V to ramp bsr.s TurnModemPowerOff ; power off modem moveq.l #8,d0 ; delay 8 msec bsr.s DelayNMsec ; ... wait for power ramp down moveq.l #0,d0 ; set CCR, turn off scc power on return rts ; ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: StdModemType ; ; Input: ; ; Outputs: d0.l has the modem Type ; ; Trashes: d0 ; ; Function: Returns type of modem installed in TIM Modem ; ;———————————————————————————————————————————————————————————————————————————————————————— StdModemType moveq.l #ModemTypeSerial,d0 ; serial modem on TIM rts ENDIF ; {hasPwrControls} ;••••••••••••••••••••••••••••••••••••••• CommsPowerTables •••••••••••••••••••••••••••••••••••••••• ;———————————————————————————————————————————————————————————————————————————————————————— ; Comms Power Table ; ; Tables of offsets to low level hardware dependent routines which are used for ; powering on and off the various communication ports, be they serial, modem, or Ethernet. ;———————————————————————————————————————————————————————————————————————————————————————— ALIGN 4 DC.L PrimsTypePtr ; flags DC.L (CommPowerTableEnd-CommsPowerTable) ; number of entries CommsPowerTable @PwrOn DC.L PortBOn-CommsPowerTable ; B serial DC.L PortAOn-CommsPowerTable ; A serial DC.L PortCOn-CommsPowerTable ; C serial (internal modem) DC.L 0 ; no ethernet @PwrOff DC.L PortBOff-CommsPowerTable ; B serial DC.L PortAOff-CommsPowerTable ; A serial DC.L PortCOff-CommsPowerTable ; C serial (internal modem) DC.L 0 ; no ethernet CommPowerTableEnd with ModemTblRec ;——————————————————————————————————————————————————————————————————————————————————————— ; PortAOn - setup for port A use ; ; ; Input : d0 = bit 0: 0 = use internal modem, 1 = ignore internal modem ; PortAOn @savedregs reg d0-d3/a0-a2 ; movem.l @savedregs,-(sp) move.l d0,d2 ; d2 = indicator bits move.l PmgrBase,a2 ; Get pmgr locals bset #SerPortAPwr,PmgrFlags2(a2); set port a powered ; Check whether we should bypass the modem check bclr.l #BypassModem,d0 ; set = ignore modem bne.s @powerScc ; use the external ports ; check modem type LoadTbl ModemTblPtr,a2,a0 beq.s @powerScc ; JsrPrimTbl GetModemType,a0 cmpi.b #ModemTypeSerial,d0 ; is this a serial modem ? bne.s @powerScc ; power on external port ; Modem is installed - read extended PRAM to see if modem should be powered on MOVE.L #(1<<16)+(PmgrStatusFlags<<0),D0 ; ADD.B PRAMBase(a2),D0 ; read the Power Manager flag byte suba.w #2,sp ; alloc pmgr command pkt movea.l sp,a0 _ReadXPRAM ; a0 = ptr to buf (writing over cmd word in pkt) btst.b #UseIntrnlModem,(a0) ; check cdev bit for modem adda.w #2,sp bne @powerScc ; set - don't power modem ; Power on the modem move.b #%00001000,d0 ; power on channel C _SerialPower move.b #(1< ; PortBOn - setup for port B use ; ; ; Input : d0 = bit 0: 0 = use internal modem, 1 = ignore internal modem ; PortBOn bset #SerPortBPwr, \ ([PMgrBase],PmgrFlags2) ; log power state bsr PowerSccOn ; turn on scc rts ;——————————————————————————————————————————————————————————————————————————————————————— ; PortCOn - setup for port C use ; ; ; Input : d0 = bit 0: 0 = use internal modem, 1 = ignore internal modem ; PortCOn bset #SerPortCPwr, \ ([PMgrBase],PmgrFlags2) ; log power state bsr ModemPowerON ; power on modem rts ;——————————————————————————————————————————————————————————————————————————————————————— ; PortAOff - release port a resources ; ; PortAOff @savedregs reg d0-d3/a0-a2 ; movem.l @savedregs,-(sp) move.l d0,d2 ; d2 = indicator bits move.l PmgrBase,a2 ; Get pmgr locals bclr.b #SerPortAPwr,PmgrFlags2(a2); read the Power Manager flag byte ; check modem type LoadTbl ModemTblPtr,a2,a0 bne.s @powerOffScc ; JsrPrimTbl GetModemType,a0 cmpi.b #ModemTypeSerial,d0 ; is this a serial modem ? bne.s @powerOffScc ; power on external port ; Power off modem move.b #%10001000,d0 ; power off channel C _SerialPower @powerOffScc bsr HandleABPower ; power off scc if necessary @Done movem.l (sp)+,@savedregs rts ;——————————————————————————————————————————————————————————————————————————————————————— ; PortBOff - release port a resources ; ; PortBOff bclr.b #SerPortBPwr,\ ([PMgrBase],PmgrFlags2) ; read the Power Manager flag byte bsr HandleABPower rts ;——————————————————————————————————————————————————————————————————————————————————————— ; PortCOn - setup for port C use ; ; ; Input : d0 = bit 0: 0 = use internal modem, 1 = ignore internal modem ; PortCOff bclr #SerPortCPwr, \ ([PMgrBase],PmgrFlags2) ; log power state bsr ModemPowerOFF ; power on modem rts ;——————————————————————————————————————————————————————————————————————————————————————— ; HandleABPower - given the current port usage, adjust power manager power control lines ; ; HandleABPower move.l d0,-(sp) ; save working set move.b ([PMgrBase],PmgrFlags2),d0 ; get a copy of the flags andi.b #((1< ; SccOn/PowerSccOff - power control for SCC ; ; ; Power on scc ; power on scc ; PowerSccOn pSCCworkreg reg d0-d1/a0-a1 movem.l pSCCworkreg,-(sp) moveq #0, d0 move.b #(sccOn|serOn), d0 ; load on command bra.s SendCmd ; power off scc ; PowerSccOff movem.l pSCCworkreg,-(sp) moveq #0, d0 move.b #(sccOff|serOff),d0 ; load off command ; bra.s SendCmd SendCmd move.l d0, -(sp) ; data to send move.l sp, -(sp) ; pmRBuffer move.l 7(SP),-(sp) ; pmSBuffer move.w #1, -(sp) ; pmLength move.w #powerCntl,-(sp) ; pmCommand MOVEA.L sp,a0 ; point to the parameter block _PMgrOp ; get the modem info move.l (sp), d0 ; return the status byte adda.l #pmBlkSize, sp ; pop buffer movem.l (sp)+,pSCCworkreg rts ;——————————————————————————————————————————————————————————————————————————————————————— ; ModemPowerON/ModemPowerOFF - power control for modem slot ; ; ; Power on scc ; Power off modem ModemPowerON powerreg reg d0-d2/a0-a2 movem.l powerreg,-(sp) ; save working set move.l #PowerOnModem,d2 ; load offset into modem primitives table for off bra.s sendCommand ModemPowerOFF movem.l powerreg,-(sp) ; save working set move.l #PowerOffModem,d2 ; load offset into modem primitives table for off ; bra.s sendCommand sendCommand move.l PmgrBase,a2 ; Get pmgr locals move.l vPMgrPrimitives(a2),d0 ; IF NoPrimitives THEN beq.s @Done ; Exit movea.l d0,a2 ; get pointer to primitives move.l ModemTblPtr(a2),d0 ; get offset to ModemTblPtr beq.s @Done ; Exit move.l d0,-(sp) bsr.l ModemStatusRT move.l d0,d1 ; setup modem status for primitive routines move.l (sp)+,d0 lea (a2,d0.L),a2 ; get pointer to ModemTblPtr move.l (a2,d2.L),d0 ; load offset to routine suba.w #pmBlkSize,sp ; alloc pmgr command pkt lea pmData(sp),a0 ; get addr of buffer move.l a0,pmRBuffer(sp) ; set ptr to rcv buffer move.l a0,pmSBuffer(sp) ; set same ptr to xmit buffer (it's expected or error) move.l sp,a0 jsr (a2,d0.L) ; Run the routine adda.w #pmBlkSize,sp ; pop power mgr pkt @Done movem.l (sp)+,powerreg ; | rts ; V endwith ; {ModemTblRec} END