; =========================================================
; An Assembly Listing of the "Shoulders of Giants" ZX81 ROM
; =========================================================
; -------------------------
; Last updated: 23-OCT-2003
; -------------------------
;
;   The "Shoulders of Giants" ZX81 ROM.
;   This file shows the altered sections of the ZX81/TS1000 ROM that produced 
;   the customized sg81.rom.
;   The main feature is the inclusion of Newton Raphson square roots.
;   The square roots are executed 3 times faster than those in the 
;   standard ROM. They are more accurate also and
;
;   PRINT SQR 100 = INT SQR 100 gives the result 1 (true) not 0 (false)
;
;   The input and storage of fractional numbers is improved
;
;   PRINT 1/2 = .5 gives the result 1 (true) and not 0 (false) 
;
;   The output of fractional numbers to the ZX Printer is corrected
;
;   LPRINT .00001 gives the output .00001 and not .0XYZ1
;
;   Other alterations have been made to create the space required by the
;   new square root routine and some are obscure and would not otherwise have 
;   been made.
;   Using uncompressed constants rectifies a logic error and improves speed.

#define DEFB .BYTE      ; TASM cross-assembler definitions
#define DEFW .WORD
#define EQU  .EQU
#define ORG  .ORG


;   the backward references
					
subtract   EQU     $174C           ; SUBTRACT
multiply   EQU     $176C           ; multiply
division   EQU     $1882           ; division
addition   EQU     $1755           ; addition
truncate   EQU     $18E4           ; truncate
e_to_fp    EQU     $155A           ; e-to-fp
TEST_ROOM  EQU     $0EC5           ; TEST-ROOM
FIND_INT   EQU     $0EA7           ; FIND-INT
STACK_A    EQU     $151D           ; STACK-A
STACK_BC   EQU     $1520           ; STACK-BC
STK_FETCH  EQU     $13F8           ; STK-FETCH
STK_STO_s  EQU     $12C3           ; STK-STO-$
FP_TO_A    EQU     $15CD           ; FP-TO-A
CLASS_06   EQU     $0D92           ; CLASS-06
CHECK_2    EQU     $0D22           ; CHECK-2
SCANNING   EQU     $0F55           ; SCANNING
PRINT_FP   EQU     $15DB           ; PRINT-FP

; -----------------------------------------------------------------------------

ORG $0010

;--------------------------------
; THE 'PRINT A CHARACTER' RESTART
;--------------------------------
; This restart prints the character in the accumulator using the alternate
; register set so there is no requirement to save the main registers.
; There is sufficient room available to separate a space (zero) from other
; characters as leading spaces need not be considered with a space.
; Note. the accumulator is preserved only when printing to the screen.

PRINT_A   AND   A               ; test for zero - space.
          JP    NZ,PRINT_CH     ; jump forward if not to PRINT-CH.

          JP    PRINT_SP        ; jump forward to PRINT-SP.

; ---

          DEFB  $01             ;+ unused location. Version. PRINT PEEK 23

; -----------------------------------------------------------------------------

ORG $0028


; -----------------------  
; THE 'CALCULATE' RESTART
; -----------------------  
;   An immediate jump is made to the CALCULATE routine the address of which 
;   has changed.

FP_CALC   JP    CALCULATE       ;+ jump to the NEW calculate routine address.

end_calc  POP   AF              ; drop the calculator return address RE-ENTRY
          EXX                   ; switch to the other set.

          EX    (SP),HL         ; transfer H'L' to machine stack for the
                                ; return address.
                                ; when exiting recursion then the previous
                                ; pointer is transferred to H'L'.

          EXX                   ; back to main set.
          RET                   ; return.

; -----------------------------------------------------------------------------

ORG	$13AE 

; ------------------------
; THE 'L-ENTER' SUBROUTINE
; ------------------------
;   Part of the LET command contains a natural subroutine which is a 
;   conditional LDIR. The copy only occurs of BC is non-zero.

L_ENTER   EX    DE,HL           ;


COND_MV   LD    A,B             ;
          OR    C               ;
          RET   Z               ;

          PUSH  DE              ;

          LDIR                  ; Copy Bytes

          POP   HL              ;
          RET                   ; Return.

; -----------------------------------------------------------------------------

ORG $14E5

; ---------------------
; THE 'NEXT DIGIT' LOOP
; ---------------------
;   Within the 'DECIMAL TO FLOATING POINT' routine, swapping the multiply and
;   divide literals preserves accuracy and ensures that .5 is evaluated 
;   as 5/10 and not as .1 * 5.

NXT_DGT_1 RST     20H             ; NEXT-CHAR
          CALL    $1514           ; routine STK-DIGIT
          JR      C,$14F5         ; forward to E-FORMAT


          RST     28H             ;; FP-CALC
          DEFB    $E0             ;;get-mem-0
          DEFB    $A4             ;;stk-ten
;;;       DEFB    $05             ;;division
          DEFB    $04             ;;+multiply
          DEFB    $C0             ;;st-mem-0
;;;       DEFB    $04             ;;multiply
          DEFB    $05             ;;+division
          DEFB    $0F             ;;addition
          DEFB    $34             ;;end-calc

          JR      NXT_DGT_1       ; loop back till exhausted to NXT-DGT-1

; -----------------------------------------------------------------------------

ORG $16B2

; -------------------------------------
; THE 'FLOATING POINT PRINT ZEROS' LOOP
; -------------------------------------

; This branch deals with zeros after decimal point.
; e.g.      .01 or .0000999
; Note. that printing to the ZX Printer destroys A and that A should be 
; initialized to '0' at each stage of the loop.
; Originally LPRINT .00001 printed as .0XYZ1

PF_ZEROS  NEG                   ; negate makes number positive 1 to 4.
          LD    B,A             ; zero count to B.

          LD    A,$1B           ; prepare character '.'
          RST   10H             ; PRINT-A

PF_ZRO_LP LD    A,$1C           ; prepare a '0' in the accumulator each time.

PFZROLP   RST   10H             ; PRINT-A

          DJNZ  PF_ZRO_LP       ;+ New loop back to PF-ZRO-LP

;;;       DJNZ  PFZROLP         ; obsolete loop back to PFZROLP


;   and continue with trailing fractional digits...

; -----------------------------------------------------------------------------

ORG     $1915


;   Up to this point all routine addresses have been maintained so that the
;   modified ROM is compatible with any machine-code software that uses ROM
;   routines.
;   The final section does not maintain address entry points as the routines
;   within are not generally called directly.

;********************************
;**  FLOATING-POINT CALCULATOR **
;********************************

;   As a general rule the calculator avoids using the IY register.
;   The exception is the 'val' function.
;   So an assembly language programmer who has disabled interrupts to use IY
;   for other purposes can still use the calculator for mathematical
;   purposes.


; ------------------------
; THE 'TABLE OF CONSTANTS'
; ------------------------
;   The ZX81 has only floating-point number representation.
;   Both the ZX80 and the ZX Spectrum have integer numbers in some form.
;   This table has been modified so that the constants are held in their
;   uncompressed, ready-to-party, 5-byte form.

;;; L1915:  DEFB    $00             ;;Bytes: 1
;;;         DEFB    $B0             ;;Exponent $00
;;;         DEFB    $00             ;;(+00,+00,+00)
;;; L1918:  DEFB    $31             ;;Exponent $81, Bytes: 1
;;;         DEFB    $00             ;;(+00,+00,+00)
;;; L191A:  DEFB    $30             ;;Exponent: $80, Bytes: 1
;;;         DEFB    $00             ;;(+00,+00,+00)
;;; L191C:  DEFB    $F1             ;;Exponent: $81, Bytes: 4
;;;         DEFB    $49,$0F,$DA,$A2 ;;
;;; L1921:  DEFB    $34             ;;Exponent: $84, Bytes: 1
;;;         DEFB    $20             ;;(+00,+00,+00)

TAB_CNST  DEFB  $00           ; the value zero.
          DEFB  $00           ;
          DEFB  $00           ;
          DEFB  $00           ;
          DEFB  $00           ;

          DEFB  $81           ; the floating point value 1.
          DEFB  $00           ;
          DEFB  $00           ;
          DEFB  $00           ;
          DEFB  $00           ;

          DEFB  $80           ; the floating point value 1/2.
          DEFB  $00           ;
          DEFB  $00           ;
          DEFB  $00           ;
          DEFB  $00           ;

          DEFB  $81           ; the floating point value pi/2.
          DEFB  $49           ;
          DEFB  $0F           ;
          DEFB  $DA           ;
          DEFB  $A2           ;

          DEFB  $84           ; the floating point value ten.
          DEFB  $20           ;
          DEFB  $00           ;
          DEFB  $00           ;
          DEFB  $00           ;

; ------------------------
; THE 'TABLE OF ADDRESSES'
; ------------------------
;
;   Starts with binary operations which have two operands and one result.
;   three pseudo binary operations first.

tbl_addrs DEFW  jump_true       ; $00 Address: $1C2F - jump-true
          DEFW  exchange        ; $01 Address: $1A72 - exchange
          DEFW  delete          ; $02 Address: $19E3 - delete

;   true binary operations.

          DEFW  subtract        ; $03 Address: $174C - subtract
          DEFW  multiply        ; $04 Address: $176C - multiply
          DEFW  division        ; $05 Address: $1882 - division
          DEFW  to_power        ; $06 Address: $1DE2 - to-power
          DEFW  or              ; $07 Address: $1AED - or

          DEFW  no_v_no         ; $08 Address: $1B03 - no-&-no
          DEFW  no_l_eql        ; $09 Address: $1B03 - no-l-eql
          DEFW  no_l_eql        ; $0A Address: $1B03 - no-gr-eql
          DEFW  no_l_eql        ; $0B Address: $1B03 - nos-neql
          DEFW  no_l_eql        ; $0C Address: $1B03 - no-grtr
          DEFW  no_l_eql        ; $0D Address: $1B03 - no-less
          DEFW  no_l_eql        ; $0E Address: $1B03 - nos-eql
          DEFW  addition        ; $0F Address: $1755 - addition

          DEFW  str_v_no        ; $10 Address: $1AF8 - str-&-no
          DEFW  no_l_eql        ; $11 Address: $1B03 - str-l-eql
          DEFW  no_l_eql        ; $12 Address: $1B03 - str-gr-eql
          DEFW  no_l_eql        ; $13 Address: $1B03 - strs-neql
          DEFW  no_l_eql        ; $14 Address: $1B03 - str-grtr
          DEFW  no_l_eql        ; $15 Address: $1B03 - str-less
          DEFW  no_l_eql        ; $16 Address: $1B03 - strs-eql
          DEFW  strs_add        ; $17 Address: $1B62 - strs-add

;   unary follow

          DEFW  negate          ; $18 Address: $1AA0 - neg

          DEFW  code            ; $19 Address: $1C06 - code
          DEFW  val             ; $1A Address: $1BA4 - val
          DEFW  len             ; $1B Address: $1C11 - len
          DEFW  sin             ; $1C Address: $1D49 - sin
          DEFW  cos             ; $1D Address: $1D3E - cos
          DEFW  tan             ; $1E Address: $1D6E - tan
          DEFW  asn             ; $1F Address: $1DC4 - asn
          DEFW  acs             ; $20 Address: $1DD4 - acs
          DEFW  atn             ; $21 Address: $1D76 - atn
          DEFW  ln              ; $22 Address: $1CA9 - ln
          DEFW  exp             ; $23 Address: $1C5B - exp
          DEFW  int             ; $24 Address: $1C46 - int
          DEFW  sqr             ; $25 Address: $1DDB - sqr
          DEFW  sgn             ; $26 Address: $1AAF - sgn
          DEFW  abs             ; $27 Address: $1AAA - abs
          DEFW  peek            ; $28 Address: $1A1B - peek
          DEFW  usr_no          ; $29 Address: $1AC5 - usr-no
          DEFW  strS            ; $2A Address: $1BD5 - str$
          DEFW  chrS            ; $2B Address: $1B8F - chrs
          DEFW  not             ; $2C Address: $1AD5 - not

;   end of true unary

          DEFW  MOVE_FP         ; $2D Address: $19F6 - duplicate
          DEFW  n_mod_m         ; $2E Address: $1C37 - n-mod-m

          DEFW  JUMP            ; $2F Address: $1C23 - jump
          DEFW  stk_data        ; $30 Address: $19FC - stk-data

          DEFW  dec_jr_nz       ; $31 Address: $1C17 - dec-jr-nz
          DEFW  less_0          ; $32 Address: $1ADB - less-0
          DEFW  greater_0       ; $33 Address: $1ACE - greater-0
          DEFW  end_calc        ; $34 Address: $002B - end-calc
          DEFW  get_argt        ; $35 Address: $1D18 - get-argt
          DEFW  truncate        ; $36 Address: $18E4 - truncate
          DEFW  fp_calc_2       ; $37 Address: $19E4 - fp-calc-2
          DEFW  e_to_fp         ; $38 Address: $155A - e-to-fp

;   the following are just the next available slots for the 128 compound 
;   literals which are in range $80 - $FF.

          DEFW  seriesg_x       ; $39 Address: $1A7F - series-xx    $80 - $9F.
          DEFW  stk_con_x       ; $3A Address: $1A51 - stk-const-xx $A0 - $BF.
          DEFW  sto_mem_x       ; $3B Address: $1A63 - st-mem-xx    $C0 - $DF.
          DEFW  get_mem_x       ; $3C Address: $1A45 - get-mem-xx   $E0 - $FF.

