; ; File: PowerMgr.a ; ; Contains: 680x0 Interface to the Power Manager. ; ; Written by: Mike Hanlon ; Remangled by Portable Terror Squad ; ; Copyright: © 1986-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/1/93 SKH Rolled in from Horror. The old supermario copy of PowerMgr was ; hopelessly out of date, this is an almost straight copy of the Horror ; version. ; 6/24/93 SWC Clear the ‘disable hard disk spindown’ flag before calling the ; spindown routine so that the hard disk really does get turned ; off. ; 6/22/93 SWC Fixed a bug that causes a crash when getting ADB packets (when a ; timeout occurred, it was jumping to the wrong label). ; 6/21/93 SWC Changed the bit ordering in the modem info selectors and munged ; the SetWakeupTimerEnable into that mess. Get/SetWakeupTimer now ; take a record containing the wakeup time and an enable flag. ; 6/16/93 SWC The polarity of the hasInternalModem bit in the modem info ; selector was backwards, mainly because the PRAM bit, ; UseIntrnlModem, is set to 1 when using an EXTERNAL modem. Added ; a ‘wakeup on ring’ bit to the modem info selector. Added a new ; selector to set/clear the ‘wakeup on ring’ bit. Changed the ; default SCSI Disk Mode address from 1 to 2 so it doesn't ; conflict with the ID of a built-in hard disk in a DuoDock. ; 6/2/93 SWC Rolled in the modem type entry in the PowerDispatch hook from ; Excelsior. Added the modem type to the modem info selector on ; the public dispatch. Copied the public dispatch vector table ; into RAM. ; 6/1/93 SWC Only run the reduced speed routines if that feature is supported ; on a particular machine. Filled in the FactoryDisp entry in the ; PowerDispatch table wth a stub so that the RAM-based table gets ; built correctly. ; 5/31/93 RLE change set/getmachineid to set/getmachineattr (this way PG&E ; won't need to be updated for each and every new machine) ; 5/28/93 SWC Moved the absolute battery voltage from private to public ; dispatch. Added several new selectors to the public dispatch. ; 5/5/93 SWC Moved RunHardDiskQueue into the hard disk spindown routine so ; that it will only be called when the hard disk will really be ; spun down. Fixed a bug in SelectIntModem: it was always ; selecting the external modem. ; 5/4/93 SWC Allocated a buffer for reading/writing PRAM since the old buffer ; (a PMgr command parameter block) isn't available anymore. Always ; clear the NTSC low-mem so we'll get square corners on the main ; screen and menu bar. ; 5/4/93 SWC Made sure HardDiskQInstall/Remove return zero if no error. ; 5/4/93 SWC Added include of PowerMgrDispatchEqu.a. Added code to ; SetProcessorSpeed to allow for dynamically switching the ; processor speed on machines that support this feature. ; 4/23/93 SWC Moved the new PowerDispatch selectors down one in the table ; because #12 was used by the factory and patched in on disk. ; 4/22/93 RLE for SetMachineID/GetMachineID, force the count to be sent as ; part of the message packet ; 4/21/93 SWC Changed all routines that reference the primitives tables to ; assume that they exist instead of checking for a nil pointer. ; The initialization code will now hang if a table hasn't been set ; up. Added some more info to the modem info selector, and two new ; selectors to deal with disabling hard disk spindown. Added a new ; selector to _PowerDispatch to return a bitmap of private Power ; Manager features. ; 4/19/93 SWC Changed the primitives table initialization since there's now a ; vector to it in the ProductInfo table. Added new ; _PowerMgrDispatch selectors. Moved DoSpinDown here from ; SCSIMiscPatch.a. ; 4/16/93 SWC Pass the pointer to the queue element into the hdProc in ; RunHardDiskQueue ; 4/15/93 SWC Added in support for the public Power Manager interface trap, ; PowerMgrDisp ($A09E). ; 4/14/93 RLE add commands to set and get cutoff voltage ; 3/29/93 RLE add SetMachineID/GetMachineID commands to provide a mechanism to ; inform microcontroller what machine it might be running on; do ; SetMachine command as part of InitPmgrVars, but move part of the ; routine into MorePmgrInit to fit within the patch space ; 03-11-93 jmp Rolled back to the rev because we now handle the turning ; on/off the CSC in the CSCPrimaryInit. ; 3/9/93 RLE in the Power1Control call, add a 50 msec delay after powering up ; the CSC's PNLPWR output to allow it to fully charge ; 3/5/93 RLE add Power1Cntl and Power1Read to list of pmgr commands not ; entirely supported by PG&E microcontroller ; 3/3/93 RLE toss and prepare to do the LCD screen save/restore in the ; driver instead of in the power manager ; 2/24/93 RLE change InitPmgrPrimitives to support multiple table entries for ; a given decoder type ; 8/10/92 ag Fixed (aX,aY.w) addressing problem in serial power. aY being ; only size word does not allow for large movement of the ; primitives record (such as into ram). ; 8/4/92 ag Fixed (aX,aY.w) addressing problem. aY being only size word does ; not allow for large movement of the primitives record (such as ; into ram). ; 8/3/92 SWC ag/IdleMind wasn't using a long table offset. This causes a ; problem if the table is moved to RAM for patching. ; 7/31/92 SWC Backed out and instead just set D0=0 on exit of Wakeup. ; 7/30/92 SWC Added D0 to the list of SleepRegs so that the original sleep ; type will be returned on exit. ; 7/29/92 SWC Added a new call in the wakeup code to make sure we have the ; appropriate AppleTalk connection selected. This mostly applies ; to a docking environment where available SCC ports may change ; during sleep. ; 7/16/92 HJR In WakeUp move the SetSupervisorMode to the very end so that we ; can run on the UserStack. Since the interrupt stack is smaller, ; this fixes a problem where PaintBehind overflows the stack doing ; region calls. ; 7/14/92 ag Initialize the shorted battery alert delay if the charger is ; installed at powerup. ; 7/13/92 SWC In Wakeup, added a call to SCSIDiskWakeAlert to put up a ; DeepShit alert if the user has a SCSI Disk Mode cable plugged in ; when we wake up from sleep. ; 7/13/92 HJR Cleaned-up PowerMgrHook a little bit. ; 7/13/92 ag Rewrote the battery interrupt handler. Added new subhandlers to ; handle the battery shorted interrupt and the changer state ; change interrupt. Added new constants to the primitive ; infotable for delay of shorted battery dialog and delta battery ; warn level when using an external monitor. ; 7/11/92 ag Changed SecondaryInitDisp to PowerMgrHookDisp. The selector is ; now a general purpose selector with data passed in the upper ; word of d0. Added 3rd level dispatch table for PowerMgrHookDisp. ; Added scsi disk mode handler, and external monitor on handler. ; 7/11/92 HJR Changed the name of WakeScrnPtr to ScreenRedrawPtr and slightly ; modified the RestoreScreen routine. ; 7/10/92 ag Added softpower vector support. Created modem primitives, so ; moved all the serial overpatch stuff and part of the serial ; power stuff to PowerMgrPrimitives.a. With the new space in the ; serial power section moved the modem sound int stuff back to ; it's original location. ; 7/1/92 ag Add new modem commands to the dartexception table. ($71,$79). ; Commented command ($5D) as a general purpose modem command which ; is modem dependent. ; 7/1/92 SWC Disable level 3 modem interrupts as well as level 1 PMGR ; interrupts on DBLite. Put a line back into InitPMgrVars to clear ; the PMGR interrupt bit in the IFR (this seems to have been lost ; in the course of changes). ; 6/30/92 HJR Moved ADBReInit earlier in WakeUp so that user will have cursor ; feedback in wake. ; 6/29/92 GMR Updated a couple of the PMGROp modem cmd/reply counts in the ; tables. ; 6/26/92 GMR Added another modem command (SetDAAID) to the PmgrOp command ; table. ; 6/25/92 djw Fixed a bug in SerPowerOff (found by Steve Christensen) where ; the data length was not being set calling the _PmgrOp. The ; result was 5v was not being turned off to the modem. ; 6/18/92 SWC Setup PMgrOp to use vectors to make patching easier. These are ; now initialized in InitPMgrVars. Also in PMgrOp, don't point to ; the SCC if no PollProc is installed to avoid overflowing the ; stack when AppleTalk is running on port A (lotsa bytes stashed). ; Added a low power warning flag to IdleMind so we can tell that's ; why we're going to sleep. ; 6/11/92 djw Removed code to post a video warning message from PSecondaryInit ; because it did not take into account video waking from sleep ; case. ; 6/4/92 SWC Temporarily changed the reply count for the battery info command ; ($6D) until the PG&E code is updated to fix a bug. ; 6/2/92 SWC Fixed a bug I introduced in . ; 6/1/92 HJR Initialized PmgrRec.Charger in InitPmgrVars. Provided support in ; PSecondaryInit for external video warning if no charger ; installed. ; 5/29/92 SWC When waking up, check if a docking bar was attached that's not ; supposed to be attached during sleep, and if so, put the machine ; back to sleep. ; 5/28/92 SWC In NewPMgrTrap, A0 was trashed when we checked if we needed to ; delay for a power control command. It isn't anymore. ; 5/27/92 SWC Converted changes in into overpatches cuz as written they ; caused code to move (ROMBind problems in system disk patches). ; 5/20/92 ag Added DartSPI flag code to enable and disable the SPI interface ; depending on the modem connected. ; 5/19/92 HJR Added new selector, PDimScreens, to PowerDispatch Trap and added ; accompanying routine which powers down video. Changed ; HdDSpinDown to RunIdleRoutines, a more generalized routine. ; 5/15/92 SWC Added PMGR diagnostics (selector-based) command ($ED) entry to ; send and receive tables. Moved SndWatch, SndWatchPonti to ; PowerManagerPrimitives as yet another primitive, and fixed ; InitPMgrVars to look up the addresses from the primitives table. ; Sound VBLs are no longer installed by secondary init dispatch, ; since the sound VBLs check to make sure that patches are ; installed before calling the sound input primitives. Changed the ; way the PMgrOp exception table is set up to make the search ; faster. ; 5/12/92 ag Added secondary init dispatch routine for code execution at ; secondary init time. Removed sound primitives check for sound ; vbl code, vbl now installed at secondary init time. All patches ; should be in by then. Added Dart exception table for SPI. Moved ; init of softshutdown vars to secondary init code. ; 5/8/92 ag Fixed SoundPonti to check expand mem vector before using the ; vector. ; 5/8/92 ag Added dartanian busy check to NewPmgrTrap. ; 5/8/92 HJR Added new primitive for refreshing the screen from wake. ; 5/7/92 ag Added soft shutdown for Dartanian. Rolled in Modem sound ; patches. Added busy check in power manager protocal. ; 5/7/92 SWC Added a check in IdleMind to bail on sleeping if sleep is not ; allowed because of the kind of bar that's attached (avoids a ; dialog). Moved the beginning and end parts of NewPMgrTrap around ; a bit so I could add SCC polling to the delay loop for power ; control commands. Added a new selector to PowerDispatch to ; return a scaled battery level. ; 4/27/92 ag Added new sound watch to use registers in Ponti to control sound ; power. ; 4/24/92 HJR Fix some alignment problems. ; 4/24/92 HJR Added new SndWatchPtch to handle DFAC activity. Changed ; CheckForNewPMgr so the Niagra uses new PMgrTrap. Fixed ; Handle_Element bug where a QueueProc element returning error ; would still cause the machine to sleep. ; 4/22/92 SWC In GoToSleep, changed the BTST to a BSET to block re-entry into ; the sleep code earlier on in the process, and removed the BSET ; in DreamAway (duplication). Added a call to DockingSleepDenied ; (DockingMgr.a) if someone tries to put the system to sleep when ; connected to a bar that doesn't want that to happen. ; 4/17/92 SWC Fixed bugs in the power control/status emulation routines that ; sometimes cause the wrong information to be returned. ; 4/13/92 SWC Changed the table entry for "send DFAC command" to 2 bytes since ; we need to pass a byte for the input source as well so the PMGR ; can switch the MUX. Changed the readBattery command emulation to ; return a scaled battery level instead of maximum since we now ; check for the existance of a battery in the battery monitoring ; routine. Fixed a bug in InitPmgrPrimitives (hysteresis was ; being copied into a byte instead of a word). ; 3/11/92 SWC In InitPmgrVars, get the address of the power cycle register ; from the primitives table instead of a chain of TestFors. ; Installed a VBL task (for machines that support it) to monitor a ; clamshell switch so we can put the machine to sleep or shut it ; down. In Wakeup, always do an ADBReInit since new cursor stuff ; needs this to handle adding devices across sleep. Renamed ; JawsPwrCycReg to PowerCycleReg since non-Jaws machines use it ; too. ; 3/9/92 SWC Fixed the receive count for the PMGR soft reset command. ; 3/3/92 SWC Exported GetLevel for use in the SCSI DiskMode code. ; 2/26/92 SWC In NewPMgrTrap, fixed register usage and saving relating to ; polling the SCC since some registers aren't setup right, and ; others are getting trashed by the PollProc. Added a 125usec ; delay to the end of NewPMgrTrap on powerCntl calls to give the ; power planes a chance to stabilize. ; 2/21/92 HJR Cleaned up InitPmgrVars a bit. Fixed Batwatch to utilize to 3 ; battery levels instead of 4. Also added sleep hysteresis to ; batwatch. Fixed reentrancy problem with sleep. ; 2/19/92 SWC Fixed the Wakeup padding (off by 2 bytes). ; 2/17/92 SWC Added docking checks to see if power cycling and/or sleep are ; allowed when we're connected to certain bars on DBLite. ; 2/14/92 SWC Added docking support to power control/status emulation on ; DBLite. ; 2/13/92 SWC Moved an emulated command in the table. Fixed a typo. ; 2/13/92 SWC Fixed a patch that the assembler had optimized to make 2 bytes ; shorter (I specified BSR.W, I got BSR.S). ; 2/10/92 SWC Added a new PowerDispatch selector to return the base Power ; Manager PRAM address so our clients won't have to go looking ; thru our globals. ; 2/7/92 SWC Write out default low/dead battery warning levels if the values ; read aren't valid. Default values now come from the ever popular ; primitives info table. ; 2/7/92 SWC Modified other places that reference Power Manager PRAM bytes to ; use the base address in the globals. Removed old HcMac code cuz ; it's confusing and more than likely won't be used again. ; 2/5/92 SWC Fixed a cut and paste boo-boo in . ; 2/5/92 SWC Moved a couple of the PMGR battery commands down in the tables ; since they were stomping on a pre-existing command. Changed how ; we get the low battery warning and dead battery levels from ; reading them from PRAM to using a specific command (available ; from the TIM days and carried on by DBLite). ; 2/4/92 SWC Adjusted padding since some stuff has moved a bit (wreaks havoc ; with patches). Re-wrote the sleep and wakeup code to use ; primitives tables to determine what needs to be done. ; 2/3/92 HJR Added support for PowerManagerPrimitives. Use PmgrPrim for ; default PRAM base, new IdleMind, and new CPUSpeed. Fixed ; HandleElement improperly aborting when encountering non-zero ; sleep queue return in DemandSleep. ; 1/28/92 SWC Did a bit of re-ordering in InitPMgrPatch2 to reduce the ; possibility of a race condition in clearing any unexpected PMGR ; interrupts. ; 1/27/92 SWC Updated NewPMgrTrap's transfer tables for new commands. ; 1/24/92 SWC Fixed the code to save/restore GSC registers over sleep. The new ; PMgrOp code (for DBLite) now uses only a 32ms timeout instead of ; having both long and short timeouts, so that when PG&E gets ; multiple interrupts we won't time out while waiting for it to ; respond. ; 1/9/92 SWC Rolled in changes for final chips: removed special case checks ; for PMGR interrupts on CA2 (now on CB1), extend DB-Lite's CPU ; speed code to support 25MHz/33MHz versions plus econo-mode. ; Added _PMgrOp emulation for SCSI and SCC clock control/status to ; the power control/status commands since these functions are now ; handled by the MSC. Save and restore GSC registers in the sleep ; code for DB-Lite. ; 10/29/91 SWC Cleared the interrupt flag before doing a blind interrupt read ; in InitPMgrPatch to make sure it isn't left in a weird state. ; Did miscellaneous cleanup in NewPMgrTrap. Turn on/off the ; serial driver chips as well as the SCC in SerialPower. ; 10/22/91 SWC Changed SCC polling in NewPMgrTrap to look at the SCC directly ; instead of using the VIA bit. Changed references to NoVRAMVidRam ; to point to the end of the record since the offsets are ; negative. ;
9/10/91 SWC Set battery voltage for ReadBattery emulation at maximum for ; now. Fixed the returned byte counts for the set/read sound ; emulated routines. ;
8/27/91 SWC Fixed the [temporary] jump to OneSecInt so that it instead jumps ; 6 bytes into the routine so the VIA's IFR is not cleared a ; second time. Depending on when one-second and ADB interrupts ; came along, it was possible to lose an ADB interrupt if it ; occurred just before the IFR bit was cleared in OneSecInt. ;

8/26/91 SWC Added Set Screen Brightness command ($41) to the command tables. ;

8/22/91 SWC Added in a call to the one-second interrupt handler from the ; PMgrInt. Vectored the send and receive count tables for ; NewPMgrTrap so we can make changes and additions without having ; to roll the ROM. ;

