CPU-Z80-Disassembler

 view release on metacpan or  search on metacpan

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


        xor b                   ; switch the pixel
        cpl                     ; restore other 7 bits

;; PLOT-END
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,0x01               ; prepare a positive sign byte.
        ret z                   ; return if FP-TO-BC indicated positive.

        ld c,0xFF               ; 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 0x18                ; GET-CHAR              x, y.
        cp 0x2C                 ; Is character the required comma ?
        jp nz,REPORT_C          ; Jump, if not, to REPORT-C
                                ; 'Nonsense in basic'

        rst 0x20                ; 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 0x28                ; ; FP-CALC              x, y, r.
        defb 0x2A               ; ;abs                   x, y, r.
        defb 0x3D               ; ;re-stack              x, y, r.
        defb 0x38               ; ;end-calc              x, y, r.

        ld a,(hl)               ; Fetch first, floating-point, exponent byte.
        cp 0x81                 ; 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 0x28                ; ; FP-CALC              x, y, r.
        defb 0x02               ; ;delete                x, y.
        defb 0x38               ; ;end-calc              x, y.

t/data/zx48_base.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 0x28                ; ; FP-CALC      x, y, sin(A/2), D.
        defb 0x02               ; ;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 0xE1               ; ;get-mem-1     x, y, sin(A/2), sin(a/2)
        defb 0x01               ; ;exchange      x, y, sin(a/2), sin(A/2)
        defb 0x05               ; ;division      x, y, sin(a/2)/sin(A/2)
        defb 0xC1               ; ;st-mem-1      x, y. f.
        defb 0x02               ; ;delete        x, y.

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

        defb 0x01               ; ;exchange      y, x.
        defb 0x31               ; ;duplicate     y, x, x.
        defb 0xE1               ; ;get-mem-1     y, x, x, f.
        defb 0x04               ; ;multiply      y, x, x*f    (=xx)
        defb 0xC2               ; ;st-mem-2      y, x, xx.
        defb 0x02               ; ;delete        y. x.