; -------------------------------
; THE 'FLOATING POINT CALCULATOR'
; -------------------------------
;
;

CALCULATE CALL  STK_PNTRS       ; routine STK-PNTRS is called to set up the
                                ; calculator stack pointers for a default
                                ; unary operation. HL = last value on stack.
                                ; DE = STKEND first location after stack.

;   the calculate routine is called at this point by the series generator...

GEN_ENT_1 LD    A,B             ; fetch the Z80 B register to A
          LD    ($401E),A       ; and store value in system variable BREG.
                                ; this will be the counter for dec-jr-nz
                                ; or if used from fp-calc2 the calculator
                                ; instruction.

;   ... and again later at this point

GEN_ENT_2 EXX                   ; switch sets
          EX    (SP),HL         ; and store the address of next instruction,
                                ; the return address, in H'L'.
                                ; If this is a recursive call then the H'L'
                                ; of the previous invocation goes on stack.
                                ; c.f. end-calc.
          EXX                   ; switch back to main set.

;   this is the re-entry looping point when handling a string of literals.

RE_ENTRY  LD    ($401C),DE      ; save end of stack in system variable STKEND
          EXX                   ; switch to alt
          LD    A,(HL)          ; get next literal
          INC   HL              ; increase pointer'

;   single operation jumps back to here

SCAN_ENT  PUSH  HL              ; save pointer on stack   *
          AND   A               ; now test the literal
          JP    P,FIRST_3D      ; forward to FIRST-3D if in range $00 - $3D
                                ; anything with bit 7 set will be one of
                                ; 128 compound literals.

;   Compound literals have the following format.
;   bit 7 set indicates compound.
;   bits 6-5 the subgroup 0-3.
;   bits 4-0 the embedded parameter $00 - $1F.
;   The subgroup 0-3 needs to be manipulated to form the next available four
;   address places after the simple literals in the address table.

          LD    D,A             ; save literal in D
          AND   $60             ; and with 01100000 to isolate subgroup
          RRCA                  ; rotate bits
          RRCA                  ; 4 places to right
          RRCA                  ; not five as we need offset * 2
          RRCA                  ; 00000xx0
          ADD   A,$72           ; add ($39 * 2) to give correct offset.
                                ; alter above if you add more literals.
          LD    L,A             ; store in L for later indexing.
          LD    A,D             ; bring back compound literal
          AND   $1F             ; use mask to isolate parameter bits
          JR    ENT_TABLE       ; forward to ENT-TABLE

; ---

;   the branch was here with simple literals.

FIRST_3D  CP    $18             ; compare with first unary operations.
          JR    NC,DOUBLE_A     ; to DOUBLE-A with unary operations

;   it is binary so adjust pointers.

          EXX                   ;
          LD    BC,$FFFB        ; the value -5
          LD    D,H             ; transfer HL, the last value, to DE.
          LD    E,L             ;
          ADD   HL,BC           ; subtract 5 making HL point to second
                                ; value.
          EXX                   ;

DOUBLE_A  RLCA                  ; double the literal
          LD    L,A             ; and store in L for indexing

ENT_TABLE LD    DE,tbl_addrs    ; Address: tbl-addrs
          LD    H,$00           ; prepare to index
          ADD   HL,DE           ; add to get address of routine
          LD    E,(HL)          ; low byte to E
          INC   HL              ;
          LD    D,(HL)          ; high byte to D

          LD    HL,RE_ENTRY     ; Address: RE-ENTRY
          EX    (SP),HL         ; goes on machine stack
                                ; address of next literal goes to HL. *


          PUSH  DE              ; now the address of routine is stacked.
          EXX                   ; back to main set
                                ; avoid using IY register.
          LD    BC,($401D)      ; STKEND_hi
                                ; nothing much goes to C but BREG to B
                                ; and continue into next ret instruction
                                ; which has a dual identity


; -----------------------
; THE 'DELETE' SUBROUTINE
; -----------------------
; (offset $02: 'delete')
;   A simple return but when used as a calculator literal this
;   deletes the last value from the calculator stack.
;   On entry, as always with binary operations,
;   HL=first number, DE=second number
;   On exit, HL=result, DE=stkend.
;   So nothing to do

delete    RET                   ; return - indirect jump if from above.

; ---------------------------------
; THE 'SINGLE OPERATION' SUBROUTINE
; ---------------------------------
;   offset $37: 'fp-calc-2'
;   this single operation is used, in the first instance, to evaluate most
;   of the mathematical and string functions found in BASIC expressions.

fp_calc_2 POP   AF              ; drop return address.
          LD    A,($401E)       ; load accumulator from system variable BREG
                                ; value will be literal eg. 'tan'
          EXX                   ; switch to alt
          JR    SCAN_ENT        ; back to SCAN-ENT
                                ; next literal will be end-calc in scanning

; ------------------------------
; THE 'TEST 5 SPACES' SUBROUTINE
; ------------------------------
;   This routine is called from MOVE-FP, STK-CONST and STK-STORE to
;   test that there is enough space between the calculator stack and the
;   machine stack for another five-byte value. It returns with BC holding
;   the value 5 ready for any subsequent LDIR.

TEST_5_SP PUSH  DE              ; save
          PUSH  HL              ; registers
          LD    BC,$0005        ; an overhead of five bytes
          CALL  TEST_ROOM       ; routine TEST-ROOM tests free RAM raising
                                ; an error if not.
          POP   HL              ; else restore
          POP   DE              ; registers.
          RET                   ; return with BC set at 5.


; ---------------------------------------------
; THE 'MOVE A FLOATING POINT NUMBER' SUBROUTINE
; ---------------------------------------------
; offset $2D: 'duplicate'
;   This simple routine is a 5-byte LDIR instruction
;   that incorporates a memory check.
;   When used as a calculator literal it duplicates the last value on the
;   calculator stack.
;   Unary so on entry HL points to last value, DE to stkend

MOVE_FP   CALL  TEST_5_SP       ; routine TEST-5-SP test free memory
                                ; and sets BC to 5.

          LDIR                  ; copy the five bytes.
          RET                   ; return with DE addressing new STKEND
                                ; and HL addressing new last value.

; -------------------------------
; THE 'STACK LITERALS' SUBROUTINE
; -------------------------------
; offset $30: 'stk-data'
;   When a calculator subroutine needs to put a value on the calculator
;   stack that is not a regular constant this routine is called with a
;   variable number of following data bytes that convey to the routine
;   the floating point form as succinctly as is possible.

stk_data  LD    H,D             ; transfer STKEND
          LD    L,E             ; to HL for result.

STK_CONST CALL  TEST_5_SP       ; routine TEST-5-SP tests that room exists
                                ; and sets BC to $05.

          EXX                   ; switch to alternate set
          PUSH  HL              ; save the pointer to next literal on stack
          EXX                   ; switch back to main set

          EX    (SP),HL         ; pointer to HL, destination to stack.

;;;       PUSH  BC              ; save BC - value 5 from test room. No need.

          LD    A,(HL)          ; fetch the byte following 'stk-data'
          AND   $C0             ; isolate bits 7 and 6
          RLCA                  ; rotate
          RLCA                  ; to bits 1 and 0  range $00 - $03.
          LD    C,A             ; transfer to C
          INC   C               ; and increment to give number of bytes
                                ; to read. $01 - $04
          LD    A,(HL)          ; reload the first byte
          AND   $3F             ; mask off to give possible exponent.
          JR    NZ,FORM_EXP     ; forward to FORM-EXP if it was possible to
                                ; include the exponent.

; else byte is just a byte count and exponent comes next.

          INC   HL              ; address next byte and
          LD    A,(HL)          ; pick up the exponent ( - $50).

FORM_EXP  ADD   A,$50           ; now add $50 to form actual exponent
          LD    (DE),A          ; and load into first destination byte.
          LD    A,$05           ; load accumulator with $05 and
          SUB   C               ; subtract C to give count of trailing
                                ; zeros plus one.
          INC   HL              ; increment source
          INC   DE              ; increment destination
;;;       LD    B,$00           ; prepare to copy. Note. B is zero.
          LDIR                  ; copy C bytes

;;;       POP   BC              ; restore 5 counter to BC.

          EX    (SP),HL         ; put HL on stack as next literal pointer
                                ; and the stack value - result pointer -
                                ; to HL.

          EXX                   ; switch to alternate set.
          POP   HL              ; restore next literal pointer from stack
                                ; to H'L'.
          EXX                   ; switch back to main set.

          LD    B,A             ; zero count to B
          XOR   A               ; clear accumulator

STK_ZEROS DEC   B               ; decrement B counter
          RET   Z               ; return if zero.          >>
                                ; DE points to new STKEND
                                ; HL to new number.

          LD    (DE),A          ; else load zero to destination
          INC   DE              ; increase destination
          JR    STK_ZEROS       ; loop back to STK-ZEROS until done.

; -------------------------------
; THE 'SKIP CONSTANTS' SUBROUTINE
; -------------------------------
; This routine traversed variable-length entries in the table of constants,
; stacking intermediate, unwanted constants onto a dummy calculator stack,
; in the first five bytes of the ZX81 ROM.
; Since the table now uses uncompressed values, some extra ROM space is 
; required for the table but much more is released by getting rid of routines
; like this.

;;; L1A2D:  AND     A               ; test if initially zero.
;;; L1A2E:  RET     Z               ; return if zero.          >>
;;;         PUSH    AF              ; save count.
;;;         PUSH    DE              ; and normal STKEND
;;;         LD      DE,$0000        ; dummy value for STKEND at start of ROM
;;;         CALL    STK_CONST       ; routine STK-CONST works through variable
;;;                                 ; length records.
;;;         POP     DE              ; restore real STKEND
;;;         POP     AF              ; restore count
;;;         DEC     A               ; decrease
;;;         JR      L1A2E           ; loop back to SKIP-NEXT

; --------------------------------
; THE 'MEMORY LOCATION' SUBROUTINE
; --------------------------------
; This routine, when supplied with a base address in HL and an index in A,
; will calculate the address of the A'th entry, where each entry occupies
; five bytes. It is used for addressing floating-point numbers in the
; calculator's memory area.

LOC_MEM   LD    C,A             ; store the original number $00-$1F.
          RLCA                  ; double.
          RLCA                  ; quadruple.
          ADD   A,C             ; now add original value to multiply by five.

          LD    C,A             ; place the result in C.
          LD    B,$00           ; set B to 0.
          ADD   HL,BC           ; add to form address of start of number in HL.

          RET                   ; return.

; -------------------------------------
; THE 'GET FROM MEMORY AREA' SUBROUTINE
; -------------------------------------
; offsets $E0 to $FF: 'get-mem-0', 'get-mem-1' etc.
; A holds $00-$1F offset.
; The calculator stack increases by 5 bytes.
; Note. first two instructions have been swapped to create a subroutine.

get_mem_x LD    HL,($401F)      ; MEM is base address of the memory cells.

INDEX_5   PUSH  DE              ; save STKEND

          CALL  LOC_MEM         ; routine LOC-MEM so that HL = first byte
          CALL  MOVE_FP         ; routine MOVE-FP moves 5 bytes with memory
                                ; check.
                                ; DE now points to new STKEND.
          POP   HL              ; the original STKEND is now RESULT pointer.
          RET                   ; return.

; ---------------------------------
; THE 'STACK A CONSTANT' SUBROUTINE
; ---------------------------------
; (offset $A0: 'stk-zero')
; (offset $A1: 'stk-one')
; (offset $A2: 'stk-half')
; (offset $A3: 'stk-pi/2')
; (offset $A4: 'stk-ten')
; This routine allows a one-byte instruction to stack up to 32 constants
; held in short form in a table of constants. In fact only 5 constants are
; required. On entry the A register holds the literal ANDed with $1F.
;
; It wasn't very efficient and it is better to hold the
; numbers in full, five byte form and stack them in a similar manner
; to that which which is used by the above routine.

stk_con_x LD    HL,TAB_CNST     ; Address: Table of constants.

          JR    INDEX_5         ; and join subsroutine above.

; ---

;;;     LD      H,D             ; save STKEND - required for result
;;;     LD      L,E             ;
;;;     EXX                     ; swap
;;;     PUSH    HL              ; save pointer to next literal
;;;     LD      HL,L1515        ; Address: stk-zero - start of table of
;;;                             ; constants
;;;     EXX                     ;
;;;     CALL    SKIP_CONS       ; routine SKIP-CONS
;;;     CALL    STK_CONST       ; routine STK-CONST
;;;     EXX                     ;
;;;     POP     HL              ; restore pointer to next literal.
;;;     EXX                     ;
;;;     RET                     ; return.

; ---------------------------------------
; THE 'STORE IN A MEMORY AREA' SUBROUTINE
; ---------------------------------------
; Offsets $C0 to $DF: 'st-mem-0', 'st-mem-1' etc.
; Although 32 memory storage locations can be addressed, only six
; $C0 to $C5 are required by the ROM and only the thirty bytes (6*5)
; required for these are allocated. ZX81 programmers who wish to
; use the floating point routines from assembly language may wish to
; alter the system variable MEM to point to 160 bytes of RAM to have
; use the full range available.
; A holds derived offset $00-$1F.
; Unary so on entry HL points to last value, DE to STKEND.

sto_mem_x PUSH  HL              ; save the result pointer.
          EX    DE,HL           ; transfer to DE.
          LD    HL,($401F)      ; fetch MEM the base of memory area.
          CALL  LOC_MEM         ; routine LOC-MEM sets HL to the destination.
          EX    DE,HL           ; swap - HL is start, DE is destination.

