CPU-Z80-Disassembler

 view release on metacpan or  search on metacpan

t/data/zx48_benchmark.asm  view on Meta::CPAN

PLOT_END:
        ld (hl), a              ; load byte to the screen.
        jp PO_ATTR              ; exit to PO-ATTR to set colours for cell.


; ------------------------------
; Put two numbers in BC register
; ------------------------------
;
;

;; STK-TO-BC

STK_TO_BC:
        call STK_TO_A           ; routine STK-TO-A
        ld b, a
        push bc
        call STK_TO_A           ; routine STK-TO-A
        ld e, c
        pop bc
        ld d, c
        ld c, a
        ret


; -----------------------
; Put stack in A register
; -----------------------
; This routine puts the last value on the calculator stack into the accumulator
; deleting the last value.

;; STK-TO-A

STK_TO_A:
        call FP_TO_A            ; routine FP-TO-A compresses last value into
                                ; accumulator. e.g. PI would become 3.
                                ; zero flag set if positive.
        jp c, REPORT_Bc         ; jump forward to REPORT-Bc if >= 255.5.

        ld c, $01               ; prepare a positive sign byte.
        ret z                   ; return if FP-TO-BC indicated positive.

        ld c, $FF               ; prepare negative sign byte and
        ret                     ; return.



; --------------------
; THE 'CIRCLE' COMMAND
; --------------------
;   "Goe not Thou about to Square eyther circle" -
;   - John Donne, Cambridge educated theologian, 1624
;
;   The CIRCLE command draws a circle as a series of straight lines.
;   In some ways it can be regarded as a polygon, but the first line is drawn 
;   as a tangent, taking the radius as its distance from the centre.
;
;   Both the CIRCLE algorithm and the ARC drawing algorithm make use of the
;   'ROTATION FORMULA' (see later).  It is only necessary to work out where 
;   the first line will be drawn and how long it is and then the rotation 
;   formula takes over and calculates all other rotated points.
;
;   All Spectrum circles consist of two vertical lines at each side and two 
;   horizontal lines at the top and bottom. The number of lines is calculated
;   from the radius of the circle and is always divisible by 4. For complete 
;   circles it will range from 4 for a square circle to 32 for a circle of 
;   radius 87. The Spectrum can attempt larger circles e.g. CIRCLE 0,14,255
;   but these will error as they go off-screen after four lines are drawn.
;   At the opposite end, CIRCLE 128,88,1.23 will draw a circle as a perfect 3x3
;   square using 4 straight lines although very small circles are just drawn as 
;   a dot on the screen.
;
;   The first chord drawn is the vertical chord on the right of the circle.
;   The starting point is at the base of this chord which is drawn upwards and
;   the circle continues in an anti-clockwise direction. As noted earlier the 
;   x-coordinate of this point measured from the centre of the circle is the 
;   radius. 
;
;   The CIRCLE command makes extensive use of the calculator and as part of
;   process of drawing a large circle, free memory is checked 1315 times.
;   When drawing a large arc, free memory is checked 928 times.
;   A single call to 'sin' involves 63 memory checks and so values of sine 
;   and cosine are pre-calculated and held in the mem locations. As a 
;   clever trick 'cos' is derived from 'sin' using simple arithmetic operations
;   instead of the more expensive 'cos' function.
;
;   Initially, the syntax has been partly checked using the class for the DRAW 
;   command which stacks the origin of the circle (X,Y).

;; CIRCLE

CIRCLE:
        rst $18                 ; GET-CHAR              x, y.
        cp $2C                  ; Is character the required comma ?
        jp nz, REPORT_C         ; Jump, if not, to REPORT-C
                                ; 'Nonsense in basic'

        rst $20                 ; NEXT-CHAR advances the parsed character address.
        call EXPT_1NUM          ; routine EXPT-1NUM stacks radius in runtime.
        call CHECK_END          ; routine CHECK-END will return here in runtime
                                ; if nothing follows the command.