;   Now scale the y coordinate.

        defb 0x01               ; ;exchange      x, y.
        defb 0x31               ; ;duplicate     x, y, y.
        defb 0xE1               ; ;get-mem-1     x, y, y, f
        defb 0x04               ; ;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 0xE2               ; ;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 0xE5               ; ;get-mem-5     x, y, yy, xx, A.
        defb 0xE0               ; ;get-mem-0     x, y, yy, xx, A, a.
        defb 0x03               ; ;subtract      x, y, yy, xx, A-a.
        defb 0xA2               ; ;stk-half      x, y, yy, xx, A-a, 1/2.
        defb 0x04               ; ;multiply      x, y, yy, xx, (A-a)/2. (=angle)
        defb 0x31               ; ;duplicate     x, y, yy, xx, angle, angle.
        defb 0x1F               ; ;sin           x, y, yy, xx, angle, sin(angle)
        defb 0xC5               ; ;st-mem-5      x, y, yy, xx, angle, sin(angle)
        defb 0x02               ; ;delete        x, y, yy, xx, angle

        defb 0x20               ; ;cos           x, y, yy, xx, cos(angle).

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

        defb 0xC0               ; ;st-mem-0      x, y, yy, xx, cos(angle).
        defb 0x02               ; ;delete        x, y, yy, xx.

        defb 0xC2               ; ;st-mem-2      x, y, yy, xx.
        defb 0x02               ; ;delete        x, y, yy.

        defb 0xC1               ; ;st-mem-1      x, y, yy.
        defb 0xE5               ; ;get-mem-5     x, y, yy, sin(angle)
        defb 0x04               ; ;multiply      x, y, yy*sin(angle).
        defb 0xE0               ; ;get-mem-0     x, y, yy*sin(angle), cos(angle)
        defb 0xE2               ; ;get-mem-2     x, y, yy*sin(angle), cos(angle), xx.
        defb 0x04               ; ;multiply      x, y, yy*sin(angle), xx*cos(angle).
        defb 0x0F               ; ;addition      x, y, xRotated.
        defb 0xE1               ; ;get-mem-1     x, y, xRotated, yy.
        defb 0x01               ; ;exchange      x, y, yy, xRotated.
        defb 0xC1               ; ;st-mem-1      x, y, yy, xRotated.
        defb 0x02               ; ;delete        x, y, yy.

        defb 0xE0               ; ;get-mem-0     x, y, yy, cos(angle).
        defb 0x04               ; ;multiply      x, y, yy*cos(angle).
        defb 0xE2               ; ;get-mem-2     x, y, yy*cos(angle), xx.
        defb 0xE5               ; ;get-mem-5     x, y, yy*cos(angle), xx, sin(angle).
        defb 0x04               ; ;multiply      x, y, yy*cos(angle), xx*sin(angle).
        defb 0x03               ; ;subtract      x, y, yRotated.
        defb 0xC2               ; ;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 0x2A               ; ;abs           x, y, yRotated'.
        defb 0xE1               ; ;get-mem-1     x, y, yRotated', xRotated.
        defb 0x2A               ; ;abs           x, y, yRotated', xRotated'.
        defb 0x0F               ; ;addition      x, y, yRotated+xRotated.
        defb 0x02               ; ;delete        x, y.

        defb 0x38               ; ;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_base.asm  view on Meta::CPAN

        defb 0xC0               ; ;st-mem-0      y, x, last-x.
        defb 0x0F               ; ;addition      y, absolute x.
        defb 0x01               ; ;exchange      tx, y.
        defb 0x38               ; ;end-calc      tx, y.

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

        rst 0x28                ; ; FP-CALC      tx, y, last-y.

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

        defb 0xC5               ; ;st-mem-5      tx, y, last-y.
        defb 0x0F               ; ;addition      tx, ty.

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

        defb 0xE0               ; ;get-mem-0     tx, ty, ax.
        defb 0xE5               ; ;get-mem-5     tx, ty, ax, ay.
        defb 0x38               ; ;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 0x28                ; ; FP-CALC
        defb 0xE1               ; ;get-mem-1     rx.
        defb 0x31               ; ;duplicate     rx, rx.
        defb 0xE3               ; ;get-mem-3     cos(a)
        defb 0x04               ; ;multiply      rx, rx*cos(a).
        defb 0xE2               ; ;get-mem-2     rx, rx*cos(a), ry.
        defb 0xE4               ; ;get-mem-4     rx, rx*cos(a), ry, sin(a).
        defb 0x04               ; ;multiply      rx, rx*cos(a), ry*sin(a).
        defb 0x03               ; ;subtract      rx, rx*cos(a) - ry*sin(a)
        defb 0xC1               ; ;st-mem-1      rx, new relative x rotated.
        defb 0x02               ; ;delete        rx.

        defb 0xE4               ; ;get-mem-4     rx, sin(a).
        defb 0x04               ; ;multiply      rx*sin(a)
        defb 0xE2               ; ;get-mem-2     rx*sin(a), ry.
        defb 0xE3               ; ;get-mem-3     rx*sin(a), ry, cos(a).
        defb 0x04               ; ;multiply      rx*sin(a), ry*cos(a).
        defb 0x0F               ; ;addition      rx*sin(a) + ry*cos(a).
        defb 0xC2               ; ;st-mem-2      new relative y rotated.
        defb 0x02               ; ;delete        .
        defb 0x38               ; ;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 0x28                ; ; FP-CALC      ax, ay.
        defb 0xC0               ; ;st-mem-0      ax, ay.
        defb 0x02               ; ;delete        ax.

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

        defb 0xE1               ; ;get-mem-1     ax, xr.
        defb 0x0F               ; ;addition      ax+xr (= new ax).
        defb 0x31               ; ;duplicate     ax, ax.
        defb 0x38               ; ;end-calc      ax, ax.

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


;   For both arcs and circles, constants derived from the central angle are
;   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 0x28                ; ; FP-CALC      z, A.
        defb 0xE5               ; ;get-mem-5     z, A, ANGLE.
        defb 0x01               ; ;exchange      z, ANGLE, A.
        defb 0x05               ; ;division      z, ANGLE/A. (Angle/count = a)
        defb 0x31               ; ;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 0x1F               ; ;sin           z, a, sin(a)
        defb 0xC4               ; ;st-mem-4      z, a, sin(a)
        defb 0x02               ; ;delete        z, a.
        defb 0x31               ; ;duplicate     z, a, a.
        defb 0xA2               ; ;stk-half      z, a, a, 1/2.
        defb 0x04               ; ;multiply      z, a, a/2.
        defb 0x1F               ; ;sin           z, a, sin(a/2).

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

        defb 0xC1               ; ;st-mem-1      z, a, sin(a/2).
        defb 0x01               ; ;exchange      z, sin(a/2), a.
        defb 0xC0               ; ;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 0x02               ; ;delete        z, sin(a/2).
        defb 0x31               ; ;duplicate     z, sin(a/2), sin(a/2).
        defb 0x04               ; ;multiply      z, sin(a/2)*sin(a/2).
        defb 0x31               ; ;duplicate     z, sin(a/2)*sin(a/2),
                                ; ;                           sin(a/2)*sin(a/2).
        defb 0x0F               ; ;addition      z, 2*sin(a/2)*sin(a/2).
        defb 0xA1               ; ;stk-one       z, 2*sin(a/2)*sin(a/2), 1.
        defb 0x03               ; ;subtract      z, 2*sin(a/2)*sin(a/2)-1.

        defb 0x1B               ; ;negate        z, 1-2*sin(a/2)*sin(a/2).

        defb 0xC3               ; ;st-mem-3      z, cos(a).
        defb 0x02               ; ;delete        z.
        defb 0x38               ; ;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
        jr DL_LARGER            ; to DL-LARGER

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

        rst 0x20                ; NEXT-CHAR
        jp S_CONT_2             ; jump forward to S-CONT-2          ===>