;;;       CALL  MOVE_FP         ; routine MOVE-FP.
;;;                             ; Note. a short ld bc,5; ldir
;;;                             ; the embedded memory check is not required
;;;                             ; so these instructions would be faster!

          LD    C,$05           ;+ one extra byte but 
          LDIR                  ;+ faster and no memory check.

          EX    DE,HL           ; DE = STKEND
          POP   HL              ; restore original result pointer
          RET                   ; return.

; -------------------------
; THE 'EXCHANGE' SUBROUTINE
; -------------------------
; offset $01: 'exchange'
; This routine exchanges the last two values on the calculator stack
; On entry, as always with binary operations,
; HL=first number, DE=second number
; On exit, HL=result, DE=stkend.

exchange  LD    B,$05           ; there are five bytes to be swapped

; start of loop.

SWAP_BYTE LD    A,(DE)          ; each byte of second
;;;       LD    C,(HL)          ; each byte of first
;;;       EX    DE,HL           ; swap pointers
          ld    c,a             ;+
          ld    a,(hl)          ;+
          LD    (DE),A          ; store each byte of first
          LD    (HL),C          ; store each byte of second
          INC   HL              ; advance both
          INC   DE              ; pointers.
          DJNZ  SWAP_BYTE       ; loop back to SWAP-BYTE until all 5 done.

;;;       EX    DE,HL           ; even up the exchanges (one byte saved)

          RET                   ; return.

; ---------------------------------
; THE 'SERIES GENERATOR' SUBROUTINE
; ---------------------------------
; offset $86: 'series-06'
; offset $88: 'series-08'
; offset $8C: 'series-0C'
; The ZX81 uses Chebyshev polynomials to generate approximations for
; SIN, ATN, LN and EXP. These are named after the Russian mathematician
; Pafnuty Chebyshev, born in 1821, who did much pioneering work on numerical
; series. As far as calculators are concerned, Chebyshev polynomials have an
; advantage over other series, for example the Taylor series, as they can
; reach an approximation in just six iterations for SIN, eight for EXP and
; twelve for LN and ATN. The mechanics of the routine are interesting but
; for full treatment of how these are generated with demonstrations in
; Sinclair BASIC see "The Complete Spectrum ROM Disassembly" by Dr Ian Logan
; and Dr Frank O'Hara, published 1983 by Melbourne House.

seriesg_x LD    B,A             ; parameter $00 - $1F to B counter
          CALL  GEN_ENT_1       ; routine GEN-ENT-1 is called.
                                ; A recursive call to a special entry point
                                ; in the calculator that puts the B register
                                ; in the system variable BREG. The return
                                ; address is the next location and where
                                ; the calculator will expect its first
                                ; instruction - now pointed to by HL'.
                                ; The previous pointer to the series of
                                ; five-byte numbers goes on the machine stack.

; The initialization phase.

          DEFB  $2D             ;;duplicate       x,x
          DEFB  $0F             ;;addition        x+x
          DEFB  $C0             ;;st-mem-0        x+x
          DEFB  $02             ;;delete          .
          DEFB  $A0             ;;stk-zero        0
          DEFB  $C2             ;;st-mem-2        0

; a loop is now entered to perform the algebraic calculation for each of
; the numbers in the series

G_LOOP    DEFB  $2D             ;;duplicate       v,v.
          DEFB  $E0             ;;get-mem-0       v,v,x+2
          DEFB  $04             ;;multiply        v,v*x+2
          DEFB  $E2             ;;get-mem-2       v,v*x+2,v
          DEFB  $C1             ;;st-mem-1
          DEFB  $03             ;;subtract
          DEFB  $34             ;;end-calc

; the previous pointer is fetched from the machine stack to H'L' where it
; addresses one of the numbers of the series following the series literal.

          CALL  stk_data        ; routine STK-DATA is called directly to
                                ; push a value and advance H'L'.
          CALL  GEN_ENT_2       ; routine GEN-ENT-2 recursively re-enters
                                ; the calculator without disturbing
                                ; system variable BREG
                                ; H'L' value goes on the machine stack and is
                                ; then loaded as usual with the next address.

          DEFB  $0F             ;;addition
          DEFB  $01             ;;exchange
          DEFB  $C2             ;;st-mem-2
          DEFB  $02             ;;delete

          DEFB  $31             ;;dec-jr-nz
          DEFB  G_LOOP - $      ;;back to L1A89, G-LOOP

; when the counted loop is complete the final subtraction yields the result
; for example SIN X.

          DEFB  $E1             ;;get-mem-1
          DEFB  $03             ;;subtract
          DEFB  $34             ;;end-calc

          RET                   ; return with H'L' pointing to location
                                ; after last number in series.

; -----------------------
; Handle unary minus (18)
; -----------------------
; Unary so on entry HL points to last value, DE to STKEND.

negate    LD    A,(HL)          ; fetch exponent of last value on the
                                ; calculator stack.
          AND   A               ; test it.
          RET   Z               ; return if zero.

          INC   HL              ; address the byte with the sign bit.
          LD    A,(HL)          ; fetch to accumulator.
          XOR   $80             ; toggle the sign bit.
          LD    (HL),A          ; put it back.
          DEC   HL              ; point to last value again.
          RET                   ; return.

; -----------------------
; Absolute magnitude (27)
; -----------------------
; This calculator literal finds the absolute value of the last value,
; floating point, on calculator stack.

abs       INC   HL              ; point to byte with sign bit.
          RES   7,(HL)          ; make the sign positive.
          DEC   HL              ; point to last value again.
          RET                   ; return.

; -----------
; Signum (26)
; -----------
; This routine replaces the last value on the calculator stack,
; (which is in floating point form), with one if positive and with minus one
; if it is negative. If it is zero then it is left unchanged.

sgn       INC   HL              ; point to first byte of 4-byte mantissa.
          LD    A,(HL)          ; pick up the byte with the sign bit.
          DEC   HL              ; point to exponent.
          DEC   (HL)            ; test the exponent for
          INC   (HL)            ; the value zero.

          SCF                   ; Set the carry flag.
          CALL  NZ,FP_0_1       ; Routine FP-0/1  replaces last value with one
                                ; if exponent indicates the value is non-zero.
                                ; In either case mantissa is now four zeros.

          INC   HL              ; Point to first byte of 4-byte mantissa.
          RLCA                  ; Rotate original sign bit to carry.
          RR    (HL)            ; Rotate the carry into sign.
          DEC   HL              ; Point to last value.
          RET                   ; Return.


; -------------------------
; Handle PEEK function (28)
; -------------------------
; This function returns the contents of a memory address.
; The entire address space can be peeked including the ROM.

peek      CALL  FIND_INT        ; routine FIND-INT puts address in BC.
          LD    A,(BC)          ; load contents into A register.

IN_PK_STK JP    STACK_A         ; exit via STACK-A to put value on the
                                ; calculator stack.

; ---------------
; USR number (29)
; ---------------
; The USR function followed by a number 0-65535 is the method by which
; the ZX81 invokes machine code programs. This function returns the
; contents of the BC register pair.
; Note. that STACK-BC re-initializes the IY register to $4000 if a user-written
; program has altered it.

usr_no    CALL  FIND_INT        ; routine FIND-INT to fetch the
                                ; supplied address into BC.

          LD    HL,STACK_BC     ; address: STACK-BC is
          PUSH  HL              ; pushed onto the machine stack.
          PUSH  BC              ; then the address of the machine code
                                ; routine.

          RET                   ; make an indirect jump to the user's routine
                                ; and, hopefully, to STACK-BC also.


; -----------------------
; Greater than zero ($33)
; -----------------------
; Test if the last value on the calculator stack is greater than zero.
; This routine is also called directly from the end-tests of the comparison
; routine.

greater_0 LD    A,(HL)          ; fetch exponent.
          AND   A               ; test it for zero.
          RET   Z               ; return if so.


          LD    A,$FF           ; prepare XOR mask for sign bit
          JR    SIGN_TO_C       ; forward to SIGN-TO-C
                                ; to put sign in carry
                                ; (carry will become set if sign is positive)
                                ; and then overwrite location with 1 or 0
                                ; as appropriate.

; ------------------------
; Handle NOT operator ($2C)
; ------------------------
; This overwrites the last value with 1 if it was zero else with zero
; if it was any other value.
;
; e.g. NOT 0 returns 1, NOT 1 returns 0, NOT -3 returns 0.
;
; The subroutine is also called directly from the end-tests of the comparison
; operator.

not       LD    A,(HL)          ; get exponent byte.
          NEG                   ; negate - sets carry if non-zero.
          CCF                   ; complement so carry set if zero, else reset.
          JR    FP_0_1          ; forward to FP-0/1.

; -------------------
; Less than zero (32)
; -------------------
; Destructively test if last value on calculator stack is less than zero.
; Bit 7 of second byte will be set if so.

less_0    XOR   A               ; set xor mask to zero
                                ; (carry will become set if sign is negative).

; transfer sign of mantissa to Carry Flag.

SIGN_TO_C INC   HL              ; address 2nd byte.
          XOR   (HL)            ; bit 7 of HL will be set if number is negative.
          DEC   HL              ; address 1st byte again.
          RLCA                  ; rotate bit 7 of A to carry.

; -----------
; Zero or one
; -----------
; This routine places an integer value zero or one at the addressed location
; of calculator stack or MEM area. The value one is written if carry is set on
; entry else zero.

FP_0_1    PUSH  HL              ; save pointer to the first byte
          LD    B,$05           ; five bytes to do.

FP_loop   LD    (HL),$00        ; insert a zero.
          INC   HL              ;
          DJNZ  FP_loop         ; repeat.

          POP   HL              ;
          RET   NC              ;

          LD    (HL),$81        ; make value 1
          RET                   ; return.


; -----------------------
; Handle OR operator (07)
; -----------------------
; The Boolean OR operator. eg. X OR Y
; The result is zero if both values are zero else a non-zero value.
;
; e.g.    0 OR 0  returns 0.
;        -3 OR 0  returns -3.
;         0 OR -3 returns 1.
;        -3 OR 2  returns 1.
;
; A binary operation.
; On entry HL points to first operand (X) and DE to second operand (Y).

or        LD    A,(DE)          ; fetch exponent of second number
          AND   A               ; test it.
          RET   Z               ; return if zero.

          SCF                   ; set carry flag
          JR    FP_0_1          ; back to FP-0/1 to overwrite the first operand
                                ; with the value 1.


; -----------------------------
; Handle number AND number (08)
; -----------------------------
; The Boolean AND operator.
;
; e.g.    -3 AND 2  returns -3.
;         -3 AND 0  returns 0.
;          0 and -2 returns 0.
;          0 and 0  returns 0.
;
; Compare with OR routine above.

no_v_no   LD    A,(DE)          ; fetch exponent of second number.
          AND   A               ; test it.
          RET   NZ              ; return if not zero.

          JR    FP_0_1          ; back to FP-0/1 to overwrite the first operand
                                ; with zero for return value.

; -----------------------------
; Handle string AND number (10)
; -----------------------------
; e.g. "YOU WIN" AND SCORE>99 will return the string if condition is true
; or the null string if false.

str_v_no  LD    A,(DE)          ; fetch exponent of second number.
          AND   A               ; test it.
          RET   NZ              ; return if number was not zero - the string
                                ; is the result.

; if the number was zero (false) then the null string must be returned by
; altering the length of the string on the calculator stack to zero.

          PUSH  DE              ; save pointer to the now obsolete number
                                ; (which will become the new STKEND)

          DEC   DE              ; point to the 5th byte of string descriptor.
          XOR   A               ; clear the accumulator.
          LD    (DE),A          ; place zero in high byte of length.
          DEC   DE              ; address low byte of length.
          LD    (DE),A          ; place zero there - now the null string.

          POP   DE              ; restore pointer - new STKEND.
          RET                   ; return.

; -------------------------------------
; Perform comparison ($09-$0E, $11-$16)
; -------------------------------------
; True binary operations.
;
; A single entry point is used to evaluate six numeric and six string
; comparisons. On entry, the calculator literal is in the B register and
; the two numeric values, or the two string parameters, are on the
; calculator stack.
; The individual bits of the literal are manipulated to group similar
; operations although the SUB 8 instruction does nothing useful and merely
; alters the string test bit.
; Numbers are compared by subtracting one from the other, strings are
; compared by comparing every character until a mismatch, or the end of one
; or both, is reached.
;
; Numeric Comparisons.
; --------------------
; The 'x>y' example is the easiest as it employs straight-thru logic.
; Number y is subtracted from x and the result tested for greater-0 yielding
; a final value 1 (true) or 0 (false).
; For 'x<y' the same logic is used but the two values are first swapped on the
; calculator stack.
; For 'x=y' NOT is applied to the subtraction result yielding true if the
; difference was zero and false with anything else.
; The first three numeric comparisons are just the opposite of the last three
; so the same processing steps are used and then a final NOT is applied.
;
; literal    Test   No  sub 8       ExOrNot  1st RRCA  exch sub  ?   End-Tests
; =========  ====   == ======== === ======== ========  ==== ===  =  === === ===
; no-l-eql   x<=y   09 00000001 dec 00000000 00000000  ---- x-y  ?  --- >0? NOT
; no-gr-eql  x>=y   0A 00000010 dec 00000001 10000000c swap y-x  ?  --- >0? NOT
; nos-neql   x<>y   0B 00000011 dec 00000010 00000001  ---- x-y  ?  NOT --- NOT
; no-grtr    x>y    0C 00000100  -  00000100 00000010  ---- x-y  ?  --- >0? ---
; no-less    x<y    0D 00000101  -  00000101 10000010c swap y-x  ?  --- >0? ---
; nos-eql    x=y    0E 00000110  -  00000110 00000011  ---- x-y  ?  NOT --- ---
;
;                                                           comp -> C/F
;                                                           ====    ===
; str-l-eql  x$<=y$ 11 00001001 dec 00001000 00000100  ---- x$y$ 0  !or >0? NOT
; str-gr-eql x$>=y$ 12 00001010 dec 00001001 10000100c swap y$x$ 0  !or >0? NOT
; strs-neql  x$<>y$ 13 00001011 dec 00001010 00000101  ---- x$y$ 0  !or >0? NOT
; str-grtr   x$>y$  14 00001100  -  00001100 00000110  ---- x$y$ 0  !or >0? ---
; str-less   x$<y$  15 00001101  -  00001101 10000110c swap y$x$ 0  !or >0? ---
; strs-eql   x$=y$  16 00001110  -  00001110 00000111  ---- x$y$ 0  !or >0? ---
;
; String comparisons are a little different in that the eql/neql carry flag
; from the 2nd RRCA is, as before, fed into the first of the end tests but
; along the way it gets modified by the comparison process. The result on the
; stack always starts off as zero and the carry fed in determines if NOT is
; applied to it. So the only time the greater-0 test is applied is if the
; stack holds zero which is not very efficient as the test will always yield
; zero. The most likely explanation is that there were once separate end tests
; for numbers and strings.