;   Now make the radius positive and ensure that it is in floating point form 
;   so that the exponent byte can be accessed for quick testing.

        rst $28                 ; ; FP-CALC              x, y, r.
        defb $2A                ; ;abs                   x, y, r.
        defb $3D                ; ;re-stack              x, y, r.
        defb $38                ; ;end-calc              x, y, r.

        ld a, (hl)              ; Fetch first, floating-point, exponent byte.
        cp $81                  ; Compare to one.
        jr nc, C_R_GRE_1        ; Forward to C-R-GRE-1
                                ; if circle radius is greater than one.

;    The circle is no larger than a single pixel so delete the radius from the
;    calculator stack and plot a point at the centre.

        rst $28                 ; ; FP-CALC              x, y, r.
        defb $02                ; ;delete                x, y.
        defb $38                ; ;end-calc              x, y.

t/data/zx48_benchmark.asm  view on Meta::CPAN


;   The ARC will consist of multiple straight lines so call the CIRCLE-DRAW
;   PARAMETERS ROUTINE to pre-calculate sine values from the angle (in mem-5)
;   and determine also the number of straight lines from that value and the
;   'diameter' which is at the top of the calculator stack.

;; DR-PRMS

DR_PRMS:
        call CD_PRMS1           ; routine CD-PRMS1

                                ; mem-0 ; (A)/No. of lines (=a) (step angle)
                                ; mem-1 ; sin(a/2) 
                                ; mem-2 ; -
                                ; mem-3 ; cos(a)                        const
                                ; mem-4 ; sin(a)                        const
                                ; mem-5 ; Angle of rotation (A)         in
                                ; B     ; Count of straight lines - max 252.

        push bc                 ; Save the line count on the machine stack.

;   Remove the now redundant diameter value D.

        rst $28                 ; ; FP-CALC      x, y, sin(A/2), D.
        defb $02                ; ;delete        x, y, sin(A/2).

;   Dividing the sine of the step angle by the sine of the total angle gives
;   the length of the initial chord on a unary circle. This factor f is used
;   to scale the coordinates of the first line which still points in the 
;   direction of the end point and may be larger.

        defb $E1                ; ;get-mem-1     x, y, sin(A/2), sin(a/2)
        defb $01                ; ;exchange      x, y, sin(a/2), sin(A/2)
        defb $05                ; ;division      x, y, sin(a/2)/sin(A/2)
        defb $C1                ; ;st-mem-1      x, y. f.
        defb $02                ; ;delete        x, y.

;   With the factor stored, scale the x coordinate first.

        defb $01                ; ;exchange      y, x.
        defb $31                ; ;duplicate     y, x, x.
        defb $E1                ; ;get-mem-1     y, x, x, f.
        defb $04                ; ;multiply      y, x, x*f    (=xx)
        defb $C2                ; ;st-mem-2      y, x, xx.
        defb $02                ; ;delete        y. x.

;   Now scale the y coordinate.

        defb $01                ; ;exchange      x, y.
        defb $31                ; ;duplicate     x, y, y.
        defb $E1                ; ;get-mem-1     x, y, y, f
        defb $04                ; ;multiply      x, y, y*f    (=yy)

;   Note. 'sin' and 'cos' trash locations mem-0 to mem-2 so fetch mem-2 to the 
;   calculator stack for safe keeping.

        defb $E2                ; ;get-mem-2     x, y, yy, xx.

;   Once we get the coordinates of the first straight line then the 'ROTATION
;   FORMULA' used in the arc loop will take care of all other points, but we
;   now use a variation of that formula to rotate the first arc through (A-a)/2
;   radians. 
;   
;       xRotated = y * sin(angle) + x * cos(angle)
;       yRotated = y * cos(angle) - x * sin(angle)
;
 
        defb $E5                ; ;get-mem-5     x, y, yy, xx, A.
        defb $E0                ; ;get-mem-0     x, y, yy, xx, A, a.
        defb $03                ; ;subtract      x, y, yy, xx, A-a.
        defb $A2                ; ;stk-half      x, y, yy, xx, A-a, 1/2.
        defb $04                ; ;multiply      x, y, yy, xx, (A-a)/2. (=angle)
        defb $31                ; ;duplicate     x, y, yy, xx, angle, angle.
        defb $1F                ; ;sin           x, y, yy, xx, angle, sin(angle)
        defb $C5                ; ;st-mem-5      x, y, yy, xx, angle, sin(angle)
        defb $02                ; ;delete        x, y, yy, xx, angle

        defb $20                ; ;cos           x, y, yy, xx, cos(angle).

