mirror of
https://github.com/elliotnunn/mac-rom.git
synced 2024-09-16 09:59:01 +00:00
4325cdcc78
Resource forks are included only for .rsrc files. These are DeRezzed into their data fork. 'ckid' resources, from the Projector VCS, are not included. The Tools directory, containing mostly junk, is also excluded.
433 lines
16 KiB
Plaintext
433 lines
16 KiB
Plaintext
;
|
|
; File: HWElemsControl.a
|
|
;
|
|
; Contains: HW Floating Point ELEMS68K ("PACK 5") that calls MC68881/882 or uses MC68040 FPU
|
|
;
|
|
; Written by: Apple Numerics Group, DSG
|
|
;
|
|
; Copyright: © 1985-1992 by Apple Computer, Inc., all rights reserved.
|
|
;
|
|
; Change History (most recent first):
|
|
;
|
|
; <SM2> 2/3/92 CSS Update from Horror: see mod history from below 06 Apr 92, 08 Apr 92.
|
|
; <2> 11/2/91 DTY Put the End after the EndIf.
|
|
; <1> 10/24/91 SAM/KSM Rolled in Regatta file.
|
|
;
|
|
; Regatta Change History:
|
|
;
|
|
; <2> 5/28/91 SAM Merged from TERROR sources. [<2> Fixed a bug in ANNUITY and
|
|
; COMPOUND that used to allow the possibility of spuriously
|
|
; signaling an invalid exception under reasonable circumstances.
|
|
; <1> 5/15/91 SAM Split off from TERROR Proj.
|
|
;
|
|
; Terror Change History:
|
|
;
|
|
; <1> 01/06/91 BG Added to TERROR/BBS for the time.
|
|
;
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; File: 881Elemscontrol.a
|
|
;; Implementation of Elems68K for machines using the Motorola 68881/2
|
|
;; Copyright Apple Computer, Inc. 1985-7,1989-92
|
|
;; All Rights Reserved
|
|
;; Confidential and Proprietary to Apple Computer,Inc.
|
|
;;
|
|
;; Written by Clayton Lewis, from the (unshipped) file ChipElems881.a
|
|
;;
|
|
;; Modification history:
|
|
;; Rev1: 17 May 89 Resurrection from ChipElems881.a begins.
|
|
;; 25 Sep 90 96-bit test for tiny fixed; version updated. -SMcD
|
|
;; 01 Oct 90 made MAIN & END conditional assembly for ROM & INIT ... ali
|
|
;; 01 Oct 90 chnaged the label STICKMAP to STICKYMAP2 to avoid
|
|
;; collision with pack 4's STICKYMAP ... ali
|
|
;; 05 Oct 90 merged SMcDÕs conditional assembly scheme for
|
|
;; ÔMAIN ... ENDÕ pair
|
|
;; 05 Apr 91 for financial functions only, a jump over the first argument
|
|
;; fetch was added. thanks to jon okada for patiently single
|
|
;; stepping to find the problem. ... ali
|
|
;; 06 Apr 92 changed version number to 9, updated copyright dates. ... JPO
|
|
;; 08 Apr 92 used FMOVEM to move result in FP0 to memory. ... JPO
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; The stack looks like:
|
|
;; _______________________________________________
|
|
;; | |
|
|
;; | address of argument 3 (if any) | - Long
|
|
;; |_______________________________________________|
|
|
;; | |
|
|
;; | address of argument 2 (if any) | - Long
|
|
;; |_______________________________________________|
|
|
;; | |
|
|
;; | address of argument 1 | - Long
|
|
;; |_______________________________________________|
|
|
;; | |
|
|
;; | opcode (NOTE: change to bit 7 !!!) | - Word
|
|
;; |_______________________________________________|
|
|
;; | |
|
|
;; | return address | - Long
|
|
;; |_______________________________________________|
|
|
;;
|
|
;; The number of addresses depends on the operation. xpwri and xpwry use 2.
|
|
;; compound and annuity use 3. All the rest use only 1.
|
|
;;
|
|
;; Opcode - If bit 7 is set, all extended data must be in 96-bit format.
|
|
;; If bit 7 is clear, all extended data must be in 80-bit format.
|
|
;;
|
|
;; Save registers D0/D1 & A0/A4 & FP2/FP3, and later save FPSR and FPCR.
|
|
;;
|
|
;; | |
|
|
;; | stack as passed |
|
|
;; |_______________________________________________|
|
|
;; | |
|
|
;; | saved D0/D1 & A0/A4 & FP2/FP3 | - 10 Longs
|
|
;; |_______________________________________________|
|
|
;; | |
|
|
;; | saved FPSR | - Long
|
|
;; |_______________________________________________|
|
|
;; | |
|
|
;; | saved FPCR | - Long
|
|
;; |_______________________________________________|
|
|
;;
|
|
;; Conventions - routines getting control through this file may assume:
|
|
;;
|
|
;; ¥ A0 contains the address of the input argument
|
|
;; ¥ A4 contains the address of the exit routine - to exit, use: JMP (A4)
|
|
;; ¥ FP0 contains the input argument itself
|
|
;; ¥ D0 & D1 are junk from routine dispatch
|
|
;; ¥ FP0 must contain the result value for delivery to common exit routines
|
|
;; ¥ routines must exit with stack unchanged!!!
|
|
;; ¥ xpwri, xpwry, compound, annuity are special cases handling own data and exit
|
|
;; ¥ Preserved registers are listed in HWElemsEqus.a
|
|
;; ¥ Routines which must know whether data is 80-bit or 96-bit can test bit 7
|
|
;; of the opcode which is at Opcode+1(SP) immediately after dispatch.
|
|
;;
|
|
;; Except - no registers are preset for annuity and compound.
|
|
;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
IF &TYPE('InitMe') = 'UNDEFINED' THEN ; to make ½SANE an INIT, MAIN & END must be
|
|
MAIN ; commented out. they are necessary for the
|
|
ENDIF ; the ROM built. ( and also regular testing )
|
|
|
|
BLANKS ON
|
|
STRING ASIS
|
|
|
|
; DATA MAIN
|
|
; ORG -$100
|
|
CODE
|
|
|
|
PRINT ON
|
|
MACHINE MC68020
|
|
MC68881
|
|
|
|
INCLUDE 'HWElemsEqus.a'
|
|
|
|
FPState EQU $A4A ; floating point state [6 bytes]
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;This is the sole entry point of the package.
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
EXPORT ELEMS881
|
|
ELEMS881
|
|
; IF DebugON THEN
|
|
; DC.W $A9FF
|
|
; ENDIF
|
|
|
|
BRA.S Elemsbegin
|
|
|
|
DC.W $00 ; MAC SPECIFIC STUFF
|
|
STRING ASIS
|
|
DC.B 'PACK'
|
|
DC.W $5
|
|
DC.W $0009 ; VERSION 9 <4/6/92, JPO>
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Beginning of code.
|
|
;; Reserve space, save registers, execute procentry, and dispatch to called routine
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
Elemsbegin
|
|
MOVEM.L Regs,-(SP) ; save working registers
|
|
FMOVEM FPRegs,-(SP)
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; ProcEntry
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
FMOVE.L FPSR,-(SP) ; Save status register (high memory)
|
|
FMOVE.L FPCR,-(SP) ; and control register (low memory)
|
|
|
|
MOVEQ #0,D1
|
|
FMOVE.L D1,FPCR ; IEEE default rounding, precision, halts
|
|
FMOVE.L D1,FPSR ; clear exceptions (condition code and Quo)
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Move the (extended) destination value to the chip and select appropriate
|
|
;; exit sequence. If it is a financial function, then skip the first fetch <T2>
|
|
;; since it is going to be a destination for the financials. <T2>
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
MOVEA.L Addr1(SP),A0 ; destination address in A0
|
|
; MOVE.B Opcode+1(SP),D0
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; <T2> thru next <T2>
|
|
;; Added this section to handle financials separately. For these functions the
|
|
;; first argument is a destination and at the start may hold garbage including
|
|
;; SNaN. For the 68040, it may slow down the financials since the garbage may
|
|
;; be an unnormalized number, which will trap to the software... ali
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
MOVE.W Opcode(SP),D0 ; grab the entire opword
|
|
BTST #IsItFinan,D0 ; high byte of opword is $C0 if financial
|
|
BEQ.S @2 ; if not, go get the first operand
|
|
BTST #flag96bit,D0 ; it is a financial, find out if 80 or 96 bit
|
|
BEQ.S @3 ; branch to unpack 80-bit input data
|
|
LEA Out96,A4 ; else just take 96-bit input data
|
|
BRA.S LiftOff
|
|
@3
|
|
LEA Out80,A4
|
|
BRA.S LiftOff
|
|
@2
|
|
BTST #flag96bit,D0 ; if not continue, find out if 80 or 96 bit
|
|
BEQ.S @1 ; branch to unpack 80-bit input data
|
|
; BPL.S @1 ; branch to unpack 80-bit input data
|
|
MOVE.W 4(A0),2(A0) ; replicate significant word into junk word <9/25/90-S.McD>
|
|
LEA Out96,A4 ; else just take 96-bit input data <T2>
|
|
FMOVE.X (A0),FP0
|
|
BRA.S LiftOff
|
|
@1
|
|
LEA Out80,A4
|
|
ADDQ.L #6,A0
|
|
MOVE.L (A0),-(SP)
|
|
MOVE.L -(A0),-(SP)
|
|
SUBQ.L #2,A0
|
|
MOVE.L (A0),-(SP)
|
|
|
|
FMOVE.X (SP)+,FP0
|
|
|
|
LiftOff
|
|
MOVE.W #OPMASK,D1
|
|
AND.B D0,D1
|
|
MOVE.W ElemsTab(D1),D1
|
|
JMP LiftOff(D1)
|
|
ElemsTab
|
|
DC.W RLnx-LiftOff
|
|
DC.W RLog2x-LiftOff
|
|
DC.W RLn1x-LiftOff
|
|
DC.W RLog21x-LiftOff
|
|
DC.W RExpx-LiftOff
|
|
DC.W RExp2x-LiftOff
|
|
DC.W RExp1x-LiftOff
|
|
DC.W RExp21x-LiftOff
|
|
|
|
DC.W RXpwri-LiftOff
|
|
DC.W RXpwry-LiftOff
|
|
DC.W RCompound-LiftOff
|
|
DC.W RAnnuity-LiftOff
|
|
|
|
DC.W RSin-LiftOff
|
|
DC.W RCos-LiftOff
|
|
DC.W RTan-LiftOff
|
|
DC.W RAtan-LiftOff
|
|
DC.W RRandom-LiftOff
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Exit code
|
|
;; All one argument routines exit through here
|
|
;; A4 contains the address of the appropriate exit routine (80 or 96-bit extended)
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
Out80
|
|
|
|
MOVEA.L Addr1(SP),A0 ; destination address in A0
|
|
; FMOVE.X FP0,-(SP) ; move result to stack - DELETED <4/8/92, JPO>
|
|
FMOVEM.X FP0,-(SP) ; move result to stack <4/8/92, JPO>
|
|
|
|
MOVE.W (SP)+,(A0)+ ; move result to memory
|
|
ADDQ.L #2,SP
|
|
MOVE.L (SP)+,(A0)+
|
|
MOVE.L (SP)+,(A0)
|
|
BRA.S Exit
|
|
|
|
Out96
|
|
MOVEA.L Addr1(SP),A0 ; destination address in A0
|
|
; FMOVE.X FP0,(A0) ; deliver result - DELETED <4/8/92, JPO>
|
|
FMOVEM.X FP0,(A0) ; deliver result <4/8/92, JPO>
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Adjust the pending exceptions
|
|
;; D0.hi MUST contain set bits for each exception to be set
|
|
;; D0.lo MUST contain set bits for each exception to be cleared
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
Exit
|
|
MOVEQ #-1,D1 ; set mask
|
|
EOR.B D1,D0 ; exception bits to be cleared now in D0.lo
|
|
FMOVE FPSR,D1 ; collect current exceptions
|
|
AND.B D0,D1 ; clear unwanted exceptions
|
|
SWAP D0 ; exceptions to be set now in D0.lo
|
|
OR.B D1,D0 ; set desired exceptions
|
|
*
|
|
* The next step is handled in the Procexit code below, so it is commented out.
|
|
* FMOVE D0,FPSR ; record pending exceptions from this "operation"
|
|
*
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Procexit
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
*
|
|
* This is a private PROCEXIT for ½SANE881's PACK5. Since all
|
|
* elementary function exceptions are triggered by PROCEXIT,
|
|
* PROCEXIT is blamed for all of PACK5's exceptions.
|
|
*
|
|
* This code performs the halt mechanism steps discussed on p.168
|
|
* of the Apple Numerics Manual, Second Edition. Figure 23-1
|
|
* (Stack frame for halt), p.169, is quite helpful.
|
|
*
|
|
* This code was written to be as independent as possible from PACK4.
|
|
* ½SANE881 PACK5 will run stand-alone without ½SANE881 PACK4 being present
|
|
* except because ½SANE881 PACK4 always runs with OPERR trapping enabled,
|
|
* the user's invalid halt enable is stored as (BSUN or SNAN), where BSUN,
|
|
* SNAN, and OPERR are, respectively, the three high bits of the eight FPU
|
|
* trap enables. See Figure 2-2 (MC68881/MC68882 FPCR Exception Enable Byte),
|
|
* p.2-2, in Motorola's 881/2 Manual.
|
|
*
|
|
* That is, halt on invalid is enabled if and only if BSUN or SNAN is set in FPCR.
|
|
*
|
|
* OPERR trapping is assumed enabled so PACK4 can override the FPU's default
|
|
* return behavior for operand errors, i.e. so PACK4 can deliver SANE's style
|
|
* of floating-point and integer NaN codes.
|
|
*
|
|
* BEFORE:
|
|
* STACK: FPCR< FPSR< D0_D1/A0_A4/FP2_FP3< RTS< opcode< &DST< &SRC< &SRC2
|
|
*
|
|
* AFTER:
|
|
* STACK: D0_D1/A0_A4/FP2_FP3< RTS< opcode< &DST< &SRC< &SRC2
|
|
*
|
|
* SIDE-EFFECT: Signals the accrued exceptions currently set on the FPU
|
|
* after restoring the saved FPCR & FPSR values on the stack.
|
|
*
|
|
* SCRATCHED: A0, D0, D1.
|
|
*
|
|
OR.B D0,7(SP) ; 'or' in stickies into saved FPSR on stack
|
|
MOVE.B 2(SP),D1 ; d1.b := saved FPCR trap enables byte
|
|
|
|
FMOVEM (SP)+,FPCR/FPSR ; restore saved environment (cc not affected)
|
|
*
|
|
* Since OPERR is always enabled, the following branch will never be taken,
|
|
* so it is commented out.
|
|
* BEQ.S PastHalter ; trap enables all zero? then skip haltcheck
|
|
*
|
|
BCLR #5,D1 ; prepare to map invalid enable onto OPERR bit
|
|
BFTST D1{24:2} ; bsun or snan set? (invalid halt enabled?)
|
|
BEQ.S @1 ; if not, leave OPERR bit clear
|
|
BSET #5,D1 ; otherwise, set OPERR bit
|
|
@1:
|
|
AND #%00111110,D1 ; any halts enabled? (ignores INEX1 for BCD)
|
|
; (note: clears high-byte of d1.w for mask)
|
|
BEQ.S PastHalter ; if none enabled then skip halt check
|
|
LSL.B #2,D1 ; left justify halt enables in byte
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Halt check.
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
AND D1,D0 ; d0.b := any halt-enable intersections?
|
|
; (note: clears high-byte of d0.w)
|
|
BEQ.S PastHalter ; if no intersections, skip halt code
|
|
LSR.B #3,D0 ; d0.w := 5 intersection bits right justified
|
|
|
|
HaltCode
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Make the FP68K halt stack frame.
|
|
;; Save & restore A0 (end of stack frame ptr).
|
|
;; Set up stack frame:
|
|
;; ------------------
|
|
;; Pending D0 - long
|
|
;; -----------------
|
|
;; Pending CCR - word
|
|
;; ------------------
|
|
;; halts&xcps - word < ----
|
|
;; ------------------ ^
|
|
;; MISC rec ptr ----------->
|
|
;; ------------------
|
|
;; SRC2 addr - long
|
|
;; ------------------
|
|
;; SRC addr - long
|
|
;; ------------------
|
|
;; DST addr - long
|
|
;; ------------------
|
|
;; opcode - word
|
|
;; ------------------
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
CLR.L -(SP) ; push bogus pending D0 for PROCEXIT
|
|
CLR.W -(SP) ; push bogus pending CCR for PROCEXIT
|
|
MOVE.B STICKYMAP2(D0),D0 ; d0.w := halts&xcps mapped to SANE order
|
|
MOVE.W D0,-(SP) ; push Halt exceptions (note: high-byte clear)
|
|
|
|
MOVE.L SP,-(SP) ; push pointer to stuff now on stack (MISC REC)
|
|
|
|
MOVE.L Addr3-8+4+2+2+4(SP),-(SP) ; push &SRC2
|
|
MOVE.L Addr2-8+4+2+2+4+4(SP),-(SP) ; push &SRC
|
|
MOVE.L Addr1-8+4+2+2+4+4+4(SP),-(SP) ; push &DST
|
|
MOVE.W Opcode-8+4+2+2+4+4+4+4(SP),-(SP) ; push ELEM's Opcode
|
|
BSET #7,(SP) ; make it distinguishable from all FP Opcodes
|
|
|
|
MOVEA.L #FPState+2,A0
|
|
MOVEA.L (A0),A0 ; haltvector
|
|
JSR (A0) ; call user's halt routine
|
|
; user pops halt frame
|
|
ADDQ #8,SP ; we pop misc rec
|
|
|
|
PastHalter
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Stack cleanup and return
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
FMOVEM (SP)+,FPRegs
|
|
MOVEM.L (SP)+,Regs ; restore registers
|
|
|
|
RTD #6 ; return, leaving stack clear
|
|
|
|
STICKYMAP2: ; maps FPU's 5 accrued bits to SANE order
|
|
; SANE's bits read backwards with OVFL<-->UNFL
|
|
DC.B 0,16,8,24,2,18,10,26,4,20,12,28,6,22,14,30
|
|
DC.B 1,17,9,25,3,19,11,27,5,21,13,29,7,23,15,31
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Here is an alternative way of producing the random number ( following the ;;
|
|
;; same algorithm ). This method allows the operations to be carried out ;;
|
|
;; all in the integer domain, which is good; that is for now. The bad news ;;
|
|
;; is that the old method is still the more efficient way of computing the ;;
|
|
;; random function on a 68040! sorry. ... ali, received from crl and stu. ;; ;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;
|
|
;; seed: d0
|
|
;; d1 <- random
|
|
;; MULU.L #16807,D1:D0
|
|
;; DIVU.L #$7FFFFFFF,D1:D0
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Functions of one argument
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
RRandom
|
|
FMUL.W #16807,FP0 ; 7^5 * x
|
|
MOVEQ #0,D0 ; report no exceptions
|
|
FMOVE.L #RANDMODULUS,FP1 ; 2^31-1
|
|
FREM FP1,FP0 ; (7^5 * x) rem (2^31-1)
|
|
FBGE.W @1 ; branch if positive
|
|
FADD FP1,FP0 ; else add 2^31-1 to make positive
|
|
@1
|
|
JMP (A4)
|
|
|
|
|
|
INCLUDE 'HWElemsExp.a'
|
|
INCLUDE 'HWElemsFinancial.a'
|
|
INCLUDE 'HWElemsCommon.a'
|
|
INCLUDE 'HWElemsLogs.a'
|
|
INCLUDE 'HWElemsTg.a'
|
|
INCLUDE 'HWElemsArcTg.a'
|
|
INCLUDE 'HWElemsSinCos.a'
|
|
|
|
INCLUDE 'HWElemsCoeffs.a'
|
|
|
|
IF &TYPE('InitMe') = 'UNDEFINED' THEN ; to make ½SANE an INIT, MAIN & END must be
|
|
; Why include a whole file to execute one pseudo-op? Seems kinda silly, doesn't it?
|
|
; INCLUDE '881ELEMSend.a' ; commented out. they are necessary for the
|
|
ENDIF ; the ROM built. ( and also regular testing )
|
|
END
|