no_l_eql  LD    A,B             ; transfer literal to accumulator.

;;;       SUB   $08             ; subtract eight - which is not useful.

          BIT   2,A             ; isolate '>', '<', '='.

          JR    NZ,EX_OR_NOT    ; skip to EX-OR-NOT with these.

          DEC   A               ; else make $00-$02, $08-$0A to match bits 0-2.

EX_OR_NOT RRCA                  ; the first RRCA sets carry for a swap.
          JR    NC,NU_OR_STR    ; forward to NU-OR-STR with other 8 cases

; for the other 4 cases the two values on the calculator stack are exchanged.

          PUSH  AF              ; save A and carry.
          PUSH  HL              ; save HL - pointer to first operand.
                                ; (DE points to second operand).

          CALL  exchange        ; routine exchange swaps the two values.
                                ; (HL = second operand, DE = STKEND)

          POP   DE              ; DE = first operand
          EX    DE,HL           ; as we were.
          POP   AF              ; restore A and carry.

; Note. it would be better if the 2nd RRCA preceded the string test.
; It would save two duplicate bytes and if we also got rid of that sub 8
; at the beginning we wouldn't have to alter which bit we test.

NU_OR_STR RRCA                  ;+ causes 'eql/neql' to set carry.
          PUSH  AF              ;+ save the carry flag.
          BIT   2,A             ; test if a string comparison.
          JR    NZ,STRINGS      ; forward to STRINGS if so.

; continue with numeric comparisons.

;;;       RRCA                  ; 2nd RRCA causes eql/neql to set carry.
;;;       PUSH    AF            ; save A and carry

          CALL  subtract        ; routine subtract leaves result on stack.
          JR    END_TESTS       ; forward to END-TESTS

; ---

STRINGS     
;;;       RRCA                  ; 2nd RRCA causes eql/neql to set carry.
;;;       PUSH    AF            ; save A and carry.

          CALL  STK_FETCH       ; routine STK-FETCH gets 2nd string params
          PUSH  DE              ; save start2 *.
          PUSH  BC              ; and the length.

          CALL  STK_FETCH       ; routine STK-FETCH gets 1st string
                                ; parameters - start in DE, length in BC.
          POP   HL              ; restore length of second to HL.

; A loop is now entered to compare, by subtraction, each corresponding character
; of the strings. For each successful match, the pointers are incremented and
; the lengths decreased and the branch taken back to here. If both string
; remainders become null at the same time, then an exact match exists.

BYTE_COMP LD    A,H             ; test if the second string
          OR    L               ; is the null string and hold flags.

          EX    (SP),HL         ; put length2 on stack, bring start2 to HL *.
          LD    A,B             ; hi byte of length1 to A

          JR    NZ,SEC_PLUS     ; forward to SEC-PLUS if second not null.

          OR    C               ; test length of first string.

SECND_LOW POP   BC              ; pop the second length off stack.
          JR    Z,BOTH_NULL     ; forward to BOTH-NULL if first string is also
                                ; of zero length.

; the true condition - first is longer than second (SECND-LESS)

          POP   AF              ; restore carry (set if eql/neql)
          CCF                   ; complement carry flag.
                                ; Note. equality becomes false.
                                ; Inequality is true. By swapping or applying
                                ; a terminal 'not', all comparisons have been
                                ; manipulated so that this is success path.
          JR    STR_TEST        ; forward to leave via STR-TEST

; ---
; the branch was here with a match

BOTH_NULL POP   AF              ; restore carry - set for eql/neql
          JR    STR_TEST        ; forward to STR-TEST

; ---
; the branch was here when 2nd string not null and low byte of first is yet
; to be tested.


SEC_PLUS  OR    C               ; test the length of first string.
          JR    Z,FRST_LESS     ; forward to FRST-LESS if length is zero.

; both strings have at least one character left.

          LD    A,(DE)          ; fetch character of first string.
          SUB   (HL)            ; subtract with that of 2nd string.
          JR    C,FRST_LESS     ; forward to FRST-LESS if carry set

          JR    NZ,SECND_LOW    ; back to SECND-LOW and then STR-TEST
                                ; if not exact match.

          DEC   BC              ; decrease length of 1st string.
          INC   DE              ; increment 1st string pointer.

          INC   HL              ; increment 2nd string pointer.
          EX    (SP),HL         ; swap with length on stack
          DEC   HL              ; decrement 2nd string length
          JR    BYTE_COMP       ; back to BYTE-COMP

; ---
; the false condition.

FRST_LESS POP   BC              ; discard length
          POP   AF              ; pop A
          AND   A               ; clear the carry for false result.

; ---
; exact match and x$>y$ rejoin here

STR_TEST  PUSH  AF              ; save A and carry

          RST   28H             ;; FP-CALC
          DEFB  $A0             ;;stk-zero      an initial false value.
          DEFB  $34             ;;end-calc

; both numeric and string paths converge here.

END_TESTS POP   AF              ; pop carry  - will be set if eql/neql
          PUSH  AF              ; save it again.

          CALL  C,not           ; routine NOT sets true(1) if equal(0)
                                ; or, for strings, applies true result.
          CALL  greater_0       ; greater-0 


          POP   AF              ; pop A
          RRCA                  ; the third RRCA - test for '<=', '>=' or '<>'.
          CALL  NC,not          ; apply a terminal NOT if so.
          RET                   ; return.

; -----------------------------------
; THE 'STRING CONCATENATION' OPERATOR
; -----------------------------------
; (offset $17: 'strs_add')
; This literal combines two strings into one e.g. LET A$ = B$ + C$
; The two parameters of the two strings to be combined are on the stack.

strs_add    
          CALL  STK_FETCH       ; routine STK-FETCH fetches string parameters
                                ; and deletes calculator stack entry.
          PUSH  DE              ; save start address.
          PUSH  BC              ; and length.

          CALL  STK_FETCH       ; routine STK-FETCH for first string
          POP   HL              ; re-fetch first length
          PUSH  HL              ; and save again
          PUSH  DE              ; save start of second string
          PUSH  BC              ; and its length.

          ADD   HL,BC           ; add the two lengths.
          LD    B,H             ; transfer to BC
          LD    C,L             ; and create
          RST   30H             ; BC-SPACES in workspace.
                                ; DE points to start of space.

          CALL  STK_STO_s       ; routine STK-STO-$ stores parameters
                                ; of new string updating STKEND.

          POP   BC              ; length of first
          POP   HL              ; address of start

;;;       LD      A,B           ; test for
;;;       OR      C             ; zero length.
;;;       JR      Z,OTHER_STR   ; to OTHER-STR if null string
;;;       LDIR                  ; copy string to workspace.

          CALL  COND_MV         ;+ a conditional (NZ) ldir routine. 

OTHER_STR POP   BC              ; now second length
          POP   HL              ; and start of string

;;;       LD    A,B             ; test this one
;;;       OR    C               ; for zero length
;;;       JR    Z,STK_PNTRS     ; skip forward to STK-PNTRS if so as complete.
;;;       LDIR                  ; else copy the bytes.

          CALL  COND_MV         ;+ a conditional (NZ) ldir routine. 

;   Continue into next routine which sets the calculator stack pointers.

; ----------------------------
; THE 'STACK POINTERS' ROUTINE
; ----------------------------
;   Register DE is set to STKEND and HL, the result pointer, is set to five
;   locations below this - the 'last value'.
;   This routine is used when it is inconvenient to save these values at the
;   time the calculator stack is manipulated due to other activity on the
;   machine stack.
;   This routine is also used to terminate the VAL routine for
;   the same reason and to initialize the calculator stack at the start of
;   the CALCULATE routine.

STK_PNTRS LD    HL,($401C)      ; fetch STKEND value from system variable.
          LD    DE,$FFFB        ; the value -5
          PUSH  HL              ; push STKEND value.

          ADD   HL,DE           ; subtract 5 from HL.

          POP   DE              ; pop STKEND to DE.
          RET                   ; return.

; -------------------
; THE 'CHR$' FUNCTION
; -------------------
; (offset $2B: 'chr$')
;   This function returns a single character string that is a result of
;   converting a number in the range 0-255 to a string e.g. CHR$ 38 = "A".
;   Note. the ZX81 does not have an ASCII character set.

chrS      CALL  FP_TO_A         ; routine FP-TO-A puts the number in A.

          JR    C,REPORT_Bd     ; forward to REPORT-Bd if overflow
          JR    NZ,REPORT_Bd    ; forward to REPORT-Bd if negative

;;;       PUSH  AF              ; save the argument.

          LD    BC,$0001        ; one space required.
          RST   30H             ; BC-SPACES makes DE point to start

;;;       POP   AF              ; restore the number.

          LD    (DE),A          ; and store in workspace

          JR    str_STK         ;+ relative jump to similar sequence in str$.

;;;       CALL  STK_STO_s       ; routine STK-STO-$ stacks descriptor.
;;;       EX    DE,HL           ; make HL point to result and DE to STKEND.
;;;       RET                   ; return.

; ---

REPORT_Bd RST   08H             ; ERROR-1
          DEFB  $0A             ; Error Report: Integer out of range

; ------------------
; THE 'VAL' FUNCTION
; ------------------
; (offset $1A: 'val')
;   VAL treats the characters in a string as a numeric expression.
;   e.g. VAL "2.3" = 2.3, VAL "2+4" = 6, VAL ("2" + "4") = 24.

val       RST   18H             ;+ shorter way to fetch CH_ADD.

;;;       LD    HL,($4016)      ; fetch value of system variable CH_ADD
          PUSH  HL              ; and save on the machine stack.

          CALL  STK_FETCH       ; routine STK-FETCH fetches the string operand
                                ; from calculator stack.

          PUSH  DE              ; save the address of the start of the string.
          INC   BC              ; increment the length for a carriage return.

          RST   30H             ; BC-SPACES creates the space in workspace.
          POP   HL              ; restore start of string to HL.
          LD    ($4016),DE      ; load CH_ADD with start DE in workspace.

          PUSH  DE              ; save the start in workspace
          LDIR                  ; copy string from program or variables or
                                ; workspace to the workspace area.
          EX    DE,HL           ; end of string + 1 to HL
          DEC   HL              ; decrement HL to point to end of new area.
          LD    (HL),$76        ; insert a carriage return at end.
                                ; ZX81 has a non-ASCII character set
          RES   7,(IY+$01)      ; update FLAGS  - signal checking syntax.
          CALL  CLASS_06        ; routine CLASS-06 - SCANNING evaluates string
                                ; expression and checks for integer result.

          CALL  CHECK_2         ; routine CHECK-2 checks for carriage return.


          POP   HL              ; restore start of string in workspace.

          LD    ($4016),HL      ; set CH_ADD to the start of the string again.
          SET   7,(IY+$01)      ; update FLAGS  - signal running program.
          CALL  SCANNING        ; routine SCANNING evaluates the string
                                ; in full leaving result on calculator stack.

          POP   HL              ; restore saved character address in program.
          LD    ($4016),HL      ; and reset the system variable CH_ADD.

          JR    STK_PNTRS       ; back to exit via STK-PNTRS.
                                ; resetting the calculator stack pointers
                                ; HL and DE from STKEND as it wasn't possible
                                ; to preserve them during this routine.

; -------------------
; THE 'STR$' FUNCTION
; -------------------
; (offset $2A: 'str$')
; This function returns a string representation of a numeric argument.
; The method used is to trick the PRINT-FP routine into thinking it
; is writing to a collapsed display file when in fact it is writing to
; string workspace.
; If there is already a newline at the intended print position and the
; column count has not been reduced to zero then the print routine
; assumes that there is only 1K of RAM and the screen memory, like the rest
; of dynamic memory, expands as necessary using calls to the ONE-SPACE
; routine. The screen is character-mapped not bit-mapped.