;   Note. mem-0, mem-1 and mem-2 can be used again now...

        defb $C0                ; ;st-mem-0      x, y, yy, xx, cos(angle).
        defb $02                ; ;delete        x, y, yy, xx.

        defb $C2                ; ;st-mem-2      x, y, yy, xx.
        defb $02                ; ;delete        x, y, yy.

        defb $C1                ; ;st-mem-1      x, y, yy.
        defb $E5                ; ;get-mem-5     x, y, yy, sin(angle)
        defb $04                ; ;multiply      x, y, yy*sin(angle).
        defb $E0                ; ;get-mem-0     x, y, yy*sin(angle), cos(angle)
        defb $E2                ; ;get-mem-2     x, y, yy*sin(angle), cos(angle), xx.
        defb $04                ; ;multiply      x, y, yy*sin(angle), xx*cos(angle).
        defb $0F                ; ;addition      x, y, xRotated.
        defb $E1                ; ;get-mem-1     x, y, xRotated, yy.
        defb $01                ; ;exchange      x, y, yy, xRotated.
        defb $C1                ; ;st-mem-1      x, y, yy, xRotated.
        defb $02                ; ;delete        x, y, yy.

        defb $E0                ; ;get-mem-0     x, y, yy, cos(angle).
        defb $04                ; ;multiply      x, y, yy*cos(angle).
        defb $E2                ; ;get-mem-2     x, y, yy*cos(angle), xx.
        defb $E5                ; ;get-mem-5     x, y, yy*cos(angle), xx, sin(angle).
        defb $04                ; ;multiply      x, y, yy*cos(angle), xx*sin(angle).
        defb $03                ; ;subtract      x, y, yRotated.
        defb $C2                ; ;st-mem-2      x, y, yRotated.

;   Now the initial x and y coordinates are made positive and summed to see 
;   if they measure up to anything significant.

        defb $2A                ; ;abs           x, y, yRotated'.
        defb $E1                ; ;get-mem-1     x, y, yRotated', xRotated.
        defb $2A                ; ;abs           x, y, yRotated', xRotated'.
        defb $0F                ; ;addition      x, y, yRotated+xRotated.
        defb $02                ; ;delete        x, y.

        defb $38                ; ;end-calc      x, y.

;   Although the test value has been deleted it is still above the calculator
;   stack in memory and conveniently DE which points to the first free byte
;   addresses the exponent of the test value.

t/data/zx48_benchmark.asm  view on Meta::CPAN

        defb $01                ; ;exchange      tx, y.
        defb $38                ; ;end-calc      tx, y.

        ld a, (COORDS_hi)       ; Fetch System Variable COORDS-y
        call STACK_A            ; routine STACK-A

        rst $28                 ; ; FP-CALC      tx, y, last-y.

;   Store the last point plotted to initialize the moving ay value.

        defb $C5                ; ;st-mem-5      tx, y, last-y.
        defb $0F                ; ;addition      tx, ty.

;   Fetch the moving ax and ay to the calculator stack.

        defb $E0                ; ;get-mem-0     tx, ty, ax.
        defb $E5                ; ;get-mem-5     tx, ty, ax, ay.
        defb $38                ; ;end-calc      tx, ty, ax, ay.

        pop bc                  ; Restore the straight line count.