8/8/91 SWC Added universal PMgrOp code which will run on any Power ; Manager-based system. Universalized PMGR interrupt setup. ; Fixed sleep/wakeup sound chip register saving so Batman-specific ; registers are only saved if we've got a Batman. Added an MSC ; entry to the CPUSpeed routine. Added DB-Lite support to the ; sleep code. Fixed an unpatchable bug in InitPMgrVars that was ; found after TERROR was frozen (it just requires moving an ; instruction down). ; ——————————————————————————————————————————————————————————————————————————————————————— ; Pre-HORROR ROM comments begin here. ; ——————————————————————————————————————————————————————————————————————————————————————— ; <40> 7/11/91 HJR Call HDSpinDown in sleep if HDSpindownflag is not clear and on ; wakeup move KdbReset before interrupts are enabled. Also added ; SleepHook and WakeUpHook to sleep code, and vectorized ModemSnd ; Routines in PMgrGlobals. ; <39> 7/9/91 HJR Added CPUSpeed to PowerDispatch Trap and set SaveSpeedo to ; appropriate speed in InitPMgrVars. ; <38> 7/7/91 HJR Add some more reset for the progressive power cycling. ; <37> 7/3/91 HJR Fix VM powercycling problem by restoring the VBR if we decide to ; skip power cycling due to character pending on SCC. ; <36> 6/25/91 HJR Updated IdleMind to reset progressive power cycling ; appropriately. Fixed bug in PowerCycling030 and PowerCycling020 ; to set AutoInt7 appropriately. ; <35> 6/25/91 ag added hysteresis low power value to power manager globals. ; <34> 6/25/91 HJR Set cursor to a watchcursor when going to sleep and fix a bug in ; IdleDelay where a BGE should have been a BLE. ; <33> 6/24/91 HJR Individually test SlpTimeOut and HDTimeOut for zero in ; InitPmgrVars. Determine whether running on '020 or '030 at ; InitPmgrVar and load appropriate power cycling code. Added ; IdleRead, IdleEnable, and IdleDisable to the list of ; PowerDispatchVectors and rewrote IdleState to use these new ; routines. ; <32> 6/24/91 djw Moved init code for notification mgr record from InstallMsg to ; InitPmgr. Added code to install and remove message in case of a ; bad battery condition interrupt from power mgr. Removed modem ; sound int handler from InitPmgr. Add support to disable posting ; notification messages for low power. Fixed bug in SerPowerOff ; and external ports. Added code to call LAPMgr for status of ; port B. ; <31> 6/12/91 ag changed the default of the network check to sleep. ; <30> 6/12/91 ag added network warning override bit in PmgrFlags. ; <29> 6/11/91 HJR Parameterized progressive power cycling with PwrCycProgGrow and ; PwrCycProgMax in PMgrRec. Fixed bug in Installmsg where stack ; was corrupted if message string was null. ; <28> 6/11/91 djw Fix modem sound support using VIA interrupt handlers. ; <27> 6/9/91 HJR Used new Default equates in InitPMgrVar for Power Cycling. Fixed ; bug in checking whether there has been enough elapsed time ; before power cycling. Added IdleDelay to PowerDispatch in order ; to improve IO performance. Modified the Batman saving and ; restoring from sleep so that the registers are written out in ; appropriate order. Cleared the Sound Latch after restoring from ; sleep to hopefully kill the buzzing sound while waking up. ; <26> 5/31/91 djw Add modem sound interrupt routines and install them in ; _SerialPower ; <25> 5/23/91 HJR Returned to a more universal power cycling scheme. Added include ; for PowerPrivEqu.a. Removed WaitStates sleep queue element since ; problem is now solved in hardware, HOPEFULLY! ; <24> 5/10/91 HJR Removed references of ResetSP since it is now saved in ; PMgrGlobalsRec. Changed PMgrTrap to call PollProc before ; interrupts re-enbled. Correct problem VM/NMI bug by setting and ; restoring NMIvector to the restore code during power cycling. ; <23> 4/29/91 HJR Added semaphor for power-up conditions in power cycling. Made ; sleep more universal by calculating the video-ram space from ; universal instead of using hard-coded addresses. ; <22> 4/23/91 ag switch back to supervisor mode on exit of sleep. ; <21> 4/23/91 ag alerts must be run in user mode! so we moved the restore to ; user mode earlier. ; <20> 4/22/91 ag Fixed missing dereference in data structure. ; <19> 4/16/91 HJR Somebody forgot to add a semicolon to the comment part of their ; line! ; <18> 4/15/91 ag added sound vbl code to turn off power to the sound circuits if ; not in use. ; <17> 4/12/91 ag changed the location of "insleep" flag in the power manager ; locals. Changed name of pram flags to avoid conflict with ; reality sources. ; <16> 4/4/91 ag slight correction to test for null proc. ; <15> 4/4/91 ag fixed bug with sleep queue execution. added check for null proc ; pointer. ; <14> 4/3/91 HJR Restore DFAC state on sleep wakeup ; <13> 4/1/91 HJR Modified the switching from User Mode to Supervisor Mode so that ; interrupts may be enable prior to running the queue out of ; wakeup since .MPP needs them enabled. Save all necessary Batman ; sound registers. ; <12> 3/29/91 ag Removed power manager bus contention check. the protocal has ; been changed to just check for busy and retry. Also changed ; timeout to take the new retry protocal into account (extended ; timeouts). ; <11> 3/19/91 jmp Oops, sombody forgot to put a semicolon in front of a comment. ; <10> 3/19/91 HJR Restore SP during wakeup since JumpintoRom sets it. Changed ; register useage in InitPMgrVars since GetRealProc trashes ; d1/a1-a2. ; <9> 3/18/91 HJR Rolled in sleep changes from Reality. Moved sleep to Ram based ; instead of video for performance improvement. Made sure that ; all access to the Video storage is done when the MMU is off ; because of ; MMU wrap. ; <8> 2/18/91 HJR Added DebugUtil call in IdleMind and GotoSleep to set the ; machine into Supervisor mode so that VM will be happy. ; <7> 1/30/91 HJR Changed PwrCycleCount to be part of PwrMgrVars and initialized ; in InitPwrMgrVars. ; <6> 1/24/91 HJR Cleared the bus prior to entering into power cycling by writing ; a zero to rom space. Also changed the write to the Jaws ; register to a Move.l and hit the waitstate register coming out ; of powercycle. ; <5> 1/24/91 HJR Moved IdleMind from PwrControlPatches.a. Also rewrote ; PowerCycling code for speed improvements and MMU bug fixes. ; <4> 1/22/91 djw Replace SerPowerOn and SerPowerOff in SerialPower trap code to ; work with TIM and the TIM modem. ; <3> 1/15/91 HJR Add WaitSSleepTask and install code to InitPMgrVars ; <2> 12/11/90 HJR Updated to the latest PowerMgr interface. ; <2.2> 6/10/89 SWC Moved InitPmgrVars here from StartInit.a. ; <2.1> 4/13/89 MSH Removed level 3 low power warning. ; <2.0> 4/7/89 MSH Gave battery and environment interrupts new names. PmgrInt now ; uses vectors to dispatch to handlers. Clearing low battery bit ; removes any low power message. CloseATalk saves D2. ; <1.9> 3/31/89 MSH Power off turns off the modem, then the rest of power, then ; calls sleep. CloseATalk rewritten. DOQueue now knows about ; sleepnow. If more than two ADB devices in use then ADBReInit ; called at wake up. A sleepnow hides the cursor. Batterymon ; replaced with SndWatch and made a vbl task. ; <1.8> 3/14/89 MSH CloseAtalk needed to close MPP. Mild warning didn't close XPP. ; At WakeUp restart timers the right way. ReInit Normandy test ; register after sleep. Ignore ENV interrupt. ; <1.7> 3/10/89 MSH PowerOff does a SleepNow ; <1.6> 3/9/89 MSH Forgot to check for any mounted file servers when sleep time ; out. ; <1.5> 3/2/89 MSH Reset keymap when sleep called. Spin down and sleep time outs ; reduced to one value each. Low power warning resources are ; loaded into local memory at init. No low power warning may occur ; before system task gets called once. Some local storage use got ; proper equ names. ; <1.4> 2/8/89 MSH Added high temperature warning support, moved all hard disk and ; sleep time out to systemtask, low power warnings are now four in ; number. The last warning goes to sleep in ten seconds. Sleep now ; has request, demand, and now, the last is for low power ; condition. Before calling the sleep queue a check is made of the ; AppleTalk world and if any servers or other activity is present. ; If so the user is warned and given the option to undo sleep. A7 ; is now saved in undisplayed video ram rather than in SERegs ; where it interfered with Macsbug. Make use of charger status ; bits to determine if charger connection state has changed. ; <1.3> 12/14/88 MSH Save and restore RAM wait state register, power off uses jump to ; startboot instead of RESET, kick on timer 1 after sleep. ; <1.2> 11/30/88 MSH Fixed the sound control stuff. Gave time out flags legit name. ; Save and restore speed from stack. ; <1.1> 11/10/88 CCH Fixed Header. ; <1.0> 11/9/88 CCH Adding to EASE. ; <2.2> 11/1/88 MSH Added a whole bunch of stuff. First, the sound watchdog is in ; and running. If no one hits on the ASC for ten seconds then the ; amplifier power is turned off. Also save and restore the ASC ; control registers at sleep. Second, the battery monitor and low ; power interrupt handler are in place. The user is alerted via ; the background notification manager of entering reserve power ; use, half reserve left, and a quarter of reserve power left. One ; SICN and three STR resources required on system disk. Last, the ; power off code called from the shutdown manager is found here. ; <2.1> 9/29/88 MSH Moved the reading of the charger state and time outs from the ; one second stuff to an interrupt routine. BatteryAlrt handles ; the charger connected state change interrupt from the power ; manager. BatteryMon checks the dirty time out data flag to see ; if it is necessary to update the time outs due to a change to ; them from the battery desk accessory. ; <1.9> 9/12/88 MSH Fixed bug in stack framse usage. One second interrupt now points ; to BatteryMon. BatteryMon first calls the one second interrupt ; then checks the state of the battery charger and the time outs. ; Screen saving no longer allocates memory. ; <1.8> 8/5/88 MSH Made WakeUp vectored. Added sleep q calling code. ; <1.7> 7/19/88 MSH Fixed potential stack error in PMGRInt. Save the state of the ; PMGR interrupt bit from VIA at entry and restore on exit. Also ; do more VIA save and restore at wake up. ; <1.6> 6/24/88 MSH Many revisions: Use pmgr local vars to save clock speed. Check ; for valid pmgr vars pointer before using it. Went back to ; original busy test of pmgr chip. Sleep command now has signature ; word sent with command. Removed InitIWM call, didn't actually do ; anything anyway. Temporarily removed call to ADBreInit and added ; kludge to keep old power managers auto polling after sleep. ; <1.5> 6/15/88 MSH Removed one too many SWAPs from PmgrDone. Also changed the test ; for pmgr ready to only use the upper half of the port. ; <1.4> 5/23/88 MSH Trashing speed code saved in upper half of D3 with a MOVEQ. ; <1.3> 5/19/88 BBM Changed two MOVEQs to MOVE.Ls as they were out of range ; <1.2> 4/21/88 MSH Fixed reset vector getting trashed when saving screen. ; <1.1> 3/28/88 BBM Made sure that this code runs at 16M. Blank screen before sleep. ; <1.0> 2/10/88 BBM Adding file for the first time into EASE… ; 12/21/87 MSH Exported GoToSleep for use by toolevents. ; 12/16/87 MSH Made start of handshake more tolerant of interrupts. ; 11/6/87 MSH Mask out PMGR interrupts while in PmgrOp. ; 11/5/87 MSH Rewrote PmgrInt to use new interrupt interface to PMGR. ; 10/21/87 MSH More of the same. ; 10/20/87 MSH Fixed some bugs, turned off interrupts while waiting for pmgr ; ack, and preserved sound bits that may be in VIA port A. ; 9/21/87 MSH New today. ; BLANKS ON STRING ASIS PRINT OFF LOAD 'StandardEqu.d' INCLUDE 'HardwarePrivateEqu.a' INCLUDE 'UniversalEqu.a' INCLUDE 'PowerPrivEqu.a' INCLUDE 'PowerMgrDispatchEqu.a' INCLUDE 'Appletalk.a' INCLUDE 'LAPEqu.a' INCLUDE 'Notification.a' INCLUDE 'IopEqu.a' INCLUDE 'Egretequ.a' INCLUDE 'AppleDeskBusPriv.a' INCLUDE 'MMUEqu.a' INCLUDE 'IOPrimitiveEqu.a' INCLUDE 'DockingEqu.a' INCLUDE 'GestaltEqu.a' INCLUDE 'ROMEqu.a' INCLUDE 'Slots.a' PRINT ON MACHINE MC68030 MC68881 Unimplement EQU $A89F ; _Unimplemented trap PowerMngr PROC EXPORT EXPORT InitPmgrVars IF hasPwrControls THEN EXPORT BatInt EXPORT BatWatch EXPORT GoToSleep EXPORT PmgrInt EXPORT PmgrOp EXPORT PortableCheck EXPORT PowerDispatch EXPORT PowerDownAll EXPORT SetSupervisorMode EXPORT WakeUp EXPORT PmgrTrap EXPORT IdleUpdate EXPORT IdleUpdateTrap EXPORT IdleDelay EXPORT IdleMind EXPORT IdleRead EXPORT IdleEnable EXPORT IdleDisable EXPORT IdleState EXPORT CPUSpeed EXPORT BasePRAM EXPORT ScaledBattery EXPORT PowerMgrHook EXPORT PDimScreens EXPORT PMGRrecv EXPORT PMGRsend EXPORT PrivateFeatures EXPORT SecondaryInitproc EXPORT SerialPower EXPORT ScsiDiskModeproc EXPORT ExternaVideoOnproc EXPORT ModemTypeProc EXPORT PowerMgrDispatch EXPORT ModemStatusRT EXPORT LCDScreenChk EXPORT GetButtonValues EXPORT SetHDState IMPORT CacheFlush ; Dispatch.a IMPORT DelayNMsec ; PowerMgrPrimitives.a IMPORT DockingSleepDenied ; DockingMgr.a IMPORT DockingWakeupDenied ; DockingMgr.a IMPORT GetHardwareInfo ; Universal.a IMPORT GetRealProc ; GetReal.a IMPORT GracefulShutdown ; DockingMgr.a IMPORT InitQueue ; Queue.a IMPORT InitSCSIHW ; SCSIMgrInit.a IMPORT InitWallyWorld ; WallyWorld.a IMPORT RdXByte ; USTPram.a IMPORT RSetKMap ; ADBMgr.a IMPORT SCSIDiskWakeAlert ; SCSIDiskMode.a IMPORT SetupTimeK ; StartInit.a IMPORT USTPMgrSendByte ; USTStartUp.a IMPORT USTPMGRSendCommand ; USTStartUp.a WITH NMRec,PmgrRec,ADBVars,ADBDeviceEntry WITH DecoderInfo,DecoderKinds,ProductInfo,VideoInfo,SpBlock WITH pmCommandRec,SleepqRec,PowerCycleRec,PowerDispRec,PmgrPramRec WITH PmgrPrimitivesRec,PmgrRoutineRec,PrimInfoTbleRec WITH IdleMindTblRec,ModemTblRec,HDQueueElement ENDIF ;••••••••••••••••••••••••••••••••••• Initialization ••••••••••••••••••••••••••••••••••••• ; ; Contains: ; ; InitPMgrVars ; SetUpPmgrBase ; SizeTables ; BuildTables ; InitPmgrGlobals ; InitPMgrOp ; DoPmgrCommands ; GetPmgrPRAM ; InstallVBLs ;________________________________________________________________________________________ ;________________________________________________________________________________________ ; ; Routine: InitPMgrVars ; ; Inputs: none ; ; Outputs: none ; ; Trashes: none ; ; Function: allocates and initialized system heap space for the Power Manager's variables, ; installs VBL tasks, etc. ;________________________________________________________________________________________ InitPmgrVars IF hasPwrControls THEN TestFor hwCbPwrMgr BNE.S @DoPMgrInit ; IF pmgr exists THEN intall pmgr stuff ENDIF ; {hasPwrControls} LEA @Traps,A1 ; ELSE remove the PmgrOp and Sleep traps MOVE.W (A1)+,D0 ; Get the Unimplimented trap location _GetTrapAddress ,newTool ; @TrapLoop ; WHILE !EndOfList DO { MOVE.W (A1)+,D0 ; Get the next trap to remove BEQ.S @exit ; _SetTrapAddress ,newOS ; Replace the trap with the Unimplimented BRA.S @TrapLoop ; } LOOP @exit ; RTS ; EndELSE @Traps _Unimplemented _PmgrOp _PowerDispatch _Sleep DC.W 0 IF hasPwrControls THEN @DoPMgrInit @WorkingSet REG D0-D3/A0-A3 MOVEM.L @WorkingSet,-(SP) ; save them regs BSR SetUpPmgrBase ; set up the PMgr Globas A3 = PmgrGlobals BSR InitPmgrGlobals ; Initialize our primitives table BSR InitPMgrOp ; Initialize variables used by PMgrOp BSR DoPmgrCommands ; Initialize variables through the PMgrOp Call BSR DoDynamicSpeedChange ; Initialize timing constants for full/reduced speed BSR GetPmgrPRAM ; Initialize variables through PRam LEA SleepQHdr(A3),A1 ; init sleep queue BSR.L InitQueue ; go do it BSR InstallVBLs ; Install our VBLs ; BSR.L HandleChargeTime ; setup for bulk charge extension LEA PMGRInt,A0 ; get addr of PMGR interrupt handler MOVE.L A0,Lvl1DT+(4*ifCB1) ; install as PMGR interrupt receiver BSR ResetPMGRInts ; clear any pending PMGR interrupts MOVEM.L (SP)+,@WorkingSet ; restore them regs RTS ;________________________________________________________________________________________ ; ; Routine: SetUpPmgrBase ; ; Inputs: none ; ; Outputs: A3 - Ptr to PmgrGlobals ; ; Trashes: none ; ; Function: Determines the appropriate size of the PmgrGlobals section. It takes as a base ; the PmgrVarSize and walks through the primitives table and sums up there sizes. ;________________________________________________________________________________________ SetUpPmgrBase @WorkingSet REG D0/A0-A2 MOVEM.L @WorkingSet,-(SP) ; Save them regs MOVEA.L UnivInfoPtr,A1 ; point to the ProductInfo table ADDA.L PowerManagerPtr(A1),A1 ; then to the Power Manager's primitives MOVE.L #PmgrVarSize,D0 ; Start with the Vars Size BSR SizeTables ; Size the table for new ptr _NewPtr ,SYS,CLEAR ; allocate space on heap and clear MOVE.L A0,PmgrBase ; save ptr to it LEA PmgrVarSize+8(A0),A2 ; get the vPrimitives pointer MOVE.L A2,vPMgrPrimitives(A0) ; set the vPMgrPrimitives vector MOVEA.L A2,A0 ; set pointer to beginning of our memory chunk BSR BuildTable ; build table BSR.L CacheFlush ; flush caches to be safe MOVE.L PmgrBase,A3 ; A3 = Ptr to PmgrGlobals MOVEQ #4/4,D0 ; get the size of the public dispatch table ADD.W PwrMgrDispVects-2,D0 ; ASL.L #2,D0 ; _NewPtr ,SYS ; and allocate space for it in the system heap BNE.S @GetOut ; -> errors aren't allowed LEA PwrMgrDispVects-4,A1 ; MOVE.L (A1)+,D0 ; get the flags and routine count MOVE.L D0,(A0)+ ; and copy them to RAM MOVE.L A0,vPublicDispatch(A3) ; save the pointer to the start of the table MOVE.L A1,D1 ; remember where the table starts in ROM SUBQ.W #1,D0 ; adjust the count for the DBRA @CopyPublic MOVE.L (A1)+,(A0) ; copy the routine offset into RAM ADD.L D1,(A0)+ ; and convert it into an address DBRA D0,@CopyPublic ; next entry @GetOut MOVEM.L (SP)+,@WorkingSet ; Restore them regs RTS ;________________________________________________________________________________________ ; ; Routine: SizeTables ; ; Inputs: A1 - Pointer to top of table ; D0 - Initial size of to be incremented ; ; Outputs: D0 - Incremented Size of Table ; ; Trashes: none ; ; Function: Determines the appropriate size of the tables. ;________________________________________________________________________________________ SizeTables @WorkingSet REG D1-D3/A1-A2 MOVEM.L @WorkingSet,-(SP) ; save them registers MOVE.L -8(A1),D1 ; get the flags ANDI.L #PrimsTypeMask,D1 ; mask out all but last two bits MOVE.W @EntryType(D1.W*2),D1 ; get the offset to that routine JMP @EntryType(D1) ; go to that routine @Table ; Size a Table of Tables MOVE.L -4(A1),D1 ; setup dbra counter LSR.L #2,D1 ; arrange it as long word entries MOVEA.L A1,A2 ; A2 = Beginning rom base of table ADD.L -4(A1),D0 ; increment for size of table ADDQ #8,D0 ; account for the size byte and the flags MOVEQ #0,D2 ; clear the register @TblLoop ; FOR 0 to MaxNum DO MOVE.L (A2,D2.L*4),D3 ; get the offset of the first routine BEQ.S @NilValue ; if offset = 0 then bail LEA (A2,D3.L),A1 ; get pointer to table BSR SizeTables ; build the table @NilValue ADDQ.L #1,D2 ; increment index CMP.L D1,D2 ; . BLT.S @TblLoop ; LOOP BRA.S @Done @PmgrEx @Ptr @Info ADD.L -4(A1),D0 ; increment the size ADDI.L #8,D0 ; add in flags and size field @Done MOVEM.L (SP)+,@WorkingSet ; restore them registers RTS @EntryType DC.W @Table-@EntryType ; Table of Tables DC.W @Ptr-@EntryType ; Table of pointers DC.W @Info-@EntryType ; Table of info DC.W @PmgrEx-@EntryType ; Table of Power Manager Op Exceptions DC.W 0 ; Expansion DC.W 0 ; Expansion DC.W 0 ; Expansion DC.W 0 ; Expansion ;________________________________________________________________________________________ ; ; Routine: BuildTables ; ; Inputs: A0 - Ptr to location where table is to be built ; A1 - Ptr to ROM table ; ; Outputs: none ; ; Trashes: none ; ; Function: Build all the tables into ram and convert all offsets into pointers for easier ; patching. ;________________________________________________________________________________________ BuildTable @WorkingSet REG D0-D3/A0-A3 MOVEM.L @WorkingSet,-(SP) ; save them registers LEA -8(A1),A1 ; get pointer to flags MOVE.L (A1)+,D0 ; get a copy of the flags MOVE.L (A1)+,D1 ; get a copy of the size MOVE.L D0,-8(A0) ; copy out the flags MOVE.L D1,-4(A0) ; copy out the size LSR.L #2,D1 ; arrange it as long word entries ANDI.L #PrimsTypeMask,D0 ; mask out all but last two bits MOVE.W @EntryType(D0.W*2),D0 ; get the offset to that routine JMP @EntryType(D0) ; go to that routine @EntryType DC.W @Table-@EntryType ; Table of Tables DC.W @Ptr-@EntryType ; Table of pointers DC.W @Info-@EntryType ; Table of info DC.W @PmgrExLoop-@EntryType ; Table of Power Manager Op Exceptions DC.W 0 ; Expansion DC.W 0 ; Expansion DC.W 0 ; Expansion DC.W 0 ; Expansion @Table ; Build a Table of Tables MOVEA.L A0,A3 ; A3 = Beginning ram base of table MOVEA.L A1,A2 ; A2 = Pointer to rom base of table MOVEQ #0,D2 ; clear the register @TblLoop ; FOR 0 to MaxNum DO ADD.L -4(A1),A0 ; increment ram pointer for size of table ADDQ.W #8,A0 ; account for the size byte and the flags @Cont MOVE.L (A2,D2.L*4),D3 ; get the offset of the first routine BEQ.S @NullVal ; IF offset != 0 THEN MOVE.L A0,(A3,D2.L*4) ; save pointer to table in table LEA (A2,D3.L),A1 ; make it a pointer BSR.S BuildTable ; build the table ADDQ.L #1,D2 ; increment index CMP.L D1,D2 ; . BLT.S @TblLoop ; LOOP BRA.S @Done @NullVal ADDQ.L #1,D2 ; increment index CMP.L D1,D2 ; . BLT.S @Cont ; LOOP BRA.S @Done ; and continue on @Ptr ; Build a Table of Pointers MOVEQ #0,D2 ; clear the register @PtrLoop ; FOR 0 to MaxNum DO MOVE.L (A1,D2.L*4),D3 ; get the offset of the first routine BEQ.S @NullValue ; IF offset != 0 THEN ADD.L A1,D3 ; add the offset @NullValue MOVE.L D3,(A0)+ ; save it in the table ADDQ.L #1,D2 ; increment index CMP.L D1,D2 ; . BLT.S @PtrLoop ; LOOP BRA.S @Done @Info ; Build a Table of Info SUBQ.W #1,D1 ; help out dbra god @InfoLoop ; FOR 0 to MaxNum DO MOVE.L (A1)+,(A0)+ ; copy info directly DBRA D1,@InfoLoop ; LOOP BRA.S @Done @PmgrExLoop ; WHILE notDone DO { MOVE.W (A1)+,D0 ; get the mask & Exception | MOVE.W D0,(A0)+ ; copy them directly into Ram Tables v BEQ.S @Done ; IF mask & Exception == NULL THEN notDone = FALSE MOVE.L A1,D0 ; get pointer to entry ADD.L (A1)+,D0 ; calculate the handler's address MOVE.L D0,(A0)+ ; stuff address in the Ram Tables BRA.S @PmgrExLoop ; } @Done MOVEM.L (SP)+,@WorkingSet ; restore them registers RTS ;________________________________________________________________________________________ ; ; Routine: InitPmgrGlobals ; ; Inputs: A3 -- Pointer to PmgrVars ; ; Outputs: none. ; ; Trashes: D0/A0-A2 ; ; Function: Determines the appropriate size of the tables. ; ;________________________________________________________________________________________ InitPmgrGlobals ; Initialize using InfoTbl LoadTbl PrimInfoTblPtr,A3,A2 MOVE.B PrimPRAMBase(A2),\ PRAMbase(A3) ; Save value in globals for future reference MOVEQ #0,D0 ; clear D0 MOVE.B PrimDefHyst(A2),D0 ; get a byte from primitives MOVE.W D0,Hysteresis(A3) ; set default hysteresis value MOVE.W PrimLowWarn(A2),\ LowWarn(A3) ; set default low/dead battery warning levels MOVE.W PrimPRAMBase(A2),D0 ; get PrimWakeLvl:PrimBatWarnCt in d0 EXT.W D0 ; extend the byte value to word (value should be < 127) MOVE.W D0,BatteryWarnDly(A3) ; load the dialog delay count for shorted battery int's MOVE.W D0,BatteryWarnCnt(A3) ; init battery warning count for shorted battery int's MOVE.L PowerCycRegAddr(A2),\ PowerCycleReg(A3) ; get the address of the power cycle register MOVE.B PrimCycRegValue(A2),\ PwrCycRegValue(A3) ; get the value for the power cycle register ; Initialize using RoutinesTbl LoadTbl PmgrRoutineTbl,A3,A2 MOVE.L PowerCycPtr(A2),D0 MOVE.L D0,PwrCycProc(A3) ; Save pointer in Globals MOVE.L PowerCycResPtr(A2),D0 MOVE.L D0,PwrCycRestore(A3) ; Save pointer in Globals MOVE.W #PwrCycWaitTmDef,\ PwrCycWaitTime(A3) ; Set PwrCycWaitTime MOVE.W #PwrCycSynCntDef,\ PwrCycSyncCount(A3) ; Set number of SyncIdles <27> HJR MOVE.W #PwrCycleDef,\ PwrCycCounter(A3) ; Set number of power cycle loops. <7> HJR MOVE.W #PwrCycDelayDef,\ PwrCycDelay(A3) ; Set default delay <27> HJR MOVE.W #PwrCycProgGrowDef,\ PwrCycProgGrow(A3) ; Set growth increment for power cycling <29> HJR MOVE.W #PwrCycProgMaxDef,\ PwrCycProgMax(A3) ; Set maximum size for power cycling <29> HJR MOVE.L #$0FFFFFFF,\ DimmingWaitTime(A3) ; Set Time till we start dimming MOVE.L EnvIntPtr(A2),D0 MOVE.L D0,vEnvInt(A3) ; save the handler address (or nil if none) ; Initialize the rest MOVEQ #1,D0 MOVE.L D0,LastAct(A3) ; Init last activity and disk counters MOVE.L D0,LastHd(A3) ADDQ.W #nmType,BNmQEntry+qType(A3) ; initialize some notification record fields MOVEQ #-1,D0 MOVE.L D0,BNmQEntry+nmSound(A3); Use default sound LEA BatInt,A1 ; Set up int handlers MOVE.L A1,vBatInt(A3) LEA WakeUp,A1 ; Get Address of WakeUp Routine MOVE.L A1,WakeVector(A3) ; Save in the Globals MOVE.L A3,A0 ; Param A0 = Ptr to Pmgr Globals IF NOT forRomulator THEN ; BSR.L GetRealProc ; Get the physical address of PMgrVars--Warning Destroys Regs D1/A1-A2 ENDIF MOVE.L A0,PmgrVarPhysPtr(A3) ; Save it off in PwrMgr Globals MOVE.W #CPUSpeedDisp,D0 ; Find out what speed we are running <39> HJR _PowerDispatch ; <39> HJR MOVE.B D0,SaveSpeedo(A3) ; Set saveSpeedo to current speed <39> HJR LEA DoSpinDown,A1 ; initialize the hard disk spindown vector MOVE.L A1,HDvector(A3) ; LEA DoHDSpinUP,A1 ; initialize the hard disk spinup vector MOVE.L A1,HDSpinUpVector(A3) ; BigLEA GracefulShutdown,A1 ; issue shutdown command (AppleEvent) MOVE.L A1,vSoftShutdown(A3) ; LEA ModemSndOnInt,A1 ; get addr of modem sound interrupt handler djw MOVE.L A1,MdmSndVect(A3) ; install PmgrGlobal <40> HJR ST TOdirtyFlag(A3) ; set time outs to be dirty BSET.B #ClamshellClosed,\ PmgrFlags(A3) ; initialize the value to be closed RTS ;________________________________________________________________________________________ ; ; Routine: InitPMgrOp ; ; Inputs: A3 -- pointer to Power Manager's variables ; ; Outputs: none ; ; Trashes: D1/A0-A2 ; ; Function: initializes all the variables required by PMgrOp ;________________________________________________________________________________________ InitPMgrOp LEA cmdCounts,A0 ; save pointers to the PMgrOp send/receive count tables MOVE.L A0,vSendCountTbl(A3) ; LEA replyCounts,A0 ; MOVE.L A0,vRecvCountTbl(A3) ; BSR.S SetupPMgrOpInterface ; get the PMgrOp exception table MOVE.L A1,pmgrOpExceptions(A3) ; and save it RTS ; ;________________________________________________________________________________________ ; ; Routine: SetupPMgrOpInterface ; ; Inputs: none ; ; Outputs: D1 -- communications protocol (0=parallel, 2=serial, ...) ; D2 -- 0 = Rom Exception Tables ; A1 -- pointer to the start of an exception table ; ; Function: returns the protocol type, and a pointer to an exception table for ; handling commands that are either partially or not handled by the ; PMGR microcontroller due to differences in hardware implementations. ; ; Each entry consists of a byte for the "don't care bits" mask, ; a byte for the command number, and a long word for a relative ; offset from the current location to the special handler. ;________________________________________________________________________________________ SetupPMgrOpInterface MOVE.L PmgrBase,A3 ; get pointer to PmgrBase MOVEQ #0,D1 ; clear register MOVE.L A3,D2 ; ADDQ.L #1,D2 ; IF Power Manager vars valid THEN BEQ.S @UseROMExceptions ; LoadTbl PrimInfoTblPtr,A3,A1 ; A0 = Power Managers Info table (RAM) MOVE.B PrimPMgrCommType(A1),D1 ; D1 = communications protocol type (RAM) LoadTbl PMgrOpExcepTbl,A3,A1 ; A1 = Power Managers Exception Table (RAM) RTS ; ELSE @UseROMExceptions ; MOVEA.L UnivInfoPtr,A1 ; A1 = ProductInfo table (ROM) ADDA.L PowerManagerPtr(A1),A1 ; A1 = ROM Power Manager's primitives (ROM) MOVEA.L A1,A2 ; A2 = A1 ADDA.L PrimInfoTblPtr(A2),A2 ; A2 = Power Managers Info table (ROM) MOVE.B PrimPMgrCommType(A2),D1 ; D1 = communications protocol type (ROM) ADDA.L PMgrOpExcepTbl(A1),A1 ; A1 = Power Managers Exception Table (ROM) RTS ; ;________________________________________________________________________________________ ; ; Routine: DoPmgrCommands ; ; Inputs: A3 -- Pointer to PmgrVars ; ; Outputs: none. ; ; Trashes: D0/A0-A1 ; ; Function: Determines the appropriate size of the tables. ; ;________________________________________________________________________________________ DoPmgrCommands CLR.L -(SP) ; zero the buffer MOVE.L SP,-(SP) ; pmRBuffer MOVE.L (SP),-(SP) ; pmSBuffer CLR.W -(SP) ; pmLength = 0 MOVE.W #PmgrADBoff,-(SP) ; pmCommand = turn off ADB auto poll MOVEA.L SP,A0 ; point to the parameter block _PMgrOp ; send the command LoadTbl PrimInfoTblPtr,A3,A1 ; get pointer to info table TST.B PrimChargerAttr(A1) ; IF charger attributes THEN BEQ.S @noChargerAttribs MOVE.B #ChargerOn,pmData(A0) ; ensure charger turned on MOVE.W #1,pmLength(A0) ; pmLength = 1 MOVE.W #power1Cntl,pmCommand(A0) ; pmCommand = power 1 control _PMgrOp ; send the command LoadTbl PrimInfoTblPtr,A3,A1 ; get pointer to info table MOVE.B PrimChargerAttr(A1),\ ; pmData(A0) ; stuff the attr MOVE.W #1,pmLength(A0) ; pmLength = 1 MOVE.W #setMachineAttr,\ ; pmCommand(A0) ; pmCommand = send cpu attributes to microcontroller _PMgrOp ; send the command @noChargerAttribs MOVE.W LowWarn(A3),D0 ; get the low warning MOVE.W D0,pmData(A0) ; stuff the default Pmgr low warning and cutoff BEQ.S @NoDefWarnings ; -> no defaults, so trust the PMGR to be right MOVE.W #2,pmLength(A0) ; pmLength = 2 MOVE.W #setBattWarning,\ pmCommand(A0) ; pmCommand = set low/dead battery levels _PMgrOp ; send the command @NoDefWarnings CLR.W pmLength(A0) ; pmLength = 0 MOVE.W #batteryRead,pmCommand(A0) ; pmCommand = read Battery State _PMgrOp ; send the command MOVEA.L pmRBuffer(A0),A0 ; get pointer to receive buffer MOVE.B (A0),Charger(A3) ; Initialize charger state BTST.B #HasCharger,Charger(A3) ; is the charger inserted BEQ.S @exitChrgStateInt ; if not, do nothing MOVE.W BatteryWarnDly(A3),\ BatteryWarnCnt(A3) ; load battery warn counter @exitChrgStateInt LEA pmBlkSize(SP),SP ; Remove stack frame