; ---

; ->
;; 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,(0x5C76)          ; fetch system variable SEED
        call STACK_BC           ; routine STACK-BC places on calculator stack

        rst 0x28                ; ; FP-CALC           ;s.
        defb 0xA1               ; ;stk-one            ;s,1.
        defb 0x0F               ; ;addition           ;s+1.
        defb 0x34               ; ;stk-data           ;
        defb 0x37               ; ;Exponent: $87,
                                ; ;Bytes: 1
        defb 0x16               ; ;(+00,+00,+00)      ;s+1,75.
        defb 0x04               ; ;multiply           ;(s+1)*75 = v
        defb 0x34               ; ;stk-data           ;v.
        defb 0x80               ; ;Bytes: 3
        defb 0x41               ; ;Exponent $91
        defb 0x00,0x00,0x80     ; ;(+00)              ;v,65537.
        defb 0x32               ; ;n-mod-m            ;remainder, result.
        defb 0x02               ; ;delete             ;remainder.
        defb 0xA1               ; ;stk-one            ;remainder, 1.
        defb 0x03               ; ;subtract           ;remainder - 1. = rnd
        defb 0x31               ; ;duplicate          ;rnd,rnd.
        defb 0x38               ; ;end-calc

        call FP_TO_BC           ; routine FP-TO-BC
        ld (0x5C76),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 0x10                ; 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...

; ->
;; S-PI
S_PI:

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

        defb 0x03               ; ;to L37FA, CASES

;; SMALL
SMALL:
        rst 0x28                ; ; FP-CALC
        defb 0xA0               ; ;stk-zero

;; CASES
CASES:
        defb 0x01               ; ;exchange
        defb 0x31               ; ;duplicate
        defb 0x31               ; ;duplicate
        defb 0x04               ; ;multiply
        defb 0x31               ; ;duplicate
        defb 0x0F               ; ;addition
        defb 0xA1               ; ;stk-one
        defb 0x03               ; ;subtract
        defb 0x8C               ; ;series-0C
        defb 0x10               ; ;Exponent: $60, Bytes: 1
        defb 0xB2               ; ;(+00,+00,+00)
        defb 0x13               ; ;Exponent: $63, Bytes: 1
        defb 0x0E               ; ;(+00,+00,+00)
        defb 0x55               ; ;Exponent: $65, Bytes: 2
        defb 0xE4,0x8D          ; ;(+00,+00)
        defb 0x58               ; ;Exponent: $68, Bytes: 2
        defb 0x39,0xBC          ; ;(+00,+00)
        defb 0x5B               ; ;Exponent: $6B, Bytes: 2
        defb 0x98,0xFD          ; ;(+00,+00)
        defb 0x9E               ; ;Exponent: $6E, Bytes: 3
        defb 0x00,0x36,0x75     ; ;(+00)
        defb 0xA0               ; ;Exponent: $70, Bytes: 3
        defb 0xDB,0xE8,0xB4     ; ;(+00)
        defb 0x63               ; ;Exponent: $73, Bytes: 2
        defb 0x42,0xC4          ; ;(+00,+00)
        defb 0xE6               ; ;Exponent: $76, Bytes: 4
        defb 0xB5,0x09,0x36,0xBE
                                ; ;
        defb 0xE9               ; ;Exponent: $79, Bytes: 4
        defb 0x36,0x73,0x1B,0x5D
                                ; ;
        defb 0xEC               ; ;Exponent: $7C, Bytes: 4
        defb 0xD8,0xDE,0x63,0xBE
                                ; ;
        defb 0xF0               ; ;Exponent: $80, Bytes: 4
        defb 0x61,0xA1,0xB3,0x0C
                                ; ;
        defb 0x04               ; ;multiply
        defb 0x0F               ; ;addition
        defb 0x38               ; ;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 0x28                ; ; FP-CALC      x.
        defb 0x31               ; ;duplicate     x, x.
        defb 0x31               ; ;duplicate     x, x, x.
        defb 0x04               ; ;multiply      x, x*x.
        defb 0xA1               ; ;stk-one       x, x*x, 1.
        defb 0x03               ; ;subtract      x, x*x-1.
        defb 0x1B               ; ;negate        x, 1-x*x.
        defb 0x28               ; ;sqr           x, sqr(1-x*x) = y
        defb 0xA1               ; ;stk-one       x, y, 1.
        defb 0x0F               ; ;addition      x, y+1.
        defb 0x05               ; ;division      x/y+1.
        defb 0x24               ; ;atn           a/2       (half the angle)
        defb 0x31               ; ;duplicate     a/2, a/2.
        defb 0x0F               ; ;addition      a.
        defb 0x38               ; ;end-calc      a.



( run in 2.561 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )