; ; File: USTPram.a ; ; Contains: This file contains the ROM based board burnin manager (RBIMgr). The ; RBI manager first tests RAM, then lights an LED on the burnin chamber, ; reads its data structure out of PRAM and executes selected tests for a ; specified number of times. If all tests pass, the RBI manager ; flashes an LED on the burnin chamber. Otherwise, it turns the LED ; off and jumps to the test manager. ; ; Written by: KC Chin. Adapted for use on Ericson and later machines by Scott Smyers ; ; Copyright: © 1983-1990, 1992-1993 by Apple Computer, Inc., all rights reserved. ; ; Change History (most recent first): ; ; 9/13/93 SAM Added a flag to USTInit to tell it not to call SizeMem (again!). ; 5/2/92 kc Roll in Horror. Comments follow: ;

3/6/92 AL Moved the PRAM routines RdXByte, WrXByte and ClkWpOff over ; to this file (from USTTestMgr.a) because they were accessed ; mainly from files included in USTStartTest.a. This saved a lot ; of work by not making me go through all the occurrences and ; changing them from BSR6 to BigBSR6,a0. The few access to them ; from files in USTStartTest1.a can be taken care of. ;

01/27/92 jmp Conditionalized the PROC parts of this file for use in lining up ; the UST part of HORROR with that of TERROR/Zydeco. ; 4/2/91 CCH Rolled in Scott Smyers' changes: Added support for 32 tests ; (instead of only 16). Added support for test ID and new word ; sized subtest ID. Made the use of PRAM during ROM burnin more ; logical. Modularized the code which copies data between PRAM and ; RAM to increase re-usability. ; 3/13/91 CCH Rolled in RBI changes from Scott Smyers. ; 1/14/91 CCH Rolled in Scott Smyers' changes. ; 12/14/90 HJR Added Elsie V8 support. ; ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ ; Pre-TERROR ROM comments begin here. ; ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ ; <10> 6/18/90 CV Rolling in changes from mainproj. Original comments below. ; {12} 6/18/90 SS Changed the error reporting for RAM tests so that the bank ; failure number goes into the high nibble of the subtest number. ; <9> 6/7/90 CV Rolling in changes from mainproj. Original comments below. ; {11} 6/5/90 SS Changed the error reporting so that all tests now report all of ; D6 instead of just a byte, word or long. This makes test result ; interpretation much easier. Also added the RBV test to the test ; table. ; <8> 5/18/90 CV Rolling in changes from mainproj. Original comments below. ; {10} 5/10/90 SS Fixed the initialization for the sound interrupt test. I now ; set up the HW info regs before calling the test. ; <7> 4/27/90 CV Rolling in changes from mainproj. Original comments below. ; {9} 4/20/90 SS Changed to use SCSI equates from the HWPrivateEqu file. Also am ; now using a VIA access for my flashing light time constant. ; <6> 4/10/90 JJ Rolling in changes from mainproj. Original comments below. ; {8} 4/9/90 SS Corrected the test numbering for status reporting. Added the ; Reverse mod3test to the suite of RAM tests. Removed the EASE ; header. ; <5> 4/2/90 CV Rolling in changes from mainproj. Original comments below. ; {7} 3/27/90 SS Added a power-on counter which increments every time ROM burn-in ; is entered. Also added a post burn signature and fixed a small ; inconsistancy (changed a move.b to move.w). ; <4> 3/12/90 CV Rolling in changes from mainproj. Original comments below. ; {6} 3/9/90 SS Changed the calling convention for RBI tests while in the RBI ; manager. Changed the name of the RBI manager to RBIMgr. Added ; power fail recovery. made the tests' access to the RBI data ; structure more robust. Added a test of memory before beginning ; RBI. ; <5> 2/27/90 SS Made the RBI signature bytes more universal. ; <4> 2/16/90 SS Added flags to statements which were changed to support Ericson. ; <3> 2/13/90 MA Fixed register usage when logging BIparameters ; <2> 2/12/90 MA 'MovePram' cleaned up and changed register usage. Fixed ; numerous bugs. Now preserves A1 in PRAM Logger. ; <1.2> 8/22/89 GMR Setup VBR and preped a0,d0,d2 before launching BITests. ; <1.1> 6/11/89 GMR Removed INCLUDES, now in header file which includes all others. ; <1.0> 6/11/89 GMR Added new universal StartTest files for first time to EASE. ; <1.3> 5/16/89 rle don't use startup code for size memory anymore ; <1.2> 3/28/89 rle now uses (some) equates from "STEqu.a" ; <1.1> 2/10/89 RLE incorporated K.C. Chin's changes to add data bus test and use ; stack-based routines ; <1.0> 2/2/89 RLE new file ; ; ;------------------------------------------------------------------------------ PRLog PROC IMPORT SetupForExecuteDTM IMPORT JGetHardwareInfo IMPORT RomTest IMPORT StartUpRomTest IMPORT Mod3Test IMPORT RevMod3Test IMPORT ExtRamTest IMPORT AddrLineTest IMPORT DataBusTest IMPORT SCCRegTest IMPORT SCCTimerTest IMPORT SCCLoopTest IMPORT Viatest IMPORT TestScsi IMPORT TestAsc IMPORT PramTest ; IMPORT SizeMemory IMPORT RdXByte IMPORT WrXByte IMPORT ClkWpOff IMPORT Error1Handler IMPORT BaseOfROM ; IMPORT TestSndInts ; <11> IMPORT TestRBV ; <11> IMPORT TestCLUT ; <13> IMPORT TestVRAM ; <13> IMPORT ReadPramSig ; IMPORT USTInit ; EXPORT RBIMgr ;<6> EXPORT RdXByte EXPORT WrXByte EXPORT ClkWpOff ;--------------- to next below MaxCTESubTests equ 29 ;maximum number of subtests supported by the RBI manager TotalRBITests equ 32 ;total number of tests supported by the RBI manager (crit and non crit) bROMTest equ 31 ; bRAMTest equ 30 ; bMovInvRAMTest equ 29 ; CritTestMask equ (1< CurTest ds.l 1 ;current test BIFailFlag ds.w 1 ;Burn-In fail flag TestLoopCount ds.b TotalRBITests ;number of times to execute each test per cycle RBITestList ds.l 2*MaxCTESubTests ;test ID/subtest ID list for RBI tests RBINumInst ds.w 1 ;number of installed tests BINumCycles ds.w 1 ;# of cycles to run BIRunMask ds.l 1 ;Mask of tests to run BIFailMask ds.l 1 ;Mask of tests that fail BICounter ds.w 1 ;# of cycles completed BIPasses ds.w 1 ;# of pass cycles BIFirstF ds.w 1 ;First failure cycle # BILastF ds.w 1 ;Last failure cycle # BITest ds.l 1 ;burnin test number BISubTest ds.l 1 ;burnin subtest number BITestResult ds.l 1 ;test result HWInfo ds.l 3 ;information returned from GetHardwareInfo (6 registers) sizeofRBIInfo equ *-startofRBIInfo ;size of the RBI information ENDR pLowerCopy EQU $0 ;start of lower copy of PRAM information pUpperCopy EQU $80 ;start address of upper copy of PRAM information pPRAMLength EQU $80 ;length of each copy of PRAM information PBSig EQU 'DONE' ;Post burn signature RBIPRAMMap RECORD pUpperCopy,increment pTstIterCnt ds.b TotalRBITests ;test iteration per cycle count pBlank1 ds.b $30 ;undefined area number 1 pFailMask ds.l 1 ;tests failed mask pTestNum ds.l 1 ;last failing test number pSubTestNum ds.l 1 ;last failing subtest number pTestResult ds.l 1 ;result of last failed test pFrstFail ds.w 1 ;cycle at first failure pLastFail ds.w 1 ;cycle at last failure pBlank2 ds.b 2 ;undefined area number 2 pPOCntr ds.w 1 ;number of power cycles during testing pCyclesDone ds.w 1 ;number of cycles completed pCyclesPass ds.w 1 ;number of cycles which passed pPBSigAddr ds.l 1 ;RBI complete signature pSTMJump ds.b 1 ;Jump to STM flag pBlank3 ds.b 1 ;another blank area pCycleCnt ds.w 1 ;number of cycles to run pTestMask ds.l 1 ;tests to run mask ENDR ; savedregs reg a0-a2 ;registers to save (MUST be reflected in HWInfo length) tstloopregs reg d0-d1/a0/a5 ;registers used during test looping ;--------------- to next above ;------------------------------------------------------------------------------- ; Routine: RBIMgr Manages ROM Based Burnin. <6> ; <4> ; Inputs: none ; <4> ; Destroys: d0-d7, a0-a7 <4> ;------------------------------------------------------------------------------- RBIMgr ;<6> WITH RBIInfo, RBIPRAMMap ; BSR6 disableMMU ;disable the MMU and require serialized access ;to IO space ; OK, everything is ready to go. Start the actual testing stuff by doing the memory ; sizing and preparation for CTE: BSR6 SizeMemory ;First, size memory bne @RBIFailure ;Bomb out if this fails ; Then next step is to test RAM to make sure we're justified in using RAM for our scratch. move.w #ErrRAMA,d7 ;Init error to Bank A error code movea.l (sp),sp ;Set stack contiguous with the chunk table movea.l sp,a4 ;get ptr to chunk table IF forRomulator THEN ; TestInRAM a1 ;are we running in RAM? bne.s @RamPassed ;don't test RAM or move vector table if so! ENDIF ; @memLoop movea.l (a4)+,a0 ;get start of bank cmpa.l #-1,a0 ;at end of table? beq.s @RamPassed ;(-1) means end of table move.l (a4)+,a1 ;get length adda.l a0,a1 ;calculate end of bank cmpa.l a1,sp ;above memory chunk table? <1.9> bhi.s @testChunk ;no, test complete chunk movea.l sp,a1 ;yes, set top to base of chunk table <1.9> @testChunk BSR6 Mod3Test ;test this bank <1.7> tst.l d6 bne @RBIFailure ;failed, go to test manager addq.w #1,d7 ;bump to next bank code bra.s @memLoop ;passed, test next bank ;--------------- to next below @RamPassed ;--------------------------------------------------------------------------- ; ; OK, if we got here, then all the RAM tests passed. Now we stoke up the RBI ; manager data structures in RAM, init CTE and start testing. ; ;--------------------------------------------------------------------------- WITH USTGlobals clr.w d7 ;zero out low word of d7 during initialization moveq #0,D2 ;tell USTInit to call SizeMem BSR6 USTInit ;size memory and initialize the universal global area ;next, move it to just below the chunk table movea.l ChunkTable(a5),a1 ;set the destination for the new global area suba.l #sizeofRBIInfo,a1 ;make room for the RBI globals (to be initialized ; later in this routine) bsr RelocateGlobals ;then do it - relocate the actual USTGlobals movea.l DcdrInfPtr(a5),a0 ;get the pointer to the decoder info movea.l PrdctInfPtr(a5),a1 ;and the product info movea.l DecoderInfo.VIA1Addr(a0),a2 ;then get the VIA1 pointer movem.l savedregs,HWInfo(a5) ;Save this info (a0-a2) for general use ; ; Next, init CTE interface and install all appropriate ROM based tests and subtests (note ; that the "installation" of the tests is NOT analagous to the InstallTest and ; InstallSubtest routines of the old CTE). I will continue to use the list of test & subtest ; IDs, and fill in the USTGlobals structure (using information gleaned from the test and ; subtest tables in the file USTSubtests.a) before calling GIM_ExecuteDTM. ; bsr CTEInit ;just do it movem.l HWInfo(a5),savedregs ;then restore our state! ; ; Finally, turn on the burnin light to let everyone know that all is well, so far. ; bsr IndicatorOn ;turn on the RBI indicator ; ;Now we must decide which half of PRAM is valid. Make a copy of the valid half to the ;other half. ; move.l a5,-(sp) ;save our global pointer bsr6 ClkWpOff ;first, turn write protect off for good move.l #SigLocs,d3 ;Get the locations of the signature bytes bsr6 ReadPramSig ;read the signature out of PRAM move.l (sp)+,a5 ;restore the global pointer cmp.l #SigBytes,d4 ;Compare result with the signature beq.s @uppertolower ;if upper half is valid, copy it to lower half ; ;OK, its the lower half of PRAM that's valid. Now we have to copy this lower ;half to the upper half before we can continue ; move.w #pLowerCopy,-(sp) ;Source PRAM address move.w #pUpperCopy,-(sp) ;Destination PRAM address bra.s @copy ;go copy @uppertolower move.l #SigLocs,d1 ;First, invalidate the signature in the lower half of PRAM swap d1 ;Get the address of the lower signature bytes moveq.l #0,d2 ;Write a zero to one of the sig bytes locations move.l a5,-(sp) ;save the global pointer BSR6 WRXBYTE move.l (sp)+,a5 ;get the global pointer back move.w #pUpperCopy,-(sp) ;source PRAM address is upper half move.w #pLowerCopy,-(sp) ;destination PRAM address is lower half @copy move.w #pPRAMLength,-(sp) ;Length of copy bsr RelocatePRAM ;do it. lea 6(sp),sp ;Clean up stack ; ; Increment the power on counter every time we get to this point ; bsr InvalidUpper ;Before updating PRAM, we need to invalidate the signature<7> _CopyPRAMToRAM #pPOCntr,TempScratch(a5),#2 ;read the power on counter addq.w #1,TempScratch(a5) ;increment the power on counter _CopyRAMToPRAM TempScratch(a5),#pPOCntr,#2 ;and write the PO counter back to PRAM bsr UpperToLower ;Copy the upper half of PRAM to the lower half<7> ; ; Now, make a copy of the PRAM burn-in information into memory ; _CopyPRAMToRAM #pCycleCnt,BINumCycles(a5),#2 ;get the number of cycles to run _CopyPRAMToRAM #pTestMask,BIRunMask(a5),#4 ;get the tests to run mask _CopyPRAMToRAM #pFailMask,BIFailMask(a5),#4 ;and get the current tests failed mask _CopyPRAMToRAM #pCyclesDone,BICounter(a5),#2 ;get the number of cycles completed _CopyPRAMToRAM #pCyclesPass,BIPasses(a5),#2 ;and the number of cycles passed _CopyPRAMToRAM #pFrstFail,BIFirstF(a5),#2 ;get the cycle # of first failure _CopyPRAMToRAM #pLastFail,BILastF(a5),#2 ;and the cycle # of last failure _CopyPRAMToRAM #pTstIterCnt,TestLoopCount(a5),#TotalRBITests ; move.w BICounter(a5),d1 sub.w d1,BINumCycles(a5) ;Subtract the cycles we've already run ; ; After each BI Cycle, this is the place to return ; @NewBICycle clr.w BIFailFlag(a5) ;initialize BIFailFlag subq.w #1,BINumCycles(a5) ;decrement BINumCycles bmi @BIDone ;if we're finished then log BICounter and go to ; Test Manager addq.w #1,BICounter(a5) ;increment the cycle counter ; ; First run the selected critical tests ; move.l BIRunMask(a5),d0 ;get the test mask move.l #TotalRBITests-1,d1 ;init test number @TestLoop ;loop here to run the selected critical tests tst.l d0 ;are we done yet? beq.w @CycleDone ;exit if so asl.l #1,d0 ;otherwise, see if we want to run the next test bcc.w @NextTest ;keep looping if not move.w d1,d7 ;remember which test we're running in case we end up in STM ori.w #$C0,d7 ;this indicates the test # is an RBI # ; ; Kludge Alert! Kludge Alert! ; ; This is really ugly, but I don't have another way to take care of it at the moment. The problem is ; this: The USTGlobals structure is >128 bytes long, and declaring the RBIGobals immediately after ; it makes the total globals I'm using for RBI way more than 128 bytes. However, the addressing mode ; we're using to access two of the fields in the RBI globals (TestLoopCount and RBITestList) is address ; indirect with index and displacement. This mode only allows the displacement (which are TestLoopCount ; and RBITestList) to be 8 bits (max 128 value). The problem is, these two fields are so numerically high ; in the records that they require more than +128 to reach them. The reason that this problem arises is ; due to the fact the the RBIInfo structure needs to be declared AFTER the USTGlobals, because I will ; always need the USTGlobals (and hence want the offsets to start at 0), but I only need the RBIInfo ; record for RBI stuff. For a picture of what we're talking about, here is a diagram of what the routine ; RelocateGlobals does: ; ; top of RAM ---> |-------------| ; | Chunk | ; | Table | ; |-------------| ; | RBI | ; | Info | ; This distance is{ |-------------| ; > +128 bytes so { | UST | } This structure was extended for the new ; the two fields { | Globals | } CTE kernel v2.1 ; TestLoopCount { | | } ; and RBITestList { | | ; can't be reached{ | | ; with the "old" { A5 ---> |-------------| ; addressing mode. | VBR | ; | register | ; |-------------| ; | CTE | ; | Workspace | ; SP ---> |-------------| ; ; ; Here is an example of the offending command that the assembler doesn't like: ; ; move.b TestLoopCount(a5,d1.w),d2 ; ; The rules say that TestLoopCount is a signed 8 bit number, but it don't work with the way things are ; set up because the value of TestLoopCount as declared in the RBIInfo record will be much ; greater than 128 (the max signed value of a byte). Any ideas? ; ; I need a way to fake out the assembler, but I don't think this will work correctly. Stay ; tuned for a fix: move.b #TestLoopCount,d3 ; it takes these four statements and.w #$0f,d3 ; #$0F OR #$00FF?????? ; to do the same job as add.w d3,d1 ; that one little statement below move.b (a5,d1.w),d2 ; ;;; move.b TestLoopCount(a5,d1.w),d2 ; get the loop count for this test and.w #$FF,d2 ;make the low word valid moveq.l #0,d5 bset.l d1,d5 move.l d5,CurTest(a5) ;set the current test bit mask and.l #CritTestMask,d5 ;is this a critical test? bne.s @CritTest ;branch if so ; ; This is a non critical test (i.e., a CTE test) ; cmp.w RBINumInst(a5),d1 ;compare our test number with the number of installed tests bge.w @NextTest ;don't do it if we don't have that many installed ; Fill in the RBIInfo fields BITest and BISubTest (this data is recorded into PRAM in case of ; failure). I have the same problem here as I did above for the TestLoopCount field, but the solution ; is a little different. Notice that the original command was this: ; ; move.l RBITestList(a5,d1.w*8),BITest(a5) ; ; The d1.w*8 needs to be taken care of first (calculated), then I can add the RBITestList value to ; it. Finally, I can use the resulting d1.w as an index to a5: ; ; NOTE: Remember to save d1.w and restore it to what it was prior to all this fooling around: ; move.w d1,TempScratch(a5) ; save d1.w in the RBI scratch area asl.w #3,d1 ; this has the same effect as d1.w*8 add.l #RBITestList,d1 ; fake out the assembler move.l (a5,d1.w),BITest(a5) ; see if this works move.l 4(a5,d1.w),BISubTest(a5) ;;;; move.l RBITestList(a5,d1.w*8),BITest(a5) ;place the test number in here ;;;; move.l RBITestList+4(a5,d1.w*8),BISubTest(a5) ;and put the subtest here move.w TempScratch(a5),d1 ; restore my original d1.w ; Do the necessary setup before calling GIM_ExecuteDTM: movem.l tstloopregs,-(sp) ; save our local registers (d0-d1/a0/a5) BigBSR6 SetupForExecuteDTM,a0 movem.l (sp),tstloopregs ; restore our registers (but leave them on the stack) movea.l CTEGlobals(a5),a3 ; load up the CTE globals for the GIM_ExecuteDTM call @CTETestLoop movem.l d1-d2/a3,-(sp) ; save the loop counter, test number and globals pointer CASE ON GIM_ExecuteDTM (a3), EXOPUSERINFO(a5), TINFOUSERINFO(a5), SINFOUSERINFO(a5), EXRESERR(a5) CASE OFF movem.l (sp)+,d1-d2/a3 ; restore this stuff move.l d0,d6 ; get the result into our result register dbne.w d2,@CTETestLoop ; and loop until error, or done bra.s @EndTestLoop ; go clean up ; ; This is a critical test (i.e., not CTE) with special considerations - don't wipe out the ; upper part of RAM, which contains all of the RBI and CTE data structures currently in ; use (yes, that means that the upper part of RAM doesn't get tested in ROM Burnin...unless ; we get around to modifying the routine to move the globals down to the lower part after ; testing there, then testing the upper part, then moving the globals back up to the top). ; See the description of the RelocateGlobals routine for a diagram of what memory looks ; like after the setup for RBI is complete, so you can get an idea of what is and is not ; being tested in the RAM tests for RBI: ; @CritTest move.l d1,BITest(a5) ;place the test number in here move.l #1,BISubTest(a5) ;and always start with a subtest of 1 lea RBICritTests,a0 ;get the base address of the critical test table move.w #TotalRBITests-1,d5 sub.w d1,d5 move.l (a0,d5.w*4),d5 ;get the offset of the test to run lea (a0,d5.l),a0 ;and get the address of the actual test movem.l tstloopregs,-(sp) ;save our working registers @CritTestLoop movem.l d2/a0,-(sp) ;save our working registers jsr (a0) ;execute the test movem.l (sp)+,d2/a0 ;restore the working registers tst.l d6 ;was there an error? dbne.w d2,@CritTestLoop ;keep going until we're used up @EndTestLoop ; ; The test failed. Log info into Pram ; movem.l (sp),tstloopregs ;restore our registers (but leave them on the stack) movem.l HWInfo(a5),savedregs ;restore other necessary information bsr IndicatorOn ;go turn on the RBI indicator again move.l d6,BITestResult(a5) ;store the error code in RAM beq.s @CleanUp ;keep going if no error bsr LogFailure ;else, log the failure to PRAM @CleanUp movem.l (sp)+,tstloopregs ;restore registers again @NextTest subq.w #1,d1 ;decrement the test number bra.s @TestLoop ;and keep going @CycleDone ;--------------------------------------------------------------------------- ; ChkFailFlag: if BIFailFlag = 0, inc BIPasses ; If inc BIPasses, log BIPasses and BICounter ;--------------------------------------------------------------------------- bsr InvalidUpper ;Before updating PRAM, we need to invalidate the signature _CopyRAMToPRAM BICounter(a5), #pCyclesDone, #2 ;update burnin cycle counter tst.w BIFailFlag(a5) ;Any failures on that last pass? bne.w @CycleFailed ;Don't increment the cycles passed count if so addq.w #1,BIPasses(a5) ;else, increase BIPasses _CopyRAMToPRAM BIPasses(a5), #pCyclesPass, #2 ;update cycles passed counter @CycleFailed bsr UpperToLower ;Copy upper half of PRAM to lower half bra.w @NewBICycle ; ;--------------------------------------------------------------------------- ; Log BICounter and go to Test Manager ;--------------------------------------------------------------------------- @BIDone clr.w d7 ;indicate we're not testing anymore bsr InvalidUpper ;Before updating PRAM, we need to invalidate the signature _CopyRAMToPRAM #PBSig, #pPBSigAddr, #4 ;log the DONE signature bsr UpperToLower ;Copy the upper half of PRAM to the lower half tst.l BIFailMask(a5) ;Now, look to see if there were any failures bne.s @RBIFailure ;If so, tell the board test people about it. ; ; See if they want to go to the serial test manager when we're done regardless ; _CopyPRAMToRAM #pSTMJump, TempScratch(a5), #1 ; tst.b TempScratch(a5) ;is our flag ­ 0? bne.s @JumpToSTM ;go directly to STM if so Ι ; ; Now, turn the burn-in light off ; bsr IndicatorOff ;Turn off the RBI indicator moveq.l #0,d2 ;remember that we're off ;------------- to next above ; ; next, we need to decide if there were any failures. If there were not, flash the LED, ; otherwise, just turn it off and leave it off. ; @flash move.w #500,d0 ;Delay for 500 mS <9> @delay0 move.w #delay1mS,d1 ;Get the 1mS time constant <9> @delay tst.b vBufB(a2) ;touch the VIA register <9> dbra.w d1,@delay ;and delay <9> dbra.w d0,@delay0 ;outer delay <9> eor.b #1,d2 ;toggle the state of the indicator beq.s @Turnitoff ;branch if it's on to turn it off bsr IndicatorOn ;else turn it on bra.s @flash ;and continue @Turnitoff bsr IndicatorOff ;turn indicator off bra.s @flash ;and keep going! <9> @RBIFailure @JumpToSTM ; ; Failure in RBI! Turn off the burnin light and go the TM in case someone has a ; mac connected for diagnostics. ; bsr IndicatorOff ;Turn off the RBI indicator ; bra Error1Handler ;For now, go to TM when there are errors ENDWITH ;and new test table ;------------------------------------------------------------------------------- ; ; This routine simply invalidates the signature in the upper half of PRAM. This ; must be done before updating the contents of PRAM. ; ;------------------------------------------------------------------------------- InvalidUpper move.l a5,-(sp) ;save a5 move.l #SigLocs,d1 ;then invalidate the signature byte moveq.l #0,d2 ;Write a zero to one of the sig byte locations BSR6 WRXBYTE move.l (sp)+,a5 ;restore a5 rts ;------------------------------------------------------------------------------- ; ; This routine first validates the signature in the upper half of PRAM, then ; invalidates the low PRAM signature, and copies the upper ; half of PRAM to the lower half. ; ;------------------------------------------------------------------------------- UpperToLower move.l a5,-(sp) ;save a5 move.l #SigLocs,d1 ;First, restore the signature byte in the upper half of PRAM move.l #SigBytes,d2 rol.l #8,d2 BSR6 WRXBYTE move.l #SigLocs,d1 ;first, invalidate the signature in the lower half of PRAM swap d1 moveq.l #0,d2 ;Write a zero to one of the sig byte locations BSR6 WRXBYTE move.w #pUpperCopy,-(sp) ;then copy upper half of PRAM to lower half move.w #pLowerCopy,-(sp) ;destination PRAM address is lower half move.w #pPRAMLength,-(sp) ;Length of copy bsr RelocatePRAM ;do it. lea 6(sp),sp ;Clean up stack move.l (sp)+,a5 ;restore a5 rts ;----------------------- to next below RelocatePRAM ;------------------------------------------------------------------------------- ; ; This routine moves a block of PRAM from one location to another. When called, ; the stack must be set up as follows: ; StackFrame1 RECORD 0, Increment ;Starts at offset 0 into the stack @SavedA5 ds.l 1 ;save space for the saved global pointer @return ds.l 1 ;Return address @byteCnt ds.w 1 ;number of bytes to move @PRAMDest ds.w 1 ;destination PRAM address @PRAMSource ds.w 1 ;source PRAM address ENDR ; ;------------------------------------------------------------------------------- move.l a5,-(sp) ;save the global pointer WITH StackFrame1 @Copy move.w @PRAMSource(sp),d1 ;Get the source PRAM address BSR6 RDXBYTE ;read a byte from there move.b d1,d2 ;prepare to write it out move.w @PRAMDest(sp),d1 ;get the destination PRAM address BSR6 WRXBYTE ;write out the byte addq.w #1,@PRAMSource(sp) ;increment the source addq.w #1,@PRAMDest(sp) ;increment the destination subq.w #1,@byteCnt(sp) ;decrement the byte count bne.s @Copy ;Continue if not zero yet ENDWITH move.l (sp)+,a5 ;restore the global pointer rts ;Else, return ;------------------------------------------------------------------------------- ; ; IndicatorOn; Indicator Off ; These routines turn on and off the SCSI indicator. In the case of 53C80 ; machines this indicator is the SCSI BSY signal, and in the case of C96 ; machines, this indicator is the ATN signal. The indicator is on when the ; corresponding SCSI signal is asserted. ; ; Inputs: ; d0 - bases valid flags ; a0 - pointer to the decoder info record ; ; Destroys: ; a3 ; ;------------------------------------------------------------------------------- IndicatorOn WITH DecoderInfo, USTGlobals move.l BasesValidFlags(a5),d0 ;get the bases valid flags btst.l #SCSIExists,d0 ;do we have a C80? beq.s @hasC96 ;branch if not movea.l SCSIAddr(a0),a3 ;Get the address of the SCSI register bset.b #3,sICR(a3) ;Turn the busy light on rts ;and return ENDWITH @hasC96 bsr FindC96Base ;get the SCSI (1 or 2) base address in a3 ;;; move.b #cSetAtn,rCMD(a3) ;assert ATN move.b #$22,rCMD(a3) ;send data rts ;------------------------------------------------------------------------------- IndicatorOff WITH DecoderInfo, USTGlobals move.l BasesValidFlags(a5),d0 ;get the bases valid flags btst.l #SCSIExists,d0 ;do we have a C80? beq.s @hasC96 ;branch if not movea.l SCSIAddr(a0),a3 ;Get the address of the SCSI register bclr.b #3,sICR(a3) ;Turn the busy light off rts ;and return ENDWITH @hasC96 bsr FindC96Base ;get the SCSI (1 or 2) base address in a3 move.b #cRstAtn,rCMD(a3) ;negate ATN rts ;------------------------------------------------------------------------------- ; ; FindC96Base ; This routine retrieves the SCSI base address of the external SCSI chip. ; This routine assumes that the SCSI chip is a 53C96. ; ; Inputs: ; d0 - bases valid flags ; a0 - pointer to the decoder info record ; ; Outputs: ; a3 - pointer to the base address of the external 53C96 ; ;------------------------------------------------------------------------------- FindC96Base WITH DecoderInfo movea.l SCSI96Addr1(a0),a3 ;assume SCSI1 is the external bus btst.l #SCSI96_2Exists,d0 ;do we have a SCSI2 bus? beq.s @SCSI1Only ;branch if so (means SCSI1 is only bus) movea.l SCSI96Addr2(a0),a3 ;else, SCSI bus 2 is the external bus ENDWITH @SCSI1Only ;;; btst.b #3,rCF1(a3) ;are we in test mode already? ;;; bne.s @inTestMode ;branch if so move.b #cRstSChp,rCMD(a3) ;else, reset the chip, move.b #cNOP,rCMD(a3) ;perform the obligatory NOP, move.b #EnableChpTstMd,rCF1(a3) ;enable chip test mode, ;;; move.b #2,rTST(a3) ;and put the chip into initiator mode move.b #1,rTST(a3) ;and put the chip into target mode (for now) @inTestMode rts ;we're OK, return EXPORT CopyRAMToPRAM CopyRAMToPRAM ;------------------------------------------------------------------------------- ; ; CopyRAMToPRAM - copies some information out of memory into PRAM ; ; input: StackFrame2 RECORD 0, Increment @return ds.l 1 ;Return address @byteCnt ds.w 1 ;number of bytes to move @PRAMDest ds.w 1 ;destination PRAM address @RAMSource ds.l 1 ;source RAM address ENDR @regsUsed reg d1/d3/a3/a5 ; ;------------------------------------------------------------------------------- WITH StackFrame2 move.w @PRAMDest(sp),d1 ;get the PRAM destination address movea.l @RAMSource(sp),a3 ;get the source RAM address move.w @byteCnt(sp),d3 ;get the number of bytes to move moveq.l #0,d2 ;zero out the value register subq.w #1,d3 ;adjust for dbra blt.s @done ;quit if the number was zero @copy move.b (a3)+,d2 ;get the next byte to write movem.l @regsUsed,-(sp) ;save our local registers bsr6 WrXByte ;go write the information movem.l (sp)+,@regsUsed ;restore our local registers addq.w #1,d1 ;increment the PRAM address dbra.w d3,@copy ;keep copying @done ENDWITH rts EXPORT CopyPRAMToRAM CopyPRAMToRAM ;------------------------------------------------------------------------------- ; ; CopyPRAMToRAM - copies some information out of PRAM into memory ; ; input: StackFrame3 RECORD 0, Increment @return ds.l 1 ;Return address @byteCnt ds.w 1 ;number of bytes to move @RAMDest ds.l 1 ;destination RAM address @PRAMSource ds.w 1 ;source PRAM address ENDR @regsUsed reg d2/d3/a3/a5 ; ;------------------------------------------------------------------------------- WITH StackFrame3 move.w @PRAMSource(sp),d2 ;get the PRAM destination address movea.l @RAMDest(sp),a3 ;get the source RAM address move.w @byteCnt(sp),d3 ;get the number of bytes to move subq.w #1,d3 ;adjust for dbra blt.s @done ;quit if the number was zero @copy move.w d2,d1 ;put the PRAM address in the right register movem.l @regsUsed,-(sp) ;save our local registers bsr6 RdXByte ;go read the information movem.l (sp)+,@regsUsed ;restore our local registers move.b d1,(a3)+ ;put the data into RAM addq.w #1,d2 ;increment the PRAM address dbra.w d3,@copy ;keep copying @done ENDWITH rts ;------------------------------------------------------------------------------- ; ; RelocateGlobals - This routine relocates the global data structure created by ; The USTInit routine, and the RBIInfo globals also. If something ; changes about how the USTInit routine generates it's globals, ; then this routine may have to change. ; ; When this routine is finished, memory looks like this (note that ; this diagram is not to scale): ; ; top of RAM ---> |-------------| ; | Chunk | ; | Table | ; |-------------| ; | RBI | ; | Info | ; |-------------| ; | UST | } This structure was extended for the new ; | Globals | } CTE kernel v2.1 ; | | } ; | | ; | | ; A5 ---> |-------------| ; | VBR | ; | register | ; |-------------| ; | CTE | ; | Workspace | ; SP ---> |-------------| ; ; ; ; Input: ; a1 - destination for new globals ; ; Destroys: ; d0, d1, a0, a1, a2 ; ;------------------------------------------------------------------------------- RelocateGlobals WITH USTGlobals lea sizeofUSTGlobals(a5),a0 ;get a ptr past the last element in globals move.l a0,d0 ;next calculate the size of the move sub.l sp,d0 ;(i.e., distance from stack to end of globals) move.l d0,d1 ;save the actual size for later movea.l a1,a2 ;and save the destination address lsr.l #2,d0 ;adjust for long moves (slop is OK) addq.l #1,d0 ;be sure to move the return address @Relocate move.l -(a0),-(a1) ;move a long word dbra.w d0,@Relocate ;and continue until done movea.l a2,a0 ;recall the original destination address suba.l d1,a0 ;calculate the address of the new stack movea.l a0,sp ;and reposition the stack addq.l #4,a0 ;point to where the vbr should be movec a0,vbr ;and put the vbr there lea -sizeofUSTGlobals(a2),a5 ;put the globals in the same relative place ENDWITH rts ;-------------------------------------------------------------------------- ; ; This disables the MMU translation, disables the program and data caches and ; requires serialized access to IO space ; ;-------------------------------------------------------------------------- nuBusTTxlat EQU $00FFC040 ; require serialized writes ioNuBusTTxlat EQU $00FFC040 ; for all I/O space disableMMU IF forRomulator THEN TestInRAM a0 ;are we in ROM? beq.s @reallyDisableMMU ;really disable MMU if we're in ROM RTS6 ;else return immediately @reallyDisableMMU ENDIF sub.l d0,d0 ; D0 = 0 bset #31,d0 ; set Data Cache Enable bit on 040s movec d0,CACR ; attempt to enable data cache (temporarily) movec CACR,d0 ; check and see if it's still there btst #31,d0 ; see if the bit exists in CACR beq.s @not040 ; IF we're on a 68040 THEN MACHINE MC68040 ; need this for the MOVEC D0,TC below cinva bc ; make sure caches are invalidated sub.l d0,d0 ; clear d0 movec d0,CACR ; disable both instruction and data caches movec d0,TC ; make sure that the MMU is disabled move.l #nuBusTTxlat,D0 ; get value to translate upper nuBus space movec d0,DTT0 ; use serialized writes on this space move.l #ioNuBusTTxlat,D0 ; get value to translate i/o and nuBus space movec d0,DTT1 ; use serialized writes on this space bra.s @not030 ; step around the rest of the 020/030 cache/tc stuff MACHINE MC68030 ; set it back to what works for 020/030 @not040 ; ELSE move.l #$2000,d0 ; CACR value w/WA bit for 030 movec d0,cacr ; disable both instruction & data caches movec cacr,d0 ; hmmm, maybe we are an 030 tst.l d0 ; see if WA bit is still on beq.s @not030 ; IF we are an 030 processor lea @TCOff,a0 ; point to TC value that disabled the MMU pmove (a0),tc ; turn off MMU ; ENDIF @not030 ; ENDIF RTS6 ; return through a6 @TCOff dc.l 0 ; a TC value to disable the MMU CTEInit ;--------------------------------------------------------------------------- ; ; CTEInit - initialize CTE in the RBI environment ; ;--------------------------------------------------------------------------- WITH CPUTestList, USTGlobals ; movea.l (sp)+,a1 ;save the return address suba.l #SizeOfCTEGlobals,sp ;make room on the stack for the CTE workspace move.l sp,CTEGlobals(a5) ;remember where the workspace is movea.l sp,a0 ;put the pointer into a predictable location move.l a1,-(sp) ;replace the return address onto the stack move.w #RunBits.RBI,RunMode(a5) ;init our environment - set the run mode flag ; to reflect that this is the RBI environment CASE ON GIM_InitInterface (a0),#SIZEOFCTEGLOBALS ;Init CTE CASE OFF ; ; Next, install all the ROM based tests for this machine. The new CTE kernel does not rely ; on the linked lists of tests and subtests; rather, it leaves the implementation up to ; the shell that is in control (in this case, the Horror ROM). The Terror implementation ; used a list of Test/Subtest IDs in the RBIInfo structure to gain the various pieces of ; info about them from the test lists (in the file USTSubtests.a), and used the InstallTest ; and InstallSubtest routines of the old CTE to install them. I will keep the list of IDs ; around, but will not use the InstallTest method (mainly because it no longer exists). ; Rather, I have enhanced the USTGlobals to represent a structure that is formatted as the ; paramters to the ExecuteDTM call (ExecutionOptions, testInfo, subtestInfo and executionResults ; structures). This structure is allocated on the stack at the end of the UST globals (see the ; diagram in the description of the RelocateGlobals routine). Before jumping into a DTM, I will ; fill in the necessary fields of the parameters. The initialization stuff will be done in the ; routine SetupForExecuteDTM, in the source file USTEnvirons.a (it's there because two of the ; ROM diagnostic environments - STM and IHT - are there, and the majority rules). ; movea.l GlobCPUTestList(a5),a0 ;get the CPU specific test list lea RBITestList(a5),a1 ;a pointer to our local list of test IDs move.w #0,RBINumInst(a5) ;clear the number of installed tests counter @InstallDTMIDs moveq.l #0,d0 ;zero out the subtest ID register move.w TLSubTest_ID(a0),d0 ;get the subtest ID number (word) cmpi.w #-1,d0 ;is this the end? beq.s @QuitCTEInit ;finished with subtests if not move.w TLrunflags(a0),d1 ;get the run flags for this test and.w RunMode(a5),d1 ;is this OK for RBI? beq.s @nextTest ;go to next test if not moveq.l #0,d1 ;prepare to get the test ID move.w TLTest_ID(a0),d1 ;get the test ID move.l d1,(a1)+ ;and put that in our test list move.l d0,(a1)+ ;then put the subtest in our local list addq.w #1,RBINumInst(a5) ;increment the number of installed subtests cmp.w #MaxCTESubTests,RBINumInst(a5) ;are we at our max? beq.s @QuitCTEInit ;quit if so @nextTest adda.w #sizeofCPUTestList,a0 ;go to next bra.s @InstallDTMIDs ENDWITH @QuitCTEInit rts ;----------------------------------------------------------------------------------- LogFailure ;----------------------------------------------------------------------------------- ; ; LogFailure - log a test failure to PRAM ; ; This routine assumes that registers a0-a2 are already set up ; ;----------------------------------------------------------------------------------- WITH RBIInfo, RBIPRAMMap ; bsr InvalidUpper ;Before updating PRAM, we need to invalidate the signature addq.w #1,BIFailFlag(a5) ;increase BIFailFlag;failure occured move.w BICounter(a5),BILastF(a5) ;BILastF = BICounter _CopyRAMToPRAM BILastF(a5),#pLastFail,#2 ; _CopyRAMToPRAM BITest(a5),#pTestNum,#4 ; _CopyRAMToPRAM BISubTest(a5),#pSubTestNum,#4 ; _CopyRAMToPRAM BITestResult(a5),#pTestResult,#4 ; tst.w BIFirstF(a5) ;is it first failure? bne.s @LogDone ;no, do not log BIFirstF move.w BICounter(a5),BIFirstF(a5) ;initialize where first failure occured _CopyRAMToPRAM BIFirstF(a5),#pFrstFail,#2 ; @LogDone move.l CurTest(a5),d2 ;modify the tests failed mask or.l d2,BIFailMask(a5) ;with the current failing test _CopyRAMToPRAM BIFailMask(a5),#pFailmask,#4 ;go log failure mask ; bsr UpperToLower ;Copy the upper half of PRAM to the lower half ENDWITH rts ;--------------------------------------------------------------------------- ; ; RBI critical tests ; ;--------------------------------------------------------------------------- RBICritTests dc.l BIRomTest-RBICritTests ;ROM critical test dc.l BIRamTest-RBICritTests ;RAM Critical test dc.l BILongRAMTest-RBICritTests ;moving inversions critical RAM test dc.l 0 ;--------------------------------------------------------------------------- ; BIRomTest ;--------------------------------------------------------------------------- BIRomTest moveq #0,d6 ;clear the result register move.l #1,BISubtest(a5) ;this is subtest 1 - byte lane ROM checksum move.l a5,-(sp) ;save the globals pointer BSR6 RomTest ;execute the test move.l (sp)+,a5 ;and restore the globals pointer tst.l d6 ;any error? bne.s @SumError ;exit if so move.l #2,BISubtest(a5) ;this is subtest 2 - startup ROM checksum move.l a5,-(sp) ;save the globals pointer BSR6 StartUpRomTest ;execute the test move.l (sp)+,a5 ;restore the globals pointer tst.l d6 ;check for errors bne.s @Test2Failure ;go report it if so clr.l BITestResult(a5) ;else, clear BIStatus bra.s @SumError ;and exit @Test2Failure move.l d1,d6 ;for StartUpRomTest, eor result is in d1 @SumError ;clear BIStatus move.l d6,BITestResult(a5) ;CCDDDEEFF error code if any <6> rts ; <6> ;----------------------- to next above ;--------------------------------------------------------------------------- ; BIRamTest ; For Mod3Test and ExtRamTest, must setup ; a0 = pointer to bottom test area ; a1 = pointer to top test area+1 ; ; For DataBusTest, ; a0 = address pointer to test the data bus ;--------------------------------------------------------------------------- ; From here to the next <6> label above was changed for new test numbers ;and new calling conventions and new RBI tests WITH USTGlobals BIRamTest moveq #0,d6 ;clear d6 movea.l ChunkTable(a5),a3 ;get the chunk table @cont movea.l (a3)+,a0 ;Get the base addr of next bank to test cmpa.l #-1,a0 ;is it -1? beq.s @testspassed ;all done if so movea.l (a3)+,a1 ;Get the size of this bank adda.l a0,a1 ;calculate the end address cmpa.l a1,sp ;make sure we don't destroy our stack, etc bhi.s @test ;continue if OK move.l sp,d0 ;else, stop here andi.b #$FC,d0 ;Make sure we're long word aligned movea.l d0,a1 @test move.l #1,BISubtest(a5) ;this is subtest 1 - mod 3 test BSR6 Mod3Test ;run the test tst.l d6 ;any error? bne.s RAMError ;exit if so move.l #2,BISubtest(a5) ;this is subtest 2 - reverse mod 3 test BSR6 RevMod3Test ;run the test tst.l d6 ;any error? bne.s RAMError ;exit if so move.l #3,BISubtest(a5) ;this is subtest 3 - extended RAM test BSR6 ExtRamTest ;run the test tst.l d6 ;any error? bne.s RAMError ;exit if so bra.s @cont ;otherwise, keep working your way through the chunk table ;------------------------------------- @testspassed movea.l ChunkTable(a5),a0 ;Get pointer to first bank movea.l (a0),a0 ; lea $100(a0),a0 ;point into memory, just in case. move.l #4,BISubtest(a5) ;this is subtest 4 - data bus test BSR6 DataBusTest ; move.l d6,BITestResult(a5) ;CCDDEEFF error code if any rts ; RAMError movea.l ChunkTable(a5),a1 ;Point to the memory info area suba.l a1,a3 ;Calculate where we are in the map move.l a3,d0 ;Get it into a data reg lsl.l #1,d0 ;Mult by 2 ($10=bankA, $20=bankB) or.l d0,BISubtest(a5) ;Put it in the high nibble of subtest # move.l d6,BITestResult(a5) ;CCDDEEFF error code rts ; ;--------------------------------------------------------------------------- ; BILongRAMTest - Performs the moving inversions RAM test. ; Note that this test takes a very long time! ; ; For MovInvTest, must setup ; a0 = pointer to bottom test area ; a1 = pointer to top test area+1 ; ;--------------------------------------------------------------------------- BILongRAMTest ; move.l a5,-(sp) ;save a5 moveq.l #0,d6 ;clear d6 move.l #1,BISubtest(a5) ;this is subtest 1 - moving inversions RAM test movea.l ChunkTable(a5),a3 ;get the chunk table @cont movea.l (a3)+,a0 ;Get the base addr of next bank to test cmpa.l #-1,a0 ;is it -1? beq.s @endtest ;all done if so movea.l (a3)+,a1 ;Get the size of this bank move.l a3,-(sp) ;save a3 adda.l a0,a1 ;calculate the end address cmpa.l a1,sp ;make sure we don't destroy our stack, etc bhi.s @test ;continue if OK move.l sp,d0 ;else, stop here andi.b #$FC,d0 ;Make sure we're long word aligned movea.l d0,a1 @test BSR6 MovInvTest ;Execute the test move.l (sp)+,a3 ;restore a3 tst.l d6 ;any error? beq.s @cont ;Continue if not move.l (sp)+,a5 ;Get the saved a5 <12> bra.s RAMError ;And error out <12> ;------------------------------------- @endtest move.l (sp)+,a5 ;restore a5 move.l d6,BITestResult(a5) ;No errors rts ; ENDWITH ;________________________________________________________________________________________ ; ; Routine: WrXByte ; ; Inputs: A1 - pointer to ProductInfo record for this machine ; A2 - VIA1 base address ; A6 - caller's return address (BSR6) ; D1 - address of PRAM byte to read ; D2 - byte to write to PRAM ; ; Outputs: none ; ; Trashes: A0,A3,A4,A5,A6,D0,D1,D2 ; ; Function: writes a byte of extended PRAM at the specified address ;________________________________________________________________________________________ WrXByte MOVEA.L A1,A3 ; point to this machine's product info