RTS ;________________________________________________________________________________________ ; ; Routine: DoDynamicSpeedChange ; ; Inputs: A3 -- Pointer to PmgrVars ; ; Outputs: none. ; ; Trashes: D0/A0-A1 ; ; Function: setup timing constants for full/reduced speed ; ;________________________________________________________________________________________ DoDynamicSpeedChange LoadTbl PrimInfoTblPtr,A3,A0 ; point to the info table for this machine MOVEQ #1< no _FullProcessorSpeed ; find out if we're running at full or reduced speed EORI.W #1,D0 ; 1=reduced, 0=full IF EconoBit THEN LSL.W #EconoBit,D0 ; shift it into the correct position ENDIF MOVE.W D0,D3 ; and save it for later BSR.S @SetProcessorSpeed ; save current timing constants and flip to the opposite speed mode MOVEA.L VIA,A0 ; save the VIA1 registers, MOVE.B vIER(A0),-(SP) MOVE.B vACR(A0),-(SP) MOVEA.L VIA2,A0 ; save the VIA2 interrupt enables MOVE.B Rv2IER(A0),-(SP) BSET #7,(SP) MOVE.B Rv2SEnb(A0),-(SP) BSET #7,(SP) MOVEQ #$7F,D0 MOVE.B D0,Rv2IER(A0) ; and disable the interrupts MOVE.B D0,Rv2SEnb(A0) MOVE.L A3,-(SP) ; save A3 BSR.L SetupTimeK ; go calculate the timing constants MOVEA.L (SP)+,A3 ; restore A3, MOVEA.L VIA2,A0 ; the VIA2 interrupt enables, MOVE.B (SP)+,Rv2SEnb(A0) MOVE.B (SP)+,Rv2IER(A0) MOVEA.L VIA,A0 ; and the VIA registers MOVE.B (SP)+,vACR(A0) MOVE.B (SP)+,vIER(A0) BSR.S @SetProcessorSpeed ; save the constants for the opposite speed mode, and restore the speed LEA fullSpeedDBRAs(A3),A1 ; assume we're running at full speed TST.W D3 ; are we? BEQ.S @RestoreDBRAs ; -> yep ADDQ.W #lowSpeedDBRAs-fullSpeedDBRAs,A1 @RestoreDBRAs MOVE.W (A1)+,TimeDBRA ; restore the original timing constants MOVE.W (A1)+,TimeSCCDB MOVE.W (A1)+,TimeSCSIDB MOVE.W (A1)+,TimeVIADB @NotDynamic RTS @SetProcessorSpeed LEA fullSpeedDBRAs(A3),A1 ; assume we're running at full speed TST.W D3 ; are we? BEQ.S @CopyDBRAs ; -> yep ADDQ.W #lowSpeedDBRAs-fullSpeedDBRAs,A1 @CopyDBRAs MOVE.W TimeDBRA,(A1)+ ; copy the timing constants from low mem MOVE.W TimeSCCDB,(A1)+ MOVE.W TimeSCSIDB,(A1)+ MOVE.W TimeVIADB,(A1)+ MOVEQ #1< SWAP D0 ; MOVE.B PRAMbase(A3),D0 ; lo-word = base address _ReadXPRam ; read in the PMGR PRAM settings CLR.B NTSC ; clear the NTSC flag (square corners) TST.W SlpTimeOut(A0) ; are sleep and hard disk timeouts valid? BNE.S @skipwrite ; -> yes MOVE.B #DfltSlpTime,\ SlpTimeOut(A0) ; Init default time outs MOVE.B #DfltHDTime,HDTimeOut(A0) MOVEQ #2,D0 ; hi-word = number of PRAM bytes to write SWAP D0 ; MOVE.B PRAMbase(A3),D0 ; lo-word = base address _WriteXPRam ; write out the default sleep and hard disk timeouts @skipwrite MOVE.B SlpTimeOut(A0),\ SleepTime(A3) ; Init default time outs for sleep MOVE.B HDTimeOut(A0),HDTime(A3); for hard disk LEA PmgrPramSize(SP),SP ; kill param block RTS ;________________________________________________________________________________________ ; Routine: InstallVBLs ; ; Inputs: A3 -- Pointer to PmgrVars ; ; Outputs: none. ; ; Trashes: none ; ; Function: Determines the appropriate size of the tables. ; ;________________________________________________________________________________________ InstallVBLs LoadTbl PmgrRoutineTbl,A3,A1 ; get pointer to Routine Table ; install the battery monitoring VBL task... MOVE.L BatteryVBLPtr(A1),D0 ; does this machine monitor the battery level? BEQ.S @NoBatteryVBL ; -> no, don't install the VBL task LEA BatVBLTask+vblCount(A3),\ A0 ; point to the end of the VBL task record MOVE.W #BatFreq,(A0) ; vblCount MOVE.L D0,-(A0) ; vblAddr ADDQ.W #vType,-(A0) ; vblType = vType SUBQ.W #vblType-vblink,A0 ; point to the beginning of the record _VInstall ; install the task @NoBatteryVBL ; install the sound usage monitoring VBL task... MOVE.L SoundVBLPtr(A1),D0 ; does this machine monitor sound usage? BEQ.S @NoSoundVBL ; -> no, don't install the VBL task LEA SwVBLTask+vblCount(A3),\ A0 ; point to the end of the VBL task record MOVE.W #SndWFreq,(A0) ; vblCount MOVE.L D0,-(A0) ; vblAddr ADDQ.W #vType,-(A0) ; vblType = vType SUBQ.W #vblType-vblink,A0 ; point to the beginning of the record _VInstall ; install the task @NoSoundVBL RTS ;•••••••••••••••••••••••••••• End Of Initialization ••••••••••••••••••••••••••••••••••••• ;••••••••••••••••••••••••••••• VBL's & Interrupts ••••••••••••••••••••••••••••••••••••••• ; ; Contains: ; ; BatWatch ; RemoveMsg ; InstallMsg ; PMGRInt ; BatInt ;________________________________________________________________________________________ ;________________________________________________________________________________________ ; ; Routine: BatWatch ; ; Inputs: A1 - pointer to VIA1 base ; ; Outputs: none ; ; Trashes: none (D0-D3, A0-A3 are preserved by the interrupt dispatcher) ; ; Function: Monitors the battery for low power conditions, and then alerts the user via ; the Notification Manager. Also updates dirty sleep and hard disk timeouts. ;________________________________________________________________________________________ BatWatch MOVE.L PmgrBase,A2 LEA BatVBLTask(A2),A0 ; Get pointer to vbl task MOVE.W #BatFreq,vblCount(A0) ; Do it again TST.B SysTaskFlag(A2) BEQ @exit @didsystask JsrRoutine GetLevelPtr,A2,A0 ; Read, average, and convert battery level BNE.S @valid ; into level -1 - 4, branch if data not ready MOVE.B #-1,LastLevel(A2) ; Reset last level BRA.S @BatWatchOut @valid TST.B D0 ; Test for negative current level BPL.S @positive @negative BSR RemoveMsg ; Remove old message (if any) MOVE.B D0,LastLevel(A2) ; Save level BRA.S @BatWatchOut @positive BEQ.S @lesser ; Level 0, do nothing CMP.B #2,D0 ; Level 2, remap to level 1 (last dialog level) BNE.S @lpowermode ; <2> … continue with low power stuff MOVEQ #1,D0 ; <2> … remap level 2 to level 1 @lpowermode TST.B LastLevel(A2) ; If LasLevel was -1 then new message BMI.S @newmsg CMP.B LastLevel(A2),D0 ; If NewLevel<=LastLevel then branch BLS.S @lesser @newmsg BSR RemoveMsg ; Remove previous level message BSR InstallMsg ; Install this level message CLR.B Level4Cnt(A2) ; Clear level 4 count down timer MOVE.B D0,LastLevel(A2) ; Save level BRA.S @BatWatchOut @lesser CMP.B #4,LastLevel(A2) ; If level 4 then inc timer BNE.S @BatWatchOut ADD.B #1,Level4Cnt(A2) @BatWatchOut LEA BatVBLTask(A2),A0 ; Get pointer to vbl task MOVE.W #BatFreq,vblCount(A0) ; Do it again LoadTbl PrimInfoTblPtr,A2,A0 ; Get pointer to info table TST.B LastLevel(A2) ; Is the level -1? BPL.S @Next ; No, try next check BCLR #PmgrWakeLvlSet,PmgrFlags(A2) ; Is the semaphor set? BEQ.S @FinishUp ; Nope. Then skip and go on MOVE.W PrimLowWarn(A0),D0 ; Get low power values BRA.S @SetPmgrLvl ; Set level and get on with life @Next CMP.B #4,LastLevel(A2) ; Check if we are in level 4 BNE.S @FinishUp ; If not, go on TST.B Level4Cnt(A2) ; Is it the first time through? BNE.S @FinishUp ; If not, go On BSET #PmgrWakeLvlSet,PmgrFlags(A2) ; Set the semaphor MOVE.W PrimLowWarn(A0),D0 ; Get low power values MOVE.B PrimWakeLvl(A0),D0 ; Get wake level values @setPmgrLvl TST.W D0 ; are there valid levels? BEQ.S @FinishUp ; -> no, figure the PMGR knows what to do SWAP D0 ; set the values in the top of buffer MOVE.L D0,-(SP) ; Set the parameter in the buffer MOVE.L SP,-(SP) ; pmRBuffer MOVE.L (SP),-(SP) ; pmSBuffer MOVE.W #2,-(SP) ; pmLength = 0 MOVE.W #setBattWarning,-(SP) ; pmCommand = set Battery warning level MOVEA.L SP,A0 ; point to the parameter block _PMgrOp ; send the command LEA pmBlkSize(SP),SP ; Remove stack frame @FinishUp TST.B TOdirtyFlag(A2) ; Check for new timeouts BEQ.S @exit CLR.B TOdirtyFlag(A2) ; Clear the dirty flag SUB.W #8,SP ; allocate some stack space MOVE.L SP,A0 ; set param block MOVEQ #PmgrPramSize,D0 ; hi-word = number of PRAM bytes to read SWAP D0 ; MOVE.B PRAMbase(A2),D0 ; lo-word = base address _ReadXPRam MOVE.B PmgrStatusFlags(A0),SleepFlags(A2) ; Get the sleep flags MOVE.W SlpTimeOut(A0),SleepTime(A2) ; Get latest timeouts ADD.W #8,SP ; free the buffer _IdleUpdateDispatch ; @exit RTS ;________________________________________________________________________________________ ; ; Routine: RemoveMsg ; ; Inputs: A2 - pointer to Power Manager's variables ; ; Outputs: none ; ; Trashes: A0 ; ; Function: removes any pending low power warning messages ;________________________________________________________________________________________ RemoveMsg BTST.B #AvoidLowPMsg,PmgrFlags(A2) ; are low power messages enabled? djw BNE.S nomsg ; no - don't do anything djw BatIntRemoveMsg TST.B lpMSGvalid(A2) ; If no messages pending then nothing to remove beq.s nomsg ; Yes. remove low power message MOVE.L D0,-(SP) LEA BNmQEntry(A2),A0 _NMRemove ; Remove low power warning message MOVE.L (SP)+,D0 CLR.B lpMSGvalid(A2) ; No messages pending nomsg RTS ;________________________________________________________________________________________ ; ; Routine: InstallMsg ; ; Inputs: A2 - pointer to Power Manager's variables ; ; Outputs: none ; ; Trashes: A0 ; ; Function: installs a low power warning message if they're allowed ;________________________________________________________________________________________ InstallMsg BTST.B #AvoidLowPMsg,PmgrFlags(A2) ; are low power messages enabled? djw BNE.S @nmproc ; don't post any message djw MOVE.L D0,-(SP) LEA BNmQEntry(A2),A0 ; Create notification entry LEA @nmproc,A1 MOVE.L A1,nmResp(A0) ; Use empty response routine below MOVE.L lpSICNHndl(A2),nmIcon(A0) ; Use the icon MOVE.W D0,D2 ; Copy level into D2 SUBQ.W #1,D2 ; Adjust for range 0 - 3 LSL.W #2,D2 ; Convert to offset MOVE.L lpSTR0Ptr(A2,D2.W),nmStr(A0) ; Index into messages strings TST.L nmStr(A0) ; Do we have a string? BEQ.S @nonminst ; Nope. Get out... _NMInstall ; Notify user of low power condition ST lpMSGvalid(A2) ; Message pending <29> HJR @nonminst MOVE.L (SP)+,D0 ; <29> HJR @nmproc RTS ; No notification proc now ;________________________________________________________________________________________ ; ; Routine: PMGRInt ; ; Inputs: A1 - pointer to VIA1 base ; ; Outputs: none ; ; Trashes: none (D0-D3 and A0-A3 are preserved by the interrupt dispatcher) ; ; Function: This is the Power Manager interrupt handler. It dispatches to the right ; interrupt handler for the type of interrupt received. ;________________________________________________________________________________________ PMGRInt MOVE.B #(1< MOVEA.L Lvl1DT+(4*ifCA2),A0 ; get the address of the one-second interrupt handler

JMP (A0) ; and call it

@isEnvInt MOVE.L PMgrBase,A2 MOVE.L vENVInt(A2),D0 ; Check for valid vector BRA.S @callHandler @isBATint MOVE.L PMgrBase,A2 MOVE.L vBatInt(A2),D0 ; Check for valid vector bra.s @callHandler @isADBint andi.b #$0F,(a0) ; mask off interrupt status bits move.b 1(a0),-(a0) ; [cmd] [stat] [cmd] subq.b #2,d1 ; actual count move.b d1,2(a0) ; [cmd] [stat] [len] move.l Lvl1DT+8,d0 ; Check for valid vector @callHandler beq.s @exit ; no handler, exit move.l d0,a1 jsr (a1) ; call handler @exit lea 12(sp),SP ; Done with stack frame rts ;________________________________________________________________________________________ ; ; Routine: BatInt ; ; 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 interrupts that occur because of a change in charger connection ; state, or a shorted battery condition is detected. ;________________________________________________________________________________________ BatInt moveq.l #0,d2 ; clear reg move.b 1(a0),d2 ; get battery flags move.b d2,Charger(a2) ; save charger state ; Charger Change interrupt btst.l #ChrgState,d2 ; was there a charger int beq.s @exitChargeState ; procede to next test btst.l #HasCharger,d2 ; is the charger inserted beq.s @exitChargeState ; if not, do nothing move.w BatteryWarnDly(a2),BatteryWarnCnt(a2); load battery warn counter @exitChargeState ; batter shorted interrupt btst.l #ShortedBat,d2 ; is there a shorted battery? beq.s @shortTestexit ; not this time tst.w BatteryWarnCnt(a2) ; if counter ble.s @postShortedBatt ; if less than or equal, post message subq.w #1,BatteryWarnCnt(a2) ; update counter bra.s @shortTestexit ; exit handler ; Shorted battery condition detected - post notification message ; Remove any pending low power messages and prevent any more from showing up so they ; don't try to remove the bad battery message. @postShortedBatt bsr BatIntRemoveMsg ; remove any old message bset.b #AvoidLowPMsg,PmgrFlags(a2) ; avoid confict with low power messages lea bbSTR0Ptr(a2),a1 ; get first vectored string bset.b #BadBatDet,PmgrFlags(a2) ; is this the first bad message? beq.s @continue addq.l #4,a1 ; index to second harsher message bclr.b #BadBatDet,PmgrFlags(a2) ; reset bad battery level for next time @continue tst.l (a1) ; does the string exist? beq.s @shortTestexit ; no string - do not post message lea BNmQEntry(a2),a0 ; a0 = ptr to notification mgr record move.l (a1),nmStr(a0) ; set string message lea @BatResp,a1 ; address of response routine move.l a1,nmResp(a0) ; set response routine move.l lpSICNHndl(a2),nmIcon(a0) ; Use the icon _NMInstall ; notify user of defective battery condition subq.l #4,sp movea.l sp,a0 ; a0 = pmgr pkt data buffer move.b #writePmgrRAM,d0 ; pmgr command moveq.l #3,d1 ; 3 bytes to send move.w #$0021,(a0) ; set addr to write to andi.l #~((1< ;•••••••••••••••••••••••••••• End of VBL's & Ints ••••••••••••••••••••••••••••••••••••••• ;••••••••••••••••••••••••••••••••••• Traps •••••••••••••••••••••••••••••••••••••••••••••• ; ; Contains: ; ; PowerDispatch ; IdleUpdate (Selector #1) ; IdleDelay (Selector #2) ; IdleMind (Selector #3) ; IdleRead (Selector #4) ; IdleEnable (Selector #5) ; IdleDisable (Selector #6) ; CPUSpeed (Selector #7) ; BasePRAM (Selector #8) ; ScaledBattery (Selector #9) ; PowerMgrHook (Selector #10) ; SecondaryInitproc ; ScsiDiskModeproc ; ExternaVideoOnproc ; ModemTypeProc ; PDimScreens (Selector #11) ; PrivateFeatures (Selector #13) ; GetButtonValues (Selector #15) ; SetHDState (Selector #16) ; ; PmgrOp ; IdleUpdate ; IdleState ; SerialPower ; Sleep ; SlpQInstall ; SlpQRemove ;________________________________________________________________________________________ ;________________________________________________________________________________________ ; ; Routine: PowerDispatch (trap $A09F) ; ; Inputs: D0 - selector ; ; Outputs: D0 - selector result, or error code if bad selector ; ; Trashes: varies by selector ; ; Function: This is the Power Manager's private dispatch trap, and provides a variety of ; miscellaneous functions. ;________________________________________________________________________________________ PowerDispatch WITH PwrDispatchRec MOVE.L PmgrBase,A0 ; Get pointer to globals LoadTbl PwrDispatchTbl,A0,A0 ; Get pointer to Table MOVEQ #0,D1 ; Clear D1 MOVE.W D0,D1 ; Get selector in D1 LSL.W #2,D1 ; Convert selector to bytes CMP.L PwrDispCount(A0),D1 ; IF Dispatch Out of Range THEN BHI.S @Error ; Get Out Of Here TST.L ([A0,D1.L]) ; ELSE Dispatch Non Existent THEN BEQ.S @Error ; Get Out of Here JMP ([A0,D1.L]) ; Go do that routine @Error MOVEQ #paramErr,D0 ; Abort and return error RTS ENDWITH ;________________________________________________________________________________________ ; ; Routine: IdleUpdate ($A285) (PowerDispatch selector #1) ; ; Inputs: none ; ; Outputs: D0 - time of last activity, in ticks ; ; Trashes: A1 ; ; Function: resets the idle compare time to Ticks unless the idle compare time is ; already pushed out beyond Ticks. ;________________________________________________________________________________________ IdleUpdateTbl DC.W Exit-IdleUpdateTbl ; $00 => OverallAct DC.W UsrAct-IdleUpdateTbl ; $01 => UsrActivity | DC.W NetAct-IdleUpdateTbl ; $02 => NetActivity v DC.W HDAct-IdleUpdateTbl ; $03 => HardDriveActivity TblEnd DC.W 0 ; $04 => padding for now IdleUpdateTrap MOVEQ #0,D0 ; clear selector IdleUpdate SWAP D0 ; Get our selector MOVE.W D0,D1 ; Get a copy of the selector MOVE.W SR,-(SP) ; No ints at this time OR.W #HiIntMask,SR MOVE.L PmgrBase,A1 ; get pmgr local MOVE.L LastAct(A1),D0 ; If LastAct > Ticks then branch CMP.L Ticks,D0 BHI.S @noLastActupdate MOVE.L Ticks,LastAct(A1) ; reset sleep/idle time @noLastActupdate CMP.W #(TblEnd-IdleUpdateTbl)/2,D1 ; IF entry outofbounds THEN BHI.S Exit ; exit MOVE.W IdleUpdateTbl(D1.W*2),D1 ; Get the relative offset to the routine. JMP IdleUpdateTbl(D1.W) ; GOTO the proper routine. UsrAct MOVE.L LastUsrAct(A1),D0 ; IF LastUsrAct > Ticks THEN CMP.L Ticks,D0 ; Exit BHI.S Exit ; ELSE MOVE.L Ticks,LastUsrAct(A1) ; Update user activity BRA.S Exit NetAct MOVE.L LastNetAct(A1),D0 ; IF LastUsrAct > Ticks THEN CMP.L Ticks,D0 ; Exit BHI.S Exit ; ELSE MOVE.L Ticks,LastNetAct(A1) ; Update user activity BRA.S Exit HDAct MOVE.L Ticks,LastHd(A1) ; Update user activity Exit MOVE.L LastAct(A1),D0 ; MOVE.W (SP)+,SR ; RTS ;________________________________________________________________________________________ ; ; Routine: IdleDelay (PowerDispatch selector #2) ; ; Inputs: none ; ; Outputs: none ; ; Trashes: D0,A1 ; ; Function: This routine will delay Idling (Power Cycling) by setting LastAct to some ; future time. This is in particular useful in Reads, and Writes where the ; PwrCycWaitTime might not be enough time for them to function properly. ;________________________________________________________________________________________ IdleDelay MOVE.W SR,-(SP) ; Save the status register ORI.W #HiIntMask,SR ; Turn off those interrupts MOVE.L PMgrBase,A1 ; Get the Globals MOVEQ #0,D0 ; Clear the register MOVE.W PwrCycDelay(A1),D0 ; Get the time delay used ADD.L Ticks,D0 ; update it with Ticks CMP.L LastAct(A1),D0 ; IF LastAct > New One THEN BLE.S @Done ; Get Out! <34> HJR MOVE.L D0,LastAct(A1) ; ELSE set new LastAct @Done MOVE.W (SP)+,SR ; Restore status register RTS ;________________________________________________________________________________________ ; ; Routine: IdleMind (PowerDispatch selector #3) ; ; Inputs: none ; ; Outputs: none ; ; Trashes: none ; ; Function: Checks for hard disk time out, sleep time out, idle time out, and low power ; sleep. The first check is for a low power condition, if so then sleep is ; immediately called. The next check is for hard disk spin down time out and ; sleep time out. These time outs may be disabled when on the battery if a ; special bit is set. The last check is for idle time out. ;________________________________________________________________________________________ IdleMind @savedRegs REG D0-D2/A0-A2 MOVEM.L @savedRegs,-(SP) ; Save some registers MOVE.L PmgrBase,A2 ; Get pmgr locals MOVE.W PwrCycSyncCount(A2),D0 ; Get the number of syncIdle before IdleMind ADDI.W #1,PMgrScratch.SyncCounter(A2) ; Increment scratch counter CMP.W PMgrScratch.SyncCounter(A2),D0 ; Have we done enough SyncIdles BLT @CommonOut CLR.W PMgrScratch.SyncCounter(A2) ; reset counter LoadTbl IdleMindTblPtr,A2,A1 ; Get pointer to IdleMindTble ST SysTaskFlag(A2) ; Flag that systemtask has been called JsrPrimTbl CountDownTimer,A1 ; Check if we are in countdown condition BNE.S @ForceSleep ; IF CountDownTimer THEN sleep JsrPrimTbl RunIdleRoutines,A1 ; Get pointer to Check for spin down HD MOVE.L LastAct(A2),D0 ; Get LastAct CMP.L Ticks,D0 ; IF LastAct > Ticks THEN BGT.S @CommonOut ; exit JsrPrimTbl SleepTimeOut,A1 ; Get pointer to Check for going to sleep BNE.S @RequestSlp ; IF SleepTimeOut THEN sleep JsrPrimTbl CheckIdle,A1 ; Get pointer to Check for going to power cycle BEQ.S @ClrOut ; IF activity THEN clearout JsrPrimTbl CalcProgPwrCyc,A1 ; Else calculate progressive count JsrPrimTbl CyclePower,A1 ; Get pointer to Go Power Cycling BRA.S @CommonOut ; That's All Folks @RequestSlp MOVEQ #SleepRequest,D0 ; Set a sleep request BRA.S @DoSleep ; Go ajourn @ForceSleep MOVEQ #SleepNow,D0 ; Set to force sleep @DoSleep BTST #dockNoSleep,dockFlags(A2) ; is sleeping allowed (if we're connected to a bar) BNE.S @SleepDeny ; -> no, avoid putting up a dialog _Sleep ; Go to sleep @SleepDeny BCLR #LowPowerSleep,PMgrFlags2(A2) ; clear the low power sleep flag MOVE.L Ticks,LastAct(A2) ; Waking updates last activity @ClrOut CLR.L PMgrScratch.ProgLastAct(A2) ; Reset progressive power cycling @CommonOut MOVEM.L (SP)+,@savedRegs ; Restore registers RTS ;________________________________________________________________________________________ ; ; Routine: IdleRead (PowerDispatch selector #4) ; ; Inputs: none ; ; Outputs: D0 - current CPU speed, in MHz ; ; Trashes: A0 ; ; Function: returns the current CPU speed ;________________________________________________________________________________________ IdleRead MOVE.W SR,-(SP) ; Save the status register ORI.W #HiIntMask,SR ; Kill those vicious interrupts MOVE.L PMgrBase,A0 ; Get pmgr locals MOVEQ #0,D0 ; Clear the register MOVE.B saveSpeedo(A0),D0 ; return current speed MOVE.W (SP)+,SR ; restore the status register RTS ;________________________________________________________________________________________ ; ; Routine: IdleEnable (PowerDispatch selector #5) ; ; Inputs: none ; ; Outputs: D0 - idle disable count ; ; Trashes: A0 ; ; Function: returns the idle disable flag count ;________________________________________________________________________________________ IdleEnable MOVE.W SR,-(SP) ; Save the status register ORI.W #HiIntMask,SR ; Kill those vicious interrupts MOVEQ #0,D0 ; Clear the register MOVE.L PMgrBase,A0 ; Get pmgr locals SUBQ.B #1,IdleFlagCnt(A0) ; pop a level BPL.S @DontClr ; IF IdleFlagCnt < 0 THEN CLR.B IdleFlagCnt(A0) ; IdleFlagCnt = 0 @DontClr MOVE.B IdleFlagCnt(A0),D0 ; return idle disable count MOVE.W (SP)+,SR ; restore the status register RTS ;________________________________________________________________________________________ ; ; Routine: IdleDisable (PowerDispatch selector #6) ; ; Inputs: none ; ; Outputs: D0 - idle disable count ; ; Trashes: A0 ; ; Function: returns the idle disable flag count ;________________________________________________________________________________________ IdleDisable MOVE.W SR,-(SP) ; Save the status register ORI.W #HiIntMask,SR ; Kill those vicious interrupts MOVEQ #0,D0 ; Clear the register MOVE.L PMgrBase,A0 ; Get pgmr locals ADD.B #1,IdleFlagCnt(A0) ; push a level MOVE.B IdleFlagCnt(A0),D0 ; return idle disable count MOVE.W (SP)+,SR ; restore the status register NoPrimRTS RTS ;________________________________________________________________________________________ ; ; Routine: CPUSpeed (PowerDispatch selector #7) ; ; Inputs: none ; ; Outputs: D0 - [maximum CPU speed (high word)][current CPU speed (low word)] ; ; Trashes: A0,A1 ; ; Function: returns the maximum and current CPU speeds, in MHz ;________________________________________________________________________________________ CPUSpeed MOVEA.L PMgrBase,A2 ; get pointer to Power Manager globals MOVEQ #0,D0 ; assume no speed JmpRoutine CPUSpeedPtr,A2,A0 ; go do that routine ;________________________________________________________________________________________ ; ; Routine: BasePRAM (PowerDispatch selector #8) ; ; Inputs: none ; ; Outputs: D0 - base PRAM address used by the Power Manager ; ; Trashes: A0,A1 ; ; Function: returns the base PRAM address that the Power Manager uses for keeping its ; permanent state information ;________________________________________________________________________________________ BasePRAM MOVEA.L PMgrBase,A2 ; get pointer to Power Manager globals MOVEQ #0,D0 MOVE.B PRAMbase(A2),D0 ; stuff in the base PRAM address we use RTS ;________________________________________________________________________________________ ; ; Routine: ScaledBattery (PowerDispatch selector #9) ; ; Inputs: none ; ; Outputs: D0 - battery information: ; bits 31: 1=battery installed ; 30: 1=battery is charging ; 29: 1=charger connected ; 23-16: warning level (0-255) ; 7- 0: battery level (0-255) ; ; Trashes: D1-D2, A0-A2 ; ; Function: returns battery and warning levels scaled into the range 0-255 ;________________________________________________________________________________________ ScaledBattery MOVEA.L PMgrBase,A2 ; get pointer to Power Manager globals MOVEQ #0,D0 ; assume no battery info JmpRoutine ScaledBattPtr,A2,A0 ; go do that routine ;________________________________________________________________________________________ ; ; Routine: PowerMgrHook (PowerDispatch selector #10) ; ; Inputs: D0 - [hook selector (high word)][PowerDispatch selector=10 (low word)] ; ; Outputs: varies with selector ; ; Trashes: D1-D2, A0-A2 ; ; Function: secondary dispatch selector for various functions ;________________________________________________________________________________________ PowerMgrHook WITH PMgrHookRec MOVE.L PmgrBase,A2 ; Get pointer to globals LoadTbl PmgrHookTbl,A2,A0 ; Get pointer to Table MOVEQ #0,D1 ; Clear D1 SWAP D0 ; Get selector MOVE.W D0,D1 ; Get selector in D1 LSL.W #2,D1 ; Convert selector to bytes CMP.L PmgrHookCount(A0),D1 ; IF Dispatch Out of Range THEN BHI.S @OutOfRange ; Get Out Of Here JMP ([A0,D1.L]) ; Go do that routine @OutOfRange MOVEQ #paramErr,D0 ; Abort and return error RTS ENDWITH ;________________________________________________________________________________________ ; ; Routine: SecondaryInitproc (PowerMgrHook selector #0) ; ; Inputs: none ; ; Outputs: none ; ; Trashes: none ; ; Function: code to execute at secondary init time ;________________________________________________________________________________________ SecondaryInitproc BCLR #PmgrShutdownReq,PmgrFlags1(A2) ; clear any pending shutdown requests BSET #PmgrShutdownEnb,PmgrFlags1(A2) ; enable shutdown requests MOVEQ #noErr,D0 RTS ;________________________________________________________________________________________ ; ; Routine: ScsiDiskModeproc (PowerMgrHook selector #1) ; ; Inputs: none ; ; Outputs: none ; ; Trashes: none ; ; Function: code to execute if SCSI Disk Mode was entered ;________________________________________________________________________________________ ScsiDiskModeproc BSET #7,PmgrFlags(A2) MOVEQ #noErr,D0 RTS ;________________________________________________________________________________________ ; ; Routine: ExternaVideoOnproc (PowerMgrHook selector #2) ; ; Inputs: none ; ; Outputs: none ; ; Trashes: none ; ; Function: code to execute if external video is running ;________________________________________________________________________________________ ExternaVideoOnproc LoadTbl PrimInfoTblPtr,A2,A1 ; Get pointer to primitives table MOVE.B PrimExtVidCor(A1),D0 ; Get Delta Threshold ADD.B D0,LowWarn(A2) ; add delta threshold to LowWarn BSR SetPwrLvlTask ; update the power manager micro MOVEQ #noErr,D0 ; Indicate no problems RTS ; ;________________________________________________________________________________________ ; ; Routine: ModemTypeProc (PowerMgrHook selector #3) ; ; Inputs: none ; ; Outputs: d0.l -- modem type ; ; Trashes: a0 ; ; Function: returns modem type constant ;________________________________________________________________________________________ ModemTypeProc WITH ModemTblRec bsr.l ModemStatusRT ; d0 has the modem status btst.l #ModemInstalled,d0 ; is the modem physically installed beq.s @noModem ; if not set, no modem, exit move.l PmgrBase,a0 ; Get pointer to globals moveq.l #ModemTypeUnk,d0 ; assume modem installed of unknown type LoadTbl ModemTblPtr,a0,a0 ; load the Modem table pointer beq.s @exit ; yes JsrPrimTbl GetModemType,A0 ; get modem type rts @noModem moveq.l #ModemTypeNone,d0 ; no modem @exit rts ENDWITH ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: ModemStatus ; ; Inputs: none ; Outputs: d0.b - modem status byte ; Trashes: none ; ; Called by: ModemTypeProc ; Calls: PMgrOp ; Function: return the modem status byte in d0 ;———————————————————————————————————————————————————————————————————————————————————————— ModemStatusRT @workingset reg a0 movem.l @workingset,-(sp) ; ; read power manager's modem bit ; MOVEQ.L #0,D0 ; clear the result register CLR.L -(SP) ; allocate a buffer for the result MOVE.L SP,-(SP) ; pmRBuffer MOVE.L (SP),-(SP) ; pmSBuffer CLR.W -(SP) ; pmLength MOVE.W #modemRead,-(SP) ; pmCommand MOVEA.L SP,A0 ; point to the parameter block _PMgrOp ; get the modem info MOVE.B pmData(SP),D0 ; return the status byte ADDA.L #pmBlkSize, sp ; pop buffer MOVEA.L (SP)+,A0 ; RTS ;________________________________________________________________________________________ ; ; Routine: PDimScreens (PowerDispatch selector #11) ; ; Inputs: D0 - high word: 0=power up, 1=power down ; ; Outputs: none ; ; Trashes: A0 ; ; Function: sets a flag so that IdleMind handles changing state of screens ;________________________________________________________________________________________ PDimScreens MOVEA.L PmgrBase,A0 ; get pointer to globals MOVE SR,D0 ; save the sr SWAP D0 ; Let get the parameter ORI #HiIntMask,SR ; Mask Interrupts BSET #PmgrDimReq,PmgrFlags1(A0) ; set the request BSET #PmgrDimState,PmgrFlags1(A0) ; assume Power Down state TST.W D0 ; IF Powering Up THEN BNE.S @Done ; BCLR #PmgrDimState,PmgrFlags1(A0) ; set Power Up State @Done SWAP D0 ; get saved sr MOVE D0,SR ; restore sr MOVEQ #0,D0 ; noErr RTS ; goodbye ;________________________________________________________________________________________ ; ; Routine: PrivateFeatures (PowerDispatch selector #13) ; ; Inputs: none ; ; Outputs: D0: bits 0: 1=extended battery status call is supported by PMGR ; 1: 1=battery ID call is supported by PMGR ; 2: 1=switch AC power on/off call is supported by PMGR ; ; Trashes: A0 ; ; Function: Returns a bitmap of private Power Manager features. ;________________________________________________________________________________________ PrivateFeatures MOVEA.L PMgrBase,A2 ; get pointer to Power Manager globals LoadTbl PrimInfoTblPtr,A2,A0 ; get pointer to Info MOVE.L PrimPrivFeatures(A0),D0 ; return the bitmap RTS ;________________________________________________________________________________________ ; ; Routine: GetButtonValues (PowerDispatch selector #15) ; ; Inputs: D0 - high word: 0=Brightness Button, 1=Contrass Button ; ; Outputs: D0 - Normalized value from 0-31 ; ; Trashes: A0 ; ; Function: Return the current state of the appropriate button ;________________________________________________________________________________________ GetButtonValues SWAP D0 ; Let get the parameter MOVE.B D0,-(SP) ; data to send = Brightness or Contrass MOVE.L SP,-(SP) ; pmRBuffer MOVE.L (SP),-(SP) ; pmSBuffer MOVE.W #1,-(SP) ; pmLength MOVE.W #readButton,-(SP) ; pmCommand MOVEA.L SP,A0 ; point to the parameter block _PMgrOp ; turn off the hard disk LEA pmRBuffer+4(SP),SP ; clean up the stack MOVEQ #0,D0 ; clear the register MOVE.B (SP)+,D0 ; get the result value and clean off the stack RTS ;________________________________________________________________________________________ ; ; Routine: SetHDState (PowerDispatch selector #16) ; ; Inputs: D0 - high word: 0=Spin down HD, 1=Spin Up HD ; ; Outputs: none ; ; Trashes: A0 ; ; Function: Return the current state of the appropriate button ;________________________________________________________________________________________ SetHDState SWAP D0 ; Let get the parameter MOVEA.L PMgrBase,A0 ; Get pointer to the globas TST.W D0 ; BEQ.S @DoSpinDown @DoHDSpinUp MOVE.L HDSpinUpVector(A0),D0 ; is there a spin up routine? BEQ.S @Done ; -> no, just exit MOVEA.L D0,A0 ; yes, call the spinup down routine JSR (A0) BRA.S @Done @DoSpinDown MOVE.L LastHD(A0),D0 ; is the hard disk spinning? BEQ.S @Done ; -> no, just exit MOVE.L HDvector(A0),D0 ; is there a spindown routine? BEQ.S @Done ; -> no, just exit MOVEA.L D0,A0 ; yes, call the spindown routine JSR (A0) @Done MOVEQ #noErr,D0 ; return no error RTS ; going home ;________________________________________________________________________________________ ; | ; Routine: PmgrOp v ; ; Inputs: D1 - trap word ; ; Outputs: D0 - selector result, or error code if bad selector ; ; Trashes: varies by selector ; ; Function: This is the PmgrOp trap, and provides which provide for ; PmgrTrap ($A085) ; IdleUpdate ($A285) ; IdleState ($A485) ; SerialPower ($A685) ;________________________________________________________________________________________ PmgrOp WITH PMgrOpTblRec ANDI.L #$00000F00,D1 ; Clear all but the trap modifier bits LSR.W #7,D1 ; Convert selector to bytes MOVE.L PmgrBase,A1 ; Get pointer to globals CMPA.L #-1,A1 ; IF Power Manager Var valid THEN BEQ.S @UseROMTable ; . LoadTbl PMgrOpTblPtr,A1,A1 ; Get pointer to Table CMP.L PMgrOpTblCount(A1),D1 ; IF Trap Out of Range THEN BHI.S @OutOfRange ; Get Out Of Here JMP ([A1,D1.L]) ; Do the TRAP @UseROMTable ; ELSE MOVEA.L UnivInfoPtr,A1 ; A0 = ProductInfo table (ROM) ADDA.L PowerManagerPtr(A1),A1 ; A0 = ROM Power Manager's primitives (ROM) ADDA.L PMgrOpTblPtr(A1),A1 ; A0 = Power Managers PMgrOpTblPtr (ROM) CMP.L PMgrOpTblCount(A1),D1 ; IF Trap Out of Range THEN BHI.S @OutOfRange ; Get Out Of Here MOVE.L (A1,D1.L),D1 ; get offset to the trap JMP (A1,D1.L) ; Do the TRAP @OutOfRange MOVEQ #paramErr,D0 ; Abort and return error RTS ; ;________________________________________________________________________________________ ; ; Routine: PmgrTrap ; ; Inputs: A0 -- pointer to Power Manager parameter block ; [word] command/reply ; [word] number of bytes to send/receive ; [long] pointer to send buffer ; [long] pointer to receive buffer ; ; Outputs: D0 -- result code: 0 if successful, else error code ; (all other registers are preserved) ; ; Uses: D1 -- byte to send/byte received ; D2 -- timeout value for handshaking ; D3 -- loop counter / miscellaneous ; D4 -- saved copy of parameter block pointer ; D5 -- timeout value ; D6 -- [saved SR][interface model: 0=parallel, 2=serial, ...] ; D7 -- pointer to top of SCC poll stack ; A1 -- pointer to VIA1 base ; A2 -- pointer to VIA2 base ; A3 -- send or receive buffer pointer ; A4 -- saved return addresses (Wait4AckLo, Wait4AckHi) ; A5 -- saved return addresses (SendByte, ReceiveByte) ; A6 -- pointer to SCC channel A data register ; ; Function: This is the Power Manager interface trap ($A085). ;________________________________________________________________________________________ PMack EQU v2PMack PMreq EQU v2PMreq PMbuf EQU vBufAH PMbufDir EQU vDirA PMgrTrap pmgrPollRegs REG D3/D4/D5/D6/A0/A1/A2/A4 @numPollRegs EQU 4+4 @savedRegs REG D1-D7/A0-A6 MOVEM.L @savedRegs,-(SP) ; save working registers BSR SetupPMgrOpInterface ; get the setup information MOVE.W SR,D6 ; save the entry SR in the upper half of D6 SWAP D6 MOVE.W D1,D6 ; and the interface model in the lower half MOVEA.L A1,A3 ; save the table pointer ; remember the current PMGR interrupt setting, and then disable PMGR interrupts... MOVEA.L VIA,A1 ; point to VIA1 MOVEA.L VIA2,A2 ; point to the VIA with the handshake lines MOVEQ #(1< interrupts are currently disabled BSET #ifIRQ,D1 ; be sure to re-enable them on exit @NoPMGRInt MOVE.B D1,-(SP) ; set up for SCC polling... LEA -4*@numPollRegs(SP),SP ; allocate space for registers saved if we call PollProc MOVE.L SP,D7 ; point to the stack top for our SCC poll stack MOVEA.L SccRd,A6 ; point to the SCC channel A's data register ADDQ.W #aData,A6 ; TST.L PollProc ; is a poll proc installed? BNE.S @WillPoll ; -> yes LEA @NoPollSCC+sccData,A6 ; no, avoid collecting bytes no one will use @WillPoll ; run thru the exception table for this machine to see if this command is emulated... MOVE.L A0,D4 ; save the pointer to the parameter block MOVE.W pmCommand(A0),D0 ; get the command TST.L A3 ; IF Exception pointer <> NULL THEN BEQ.S @NoExceptions ; LEA -4(A3),A3 ; subtract 4 bytes for the loop @NextException ; WHILE notDone DO { ADDQ.W #4,A3 ; skip over the offset to the previous handler MOVE.B (A3)+,D1 ; get the mask BEQ.S @NoExceptions ; IF mask = NULL THEN end of list AND.B D0,D1 ; mask off the bits we care about CMP.B (A3)+,D1 ; IF this command emulated THEN BNE.S @NextException ; MOVEM.L A0-A2,-(SP) ; save state ORI #HiIntMask,SR ; mask interrupts TST.L D2 ; IF rom tables THEN BNE.S @RamTables ; . ADDA.L (A3),A3 ; calculate the handler's address JSR (A3) ; and call the handler BRA.S @ExceptDone ; @RamTables JSR ([A3]) ; call the handler @ExceptDone MOVEM.L (SP)+,A0-A2 ; restore state BEQ @PMgrOpExit ; and exit @NoExceptions ; } MOVE.W TimeVIAdb,D5 ; calculate a 32ms timeout value LSL.W #5,D5 MOVE.W #8*64-1,D3 ; initialize the retry count (8 ms) MOVE.W (A0)+,D1 ; get the command byte ; wait for /ack to go away in case the PMGR is busy... @CmdRetry MOVE.W #pmBusyErr,D0 ; assume a timeout MOVE.W timeVIAdb,D2 ; use a 4 ms timeout value LSL.W #2,D2 BSR Wait4AckHi ; wait for /ack to go high if it's currently low BEQ @PMgrOpExit ; -> timed out waiting for initial conditions IF hasNiagra THEN ; IF isUniversal THEN ; TestFor NiagraExistsBit ; beq @pmgrFree ; call old routine ENDIF ; @BusyCheck MOVE.W timeVIAdb,D2 ; Get 1ms timeout value LSL.W #4,D2 ; multiply by 16 MOVE.B #0,PMBufDir(A2) ; set Port A is input @pmgrbusy CMP.B #$07,PMbuf(A2) ; Get port status DBNE D2,@pmgrbusy ; Wait pmgr not busy BNE.S @pmgrFree ; if not busy status, continue MOVE.L #pmBusyErr,D0 ; Timeout, flag error BRA @PMgrOpExit ; Timed out waiting for initial conditions @pmgrFree ENDIF ; ; send the command byte... ORI #HiIntMask,SR ; no interrupts while sending the command bytes BSR SendByte ; send the command byte BEQ.S @SendCount ; -> success BSR UnloadPollstack ; unload any SCC data stashed while trying to send the command byte MOVE.W D5,D2 ; wait for the handshake to abort SWAP D6 @spin MOVE.W D6,SR ; restore the interrupt level DBRA D2,@spin SWAP D6 DBRA D3,@CmdRetry ; try again BRA @PMgrOpExit ; -> enough retries: just abort ; send the byte count... ; ; As an optimization, commands with fixed length data do not have their ; count byte sent to the PMGR since both sides know what the count is. @SendCount MOVE.W (A0)+,D3 ; get the byte count TestFor PMgrNewIntf ; new PMGR? BEQ.S @SendTheCnt ; -> no, it still sends the count anyway LEA cmdCounts,A3 MOVEA.L PMgrBase,A4 ; point to the Power Manager's variables

CMPA.W #-1,A4 ; are they valid yet (-1 if not initialized)?

BEQ.S @NoSendTbl ; -> no, use the default table

MOVEA.L vSendCountTbl(A4),A3 ; yes, use the vectored send table @NoSendTbl TST.B 0(A3,D1) ; do we need to send the count?

BPL.S @NoSendCnt ; -> no, both sides know what it is @SendTheCnt MOVE.W D3,D1 ; use the count passed in BSR SendByte ; send the byte count to the PMGR BNE.S @PMgrOpExit ; -> error ; send the command's data bytes to the PMGR... @NoSendCnt MOVEA.L (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 SendByte ; and send it @StartSend DBNE D3,@SendData ; -> more bytes to send BNE.S @PMgrOpExit ; -> error ; the command has been sent, so check if we need to wait for a reply... SUBQ.W #pmSBuffer-pmCommand,A0 ; point to the start of the parameter block MOVE.W (A0),D1 ; get the command byte before it gets trashed LEA replyCounts,A3 MOVEA.L PMgrBase,A4 ; point to the Power Manager's variables

CMPA.W #-1,A4 ; are they valid yet (-1 if not initialized)?

BEQ.S @NoRecvTbl ; -> no, use the default table

MOVEA.L vRecvCountTbl(A4),A3 ; yes, use the vectored receive table @NoRecvTbl MOVE.B 0(A3,D1),D3 ; get the reply byte count

EXT.W D3 MOVE.W D3,pmLength(A0) ; stuff the count in case there's no reply BEQ.S @PMgrOpExit ; -> no reply, so we're done ; read in the reply byte... ; ; We don't expect a reply byte except for specific commands. TestFor PMgrNewIntf ; new PMGR? BEQ.S @ReadReply ; -> no, it still wants the reply byte anyway SUBQ.W #1,D3 ; do we need to get a reply byte? BNE.S @NoReply ; -> nope @ReadReply BSR ReceiveByte ; get the reply byte BNE.S @ReplyTO ; -> error @NoReply MOVE.W D1,(A0)+ ; save the reply byte in the parameter block ; read in the reply byte count... ; ; As an optimization, replies with fixed length data do not have their ; count byte sent by the PMGR since both sides know what the count is. TestFor PMgrNewIntf ; new PMGR? BEQ.S @ReadCount ; -> no, it still wants the count anyway TST.W D3 ; is the PMGR sending us a byte count? BPL.S @NoReplyCnt ; -> no, we both know what it is @ReadCount BSR ReceiveByte ; get the count byte BNE.S @PMgrOpExit ; -> error MOVE.W D1,D3 ; update the reply data byte count @NoReplyCnt MOVE.W D3,(A0)+ ; save the byte count in the parameter block ; get the reply's data bytes from the PMGR... ADDQ.W #pmRBuffer-pmSBuffer,A0 MOVEA.L (A0)+,A3 ; get the pointer to where the reply bytes will go BRA.S @StartReply @ReplyData BSR ReceiveByte ; get the next reply data byte BNE.S @PMgrOpExit ; -> error MOVE.B D1,(A3)+ ; save the byte in the reply data buffer @StartReply DBRA D3,@ReplyData ; -> more bytes to get ; special-case the power control command, and add a delay if anything was turned on... @PMgrOpExit MOVEA.L VIA,A1 MOVEA.L D4,A0 ; restore the pointer to the parameter block MOVE.W D0,D4 ; save the result code BNE.S @NotPC ; -> no point in delaying if the command failed CMPI.W #powerCntl,pmCommand(A0); was this a power control command? BNE.S @NotPC ; -> nope, really all done MOVEA.L pmSBuffer(A0),A2 ; point to the send buffer CMPI.B #(1< BLS.S @NotPC ; -> nope, no need to wait MOVE.W TimeVIAdb,D2 ; wait 125usec for the power planes to stabilize LSR.W #3,D2 ; @Wait125us TST.B (A1) ; (throttle execution speed with a VIA access) BTST #RxCA,-sccData(A6) ; SCC data available? BEQ.S @NoSCCData ; MOVE.B (A6),-(SP) ; yes, push it on the stack @NoSCCData DBRA D2,@Wait125us ; -> keep looping if not ; check if any SCC bytes were collected so we can send them to the serial driver... @NotPC BSR UnloadPollstack ; unload any SCC data stashed while interrupts were disabled LEA 4*@numPollRegs(SP),SP ; de-allocate PollProc saved register frame ; clean everything up... MOVE.B (SP)+,vIER(A1) ; restore the PMGR interrupt state SWAP D6 MOVE.W D6,SR MOVE.W D4,D0 ; restore the result EXT.L D0 ; set condition codes MOVEM.L (SP)+,@savedRegs ; restore working registers RTS @ReplyTO MOVE.W #pmReplyTOErr,D0 ; timed out waiting for the reply BRA.S @PMgrOpExit @NoPollSCC DC.W 0 ; A6 points here if no PollProc to avoid stack overflow ;________________________________________________________________________________________ ; ; Routine: Wait4AckLo ; ; Inputs: D2 -- timeout count ; A1 -- pointer to VIA1 base ; A2 -- pointer to VIA2 base ; A6 -- pointer to SCC channel A data register ; ; Outputs: CCR-- BEQ: /ack has gone low, BNE: timed out ; ; Trashes: D2,A4 ; ; Function: waits for the /ack handshake line to go low (may time out) ;________________________________________________________________________________________ Wait4AckLo MOVEA.L (SP)+,A4 ; save the return address BRA.S @WaitLoop @CheckSCC TST.B (A1) ; (throttle execution speed with a VIA access) BTST #RxCA,-sccData(A6) ; SCC data available? BEQ.S @WaitLoop ; MOVE.B (A6),-(SP) ; yes, push it on the stack @WaitLoop BTST.B #PMack,(A2) ; has /ack gone low yet? DBEQ D2,@CheckSCC ; -> keep looping if not JMP (A4) ;________________________________________________________________________________________ ; ; Routine: Wait4AckHi ; ; Inputs: D2 -- timeout count ; A1 -- pointer to VIA1 base ; A2 -- pointer to VIA2 base ; A6 -- pointer to SCC channel A data register ; ; Outputs: CCR-- BNE: /ack has gone high, BEQ: timed out ; ; Trashes: D2,A4 ; ; Function: waits for the /ack handshake line to go high (may time out) ;________________________________________________________________________________________ Wait4AckHi MOVEA.L (SP)+,A4 ; save the return address BRA.S @WaitLoop @CheckSCC TST.B (A1) ; (throttle execution speed with a VIA access) BTST #RxCA,-sccData(A6) ; SCC data available? BEQ.S @WaitLoop ; MOVE.B (A6),-(SP) ; yes, push it on the stack @WaitLoop BTST.B #PMack,(A2) ; has /ack gone high yet? DBNE D2,@CheckSCC ; -> keep looping if not JMP (A4) ;________________________________________________________________________________________ ; ; Routine: ReceiveByte ; ; Inputs: D6 -- interface type ; 0=parallel ; 2=serial ; A1 -- pointer to VIA1 base ; A2 -- pointer to VIA2 base ; A6 -- pointer to SCC channel A data register ; ; Outputs: D0 -- result code (0 if successful) ; D1 -- byte received ; ; Trashes: D2,A4,A5 ; ; Function: handshakes a byte from the PMGR micro ;________________________________________________________________________________________ ReceiveByte MOVE.W D5,D2 ; use the standard timeout value MOVE.W #pmRecvStartErr,D0 ; assume we'll time out MOVEA.L (SP)+,A5 ; save the return address JMP @ReadByte(D6.W) ; go read a byte @ReadByte BRA.S @RecvParallel ; BRA.S @RecvSerial @RecvSerial ORI.B #%00001100,vACR(A1) ; ***for BlackBird_EVT0 HJR*** BCLR #4,vACR(A1) ; set shift register to shift in under external clock TST.B vSR(A1) ; read a byte to reset the shifter BCLR #PMreq,(A2) ; assert /req to start the handshake BSR.S Wait4AckLo ; wait for /ack to go low BNE.S SendRcvDone ; -> timed out BSET #PMreq,(A2) ; de-assert /req since we've seen /ack asserted MOVE.W #pmRecvEndErr,D0 ; assume we'll time out MOVE.W D5,D2 BSR.S Wait4AckHi ; wait for /ack to go high BEQ.S SendRcvDone ; -> timed out MOVE.B vSR(A1),D1 ; read in the byte MOVEQ #NoErr,D0 ; success! BRA.S SendRcvDone @RecvParallel MOVE.B #$00,PMBufDir(A2) ; make the data port an input BSR.S Wait4AckLo ; wait for /ack to go low BNE.S SendRcvDone ; -> timed out BCLR #PMreq,(A2) ; assert the /req line MOVE.B PMbuf(A2),D1 ; read in the byte MOVE.W #pmRecvEndErr,D0 ; assume we'll time out FinishHshk MOVE.W D5,D2 BSR.S Wait4AckHi ; wait for /ack to go high BEQ.S SendRcvDone ; -> timed out MOVEQ #NoErr,D0 ; success! SendRcvDone BSET #PMreq,(A2) ; be sure /req is not asserted JSR DisableBuf(D6.W) ; turn off the data buffer TST.W D0 ; set the condition codes JMP (A5) ; ;________________________________________________________________________________________ ; ; Routine: SendByte ; ; Inputs: D1 -- byte to send ; D2 -- timeout count ; D6 -- interface type ; 0=parallel ; 2=serial ; A1 -- pointer to VIA1 base ; A2 -- pointer to VIA2 base ; A6 -- pointer to SCC channel A data register ; ; Outputs: D0 -- result code (0 if successful) ; ; Trashes: D2,A4,A5 ; ; Function: handshakes a byte to the PMGR micro ;________________________________________________________________________________________ SendByte MOVE.L D5,D2 ; use the standard timeout value MOVEA.L (SP)+,A5 ; save the return address JSR @SendByte(D6.W) ; send the byte BCLR #PMreq,(A2) ; assert the /req line MOVE.W #pmSendStartErr,D0 ; assume we'll time out BSR Wait4AckLo ; wait for /ack to go low BNE.S SendRcvDone ; -> timed out BSET #PMreq,(A2) ; de-assert the /req line MOVE.W #pmSendEndErr,D0 ; assume we'll time out BRA.S FinishHshk ; wait for /ack to go high @SendByte BRA.S @SendParallel ; [0] parallel interface ; BRA.S @SendSerial ; [2] serial interface @SendSerial ORI.B #%00011100,vACR(A1) ; ***for BlackBird_EVT0 HJR*** ; BSET #4,vACR(A1) ; set shift register to shift out under external clock MOVE.B D1,vSR(A1) ; write out the byte to the shift register RTS @SendParallel MOVE.B #$FF,PMBufDir(A2) ; make the data port an output MOVE.B D1,PMBuf(A2) ; and write out the byte RTS DisableBuf BRA.S @Buf2Input ; [0] parallel interface ; BRA.S @DisableSR ; [2] serial interface @DisableSR ORI.B #%00011100,vACR(A1) ; ***for BlackBird_EVT0 HJR*** ; BSET #4,vACR(A1) ; make the shift register shift out RTS @Buf2Input MOVE.B #0,PMBufDir(A2) ; switch the data port back to an input RTS ;________________________________________________________________________________________ ; ; Routine: UnloadPollstack ; ; Inputs: D7 -- pointer to top of poll stack ; A1 -- pointer to VIA1 base ; A6 -- pointer to SCC channel A data register ; SP -- pointer to bottom of poll stack ; ; Outputs: D7 -- pointer to top of poll stack ; A1 -- pointer to VIA1 base ; A6 -- pointer to SCC channel A data register ; SP -- pointer to top of poll stack ; ; Trashes: D0,D1,D2,A3,A4,A5 ; ; Function: calls the poll proc to unload any bytes stashed from SCC ; port A while interrupts were disabled ;________________________________________________________________________________________ UnloadPollstack MOVEA.L (SP)+,A4 ; pop the return address CMPA.L D7,SP ; is there any poll data? BEQ.S @NoSCCData ; -> no MOVE.L PollProc,D0 ; is there a poll proc? BEQ.S @NoPollProc ; -> no MOVE.L D7,PollStack ; stuff the PollStack MOVEA.L D7,A3 MOVEM.L pmgrPollRegs,(A3) ; save regs while calling poll proc W/O USING STACK LEA vBufA(A1),A5 ; point to the register with the SCC WR/REQ bit in VIA 1 MOVEA.L D0,A3 JSR (A3) ; run the poll proc MOVEA.L D7,A3 MOVEM.L (A3),pmgrPollRegs ; restore our registers @NoPollProc MOVEA.L D7,SP ; toss any bytes left on the pollstack @NoSCCData JMP (A4) ;_______________________________________________________________________________________ ; ; This table is used to determine if the 680x0 needs to send a count byte to the PMGR. ; A positive value means that the count is known by both sides, and so it is not sent. ; A negative value means that the command can send variable amounts of data, so the ; count byte needs to be sent. ; ; Unused commands will be marked as expecting the count to be sent so that commands ; may be added without having to change the ROM. cmdCounts DC.B -1 ; [$00] - DC.B -1 ; [$01] - DC.B -1 ; [$02] - DC.B -1 ; [$03] - DC.B -1 ; [$04] - DC.B -1 ; [$05] - DC.B -1 ; [$06] - DC.B -1 ; [$07] - DC.B -1 ; [$08] - DC.B -1 ; [$09] - DC.B -1 ; [$0A] - DC.B -1 ; [$0B] - DC.B -1 ; [$0C] - DC.B -1 ; [$0D] - DC.B -1 ; [$0E] - DC.B -1 ; [$0F] - DC.B 1 ; [$10] Subsystem Power/Clock Control DC.B 1 ; [$11] Subsystem Power/Clock Control (yet more) DC.B -1 ; [$12] - DC.B -1 ; [$13] - DC.B -1 ; [$14] - DC.B -1 ; [$15] - DC.B -1 ; [$16] - DC.B -1 ; [$17] - DC.B 0 ; [$18] Read Power/Clock Status DC.B 0 ; [$19] Read Power/Clock Status (yet more) DC.B -1 ; [$1A] - DC.B -1 ; [$1B] - DC.B -1 ; [$1C] - DC.B -1 ; [$1D] - DC.B -1 ; [$1E] - DC.B 0 ; [$1F] RESERVED FOR MSC/PG&E EMULATION DC.B -1 ; [$20] Set New Apple Desktop Bus Command DC.B 0 ; [$21] ADB Autopoll Abort DC.B 2 ; [$22] ADB Set Keyboard Addresses DC.B 1 ; [$23] ADB Set Hang Threshold DC.B 1 ; [$24] ADB Enable/Disable Programmers Key DC.B -1 ; [$25] - DC.B -1 ; [$26] - DC.B -1 ; [$27] - DC.B 0 ; [$28] ADB Transaction Read DC.B -1 ; [$29] - DC.B -1 ; [$2A] - DC.B -1 ; [$2B] - DC.B -1 ; [$2C] - DC.B -1 ; [$2D] - DC.B -1 ; [$2E] - DC.B -1 ; [$2F] - DC.B 4 ; [$30] Set Realtime Clock. DC.B 20 ; [$31] Write Parameter RAM DC.B -1 ; [$32] Write Extended Parameter RAM. DC.B -1 ; [$33] - DC.B -1 ; [$34] - DC.B -1 ; [$35] - DC.B -1 ; [$36] - DC.B -1 ; [$37] - DC.B 0 ; [$38] Read Realtime Clock. DC.B 0 ; [$39] Read Parameter RAM DC.B 2 ; [$3A] Read Extended Parameter RAM. DC.B -1 ; [$3B] - DC.B -1 ; [$3C] - DC.B -1 ; [$3D] - DC.B -1 ; [$3E] - DC.B -1 ; [$3F] - DC.B 1 ; [$40] Set Screen Contrast DC.B 1 ; [$41] Set Screen Brightness

DC.B -1 ; [$42] - DC.B -1 ; [$43] - DC.B -1 ; [$44] - DC.B -1 ; [$45] - DC.B -1 ; [$46] - DC.B -1 ; [$47] - DC.B 0 ; [$48] Read Screen Contrast DC.B 0 ; [$49] Read Screen Brightness DC.B -1 ; [$4A] - DC.B -1 ; [$4B] - DC.B -1 ; [$4C] - DC.B -1 ; [$4D] - DC.B -1 ; [$4E] - DC.B -1 ; [$4F] - DC.B 1 ; [$50] Set Internal Modem Control Bits DC.B 0 ; [$51] Clear FIFOs DC.B 2 ; [$52] Set FIFO Interrupt Marks DC.B 2 ; [$53] Set FIFO Sizes DC.B -1 ; [$54] Write Data to Modem DC.B 1 ; [$55] Set Data Mode DC.B 3 ; [$56] Set Flow Control Mode DC.B 1 ; [$57] Set DAA control lines DC.B 0 ; [$58] Read Internal Modem Status DC.B 1 ; [$59] Get DAA Identification DC.B 0 ; [$5A] Get FIFO Counts DC.B 0 ; [$5B] Get Maximum FIFO Sizes DC.B 0 ; [$5C] Read Data From Modem DC.B -1 ; [$5D] General Purpose modem command (modem dependent) DC.B -1 ; [$5E] - DC.B -1 ; [$5F] - DC.B 2 ; [$60] Set low power warning and cutoff levels DC.B -1 ; [$61] - DC.B -1 ; [$62] - DC.B -1 ; [$63] - DC.B -1 ; [$64] DC.B -1 ; [$65] - DC.B -1 ; [$66] - DC.B -1 ; [$67] - DC.B 0 ; [$68] Read Charger State, Battery Voltage, Temperature DC.B 0 ; [$69] Read Instantaneous Charger, Battery, Temperature DC.B 0 ; [$6A] Read low power warning and cutoff levels DC.B 0 ; [$6B] Read Extended Battery Status DC.B 0 ; [$6C] Read Battery ID DC.B 0 ; [$6D] Battery Parameters DC.B -1 ; [$6E] - DC.B -1 ; [$6F] - DC.B 1 ; [$70] Set One-Second Interrupt DC.B 1 ; [$71] Modem Interrupt Control DC.B 1 ; [$72] Set Modem Interrupt DC.B -1 ; [$73] - DC.B -1 ; [$74] - DC.B -1 ; [$75] - DC.B -1 ; [$76] - DC.B -1 ; [$77] - DC.B 0 ; [$78] Read Interrupt Flag Register. DC.B 0 ; [$79] Read Modem Interrupt Data DC.B -1 ; [$7A] - DC.B -1 ; [$7B] - DC.B -1 ; [$7C] - DC.B -1 ; [$7D] - DC.B 4 ; [$7E] Enter Shutdown Mode DC.B 4 ; [$7F] Enter Sleep Mode DC.B 4 ; [$80] Set Wakeup Timer DC.B -1 ; [$81] - DC.B 0 ; [$82] Disable Wakeup Timer DC.B -1 ; [$83] - DC.B -1 ; [$84] - DC.B -1 ; [$85] - DC.B -1 ; [$86] - DC.B -1 ; [$87] - DC.B 0 ; [$88] Read Wakeup Timer DC.B -1 ; [$89] - DC.B -1 ; [$8A] - DC.B -1 ; [$8B] - DC.B -1 ; [$8C] - DC.B -1 ; [$8D] - DC.B -1 ; [$8E] - DC.B -1 ; [$8F] - DC.B 1 ; [$90] Set Sound Control Bits DC.B 2 ; [$91] Set DFAC Control Register DC.B -1 ; [$92] - DC.B -1 ; [$93] - DC.B -1 ; [$94] - DC.B -1 ; [$95] - DC.B -1 ; [$96] - DC.B -1 ; [$97] - DC.B 0 ; [$98] Read Sound Control Status DC.B 0 ; [$99] Read DFAC Control Register DC.B -1 ; [$9A] - DC.B -1 ; [$9B] - DC.B -1 ; [$9C] - DC.B -1 ; [$9D] - DC.B -1 ; [$9E] - DC.B -1 ; [$9F] - DC.B 2 ; [$A0] Write Modem Register DC.B 2 ; [$A1] Clear Modem Register Bits DC.B 2 ; [$A2] Set Modem Register Bits DC.B 4 ; [$A3] Write DSP RAM DC.B -1 ; [$A4] Set Filter Coefficients DC.B 0 ; [$A5] Reset Modem DC.B -1 ; [$A6] - DC.B -1 ; [$A7] - DC.B 1 ; [$A8] Read Modem Register DC.B 1 ; [$A9] Send Break DC.B 3 ; [$AA] Dial Digit DC.B 2 ; [$AB] Read DSP RAM DC.B -1 ; [$AC] - DC.B -1 ; [$AD] - DC.B -1 ; [$AE] - DC.B -1 ; [$AF] - DC.B -1 ; [$B0] - DC.B -1 ; [$B1] - DC.B -1 ; [$B2] - DC.B -1 ; [$B3] - DC.B -1 ; [$B4] - DC.B -1 ; [$B5] - DC.B -1 ; [$B6] - DC.B -1 ; [$B7] - DC.B -1 ; [$B8] - DC.B -1 ; [$B9] - DC.B -1 ; [$BA] - DC.B -1 ; [$BB] - DC.B -1 ; [$BC] - DC.B -1 ; [$BD] - DC.B -1 ; [$BE] - DC.B -1 ; [$BF] - DC.B -1 ; [$C0] - DC.B -1 ; [$C1] - DC.B -1 ; [$C2] - DC.B -1 ; [$C3] - DC.B -1 ; [$C4] - DC.B -1 ; [$C5] - DC.B -1 ; [$C6] - DC.B -1 ; [$C7] - DC.B -1 ; [$C8] - DC.B -1 ; [$C9] - DC.B -1 ; [$CA] - DC.B -1 ; [$CB] - DC.B -1 ; [$CC] - DC.B -1 ; [$CD] - DC.B -1 ; [$CE] - DC.B -1 ; [$CF] - DC.B 0 ; [$D0] Reset CPU DC.B -1 ; [$D1] - DC.B -1 ; [$D2] - DC.B -1 ; [$D3] - DC.B -1 ; [$D4] - DC.B -1 ; [$D5] - DC.B -1 ; [$D6] - DC.B -1 ; [$D7] - DC.B 1 ; [$D8] Read A/D Status DC.B 1 ; [$D9] Read User Input DC.B -1 ; [$DA] - DC.B -1 ; [$DB] - DC.B 0 ; [$DC] read external switches DC.B 0 ; [$DD] - DC.B -1 ; [$DE] - DC.B -1 ; [$DF] - DC.B -1 ; [$E0] Write to internal PMGR memory DC.B 4 ; [$E1] Download Flash EEPROM Code DC.B 0 ; [$E2] Get Flash EEPROM Status DC.B -1 ; [$E3] - DC.B -1 ; [$E4] - DC.B -1 ; [$E5] - DC.B -1 ; [$E6] - DC.B -1 ; [$E7] - DC.B 3 ; [$E8] Read PMGR internal memory DC.B -1 ; [$E9] - DC.B 0 ; [$EA] Read PMGR firmware version number DC.B -1 ; [$EB] - DC.B 0 ; [$EC] Execute self test DC.B -1 ; [$ED] PMGR diagnostics (selector-based) DC.B -1 ; [$EE] - DC.B 0 ; [$EF] PMGR soft reset DC.B -1 ; [$F0] - DC.B -1 ; [$F1] - DC.B -1 ; [$F2] - DC.B -1 ; [$F3] - DC.B -1 ; [$F4] - DC.B -1 ; [$F5] - DC.B -1 ; [$F6] - DC.B -1 ; [$F7] - DC.B -1 ; [$F8] - DC.B -1 ; [$F9] - DC.B -1 ; [$FA] - DC.B -1 ; [$FB] - DC.B -1 ; [$FC] - DC.B -1 ; [$FD] - DC.B -1 ; [$FE] - DC.B -1 ; [$FF] - ; This table is used to determine how the 680x0 needs to handle the reply: ; ; =0: no reply should be expected. ; =1: only a reply byte will be sent (this is a special case for a couple of commands) ; <0: a reply is expected and the PMGR will send a count byte. ; >1: a reply is expected and the PMGR will not send a count byte, ; but the count will be (value-1). ; ; Unused commands in the range $x8 to $xF will be marked as expecting a reply (with count) ; so that commands may be added without having to change the ROM. replyCounts DC.B 0 ; [$00] - DC.B 0 ; [$01] - DC.B 0 ; [$02] - DC.B 0 ; [$03] - DC.B 0 ; [$04] - DC.B 0 ; [$05] - DC.B 0 ; [$06] - DC.B 0 ; [$07] - DC.B -1 ; [$08] - DC.B -1 ; [$09] - DC.B -1 ; [$0A] - DC.B -1 ; [$0B] - DC.B -1 ; [$0C] - DC.B -1 ; [$0D] - DC.B -1 ; [$0E] - DC.B -1 ; [$0F] - DC.B 0 ; [$10] Subsystem Power/Clock Control DC.B 0 ; [$11] Subsystem Power/Clock Control (yet more) DC.B 0 ; [$12] - DC.B 0 ; [$13] - DC.B 0 ; [$14] - DC.B 0 ; [$15] - DC.B 0 ; [$16] - DC.B 0 ; [$17] - DC.B 1+1 ; [$18] Read Power/Clock Status DC.B 1+1 ; [$19] Read Power/Clock Status (yet more) DC.B -1 ; [$1A] - DC.B -1 ; [$1B] - DC.B -1 ; [$1C] - DC.B -1 ; [$1D] - DC.B -1 ; [$1E] - DC.B 0 ; [$1F] RESERVED FOR MSC/PG&E EMULATION DC.B 0 ; [$20] Set New Apple Desktop Bus Command DC.B 0 ; [$21] ADB Autopoll Abort DC.B 0 ; [$22] ADB Set Keyboard Addresses DC.B 0 ; [$23] ADB Set Hang Threshold DC.B 0 ; [$24] ADB Enable/Disable Programmers Key DC.B 0 ; [$25] - DC.B 0 ; [$26] - DC.B 0 ; [$27] - DC.B -1 ; [$28] ADB Transaction Read DC.B -1 ; [$29] - DC.B -1 ; [$2A] - DC.B -1 ; [$2B] - DC.B -1 ; [$2C] - DC.B -1 ; [$2D] - DC.B -1 ; [$2E] - DC.B -1 ; [$2F] - DC.B 0 ; [$30] Set Realtime Clock. DC.B 0 ; [$31] Write Parameter RAM DC.B 0 ; [$32] Write Extended Parameter RAM. DC.B 0 ; [$33] - DC.B 0 ; [$34] - DC.B 0 ; [$35] - DC.B 0 ; [$36] - DC.B 0 ; [$37] - DC.B 4+1 ; [$38] Read Realtime Clock. DC.B 20+1 ; [$39] Read Parameter RAM DC.B -1 ; [$3A] Read Extended Parameter RAM. DC.B -1 ; [$3B] - DC.B -1 ; [$3C] - DC.B -1 ; [$3D] - DC.B -1 ; [$3E] - DC.B -1 ; [$3F] - DC.B 0 ; [$40] Set Screen Contrast DC.B 0 ; [$41] Set Screen Brightness

DC.B 0 ; [$42] - DC.B 0 ; [$43] - DC.B 0 ; [$44] - DC.B 0 ; [$45] - DC.B 0 ; [$46] - DC.B 0 ; [$47] - DC.B 1+1 ; [$48] Read Screen Contrast DC.B 1+1 ; [$49] Read Screen Brightness DC.B -1 ; [$4A] - DC.B -1 ; [$4B] - DC.B -1 ; [$4C] - DC.B -1 ; [$4D] - DC.B -1 ; [$4E] - DC.B -1 ; [$4F] - DC.B 0 ; [$50] Set Internal Modem Control Bits DC.B 0 ; [$51] Clear FIFOs DC.B 0 ; [$52] Set FIFO Interrupt Marks DC.B 0 ; [$53] Set FIFO Sizes DC.B 0 ; [$54] Write Data to Modem DC.B 0 ; [$55] Set Data Mode DC.B 0 ; [$56] Set Flow Control Mode DC.B 0 ; [$57] Set DAA control lines DC.B 1+1 ; [$58] Read Internal Modem Status DC.B 0 ; [$59] Get DAA Identification DC.B 2+1 ; [$5A] Get FIFO Counts DC.B 2+1 ; [$5B] Get Maximum FIFO Sizes DC.B -1 ; [$5C] Read Data From Modem DC.B -1 ; [$5D] General Purpose modem command (modem dependent) DC.B -1 ; [$5E] - DC.B -1 ; [$5F] - DC.B 0 ; [$60] Set low power warning and cutoff levels DC.B 0 ; [$61] - DC.B 0 ; [$62] - DC.B 0 ; [$63] - DC.B 0 ; [$64] - DC.B 0 ; [$65] - DC.B 0 ; [$66] - DC.B 0 ; [$67] - DC.B 3+1 ; [$68] Read Charger State, Battery Voltage, Temperature DC.B 3+1 ; [$69] Read Instantaneous Charger, Battery, Temperature DC.B 2+1 ; [$6A] Read low power warning and cutoff levels DC.B 8+1 ; [$6B] Read Extended Battery Status DC.B -1 ; [$6C] Read Battery ID DC.B -1 ;10+1 ; [$6D] Battery Parameters DC.B -1 ; [$6E] - DC.B -1 ; [$6F] - DC.B 0 ; [$70] Set One-Second Interrupt DC.B 0 ; [$71] Modem Interrupt Control DC.B 0 ; [$72] Set Modem Interrupt DC.B 0 ; [$73] - DC.B 0 ; [$74] - DC.B 0 ; [$75] - DC.B 0 ; [$76] - DC.B 0 ; [$77] - DC.B -1 ; [$78] Read Interrupt Flag Register. DC.B -1 ; [$79] Read Modem Interrupt Data DC.B -1 ; [$7A] - DC.B -1 ; [$7B] - DC.B -1 ; [$7C] - DC.B -1 ; [$7D] - DC.B 0+1 ; [$7E] Enter Shutdown Mode DC.B 0+1 ; [$7F] Enter Sleep Mode DC.B 0 ; [$80] Set Wakeup Timer DC.B 0 ; [$81] - DC.B 0 ; [$82] Disable Wakeup Timer DC.B 0 ; [$83] - DC.B 0 ; [$84] - DC.B 0 ; [$85] - DC.B 0 ; [$86] - DC.B 0 ; [$87] - DC.B 5+1 ; [$88] Read Wakeup Timer DC.B -1 ; [$89] - DC.B -1 ; [$8A] - DC.B -1 ; [$8B] - DC.B -1 ; [$8C] - DC.B -1 ; [$8D] - DC.B -1 ; [$8E] - DC.B -1 ; [$8F] - DC.B 0 ; [$90] Set Sound Control Bits DC.B 0 ; [$91] Set DFAC Control Register DC.B 0 ; [$92] - DC.B 0 ; [$93] - DC.B 0 ; [$94] - DC.B 0 ; [$95] - DC.B 0 ; [$96] - DC.B 0 ; [$97] - DC.B 1+1 ; [$98] Read Sound Control Status DC.B 1+1 ; [$99] Read DFAC Control Register DC.B -1 ; [$9A] - DC.B -1 ; [$9B] - DC.B -1 ; [$9C] - DC.B -1 ; [$9D] - DC.B -1 ; [$9E] - DC.B -1 ; [$9F] - DC.B 0 ; [$A0] Write Modem Register DC.B 0 ; [$A1] Clear Modem Register Bits DC.B 0 ; [$A2] Set Modem Register Bits DC.B 0 ; [$A3] Write DSP RAM DC.B 0 ; [$A4] Set Filter Coefficients DC.B 0 ; [$A5] Reset Modem DC.B 0 ; [$A6] - DC.B 0 ; [$A7] - DC.B 1+1 ; [$A8] Read Modem Register DC.B 0 ; [$A9] Send Break DC.B 0 ; [$AA] Dial Digit DC.B 0 ; [$AB] Read DSP RAM DC.B -1 ; [$AC] - DC.B -1 ; [$AD] - DC.B -1 ; [$AE] - DC.B -1 ; [$AF] - DC.B 0 ; [$B0] - DC.B 0 ; [$B1] - DC.B 0 ; [$B2] - DC.B 0 ; [$B3] - DC.B 0 ; [$B4] - DC.B 0 ; [$B5] - DC.B 0 ; [$B6] - DC.B 0 ; [$B7] - DC.B -1 ; [$B8] - DC.B -1 ; [$B9] - DC.B -1 ; [$BA] - DC.B -1 ; [$BB] - DC.B -1 ; [$BC] - DC.B -1 ; [$BD] - DC.B -1 ; [$BE] - DC.B -1 ; [$BF] - DC.B 0 ; [$C0] - DC.B 0 ; [$C1] - DC.B 0 ; [$C2] - DC.B 0 ; [$C3] - DC.B 0 ; [$C4] - DC.B 0 ; [$C5] - DC.B 0 ; [$C6] - DC.B 0 ; [$C7] - DC.B -1 ; [$C8] - DC.B -1 ; [$C9] - DC.B -1 ; [$CA] - DC.B -1 ; [$CB] - DC.B -1 ; [$CC] - DC.B -1 ; [$CD] - DC.B -1 ; [$CE] - DC.B -1 ; [$CF] - DC.B 0 ; [$D0] Reset CPU DC.B 0 ; [$D1] - DC.B 0 ; [$D2] - DC.B 0 ; [$D3] - DC.B 0 ; [$D4] - DC.B 0 ; [$D5] - DC.B 0 ; [$D6] - DC.B 0 ; [$D7] - DC.B 1+1 ; [$D8] Read A/D Status DC.B 1+1 ; [$D9] Read User Input DC.B -1 ; [$DA] - DC.B -1 ; [$DB] - DC.B 1+1 ; [$DC] read external switches DC.B -1 ; [$DD] - DC.B -1 ; [$DE] - DC.B -1 ; [$DF] - DC.B 0 ; [$E0] Write to internal PMGR memory DC.B 0 ; [$E1] Download Flash EEPROM Code DC.B 0+1 ; [$E2] Get Flash EEPROM Status DC.B 0 ; [$E3] - DC.B 0 ; [$E4] - DC.B 0 ; [$E5] - DC.B 0 ; [$E6] - DC.B 0 ; [$E7] - DC.B -1 ; [$E8] Read PMGR internal memory DC.B -1 ; [$E9] - DC.B 1+1 ; [$EA] Read PMGR firmware version number DC.B -1 ; [$EB] - DC.B -1 ; [$EC] Execute self test DC.B -1 ; [$ED] PMGR diagnostics (selector-based) DC.B -1 ; [$EE] - DC.B 0 ; [$EF] PMGR soft reset DC.B 0 ; [$F0] - DC.B 0 ; [$F1] - DC.B 0 ; [$F2] - DC.B 0 ; [$F3] - DC.B 0 ; [$F4] - DC.B 0 ; [$F5] - DC.B 0 ; [$F6] - DC.B 0 ; [$F7] - DC.B -1 ; [$F8] - DC.B -1 ; [$F9] - DC.B -1 ; [$FA] - DC.B -1 ; [$FB] - DC.B -1 ; [$FC] - DC.B -1 ; [$FD] - DC.B -1 ; [$FE] - DC.B -1 ; [$FF] - ;________________________________________________________________________________________ ; ; _CommsPower - Communications' port power control ; _SerialPower ($A685) - Serial port power control ; ; Enter with: D0 = bit pattern ; BIT INDICATION ; 0 1 = ignore internal modem ; 1 not used ; 2-6 0000 = Do something to port B serial ; 0001 = Do something to port A serial/modem ; 0010 = Do something to port C modem ; 0011 = Do something to Ethernet Port ; 0100 thru 1111 are not used (yet) ; 7 1 = power port OFF ; 0 = power port ON ; Enter with: D1 - trap word ; ; Implementation: ; The SerialPower (now also CommsPower) code now resides in the CommsPowerTables of the ; Power Manager Primitives. ; ; History: ; The TIM internal (serial) modem is multiplexed thru port A. So bit 0 controls ; the use of the internal modem. When port C was created (for Dart), the used of it ; was backpatched to TIM. Now if you power ON port A and a serial modem is installed, ; as in TIM, the 'power A ON' code calls 'power C ON', which turns the modem on. ; ; On Dart, the internal modem is not multiplexed thru serial port A. To Power on the ; modem, call 'power C ON' directly. ; ; On Blackbird, the internal modem is not multiplexed thru serial port A. To Power on the ; modem, call 'power C ON' directly. Onboard Ethernet is treated like another port; Call ; 'Power Enet ON' directly. ; ; To power the SONIC LP: ; Turn on power to the SONIC (assert Whitney signal ENET_RESET_L) ; Wait for crystal to stabilize (50 ms, a LONG time) ; Deassert SLEEP line (deassert Whitney signal ENET_RESET_L) ; ; ------------------- ; For Posterity, the TIM Algorithm: ; Determine what needs to be powered on for the SCC port that is being opened. There ; are two SCC ports (A/B) which may be connected to the external ports (the modem and ; printer ports), or the internal modem (for port B only). ; ; 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: ; make MODEM_RESET an input (to assert reset) - signal will float up ; turn on +5V and -5V to modem (MODEM_PWROUT) ; wait >2ms to allow +5V and -5V to settle ; enable the modem (*MODEM_PWR) ; turn on SCC ; wait >5ms to allow reset time ; make MODEM_RESET an output ; write a zero to MODEM_RESET to de-assert reset - drive it low ; ;________________________________________________________________________________________ CommsPower SerialPower with PrimInfoTbleRec,PmgrRec,PmgrPramRec,PmgrPrimitivesRec,ModemTblRec,CommsPwrTblRec @workregs REG d1-d2/a0 ; movem.l @workregs,-(sp) ; save our regs move.l PMgrBase, a0 ; point to Power Manager globals LoadTbl PmgrCommTblPtr,a0,a0 ; get pointer to Comms Power primitive table beq @OutOfRange ; sorry no table clr.l d1 ; clear work register move.w d0,d1 ; get a copy of d0 to work on asr.w #2,d1 ; right justify selector field andi.w #$1f,d1 ; mask only selector field move.l CommsPwrCount(a0), d2 ; length of table asr.l #2, d2 ; number of entries in table asr.l #1, d2 ; half as many valid selectors as entries cmp.l d2, d1 ; are we in range? bhs.s @OutOfRange ; no, so punt btst #7,d0 ; is it on or off beq.s @on ; on, so look at first half of table add.l d2, d1 ; off, so look at second half of table @on tst.l ([a0, d1.l*4]) ; is there a routine? beq.s @done ; zero offset means no rtn jsr ([a0, d1.l*4]) ; go execute routine moveq #noErr, d0 ; everything just dandy bra.s @done ; @OutOfRange MOVE.W #paramErr,D0 ; Abort and return error @done movem.l (sp)+,@workregs ; restore our regs RTS ;——————————————————————————————————————————————————————————————————————————————————————— djw ; Modem Sound Interrupt Handlers ; ; Modem sound on Tim is implemented through DFAC's aux channel. The modem demands ; and releases the sound path through the MODEM_SND_ENABLE signal (on VIA1 CB2). The ; system monitors that bit to determine whether it should enable or disable the modem ; sound path. ; ; When the modem is powered, a CB2 interrupt handler is installed. When the modem ; demands sound, we immediately enable the sound path. The interrupt handler then ; re-configures the VIA CB2 interrupt to trigger on the falling edge. A new interrupt ; handler is installed which disables the sound path. The interrupt handlers ping-pongs ; back a ; ; Input: none ; Output: none ; ModemSndOnInt move.b #(1< jsrTBL sndPlayThruVol ; set volume for playthrough moveq.l #sndAuxiliary,d0 ; jsrTbl sndInputSelect ; select aux source bclr.b #6,([VIA],vPCR) ; change from pos to neg edge int lea ModemSndOffInt,a0 ; disable sound routine move.l a0,jModemSnd ; install in Level 1 VIA1 dispatch table rts ;——————————————————————————————————————————————————————————————————————————————————————— ; Modem Sound Interrupt Handlers | ; V ; Modem sound on Tim is implemented through DFAC's aux channel. The modem demands ; and releases the sound path through the MODEM_SND_ENABLE signal (on VIA1 CB2). The ; system monitors that bit to determine whether it should enable or disable the modem ; sound path. ; ; When the modem is powered, a CB2 interrupt handler is installed. When the modem ; demands sound, we immediately enable the sound path. The interrupt handler then ; re-configures the VIA CB2 interrupt to trigger on the falling edge. A new interrupt ; handler is installed which disables the sound path. The interrupt handlers ping-pongs ; back a ; ; Input: none ; Output: none ; ModemSndOffInt move.b #(1< 0, disable idle ; Exit with: D0 = idle disable count ;________________________________________________________________________________________ IdleState TST.L D0 ; What selector do we have BGT IdleDisable ; D0 > 0 BEQ IdleEnable ; D0 = 0 BMI IdleRead ; D0 < 0 ;________________________________________________________________________________________ ; ; GoToSleep, WakeUp, SleepQInstall, SleepQRemove ; ; Enter : D1 = trap word ; D0 = Sleep type ; ; Exit : All regs unchanged ; ; SlpQInstall adds a sleep queue entry into the sleep queue. ; ; SlpQRemove deletes a sleep queue entry from the sleep queue. ; ; GoToSleep is called by the event manager when it determines that ; there is no work being done. Drivers are called to save their state, ; then the PMGR is ordered to put the system to sleep. When a waking ; event occurs, the PMGR powers up the system. The reset code jumps to ; WakeUp if the sleep flag is set. WakeUp restores the system hardware ; state, reloads the 68000 regs, and returns to the calling routine. ;________________________________________________________________________________________ GoToSleep BTST #9,D1 ; $A28A BNE SlpQInstall BTST #10,D1 ; $A48A BNE SlpQRemove *** BRA @Sleep ; $A08A @Sleep MOVE.L PMgrBase,A0 ; Get Power Manager Base Pointer BSET #InSleep,PmgrFlags(A0) ; test and set sleep semaphore BNE.S @Exit ; if in sleep, exit! don't re-enter MOVE.L D0,D1 ; Save the sleep type BSR.W SetSupervisorMode ; Set the machine to supervisor mode MOVE.W D0,SaveCPUState.\ CPUSRsave(A0) ; Save the store away the status register MOVE.W D0,PwrCycSave.\ PCRSRsave(A0) ; Save this stupid value for historical purposes MOVE.L D1,D0 ; restore the sleep type BSR.S TakeASnooze ; Take a snooze MOVE.W SaveCPUState.\ CPUSRsave(A0),SR ; Restore the Status Register to the proper world @Exit RTS ;________________________________________________________________________________________ ;________________________________________________________________________________________ TakeASnooze SleepRegs REG A0-A6/D1-D7 MOVEM.L SleepRegs,-(SP) ; Save registers MOVE.L D0,D2 ; Save a sleep type in D2 @checkinprogress BTST #dockNoSleep,\ dockFlags(A0) ; is sleeping allowed (if we're connected to a bar) BEQ.S @checkAuto ; -> yes, onward! BSR.L DockingSleepDenied ; have the Docking Manager put up a notification BRA AbortSleep ; then abort! @checkAuto CMP.B #SleepRequest,D0 ; auto sleep request BEQ.S @validselector @checkDnd CMP.B #SleepDemand,D0 ; User sleep, from finder or command key BEQ.S @validselector @checkNow CMP.B #SleepNow,D0 ; Critical low power sleep BNE AbortSleep @validselector MOVE.W SaveCPUState.\ CPUSRsave(A0),SR ; Restore the SR so that when VM is on the queue is run in User Mode BCLR #AvoidNetDiag,\ PmgrFlags(A0) ; tst/clear the avoid bit BNE.S @traverse ; if bit was set, skip dialog MOVE.L SleepNetHook(A0),D1 ; IF SleepNetHook present THEN BEQ.S @nextHook ; MOVEA.L D1,A1 ; Get pointer JSR (A1) ; Call Hook BNE AbortSleep ; IF Bad Close THEN exit @nextHook ; ELSE MOVE.L SleepHook(A0),D1 ; IF SleepNetHook present THEN BEQ.S @closeAT ; MOVEA.L D1,A1 ; Get pointer JSR (A1) ; Call Hook @closeAT ; ENDIF BSR.W CheckAppleTalk ; Close AppleTalk BNE AbortSleep ; Branch if close denied @traverse MOVE.W D2,D0 ; Restore D0 CMP.W #SleepNow,D0 ; If passing sleepnow to sleepq then change it BNE.S @doQ ; to a sleep demand MOVEQ #SleepDemand,D0 ; sleepNow -> sleepDemand @doQ BSR.W DoQueueStack ; Walk the queue MOVE.L D0,D1 ; Save a copy of DoQueueStack result BSR.W SetSupervisorMode ; Return to supervisor mode so that we may continue TST.L D1 ; DoQueueStack == Ok to goto sleep ? BNE.W AbortSleep ; Nope. Get out! BSR.W CloseAppleTalk ; Else shut down atalk BCLR #InSleep,PmgrFlags(A0) ; clr the sleep indicator BCLR #PmgrShutdownEnb,\ PmgrFlags1(A0) ; disable shutdown across sleep MOVEA.L A0,A2 ; A2 = Power Manager Vars BSR.W SaveSetCrsr ; Save and Set cursor to a watch cursor ; run thru the table of machine-specific operations to perform for going to sleep... ORI.W #HiIntMask,SR ; Disable interrupts BackToSleep LoadTbl SleepTblPtr,A2,A0 ; get pointer to the SleepTable LEA @Resume,A1 ; set up return address in A1 BRA.S @Resume ; WHILE tableItem != NIL DO { @SaveLoop MOVEA.L D0,A2 ; get address of calling routine JMP (A2) ; do the routine @Resume MOVE.L (A0)+,D0 ; increment to the next table item BNE.S @SaveLoop ; } BRA.S * ; wait for sleep (yawn...) AbortSleep BSR.W SetSupervisorMode ; Set the machine to supervisor mode BCLR #InSleep,PmgrFlags(A0) ; Clear the sleep indicator MOVE.W D2,D0 ; Restore sleep type to D0 MOVEM.L (SP)+,SleepRegs RTS ; Return to caller ;________________________________________________________________________________________ ; WakeUp is reached from the reset (power up) code if the sleep flag is set. ; ; Enter : A2 = Pointer to Pmgr Globals ; Exit : All regs unchanged ;________________________________________________________________________________________ WakeUp ORI.W #HiIntMask,SR ; no more interrupts MOVEA.L PmgrBase,A0 ; make sure that we have pointer to PmgrBase LoadTbl WakeTblPtr,A0,A0 ; get pointer to the WakeTable LEA @Resume,A1 ; set up return address in A1 BRA.S @Resume ; WHILE tableItem != NIL DO { @RestoreLp MOVEA.L D0,A2 ; get address of calling routine JMP (A2) ; do the routine @Resume MOVE.L (A0)+,D0 ; increment to the next table item BNE.S @RestoreLp ; } MOVEA.L PmgrBase,A2 ; point to the Power Manager globals BTST #dockNoWakeup,\ ; dockFlags(A2) ; can we wakeup with this bar attached? BEQ.S @CanWakeup ; -> yes, continue waking up MOVEM.L (SP),SleepRegs ; restore the registers but don't touch the stack MOVEA.L PmgrBase,A2 ; point to the Power Manager globals again BSR.L DockingWakeupDenied ; setup the notification message BRA BackToSleep ; then go put the machine back to sleep @CanWakeup ; MOVEM.L (SP),SleepRegs ; Restore the registers but don't touch the stack BSR.L SCSIDiskWakeAlert ; check if a disk mode cable is plugged in BSR.L InitSCSIHW ; Init the SCSI chip LEA Time,A0 ; load parameter for ReadDateTime _ReadDateTime BSR KbdReset ; clear the keyboard maps MOVEA.L PmgrBase,A2 ; Get Power Manager globals base TST.L WakeUpHook(A2) ; Do we have a sleep hook? BEQ.S @noHook ; Nope... go on MOVE.L WakeUpHook(A2),A0 ; Get the sleep hook JSR (A0) ; Go do it... @noHook MOVE.W SaveCPUState.\ CPUSRsave(A2),SR ; Restore the SR so that when VM is on the queue is run in User Mode _ADBReInit ; Init the ADB devices _ShowCursor ; Alive now tell the user it's OK BSR.W MPPOpen ; Open the driver since elements in the queue might need some MPP services MOVEQ #SleepWakeUp,D0 ; Go through wake queue BSR.W DoQueue ; Run through the queue in proper order MOVEA.L PmgrBase,A2 ; Get Power Manager globals base BSR.W RemoveMsg CLR.B Level4Cnt(A2) ; Clear level 4 count down timer MOVE.B #-1,LastLevel(A2) ; Reset battery level CLR.L BatQ(A2) ; Clear queue CLR.L BatQ+4(A2) MOVE.B #8,BatQIndex(A2) ; Reset index BCLR #PmgrShutdownReq,\ PmgrFlags1(A2) ; clear any shutdown request BSET #PmgrShutdownEnb,\ PmgrFlags1(A2) ; enable shutdown BSR.W RestoreScreen ; Restore cursor and screen and back into the world. BSR.W SetSupervisorMode ; Return to supervisor mode MOVEM.L (SP)+,SleepRegs ; Now really restore the world MOVEQ #0,D0 ; and return a zero result RTS ;________________________________________________________________________________________ ; ; CheckAppleTalk - checks AppleTalk drivers depending on the sleep ; level, what is open, and what the user OK's. ; ;________________________________________________________________________________________ STRING PASCAL CheckAppleTalk MOVEM.L A0-A3/D1-D2,-(SP) ; Try to close AppleTalk and warn user of this SUB.W #ioQElSize,SP ; Allocate IO stack frame MOVE.L SP,A3 ; Save this place MOVE.L PmgrBase,A2 ; MOVEQ #$0F,D1 ; Lower nibble indicates ATalk in use AND.B PortBUse,D1 CMP.B #1,D1 BNE @okexit ; Do exit if no Atalk MOVE.W D0,D1 ; Case on request, demand, and now sleeps CMP.B #SleepRequest,D0 ; Request case BNE.S @dmndcase ;________________________________________________________________________________________ ; Request sleep (time out) case. If plugged in or chooser bit set or server mounted then sleep ; denied, else ok. ;________________________________________________________________________________________ @reqcase BTST #noATChg,ChooserBits ; If magic chooser bit set then no sleep BEQ @done BTST #HasCharger,Charger(A2) ; If plugged in then no sleep BNE @done BTST #XPPLoadedBit,PortBUse ; Test for XPP in use BNE.S @reqcase1 ; Branch if so MOVE.B #ClosedMPP,WakeWarn(A2) ; Set flag for wake up warning (MPP closed) BRA @okexit ; Bye now @reqcase1 BSR XPPCheck ; Try to close XPP BNE @done ; Branch if not MOVE.B #ClosedXPP,WakeWarn(A2) ; Set flag for wake up warning (XPP closed) BRA @okexit ;________________________________________________________________________________________ ; Demand sleep (Finder - Battery DA) case. User warned of different conditions and given the choice ; to sleep or not. ;________________________________________________________________________________________ @dmndcase CMP.B #SleepDemand,D0 ; Demand case BNE.S @nowcase BTST #noATChg,ChooserBits ; If no magic chooser bit then branch BNE.S @dmndcase1 ; Magic chooser bit is set so give the user the big bad warning. BSR.W HarshWarn ; Warn user of impending doom BNE @done ; No sleep if user is scared off MOVE.B #ClearedChsr,WakeWarn(A2) ; Set flag for wake up warning (magic bit cleared) BSET #noATChg,ChooserBits ; Clear magic chooser bit BRA @okexit ; Only MPP is open so give the user the wimpy warning. @dmndcase1 BTST #XPPLoadedBit,PortBUse ; Test for XPP in use BNZ.S @dmndcase2 ; Branch if so BSR.W WimpyWarn ; Warn user of possible problems BNE.S @done ; Branch if chickened out MOVE.B #ClosedMPP,WakeWarn(A2) ; Set flag for wake up warning (MPP closed) BRA.S @okexit ; We're cool ; XPP is open and a server may be mounted. If no server then give the wimpy warning, else ; give a stronger one. @dmndcase2 BSR XPPCheck ; Try to close XPP BNE.S @dmndcase3 ; Branch if not able BSR.W WimpyWarn ; Warn user of possible problems BNE.S @done ; Branch if chickened out MOVE.B #ClosedXPP,WakeWarn(A2) ; Set flag for wake up warning (XPP closed) BRA.S @okexit ; We're cool ; Server is mounted so give the strong warning. @dmndcase3 BSR.W StrongWarn ; Be firm but gentle BNE.S @done ; Talked him out of it MOVE.B #ClosedSvr,WakeWarn(A2) ; Set flag for wake up warning (server lost) BRA.S @okexit ; It was rough but we're fine ;________________________________________________________________________________________ ; Now sleep (Low power) case. Close any and all but select the right wake up warning. ;________________________________________________________________________________________ @nowcase MOVE.B #ClearedChsr,WakeWarn(A2) ; Set flag for wake up warning (magic bit cleared) BSET #noATChg,ChooserBits ; Clear magic chooser bit BEQ.S @nowcase4 ; Branch if bit was active MOVE.B #ClosedMPP,WakeWarn(A2) ; Set flag for wake up warning (MPP closed) BTST #XPPLoadedBit,PortBUse ; Test for XPP in use BNZ.S @nowcase4 ; Branch if so MOVE.B #ClosedXPP,WakeWarn(A2) ; Set flag for wake up warning (XPP closed) BSR XPPCheck ; Try to close XPP BEQ.S @nowcase4 ; Branch if did MOVE.B #ClosedSvr,WakeWarn(A2) ; Set flag for wake up warning (server lost) @nowcase4 @okexit MOVEQ #0,D0 @done ADD.W #ioQElSize,SP ; Release stack frame MOVEM.L (SP)+,A0-A3/D1-D2 TST.W D0 RTS ;________________________________________________________________________________________ ; ; 91/02/13 - AG ; ; Close appletalk drivers if necessary ; ;________________________________________________________________________________________ CloseAppleTalk MOVEM.L A0-A3/D1-D2,-(SP) ; Try to close AppleTalk and warn user of this SUB.W #ioQElSize,SP ; Allocate IO stack frame MOVE.L SP,A3 ; Save this place MOVE.L PmgrBase,A2 ; MOVEQ #$0F,D1 ; Lower nibble indicates ATalk in use AND.B PortBUse,D1 CMP.B #1,D1 BNE @Closedone ; Do exit if no Atalk BSR.W AllClose ; Shutdown everything @Closedone ADD.W #ioQElSize,SP ; Release stack frame MOVEM.L (SP)+,A0-A3/D1-D2 TST.W D0 RTS ;——————————————————————————————————————————————————————————————————————— ; CloseATalk support routines. ;——————————————————————————————————————————————————————————————————————— StrongWarn MOVE.W #-16386,D0 BRA.S Warn WimpyWarn MOVE.W #-16387,D0 BRA.S Warn HarshWarn MOVE.W #-16388,D0 Warn CLR.W -(SP) ; Send the warning MOVE.W D0,-(SP) CLR.L -(SP) _Alert MOVE.W (SP)+,D0 ; D0 non-zero if canceled SUBQ.W #1,D0 ; 1 is sleep button RTS MPPClose MOVE.L A3,A0 ; Get stack frame pointer MOVE #~MPPUnitNum,ioRefNum(A0) ; Close MPP _Close TST.W D0 RTS ; Sucess returned in status XPPClose MOVE.L A3,A0 ; Get stack frame pointer LEA #'.XPP',A1 ; Get XPP refnum MOVE.L A1,ioVNPtr(A0) MOVE.B #fsCurPerm,ioPermssn(A0) _Open _Close ; Close XPP TST.W D0 RTS ; Sucess returned in status AllClose MOVE.L A3,A0 ; Get stack frame pointer LEA #'.XPP',A1 ; Get XPP refnum MOVE.L A1,ioVNPtr(A0) MOVE.B #fsCurPerm,ioPermssn(A0) _Open MOVE #CloseAll,csCode(A0) ; Close everything _Control _Close MOVE #~MPPUnitNum,ioRefNum(A0); Close MPP _Close TST.W D0 RTS ; Sucess returned in status ;——————————————————————————————————————————————————————————————————————— ; ; MPPOpen - routine to open the MPP driver. If the driver is not necessary, the open ; will fail, so just try to open the driver blindly ; ; input ; none ; ; output ; none ; ; usage ; a0 - pointer to iopb ; a1 - pointer to driver name ; ;——————————————————————————————————————————————————————————————————————— MPPOpen MOVEM.L A0-A3/D1-D2,-(SP) ;•••••••••••••••• ; temporarily don't even call this! ; so we stop switching from ethertalk to localtalk across sleep IF BlackBirdDebug THEN TestFor PrattExists bne.s @skipit BSR SelectAtlkPort ; make sure AppleTalk knows which port to use @skipit ENDIF ;•••••••••••••••• LEA -ioQElSize(SP),SP ; Allocate IO stack frame MOVEA.L SP,A0 ; Save this place LEA #'.MPP',A1 ; Get MPP refnum MOVE.L A1,ioVNPtr(A0) MOVE.B #fsCurPerm,ioPermssn(A0) _Open LEA ioQElSize(SP),SP ; Release stack frame MOVEM.L (SP)+,A0-A3/D1-D2 RTS ;________________________________________________________________________________________ ; ; Routine: SelectAtlkPort ; ; Inputs: none ; ; Outputs: none ; ; Trashes: D0-D1/A0-A1 ; ; Function: Calls the lmgr to see if the AppleTalk connection in use ; before going to sleep is still available. If it isn't, it ; switches to the preferred connection (usually SCC port B). ;________________________________________________________________________________________ SelectAtlkPort ; MOVE.W #mapTrue,RomMapInsert ; map the ROM into the resource chain SUBQ.W #4,SP PEA 'lmgr' ; theType CLR.W -(SP) ; theID _GetResource ; try to load the ‘lmgr’ MOVE.L (SP)+,D0 ; did we get it BEQ.S @NoLmgr ; -> no, just exit MOVEA.L D0,A0 ; get the handle to the ‘lmgr’ MOVE.L A0,-(SP) ; and push a copy for ReleaseResource CLR.L -(SP) ; param1 (none) CLR.L -(SP) ; param2 (none) PEA 6 ; selector MOVEA.L (A0),A0 JSR 2(A0) ; call the ‘lmgr’ to switch the ‘atlk’ if necessary LEA 12(SP),SP ; (toss parameters, C style) _ReleaseResource ; all done, so unload the resource @NoLmgr RTS ;________________________________________________________________________________________ ; ; 91/02/13 - AG ; ; DoQueueStack - this routine is called to traverse the sleep queue. if ; the sleep type is "now", "demand", or "WakeUp", the queue will only ; be run once. otherwise, the queue will be run twice. once to check if ; its ok to sleep, if ok, the sleep type is changed to "demand" and the ; queue is rerun. if not ok, then the sleep type is change to "Unlock" ; and the queue is rerun, Each proc is passed its queue entry in A0. ; ; input ; d0 sleep type ; a0 ptr to pmgrglobals ; ; output ; d0 ; == 0 ok to sleep ; != 0 queue rejected auto sleep ; ; usage ; d d0 sleep type/result ; a0 pointer to first element/ ptr to pmgrglobals ; ;——————————————————————————————————————————————————————————————————————— DoQueueStack MOVEM.L A0/D1,-(SP) ; save pmgrglobals ptr MOVE.L SlpQHead(A0),A0 ; Get ptr to first element CMP.W #SleepRequest,D0 ; is it auto sleep ? BNE.S @runTheQueue ; if no, just run the queue @testTheQueue BSR.s Handle_Element ; ask queue about auto sleep BNE.s @undosleepreq ; if error returned, undo sleep MOVEQ #SleepDemand,D0 ; ... else set to sleep demand @runTheQueue BSR.S Handle_Element ; handle element CLR.L D0 ; return no error @exit MOVEM.L (SP)+,A0/D1 ; restore pmgrglobals ptr RTS @undosleepreq MOVEQ #SleepUnlock,D0 ; Since sleep denied, set to unsleep BSR.S Handle_Element ; handle element MOVEQ #SleepUnlock,D0 ; return non zero result BRA.S @exit ;——————————————————————————————————————————————————————————————————————— ; ; Handle Element - this is a routine which is used to execute a queue element. if the ; element pointer is null, the routine exits and returns 0 in d0. if the element ; pointer is not null, then the routine will do two thing: ; 1) handle the next element recursively ; 2) execute the current element's sleep proc ; the queue will essentially be traversed in post fixed order ; ; input ; a0 pointer to queue element ; d0 sleep type ; ; output ; d0 result ; == 0 ok to sleep ; != 0 sleep rejected ; ; Usage ; a0 pointer to element ; d a1 pointer to sleep proc ; d d0 sleep type/result ; d d1 saved sleep type/ condition code check ; ;——————————————————————————————————————————————————————————————————————— ProcRegs REG a0-a6/d1-d7 Handle_Element MOVE.L a0,d1 ; test element pointer BEQ.S @noelement ; if invalid element ptr, exit with d0 clear MOVE.L d0,d1 ; save a copy of type in d1 @validelement MOVEM.L a0/d1,-(sp) ; save element pointer and type MOVE.L SleepqLink(a0),a0 ; pass in pointer to next element BSR.S Handle_Element ; handle the next element in queue MOVEM.L (sp)+,a0/d1 ; restore pointer and type BNE.S @exit ; if result != zero, exit @callproc MOVE.L SleepqProc(a0),D0 ; get a pointer to the proc BEQ.S @noelement ; if no pointer, exit; flags set by move MOVEA.L d0,a1 ; load pointer in address register for execution MOVE.L d1,d0 ; restore type to d0 MOVEM.L ProcRegs,-(sp) ; save the world before calling proc <15> ag JSR (a1) ; call sleep proc MOVEM.L (sp)+,ProcRegs ; restore the world CMP.W #SleepRequest,D1 ; is this request or demand? BNE.S @noelement ; IF NOT Request THEN clear result & exit TST.L d0 ; ELSE set condition codes on result RTS ; @noelement moveq #0,d0 ; clear result @exit RTS ; exit ;——————————————————————————————————————————————————————————————————————— ; ; XPPCheck - this is a routine checks to see if any servers are mounted. ; D0 will return the error from the close. if no error, no servers! ; to restore the world, reopen the driver after. ; ; input ; a3 pointer to pb ; ; output ; d0 result ; == 0 no servers mounted ; != 0 servers mounted ; ; Usage ; a0 pointer to pb ; a1 pointer to driver name ; a3 pointer to pb ; ;——————————————————————————————————————————————————————————————————————— XPPCheck MOVE.L a3,a0 ; Get pb pointer MOVE.W #~xppUnitNum,ioRefNum(A0) ; _Close ; Close XPP MOVE.L d0,-(sp) ; save result LEA #'.XPP',a1 ; Get XPP refnum MOVE.L a1,ioVNPtr(a0) ; load pointer to driver MOVE.B #fsCurPerm,ioPermssn(a0) ; set permission _Open ; get refnum MOVE.L (sp)+,d0 ; restore result TST.W d0 ; test close result RTS ; Sucess returned in status ;——————————————————————————————————————————————————————————————————————— ; ; DoQueue - Call each procedure in the sleep/wake queue with the parameter passed ; in D0. Each proc is passed its queue entry in A0. ;——————————————————————————————————————————————————————————————————————— DoQueue CMP.W #SleepNow,D0 ; Sleep now BEQ.S @mustsleep CMP.W #SleepDemand,D0 ; Sleep demand BEQ.S @mustsleep CMP.W #SleepWakeUp,D0 ; or wake calls are not denied BEQ.S @mustsleep @startreq MOVE.L D0,D7 ; Save call type MOVE.L PmgrBase,A1 MOVE.L SlpQHead(A1),A0 ; Get head pointer to sleep queue entries MOVE.L A0,D2 BEQ @noentries ; Skip rest if no entries @getreq MOVE.L SleepqProc(A0),A2 ; Get sleep proc MOVE.L A2,D2 BEQ.S @nextreq ; Go to next if no proc MOVE.W D7,D0 MOVEM.L A0-A1,-(SP) JSR (A2) ; Execute proc MOVEM.L (SP)+,A0-A1 TST.L D0 BEQ.S @nextreq ; Request ok CMP.W #SleepUnlock,D7 ; If unlocking then continue BEQ.S @nextreq MOVEQ #SleepUnlock,D0 ; Since sleep denied MOVE.L D0,D7 BRA.S @startreq ; unlock everything @nextreq CMP.L SlpQTail(A1),A0 ; Check for more entries BEQ.S @checkreq ; Branch if no more MOVE.L SleepqLink(A0),A0 ; Get next queue entry BRA.S @getreq @checkreq MOVEQ #SleepDemand,D0 ; Queue says sleep is ok CMP.W #SleepUnlock,D7 ; If we were unlocking then we are done now BNE.S @mustsleep MOVEQ #SleepDeny,D0 ; Sleep request failed RTS ; Sleep demand case @mustsleep MOVE.W D0,D7 ; Save command MOVE.L PmgrBase,A1 MOVE.L SlpQHead(A1),A0 ; Get head pointer to sleep queue entries MOVE.L A0,D2 BEQ.S @noentries ; Skip rest if no entries @getdemand MOVE.L SleepqProc(A0),A2 ; Get sleep proc MOVE.L A2,D2 BEQ.S @nextdemand ; Go to next if no proc MOVE.W D7,D0 MOVEM.L A0-A1,-(SP) JSR (A2) ; Execute proc MOVEM.L (SP)+,A0-A1 @nextdemand CMP.L SlpQTail(A1),A0 ; Check for more entries BEQ.S @noentries ; Branch if no more MOVE.L SleepqLink(A0),A0 ; Get next queue entry BRA.S @getdemand @noentries CLR.L D0 RTS ;——————————————————————————————————————————————————————————————————————— ; KbdReset - Clears all keymaps ;——————————————————————————————————————————————————————————————————————— KbdReset BigJSR RSetKMap,A1 ; Reset global keymap MOVEQ #numFDBAdr, D1 ; Number of table entries MOVE.L ADBBase, A1 ; Put Base in A1 BRA.S @10 ; Skip past record increment @loop ADD #FRecSize, A1 ; Get to next record @10 MOVEQ #2, D0 ; We're looking for keyboards CMP.B FDBOAddr(A1), D0 ; Is this one? BNE.S @notkbd ; Nope, skip around MOVE.L FDBOpData(A1),A0 ; Retrieve pointer to kbd data LEA 4(A0),A0 ; Get address of keybits MOVEQ #0,D0 MOVE.L D0,(A0)+ ; Clear the key bits MOVE.L D0,(A0)+ MOVE.L D0,(A0)+ MOVE.L D0,(A0)+ @notkbd DBRA D1, @loop ; Loop until no more RTS STRING ASIS ;——————————————————————————————————————————————————————————————————————— ; ; SlpQInstall/SlpQRemove - Installs/Removes entries in the sleep queue. ; ; Enrty: A0 = SlpQRec (pointer) ; ; Exit: D0 = Result code (word) ;——————————————————————————————————————————————————————————————————————— SlpQInstall CMP.W #slpQType,SleepqType(A0) BNE.S SlpQErr MOVE.L PmgrBase,A1 LEA SleepQHdr(A1),A1 _Enqueue RTS SlpQErr MOVEQ #SlpTypeErr,D0 RTS SlpQRemove CMP.W #slpQType,SleepqType(A0) BNE.S SlpQErr MOVE.L PmgrBase,A1 LEA SleepQHdr(A1),A1 _Dequeue RTS ;•••••••••••••••••••••••••••••••• End of Traps •••••••••••••••••••••••••••••••••••••••••• ;••••••••••••••••••••••••••••••••••• Misc ••••••••••••••••••••••••••••••••••••••••••••••• ; ; Contains: ; ; ResetPMGRInts ; SaveSetCrsr ; RestoreScreen ; SetSupervisorMode ; PMGRsend/recv ; SetPwrLvlTask ; DoSpinDown ; DoHDSpinUP ; PowerDownAll ; PortableCheck ; ;________________________________________________________________________________________ ;________________________________________________________________________________________ ; ; Routine: ResetPMGRInts ; ; Inputs: A1 - VIA1 base address ; ; Outputs: A1 - VIA1 base address ; ; Trashes: D0, D1, A0 ; ; Function: clears any pending PMGR interupts and disables modem interrupts ;________________________________________________________________________________________ ResetPMGRInts ; clear any pending PMGR interrupts MOVE.L VIA,A1 ; get VIA base address MOVE.B #(1< MOVE.L SP,A0 ; and point to it ADDQ.L #2,A0 ; (why do we have to do this?) MOVEQ #ReadINT,D0 ; PMGR command = get interrupt data BSR PMGRrecv ; go get the interrupt data (we'll just toss it) ; disable all modem interrupt sources so we won't get spurious level 3 interrupts from PG&E... CLR.B (A0) ; data to send = disable all modem interrupt sources MOVEQ #1,D1 ; 1 bytes to send MOVEQ #SetModemInts,D0 ; command = set modem interrupt sources BSR PMGRsend ; tell PG&E to turn them all off LEA 12(SP),SP ; toss the buffer MOVE.B #(1< ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: SaveSetCrsr ; ; Inputs: A2 - pointer to Power Manager variables ; A5 - Quickdraw globals ; ; Outputs: none ; ; Trashes: A0 - A1 ; ; Function: Save current state of cursor and set cursor to a watch cursor. ;———————————————————————————————————————————————————————————————————————————————————————— SaveSetCrsr MOVE.L (A5),A0 ; point to QuickDraw globals LEA Arrow(A0),A0 ; Get pointer to current cursor MOVE.L A0,SleepSaveCrsr(A2) ; save the address of cursor in our little storage SUBA.L #4,SP ; Get some stack space <34> HJR MOVE.W #watchCursor,-(SP) ; Get the watch cursor _GetCursor MOVE.L (SP)+,A0 ; Get handle to cursor MOVE.L (A0),A0 ; Dereference once MOVE.L A0,-(SP) ; push parameter on stack _SetCursor _HideCursor ; RTS ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: RestoreScreen ; ; Inputs: A2 - pointer to Power Manager variables ; A5 - Quickdraw globals ; ; Outputs: none ; ; Trashes: A0 ; ; Function: Restores the cursor to its previous state and updates the screen (but not ; in that order :-) ;———————————————————————————————————————————————————————————————————————————————————————— RestoreScreen JsrRoutine ScreenRedrawPtr,A2,A0 ; (Calls RedrawScrn from the Primitives.) MOVE.L SleepSaveCrsr(A2),-(SP) ; Get ptr to saved crsr _SetCursor ; Set the cursor _ShowCursor ; Show it to the world RTS ;________________________________________________________________________________________ ; SetSupervisorMode ; ; Input: None ; ; Destroys: D0/A1 ; ; Called by: BSR from DreamAway & Wakeup. ; ; Function: When VM is running we must switch to supervisor mode so ; that we may run necessary priviledged instructions. Yes ; we are the operatorating system and that is acceptable!!! ;________________________________________________________________________________________ SetSupervisorMode MOVE.L (SP)+,A1 ; Save a copy of return address since we might switch stacks MOVEQ #8,D0 ; Set selector to Set Supervisor Mode for VM _DebugUtil ; CMPI.W #paramErr,D0 ; IF VM is on THEN D0 = Status Register BNE.S @Cont ; ELSE MOVE.W SR,D0 ; Save the current Status Register @Cont ANDI.W #$EFFF,SR ; Make sure that we are in the interrupt stack JMP (A1) ; Get out of here ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: PMGRsend/recv ; ; Inputs: A0 - data buffer ; D0 - command ; D1 - length ; ; Outputs: A0 - data buffer ; D0 - result code ; ; Trashes: A0 - A1 ; ; Function: Handy PmgrOp calling routines. ;———————————————————————————————————————————————————————————————————————————————————————— PMGRrecv MOVEQ #0,D1 ; Get data from pmgr PMGRsend MOVE.L A0,-(SP) ; pmRBuffer MOVE.L A0,-(SP) ; pmSBuffer MOVE.W D1,-(SP) ; pmLength MOVE.W D0,-(SP) ; pmCommand MOVE.L SP,A0 _PmgrOp MOVE.W pmLength(sp),d1 ; return count LEA pmRBuffer(SP),SP ; Release stack frame MOVE.L (SP)+,A0 ; pmRBuffer RTS ;———————————————————————————————————————————————————————————————————————————————————————— ; Routine: SetPwrLvlTask ; ; Inputs: A2 - PMgrBase ; ; Outputs: none ; ; Trashes: A0 ; ; Function: set the power manager micro levels to match those set in the 680x0 pmgr globals ;———————————————————————————————————————————————————————————————————————————————————————— SetPwrLvlTask @SetPwrLvlRegs REG A0-A1/D0-D1 MOVEM.L @SetPwrLvlRegs,-(SP) ; working registers MOVE.W #$4201,-(SP) ; pushpram byte 0x42 + one byte of data MOVE.W LowWarn(A2),-(SP) ; move the current 68K pmgr setting into buffer for set MOVEA.L SP,A0 ; set up data buffer MOVEQ #xPramWrite,D0 ; command to send MOVEQ #3,D1 ; number of data bytes BSR.S PMGRsend ; send command CLR.L (SP)+ ; clean up buffer MOVEM.L (SP)+,@SetPwrLvlRegs ; Restore working registers RTS ;________________________________________________________________________________________ ; ; Routine: DoSpinDown ; ; Inputs: none ; ; Outputs: none ; ; Trashes: D0, A0 ; ; Function: kills power to the internal hard disk, unless spindown is disabled ;________________________________________________________________________________________ DoSpinDown MOVEM.L A0-A1,-(SP) ; save some registers MOVEA.L PMgrBase,A1 ; point to the Power Manager's globals BTST.B #QuickHDSpinDwn,PmgrFlags2(A1) ; quick spin down enabled? BNE.S @MustSpindown ; -> yes, have to spin down even if disabled IF HDSpinDownDisable=7 THEN TST.B PmgrFlags2(A1) ; is spindown allowed? BMI.S @Done ; -> no, ignore it ELSE BTST #HDSpinDownDisable,PmgrFlags2(A1) ; is spindown allowed? BNE.S @Done ; -> no, ignore it ENDIF @MustSpindown ; BCLR #HDPowerOn,PMgrFlags(A1); clear the flag, indicating that the drive is now spun down ; call each of the routines in the hard disk queue to notify anyone who cares ; that the hard disk is about to be spun down MOVEM.L D1-D2/A1-A2,-(SP) LEA hdQHead(A1),A2 ; point to the start of the hard disk queue BRA.S @NoProc @NextElement MOVEA.L D0,A2 ; point to the next queue element MOVE.L hdProc(A2),D0 ; get the pointer to the routine BEQ.S @NoProc ; -> there isn't one MOVE.L A2,-(SP) ; push a pointer to the queue element MOVEA.L D0,A0 ; point to the routine JSR (A0) ; and call it @NoProc MOVE.L hdQLink(A2),D0 ; end of the queue? BNE.S @NextElement ; -> no, keep running (huff! puff!) MOVEM.L (SP)+,D1-D2/A1-A2 ; finally, spin down the internal hard disk MOVE.B #hdOff,-(SP) ; data to send = turn off hard disk power plane MOVE.L SP,-(SP) ; pmRBuffer (not used) MOVE.L (SP),-(SP) ; pmSBuffer MOVE.W #1,-(SP) ; pmLength MOVE.W #powerCntl,-(SP) ; pmCommand MOVEA.L SP,A0 ; point to the parameter block _PMgrOp ; turn off the hard disk LEA pmRBuffer+4+2(SP),SP ; clean up the stack BNE.S @Done ; -> an error occurred CLR.L LastHD(A1) ; stop calling - disk is spun down BCLR #HDPowerOn,PMgrFlags(A1); clear the flag, indicating that the drive is now spun down MOVEM.L (SP)+,A0-A1 ; restore some registers @Done RTS ;________________________________________________________________________________________ ; ; Routine: DoHDSpinUp ; ; Inputs: none ; ; Outputs: none ; ; Trashes: D0, A0 ; ; Function: restores power to the internal hard disk ;________________________________________________________________________________________ DoHDSpinUp ; This portion of code freezes the "Spin Down" timer for the hard disk, preventing ; the Power Mgr from cutting hard disk power during ANY SCSI transaction. MOVEM.L A0-A1/D0,-(SP) ; save them regs MOVEA.L PmgrBase,A1 ; point to Pmgr locals MOVE.L #0,LastHd(A1) ; always freeze the spin down timer BTST.B #HDPowerOn,PmgrFlags(A1) ; set flag indicating drive is now spun up djw BNE.S @Done ; drive already spun up - skip PmgrOp call djw MOVE.B #hdOn,-(SP) ; data to send = turn off hard disk power plane MOVE.L SP,-(SP) ; pmRBuffer (not used) MOVE.L (SP),-(SP) ; pmSBuffer MOVE.W #1,-(SP) ; pmLength MOVE.W #powerCntl,-(SP) ; pmCommand MOVEA.L SP,A0 ; point to the parameter block _PMgrOp ; turn off the hard disk LEA pmRBuffer+4+2(SP),SP ; clean up the stack BNE.S @Done ; -> an error occurred MOVE.L #250,D0 ; wait 250 Miliseconds BigJsr DelayNMsec,A0 ; … Spin are wheels BSET.B #HDPowerOn,PmgrFlags(A1) ; set flag indicating drive is now spun up djw @Done MOVEM.L (SP)+,A0-A1/D0 ; restore them registers RTS ; return to SCSIGet ;________________________________________________________________________________________ ; ; Routine: PowerDownAll ; ; Inputs: none ; ; Outputs: none ; ; Trashes: D0, A0 ; ; Function: called by StartInit to power down all peripheral subsystems ;________________________________________________________________________________________ PowerDownAll TestFor hwCbPwrMgr ; is there Power Mgr in this Mac ? BEQ.S @NoPMGR ; -> no, skip MOVE.B #allOff,-(SP) ; buffer contains "turn off unused devices" IF forRomulator THEN ANDI.B #~((1< ENDIF MOVE.L SP,-(SP) ; point to receive buffer MOVE.L (SP),-(SP) ; point to transmit buffer MOVE.W #1,-(SP) ; one byte of transmit data MOVE.W #PowerCntl,-(SP) ; PMGR command: power control MOVE.L SP,A0 ; A0 gets pointer to parameter block _PmgrOp LEA pmRBuffer+4+2(SP),SP ; Remove stack frame MOVEA.L PmgrBase,A0 ; get addr of globals BCLR.B #HDPowerOn,PmgrFlags(A0) ; clear HD powered on flag @NoPMGR RTS ;________________________________________________________________________________________ ; ; Routine: PortableCheck ; ; Inputs: D2 - Bits 31..16, hwCfgFlags info (possibly unknown) ; D2 - Bits 7..0, Address Decoder Kind (zero if unknown) ; A1 - Productinfo ; ; Outputs: none ; ; Trashes: D0-D1, A0-A6 ; ; Function: Called by a BSR6 from StartInit to check if we're booting ; or waking up. Since we've just called JumpIntoROM, D2 ; has the decoder kind. ;________________________________________________________________________________________ beok EQU 27 ;a BusError is expected and is OK (copied from STEqu.a to avoid duplicate label if included) PortableCheck IF isUniversal THEN BTST.L #(hwCbPwrMgr+16),D2 ; Are we running on Pmgr System BEQ.W NonPwrMgr ; NOPE. Branch... ENDIF MOVEA.L A6,A5 ; save return addr LEA @NoRAM,A6 ; load return addr in case of bus error MOVE.L PmgrBase,A2 ; get the addr of PMgrVars CMP.L #SleepConst,SleepSaveFlag(A2) ; are we waking from sleep? BNE.S @noRAM ; branch if not CLR.L SleepSaveFlag(A2) ; clear the sleep flag MOVE.L WakeVector(A2),A0 ; Go restore ourself JMP (A0) ; . NOP ; keep everything aligned @NoRAM MOVEA.L A5,A6 ; restore return addr ;———————————————————————————————————————————————————————————————————————————————— ; Routine: CheckEconoMode ; ; Input: D2 - Bits 31..16, hwCfgFlags info (possibly unknown) ; D2 - Bits 7..0, Address Decoder Kind (zero if unknown) ; ; Destroys: A0-A7,D0-D6 ; ; Called by: BSR6 from StartInit. ; ; Function: checks to see if a portable needs to be switched into econo-mode ;———————————————————————————————————————————————————————————————————————————————— CheckEconoMode ; do we have a Pratt? btst #(PrattExists//8),(ProductInfo.BasesValid1+3-(PrattExists/8))(a1) bne NonPwrMgr ; yes don't do anything MOVEA.L A6,A7 ; save the return address BigBSR6 InitWallyWorld,A1 ; download code into the PMGR, if necessary MOVEQ #0,D2 ; BigBSR6 GetHardwareInfo,A0 ; figure out what we're running on MOVE.L D2,D4 ; save the decoder type around the PRAM calls MOVEQ #$10,D1 ; read the PRAM validation byte MOVEA.L DecoderInfo.VIA1Addr(A0),A2 ; point to base of VIA1 BigBSR6 RdXByte,A0 ; MOVEQ #$A8-256,D0 ; compare what was read against expected value SUB.B D1,D0 ; MOVEQ #0,D1 ; (assume PRAM's invalid) TST.B D0 ; is PRAM OK? BNE.S @BadPRAM ; -> no, run at full speed for now MOVEQ #PmgrPramBase+PmgrPramRec.PmgrOtherFlags,D1 ; default to standard PRAM location MOVE.L PowerManagerPtr(A1),D0 ; does this box have a PMgr primitives table? IF 0 THEN BEQ.S @UseDefPRAM ; -> no, use the default location MOVEA.L A1,A2 ; ADDA.L D0,A2 ; point to the primitives table for this box ADDA.L PmgrPrimsRec.PrimInfoPtr(A2),A2 ; and then to the primitives info table MOVE.B PrimInfoTbleRec.PrimPRAMBase(A2),D1 ; get the base Power Manager PRAM byte ADDQ.B #PmgrPramRec.PmgrOtherFlags,D1 ; and adjust for the byte we want ENDIF @UseDefPRAM MOVEA.L A1,A0 ; point back to the DecoderInfo table ADDA.L DecoderInfoPtr(A0),A0 ; MOVEA.L DecoderInfo.VIA1Addr(A0),A2 ; point to the base of VIA1 BigBSR6 RdXByte,A0 ; read the desired econo-mode setting from PRAM ANDI.B #(1< ADDA.L DecoderInfoPtr(A0),A0 ; one last time ; at this point: ; A0 - pointer to DecoderInfo table ; A1 - pointer to ProductInfo table ; D2 - decoder type IF hasJAWS | hasNiagra THEN ;•••••••••••••••••••••••••••••••••••••••••••••• JAWS •••••••••••••••••••••••••••••••••••••••••••••• SPIN_WAIT equ $40000 ; delay for power manager to hit reset SPEED25MHZBIT equ 0 ; 1 = running at 25mhz IF isUniversal THEN cmp.b #Decoderkinds.NiagraDecoder,D2 ; Do we have a Niagra decoder? beq.s @DoNiagra ; -> yes, go do it.. cmp.b #Decoderkinds.JAWSDecoder,D2 ; Do we have a JAWS decoder ? bne.s NotJaws ; -> no, do next one ENDIF IF hasJAWS THEN @DoJaws MOVEA.L DecoderInfo.JAWSAddr(A0),A1 ; A1 = pointer to the base of the JAWS decoder MOVE.L #JAWSGetCPUClock,D0 ; get the offset to the CPU clock frequency register BTST #SPEED25MHZBIT,0(A1,D0.L) ; are we running at 25MHz? BEQ.S @JAWSDone ; -> no, done ENDIF @DoNiagra MOVEA.L DecoderInfo.JAWSAddr(A0),A1 ; A1 = pointer to the base of the JAWS decoder MOVEQ #(1< yes, done MOVE.B D1,JAWSEconoMode(A1) ; stuff the new mode into the econo register MOVE.W #(0<<8)+(resetCPU<<0),D3 ; data length=0, command=reset PMGR BigBSR6 USTPMGRSendCommand,A2 ; reset the system MOVE.L #SPIN_WAIT,D3 ; wait awhile for the PMGR to reset the system @spin SUBQ.L #1,D3 BNE.S @spin @JAWSDone ; bra ExitEconoMode ; NotJaws ENDIF IF hasMSC THEN ;•••••••••••••••••••••••••••••••••••••••••••••• MSC ••••••••••••••••••••••••••••••••••••••••••••••• IF isUniversal THEN CMPI.B #DecoderKinds.MSCDecoder,D2 ; do we have a MSC decoder? BNE NotMSC ; -> nope, bail ENDIF ORI.B #MSCDefConfig,D1 ; this sets 25Mhz mode by default ; D2 now has the value of the Econo Mode bit in PRAM. Yeager needs ; to make sure that this bit is never set, or it will break! MOVEQ #$1F,D2 ; mask off the CPU ID AND.W CPUIDValue(A1),D2 ; BEQ.S @MSC33MHz ; is it a new Yeager MBT 040 CMP.W #16,D2 ; is it a Yeager (040)? BEQ.S @MSC33MHz ; -> yes, do setup AND.W #7,D2 ; mask off the CPU ID CMP.W #5,D2 ; is it a 33MHz system (DB Lite)? BEQ.S @MSC33MHz ; -> yes, do setup CMP.W #2,D2 ; maybe it's a 33MHz Escher? BEQ.S @MSC33MHz ; -> yes, do setup BTST #EconoBit,D1 ; are we in going to run in econo-mode? BNE.S @NotMSC33MHz ; -> yes, we'll be running at 16MHz regardless @MSC33MHz BCLR #MSC25MHz,D1 ; setup the state machines to run at 33MHz @NotMSC33MHz MOVEA.L DecoderInfo.RBVAddr(A0),A1 ; point to the base of the MSC decoder MOVEQ #(%11111000)-256,D2 ; mask off the RAM size information AND.B MSCConfig(A1),D2 ; OR.B D2,D1 ; and add it to the base configuration MOVE.B D1,MSCConfig(A1) ; stuff the configuration into the register
MOVE.L #(0<<16)|(1<<8)|(SetModemInts<<0),D3 ; BigBSR6 USTPmgrSendCommand,A2 ; turn off modem interrupts ;◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊ ; ; This is the nasty hack. When a DBLite is docked to a Gemini or DeskBar with an external SCSI ; hard disk connected and powered up, the SCC gets charged up a little bit and ends up in a weird ; state, usually generating level 4 interrupts. Normally we initialize the SCC later, but since ; the Docking Manager isn't initialized soon enough, as soon as interrupts are opened up, we get ; stuck in the level 4 interrupt handler (which is hopefully set up). This hack will talk to the ; Gemini/DeskBar hardware directly and reset the external SCC. ROMSigAddr EQU $FEFFFFE4 ; where to find the ROM signature ROMSigLo EQU 'Russ' ; and what it is ROMSigHi EQU 'SWC!' vscClockPwr EQU $FEE00021 ; VSC power control register vscSCCclock EQU 1 ; 1=turn on SCC clock vscSCCAddr EQU $FEE08000 ; SCC base address NastyHack If Not ForRomulator Then MOVEA.L SP,A5 ; save the return address BSET #beok,D7 ; allow bus errors BSR6 @WhackSCC ; go whack the SCC (bad SCC! bad SCC! blah blah) BCLR #beok,D7 ; disallow bus errors Endif BRA.S ExitEconoMode @InitBData DC.B 9,$C0 ; do a hard reset DC.B 9,$40 ; reset the channel DC.B 4,$4C ; set async mode (magic?) DC.B 2,$00 ; zero interrupt vector for dispatcher DC.B 3,$C0 ; DCD not an auto-enable DC.B 15,$00 ; no interrupts DC.B 0,$10 ; reset ext/sts interrupts twice DC.B 0,$10 DC.B 1,$00 ; no interrupts @InitAData DC.B 9,$80 ; reset the channel DC.B 4,$4C ; set async mode (magic?) DC.B 3,$C0 ; DCD not an auto-enable DC.B 15,$00 ; no interrupts DC.B 0,$10 ; reset ext/sts interrupts twice DC.B 0,$10 DC.B 1,$00 ; no interrupts @WhackSCC CMPI.L #ROMSigLo,ROMSigAddr ; is the signature in the config ROM? BNE.S @NotGemini ; -> no, not the ROM we're looking for CMPI.L #ROMSigHi,ROMSigAddr+4 ; ditto with the other part of the signature BNE.S @NotGemini ; -> no, not the ROM we're looking for BSET #vscSCCclock,vscClockPwr ; turn on clocks to the SCC LEA vscSCCAddr,A0 LEA @InitBData,A2 ; point to channel B init data MOVEQ #@InitAData-@InitBData,D1 LEA @ResumeB,A1 BRA.S @WriteSCC @ResumeB ADDQ.W #ACtl,A0 ; point to channel A MOVEQ #@WhackSCC-@InitAData,D1 LEA @ResumeA,A1 @WriteSCC MOVE.B (A0),D2 ; read to make sure the SCC is sync'ed up BRA.S @2 ; delay for timing, too @1 MOVE.L (SP),(SP) ; delay long for reset MOVE.L (SP),(SP) MOVE.B (A2)+,(A0) @2 DBRA D1,@1 JMP (A1) @ResumeA BCLR #vscSCCclock,vscClockPwr ; turn off clocks to the SCC @NotGemini RTS6 ;◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊ NotMSC ENDIF ExitEconoMode MOVEA.L A7,A6 ; restore the return address ;________________________________________________________________________________________ ; Routine: InitAndBlankScreen | ; V ; Input: a6 - return address ; ; Destroys: A0-A7,D0-D6 ; ; Called by: BSR6 from StartInit. ; ; Function: initialize and blank the screen to meet timing requirements ;________________________________________________________________________________________ InitAndBlankScreen IF hasNiagra | hasMSC THEN movea.l a6,a7 ; save original return address MOVEQ #0,D2 ; BigBSR6 GetHardwareInfo,A0 ; figure out what we're running on cmp.b #Decoderkinds.NiagraDecoder,D2 ; Do we have a Niagra decoder? beq.s @BlankNiagra ; -> yes cmpi.b #DecoderKinds.MSCDecoder,D2 ; do we have a MSC decoder? bne @ExitInitAndBlank ; -> no, continue IF hasMSC THEN ; MOVEA.L DecoderInfo.RBVAddr(A0),A2 ; point to the base of the MSC BSET #MSCLCDReset,MSCClkCntl(A2) ; turn on clocks to the GSC so we can program it BRA.S @TestForGSC ; ENDIF ; IF hasNiagra THEN ; send command to power manager to blank the screen and delay before talking to the gsc ; D3- [data2] [data1] [length] [command] ; A0- pointer to DecoderInfo ; A6- return address @BlankNiagra MOVE.l #($E0<<0) | \ ; Write Pmgr Ram (03<<8) | \ ; count 3, 2 address + 1 data (00<<16) | \ ; addrH - $00xxH ($EA<<24),D3 ; addrL - $xxEA BigBSR6 USTPMGRSendCommand,A2 ; reset the system move.b #$0A,D3 ; port 4: d[2] = 0 (blank), d[0] = 1 adb inactive BigBSR5 USTPMgrSendByte,A4 ; and send it * bra.s @TestForGSC ; ENDIF ; test for gsc chip @TestForGSC MOVEA.L DecoderInfo.VDACAddr(A0),A0 ; point to base of gsc movea.l a7,a5 ; save return address in case of bus error bset.l #beok,d7 ; allow bus errors bsr6 @checkforGSC ; check for gsc chip bra @ExitInitAndBlank ; if not zero, buserror, no gsc, exit ; bne @ExitInitAndBlank ; if not zero, buserror, no gsc, exit ; initialize GSC early to meet hardware timing spec @loadSetup moveq.l #7,D0 ; mask off the display ID And.b GSCPanelID(A0),D0 ; get the display id MULU #(GSCPanelSkew-GSCPanelSetup+1)+(GSCDiag2-GSCDiag0+1),D0 ; LEA @GSCInitTable,A2 ; point to the entry for this display ADDA.L D0,A2 ADDQ.W #GSCPanelSetup,A0 ; point to the first register to blast MOVE.L (A2)+,(A0)+ ; initialize the main display registers MOVE.L (A2)+,(A0)+ ; LEA GSCDiag0-GSCPanelSkew-1(A0),A0 ; point to the diagnostic registers MOVE.B (A2)+,(A0)+ ; and initialize them too MOVE.W (A2)+,(A0)+ ; bra.s @ExitInitAndBlank ; done @checkforGSC move.b GSCPanelID(A0),D0 ; try reading a register moveq #0,d0 ; set CC to Equal, buserr will return not Equal rts6 ; GSC initialization table. Each entry is based on the LCD panel ID. ; ; panel gray poly panel ACD refresh blank panel ; setup scale adjust adjust clock rate shade skew diag0 diag1 diag2 @GSCInitTable DC.B $10, $00, $64, $00, $80, $02, $00, $A0, $00, $00, $03 ; ID=0 TFT1Bit DC.B $12, $00, $64, $00, $80, $02, $00, $FF, $00, $00, $03 ; ID=1 TFT3Bit DC.B $10, $00, $64, $00, $80, $02, $00, $FF, $00, $00, $03 ; ID=2 TFT4Bit DC.B $10, $00, $64, $00, $80, $02, $00, $A0, $00, $00, $03 ; ID=3 NotAssignedTFT DC.B $10, $00, $64, $00, $80, $05, $00, $A0, $00, $00, $03 ; ID=4 NotAssignedSTN DC.B $10, $00, $64, $00, $80, $05, $00, $A0, $00, $00, $03 ; ID=5 TimSTN DC.B $10, $00, $63, $00, $80, $05, $00, $9C, $00, $00, $03 ; ID=6 DBLiteSTN DC.B $10, $00, $64, $00, $80, $05, $00, $A0, $00, $00, $03 ; ID=7 No Display @ExitInitAndBlank bclr.l #beok,d7 ; disallow bus errors movea.l a7,a6 ; restore original return address ENDIF ; {hasNiagra | hasMSC} ;________________________________________________________________________________________ ; Routine: Exit ;________________________________________________________________________________________ ; V NonPwrMgr RTS6 ; return to start init ; All done ;•••••••••••••••••••••••••••••••••••• End of Misc. ••••••••••••••••••••••••••••••••••••••••••••••••• ;•••••••••••••••••••••••••••••••• Public Power Manager ••••••••••••••••••••••••••••••••••••••••••••• ; ; Contains: ; ;________________________________________________________________________________________ ;________________________________________________________________________________________ ; ; Routine: PowerMgrDispatch (trap $A09E) ; ; Inputs: D0 - selector ; ; Outputs: D0 - selector result, or error code if bad selector ; ; Trashes: varies by selector ; ; Function: This is the Power Manager's public dispatch trap, which provides a variety of ; miscellaneous functions to the public, hackers, etc. ; ; NOTE: The offset-based dispatch table is copied into RAM and converted to ; absolute addresses by InitPMgrVars. ;________________________________________________________________________________________ PowerMgrDispatch MOVEA.L PMgrBase,A2 ; point to the Power Manager's globals MOVE.L vPublicDispatch(A2),A1 ; and then to the public dispatch table CMP.W -2(A1),D0 ; is the selector in range? BHS.S @OutOfRange ; -> no, bail with an error MOVEA.L 0(A1,D0.W*4),A1 ; point to the routine JMP (A1) ; and call it @OutOfRange MOVEQ #paramErr,D0 ; abort and return error RTS ALIGN 4 DC.W 0 ; flags DC.W (PwrMgrDispEnd-PwrMgrDispVects)/4 ; number of table entries PwrMgrDispVects DC.L PMSelectorCount-PwrMgrDispVects ; [ 0] return the number of selectors supported DC.L PMFeatures-PwrMgrDispVects ; [ 1] return bitmap of Power Manager features DC.L GetSleepTimeout-PwrMgrDispVects ; [ 2] get the sleep timeout DC.L SetSleepTimeout-PwrMgrDispVects ; [ 3] set the sleep timeout DC.L GetHardDiskTimeout-PwrMgrDispVects ; [ 4] get the hard disk spindown timeout DC.L SetHardDiskTimeout-PwrMgrDispVects ; [ 5] set the hard disk spindown timeout DC.L HardDiskPowered-PwrMgrDispVects ; [ 6] returns true if hard disk is powered up DC.L SpinDownHardDisk-PwrMgrDispVects ; [ 7] spin down the hard disk DC.L IsSpindownDisabled-PwrMgrDispVects ; [ 8] returns whether or not spindown is disabled DC.L SetSpindownDisable-PwrMgrDispVects ; [ 9] enables/disables hard disk spindown DC.L HardDiskQInstall-PwrMgrDispVects ; [10] add element to HD queue DC.L HardDiskQRemove-PwrMgrDispVects ; [11] remove element from HD queue DC.L ScaledBattery-PwrMgrDispVects ; [12] return the scaled battery level DC.L AutoSleepControl-PwrMgrDispVects ; [13] enables/disables auto sleep DC.L GetIntModemInfo-PwrMgrDispVects ; [14] return information about an internal modem DC.L SetIntModemState-PwrMgrDispVects ; [15] sets the state of the internal modem DC.L MaximumProcessorSpeed-PwrMgrDispVects ; [16] return maximum processor speed DC.L CurrentProcessorSpeed-PwrMgrDispVects ; [17] return current processor speed DC.L FullProcessorSpeed-PwrMgrDispVects ; [18] returns true if processor running at full speed DC.L SetProcessorSpeed-PwrMgrDispVects ; [19] set full/reduced processor speed DC.L GetSCSIDiskModeAddress-PwrMgrDispVects ; [20] get SCSI Disk Mode HD address DC.L SetSCSIDiskModeAddress-PwrMgrDispVects ; [21] set SCSI Disk Mode HD address DC.L GetWakeupTimer-PwrMgrDispVects ; [22] get wakeup time DC.L SetWakeupTimer-PwrMgrDispVects ; [23] set wakeup time DC.L GetProcessorCycling-PwrMgrDispVects ; [24] get processor cycling state DC.L SetProcessorCycling-PwrMgrDispVects ; [25] set processor cycling state DC.L BatteryCount-PwrMgrDispVects ; [26] returns number of internal batteries DC.L GetBatteryVoltage-PwrMgrDispVects ; [27] return absolute battery voltage DC.L GetBatteryTimes-PwrMgrDispVects ; [28] returns information about battery times DC.L GetDimTimeout-PwrMgrDispVects ; [29] get the dimming timeout DC.L SetDimTimeout-PwrMgrDispVects ; [30] set the dimming timeout DC.L DimControl-PwrMgrDispVects ; [31] enables/disables dimming DC.L IsDimmingDisabled-PwrMgrDispVects ; [32] returns whether or not dimming is disabled DC.L IsAutoSlpDisabled-PwrMgrDispVects ; [33] returns whether or not autosleep is disabled PwrMgrDispEnd ;________________________________________________________________________________________ ; ; Routine: PMSelectorCount (PowerMgrDispatch selector #0) ; ; Inputs: A2 - pointer to Power Manager globals ; ; Outputs: D0 - number of selectors ; ; Trashes: A0 ; ; Function: returns the number of selectors so users can determine what selectors are ; supported ;________________________________________________________________________________________ PMSelectorCount MOVEQ #0,D0 MOVE.L vPublicDispatch(A2),A0 ; point to the public dispatch table MOVE.W -(A0),D0 ; and get the number of selectors RTS ;________________________________________________________________________________________ ; ; Routine: PMFeatures (PowerMgrDispatch selector #1) ; ; Inputs: A2 - pointer to Power Manager globals ; ; Outputs: D0 - bitmap of supported features ; ; Trashes: none ; ; Function: returns a bitmap containing bits describing which software/hardware ; features are supported on this machine ;________________________________________________________________________________________ PMFeatures LoadTbl PrimInfoTblPtr,A2,A0 ; get pointer to Info MOVE.L PrimPubFeatures(A0),D0 ; get the features bits RTS ;________________________________________________________________________________________ ; ; Routine: GetSleepTimeout (PowerMgrDispatch selector #2) ; ; Inputs: A2 - pointer to Power Manager globals ; ; Outputs: D0 - sleep timeout (number of 15 second intervals) ; ; Trashes: A0 ; ; Function: returns the current sleep timeout time ;________________________________________________________________________________________ GetSleepTimeout MOVEQ #SlpTimeOut,D0 ; which byte to read BRA.S ReadPMgrPRAM ; go read and return the value ;________________________________________________________________________________________ ; ; Routine: SetSleepTimeout (PowerMgrDispatch selector #3) ; ; Inputs: A2 - pointer to Power Manager globals ; D0 - high word: sleep time to set (number of 15 second intervals) ; ; Outputs: D0 - result code (always zero) ; ; Trashes: D1, A0 ; ; Function: sets the current sleep timeout ;________________________________________________________________________________________ SetSleepTimeout SWAP D0 ; get the new value TST.B D0 ; is it zero? BNE.S @NotZero ; -> no MOVEQ #DfltSlpTime,D0 ; yes, set it to the default @NotZero MOVE.B D0,SleepTime(A2) ; save it MOVE.B D0,D1 MOVEQ #SlpTimeOut,D0 ; where to put it BRA.S WritePMgrPRAM ; go write it out ;________________________________________________________________________________________ ; ; Routine: GetHardDiskTimeout (PowerMgrDispatch selector #4) ; ; Inputs: A2 - pointer to Power Manager globals ; ; Outputs: D0 - hard disk time (number of 15 second intervals) ; ; Trashes: A0 ; ; Function: returns the amount of time the system will wait after the last hard disk ; access before shutting down power to the hard disk ;________________________________________________________________________________________ GetHardDiskTimeout MOVEQ #HDTimeOut,D0 ; which byte to read ; Inputs: D0 - PRAM address (upper word must be zero) ; ; Outputs: D0 - byte read from PRAM ; ; Trashes: D0 ReadPMgrPRAM ADD.B PRAMBase(A2),D0 ; get the absolute PRAM address SWAP D0 ADDQ.W #1,D0 ; one byte SWAP D0 CLR.W -(SP) ; make space for a buffer on the stack MOVEA.L SP,A0 ; and point to it _ReadXPRAM ; read the byte MOVEQ #0,D0 MOVE.B (SP)+,D0 ; and return it in D0 RTS ;________________________________________________________________________________________ ; ; Routine: SetHardDiskTimeout (PowerMgrDispatch selector #5) ; ; Inputs: A2 - pointer to Power Manager globals ; D0 - high word: hard disk time to set (number of 15 second intervals) ; ; Outputs: D0 - result code (always zero) ; ; Trashes: D1, A0 ; ; Function: sets the amount of time the system will wait after the last hard disk ; access before shutting down power to the hard disk ;________________________________________________________________________________________ SetHardDiskTimeout SWAP D0 ; get the new value TST.B D0 ; is it zero? BNE.S @NotZero ; -> no MOVEQ #DfltHDTime,D0 ; yes, set it to the default @NotZero MOVE.B D0,HDTime(A2) ; save it MOVE.B D0,D1 MOVEQ #HDTimeOut,D0 ; where to put it ; Inputs: D0 - PRAM address (upper word must be zero) ; D1 - byte to write ; ; Outputs: D0 - none ; ; Trashes: D0 WritePMgrPRAM MOVE.B D1,-(SP) ; push the byte to write MOVEA.L SP,A0 ; and point to it ADD.B PRAMBase(A2),D0 ; get the absolute PRAM address SWAP D0 ADDQ.W #1,D0 ; one byte SWAP D0 _WriteXPRAM ; write the byte ADDQ.W #2,SP ST TODirtyFlag(A2) ; force an update of the new values RTS ;________________________________________________________________________________________ ; ; Routine: HardDiskPowered (PowerMgrDispatch selector #6) ; ; Inputs: A2 - pointer to Power Manager globals ; ; Outputs: D0 - 1=hard disk spinning, 0=hard disk powered down ; ; Trashes: A2 ; ; Function: spins down the internal hard disk immediately ;________________________________________________________________________________________ HardDiskPowered MOVEQ #0,D0 ; zero-extend the result TST.L LastHd(A2) ; is the hard disk powered up? SNE D0 ; $FF if so, $00 if not NEG.B D0 ; 1 if so, 0 if not RTS ;________________________________________________________________________________________ ; ; Routine: SpinDownHardDisk (PowerMgrDispatch selector #7) ; ; Inputs: A2 - pointer to Power Manager globals ; ; Outputs: D0 - result code (always zero) ; ; Trashes: A0 ; ; Function: spins down the internal hard disk immediately ;________________________________________________________________________________________ SpinDownHardDisk _SpinDownHardDrive MOVEQ #0,D0 @Done RTS ;________________________________________________________________________________________ ; ; Routine: IsSpindownDisabled (PowerMgrDispatch selector #8) ; ; Inputs: A2 - pointer to Power Manager globals ; ; Outputs: D0 - boolean: 1=disabled, 0=enabled ; ; Trashes: none ; ; Function: returns a boolean telling whether hard disk spindown is enabled or disabled ;________________________________________________________________________________________ IsSpindownDisabled MOVEQ #0,D0 IF HDSpinDownDisable=7 THEN TST.B PmgrFlags2(A2) SMI D0 ELSE BTST #HDSpinDownDisable,PmgrFlags2(A2) SNE D0 ENDIF NEG.B D0 RTS ;________________________________________________________________________________________ ; ; Routine: SetSpindownDisable (PowerMgrDispatch selector #9) ; ; Inputs: A2 - pointer to Power Manager globals ; D0 - high word: 1=disable, 0=enable ; ; Outputs: D0 - result code (always zero) ; ; Trashes: none ; ; Function: enables or disables hard disk spindown ;________________________________________________________________________________________ SetSpindownDisable SWAP D0 TST.B D0 ; is it to be disabled? BNE.S @disable ; -> yes BCLR #HDSpinDownDisable,PmgrFlags2(A2) BRA.S @Done ; no, enable it @disable BSET #HDSpinDownDisable,PmgrFlags2(A2) @Done MOVEQ #0,D0 RTS ;________________________________________________________________________________________ ; ; Routine: HardDiskQInstall (PowerMgrDispatch selector #10) ; ; Inputs: A0 - pointer to queue element ; A2 - pointer to Power Manager globals ; ; Outputs: D0 - result code (always zero) ; ; Trashes: A0, A1, D0 ; ; Function: adds an element to the hard disk spindown notification queue ;________________________________________________________________________________________ HardDiskQInstall MOVEQ #slpTypeErr,D0 ; assume bad queue type CMP.W #HDPwrQType,hdQType(A0) ; is it? BNE.S @Done ; -> yes, bail LEA HardDiskQHdr(A2),A1 ; point to the hard disk queue _Enqueue ; and add the element to the queue MOVEQ #0,D0 ; @Done RTS ;________________________________________________________________________________________ ; ; Routine: HardDiskQRemove (PowerMgrDispatch selector #11) ; ; Inputs: A0 - pointer to queue element ; A2 - pointer to Power Manager globals ; ; Outputs: D0 - result code ; ; Trashes: A0, A1, D0 ; ; Function: removes an element from the hard disk spindown notification queue ;________________________________________________________________________________________ HardDiskQRemove MOVEQ #slpTypeErr,D0 ; assume bad queue type CMP.W #HDPwrQType,hdQType(A0) ; is it? BNE.S @Done ; -> yes, bail LEA HardDiskQHdr(A2),A1 ; point to the hard disk queue _Dequeue ; and remove the element from the queue MOVEQ #0,D0 ; @Done RTS ;________________________________________________________________________________________ ; ; Routine: AutoSleepControl (PowerMgrDispatch selector #13) ; ; Inputs: A2 - pointer to Power Manager globals ; D0 - high word: 0=disable sleep, 1=enable sleep ; ; Outputs: D0 - result code (always zero) ; ; Trashes: A0 ; ; Function: Disables or enables auto sleep by incrementing or decrementing the auto sleep ; semaphone. Note that since the semaphore supports multiple ‘levels’, if it's ; been disabled n times in a row, it needs to be enabled n times before auto ; sleep will really be enabled. ;________________________________________________________________________________________ AutoSleepControl SWAP D0 ; get the flag TST.W D0 ; enable or disable? BEQ.S @disable ; -> disable SUBQ.B #1,AutoSlpDisable(A2) ; ‘pop’ a level towards sleep enabled BGE.S @done ; -> haven't rolled over CLR.B AutoSlpDisable(A2) ; pin the semaphore a zero BRA.S @done ; @disable ADDQ.B #1,AutoSlpDisable(A2) ; set the semaphore to >0 (‘push’ a level) BHI.S @done ; -> we're done if it's still >0 SUBQ.B #1,AutoSlpDisable(A2) ; it rolled over, so make it >0 again @done MOVEQ #0,D0 ; RTS ; ;________________________________________________________________________________________ ; ; Routine: GetIntModemInfo (PowerMgrDispatch selector #14) ; ; Inputs: A2 - pointer to Power Manager globals ; ; Outputs: D0 - bitmap of internal modem info: ; 0: 1=modem installed ; 1: 1=modem ring detected ; 2: 1=modem off hook ; 3: 1=wakeup on ring is enabled ; 4: 1=external modem selected ; 15-3: 0=reserved ; 31-16: modem type ; -1 = modem installed but type unknown ; 0 = no modem installed ; 1 = original serial modem ; 2 = Rockwell data pump modem (RC144DPL) ; 3 = Rockwell data pump modem (RC144DPL) + 6805 controller ; ; Trashes: A0 ; ; Function: returns a bitmap of information about the internal modem, if any ;________________________________________________________________________________________ GetIntModemInfo CLR.W -(SP) ; allocate a buffer for the result MOVE.L SP,-(SP) ; pmRBuffer MOVE.L (SP),-(SP) ; pmSBuffer CLR.W -(SP) ; pmLength MOVE.W #modemRead,-(SP) ; pmCommand MOVEA.L SP,A0 ; point to the parameter block _PMgrOp ; get the modem info LEA pmRBuffer+4(SP),SP ; toss the parameter block MOVEQ #PmgrStatusFlags,D0 ; read the byte containing modem status BSR ReadPMgrPRAM ; MOVEQ #1< AND.B D0,D1 ; LSL.B #extModemSelected-UseIntrnlModem,D1 ; and shift it into position MOVEQ #(1< AND.B (SP)+,D0 ; mask off the bits we want, ROR.B #ModemInstalled-hasInternalModem,D0 ; ; and shift them into position BCLR #8-(ModemInstalled-hasInternalModem)+RingWakeEnable,D0 ; BEQ.S @NoWake ; -> the ‘wakeup on ring’ bit isn't set ADDQ.B #1< @NoWake OR.B D1,D0 ; combine all the bits so far BTST #hasInternalModem,D0 ; is an internal modem installed? BEQ.S @Done ; -> no, we're done MOVE.L D0,-(SP) ; save the rest of the information BSR ModemTypeProc ; find out what kind of modem is installed SWAP D0 ; and move it into bits 16-31 CLR.W D0 ; (make sure we don't have any stray bits) OR.L (SP)+,D0 ; then OR in the rest of the info @Done RTS ;________________________________________________________________________________________ ; ; Routine: SetIntModemState (PowerMgrDispatch selector #15) ; ; Inputs: A2 - pointer to Power Manager globals ; D0 - high word: bitmap of bits to set or clear ; 18: 1=wakeup on ring is enabled if bit 31=1, or disabled if bit 31=0 ; 19: 1=external modem selected if bit 31=1, or external if bit 31=0 ; 31: 1=set all other 1-bits, 0=clear all other 1-bits ; ; ; Outputs: D0 - result code (always zero) ; ; Trashes: A0 ; ; Function: Configures some of the state information for the internal modem. ;________________________________________________________________________________________ SetIntModemState SWAP D0 ; get the flags BTST #intModemRingWakeEnb,D0 ; do we need to update ‘wakeup on ring’? BEQ.S @NoWake ; -> nope LoadTbl PrimInfoTblPtr,A2,A0 MOVE.L PrimPubFeatures(A0),D1 ; does this machine support ‘wakeup on ring’? BTST #canWakeupOnRing,D1 BEQ.S @NoWake ; -> no, skip it MOVE.W D0,-(SP) ; save the flags SMI D0 ; $FF=enabled, $00=disabled MOVEQ #1< nope, all done TST.W D0 ; test for zero/non-zero SMI -(SP) ; push $00=internal/$FF=external on stack MOVEQ #PmgrStatusFlags,D0 ; read the byte containing modem status BSR ReadPMgrPRAM MOVEQ #~(1< LoadTbl PrimInfoTblPtr,A2,A0 MOVEQ #1< AND.L PrimPubFeatures(A0),D1 ; BEQ.S @Done ; -> no, we're done MOVEQ #PmgrOtherFlags,D0 ; read the byte containing the CPU speed bit BSR.S ReadPMgrPRAM IF EconoBit≠0 THEN LSR.B #EconoBit,D0 ; shift the bit into bit zero ENDIF MOVEQ #1,D1 EOR.B D1,D0 ; invert the bit so true=full speed AND.B D1,D0 ; and mask off the bit @Done RTS ;________________________________________________________________________________________ ; ; Routine: SetProcessorSpeed (PowerMgrDispatch selector #19) ; ; Inputs: A2 - pointer to Power Manager globals ; D0 - high word: 1=full speed, 0=half speed ; ; Outputs: D0 - boolean: true=speed was changed, false=only PRAM was changed ; ; Trashes: D1, D2, A0, A1, A2 ; ; Function: Updates PRAM to reflect the desired CPU speed on the next restart. If the ; machine supports dynamic speed switching, the hardware will be whacked to ; change the speed, the low-mem timing constants (TimeDBRA, etc.) will be ; updated, and the AppleTalk Transition Queue will be run to notify anyone ; who cares that they need to recalculate their timing parameters. ;________________________________________________________________________________________ ATQEntry RECORD 0,INCREMENT qLink DS.L 1 ; pointer to next queue entry qType DS.W 1 ; queue type CallAddr DS.L 1 ; pointer to routine to be called ENDR ATTransSpeedChange EQU 'sped' ; event = speed change SetProcessorSpeed SWAP D0 ; get the boolean, MOVEQ #1,D1 EOR.B D1,D0 ; invert it so true=reduced speed, AND.B D0,D1 ; mask it off to a single bit, IF EconoBit=1 THEN ADD.B D1,D1 ; and then shift it into the correct bit position ELSEIF EconoBit>1 LSL.B #EconoBit,D1 ; and then shift it into the correct bit position ENDIF MOVEQ #1,D0 ; assume full speed LoadTbl PrimInfoTblPtr,A2,A0 MOVEQ #1< AND.L PrimPubFeatures(A0),D2 ; BEQ @Done ; -> no, we're done ; update the setting in PRAM MOVEQ #PmgrOtherFlags,D0 ; read the byte containing the CPU speed bit BSR.S ReadPMgrPRAM MOVE.B D0,D2 ANDI.B #~(1< no, just exit MOVEQ #PmgrOtherFlags,D0 ; yes, write the PRAM byte back out BSR.S WritePMgrPRAM MOVEQ #1< AND.L PrimPubFeatures(A0),D0 ; BEQ.S @Done ; -> no, we're done MOVE.W D1,-(SP) ; (save D1 across the call) JsrRoutine SpeedChangePtr,A2,A0 ; and go do the switch MOVE.W (SP)+,D1 ; (restore D1) TST.W D0 ; did we do the speed switch? BEQ.S @Done ; -> no, we're done MOVEQ #CPUSpeedDisp,D0 ; get the current CPU speed _PowerDispatch MOVE.B D0,saveSpeedo(A0) ; and update speedo for people using IdleRead ; setup new timing constants for the TimeDBRA low-mems LSL.W #3-EconoBit,D1 ; convert D1 to an index (0 or 8) LEA fullSpeedDBRAs(A2),A1 ; point to the correct table ADDA.W D1,A1 MOVE.W (A1)+,TimeDBRA ; copy the timing constants to low mem MOVE.W (A1)+,TimeSCCDB MOVE.W (A1)+,TimeSCSIDB MOVE.W (A1)+,TimeVIADB ; run the AppleTalk transition queue MOVE.L #gestaltAppleTalkVersion,D0 ; get the version of AppleTalk _Gestalt BNE.S @NoATalk ; -> error means it's not installed MOVE.L A0,D0 ; get the AppleTalk version BEQ.S @NoATalk ; -> zero means AppleTalk is turned off MOVEQ #25,D0 ; get a pointer to the transition queue MOVEA.L AtalkHk2,A0 JSR LapMgrCall(A0) LEA qHead(A1),A2 ; point to the head of the queue BRA.S @StartQueue @NextQueue MOVE.L D0,A2 ; point to the queue element MOVEM.L D3-D7/A2-A6,-(SP) ; save registers cuz apparently some procs do trash them CLR.L -(SP) ; no routine-specific parameters MOVE.L A2,-(SP) ; *ATQEntry PEA ATTransSpeedChange ; selector = speed change MOVEA.L ATQEntry.CallAddr(A2),A0 ; call the transition queue element's routine JSR (A0) LEA 4+4+4(SP),SP ; toss the parameters MOVEM.L (SP)+,D3-D7/A2-A6 @StartQueue MOVE.L qLink(A2),D0 ; point to the next queue element; end of the queue? BNE.S @NextQueue ; -> no, keep looping @NoATalk MOVEQ #1,D0 @Done RTS ;________________________________________________________________________________________ ; ; Routine: GetSCSIDiskModeAddress (PowerMgrDispatch selector #20) ; ; Inputs: A2 - pointer to Power Manager globals ; ; Outputs: D0 - SCSI bus addresse internal hard disk (1-6) ; ; Trashes: D1, A0 ; ; Function: returns the SCSI Disk Mode address of the internal hard disk ;________________________________________________________________________________________ GetSCSIDiskModeAddress LoadTbl PrimInfoTblPtr,A2,A0 MOVEQ #1< ;________________________________________________________________________________________ ; ; Routine: SetSCSIDiskModeAddress (PowerMgrDispatch selector #21) ; ; Inputs: A2 - pointer to Power Manager globals ; D0 - high word: SCSI bus address of the internal hard disk (1-6) ; ; Outputs: D0 - result code (always zero) ; ; Trashes: D0, D1, A0 ; ; Function: updates PRAM with the SCSI Disk Mode address for the internal hard disk ;________________________________________________________________________________________ SetSCSIDiskModeAddress LoadTbl PrimInfoTblPtr,A2,A0 MOVEQ #1< yes MOVEQ #7,D0 ; no, pin it @MapID MOVE.B mapSCSIAddr(D0),D0 ; make sure the address is valid, IF DiskModeAddr≠0 THEN LSL.B #DiskModeAddr,D0 ; and then shift it into the correct bit position ENDIF MOVE.B D0,D1 ; save it for the write MOVEQ #PmgrOtherFlags,D0 ; read the byte containing the CPU speed bit BSR ReadPMgrPRAM ANDI.W #~(%111< no, we're done MOVE.L A0,-(SP) ; pmRBuffer CLR.L -(SP) ; pmSBuffer CLR.W -(SP) ; pmLength MOVE.W #timerRead,-(SP) ; pmCommand MOVEA.L SP,A0 ; point to the parameter block _PMgrOp ; read the wakeup time LEA pmRBuffer+4(SP),SP ; toss the parameter block RTS @NoTimer CLR.L (A0)+ ; zero out all the fields CLR.B (A0)+ ; RTS ; ;________________________________________________________________________________________ ; ; Routine: SetWakeupTimer (PowerMgrDispatch selector #23) ; ; Inputs: A0 - pointer to buffer that contains the wakeup time and enable/disable flag byte ; A2 - pointer to Power Manager globals ; ; Outputs: D0 - none ; ; Trashes: D0,A0,A1 ; ; Function: sets the time when the PowerBook will wake up ;________________________________________________________________________________________ SetWakeupTimer LoadTbl PrimInfoTblPtr,A2,A1 MOVEQ #1< no, we're done MOVEA.L A0,A1 ; save a copy of the buffer pointer CLR.L -(SP) ; pmRBuffer MOVE.L A0,-(SP) ; pmSBuffer MOVE.W #4,-(SP) ; pmLength MOVE.W #timerSet,-(SP) ; pmCommand MOVEA.L SP,A0 ; point to the parameter block _PMgrOp ; set the timer TST.B 4(A1) ; is the timer to be disabled? BNE.S @Done ; -> nope, all done MOVE.W #$82,pmCommand(A0) ; send a ‘disable wakeup timer’ command CLR.W pmLength(A0) ; _PMgrOp ; @Done LEA pmRBuffer+4(SP),SP ; clean up the stack @NoTimer RTS ;________________________________________________________________________________________ ; ; Routine: GetProcessorCycling (PowerMgrDispatch selector #24) ; ; Inputs: A2 - pointer to Power Manager globals ; ; Outputs: D0 - boolean: 1=cycling is enabled, 0=cycling is disabled ; ; Trashes: A0 ; ; Function: returns whether or not processor cycling is enabled ;________________________________________________________________________________________ GetProcessorCycling MOVEQ #PmgrStatusFlags,D0 ; read the byte containing modem status BSR ReadPMgrPRAM ; BTST #IdleBit,D0 ; 1=disabled, 0=enabled SEQ D0 NEG.B D0 ; 1=enabled, 0=disabled RTS ;________________________________________________________________________________________ ; ; Routine: SetProcessorCycling (PowerMgrDispatch selector #25) ; ; Inputs: A2 - pointer to Power Manager globals ; D0 - boolean: 1=enable cycling, 0=disable cycling ; ; Outputs: none ; ; Trashes: A0 ; ; Function: enables/disables processor cycling ;________________________________________________________________________________________ SetProcessorCycling SWAP D0 ; get the boolean TST.W D0 ; enable or disable? BNE.S @enable BSET #IdleBit,SleepFlags(A2) ; disable idle BRA.S @common @enable BCLR #IdleBit,SleepFlags(A2) ; enable idle @common MOVEQ #PmgrStatusFlags,D0 ; write the byte containing idle status MOVE.B SleepFlags(A2),D1 BRA WritePMgrPRAM ;________________________________________________________________________________________ ; ; Routine: BatteryCount (PowerMgrDispatch selector #26) ; ; Inputs: A2 - pointer to Power Manager globals ; ; Outputs: D0 - number of internal batteries we can have ; ; Trashes: A0 ; ; Function: returns the number of internal batteries we can have ;________________________________________________________________________________________ BatteryCount MOVEA.L PMgrBase,A2 ; get pointer to Power manager globals LoadTbl PrimInfoTblPtr,A2,A0 MOVEQ #0,D0 ; get the number of internal batteries we can have MOVE.W PrimBatteryCount(A0),D0 RTS ;________________________________________________________________________________________ ; ; Routine: GetBatteryVoltage (PowerMgrDispatch selector #27) ; ; Inputs: A2 - pointer to Power Manager globals ; D0 - high word: battery number (0-n) ; ; Outputs: D0 - battery voltage ; ; Trashes: A0-A1, D1-D2 ; ; Function: returns the battery voltage as a fixed-point number so that nobody external ; to the Power Manager needs to do the calculation anymore. ;________________________________________________________________________________________ GetBatteryVoltage MOVEA.L PMgrBase,A2 ; get pointer to Power manager globals LoadTbl PrimInfoTblPtr,A2,A0 SWAP D0 ; get the battery number CMP.W PrimBatteryCount(A0),D0 ; do we support this many batteries? BHS.S @NoBattery ; -> no, return zero JmpRoutine AbsoluteBattPtr,A2,A0; run the routine @NoBattery MOVEQ #0,D0 ; return zero volts RTS ; ;________________________________________________________________________________________ ; ; Routine: GetBatteryTimes (PowerMgrDispatch selector #28) ; ; Inputs: A2 - pointer to Power Manager globals ; A0 - 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. ;________________________________________________________________________________________ GetBatteryTimes MOVEA.L PMgrBase,A2 ; get pointer to Power manager globals LoadTbl PrimInfoTblPtr,A2,A1 SWAP D0 ; get the battery number CMP.W PrimBatteryCount(A1),D0 ; do we support this many batteries? BHS.S @NoBattery ; -> no, return zero JmpRoutine BatteryTimePtr,A2,A1 ; run the routine (D0.W contains battery number or system) @NoBattery CLR.L (A0)+ ; zero out the fields CLR.L (A0)+ CLR.L (A0)+ CLR.L (A0)+ MOVEQ #0,D0 RTS ;________________________________________________________________________________________ ; ; Routine: LCDScreenChk ; ; Inputs: none ; ; Outputs: CCR - BNE if LCD screen ; ; Trashes: D0, A0, A1 ; ; Function: For Color QuickDraw machines, check if a flag in the video attributes sRsrc ; says we have an LCD screen for built-in video. If the sRsrc doesn't exist, ; or the flag isn't set, we assume a CRT. ;________________________________________________________________________________________ LCDScreenChk WITH SpBlock Cmp.w #$3FFF,ROM85 ; If Color QuickDraw is not around, Bne.s @NoCQD ; then just leave. Tst.l DeviceList ; If the DeviceList is empty, Beq.s @NoDevices ; then just leave. Move.l MainDevice,D0 ; If there isn’t a MainDevice, Beq.s @NoDevices ; then just leave. Move.l D0,A0 ; Get the Handle to the MainDevice. Move.l (A0),A0 ; Make it a pointer. Moveq #0,D0 ; Prepare D0 for unsigned .w references. Move.w gdRefNum(A0),D0 ; If there’s no driver, then Beq.s @NoDevices ; we can’t do anything here. Not.w D0 ; Convert the refNum into… Lsl.w #2,D0 ; …a UTable index. Add.l UTableBase,D0 ; Get a ptr to the AuxDCEHandle. Move.l D0,A1 ; Get it into A1. Move.l (A1),A1 ; Get the AuxDCEHandle. Move.l (A1),A1 ; Get the AuxDCEPtr. Lea -spBlockSize(sp),Sp ; Put a slot parameter block on the stack. Move.l Sp,A0 ; Point to it with A0. Move.b dCtlSlot(A1),spSlot(A0) ; Get the slot number. Move.b dCtlSlotID(A1),spID(A0) ; Get the spID of the video sRsrc. Clr.b spExtDev(A0) ; Don’t ask why, just clear this guy. _sRsrcInfo ; Get the spsPointer. Bne.s @AssumeCRT ; If failed, just quit. Move.b #sVidAttributes,spID(A0); Say to get the video sRsrc attributes. _sReadWord ; Get ’em. Bne.s @AssumeCRT ; If failed, just quit. Moveq #1< ; | ; Routine: GetDimTimeout (PowerMgrDispatch selector #29) v ; ; Inputs: A2 - pointer to Power Manager globals ; ; Outputs: D0 - sleep timeout (number of 15 second intervals) ; ; Trashes: A0 ; ; Function: returns the current sleep timeout time ;________________________________________________________________________________________ GetDimTimeout MOVEQ #0,D0 ; assume the result is zero MOVE.L DimmingWaitTime(A2),D1 ; get value from globals BEQ.S @Done DIVU.L #pram2ticks,D1 ; convert from ticks value to 15 sec intervals MOVE.B D1,D0 ; return result @Done RTS ;________________________________________________________________________________________ ; ; Routine: SetDimTimeout (PowerMgrDispatch selector #30) ; ; Inputs: A2 - pointer to Power Manager globals ; D0 - high word: sleep time to set (number of 15 second intervals) ; ; Outputs: D0 - result code (always zero) ; ; Trashes: D1, A0 ; ; Function: sets the current sleep timeout ;________________________________________________________________________________________ SetDimTimeout SWAP D0 ; get the new value MOVEQ #0,D1 ; clear reg MOVE.B D0,D1 ; get the new value MULU.W #pram2ticks,D1 ; set ticks value MOVE.L D1,DimmingWaitTime(A2) ; save value in globals _UsrIdleUpdate MOVEQ #0,D0 ; return noErr RTS ;________________________________________________________________________________________ ; ; Routine: DimControl (PowerMgrDispatch selector #31) ; ; Inputs: A2 - pointer to Power Manager globals ; D0 - high word: 0=disable sleep, 1=enable sleep ; ; Outputs: D0 - result code (always zero) ; ; Trashes: A0 ; ; Function: Disables or enables auto sleep by incrementing or decrementing the auto sleep ; semaphone. Note that since the semaphore supports multiple ‘levels’, if it's ; been disabled n times in a row, it needs to be enabled n times before auto ; sleep will really be enabled. ;________________________________________________________________________________________ DimControl SWAP D0 ; get the flag TST.W D0 ; enable or disable? BEQ.S @disable ; -> disable SUBQ.B #1,DimmingDisable(A2) ; ‘pop’ a level towards sleep enabled BGE.S @done ; -> haven't rolled over CLR.B DimmingDisable(A2) ; pin the semaphore a zero BRA.S @done ; @disable ADDQ.B #1,DimmingDisable(A2) ; set the semaphore to >0 (‘push’ a level) BHI.S @done ; -> we're done if it's still >0 SUBQ.B #1,DimmingDisable(A2) ; it rolled over, so make it >0 again @done MOVEQ #0,D0 ; RTS ; ;________________________________________________________________________________________ ; ; Routine: IsDimmingDisabled (PowerMgrDispatch selector #32) ; ; Inputs: A2 - pointer to Power Manager globals ; ; Outputs: D0 - boolean: 1=disabled, 0=enabled ; ; Trashes: none ; ; Function: returns a boolean telling whether dimming is enabled or disabled ;________________________________________________________________________________________ IsDimmingDisabled MOVEQ #0,D0 ; assume enabled TST.B DimmingDisable(A2) ; IF counting semaphor not nil THEN BEQ.S @Done ; MOVEQ #1,D0 ; tell the world it's disabled @Done RTS ; that's all folks ;________________________________________________________________________________________ ; ; Routine: IsAutoSlpDisabled (PowerMgrDispatch selector #33) ; ; Inputs: A2 - pointer to Power Manager globals ; ; Outputs: D0 - boolean: 1=disabled, 0=enabled ; ; Trashes: none ; ; Function: returns a boolean telling whether autosleep is enabled or disabled ;________________________________________________________________________________________ IsAutoSlpDisabled MOVEQ #0,D0 ; assume enabled TST.B AutoSlpDisable(A2) ; IF counting semaphor not nil THEN BEQ.S @Done ; MOVEQ #1,D0 ; tell the world it's disabled @Done RTS ; that's all folks RTS ENDIF ; {hasPwrControls} END