; -----------------------------------
; THE 'CIRCLE/DRAW CONVERGENCE POINT'
; -----------------------------------
;   The CIRCLE and ARC-DRAW commands converge here. 
;
;   Note. for both the CIRCLE and ARC commands the minimum initial line count 
;   is 4 (as set up by the CD_PARAMS routine) and so the zero flag will never 
;   be set and the loop is always entered.  The first test is superfluous and
;   the jump will always be made to ARC-START.

;; DRW-STEPS

DRW_STEPS:
        dec b                   ; decrement the arc count (4,8,12,16...).

        jr z, ARC_END           ; forward, if zero (not possible), to ARC-END

        jr ARC_START            ; forward to ARC-START


; --------------
; THE 'ARC LOOP'
; --------------
;
;   The arc drawing loop will draw up to 31 straight lines for a circle and up 
;   251 straight lines for an arc between two points. In both cases the final
;   closing straight line is drawn at ARC_END, but it otherwise loops back to 
;   here to calculate the next coordinate using the ROTATION FORMULA where (a)
;   is the previously calculated, constant CENTRAL ANGLE of the arcs.
;
;       Xrotated = x * cos(a) - y * sin(a)
;       Yrotated = x * sin(a) + y * cos(a)
;
;   The values cos(a) and sin(a) are pre-calculated and held in mem-3 and mem-4 
;   for the duration of the routine.
;   Memory location mem-1 holds the last relative x value (rx) and mem-2 holds
;   the last relative y value (ry) used by DRAW.
;
;   Note. that this is a very clever twist on what is after all a very clever,
;   well-used formula.  Normally the rotation formula is used with the x and y
;   coordinates from the centre of the circle (or arc) and a supplied angle to 
;   produce two new x and y coordinates in an anticlockwise direction on the 
;   circumference of the circle.
;   What is being used here, instead, is the relative X and Y parameters from
;   the last point plotted that are required to get to the current point and 
;   the formula returns the next relative coordinates to use. 

;; ARC-LOOP

ARC_LOOP:
        rst $28                 ; ; FP-CALC
        defb $E1                ; ;get-mem-1     rx.
        defb $31                ; ;duplicate     rx, rx.
        defb $E3                ; ;get-mem-3     cos(a)
        defb $04                ; ;multiply      rx, rx*cos(a).
        defb $E2                ; ;get-mem-2     rx, rx*cos(a), ry.
        defb $E4                ; ;get-mem-4     rx, rx*cos(a), ry, sin(a).
        defb $04                ; ;multiply      rx, rx*cos(a), ry*sin(a).
        defb $03                ; ;subtract      rx, rx*cos(a) - ry*sin(a)
        defb $C1                ; ;st-mem-1      rx, new relative x rotated.
        defb $02                ; ;delete        rx.

        defb $E4                ; ;get-mem-4     rx, sin(a).
        defb $04                ; ;multiply      rx*sin(a)
        defb $E2                ; ;get-mem-2     rx*sin(a), ry.
        defb $E3                ; ;get-mem-3     rx*sin(a), ry, cos(a).
        defb $04                ; ;multiply      rx*sin(a), ry*cos(a).
        defb $0F                ; ;addition      rx*sin(a) + ry*cos(a).
        defb $C2                ; ;st-mem-2      new relative y rotated.
        defb $02                ; ;delete        .
        defb $38                ; ;end-calc      .

;   Note. the calculator stack actually holds   tx, ty, ax, ay
;   and the last absolute values of x and y 
;   are now brought into play.
;
;   Magically, the two new rotated coordinates rx and ry are all that we would
;   require to draw a circle or arc - on paper!
;   The Spectrum DRAW routine draws to the rounded x and y coordinate and so 
;   repetitions of values like 3.49 would mean that the fractional parts 
;   would be lost until eventually the draw coordinates might differ from the 
;   floating point values used above by several pixels.
;   For this reason the accurate offsets calculated above are added to the 
;   accurate, absolute coordinates maintained in ax and ay and these new 
;   coordinates have the integer coordinates of the last plot position 
;   ( from System Variable COORDS ) subtracted from them to give the relative 
;   coordinates required by the DRAW routine.

;   The mid entry point.

;; ARC-START

ARC_START:
        push bc                 ; Preserve the arc counter on the machine stack.

;   Store the absolute ay in temporary variable mem-0 for the moment.

        rst $28                 ; ; FP-CALC      ax, ay.
        defb $C0                ; ;st-mem-0      ax, ay.
        defb $02                ; ;delete        ax.

;   Now add the fractional relative x coordinate to the fractional absolute
;   x coordinate to obtain a new fractional x-coordinate.

        defb $E1                ; ;get-mem-1     ax, xr.
        defb $0F                ; ;addition      ax+xr (= new ax).

t/data/zx48_benchmark.asm  view on Meta::CPAN

;   stored in the 'mem' locations.  Some are not relevant for the circle.

;; DRAW-SAVE

DRAW_SAVE:
        push af                 ; Save the line count (A) on the machine stack.

        call STACK_A            ; Routine STACK-A stacks the modified count(A).

        rst $28                 ; ; FP-CALC      z, A.
        defb $E5                ; ;get-mem-5     z, A, ANGLE.
        defb $01                ; ;exchange      z, ANGLE, A.
        defb $05                ; ;division      z, ANGLE/A. (Angle/count = a)
        defb $31                ; ;duplicate     z, a, a.

;  Note. that cos (a) could be formed here directly using 'cos' and stored in 
;  mem-3 but that would spoil a good story and be slightly slower, as also 
;  would using square roots to form cos (a) from sin (a).

        defb $1F                ; ;sin           z, a, sin(a)
        defb $C4                ; ;st-mem-4      z, a, sin(a)
        defb $02                ; ;delete        z, a.
        defb $31                ; ;duplicate     z, a, a.
        defb $A2                ; ;stk-half      z, a, a, 1/2.
        defb $04                ; ;multiply      z, a, a/2.
        defb $1F                ; ;sin           z, a, sin(a/2).

;   Note. after second sin, mem-0 and mem-1 become free.

        defb $C1                ; ;st-mem-1      z, a, sin(a/2).
        defb $01                ; ;exchange      z, sin(a/2), a.
        defb $C0                ; ;st-mem-0      z, sin(a/2), a.  (for arc only)

;   Now form cos(a) from sin(a/2) using the 'DOUBLE ANGLE FORMULA'.

        defb $02                ; ;delete        z, sin(a/2).
        defb $31                ; ;duplicate     z, sin(a/2), sin(a/2).
        defb $04                ; ;multiply      z, sin(a/2)*sin(a/2).
        defb $31                ; ;duplicate     z, sin(a/2)*sin(a/2),
                                ; ;                           sin(a/2)*sin(a/2).
        defb $0F                ; ;addition      z, 2*sin(a/2)*sin(a/2).
        defb $A1                ; ;stk-one       z, 2*sin(a/2)*sin(a/2), 1.
        defb $03                ; ;subtract      z, 2*sin(a/2)*sin(a/2)-1.

        defb $1B                ; ;negate        z, 1-2*sin(a/2)*sin(a/2).

        defb $C3                ; ;st-mem-3      z, cos(a).
        defb $02                ; ;delete        z.
        defb $38                ; ;end-calc      z.

;   The radius/diameter is left on the calculator stack.

        pop bc                  ; Restore the line count to the B register.

        ret                     ; Return.