ADDA.L ProductInfo.ClockPRAMPtr(A3),A3 ; and get the address of its clock/PRAM table

MOVE.L 4*cpWrXByte(A3),D0 ; get the offset to the routine

BEQ.S @NoEntry ; -> this function is not supported

ADDA.L D0,A3 ; calculate the routine's address

EXG A6,A3 ; save return address in A3, put routine's address in A6

@NoEntry JMP (A6) ; and either call the routine or just return

;________________________________________________________________________________________ ; ; Routine: RdXByte ; ; Inputs: A1 - pointer to ProductInfo record for this machine ; A2 - VIA1 base address ; A6 - return address (BSR6) ; D1 - address of PRAM byte to read ; ; Outputs: D1 - byte read from PRAM ; ; Trashes: A0,A3,A4,A5,A6,D0,D2 ; ; Function: reads a byte of extended PRAM at the specified address ;________________________________________________________________________________________ RdXByte MOVEA.L A1,A3 ; point to this machine's product info

ADDA.L ProductInfo.ClockPRAMPtr(A3),A3 ; and get the address of its clock/PRAM table

MOVE.L 4*cpRdXByte(A3),D0 ; get the offset to the routine

BEQ.S @NoEntry ; -> this function is not supported

ADDA.L D0,A3 ; calculate the routine's address

EXG A6,A3 ; save return address in A3, put routine's address in A6

@NoEntry JMP (A6) ; and either call the routine or just return

;________________________________________________________________________________________ ; ; Routine: ClkWPOff ; ; Inputs: A1 - pointer to ProductInfo record for this machine ; A2 - VIA1 base address ; A6 - return address (BSR6) ; ; Outputs: none ; ; Trashes: D0,D1,D2,A0,A5,A6 ; ; Function: write-enables the clock chip if supported by the clock implementation ;________________________________________________________________________________________ ClkWPOff MOVEA.L A1,A5 ; point to this machine's product info

ADDA.L ProductInfo.ClockPRAMPtr(A5),A5 ; and get the address of its clock/PRAM table

MOVE.L 4*cpWrProtOff(A5),D0 ; get the offset to the routine

BEQ.S @NoEntry ; -> this function is not supported

ADDA.L D0,A5 ; calculate the routine's address

EXG A6,A5 ; save return address in A5, put routine's address in A6

@NoEntry JMP (A6) ; and either call the routine or just return

ENDPROC ;