strS      LD    BC,$0001        ; create an initial byte in workspace
          RST   30H             ; using BC-SPACES restart.

          LD    (HL),$76        ; place a carriage return there.

          LD    HL,($4039)      ; fetch value of S_POSN column/line
          PUSH  HL              ; and preserve on stack.

          LD    L,$FF           ; make column value high to create a
                                ; contrived buffer of length 254.
          LD    ($4039),HL      ; and store in system variable S_POSN.

          LD    HL,($400E)      ; fetch value of DF_CC
          PUSH  HL              ; and preserve on stack also.

          LD    ($400E),DE      ; now set DF_CC which normally addresses
                                ; somewhere in the display file to the start
                                ; of workspace.
          PUSH  DE              ; save the start of new string.

          CALL  PRINT_FP        ; routine PRINT-FP.

          POP   DE              ; retrieve start of string.

          LD    HL,($400E)      ; fetch end of string from DF_CC.
          AND   A               ; prepare for true subtraction.
          SBC   HL,DE           ; subtract to give length.

          LD    B,H             ; and transfer to the BC
          LD    C,L             ; register.

          POP   HL              ; restore original
          LD    ($400E),HL      ; DF_CC value

          POP   HL              ; restore original
          LD    ($4039),HL      ; S_POSN values.

;   New entry-point to exploit similarities and save 3 bytes of code.

str_STK CALL    STK_STO_s       ; routine STK-STO-$ stores the string
                                ; descriptor on the calculator stack.

          EX    DE,HL           ; HL = last value, DE = STKEND.
          RET                   ; return.


; -------------------
; THE 'CODE' FUNCTION
; -------------------
; (offset $19: 'code')
; Returns the code of a character or first character of a string
; e.g. CODE "AARDVARK" = 38  (not 65 as the ZX81 does not have an ASCII
; character set).


code      CALL  STK_FETCH       ; routine STK-FETCH to fetch and delete the
                                ; string parameters.
                                ; DE points to the start, BC holds the length.
          LD    A,B             ; test length
          OR    C               ; of the string.
          JR    Z,STK_CODE      ; skip to STK-CODE with zero if the null string.

          LD    A,(DE)          ; else fetch the first character.

STK_CODE  JP    STACK_A         ; jump back to STACK-A (with memory check)

; --------------------
; THE 'LEN' SUBROUTINE
; --------------------
; (offset $1b: 'len')
; Returns the length of a string.
; In Sinclair BASIC strings can be more than twenty thousand characters long
; so a sixteen-bit register is required to store the length

len       CALL  STK_FETCH       ; routine STK-FETCH to fetch and delete the
                                ; string parameters from the calculator stack.
                                ; register BC now holds the length of string.

          JP    STACK_BC        ; jump back to STACK-BC to save result on the
                                ; calculator stack (with memory check).

; -------------------------------------
; THE 'DECREASE THE COUNTER' SUBROUTINE
; -------------------------------------
; (offset $31: 'dec-jr-nz')
; The calculator has an instruction that decrements a single-byte
; pseudo-register and makes consequential relative jumps just like
; the Z80's DJNZ instruction.

dec_jr_nz EXX                   ; switch in set that addresses code

          PUSH  HL              ; save pointer to offset byte
          LD    HL,$401E        ; address BREG in system variables
          DEC   (HL)            ; decrement it
          POP   HL              ; restore pointer

          JR    NZ,JUMP_2       ; to JUMP-2 if not zero

          INC   HL              ; step past the jump length.
          EXX                   ; switch in the main set.
          RET                   ; return.

; Note. as a general rule the calculator avoids using the IY register
; otherwise the cumbersome 4 instructions in the middle could be replaced by
; dec (iy+$xx) - using three instruction bytes instead of six.


; ---------------------
; THE 'JUMP' SUBROUTINE
; ---------------------
; (Offset $2F; 'jump')
; This enables the calculator to perform relative jumps just like
; the Z80 chip's JR instruction.
; This is one of the few routines that was polished for the ZX Spectrum.

JUMP      EXX                   ;switch in pointer set

JUMP_2    LD    E,(HL)          ; the jump byte 0-127 forward, 128-255 back.

;   Note. Elegance from the ZX Spectrum.

          LD    A,E             ;+
          RLA                   ;+
          SBC   A,A             ;+

;   The original ZX81 code.

;;;       XOR   A               ; clear accumulator.
;;;       BIT   7,E             ; test if negative jump
;;;       JR    Z,JUMP_3        ; skip, if positive, to JUMP-3.
;;;       CPL                   ; else change to $FF.

JUMP_3    LD    D,A             ; transfer to high byte.
          ADD   HL,DE           ; advance calculator pointer forward or back.

          EXX                   ; switch out pointer set.
          RET                   ; return.

; -----------------------------
; THE 'JUMP ON TRUE' SUBROUTINE
; -----------------------------
; (Offset $00; 'jump-true')
; This enables the calculator to perform conditional relative jumps
; dependent on whether the last test gave a true result
; On the ZX81, the exponent will be zero for zero or else $81 for one.

jump_true LD    A,(DE)          ; collect exponent byte

          AND   A               ; is result 0 or 1 ?
          JR    NZ,JUMP         ; back to JUMP if true (1).

          EXX                   ; else switch in the pointer set.
          INC   HL              ; step past the jump length.
          EXX                   ; switch in the main set.
          RET                   ; return.


; ------------------------
; THE 'MODULUS' SUBROUTINE
; ------------------------
; ( Offset $2E: 'n-mod-m' )
; ( i1, i2 -- i3, i4 )
; The subroutine calculate N mod M where M is the positive integer, the
; 'last value' on the calculator stack and N is the integer beneath.
; The subroutine returns the integer quotient as the last value and the
; remainder as the value beneath.
; e.g.    17 MOD 3 = 5 remainder 2
; It is invoked during the calculation of a random number and also by
; the PRINT-FP routine.

n_mod_m   RST   28H             ;; FP-CALC          17, 3.
          DEFB  $C0             ;;st-mem-0          17, 3.
          DEFB  $02             ;;delete            17.
          DEFB  $2D             ;;duplicate         17, 17.
          DEFB  $E0             ;;get-mem-0         17, 17, 3.
          DEFB  $05             ;;division          17, 17/3.
          DEFB  $24             ;;int               17, 5.
          DEFB  $E0             ;;get-mem-0         17, 5, 3.
          DEFB  $01             ;;exchange          17, 3, 5.
          DEFB  $C0             ;;st-mem-0          17, 3, 5.
          DEFB  $04             ;;multiply          17, 15.
          DEFB  $03             ;;subtract          2.
          DEFB  $E0             ;;get-mem-0         2, 5.
          DEFB  $34             ;;end-calc          2, 5.

          RET                   ; return.


; ----------------------
; THE 'INTEGER' FUNCTION
; ----------------------
; (offset $24: 'int')
; This function returns the integer of x, which is just the same as truncate
; for positive numbers. The truncate literal truncates negative numbers
; upwards so that -3.4 gives -3 whereas the BASIC INT function has to
; truncate negative numbers down so that INT -3.4 is 4.
; It is best to work through using, say, plus and minus 3.4 as examples.

int       RST   28H             ;; FP-CALC              x.    (= 3.4 or -3.4).
          DEFB  $2D             ;;duplicate             x, x.
          DEFB  $32             ;;less-0                x, (1/0)
          DEFB  $00             ;;jump-true             x, (1/0)
          DEFB  $04             ;;to L1C46, X-NEG

          DEFB  $36             ;;truncate              trunc 3.4 = 3.
          DEFB  $34             ;;end-calc              3.

          RET                   ; return with + int x on stack.


X_NEG     DEFB  $2D             ;;duplicate             -3.4, -3.4.
          DEFB  $36             ;;truncate              -3.4, -3.
          DEFB  $C0             ;;st-mem-0              -3.4, -3.
          DEFB  $03             ;;subtract              -.4
          DEFB  $E0             ;;get-mem-0             -.4, -3.
          DEFB  $01             ;;exchange              -3, -.4.
          DEFB  $2C             ;;not                   -3, (0).
          DEFB  $00             ;;jump-true             -3.
          DEFB  $03             ;;to L1C59, EXIT        -3.

          DEFB  $A1             ;;stk-one               -3, 1.
          DEFB  $03             ;;subtract              -4.

EXIT      DEFB  $34             ;;end-calc              -4.

          RET                   ; return.


; --------------------------
; THE 'EXPONENTIAL' FUNCTION
; --------------------------
; (Offset $23: 'exp')
;   The exponential function returns the exponential of the argument, or the
;   value of 'e' (2.7182818...) raised to the power of the argument.
;   PRINT EXP 1 gives 2.7182818
;
;   EXP is the opposite of the LN function (see below) and is equivalent to 
;   the 'antiln' function found on pocket calculators or the 'Inverse ln'
;   function found on the Windows scientific calculator.
;   So PRINT EXP LN 5.3 will give 5.3 as will PRINT LN EXP 5.3 or indeed
;   any number e.g. PRINT EXP LN PI.
;
;   The applications of the exponential function are in areas where exponential
;   growth is experienced, calculus, population growth and compound interest.
;
;   Error 6 if the argument is above 88.

exp       RST   28H             ;; FP-CALC
          DEFB  $30             ;;stk-data			1/LN 2
          DEFB  $F1             ;;Exponent: $81, Bytes: 4
          DEFB  $38,$AA,$3B,$29 ;;
          DEFB  $04             ;;multiply
          DEFB  $2D             ;;duplicate
          DEFB  $24             ;;int
          DEFB  $C3             ;;st-mem-3
          DEFB  $03             ;;subtract
          DEFB  $2D             ;;duplicate
          DEFB  $0F             ;;addition
          DEFB  $A1             ;;stk-one
          DEFB  $03             ;;subtract
          DEFB  $88             ;;series-08
          DEFB  $13             ;;Exponent: $63, Bytes: 1
          DEFB  $36             ;;(+00,+00,+00)
          DEFB  $58             ;;Exponent: $68, Bytes: 2
          DEFB  $65,$66         ;;(+00,+00)
          DEFB  $9D             ;;Exponent: $6D, Bytes: 3
          DEFB  $78,$65,$40     ;;(+00)
          DEFB  $A2             ;;Exponent: $72, Bytes: 3
          DEFB  $60,$32,$C9     ;;(+00)
          DEFB  $E7             ;;Exponent: $77, Bytes: 4
          DEFB  $21,$F7,$AF,$24 ;;
          DEFB  $EB             ;;Exponent: $7B, Bytes: 4
          DEFB  $2F,$B0,$B0,$14 ;;
          DEFB  $EE             ;;Exponent: $7E, Bytes: 4
          DEFB  $7E,$BB,$94,$58 ;;
          DEFB  $F1             ;;Exponent: $81, Bytes: 4
          DEFB  $3A,$7E,$F8,$CF ;;
          DEFB  $E3             ;;get-mem-3
          DEFB  $34             ;;end-calc

          CALL  FP_TO_A         ; routine FP-TO-A
          JR    NZ,N_NEGTV      ; to N-NEGTV

          JR    C,REPORT_6b     ; to REPORT-6b

          ADD   A,(HL)          ;
          JR    NC,RESULT_OK    ; to RESULT-OK


REPORT_6b RST   08H             ; ERROR-1
          DEFB  $05             ; Error Report: Number too big

N_NEGTV   JR    C,RSLT_ZERO     ; to RSLT-ZERO

          SUB   (HL)            ;
          JR    NC,RSLT_ZERO    ; to RSLT-ZERO

          NEG                   ; Negate

RESULT_OK LD    (HL),A          ;
          RET                   ; return.


RSLT_ZERO RST   28H             ;; FP-CALC
          DEFB  $02             ;;delete
          DEFB  $A0             ;;stk-zero
          DEFB  $34             ;;end-calc

          RET                   ; return.


; --------------------------------
; THE 'NATURAL LOGARITHM' FUNCTION
; --------------------------------
; (offset $22: 'ln')
;   Like the ZX81 itself, 'natural' logarithms came from Scotland.
;   They were devised in 1614 by well-traveled Scotsman John Napier who noted
;   "Nothing doth more molest and hinder calculators than the multiplications,
;    divisions, square and cubical extractions of great numbers".
;   Napier's logarithms enabled the above operations to be accomplished by 
;   simple addition and subtraction simplifying the navigational and 
;   astronomical calculations which beset his age.
;   Napier's logarithms were quickly overtaken by logarithms to the base 10
;   devised, in conjunction with Napier, by Henry Briggs a Cambridge-educated 
;   professor of Geometry at Oxford University. These simplified the layout
;   of the tables enabling humans to easily scale calculations.
;
;   It is only recently with the introduction of pocket calculators and
;   computers like the ZX81 that natural logarithms are once more at the fore,
;   although some computers retain logarithms to the base ten.
;   'Natural' logarithms are powers to the base 'e', which like 'pi' is a 
;   naturally occurring number in branches of mathematics.
;   Like 'pi' also, 'e' is an irrational number and starts 2.718281828...
;
;   The tabular use of logarithms was that to multiply two numbers one looked
;   up their two logarithms in the tables, added them together and then looked 
;   for the result in a table of antilogarithms to give the desired product.
;
;   The EXP function is the BASIC equivalent of a calculator's 'antiln' function 
;   and by picking any two numbers, 1.72 and 6.89 say,
;     10 PRINT EXP ( LN 1.72 + LN 6.89 ) 
;   will give just the same result as
;     20 PRINT 1.72 * 6.89.
;   Division is accomplished by subtracting the two logs.
;
;   Napier also mentioned "square and cubicle extractions". 
;   To raise a number to the power 3, find its 'ln', multiply by 3 and find the 
;   'antiln'.  e.g. PRINT EXP( LN 4 * 3 )  gives 64.
;   Similarly to find the n'th root divide the logarithm by 'n'.
;   The ZX81 ROM used PRINT EXP ( LN 9 / 2 ) to find the square root of the 
;   number 9. The Napieran square root function is just a special case of 
;   the 'to_power' function. A cube root or indeed any root/power would be just
;   as simple.