; --------------------------
; THE 'DOUBLE ANGLE FORMULA'
; --------------------------
;   This formula forms cos(a) from sin(a/2) using simple arithmetic.
;
;   THE GEOMETRIC PROOF OF FORMULA   cos (a) = 1 - 2 * sin(a/2) * sin(a/2)
;                                                                    
;                                                                   
;                                            A                     
;                                                                 
;                                         . /|\                      
;                                     .    / | \                     
;                                  .      /  |  \                    
;                               .        /   |a/2\                   
;                            .          /    |    \                  
;                         .          1 /     |     \                 
;                      .              /      |      \                
;                   .                /       |       \               
;                .                  /        |        \              
;             .  a/2             D / a      E|-+       \             
;          B ---------------------/----------+-+--------\ C
;            <-         1       -><-       1           ->           
;
;   cos a = 1 - 2 * sin(a/2) * sin(a/2)
;
;   The figure shows a right triangle that inscribes a circle of radius 1 with
;   centre, or origin, D.  Line BC is the diameter of length 2 and A is a point 
;   on the circle. The periphery angle BAC is therefore a right angle by the 
;   Rule of Thales.
;   Line AC is a chord touching two points on the circle and the angle at the 
;   centre is (a).
;   Since the vertex of the largest triangle B touches the circle, the 
;   inscribed angle (a/2) is half the central angle (a).
;   The cosine of (a) is the length DE as the hypotenuse is of length 1.
;   This can also be expressed as 1-length CE.  Examining the triangle at the
;   right, the top angle is also (a/2) as angle BAE and EBA add to give a right
;   angle as do BAE and EAC.
;   So cos (a) = 1 - AC * sin(a/2) 
;   Looking at the largest triangle, side AC can be expressed as 
;   AC = 2 * sin(a/2)   and so combining these we get 
;   cos (a) = 1 - 2 * sin(a/2) * sin(a/2).
;
;   "I will be sufficiently rewarded if when telling it to others, you will 
;    not claim the discovery as your own, but will say it is mine."
;   - Thales, 640 - 546 B.C.
;
; --------------------------
; THE 'LINE DRAWING' ROUTINE
; --------------------------
;
;

;; DRAW-LINE

DRAW_LINE:
        call STK_TO_BC          ; routine STK-TO-BC
        ld a, c
        cp b
        jr nc, DL_X_GE_Y        ; to DL-X-GE-Y

        ld l, c
        push de
        xor a
        ld e, a

t/data/zx48_benchmark.asm  view on Meta::CPAN


; ---

; ->
;; S-FN

S_FN:
        jp S_FN_SBRN            ; jump forward to S-FN-SBRN.


; --------------------------------------------------------------------
;
;   RANDOM THEORY from the ZX81 manual by Steven Vickers
;
;   (same algorithm as the ZX Spectrum).
; 
;   Chapter 5. Exercise 6. (For mathematicians only.)
;
;   Let p be a [large] prime, & let a be a primitive root modulo p.
;   Then if b_i is the residue of a^i modulo p (1<=b_i<p-1), the 
;   sequence             
;   
;                           (b_i-1)/(p-1)
;               
;   is a cyclical sequence of p-1 distinct numbers in the range 0 to 1
;   (excluding 1). By choosing a suitably, these can be made to look 
;   fairly random.
;
;     65537 is a Mersenne prime 2^16-1. Note.
;
;   Use this, & Gauss' law of quadratic reciprocity, to show that 75 
;   is a primitive root modulo 65537.
;
;     The ZX81 uses p=65537 & a=75, & stores some b_i-1 in memory. 
;   The function RND involves replacing b_i-1 in memory by b_(i+1)-1, 
;   & yielding the result (b_(i+1)-1)/(p-1). RAND n (with 1<=n<=65535)
;   makes b_i equal to n+1.
;
; --------------------------------------------------------------------
;
; Steven Vickers writing in comp.sys.sinclair on 20-DEC-1993
; 
;   Note. (Of course, 65537 is 2^16 + 1, not -1.)
;
;   Consider arithmetic modulo a prime p. There are p residue classes, and the
;   non-zero ones are all invertible. Hence under multiplication they form a
;   group (Fp*, say) of order p-1; moreover (and not so obvious) Fp* is cyclic.
;   Its generators are the "primitive roots". The "quadratic residues modulo p"
;   are the squares in Fp*, and the "Legendre symbol" (d/p) is defined (when p
;   does not divide d) as +1 or -1, according as d is or is not a quadratic
;   residue mod p.
;
;   In the case when p = 65537, we can show that d is a primitive root if and
;   only if it's not a quadratic residue. For let w be a primitive root, d
;   congruent to w^r (mod p). If d is not primitive, then its order is a proper
;   factor of 65536: hence w^{32768*r} = 1 (mod p), so 65536 divides 32768*r,
;   and hence r is even and d is a square (mod p). Conversely, the squares in
;   Fp* form a subgroup of (Fp*)^2 of index 2, and so cannot be generators.
;
;   Hence to check whether 75 is primitive mod 65537, we want to calculate that
;   (75/65537) = -1. There is a multiplicative formula (ab/p) = (a/p)(b/p) (mod
;   p), so (75/65537) = (5/65537)^2 * (3/65537) = (3/65537). Now the law of
;   quadratic reciprocity says that if p and q are distinct odd primes, then
;
;    (p/q)(q/p) = (-1)^{(p-1)(q-1)/4}
;
;   Hence (3/65537) = (65537/3) * (-1)^{65536*2/4} = (65537/3)
;            = (2/3)  (because 65537 = 2 mod 3)
;            = -1
;
;   (I referred to Pierre Samuel's "Algebraic Theory of Numbers".)
;
; ->

;; S-RND

S_RND:
        call SYNTAX_Z           ; routine SYNTAX-Z
        jr z, S_RND_END         ; forward to S-RND-END if checking syntax.

        ld bc, (SEED)           ; fetch system variable SEED
        call STACK_BC           ; routine STACK-BC places on calculator stack

        rst $28                 ; ; FP-CALC           ;s.
        defb $A1                ; ;stk-one            ;s,1.
        defb $0F                ; ;addition           ;s+1.
        defb $34                ; ;stk-data           ;
        defb $37                ; ;Exponent: $87,
                                ; ;Bytes: 1
        defb $16                ; ;(+00,+00,+00)      ;s+1,75.
        defb $04                ; ;multiply           ;(s+1)*75 = v
        defb $34                ; ;stk-data           ;v.
        defb $80                ; ;Bytes: 3
        defb $41, $00, $00, $80 ; ;Exponent $91
                                ; ;(+00)              ;v,65537.
        defb $32                ; ;n-mod-m            ;remainder, result.
        defb $02                ; ;delete             ;remainder.
        defb $A1                ; ;stk-one            ;remainder, 1.
        defb $03                ; ;subtract           ;remainder - 1. = rnd
        defb $31                ; ;duplicate          ;rnd,rnd.
        defb $38                ; ;end-calc

        call FP_TO_BC           ; routine FP-TO-BC
        ld (SEED), bc           ; store in SEED for next starting point.
        ld a, (hl)              ; fetch exponent
        and a                   ; is it zero ?
        jr z, S_RND_END         ; forward if so to S-RND-END

        sub $10                 ; reduce exponent by 2^16
        ld (hl), a              ; place back

;; S-RND-END

S_RND_END:
        jr S_PI_END             ; forward to S-PI-END


; ---

; the number PI 3.14159...

t/data/zx48_benchmark.asm  view on Meta::CPAN

        defb $33                ; ;jump
        defb $03                ; ;to L37FA, CASES

;; SMALL

SMALL:
        rst $28                 ; ; FP-CALC
        defb $A0                ; ;stk-zero

;; CASES

CASES:
        defb $01                ; ;exchange
        defb $31                ; ;duplicate
        defb $31                ; ;duplicate
        defb $04                ; ;multiply
        defb $31                ; ;duplicate
        defb $0F                ; ;addition
        defb $A1                ; ;stk-one
        defb $03                ; ;subtract
        defb $8C                ; ;series-0C
        defb $10                ; ;Exponent: $60, Bytes: 1
        defb $B2                ; ;(+00,+00,+00)
        defb $13                ; ;Exponent: $63, Bytes: 1
        defb $0E                ; ;(+00,+00,+00)
        defb $55                ; ;Exponent: $65, Bytes: 2
        defb $E4, $8D           ; ;(+00,+00)
        defb $58                ; ;Exponent: $68, Bytes: 2
        defb $39, $BC           ; ;(+00,+00)
        defb $5B                ; ;Exponent: $6B, Bytes: 2
        defb $98, $FD           ; ;(+00,+00)
        defb $9E                ; ;Exponent: $6E, Bytes: 3
        defb $00, $36, $75      ; ;(+00)
        defb $A0                ; ;Exponent: $70, Bytes: 3
        defb $DB, $E8, $B4      ; ;(+00)
        defb $63                ; ;Exponent: $73, Bytes: 2
        defb $42, $C4           ; ;(+00,+00)
        defb $E6                ; ;Exponent: $76, Bytes: 4
        defb $B5, $09, $36, $BE ; ;
        defb $E9                ; ;Exponent: $79, Bytes: 4
        defb $36, $73, $1B, $5D ; ;
        defb $EC                ; ;Exponent: $7C, Bytes: 4
        defb $D8, $DE, $63, $BE ; ;
        defb $F0                ; ;Exponent: $80, Bytes: 4
        defb $61, $A1, $B3, $0C ; ;
        defb $04                ; ;multiply
        defb $0F                ; ;addition
        defb $38                ; ;end-calc

        ret                     ; return.



; ---------------------
; THE 'ARCSIN' FUNCTION
; ---------------------
; (Offset $22: 'asn')
;   The inverse sine function with result in radians.
;   Derived from arctan function above.
;   Error A unless the argument is between -1 and +1 inclusive.
;   Uses an adaptation of the formula asn(x) = atn(x/sqr(1-x*x))
;
;
;                 /|
;                / |
;              1/  |x
;              /a  |
;             /----|    
;               y
;
;   e.g. We know the opposite side (x) and hypotenuse (1) 
;   and we wish to find angle a in radians.
;   We can derive length y by Pythagoras and then use ATN instead. 
;   Since y*y + x*x = 1*1 (Pythagoras Theorem) then
;   y=sqr(1-x*x)                         - no need to multiply 1 by itself.
;   So, asn(a) = atn(x/y)
;   or more fully,
;   asn(a) = atn(x/sqr(1-x*x))

;   Close but no cigar.

;   While PRINT ATN (x/SQR (1-x*x)) gives the same results as PRINT ASN x,
;   it leads to division by zero when x is 1 or -1.
;   To overcome this, 1 is added to y giving half the required angle and the 
;   result is then doubled. 
;   That is, PRINT ATN (x/(SQR (1-x*x) +1)) *2
;
;   GEOMETRIC PROOF.
;
;
;               . /|
;            .  c/ |
;         .     /1 |x
;      . c   b /a  |
;    ---------/----|    
;      1      y
;
;   By creating an isosceles triangle with two equal sides of 1, angles c and 
;   c are also equal. If b+c+c = 180 degrees and b+a = 180 degrees then c=a/2.
;
;   A value higher than 1 gives the required error as attempting to find  the
;   square root of a negative number generates an error in Sinclair BASIC.

;; asn

asn:
        rst $28                 ; ; FP-CALC      x.
        defb $31                ; ;duplicate     x, x.
        defb $31                ; ;duplicate     x, x, x.
        defb $04                ; ;multiply      x, x*x.
        defb $A1                ; ;stk-one       x, x*x, 1.
        defb $03                ; ;subtract      x, x*x-1.
        defb $1B                ; ;negate        x, 1-x*x.
        defb $28                ; ;sqr           x, sqr(1-x*x) = y
        defb $A1                ; ;stk-one       x, y, 1.
        defb $0F                ; ;addition      x, y+1.
        defb $05                ; ;division      x/y+1.
        defb $24                ; ;atn           a/2       (half the angle)
        defb $31                ; ;duplicate     a/2, a/2.
        defb $0F                ; ;addition      a.
        defb $38                ; ;end-calc      a.



( run in 1.916 second using v1.01-cache-2.11-cpan-39bf76dae61 )