mirror of
https://github.com/elliotnunn/supermario.git
synced 2024-11-29 05:49:19 +00:00
932 lines
41 KiB
Plaintext
932 lines
41 KiB
Plaintext
;
|
|
; File: PwrControlsPatches.a
|
|
;
|
|
; Contains: Patches for new features dealing with PowerControls.
|
|
;
|
|
; Written by: Helder J. Ramalho
|
|
;
|
|
; Copyright: © 1990-1993 by Apple Computer, Inc. All rights reserved.
|
|
;
|
|
; This file is used in these builds: ROM
|
|
;
|
|
; Change History (most recent first):
|
|
;
|
|
; <SM2> 12/13/93 PN Roll in KAOs and Horror changes to support Malcom and AJ
|
|
; machines.
|
|
; <SM1> 9/1/93 SKH Rolled into SuperMario for the first time. After Kaos stops changing
|
|
; this will be rolled into PowerMgr.a
|
|
;
|
|
; <H43> 6/24/93 RLE ensure charger turned on as part of pmgrinit
|
|
; <H42> 5/31/93 RLE change setMachineID command to setMachineAttr
|
|
; <H41> 05-12-93 jmp For reanimator builds, quit whacking the SCC.
|
|
; <H40> 5/7/93 RLE disable dynamic speed switching for Escher's ROM
|
|
; <H39> 5/4/93 SWC Calculate full/reduced speed timing constants (TimeDBRA, etc.)
|
|
; for machines that support dynamic speed changes.
|
|
; <H38> 4/30/93 RLE update MSC initialization to configure 33MHz Escher correctly
|
|
; <H37> 4/20/93 SWC Fixed a bug that was causing us to hang. It looks like this one
|
|
; has been around for awhile, too.
|
|
; <H36> 4/19/93 SWC Modified CheckEconoMode to look for the pointer to the Power
|
|
; Manager primitives directly in a box's ProductInfo, instead of
|
|
; using the PmgrPrimLookUp table (which is now gone).
|
|
; <H35> 3/29/93 RLE include new patch for InitPmgrVars since it's grown too big to
|
|
; fit in the original file
|
|
; <H34> 3/19/93 RLE fix a narly problem with PortableCheck using uninitialized ram
|
|
; <H33> 3/3/93 RLE toss <H32> in preparation for doing the LCD screen save/restore
|
|
; in the driver instead of in the power manager
|
|
; <H32> 2/25/93 RLE modify PmgrPrimLookUp calls to support multiple table entries
|
|
; for a given memory decoder
|
|
; <H31> 8/21/92 SWC Added a nasty hack to fix an SCC initialization problem on
|
|
; Gemini and DeskBar. This will eventually be fixed in their
|
|
; hardware, but we need this to be working correctly now.
|
|
; <H30> 8/10/92 SWC Disable PG&E modem interrupts so we don't get hit before the
|
|
; Power Manager gets set up.
|
|
; <H29> 7/27/92 SWC Blank the GSC-based LCD screen on DBLite as well as Niagra.
|
|
; <H28> 7/21/92 ag Change the GSC initialization values, the screen should not
|
|
; blank with active pixels, this will highlight bad pixels. The
|
|
; new values will blank with inactive pixels avoiding this
|
|
; problem.
|
|
; <H27> 7/15/92 ag Changed the initial setting for GSC to blank the screen.
|
|
; <H26> 7/15/92 ag Moved a7 to a5 for storage before testing for bus error. The
|
|
; bus error handler will move a5 to a7. Set the condition codes
|
|
; in bus error check. Set the condition codes before returning in
|
|
; the spi timeout code.
|
|
; <H25> 7/14/92 ag Fixed trashed register problem in gsc blank routine.
|
|
; <H24> 7/14/92 ag Added timeouts to the spi code to avoid infinite loops.
|
|
; <H23> 7/14/92 ag Added initialization and blanking of the display for niagra. the
|
|
; timing spec would be violated if initialization is done later.
|
|
; Changed the spin wait loop constant for economode to reduce
|
|
; possible extended delay.
|
|
; <H22> 7/10/92 ag fixed dart exception processing for $5x commands, $50 should
|
|
; still go to the power manager.
|
|
; <H21> 6/18/92 SWC Modified the Dart SPI routines to use the vectors for the
|
|
; command send/receive count tables to make patching easier.
|
|
; <H20> 6/12/92 ag Changing the spi receive protocal. on receive, data should be
|
|
; read on the RISING edge of ack. this protocal sucks!
|
|
; <H19> 6/10/92 HJR Fixed some bugs to allow Econo-Mode to work.
|
|
; <H18> 5/19/92 SWC Moved InitWallyWorld to CheckEconoMode from USTStartup.a since
|
|
; we read PRAM and thus need the PMGR initialized.
|
|
; <H17> 5/12/92 ag Set the condition codes before leaving the exception handling
|
|
; routine. This tells the code down stream that the exception
|
|
; handler handled the call.
|
|
; <H16> 5/7/92 ag Added Dartanian SPI code.
|
|
; <H15> 5/7/92 SWC Preserve the RAM size info when configuring the MSC. Also, leave
|
|
; the MSC25MHz bit set when running in econo-mode regardless of
|
|
; the maximum CPU speed so the state machines will be better
|
|
; optimized.
|
|
; <H14> 4/24/92 HJR Provided Econo-Mode support for Niagra.
|
|
; <H13> 4/3/92 SWC Moved all the ADB and DebugUtil routines to ADBPrimitives.a.
|
|
; Moved EnableSCSIIntsPatch to InterruptHandlers.a (where it
|
|
; should go).
|
|
; <H12> 3/19/92 HJR for GMR - Fixed bug in below change on TIM- moved fDBExpActive
|
|
; check earlier in StartRequestPMGR rtn.
|
|
; <H11> 3/19/92 HJR for GMR - Added fDBExpActive flag in StartReqPMGR,ReqDonePMGR to
|
|
; prevent the same explicit command from going out twice if
|
|
; autopoll data came in at the same time.
|
|
; <H10> 3/16/92 SWC Added support for 33MHz MSC systems to CheckEconoMode.
|
|
; <H9> 2/21/92 HJR Modified check sleep to use new ram locations instead of
|
|
; obsolete VRam space.
|
|
; <H8> 2/10/92 HJR Fix a bug where we are returning from a BSR6 without setting the
|
|
; return address in A6.
|
|
; <H7> 2/7/92 SWC Modified the CheckEconoMode code to lookup the correct base
|
|
; Power Manager PRAM address from the primitives tables.
|
|
; <H6> 1/24/92 SWC In CheckEconoMode, do both econo-mode setup and chip
|
|
; configuration for the MSC case.
|
|
; <H5> 1/9/92 SWC Rolled in changes for final chips: removed special case checks
|
|
; for PMGR interrupts on CA2 (now on CB1). Re-wrote CheckEconoMode
|
|
; as part of adding support for DB-Lite and to simplify it.
|
|
; <H4> 10/29/91 SWC Added the signature to the shutdown command in PMgrPowerOffPatch
|
|
; so the PMGR will actually turn us off (I don't know why it
|
|
; wasn't there before, cuz it's specifically required).
|
|
; <H3> 10/22/91 SWC PortableCheck: changed references to NoVRAMVidRam to point to
|
|
; the end of the record since the offsets are negative.
|
|
; <H2> 8/8/91 SWC Added import of USTPMGRSendCommand and modified the econo mode
|
|
; code to use it instead of sending the bytes discretely.
|
|
; Universalized the ADB code so that it supports interrupts on CA2
|
|
; and PMGR auto-polling for DB-Lite. Fixed PortableCheck so it
|
|
; handles the case when VRAM doesn't retain power across sleep
|
|
; (state saved in PMgrGlobals). Added EnableSCSIIntsPatch so we
|
|
; won't enable SCSI interrupts on DB-Lite.
|
|
; ———————————————————————————————————————————————————————————————————————————————————————
|
|
; Pre-HORROR ROM comments begin here.
|
|
; ———————————————————————————————————————————————————————————————————————————————————————
|
|
; <17> 7/9/91 HJR Added more overpatch space
|
|
; <16> 6/24/91 HJR Sleepq and pmCommand are now record, so the appropriate With
|
|
; statements were added.
|
|
; <15> 5/23/91 HJR Added include for PowerPrivEqu.a.
|
|
; <14> 5/10/91 HJR Removed NMIExcpPatch for a much cleaner implementation in
|
|
; PowerMgr.a. Removed BigBSR5 macros since it is now in private.a.
|
|
; <13> 4/29/91 HJR Added NMIExcpPatch in order to hit NMI while power-cycling and
|
|
; modified checkwakeup to use physical video location from
|
|
; universal instead of using hard-coded addresses.
|
|
; <12> 4/22/91 ag added check and code for economode.
|
|
; <11> 3/5/91 ag added install code for backlight driver.
|
|
; <10> 3/5/91 HJR Removed TaskDonePatch since IdleMind is now called from
|
|
; SynIdlePatch.
|
|
; <9> 2/26/91 HJR Added PmgrPowerOffPatch: an overpatch of the PowerOff trap in
|
|
; InterruptHandlers.a.
|
|
; <8> 2/22/91 HJR Fix bug in CheckWakeUp to prevent BusErr on non JAWSDecoder
|
|
; machines.
|
|
; <7> 1/24/91 HJR Moved IdleMind and powercycling code to PowerMgr.a.
|
|
; <6> 1/15/91 HJR Changed IdleMind so that it uses SleepTime off of PwrMgrVar
|
|
; instead of hardcoded constant.
|
|
; <5> 12/18/90 HJR Added DeskMgr.a overpatch, i.e. TaskDonePatch and IdleMind.
|
|
; <4> 12/11/90 HJR Fix bug.
|
|
; <3> 12/11/90 HJR Added Overpatching from StartInit.a, InterruptHandles.a and
|
|
; KbdADB.a.
|
|
; <2> 11/15/90 HJR Fixing comments.
|
|
; <1> 11/15/90 HJR first checked in
|
|
;
|
|
|
|
PRINT OFF
|
|
LOAD 'StandardEqu.d'
|
|
INCLUDE 'HardwarePrivateEqu.a'
|
|
INCLUDE 'UniversalEqu.a'
|
|
INCLUDE 'PowerPrivEqu.a'
|
|
INCLUDE 'PowerMgrDispatchEqu.a'
|
|
INCLUDE 'IopEqu.a'
|
|
INCLUDE 'EgretEqu.a'
|
|
INCLUDE 'AppleDeskBusPriv.a'
|
|
INCLUDE 'MMUEqu.a'
|
|
INCLUDE 'Appletalk.a'
|
|
INCLUDE 'LAPEqu.a'
|
|
PRINT ON
|
|
|
|
BLANKS ON
|
|
STRING ASIS
|
|
|
|
MACHINE MC68030
|
|
|
|
|
|
IMPORT GetHardwareInfo
|
|
IMPORT RdXByte
|
|
IMPORT USTPMGRSendCommand ; <H2>
|
|
IMPORT USTPMgrSendByte
|
|
IMPORT InitWallyWorld ; <H18>
|
|
IMPORT SetupTimeK ; <H39>
|
|
|
|
|
|
|
|
PwrCntrlPatch PROC
|
|
|
|
beok EQU 27 ;a BusError is expected and is OK <H23>
|
|
|
|
;••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
|
|
; PATCHES TO StartInit.a
|
|
;--------------------------------------------------------------------------------
|
|
|
|
IF hasPwrControls THEN
|
|
;————————————————————————————————————————————————————————————————————————————————
|
|
; Routine: PortableCheck
|
|
;
|
|
; Input: D2 - Bits 31..16, hwCfgFlags info (possibly unknown)
|
|
; D2 - Bits 7..0, Address Decoder Kind (zero if unknown)
|
|
; A1 - Productinfo
|
|
;
|
|
; Destroys: A0
|
|
;
|
|
; Called by: BSR6 from StartInit.
|
|
;
|
|
; Function: Since we've just called JumpIntoROM, D2 has the decoder kind.
|
|
; We check to see if the wakeup flag is set. If it is, take wakeup
|
|
; code path, otherwise, continue with the bootup process.
|
|
;————————————————————————————————————————————————————————————————————————————————
|
|
WITH DecoderInfo,DecoderKinds,ProductInfo, ProductInfo, VideoInfo, PmgrRec, pmCommandRec
|
|
PortableCheck
|
|
CheckWakeUp
|
|
IF isUniversal THEN
|
|
TestFor hwCbPwrMgr
|
|
BEQ.W NonPwrMgr ; NOPE. Branch...
|
|
ENDIF
|
|
|
|
MOVEA.L A6,A5 ; save return addr <H34>
|
|
LEA @NoRAM,A6 ; load return addr in case of bus error <H34>
|
|
|
|
MOVE.L PmgrBase,A2 ; get the addr of PMgrVars <H34>
|
|
CMP.L #SleepConst,SleepSaveFlag(A2) ; are we waking from sleep?
|
|
BNE.S @noRAM ; branch if not <H34>
|
|
|
|
CLR.L SleepSaveFlag(A2) ; clear the sleep flag
|
|
MOVE.L WakeVector(A2),A0 ; Go restore ourself
|
|
JMP (A0) ; .
|
|
|
|
NOP ; keep everything aligned <H34>
|
|
|
|
@NoRAM MOVEA.L A5,A6 ; restore return addr <H34>
|
|
|
|
IF 0 THEN
|
|
;————————————————————————————————————————————————————————————————————————————————
|
|
; 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
|
|
MOVEA.L A6,A7 ; save the return address <H18>
|
|
|
|
BigBSR6 InitWallyWorld,A1 ; download code into the PMGR, if necessary <H18>
|
|
|
|
MOVEQ #0,D2 ; <H18>
|
|
BigBSR6 GetHardwareInfo,A0 ; figure out what we're running on
|
|
|
|
MOVE.L D2,D4 ; save the decoder type around the PRAM calls <H18>
|
|
MOVEQ #$10,D1 ; read the PRAM validation byte <H18>
|
|
MOVEA.L DecoderInfo.VIA1Addr(A0),A2 ; point to base of VIA1 <H19>
|
|
BigBSR6 RdXByte,A0 ; <H18>
|
|
MOVEQ #$A8-256,D0 ; compare what was read against expected value <H18>
|
|
SUB.B D1,D0 ; <H18>
|
|
MOVEQ #0,D1 ; (assume PRAM's invalid) <H18>
|
|
TST.B D0 ; is PRAM OK? <H18>
|
|
BNE.S @BadPRAM ; -> no, run at full speed for now <H18>
|
|
|
|
MOVEQ #PmgrPramBase+PmgrPramRec.PmgrOtherFlags,D1 ; default to standard PRAM location <H7>
|
|
MOVE.L PowerManagerPtr(A1),D0 ; does this box have a PMgr primitives table? <H36><H37>
|
|
BEQ.S @UseDefPRAM ; -> no, use the default location <H36>
|
|
MOVEA.L A1,A2 ; <H36><H37>
|
|
ADDA.L D0,A2 ; point to the primitives table for this box <H36>
|
|
ADDA.L PmgrPrimsRec.PrimInfoPtr(A2),A2 ; and then to the primitives info table <H7>
|
|
MOVE.B PrimInfoTbleRec.PrimPRAMBase(A2),D1 ; get the base Power Manager PRAM byte <H7>
|
|
ADDQ.B #PmgrPramRec.PmgrOtherFlags,D1 ; and adjust for the byte we want <H7>
|
|
|
|
@UseDefPRAM MOVEA.L A1,A0 ; point back to the DecoderInfo table <H37>
|
|
ADDA.L DecoderInfoPtr(A0),A0 ; <H37>
|
|
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<<EconoBit),D1 ; and mask off the econo-mode bit
|
|
@BadPRAM MOVE.L D4,D2 ; restore the decoder type
|
|
|
|
MOVEA.L A1,A0 ; point back to the DecoderInfo table <H37>
|
|
ADDA.L DecoderInfoPtr(A0),A0 ; one last time <H37>
|
|
|
|
; at this point:
|
|
; A0 - pointer to DecoderInfo table
|
|
; A1 - pointer to ProductInfo table
|
|
; D2 - decoder type
|
|
|
|
|
|
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 ; econo-mode + other default configuration <H6>
|
|
BTST #EconoBit,D1 ; are we in going to run in econo-mode? <H15>
|
|
BNE.S @NotMSC33MHz ; -> yes, we'll be running at 16MHz regardless <H15>
|
|
MOVEQ #7,D2 ; mask off the CPU ID <H10>
|
|
AND.W CPUIDValue(A1),D2 ; <H10>
|
|
CMP.W #5,D2 ; is it a 33MHz system (DB Lite)? <H38>
|
|
BEQ.S @MSC33MHz ; -> yes, do setup <H38>
|
|
CMP.W #2,D2 ; maybe it's a 33MHz Escher? <H38>
|
|
BNE.S @NotMSC33MHz ; -> no, skip <H38>
|
|
@MSC33MHz BCLR #MSC25MHz,D1 ; setup the state machines to run at 33MHz <H10>
|
|
@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 <H15>
|
|
AND.B MSCConfig(A1),D2 ; <H15>
|
|
OR.B D2,D1 ; and add it to the base configuration <H15>
|
|
MOVE.B D1,MSCConfig(A1) ; stuff the configuration into the register <H6>
|
|
|
|
MOVE.L #(0<<16)|(1<<8)|(SetModemInts<<0),D3 ; <H30>
|
|
BigBSR6 USTPmgrSendCommand,A2 ; turn off modem interrupts <H30>
|
|
|
|
;◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊ <H31>
|
|
;
|
|
; This is the nasty hack. When a Duo is docked to a DuoDock™ or MiniDock™ 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
|
|
; DuoDock/MiniDock 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
|
|
|
|
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
|
|
|
|
;◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊ <H31>
|
|
|
|
NotMSC
|
|
ENDIF
|
|
|
|
ExitEconoMode
|
|
MOVEA.L A7,A6 ; restore the return address <H23>
|
|
|
|
;———————————————————————————————————————————————————————————————————————————————— <H23>
|
|
; 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
|
|
IF isUniversal THEN
|
|
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
|
|
ENDIF
|
|
|
|
IF hasMSC THEN ; <H29>
|
|
MOVEA.L DecoderInfo.RBVAddr(A0),A2 ; point to the base of the MSC <H29>
|
|
BSET #MSCLCDReset,MSCClkCntl(A2) ; turn on clocks to the GSC so we can program it<H29>
|
|
BRA.S @TestForGSC ; <H29>
|
|
ENDIF ; <H29>
|
|
|
|
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 ; <H29>
|
|
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 <H26>
|
|
bset.l #beok,d7 ; allow bus errors
|
|
bsr6 @checkforGSC ; check for gsc chip
|
|
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 <H25>
|
|
MOVE.L (A2)+,(A0)+ ; initialize the main display registers <H25>
|
|
MOVE.L (A2)+,(A0)+ ; <H25>
|
|
LEA GSCDiag0-GSCPanelSkew-1(A0),A0 ; point to the diagnostic registers <H25>
|
|
MOVE.B (A2)+,(A0)+ ; and initialize them too <H25>
|
|
MOVE.W (A2)+,(A0)+ ; <H25>
|
|
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 <H26>
|
|
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 <H27>
|
|
DC.B $12, $00, $64, $00, $80, $02, $00, $FF, $00, $00, $03 ; ID=1 TFT3Bit <H27>
|
|
DC.B $10, $00, $64, $00, $80, $02, $00, $FF, $00, $00, $03 ; ID=2 TFT4Bit <H27>
|
|
DC.B $10, $00, $64, $00, $80, $02, $00, $A0, $00, $00, $03 ; ID=3 NotAssignedTFT <H27>
|
|
DC.B $10, $00, $64, $00, $80, $05, $00, $A0, $00, $00, $03 ; ID=4 NotAssignedSTN <H27>
|
|
DC.B $10, $00, $64, $00, $80, $05, $00, $A0, $00, $00, $03 ; ID=5 TimSTN <H27>
|
|
DC.B $10, $00, $63, $00, $80, $05, $00, $9C, $00, $00, $03 ; ID=6 DBLiteSTN <H29>
|
|
DC.B $10, $00, $64, $00, $80, $05, $00, $A0, $00, $00, $03 ; ID=7 No Display <H27>
|
|
|
|
|
|
@ExitInitAndBlank
|
|
bclr.l #beok,d7 ; disallow bus errors
|
|
movea.l a7,a6 ; restore original return address
|
|
|
|
ENDIF ; {hasNiagra | hasMSC}
|
|
|
|
ENDIF ; if 0 then
|
|
;————————————————————————————————————————————————————————————————————————————————
|
|
; Routine: Exit
|
|
;———————————————————————————————————————————————————————————————————————————————— |
|
|
; V
|
|
NonPwrMgr RTS6 ; return to start init <H23> ; All done
|
|
|
|
ENDIF ; is hasPwrControls
|
|
|
|
|
|
;————————————————————————————————————————————————————————————————————————————————
|
|
; Routine: BklightInstall
|
|
; Input: None
|
|
;
|
|
; Destroys: None
|
|
;
|
|
; Called by: BSR.L from StartInit.
|
|
;
|
|
; Function: Installs and opens ROM backlight driver
|
|
;————————————————————————————————————————————————————————————————————————————————
|
|
STRING PASCAL
|
|
|
|
DriverID equ -16511 ; id of driver resource
|
|
DriverType equ 'DRVR' ; resource type of driver
|
|
StartEntry equ (48-1) ; this avoids appletalk area
|
|
|
|
BacklightRegs reg A0-A3/D0-D3
|
|
|
|
EXPORT BacklightPatch
|
|
BacklightPatch
|
|
bsr.s BklightInstall
|
|
move.l (sp)+,a0
|
|
SUBQ #4, SP ; save space for handle
|
|
MOVE.L #'SERD', -(SP) ; push type
|
|
jmp (a0)
|
|
|
|
EXPORT BklightInstall
|
|
BklightInstall ;
|
|
movem.l BacklightRegs,-(sp) ; save the world
|
|
IF isUniversal THEN ;
|
|
TestFor hwCbPwrMgr
|
|
beq.s @ExitInstallBacklight
|
|
ENDIF
|
|
|
|
@install
|
|
move.l #DriverType,d1 ; load resource type in d1
|
|
move.w #DriverID,d2 ; load resource id in d2
|
|
|
|
bsr.s GetDetachRes ; get and detach resource (a1)
|
|
beq.s @exit ; exit if no handle
|
|
|
|
@findEntry move.l UTableBase,a0 ; point to utable array
|
|
move.l #(StartEntry*4),d3 ; start at entry (48-1)
|
|
|
|
@testEntry addq.l #4,d3 ; increment to next entry
|
|
tst.l 0(a0,d3) ; test entry
|
|
bne.s @testEntry ; if != 0, next entry
|
|
|
|
@createDce move.l d3,d0 ; put offset into d0
|
|
lsr.l #2,d0 ; divide by 4 to get entry number
|
|
addq.l #1,d0 ; add 1 (refnum is -(entry number + 1)
|
|
neg.l d0 ; negate to get reference number
|
|
_DrvrInstall ; create dce
|
|
tst.l d0 ; test for error
|
|
bne.s @releaseDrvr ; ... exit if error
|
|
|
|
move.l (a0,d3),a3 ; get handle to dce in a3
|
|
move.l (a3),a3 ; get pointer to dce
|
|
move.l a1,dCtlDriver(a3) ; load driver
|
|
|
|
move.l (a1),a1 ; get pointer to driver
|
|
move.w drvrFlags(a1),dCtlFlags(a3) ; copy data to dce
|
|
move.w drvrDelay(a1),dCtlDelay(a3)
|
|
move.w drvrEMask(a1),dCtlEMask(a3)
|
|
move.w drvrMenu(a1),dCtlMenu(a3)
|
|
|
|
bset.b #dNeedLock,dCtlFlags+1(a3) ; set the handle bit
|
|
|
|
@openDrvr lea #'.Backlight',A1 ; load pointer to driver name
|
|
bsr.s OpenDRVR
|
|
@exit
|
|
movem.l (sp)+,BacklightRegs ; restore the world
|
|
rts
|
|
|
|
@releaseDrvr move.l a1,a0 ; move handle to a0
|
|
_disposHandle ; release the memory
|
|
|
|
@ExitInstallBacklight
|
|
movem.l (sp)+,BacklightRegs ; restore the world
|
|
rts
|
|
|
|
;------------------------------------------------------------------------------------------
|
|
;
|
|
; GetDetachRes - Gets and detaches a resource.
|
|
;
|
|
; input
|
|
; d1 Resource Type
|
|
; d2 Resource ID
|
|
;
|
|
; output
|
|
; a1 Handle to resource
|
|
;
|
|
; usage
|
|
; d a1 - Handle to resource
|
|
; d d0 - Resource Type
|
|
; d1 - Resource Type
|
|
; d2 - Resource ID
|
|
;
|
|
;------------------------------------------------------------------------------------------
|
|
|
|
GetDetachRes
|
|
SUBQ.L #4, SP ; For return address
|
|
MOVE.L D1, -(SP) ; Resource type
|
|
MOVE.W D2, -(SP) ; Resource ID
|
|
_GetResource
|
|
MOVE.L (SP), A1 ; Get resource handle to return
|
|
_DetachResource
|
|
MOVE.L A1,D0 ; Set result code
|
|
RTS
|
|
|
|
;------------------------------------------------------------------------------------------
|
|
;
|
|
; OpenDRVR - routine used to open a driver
|
|
;
|
|
; input
|
|
; a1 - pointer to driver name
|
|
;
|
|
; output
|
|
; none
|
|
;
|
|
; usage
|
|
; a0 - pointer to iopb
|
|
; a1 - pointer to driver name
|
|
;
|
|
;------------------------------------------------------------------------------------------
|
|
OpenRegs reg A0-A3/D1-D2
|
|
|
|
OpenDRVR MOVEM.L OpenRegs,-(SP)
|
|
SUB.W #ioQElSize,SP ; Allocate IO stack frame
|
|
|
|
MOVE.L SP,A0 ; set a0 to point to the pb
|
|
MOVE.L A1,ioVNPtr(A0) ; load pointer to name
|
|
MOVE.B #fsCurPerm,ioPermssn(A0) ; set permission (not used)
|
|
_Open
|
|
|
|
ADD.W #ioQElSize,SP ; Release stack frame
|
|
MOVEM.L (SP)+,OpenRegs
|
|
RTS ; Sucess returned in status
|
|
|
|
|
|
;••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
|
|
; PATCHES TO InterruptHandlers.a
|
|
;--------------------------------------------------------------------------------
|
|
|
|
;———————————————————————————————————————————————————————————————————————————————— <9> HJR
|
|
; Routine: PowerOffPatch
|
|
;
|
|
; Destroys: None
|
|
;
|
|
; Called by: PowerOff. from BSR
|
|
;
|
|
; Calls: _PmgrOp, _HideCursor, _Sleep
|
|
;————————————————————————————————————————————————————————————————————————————————
|
|
EXPORT PmgrPowerOffPatch
|
|
IMPORT StartBoot
|
|
WITH pmCommandRec
|
|
PmgrPowerOffPatch
|
|
IF hasPwrControls THEN
|
|
IF isUniversal THEN
|
|
TestFor hwCbPwrMgr
|
|
beq.s PmgrOffDone
|
|
ENDIF
|
|
|
|
CLR.L -(SP) ; pmRBuffer = nil
|
|
CLR.L -(SP) ; pmRBuffer = nil
|
|
CLR.W -(SP) ; pmLength = 0
|
|
MOVE.W #PmgrADBoff,-(SP) ; pmCommand = turn ADB autopoll off
|
|
MOVE.L SP,A0 ; point to the parameter block
|
|
_PmgrOp
|
|
LEA pmRBuffer+4(SP),SP ; clean up the stack
|
|
|
|
_HideCursor
|
|
CLR.L WarmStart ; be sure to do startup testing
|
|
|
|
; If we have the new PMGR then this command will work. If not then the call will
|
|
; fail and we will do it the old fashioned way.
|
|
|
|
PEA 'MATT' ; shutdown signature <H4>
|
|
MOVE.L SP,-(SP) ; pmRBuffer (not used) <H4>
|
|
MOVE.L (SP),-(SP) ; pmSBuffer (the sleep signature) <H4>
|
|
MOVE.W #4,-(SP) ; pmLength = 4 <H4>
|
|
MOVE.W #PmgrPWRoff,-(SP) ; pmCommand = power off <H4>
|
|
MOVE.L SP,A0 ; point to the parameter block <H4>
|
|
_PmgrOp ; power off using the PMGR <H4>
|
|
LEA pmRBuffer+4+4(SP),SP; clean up the stack <H4>
|
|
BNE.S @callSleep ; -> the call failed, so we have an old PMGR <H4>
|
|
|
|
BRA.S * ; Let the cyanide take affect
|
|
|
|
@callsleep MOVE.W #SleepNow,D0 ; Set it to sleep
|
|
_Sleep
|
|
BigJSR StartBoot,A0 ; Reboot the guy.
|
|
|
|
PmgrOffDone
|
|
ENDIF ;
|
|
RTS ; Get Out of Here <9> HJR
|
|
ENDWITH
|
|
|
|
|
|
;••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
|
|
; PATCHES TO PmgrOp
|
|
;--------------------------------------------------------------------------------
|
|
|
|
;————————————————————————————————————————————————————————————————————————————————
|
|
; Routine: DartModemCmds
|
|
;
|
|
; Input: A0 - points the pmgrop PB
|
|
;
|
|
; Destroys: d1
|
|
;
|
|
; Called by: Bra from NewPmgrOp
|
|
;
|
|
; Function: filter the modem $5X commands, $50 should still go the power manager
|
|
;————————————————————————————————————————————————————————————————————————————————
|
|
EXPORT DartModemCmds
|
|
DartModemCmds
|
|
cmp.w #modemSet,pmCommand(A0) ; <H22>
|
|
bne.s DartSPI ; Handle call through SPI <H22>
|
|
moveq #1,d1 ; set CC to not handled here <H22>
|
|
rts ; return <H22>
|
|
|
|
;————————————————————————————————————————————————————————————————————————————————
|
|
; Routine: DartSPI <H16>
|
|
;
|
|
; Input: A0 - points the pmgrop PB
|
|
;
|
|
; Destroys: d1
|
|
;
|
|
; Called by: Bra from NewPmgrOp
|
|
;
|
|
; Function: transfer data through new SPI port
|
|
;————————————————————————————————————————————————————————————————————————————————
|
|
EXPORT DartSPI
|
|
DartSPI
|
|
|
|
savedRegs REG D1-D4/A0-A3
|
|
|
|
|
|
; ----- dynamic test for SPI ----
|
|
movea.l PMgrBase,a3 ; point to the Power Manager's variables <H17>
|
|
btst.b #PmgrDartSPI,PmgrFlags1(a3) ; test if SPI modem installed <H17>
|
|
bne.s @SPIStart ; Handle call through SPI <H17>
|
|
moveq #1,d1 ; set CC to not handled here <H17>
|
|
rts ; return
|
|
|
|
@SPIStart
|
|
movem.l savedRegs,-(sp)
|
|
MOVEA.L UnivInfoPtr,a2 ; point to the ProductInfo table,
|
|
ADDA.L DecoderInfoPtr(a2),a2 ; then to the DecoderInfo table,
|
|
MOVEA.L JAWSAddr(a2),a2 ; then to the Niagra base address,
|
|
|
|
ADDA.L #NiagraGUR,a2 ; point to the base of the GUR space
|
|
|
|
; ----- send command and count ----
|
|
|
|
move.w pmCommand(a0),d3
|
|
move.w d3,d1
|
|
bsr SendSPI ; send command byte
|
|
bne.s @PMgrOpExit ; exit if error returned <H24>
|
|
|
|
MOVEA.L PMgrBase,A1 ; point to the Power Manager's variables <H21>
|
|
MOVEA.L vSendCountTbl(A1),A1 ; and get the send count table <H21>
|
|
move.w pmLength(a0),d2 ; pop the count into d2
|
|
tst.b (a1,d3)
|
|
bpl.s @noCount ; if positive, no count
|
|
|
|
move.w d2,d1
|
|
bsr SendSPI ; send count byte
|
|
bne.s @PMgrOpExit ; exit if error returned <H24>
|
|
|
|
; ----- send data ----
|
|
|
|
@noCount movea.l pmSBuffer(a0),a3 ; get the pointer to the command's data bytes
|
|
moveq #0,d1 ; (set CCR for BEQ so DBNE below won't fall thru)
|
|
bra.s @StartSend
|
|
|
|
@SendData MOVE.B (a3)+,D1 ; get the next data byte
|
|
BSR SendSPI ; and send it
|
|
@StartSend DBNE d2,@SendData ; -> more bytes to send
|
|
BNE.S @PMgrOpExit ; -> error
|
|
|
|
; ----- receive data -----
|
|
MOVEA.L PMgrBase,A1 ; point to the Power Manager's variables <H21>
|
|
MOVEA.L vRecvCountTbl(A1),A1 ; and get the receive count table <H21>
|
|
clr.l d4 ; clear count register
|
|
move.b (a1,d3),d4 ; initialize to count
|
|
bmi.s @readReplyCount ; (<0)
|
|
cmp.b #1,d4 ; test against 1
|
|
ble.s @readData ; if ( =0 or =1 ) go to read
|
|
subq #1,d4 ; (>1) correct count
|
|
bra.s @readData ; if 0 or 1 go to read
|
|
|
|
@readReplyCount
|
|
bsr ReceiveSPI ; read first byte for receive count
|
|
bne.s @PMgrOpExit ; exit if error returned <H24>
|
|
move.w d1,d4 ; move count into d4
|
|
|
|
@readData ; d4 has receive byte count
|
|
movea.l pmRBuffer(a0),a3 ; a3 new points
|
|
move.w d4,pmLength(a0) ;
|
|
bra.s @StartReceive ; start receiving data
|
|
|
|
@ReceiveByte
|
|
bsr.s ReceiveSPI ; read a byte into d1
|
|
bne.s @PMgrOpExit ; -> error
|
|
move.b d1,(a3)+ ; move data byte into buffer
|
|
|
|
@StartReceive
|
|
dbra d4,@ReceiveByte ; -> more bytes to send
|
|
|
|
@PMgrOpExit
|
|
moveq #0,d1 ; indicate we handled call <H17>
|
|
@exit
|
|
movem.l (sp)+,savedRegs ; restore working registers
|
|
rts
|
|
|
|
;———————————————————————————————————————————————————————————————————————————————— <H24>
|
|
; Routine: WaitSPIAckHi
|
|
;
|
|
; Input: a2.l - pointer to GUR space
|
|
;
|
|
; Destroys: d0
|
|
;
|
|
; Returns d0.b - 0 = ok, non-zero = error
|
|
;
|
|
; Function: wait for SPI ack high
|
|
;———————————————————————————————————————————————————————————————————————————————— <H24>
|
|
WaitSPIAckHi
|
|
@workRegs reg D1-d2
|
|
|
|
movem.l @workRegs,-(sp) ; save working set
|
|
move.w #32,d2 ; loop to max 32 msec
|
|
@nextmsec
|
|
move.w timedbra,d1 ; 1 msec count
|
|
@waitAckhi
|
|
btst.b #PontiSPIAck,PontiSPIMdmCtl(a2) ; test ack
|
|
dbne d1,@waitAckhi ; loop for upto 1 msec
|
|
dbne d2,@nextmsec ; loop for d2 msec
|
|
seq d0 ; set result (d0 <> 0 = error), bit lo
|
|
tst.b d0 ; set the condition codes <H26>
|
|
movem.l (sp)+,@workRegs
|
|
rts
|
|
|
|
;———————————————————————————————————————————————————————————————————————————————— <H24>
|
|
; Routine: WaitSPIAckLo
|
|
;
|
|
; Input: a2 - pointer to GUR space
|
|
;
|
|
; Destroys: d0
|
|
;
|
|
; Returns d0 - 0 = ok, non-zero = error
|
|
;
|
|
; Function: wait for SPI ack lo
|
|
;
|
|
;———————————————————————————————————————————————————————————————————————————————— <H24>
|
|
WaitSPIAckLo
|
|
@workRegs reg D1-d2
|
|
|
|
movem.l @workRegs,-(sp) ; save working set
|
|
move.w #32,d2 ; loop to max 32 msec
|
|
@nextmsec
|
|
move.w timedbra,d1 ; 1 msec count
|
|
@waitAckhi
|
|
btst.b #PontiSPIAck,PontiSPIMdmCtl(a2) ; test ack
|
|
dbeq d1,@waitAckhi ; loop for upto 1 msec
|
|
dbeq d2,@nextmsec ; loop for d2 msec
|
|
sne d0 ; set result (d0 <> 0 = error), bit hi
|
|
tst.b d0 ; set the condition codes <H26>
|
|
movem.l (sp)+,@workRegs
|
|
rts
|
|
|
|
;————————————————————————————————————————————————————————————————————————————————
|
|
; Routine: SendSPI
|
|
;
|
|
; Input: a2.l - pointer to GUR space
|
|
; d1.b - byte to send
|
|
;
|
|
; Destroys: d0
|
|
;
|
|
; Returns d0.w - 0 = ok, non-zero = error
|
|
;
|
|
; Function: send a byte thru the SPI
|
|
;
|
|
;————————————————————————————————————————————————————————————————————————————————
|
|
SendSPI
|
|
bsr.s WaitSPIAckHi ; (1) wait for pmgr idle <H24>
|
|
bne.s @error ; if bit low, error <H24>
|
|
;begin transaction
|
|
bset.b #PontiLmpSPIDir,PontiLmpSftCtl(a2) ; (2) set direction to output
|
|
move.b d1,PontiSPISftReg(a2) ; (3) write data
|
|
bclr.b #PontiSPIReq,PontiSPIMdmCtl(a2) ; (4) assert data valid
|
|
bsr.s WaitSPIAckLo ; (5) --> modem shift data <H24>
|
|
; (6) wait for data accepted <H24>
|
|
bne.s @error ; if bit low, error <H24>
|
|
bset.b #PontiSPIReq,PontiSPIMdmCtl(a2) ; (7) clear data valid
|
|
|
|
MOVE.W #noErr,D0 ; report no error
|
|
rts
|
|
|
|
@error ; <H24>
|
|
MOVE.W #pmSendStartErr,D0 ; report send error <H24>
|
|
rts
|
|
|
|
;————————————————————————————————————————————————————————————————————————————————
|
|
; Routine: ReceiveSPI
|
|
;
|
|
; Input: a2.l - pointer to GUR space
|
|
; d1.b - byte to send
|
|
;
|
|
; Destroys: d0
|
|
;
|
|
; Returns d0.w - 0 = ok, non-zero = error
|
|
;
|
|
; Function: read a byte from the spi
|
|
;
|
|
;————————————————————————————————————————————————————————————————————————————————
|
|
ReceiveSPI
|
|
bsr.s WaitSPIAckHi ; (1) wait for pmgr idle <H24>
|
|
bne.s @error ; if bit low, error <H24>
|
|
|
|
;begin transaction
|
|
bclr.b #PontiLmpSPIDir,PontiLmpSftCtl(a2) ; (2) set direction to input
|
|
bclr.b #PontiSPIReq,PontiSPIMdmCtl(a2) ; (3) (RFD) ready for data
|
|
bsr.s WaitSPIAckLo ; (4) acknowledge req <H24>
|
|
; (5) <-- modem shifting <H24>
|
|
bne.s @error ; if bit low, error <H24>
|
|
bset.b #PontiSPIReq,PontiSPIMdmCtl(a2) ; (6) acknowledge ack
|
|
|
|
bsr.s WaitSPIAckHi ; (7) wait (DAV) <H24>
|
|
bne.s @error ; if bit low, error <H24>
|
|
move.b PontiSPISftReg(a2),d1 ; (8) read data
|
|
|
|
MOVE.W #noErr,D0 ; report no error
|
|
rts
|
|
|
|
@error
|
|
MOVE.W #pmRecvStartErr,D0 ; mark as recieve error <H24>
|
|
rts ; <H24>
|
|
|
|
|
|
|
|
;••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
|
|
; That's all folks.
|
|
;--------------------------------------------------------------------------------
|
|
|
|
END
|
|
|