;   First test that the argument to LN is a positive, non-zero number.


ln        RST   28H             ;; FP-CALC		x.
          DEFB  $2D             ;;duplicate		x,x.
          DEFB  $33             ;;greater-0		x,(0/1).
          DEFB  $00             ;;jump-true		x.
          DEFB  $04             ;;to L1CB1, VALID

          DEFB  $34             ;;end-calc		x.


REPORT_Ab RST   08H             ; ERROR-1
          DEFB  $09             ; Error Report: Invalid argument

VALID      	 
;;;       DEFB  $A0             ;;stk-zero	
;;;       DEFB  $02             ;;delete
          DEFB  $34             ;;end-calc		x.

;   Register HL addresses the 'last value' x.

          LD    A,(HL)          ; Fetch exponent to A.

          LD    (HL),$80        ; Insert 'plus zero' as exponent.
          CALL  STACK_A         ; routine STACK-A stacks true binary exponent.
		
          RST   28H             ;; FP-CALC
          DEFB  $30             ;;stk-data
          DEFB  $38             ;;Exponent: $88, Bytes: 1
          DEFB  $00             ;;(+00,+00,+00)
          DEFB  $03             ;;subtract
          DEFB  $01             ;;exchange
          DEFB  $2D             ;;duplicate
          DEFB  $30             ;;stk-data
          DEFB  $F0             ;;Exponent: $80, Bytes: 4
          DEFB  $4C,$CC,$CC,$CD ;;
          DEFB  $03             ;;subtract
          DEFB  $33             ;;greater-0
          DEFB  $00             ;;jump-true
          DEFB  $08             ;;to L1CD2, GRE.8

          DEFB  $01             ;;exchange
          DEFB  $A1             ;;stk-one
          DEFB  $03             ;;subtract
          DEFB  $01             ;;exchange
          DEFB  $34             ;;end-calc

          INC   (HL)            ;

          RST   28H             ;; FP-CALC

GRE_8     DEFB  $01             ;;exchange
          DEFB  $30             ;;stk-data			LN 2
          DEFB  $F0             ;;Exponent: $80, Bytes: 4
          DEFB  $31,$72,$17,$F8 ;;
          DEFB  $04             ;;multiply
          DEFB  $01             ;;exchange
          DEFB  $A2             ;;stk-half
          DEFB  $03             ;;subtract
          DEFB  $A2             ;;stk-half
          DEFB  $03             ;;subtract
          DEFB  $2D             ;;duplicate
          DEFB  $30             ;;stk-data
          DEFB  $32             ;;Exponent: $82, Bytes: 1
          DEFB  $20             ;;(+00,+00,+00)
          DEFB  $04             ;;multiply
          DEFB  $A2             ;;stk-half
          DEFB  $03             ;;subtract
          DEFB  $8C             ;;series-0C
          DEFB  $11             ;;Exponent: $61, Bytes: 1
          DEFB  $AC             ;;(+00,+00,+00)
          DEFB  $14             ;;Exponent: $64, Bytes: 1
          DEFB  $09             ;;(+00,+00,+00)
          DEFB  $56             ;;Exponent: $66, Bytes: 2
          DEFB  $DA,$A5         ;;(+00,+00)
          DEFB  $59             ;;Exponent: $69, Bytes: 2
          DEFB  $30,$C5         ;;(+00,+00)
          DEFB  $5C             ;;Exponent: $6C, Bytes: 2
          DEFB  $90,$AA         ;;(+00,+00)
          DEFB  $9E             ;;Exponent: $6E, Bytes: 3
          DEFB  $70,$6F,$61     ;;(+00)
          DEFB  $A1             ;;Exponent: $71, Bytes: 3
          DEFB  $CB,$DA,$96     ;;(+00)
          DEFB  $A4             ;;Exponent: $74, Bytes: 3
          DEFB  $31,$9F,$B4     ;;(+00)
          DEFB  $E7             ;;Exponent: $77, Bytes: 4
          DEFB  $A0,$FE,$5C,$FC ;;
          DEFB  $EA             ;;Exponent: $7A, Bytes: 4
          DEFB  $1B,$43,$CA,$36 ;;
          DEFB  $ED             ;;Exponent: $7D, Bytes: 4
          DEFB  $A7,$9C,$7E,$5E ;;
          DEFB  $F0             ;;Exponent: $80, Bytes: 4
          DEFB  $6E,$23,$80,$93 ;;
          DEFB  $04             ;;multiply
          DEFB  $0F             ;;addition
          DEFB  $34             ;;end-calc

          RET                   ; return.

; ------------------------------
; THE NEW 'SQUARE ROOT' FUNCTION
; ------------------------------
; (Offset $25: 'sqr')
;   "If I have seen further, it is by standing on the shoulders of giants" -
;   Sir Isaac Newton, Cambridge 1676.
;   The sqr function has been re-written to use the Newton-Raphson method.
;   Joseph Raphson was a student of Sir Isaac Newton at Cambridge University
;   and helped publicize his work.
;   Although Newton's method is centuries old, this routine, appropriately, is 
;   based on a FORTH word written by Steven Vickers in the Jupiter Ace manual.
;   Whereas that method uses an initial guess of one, this one manipulates 
;   the exponent byte to obtain a better starting guess. 
;   First test for zero and return zero, if so, as the result.
;   If the argument is negative, then produce an error.

sqr       RST   28H             ;; FP-CALC              x
          DEFB  $C3             ;;st-mem-3              x.   (seed for guess)
          DEFB  $34             ;;end-calc		x.

;   HL now points to exponent of argument on calculator stack.

          LD    A,(HL)          ; Test for zero argument
          AND   A               ; 

          RET   Z               ; Return with zero on the calculator stack.

;   Test for a positive argument

          INC   HL              ; Address byte with sign bit.
          BIT   7,(HL)          ; Test the bit.

          JR    NZ,REPORT_Ab    ; back to REPORT_A 
                                ; 'Invalid argument'
 
;   This guess is based on a Usenet discussion.
;   Halve the exponent to achieve a good guess.(accurate with .25 16 64 etc.)

          LD    HL,$4071        ; Address first byte of mem-3

          LD    A,(HL)          ; fetch exponent of mem-3
          XOR   $80             ; toggle sign of exponent of mem-3
          SRA   A               ; shift right, bit 7 unchanged.
          INC   A               ;
          JR    Z,ASIS          ; forward with say .25 -> .5
          JP    P,ASIS          ; leave increment if value > .5
          DEC   A               ; restore to shift only.
ASIS      XOR   $80             ; restore sign.
          LD    (HL),A          ; and put back 'halved' exponent.

;   Now re-enter the calculator.

          RST   28H             ;; FP-CALC              x

SLOOP     DEFB  $2D             ;;duplicate             x,x.
          DEFB  $E3             ;;get-mem-3             x,x,guess
          DEFB  $C4             ;;st-mem-4              x,x,guess
          DEFB  $05             ;;div                   x,x/guess.
          DEFB  $E3             ;;get-mem-3             x,x/guess,guess
          DEFB  $0F             ;;addition              x,x/guess+guess
          DEFB  $A2             ;;stk-half              x,x/guess+guess,.5
          DEFB  $04             ;;multiply              x,(x/guess+guess)*.5
          DEFB  $C3             ;;st-mem-3              x,newguess
          DEFB  $E4             ;;get-mem-4             x,newguess,oldguess
          DEFB  $03             ;;subtract              x,newguess-oldguess
          DEFB  $27             ;;abs                   x,difference.
          DEFB  $33             ;;greater-0             x,(0/1).
          DEFB  $00             ;;jump-true             x.

          DEFB  SLOOP - $       ;;to sloop              x.

          DEFB  $02             ;;delete                .
          DEFB  $E3             ;;get-mem-3             retrieve final guess.
          DEFB  $34             ;;end-calc              sqr x.

          RET                  ; return with square root on stack

;   or in ZX81 BASIC
;
;      5 PRINT "NEWTON RAPHSON SQUARE ROOTS"
;     10 INPUT "NUMBER ";N
;     20 INPUT "GUESS ";G
;     30 PRINT " NUMBER "; N ;" GUESS "; G
;     40 FOR I = 1 TO 10
;     50  LET B = N/G
;     60  LET C = B+G
;     70  LET G = C/2
;     80  PRINT I; " VALUE "; G
;     90 NEXT I
;    100 PRINT "NAPIER METHOD"; SQR N

; -----------------------------
; THE 'TRIGONOMETRIC' FUNCTIONS
; -----------------------------
;   Trigonometry is rocket science. It is also used by carpenters and pyramid
;   builders. 
;   Some uses can be quite abstract but the principles can be seen in simple
;   right-angled triangles. Triangles have some special properties -
;
;   1) The sum of the three angles is always PI radians (180 degrees).
;      Very helpful if you know two angles and wish to find the third.
;   2) In any right-angled triangle the sum of the squares of the two shorter
;      sides is equal to the square of the longest side opposite the right-angle.
;      Very useful if you know the length of two sides and wish to know the
;      length of the third side.
;   3) Functions sine, cosine and tangent enable one to calculate the length 
;      of an unknown side when the length of one other side and an angle is 
;      known.
;   4) Functions arcsin, arccosine and arctan enable one to calculate an unknown
;      angle when the length of two of the sides is known.

; --------------------------------
; THE 'REDUCE ARGUMENT' SUBROUTINE
; --------------------------------
; (offset $35: 'get-argt')
;
;   This routine performs two functions on the angle, in radians, that forms
;   the argument to the sine and cosine functions.
;   First it ensures that the angle 'wraps round'. That if a ship turns through 
;   an angle of, say, 3*PI radians (540 degrees) then the net effect is to turn 
;   through an angle of PI radians (180 degrees).
;   Secondly it converts the angle in radians to a fraction of a right angle,
;   depending within which quadrant the angle lies, with the periodicity 
;   resembling that of the desired sine value.
;   The result lies in the range -1 to +1.              
;
;                       90 deg.
; 
;                       (pi/2)
;                II       +1        I
;                         |
;          sin+      |\   |   /|    sin+
;          cos-      | \  |  / |    cos+
;          tan-      |  \ | /  |    tan+
;                    |   \|/)  |           
;   180 deg. (pi) 0 -|----+----|-- 0  (0)   0 degrees
;                    |   /|\   |
;          sin-      |  / | \  |    sin-
;          cos-      | /  |  \ |    cos+
;          tan+      |/   |   \|    tan-
;                         |
;                III      -1       IV
;                       (3pi/2)
;
;                       270 deg.


get_argt  RST   28H             ;; FP-CALC         X.
          DEFB  $30             ;;stk-data
          DEFB  $EE             ;;Exponent: $7E, 
                                ;;Bytes: 4
          DEFB  $22,$F9,$83,$6E ;;                 X, 1/(2*PI)             
          DEFB  $04             ;;multiply         X/(2*PI) = fraction

          DEFB  $2D             ;;duplicate             
          DEFB  $A2             ;;stk-half
          DEFB  $0F             ;;addition
          DEFB  $24             ;;int

          DEFB  $03             ;;subtract         now range -.5 to .5

          DEFB  $2D             ;;duplicate
          DEFB  $0F             ;;addition         now range -1 to 1.
          DEFB  $2D             ;;duplicate
          DEFB  $0F             ;;addition         now range -2 to 2.

;   quadrant I (0 to +1) and quadrant IV (-1 to 0) are now correct.
;   quadrant II ranges +1 to +2.
;   quadrant III ranges -2 to -1.

          DEFB  $2D             ;;duplicate        Y, Y.
          DEFB  $27             ;;abs              Y, abs(Y).    range 1 to 2
          DEFB  $A1             ;;stk-one          Y, abs(Y), 1.
          DEFB  $03             ;;subtract         Y, abs(Y)-1.  range 0 to 1
          DEFB  $2D             ;;duplicate        Y, Z, Z.
          DEFB  $33             ;;greater-0        Y, Z, (1/0).

          DEFB  $C0             ;;st-mem-0         store as possible sign 
                                ;;                 for cosine function.

          DEFB  $00             ;;jump-true
          DEFB  $04             ;;to L1D35, ZPLUS  with quadrants II and III

;   else the angle lies in quadrant I or IV and value Y is already correct.

          DEFB  $02             ;;delete          Y    delete test value.
          DEFB  $34             ;;end-calc        Y.

          RET                   ; return.         with Q1 and Q4 >>>

;   The branch was here with quadrants II (0 to 1) and III (1 to 0).
;   Y will hold -2 to -1 if this is quadrant III.

ZPLUS     DEFB  $A1             ;;stk-one         Y, Z, 1
          DEFB  $03             ;;subtract        Y, Z-1.       Q3 = 0 to -1
          DEFB  $01             ;;exchange        Z-1, Y.
          DEFB  $32             ;;less-0          Z-1, (1/0).
          DEFB  $00             ;;jump-true       Z-1.
          DEFB  $02             ;;to L1D3C, YNEG
                                ;;if angle in quadrant III

;   else angle is within quadrant II (-1 to 0)

          DEFB  $18             ;;negate          range +1 to 0


YNEG      DEFB  $34             ;;end-calc        quadrants II and III correct.

          RET                   ; return.


; ---------------------
; THE 'COSINE' FUNCTION
; ---------------------
; (offset $1D: 'cos')
;   Cosines are calculated as the sine of the opposite angle rectifying the 
;   sign depending on the quadrant rules. 
;
;
;             /|
;          h /y|
;           /  |o
;          /x  |
;         /----|    
;           a
;
;   The cosine of angle x is the adjacent side (a) divided by the hypotenuse 1.
;   However if we examine angle y then a/h is the sine of that angle.
;   Since angle x plus angle y equals a right-angle, we can find angle y by 
;   subtracting angle x from pi/2.
;   However it's just as easy to reduce the argument first and subtract the
;   reduced argument from the value 1 (a reduced right-angle).
;   It's even easier to subtract 1 from the angle and rectify the sign.
;   In fact, after reducing the argument, the absolute value of the argument
;   is used and rectified using the test result stored in mem-0 by 'get-argt'
;   for that purpose.

cos       RST   28H             ;; FP-CALC              angle in radians.
          DEFB  $35             ;;get-argt              X       reduce -1 to +1

          DEFB  $27             ;;abs                   ABS X   0 to 1
          DEFB  $A1             ;;stk-one               ABS X, 1.
          DEFB  $03             ;;subtract              now opposite angle 
                                ;;                      though negative sign.
          DEFB  $E0             ;;get-mem-0             fetch sign indicator.
          DEFB  $00             ;;jump-true
          DEFB  $06             ;;fwd to L1D4B, C-ENT
                                ;;forward to common code if in QII or QIII 


          DEFB  $18             ;;negate                else make positive.
          DEFB  $2F             ;;jump
          DEFB  $03             ;;fwd to L1D4B, C-ENT
                                ;;with quadrants QI and QIV 

; -------------------
; THE 'SINE' FUNCTION
; -------------------
; (offset $1C: 'sin')
;   This is a fundamental transcendental function from which others such as cos
;   and tan are directly, or indirectly, derived.
;   It uses the series generator to produce Chebyshev polynomials.
;
;
;             /|
;          1 / |
;           /  |x
;          /a  |
;         /----|    
;           y
;
;   The 'get-argt' function is designed to modify the angle and its sign 
;   in line with the desired sine value and afterwards it can launch straight
;   into common code.

sin       RST   28H             ;; FP-CALC      angle in radians
          DEFB  $35             ;;get-argt      reduce - sign now correct.

C_ENT     DEFB  $2D             ;;duplicate
          DEFB  $2D             ;;duplicate
          DEFB  $04             ;;multiply
          DEFB  $2D             ;;duplicate
          DEFB  $0F             ;;addition
          DEFB  $A1             ;;stk-one
          DEFB  $03             ;;subtract

          DEFB  $86             ;;series-06
          DEFB  $14             ;;Exponent: $64, Bytes: 1
          DEFB  $E6             ;;(+00,+00,+00)
          DEFB  $5C             ;;Exponent: $6C, Bytes: 2
          DEFB  $1F,$0B         ;;(+00,+00)
          DEFB  $A3             ;;Exponent: $73, Bytes: 3
          DEFB  $8F,$38,$EE     ;;(+00)
          DEFB  $E9             ;;Exponent: $79, Bytes: 4
          DEFB  $15,$63,$BB,$23 ;;
          DEFB  $EE             ;;Exponent: $7E, Bytes: 4
          DEFB  $92,$0D,$CD,$ED ;;
          DEFB  $F1             ;;Exponent: $81, Bytes: 4
          DEFB  $23,$5D,$1B,$EA ;;

          DEFB  $04             ;;multiply
          DEFB  $34             ;;end-calc

          RET                   ; return.


; ----------------------
; THE 'TANGENT' FUNCTION
; ----------------------
; (offset $1E: 'tan')
;
;   Evaluates tangent x as    sin(x) / cos(x).
;
;
;             /|
;          h / |
;           /  |o
;          /x  |
;         /----|    
;           a
;
;   The tangent of angle x is the ratio of the length of the opposite side 
;   divided by the length of the adjacent side.  As the opposite length can 
;   be calculates using sin(x) and the adjacent length using cos(x) then 
;   the tangent can be defined in terms of the previous two functions.

;   Error 6 if the argument, in radians, is too close to one like pi/2
;   which has an infinite tangent. e.g. PRINT TAN (PI/2)  evaluates as 1/0.
;   Similarly PRINT TAN (3*PI/2), TAN (5*PI/2) etc.

tan       RST   28H             ;; FP-CALC          x.
          DEFB  $2D             ;;duplicate         x, x.
          DEFB  $1C             ;;sin               x, sin x.
          DEFB  $01             ;;exchange          sin x, x.
          DEFB  $1D             ;;cos               sin x, cos x.
          DEFB  $05             ;;division          sin x/cos x (= tan x).
          DEFB  $34             ;;end-calc          tan x.

          RET                   ; return.

; ---------------------
; THE 'ARCTAN' FUNCTION
; ---------------------
; (Offset $21: 'atn')
;   The inverse tangent function with the result in radians.
;   This is a fundamental transcendental function from which others such as asn
;   and acs are directly, or indirectly, derived.
;   It uses the series generator to produce Chebyshev polynomials.

atn       LD    A,(HL)          ; fetch exponent
          CP    $81             ; compare to that for 'one'
          JR    C,SMALL         ; forward, if less, to SMALL

          RST   28H             ;; FP-CALC      X.
          DEFB  $A1             ;;stk-one
          DEFB  $18             ;;negate
          DEFB  $01             ;;exchange
          DEFB  $05             ;;division
          DEFB  $2D             ;;duplicate
          DEFB  $32             ;;less-0
          DEFB  $A3             ;;stk-pi/2
          DEFB  $01             ;;exchange
          DEFB  $00             ;;jump-true
          DEFB  $06             ;;to L1D8B, CASES

          DEFB  $18             ;;negate
          DEFB  $2F             ;;jump
          DEFB  $03             ;;to L1D8B, CASES

; ---

SMALL     RST   28H             ;; FP-CALC
          DEFB  $A0             ;;stk-zero

CASES     DEFB  $01             ;;exchange
          DEFB  $2D             ;;duplicate
          DEFB  $2D             ;;duplicate
          DEFB  $04             ;;multiply
          DEFB  $2D             ;;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  $34             ;;end-calc

          RET                   ; return.


; ---------------------
; THE 'ARCSIN' FUNCTION
; ---------------------
; (Offset $1F: '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
;
;
;               . /|
;            .  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 degress 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       RST   28H             ;; FP-CALC      x.
          DEFB  $2D             ;;duplicate     x, x.
          DEFB  $2D             ;;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  $18             ;;negate        x, 1-x*x.
          DEFB  $25             ;;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  $21             ;;atn           a/2     (half the angle)
          DEFB  $2D             ;;duplicate     a/2, a/2.
          DEFB  $0F             ;;addition      a.
          DEFB  $34             ;;end-calc      a.

          RET                   ; return.


; ------------------------
; THE 'ARCCOS' FUNCTION
; ------------------------
; (Offset $20: 'acs')
; the inverse cosine function with the result in radians.
; Error A unless the argument is between -1 and +1.
; Result in range 0 to pi.
; Derived from asn above which is in turn derived from the preceding atn.
; It could have been derived directly from atn using acs(x) = atn(sqr(1-x*x)/x).
; However, as sine and cosine are horizontal translations of each other,
; uses acs(x) = pi/2 - asn(x)

; e.g. the arccosine of a known x value will give the required angle b in 
; radians.
; We know, from above, how to calculate the angle a using asn(x). 
; Since the three angles of any triangle add up to 180 degrees, or pi radians,
; and the largest angle in this case is a right-angle (pi/2 radians), then
; we can calculate angle b as pi/2 (both angles) minus asn(x) (angle a).
; 
;
;           /|
;        1 /b|
;         /  |x
;        /a  |
;       /----|    
;         y
;

acs       RST   28H             ;; FP-CALC      x.
          DEFB  $1F             ;;asn           asn(x).
          DEFB  $A3             ;;stk-pi/2      asn(x), pi/2.
          DEFB  $03             ;;subtract      asn(x) - pi/2.
          DEFB  $18             ;;negate        pi/2 - asn(x) = acs(x).
          DEFB  $34             ;;end-calc      acs(x)

          RET                   ; return.


; --------------------------
; THE OLD 'SQUARE ROOT' FUNCTION
; --------------------------
; (Offset $25: 'sqr')
; Error A if argument is negative.
; This routine is remarkable for its brevity - 7 bytes.
; This routine uses Napier's method for calculating square roots which was 
; devised in 1614 and calculates the value as EXP (LN 'x' * 0.5).
;
; This is a little on the slow side as it involves two polynomial series.
; A series of 12 for LN and a series of 8 for EXP.  This was of no concern
; to John Napier since his tables were 'compiled forever'.
;
;;; L1DDB:  RST     28H             ;; FP-CALC              x.
;;;         DEFB    $2D             ;;duplicate             x, x.
;;;         DEFB    $2C             ;;not                   x, 1/0
;;;         DEFB    $00             ;;jump-true             x, (1/0).
;;;         DEFB    $1E             ;;to L1DFD, LAST        exit if argument zero
;;;                                 ;;                      with zero result.
;;;
;;; else continue to calculate as x ** .5
;;;
;;;         DEFB    $A2             ;;stk-half              x, .5.
;;;         DEFB    $34             ;;end-calc              x, .5.


; ------------------------
; THE 'TO POWER' OPERATION
; ------------------------
; (Offset $06: 'to-power')
;   The 'Exponential' operation.
;   This raises the first number X to the power of the second number Y.
;   e.g. PRINT 2 ** 3 gives the result 8
;   As with the ZX80,
;   0 ** 0 = 1
;   0 ** +n = 0
;   0 ** -n = arithmetic overflow.

to_power  RST   28H             ;; FP-CALC              X,Y.
          DEFB  $01             ;;exchange              Y,X.
          DEFB  $2D             ;;duplicate             Y,X,X.
          DEFB  $2C             ;;not                   Y,X,(1/0).
          DEFB  $00             ;;jump-true
          DEFB  $07             ;;forward to L1DEE, XISO if X is zero.

;   else X is non-zero. function 'ln' will catch a negative value of X.

          DEFB  $22             ;;ln                    Y, LN X.

;   Multiply the power by the logarithm of the argument.

          DEFB  $04             ;;multiply              Y * LN X
          DEFB  $34             ;;end-calc

          JP    exp             ; jump back to EXP routine             ->> 
				; to find the 'antiln'

; ---

;   these routines form the three simple results when the number is zero.
;   begin by deleting the known zero to leave Y the power factor.

XISO      DEFB  $02             ;;delete                Y.
          DEFB  $2D             ;;duplicate             Y, Y.
          DEFB  $2C             ;;not                   Y, (1/0).
          DEFB  $00             ;;jump-true     
          DEFB  $09             ;;forward to L1DFB, ONE if Y is zero.

;   the power factor is not zero. If negative then an error exists.

          DEFB  $A0             ;;stk-zero              Y, 0.
          DEFB  $01             ;;exchange              0, Y.
          DEFB  $33             ;;greater-0             0, (1/0).
          DEFB  $00             ;;jump-true             0
          DEFB  $06             ;;to L1DFD, LAST        if Y was any positive 
                                ;;                      number.

;   else force division by zero thereby raising an Arithmetic overflow error.
;   As an alternative, this now raises an error directly.

;;;       DEFB  $A1             ;;stk-one               0, 1.
;;;       DEFB  $01             ;;exchange              1, 0.
;;;       DEFB  $05             ;;division              1/0    >> error 

          DEFB  $34             ;+ end-calc
REPORT_6c RST   08H             ;+ ERROR-1
          DEFB  $05             ;+ Error Report: Number too big

; ---

ONE       DEFB  $02             ;;delete                .
          DEFB  $A1             ;;stk-one               1.

LAST      DEFB  $34             ;;end-calc              last value 1 or 0.

          RET                   ; return.

; ---------------------
; THE 'SPARE LOCATIONS'
; ---------------------

L1DFE:

          DEFB  $FF, $FF	; Two spare bytes.


ORG    $1E00

; ------------------------
; THE 'ZX81 CHARACTER SET'
; ------------------------


; $00 - Character: ' '          CHR$(0)

char_set  DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000000

; $01 - Character: mosaic       CHR$(1)

          DEFB  %11110000
          DEFB  %11110000
          DEFB  %11110000
          DEFB  %11110000
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000000


; $02 - Character: mosaic       CHR$(2)

          DEFB  %00001111
          DEFB  %00001111
          DEFB  %00001111
          DEFB  %00001111
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000000


; $03 - Character: mosaic       CHR$(3)

          DEFB  %11111111
          DEFB  %11111111
          DEFB  %11111111
          DEFB  %11111111
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000000

; $04 - Character: mosaic       CHR$(4)

          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %11110000
          DEFB  %11110000
          DEFB  %11110000
          DEFB  %11110000

; $05 - Character: mosaic       CHR$(1)

          DEFB  %11110000
          DEFB  %11110000
          DEFB  %11110000
          DEFB  %11110000
          DEFB  %11110000
          DEFB  %11110000
          DEFB  %11110000
          DEFB  %11110000

; $06 - Character: mosaic       CHR$(1)

          DEFB  %00001111
          DEFB  %00001111
          DEFB  %00001111
          DEFB  %00001111
          DEFB  %11110000
          DEFB  %11110000
          DEFB  %11110000
          DEFB  %11110000

; $07 - Character: mosaic       CHR$(1)

          DEFB  %11111111
          DEFB  %11111111
          DEFB  %11111111
          DEFB  %11111111
          DEFB  %11110000
          DEFB  %11110000
          DEFB  %11110000
          DEFB  %11110000

; $08 - Character: mosaic       CHR$(1)

          DEFB  %10101010
          DEFB  %01010101
          DEFB  %10101010
          DEFB  %01010101
          DEFB  %10101010
          DEFB  %01010101
          DEFB  %10101010
          DEFB  %01010101

; $09 - Character: mosaic       CHR$(1)

          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %10101010
          DEFB  %01010101
          DEFB  %10101010
          DEFB  %01010101

; $0A - Character: mosaic       CHR$(10)

          DEFB  %10101010
          DEFB  %01010101
          DEFB  %10101010
          DEFB  %01010101
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000000

; $0B - Character: '"'          CHR$(11)

          DEFB  %00000000
          DEFB  %00100100
          DEFB  %00100100
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000000

; $0B - Character: 'œ'          CHR$(12)

          DEFB  %00000000
          DEFB  %00011100
          DEFB  %00100010
          DEFB  %01111000
          DEFB  %00100000
          DEFB  %00100000
          DEFB  %01111110
          DEFB  %00000000

; $0B - Character: '$'          CHR$(13)

          DEFB  %00000000
          DEFB  %00001000
          DEFB  %00111110
          DEFB  %00101000
          DEFB  %00111110
          DEFB  %00001010
          DEFB  %00111110
          DEFB  %00001000

; $0B - Character: ':'          CHR$(14)

          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00010000
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00010000
          DEFB  %00000000

; $0B - Character: '?'          CHR$(15)

          DEFB  %00000000
          DEFB  %00111100
          DEFB  %01000010
          DEFB  %00000100
          DEFB  %00001000
          DEFB  %00000000
          DEFB  %00001000
          DEFB  %00000000

; $10 - Character: '('          CHR$(16)

          DEFB  %00000000
          DEFB  %00000100
          DEFB  %00001000
          DEFB  %00001000
          DEFB  %00001000
          DEFB  %00001000
          DEFB  %00000100
          DEFB  %00000000

; $11 - Character: ')'          CHR$(17)

          DEFB  %00000000
          DEFB  %00100000
          DEFB  %00010000
          DEFB  %00010000
          DEFB  %00010000
          DEFB  %00010000
          DEFB  %00100000
          DEFB  %00000000

; $12 - Character: '>'          CHR$(18)

          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00010000
          DEFB  %00001000
          DEFB  %00000100
          DEFB  %00001000
          DEFB  %00010000
          DEFB  %00000000

; $13 - Character: '<'          CHR$(19)

          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000100
          DEFB  %00001000
          DEFB  %00010000
          DEFB  %00001000
          DEFB  %00000100
          DEFB  %00000000

; $14 - Character: '='          CHR$(20)

          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00111110
          DEFB  %00000000
          DEFB  %00111110
          DEFB  %00000000
          DEFB  %00000000

; $15 - Character: '+'          CHR$(21)

          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00001000
          DEFB  %00001000
          DEFB  %00111110
          DEFB  %00001000
          DEFB  %00001000
          DEFB  %00000000

; $16 - Character: '-'          CHR$(22)

          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00111110
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000000

; $17 - Character: '*'          CHR$(23)

          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00010100
          DEFB  %00001000
          DEFB  %00111110
          DEFB  %00001000
          DEFB  %00010100
          DEFB  %00000000

; $18 - Character: '/'          CHR$(24)

          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000010
          DEFB  %00000100
          DEFB  %00001000
          DEFB  %00010000
          DEFB  %00100000
          DEFB  %00000000

; $19 - Character: ';'          CHR$(25)

          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00010000
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00010000
          DEFB  %00010000
          DEFB  %00100000

; $1A - Character: ','          CHR$(26)

          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00001000
          DEFB  %00001000
          DEFB  %00010000

; $1B - Character: '"'          CHR$(27)

          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00000000
          DEFB  %00011000
          DEFB  %00011000
          DEFB  %00000000

; $1C - Character: '0'          CHR$(28)

          DEFB  %00000000
          DEFB  %00111100
          DEFB  %01000110
          DEFB  %01001010
          DEFB  %01010010
          DEFB  %01100010
          DEFB  %00111100
          DEFB  %00000000

; $1D - Character: '1'          CHR$(29)

          DEFB  %00000000
          DEFB  %00011000
          DEFB  %00101000
          DEFB  %00001000
          DEFB  %00001000
          DEFB  %00001000
          DEFB  %00111110
          DEFB  %00000000

; $1E - Character: '2'          CHR$(30)

          DEFB  %00000000
          DEFB  %00111100
          DEFB  %01000010
          DEFB  %00000010
          DEFB  %00111100
          DEFB  %01000000
          DEFB  %01111110
          DEFB  %00000000

; $1F - Character: '3'          CHR$(31)

          DEFB  %00000000
          DEFB  %00111100
          DEFB  %01000010
          DEFB  %00001100
          DEFB  %00000010
          DEFB  %01000010
          DEFB  %00111100
          DEFB  %00000000

; $20 - Character: '4'          CHR$(32)

          DEFB  %00000000
          DEFB  %00001000
          DEFB  %00011000
          DEFB  %00101000
          DEFB  %01001000
          DEFB  %01111110
          DEFB  %00001000
          DEFB  %00000000

; $21 - Character: '5'          CHR$(33)

          DEFB  %00000000
          DEFB  %01111110
          DEFB  %01000000
          DEFB  %01111100
          DEFB  %00000010
          DEFB  %01000010
          DEFB  %00111100
          DEFB  %00000000

; $22 - Character: '6'          CHR$(34)

          DEFB  %00000000
          DEFB  %00111100
          DEFB  %01000000
          DEFB  %01111100
          DEFB  %01000010
          DEFB  %01000010
          DEFB  %00111100
          DEFB  %00000000

; $23 - Character: '7'          CHR$(35)

          DEFB  %00000000
          DEFB  %01111110
          DEFB  %00000010
          DEFB  %00000100
          DEFB  %00001000
          DEFB  %00010000
          DEFB  %00010000
          DEFB  %00000000

; $24 - Character: '8'          CHR$(36)

          DEFB  %00000000
          DEFB  %00111100
          DEFB  %01000010
          DEFB  %00111100
          DEFB  %01000010
          DEFB  %01000010
          DEFB  %00111100
          DEFB  %00000000

; $25 - Character: '9'          CHR$(37)

          DEFB  %00000000
          DEFB  %00111100
          DEFB  %01000010
          DEFB  %01000010
          DEFB  %00111110
          DEFB  %00000010
          DEFB  %00111100
          DEFB  %00000000

; $26 - Character: 'A'          CHR$(38)

          DEFB  %00000000
          DEFB  %00111100
          DEFB  %01000010
          DEFB  %01000010
          DEFB  %01111110
          DEFB  %01000010
          DEFB  %01000010
          DEFB  %00000000

; $27 - Character: 'B'          CHR$(39)

          DEFB  %00000000
          DEFB  %01111100
          DEFB  %01000010
          DEFB  %01111100
          DEFB  %01000010
          DEFB  %01000010
          DEFB  %01111100
          DEFB  %00000000

; $28 - Character: 'C'          CHR$(40)

          DEFB  %00000000
          DEFB  %00111100
          DEFB  %01000010
          DEFB  %01000000
          DEFB  %01000000
          DEFB  %01000010
          DEFB  %00111100
          DEFB  %00000000

; $29 - Character: 'D'          CHR$(41)

          DEFB  %00000000
          DEFB  %01111000
          DEFB  %01000100
          DEFB  %01000010
          DEFB  %01000010
          DEFB  %01000100
          DEFB  %01111000
          DEFB  %00000000

; $2A - Character: 'E'          CHR$(42)

          DEFB  %00000000
          DEFB  %01111110
          DEFB  %01000000
          DEFB  %01111100
          DEFB  %01000000
          DEFB  %01000000
          DEFB  %01111110
          DEFB  %00000000

; $2B - Character: 'F'          CHR$(43)

          DEFB  %00000000
          DEFB  %01111110
          DEFB  %01000000
          DEFB  %01111100
          DEFB  %01000000
          DEFB  %01000000
          DEFB  %01000000
          DEFB  %00000000

; $2C - Character: 'G'          CHR$(44)

          DEFB  %00000000
          DEFB  %00111100
          DEFB  %01000010
          DEFB  %01000000
          DEFB  %01001110
          DEFB  %01000010
          DEFB  %00111100
          DEFB  %00000000

; $2D - Character: 'H'          CHR$(45)

          DEFB  %00000000
          DEFB  %01000010
          DEFB  %01000010
          DEFB  %01111110
          DEFB  %01000010
          DEFB  %01000010
          DEFB  %01000010
          DEFB  %00000000

; $2E - Character: 'I'          CHR$(46)

          DEFB  %00000000
          DEFB  %00111110
          DEFB  %00001000
          DEFB  %00001000
          DEFB  %00001000
          DEFB  %00001000
          DEFB  %00111110
          DEFB  %00000000

; $2F - Character: 'J'          CHR$(47)

          DEFB  %00000000
          DEFB  %00000010
          DEFB  %00000010
          DEFB  %00000010
          DEFB  %01000010
          DEFB  %01000010
          DEFB  %00111100
          DEFB  %00000000

; $30 - Character: 'K'          CHR$(48)

          DEFB  %00000000
          DEFB  %01000100
          DEFB  %01001000
          DEFB  %01110000
          DEFB  %01001000
          DEFB  %01000100
          DEFB  %01000010
          DEFB  %00000000

; $31 - Character: 'L'          CHR$(49)

          DEFB  %00000000
          DEFB  %01000000
          DEFB  %01000000
          DEFB  %01000000
          DEFB  %01000000
          DEFB  %01000000
          DEFB  %01111110
          DEFB  %00000000

; $32 - Character: 'M'          CHR$(50)

          DEFB  %00000000
          DEFB  %01000010
          DEFB  %01100110
          DEFB  %01011010
          DEFB  %01000010
          DEFB  %01000010
          DEFB  %01000010
          DEFB  %00000000

; $33 - Character: 'N'          CHR$(51)

          DEFB  %00000000
          DEFB  %01000010
          DEFB  %01100010
          DEFB  %01010010
          DEFB  %01001010
          DEFB  %01000110
          DEFB  %01000010
          DEFB  %00000000

; $34 - Character: 'O'          CHR$(52)

          DEFB  %00000000
          DEFB  %00111100
          DEFB  %01000010
          DEFB  %01000010
          DEFB  %01000010
          DEFB  %01000010
          DEFB  %00111100
          DEFB  %00000000

; $35 - Character: 'P'          CHR$(53)

          DEFB  %00000000
          DEFB  %01111100
          DEFB  %01000010
          DEFB  %01000010
          DEFB  %01111100
          DEFB  %01000000
          DEFB  %01000000
          DEFB  %00000000

; $36 - Character: 'Q'          CHR$(54)

          DEFB  %00000000
          DEFB  %00111100
          DEFB  %01000010
          DEFB  %01000010
          DEFB  %01010010
          DEFB  %01001010
          DEFB  %00111100
          DEFB  %00000000

; $37 - Character: 'R'          CHR$(55)

          DEFB  %00000000
          DEFB  %01111100
          DEFB  %01000010
          DEFB  %01000010
          DEFB  %01111100
          DEFB  %01000100
          DEFB  %01000010
          DEFB  %00000000

; $38 - Character: 'S'          CHR$(56)

          DEFB  %00000000
          DEFB  %00111100
          DEFB  %01000000
          DEFB  %00111100
          DEFB  %00000010
          DEFB  %01000010
          DEFB  %00111100
          DEFB  %00000000

; $39 - Character: 'T'          CHR$(57)

          DEFB  %00000000
          DEFB  %11111110
          DEFB  %00010000
          DEFB  %00010000
          DEFB  %00010000
          DEFB  %00010000
          DEFB  %00010000
          DEFB  %00000000

; $3A - Character: 'U'          CHR$(58)

          DEFB  %00000000
          DEFB  %01000010
          DEFB  %01000010
          DEFB  %01000010
          DEFB  %01000010
          DEFB  %01000010
          DEFB  %00111100
          DEFB  %00000000

; $3B - Character: 'V'          CHR$(59)

          DEFB  %00000000
          DEFB  %01000010
          DEFB  %01000010
          DEFB  %01000010
          DEFB  %01000010
          DEFB  %00100100
          DEFB  %00011000
          DEFB  %00000000

; $3C - Character: 'W'          CHR$(60)

          DEFB  %00000000
          DEFB  %01000010
          DEFB  %01000010
          DEFB  %01000010
          DEFB  %01000010
          DEFB  %01011010
          DEFB  %00100100
          DEFB  %00000000

; $3D - Character: 'X'          CHR$(61)

          DEFB  %00000000
          DEFB  %01000010
          DEFB  %00100100
          DEFB  %00011000
          DEFB  %00011000
          DEFB  %00100100
          DEFB  %01000010
          DEFB  %00000000

; $3E - Character: 'Y'          CHR$(62)

          DEFB  %00000000
          DEFB  %10000010
          DEFB  %01000100
          DEFB  %00101000
          DEFB  %00010000
          DEFB  %00010000
          DEFB  %00010000
          DEFB  %00000000

; $3F - Character: 'Z'          CHR$(63)

          DEFB  %00000000
          DEFB  %01111110
          DEFB  %00000100
          DEFB  %00001000
          DEFB  %00010000
          DEFB  %00100000
          DEFB  %01111110
          DEFB  %00000000

.END                                ;TASM assembler instruction.