commit e254b0de3432fdbf3b9c20f36537442cfc56c4f0
parent 7612f86a3c257c890d23a2eb835f6b0e9b20d85d
Author: Brian Swetland <swetland@frotz.net>
Date: Thu, 18 Aug 2022 23:13:19 -0700
check in mirror of original listings
Diffstat:
4 files changed, 40430 insertions(+), 0 deletions(-)
diff --git a/mirror/hackaday.io/zx81dual.html b/mirror/hackaday.io/zx81dual.html
@@ -0,0 +1,16082 @@
+; ===========================================================
+; An Assembly Listing of the Operating System of the ZX81 ROM
+; ===========================================================
+------------------------------------------------------------------------
+
+; Last updated: 13-DEC-2004
+; 2011 Updated to remove -, +, /, *, &,
+; characters from labels (which confuse assemblers)
+;
+; 2011 Updated for conditional assembly of ORIGINAL or "Shoulders of Giants" ROM
+;
+; 2014-08-01 Updated to add CHARS_PER_LINE_WINDOW which is normally 32.
+;
+; The ideal pixel rates for square pixels on a PAL system are
+; 14.75 MHz (interlaced) and
+; 7.375 MHz (non-interlaced, which the ZX80/ZX81 are).
+; These are not commonly available but fortunately one can buy
+; baud-rate generator frequencies such as
+; 14.7456 and 7.3728 MHz that are only 0.03% low
+; which is more than close enough.
+;
+; ZX video normally has 6.5 MHz pixel rate,
+; so 32 characters take 256 pixels in 39.4 microseconds.
+; A 7.3728 MHz clock and 40 characters produces
+; 320 pixels in 43.4 microseconds.
+;
+; ZX80 video generation is software defined so it is
+; easy to get square pixels simply by subtracting 8 from the bytes
+; at hex addresses 287, 2AA and 2B8.
+; The video will appear to the left of the screen but
+; the characters will be square and a diagonal graphic line
+; will be at 45 degrees.
+;
+; ZX81 video generation in fast mode exactly the same as the ZX80.
+;
+; ZX81 video generation in slow mode is problematic, in that
+; the NMI generator expects a 3.25 MHz CPU clock
+; (3.25MHz / 208 = 15.625 kHz = 64 microsecond line period)
+; It is inside the ULA where it cannot be modified.
+;
+; Simply fitting a 7.3728 MHz crystal would reduce the line period to
+; 57.3 microseconds. Slow mode would require the CPU clock to be
+; divided by 236.
+;
+; Square pixels on NTSC requires 11+3/11 = 11.272... MHz (interlaced)
+; or 5.63.. non-interlaced which is slower than the original 6.5 MHz.
+; The NTSC line period is still 64 microseconds, so 256 pixels
+; stretch over 45 microseconds, and 320 pixels over 56 microseconds.
+; Thus it is possible to get square pixels on an NTSC display,
+; it is not possible to get 40 column text as well.
+; That would require the PAL clock, but pixels would not be square.
+;
+; The ZX printer is fixed in hardware.
+; It will not work in 40-column mode.
+;
+;
+;
+; PIXEL_CLOCK equ 7372500
+;
+; on-line assembler complains about the line above
+;
+; CHARS_PER_LINE_WINDOW always 32 for 6.5 MHz pixel rate
+; always 40 for 7.375 MHz PAL square pixel rate
+;
+CHARS_PER_LINE_WINDOW equ 40 ; 32 originally
+;
+; CHARS_PER_LINE always 32 for 6.5 MHz pixel rate
+; but 32 or 40 if using PAL square pixel rate
+;
+CHARS_HORIZONTAL equ 40 ; 32 originally
+CHARS_VERTICAL equ 24
+;
+; 2014-08-01
+; Largely working but some bugs remain.
+; Working:
+; You can type text and it takes 40 characters before new line.
+; 40 characters are nicely centred in the screen.
+; PLOT X,Y accepts X from 0 to 79.
+; Faulty:
+; System crashing in an authentic ZX81 fashion,
+; I don't know if this is due to software bugs
+; or socket joint disturbance from key presses.
+;
+;
+; 2018-01-09 add org
+; Assembles using on-line assembler "zasm" at:
+;
+; http://k1.spdns.de/cgi-bin/zasm.cgi
+;
+ org 0
+
+FALSE equ 0
+
+ORIGINAL equ 0
+NOT_BODGED equ 1
+
+; 2018-02-09 CHARS_HORIZONTAL placed in SCROLL routine.
+; Thanks to Adam Klotblixt for testing code and spotting this bug.
+; Also added to some G007 routines.
+;
+
+------------------------------------------------------------------------
+
+;
+; Work in progress.
+; This file will cross-assemble an original version of the "Improved"
+; ZX81 ROM.
+; The file can be modified to change the behaviour of the ROM
+; when used in emulators although there is no spare space available.
+;
+; The documentation is incomplete and if you can find a copy
+; of "The Complete Spectrum ROM Disassembly" then many routines
+; such as POINTERS and most of the mathematical routines are
+; similar and often identical.
+;
+; I've used the labels from the above book in this file and also
+; some from the more elusive Complete ZX81 ROM Disassembly
+; by the same publishers, Melbourne House.
+
+#if 0
+; zasm does not understand these:
+#define DEFB .BYTE ; TASM cross-assembler definitions
+#define DEFW .WORD
+#define EQU .EQU
+#endif
+
+; define stuff sensibly:
+;
+; I/O locations:
+;
+IO_PORT_TAPE equ $FF ; write
+IO_PORT_SCREEN equ $FF ; write
+
+IO_PORT_KEYBOARD_RD equ $FE ; A0 low
+IO_PORT_NMI_GEN_ON equ $FE ; A0 low
+IO_PORT_NMI_GEN_OFF equ $FD ; A1 low
+IO_PORT_PRINTER equ $FB ; A2 low
+------------------------------------------------------------------------
+
+;
+; System variables:
+;
+RAMBASE equ $4000
+ERR_NR equ $4000 ; The report code. Incremented before printing.
+FLAGS equ $4001 ; Bit 0: Suppression of leading space.
+ ; Bit 1: Control Flag for the printer.
+ ; Bit 2: Selects K or F mode; or, F or G
+ ; Bit 6: FP no. or string parameters.
+ ; Bit 7: Reset during syntax checking."
+ERR_SP equ $4002 ; Pointer to the GOSUB stack.
+RAMTOP equ $4004 ; The top of available RAM, or as specified.
+MODE equ $4006 ; Holds the code for K or F
+PPC equ $4007 ; Line number of the current statement.
+PPC_hi equ PPC+1
+VERSN equ $4009 ; Marks the start of the RAM that is saved.
+E_PPC equ $400A ; The BASIC line with the cursor
+D_FILE equ $400C ; Pointer to Display file
+DF_CC equ $400E ; Address for PRINT AT position
+VARS equ $4010 ; Pointer to variable area
+DEST equ $4012 ; Address of current variable in program area
+E_LINE equ $4014 ; Pointer to workspace
+E_LINE_hi equ E_LINE+1
+CH_ADD equ $4016 ; Pointer for scanning a line, in program or workspace
+X_PTR equ $4018 ; Pointer to syntax error.
+X_PTR_lo equ X_PTR
+X_PTR_hi equ X_PTR+1
+STKBOT equ $401A ; Pointer to calculator stack bottom.
+STKEND equ $401C ; Pointer to calculator stack end.
+BERG equ $401E ; Used for many different counting purposes
+MEM equ $401F ; Pointer to base of table of fp. nos, either in calc. stack or variable area.
+; ; Unused by ZX BASIC. Or FLAG Y for G007
+DF_SZ equ $4022 ; Number of lines in the lower screen
+S_TOP equ $4023 ; Current line number of automatic listing
+LAST_K equ $4025 ; Last Key pressed
+DEBOUNCE_VAR equ $4027 ; The de-bounce status
+MARGIN equ $4028 ; Adjusts for differing TV standards
+NXTLIN equ $4029 ; Next BASIC line to be interpreted
+OLDPPC equ $402B ; Last line number, in case needed.
+FLAGX equ $402D ; Bit 0: Reset indicates an arrayed variable
+ ; Bit 1: Reset indicates a given variable exists
+ ; Bit 5: Set during INPUT mode
+ ; Bit 7: Set when INPUT is to be numeric
+STRLEN equ $402E ; Length of a string, or a BASIC line
+STRLEN_lo equ STRLEN ;
+T_ADDR equ $4030 ; Pointer to parameter table. & distinguishes between PLOT & UNPLOT
+SEED equ $4032 ; For RANDOM function
+FRAMES equ $4034 ; Frame counter
+FRAMES_hi equ FRAMES+1 ;
+COORDS equ $4036 ; X & Y for PLOT
+COORDS_x equ COORDS ;
+PR_CC equ $4038 ; Print buffer counter
+S_POSN equ $4039 ; Line & Column for PRINT AT
+S_POSN_x equ $4039 ;
+S_POSN_y equ $403A ;
+CDFLAG equ $403B ; Bit 6 = the true fast/slow flag
+ ; Bit 7 = copy of the fast/slow flag. RESET when FAST needed
+PRBUFF equ $403C ; Printer buffer
+PRBUFF_END equ $405C ;
+MEM_0_1st equ $405D ; room for 5 floating point numbers (meme_0 to mem_ 5???)
+; $407B ; unused. Or RESTART to G007
+; $407D ; The BASIC program starts here
+; equ $40
+; equ $40
+; equ $40
+; First byte after system variables:
+USER_RAM equ $407D
+MAX_RAM equ $7FFF
+------------------------------------------------------------------------
+
+;===============================
+; ZX81 constants:
+;===============================
+; ZX characters (not the same as ASCII)
+;-------------------------------
+ZX_SPACE equ $00
+; ZX_graphic equ $01
+; ZX_graphic equ $02
+; ZX_graphic equ $03
+; ZX_graphic equ $04
+; ZX_graphic equ $05
+; ZX_graphic equ $06
+; ZX_graphic equ $07
+; ZX_graphic equ $08
+; ZX_graphic equ $09
+; ZX_graphic equ $0A
+ZX_QUOTE equ $0B
+ZX_POUND equ $0C
+ZX_DOLLAR equ $0D
+ZX_COLON equ $0E
+ZX_QUERY equ $0F
+ZX_BRACKET_LEFT equ $10
+ZX_BRACKET_RIGHT equ $11
+ZX_GREATER_THAN equ $12
+ZX_LESS_THAN equ $13
+ZX_EQUAL equ $14
+ZX_PLUS equ $15
+ZX_MINUS equ $16
+ZX_STAR equ $17
+ZX_SLASH equ $18
+ZX_SEMICOLON equ $19
+ZX_COMMA equ $1A
+ZX_PERIOD equ $1B
+ZX_0 equ $1C
+ZX_1 equ $1D
+ZX_2 equ $1E
+ZX_3 equ $1F
+ZX_4 equ $20
+ZX_5 equ $21
+ZX_6 equ $22
+ZX_7 equ $23
+ZX_8 equ $24
+ZX_9 equ $25
+ZX_A equ $26
+ZX_B equ $27
+ZX_C equ $28
+ZX_D equ $29
+ZX_E equ $2A
+ZX_F equ $2B
+ZX_G equ $2C
+ZX_H equ $2D
+ZX_I equ $2E
+ZX_J equ $2F
+ZX_K equ $30
+ZX_L equ $31
+ZX_M equ $32
+ZX_N equ $33
+ZX_O equ $34
+ZX_P equ $35
+ZX_Q equ $36
+ZX_R equ $37
+ZX_S equ $38
+ZX_T equ $39
+ZX_U equ $3A
+ZX_V equ $3B
+ZX_W equ $3C
+ZX_X equ $3D
+ZX_Y equ $3E
+ZX_Z equ $3F
+ZX_RND equ $40
+ZX_INKEY_STR equ $41
+ZX_PI equ $42
+;
+; $43 to $6F not used
+;
+ZX_cursor_up equ $70
+ZX_cursor_down equ $71
+ZX_cursor_left equ $72
+ZX_cursor_right equ $73
+
+ZX_GRAPHICS equ $74
+ZX_EDIT equ $75
+ZX_NEWLINE equ $76
+ZX_RUBOUT equ $77
+ZX_KL equ $78
+ZX_FUNCTION equ $79
+;
+; $7A to $7F not used
+;
+ZX_CURSOR equ $7F
+;
+; $80 to $BF are inverses of $00 to $3F
+;
+; ZX_graphic equ $80 ; inverse space
+; ZX_graphic equ $81
+; ZX_graphic equ $82
+; ZX_graphic equ $83
+; ZX_graphic equ $84
+; ZX_graphic equ $85
+; ZX_graphic equ $86
+; ZX_graphic equ $87
+; ZX_graphic equ $88
+; ZX_graphic equ $89
+; ZX_graphic equ $8A
+ZX_INV_QUOTE equ $8B
+ZX_INV_POUND equ $8C
+ZX_INV_DOLLAR equ $8D
+ZX_INV_COLON equ $8E
+ZX_INV_QUERY equ $8F
+ZX_INV_BRACKET_RIGHT equ $90
+ZX_INV_BRACKET_LEFT equ $91
+ZX_INV_GT equ $92
+
+ZX_INV_PLUS equ $95
+ZX_INV_MINUS equ $96
+
+ZX_INV_K equ $B0
+ZX_INV_S equ $B8
+
+ZX_DOUBLE_QUOTE equ $C0
+ZX_AT equ $C1
+ZX_TAB equ $C2
+; not used equ $C3
+ZX_CODE equ $C4
+ZX_VAL equ $C5
+ZX_LEN equ $C6
+ZX_SIN equ $C7
+ZX_COS equ $C8
+ZX_TAN equ $C9
+ZX_ASN equ $CA
+ZX_ACS equ $CB
+ZX_ATN equ $CC
+ZX_LN equ $CD
+ZX_EXP equ $CE
+ZX_INT equ $CF
+
+ZX_SQR equ $D0
+ZX_SGN equ $D1
+ZX_ABS equ $D2
+ZX_PEEK equ $D3
+ZX_USR equ $D4
+ZX_STR_STR equ $D5 ; STR$
+ZX_CHR_STR equ $D6 ; CHR$
+ZX_NOT equ $D7
+ZX_POWER equ $D8
+ZX_OR equ $D9
+ZX_AND equ $DA
+ZX_LESS_OR_EQUAL equ $DB
+ZX_GREATER_OR_EQUAL equ $DC
+ZX_NOT_EQUAL equ $DD
+ZX_THEN equ $DE
+ZX_TO equ $DF
+
+ZX_STEP equ $E0
+ZX_LPRINT equ $E1
+ZX_LLIST equ $E2
+ZX_STOP equ $E3
+ZX_SLOW equ $E4
+ZX_FAST equ $E5
+ZX_NEW equ $E6
+ZX_SCROLL equ $E7
+ZX_CONT equ $E8
+ZX_DIM equ $E9
+ZX_REM equ $EA
+ZX_FOR equ $EB
+ZX_GOTO equ $EC
+ZX_GOSUB equ $ED
+ZX_INPUT equ $EE
+ZX_LOAD equ $EF
+
+ZX_LIST equ $F0
+ZX_LET equ $F1
+ZX_PAUSE equ $F2
+ZX_NEXT equ $F3
+ZX_POKE equ $F4
+ZX_PRINT equ $F5
+ZX_PLOT equ $F6
+ZX_RUN equ $F7
+ZX_SAVE equ $F8
+ZX_RAND equ $F9
+ZX_IF equ $FA
+ZX_CLS equ $FB
+ZX_UNPLOT equ $FC
+ZX_CLEAR equ $FD
+ZX_RETURN equ $FE
+ZX_COPY equ $FF
+------------------------------------------------------------------------
+
+
+;
+_CLASS_00 equ 0
+_CLASS_01 equ 1
+_CLASS_02 equ 2
+_CLASS_03 equ 3
+_CLASS_04 equ 4
+_CLASS_05 equ 5
+_CLASS_06 equ 6
+------------------------------------------------------------------------
+
+
+
+; These values taken from BASIC manual
+;
+;
+ERROR_CODE_SUCCESS equ 0
+ERROR_CODE_CONTROL_VARIABLE equ 1
+ERROR_CODE_UNDEFINED_VARIABLE equ 2
+ERROR_CODE_SUBSCRIPT_OUT_OF_RANGE equ 3
+ERROR_CODE_NOT_ENOUGH_MEMORY equ 4
+ERROR_CODE_NO_ROOM_ON_SCREEN equ 5
+ERROR_CODE_ARITHMETIC_OVERFLOW equ 6
+ERROR_CODE_RETURN_WITHOUT_GOSUB equ 7
+ERROR_CODE_INPUT_AS_A_COMMAND equ 8
+ERROR_CODE_STOP equ 9
+ERROR_CODE_INVALID_ARGUMENT equ 10
+
+ERROR_CODE_INTEGER_OUT_OF_RANGE equ 11
+ERROR_CODE_VAL_STRING_INVALID equ 12
+ERROR_CODE_BREAK equ 13
+
+ERROR_CODE_EMPTY_PROGRAM_NAME equ 15
+------------------------------------------------------------------------
+
+;
+; codes for Forth-like calculator
+;
+__jump_true equ $00
+__exchange equ $01
+__delete equ $02
+__subtract equ $03
+__multiply equ $04
+__division equ $05
+__to_power equ $06
+__or equ $07
+__boolean_num_and_num equ $08
+__num_l_eql equ $09
+__num_gr_eql equ $0A
+__nums_neql equ $0B
+__num_grtr equ $0C
+__num_less equ $0D
+__nums_eql equ $0E
+__addition equ $0F
+__strs_and_num equ $10
+__str_l_eql equ $11
+__str_gr_eql equ $12
+__strs_neql equ $13
+__str_grtr equ $14
+__str_less equ $15
+__strs_eql equ $16
+__strs_add equ $17
+__negate equ $18
+__code equ $19
+__val equ $1A
+__len equ $1B
+__sin equ $1C
+__cos equ $1D
+__tan equ $1E
+__asn equ $1F
+__acs equ $20
+__atn equ $21
+__ln equ $22
+__exp equ $23
+__int equ $24
+__sqr equ $25
+__sgn equ $26
+__abs equ $27
+__peek equ $28
+__usr_num equ $29
+__str_dollar equ $2A
+__chr_dollar equ $2B
+__not equ $2C
+__duplicate equ $2D
+__n_mod_m equ $2E
+__jump equ $2F
+__stk_data equ $30
+__dec_jr_nz equ $31
+__less_0 equ $32
+__greater_0 equ $33
+__end_calc equ $34
+__get_argt equ $35
+__truncate equ $36
+__fp_calc_2 equ $37
+__e_to_fp equ $38
+
+;
+; __series_xx equ $39 : $80__$9F.
+; tells the stack machine to push
+; 0 to 31 floating-point values on the stack.
+;
+__series_06 equ $86
+__series_08 equ $88
+__series_0C equ $8C
+; __stk_const_xx equ $3A : $A0__$BF.
+; __st_mem_xx equ $3B : $C0__$DF.
+; __get_mem_xx equ $3C : $E0__$FF.
+
+__st_mem_0 equ $C0
+__st_mem_1 equ $C1
+__st_mem_2 equ $C2
+__st_mem_3 equ $C3
+__st_mem_4 equ $C4
+__st_mem_5 equ $C5
+__st_mem_6 equ $C6
+__st_mem_7 equ $C7
+
+
+__get_mem_0 equ $E0
+__get_mem_1 equ $E1
+__get_mem_2 equ $E2
+__get_mem_3 equ $E3
+__get_mem_4 equ $E4
+
+
+__stk_zero equ $A0
+__stk_one equ $A1
+__stk_half equ $A2
+__stk_half_pi equ $A3
+__stk_ten equ $A4
+------------------------------------------------------------------------
+
+;*****************************************
+;** Part 1. RESTART ROUTINES AND TABLES **
+;*****************************************
+
+------------------------------------------------------------------------
+
+; THE *'START'*
+------------------------------------------------------------------------
+
+; All Z80 chips start at location zero.
+; At start-up the Interrupt Mode is 0, ZX computers use Interrupt Mode 1.
+; Interrupts are disabled .
+
+mark_0000:
+*START:*
+ OUT (IO_PORT_NMI_GEN_OFF),A ; Turn off the NMI generator if this ROM is
+ ; running in ZX81 hardware. This does nothing
+ ; if this ROM is running within an upgraded
+ ; ZX80.
+ LD BC,MAX_RAM ; Set BC to the top of possible RAM.
+ ; The higher unpopulated addresses are used for
+ ; video generation.
+ JP RAM_CHECK <#RAM_CHECK> ; Jump forward to RAM_CHECK.
+
+------------------------------------------------------------------------
+
+; THE *'ERROR'* RESTART
+------------------------------------------------------------------------
+
+; The error restart deals immediately with an error.
+; ZX computers execute the same code in runtime as when checking syntax.
+; If the error occurred while running a program
+; then a brief report is produced.
+; If the error occurred while entering a BASIC line or in input etc.,
+; then the error marker indicates the exact point at which the error lies.
+
+mark_0008:
+*ERROR_1:*
+ LD HL,(CH_ADD) ; fetch character address from CH_ADD.
+ LD (X_PTR),HL ; and set the error pointer X_PTR.
+ JR ERROR_2 <#ERROR_2> ; forward to continue at ERROR_2.
+
+------------------------------------------------------------------------
+
+; 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.
+
+mark_0010:
+*PRINT_A:*
+ AND A ; test for zero - space.
+ JP NZ,PRINT_CH <#PRINT_CH> ; jump forward if not to PRINT_CH.
+
+ JP PRINT_SP <#PRINT_SP> ; jump forward to PRINT_SP.
+
+; ___
+#if ORIGINAL
+ DEFB $FF ; unused location.
+#else
+ DEFB $01 ;+ unused location. Version. PRINT PEEK 23
+#endif
+
+------------------------------------------------------------------------
+
+; THE *'COLLECT A CHARACTER'* RESTART
+------------------------------------------------------------------------
+
+; The character addressed by the system variable CH_ADD is fetched and if it
+; is a non-space, non-cursor character it is returned else CH_ADD is
+; incremented and the new addressed character tested until it is not a space.
+
+mark_0018:
+*GET_CHAR:*
+ LD HL,(CH_ADD) ; set HL to character address CH_ADD.
+ LD A,(HL) ; fetch addressed character to A.
+
+*TEST_SP:*
+ AND A ; test for space.
+ RET NZ ; return if not a space
+
+ NOP ; else trickle through
+ NOP ; to the next routine.
+
+------------------------------------------------------------------------
+
+; THE *'COLLECT NEXT CHARACTER'* RESTART
+------------------------------------------------------------------------
+
+; The character address is incremented and the new addressed character is
+; returned if not a space, or cursor, else the process is repeated.
+
+mark_0020:
+*NEXT_CHAR:*
+ CALL CH_ADD_PLUS_1 <#CH_ADD_PLUS_1> ; gets next immediate
+ ; character.
+ JR TEST_SP <#TEST_SP> ; back
+; ___
+
+ DEFB $FF, $FF, $FF ; unused locations.
+
+------------------------------------------------------------------------
+
+; THE *'FLOATING POINT CALCULATOR'* RESTART
+------------------------------------------------------------------------
+
+; this restart jumps to the recursive floating-point calculator.
+; the ZX81's internal, FORTH-like, stack-based language.
+;
+; In the five remaining bytes there is, appropriately, enough room for the
+; end-calc literal - the instruction which exits the calculator.
+
+mark_0028:
+*FP_CALC:*
+#if ORIGINAL
+ JP CALCULATE <#CALCULATE> ; jump immediately to the CALCULATE routine.
+#else
+
+ JP CALCULATE <#CALCULATE> ;+ jump to the NEW calculate routine address.
+#endif
+
+mark_002B:
+*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.
+
+
+------------------------------------------------------------------------
+
+; THE *'MAKE BC SPACES'* RESTART
+------------------------------------------------------------------------
+
+; This restart is used eight times to create, in workspace, the number of
+; spaces passed in the BC register.
+
+mark_0030:
+*BC_SPACES:*
+ PUSH BC ; push number of spaces on stack.
+ LD HL,(E_LINE) ; fetch edit line location from E_LINE.
+ PUSH HL ; save this value on stack.
+ JP RESERVE <#RESERVE> ; jump forward to continue at RESERVE.
+
+
+------------------------------------------------------------------------
+
+_START equ $00
+_ERROR_1 equ $08
+_PRINT_A equ $10
+_GET_CHAR equ $18
+_NEXT_CHAR equ $20
+_FP_CALC equ $28
+_BC_SPACES equ $30
+------------------------------------------------------------------------
+
+; THE *'INTERRUPT'* RESTART
+------------------------------------------------------------------------
+
+; The Mode 1 Interrupt routine is concerned solely with generating the central
+; television picture.
+; On the ZX81 interrupts are enabled only during the interrupt routine,
+; although the interrupt
+;
+; This Interrupt Service Routine automatically disables interrupts at the
+; outset and the last interrupt in a cascade exits before the interrupts are
+; enabled.
+;
+; There is no DI instruction in the ZX81 ROM.
+;
+; A maskable interrupt is triggered when bit 6 of the Z80's Refresh register
+; changes from set to reset.
+;
+; The Z80 will always be executing a HALT (NEWLINE) when the interrupt occurs.
+; A HALT instruction repeatedly executes NOPS but the seven lower bits
+; of the Refresh register are incremented each time as they are when any
+; simple instruction is executed. (The lower 7 bits are incremented twice for
+; a prefixed instruction)
+;
+; This is controlled by the Sinclair Computer Logic Chip - manufactured from
+; a Ferranti Uncommitted Logic Array.
+;
+; When a Mode 1 Interrupt occurs the Program Counter, which is the address in
+; the upper echo display following the NEWLINE/HALT instruction, goes on the
+; machine stack. 193 interrupts are required to generate the last part of
+; the 56th border line and then the 192 lines of the central TV picture and,
+; although each interrupt interrupts the previous one, there are no stack
+; problems as the 'return address' is discarded each time.
+;
+; The scan line counter in C counts down from 8 to 1 within the generation of
+; each text line. For the first interrupt in a cascade the initial value of
+; C is set to 1 for the last border line.
+; Timing is of the utmost importance as the RH border, horizontal retrace
+; and LH border are mostly generated in the 58 clock cycles this routine
+; takes .
+
+
+------------------------------------------------------------------------
+
+MARK_0038:
+*INTERRUPT:*
+ DEC C ; (4) decrement C - the scan line counter.
+ JP NZ,SCAN_LINE <#SCAN_LINE> ; (10/10) JUMP forward if not zero to SCAN_LINE
+
+ POP HL ; (10) point to start of next row in display
+ ; file.
+
+ DEC B ; (4) decrement the row counter. (4)
+ RET Z ; (11/5) return when picture complete to R_IX_1_LAST_NEWLINE
+ ; with interrupts disabled.
+
+ SET 3,C ; (8) Load the scan line counter with eight.
+ ; Note. LD C,$08 is 7 clock cycles which
+ ; is way too fast.
+
+; ->
+
+mark_0041:
+*WAIT_INT:*
+;
+; NB $DD is for 32-column display
+;
+ LD R,A ; (9) Load R with initial rising value $DD.
+
+ EI ; (4) Enable Interrupts. [ R is now $DE ].
+
+ JP (HL) ; (4) jump to the echo display file in upper
+ ; memory and execute characters $00 - $3F
+ ; as NOP instructions. The video hardware
+ ; is able to read these characters and,
+ ; with the I register is able to convert
+ ; the character bitmaps in this ROM into a
+ ; line of bytes. Eventually the NEWLINE/HALT
+ ; will be encountered before R reaches $FF.
+ ; It is however the transition from $FF to
+ ; $80 that triggers the next interrupt.
+ ; [ The Refresh register is now $DF ]
+
+; ___
+
+mark_0045:
+*SCAN_LINE:*
+ POP DE ; (10) discard the address after NEWLINE as the
+ ; same text line has to be done again
+ ; eight times.
+
+ RET Z ; (5) Harmless Nonsensical Timing.
+ ; (condition never met)
+
+ JR WAIT_INT <#WAIT_INT> ; (12) back to WAIT_INT
+
+; Note. that a computer with less than 4K or RAM will have a collapsed
+; display file and the above mechanism deals with both types of display.
+;
+; With a full display, the 32 characters in the line are treated as NOPS
+; and the Refresh register rises from $E0 to $FF and, at the next instruction
+; - HALT, the interrupt occurs.
+; With a collapsed display and an initial NEWLINE/HALT, it is the NOPs
+; generated by the HALT that cause the Refresh value to rise from $E0 to $FF,
+; triggering an Interrupt on the next transition.
+; This works happily for all display lines between these extremes and the
+; generation of the 32 character, 1 pixel high, line will always take 128
+; clock cycles.
+
+------------------------------------------------------------------------
+
+; THE *'INCREMENT CH_ADD'* SUBROUTINE
+------------------------------------------------------------------------
+
+; This is the subroutine that increments the character address system variable
+; and returns if it is not the cursor character. The ZX81 has an actual
+; character at the cursor position rather than a pointer system variable
+; as is the case with prior and subsequent ZX computers.
+
+mark_0049:
+CH_ADD_PLUS_1:
+ LD HL,(CH_ADD) ; fetch character address to CH_ADD.
+
+mark_004C:
+*TEMP_PTR1:*
+ INC HL ; address next immediate location.
+
+mark_004D:
+*TEMP_PTR2:*
+ LD (CH_ADD),HL ; update system variable CH_ADD.
+
+ LD A,(HL) ; fetch the character.
+ CP ZX_CURSOR ; compare to cursor character.
+ RET NZ ; return if not the cursor.
+
+ JR TEMP_PTR1 <#TEMP_PTR1> ; back for next character to TEMP_PTR1.
+
+------------------------------------------------------------------------
+
+; THE *'ERROR_2'* BRANCH
+------------------------------------------------------------------------
+
+; This is a continuation of the error restart.
+; If the error occurred in runtime then the error stack pointer will probably
+; lead to an error report being printed unless it occurred during input.
+; If the error occurred when checking syntax then the error stack pointer
+; will be an editing routine and the position of the error will be shown
+; when the lower screen is reprinted.
+
+mark_0056:
+*ERROR_2:*
+ POP HL ; pop the return address which points to the
+ ; DEFB, error code, after the RST 08.
+ LD L,(HL) ; load L with the error code. HL is not needed
+ ; anymore.
+
+mark_0058:
+*ERROR_3:*
+ LD (IY+ERR_NR-RAMBASE),L ; place error code in system variable ERR_NR
+ LD SP,(ERR_SP) ; set the stack pointer from ERR_SP
+ CALL SLOW_FAST <#SLOW_FAST> ; selects slow mode.
+ JP SET_MIN <#SET_MIN> ; exit to address on stack via routine SET_MIN.
+
+; ___
+
+ DEFB $FF ; unused.
+
+------------------------------------------------------------------------
+
+; THE *'NON MASKABLE INTERRUPT'* ROUTINE
+------------------------------------------------------------------------
+
+; Jim Westwood's technical dodge using Non-Maskable Interrupts solved the
+; flicker problem of the ZX80 and gave the ZX81 a multi-tasking SLOW mode
+; with a steady display. Note that the AF' register is reserved for this
+; function and its interaction with the display routines. When counting
+; TV lines, the NMI makes no use of the main registers.
+; The circuitry for the NMI generator is contained within the SCL (Sinclair
+; Computer Logic) chip.
+; ( It takes 32 clock cycles while incrementing towards zero ).
+
+mark_0066:
+*NMI:*
+ EX AF,AF' ; (4) switch in the NMI's copy of the
+ ; accumulator.
+ INC A ; (4) increment.
+ JP M,NMI_RET <#NMI_RET> ; (10/10) jump, if minus, to NMI_RET as this is
+ ; part of a test to see if the NMI
+ ; generation is working or an intermediate
+ ; value for the ascending negated blank
+ ; line counter.
+
+ JR Z,NMI_CONT <#NMI_CONT> ; (12) forward to NMI_CONT
+ ; when line count has incremented to zero.
+
+; Note. the synchronizing NMI when A increments from zero to one takes this
+; 7 clock cycle route making 39 clock cycles in all.
+
+mark_006D:
+*NMI_RET:*
+ EX AF,AF' ; (4) switch out the incremented line counter
+ ; or test result $80
+ RET ; (10) return to User application for a while.
+
+; ___
+
+; This branch is taken when the 55 (or 31) lines have been drawn.
+
+mark_006F:
+*NMI_CONT:*
+ EX AF,AF' ; (4) restore the main accumulator.
+
+ PUSH AF ; (11) * Save Main Registers
+ PUSH BC ; (11) **
+ PUSH DE ; (11) ***
+ PUSH HL ; (11) ****
+
+; the next set-up procedure is only really applicable when the top set of
+; blank lines have been generated.
+
+ LD HL,(D_FILE) ; (16) fetch start of Display File from D_FILE
+ ; points to the HALT at beginning.
+ SET 7,H ; (8) point to upper 32K 'echo display file'
+
+ HALT ; (1) HALT synchronizes with NMI.
+ ; Used with special hardware connected to the
+ ; Z80 HALT and WAIT lines to take 1 clock cycle.
+
+------------------------------------------------------------------------
+
+; the NMI has been generated - start counting.
+; The cathode ray is at the RH side of the TV.
+;
+; First the NMI servicing, similar to CALL = 17 clock cycles.
+; Then the time taken by the NMI for zero-to-one path = 39 cycles
+; The HALT above = 01 cycles.
+; The two instructions below = 19 cycles.
+; The code at R_IX_1 <#R_IX_1> up to and including the CALL = 43 cycles.
+; The Called routine at DISPLAY_5 <#DISPLAY_5> = 24 cycles.
+; -------------------------------------- ---
+; Total Z80 instructions = 143 cycles.
+;
+; Meanwhile in TV world,
+; Horizontal retrace = 15 cycles.
+; Left blanking border 8 character positions = 32 cycles
+; Generation of 75% scanline from the first NEWLINE = 96 cycles
+; --------------------------------------- ---
+; = 143 cycles
+;
+; Since at the time the first JP (HL) is encountered to execute the echo
+; display another 8 character positions have to be put out, then the
+; Refresh register need to hold $F8. Working back and counteracting
+; the fact that every instruction increments the Refresh register then
+; the value that is loaded into R needs to be $F5. :-)
+;
+;
+ OUT (IO_PORT_NMI_GEN_OFF),A ; (11) Stop the NMI generator.
+
+ JP (IX) ; (8) forward to R_IX_1 (after top) or R_IX_2
+
+; ****************
+; ** KEY TABLES **
+; ****************
+
+------------------------------------------------------------------------
+
+; THE *'UNSHIFTED'* CHARACTER CODES
+------------------------------------------------------------------------
+
+
+mark_007E:
+*K_UNSHIFT*:
+ DEFB ZX_Z
+ DEFB ZX_X
+ DEFB ZX_C
+ DEFB ZX_V
+
+ DEFB ZX_A
+ DEFB ZX_S
+ DEFB ZX_D
+ DEFB ZX_F
+ DEFB ZX_G
+
+ DEFB ZX_Q
+ DEFB ZX_W
+ DEFB ZX_E
+ DEFB ZX_R
+ DEFB ZX_T
+
+ DEFB ZX_1
+ DEFB ZX_2
+ DEFB ZX_3
+ DEFB ZX_4
+ DEFB ZX_5
+
+ DEFB ZX_0
+ DEFB ZX_9
+ DEFB ZX_8
+ DEFB ZX_7
+ DEFB ZX_6
+
+ DEFB ZX_P
+ DEFB ZX_O
+ DEFB ZX_I
+ DEFB ZX_U
+ DEFB ZX_Y
+
+ DEFB ZX_NEWLINE
+ DEFB ZX_L
+ DEFB ZX_K
+ DEFB ZX_J
+ DEFB ZX_H
+
+ DEFB ZX_SPACE
+ DEFB ZX_PERIOD
+ DEFB ZX_M
+ DEFB ZX_N
+ DEFB ZX_B
+
+
+------------------------------------------------------------------------
+
+; THE *'SHIFTED'* CHARACTER CODES
+------------------------------------------------------------------------
+
+
+
+mark_00A5:
+K_SHIFT:
+ DEFB ZX_COLON ; :
+ DEFB ZX_SEMICOLON ; ;
+ DEFB ZX_QUERY ; ?
+ DEFB ZX_SLASH ; /
+ DEFB ZX_STOP
+ DEFB ZX_LPRINT
+ DEFB ZX_SLOW
+ DEFB ZX_FAST
+ DEFB ZX_LLIST
+ DEFB $C0 ; ""
+ DEFB ZX_OR
+ DEFB ZX_STEP
+ DEFB $DB ; <=
+ DEFB $DD ; <>
+ DEFB ZX_EDIT
+ DEFB ZX_AND
+ DEFB ZX_THEN
+ DEFB ZX_TO
+ DEFB $72 ; cursor-left
+ DEFB ZX_RUBOUT
+ DEFB ZX_GRAPHICS
+ DEFB $73 ; cursor-right
+ DEFB $70 ; cursor-up
+ DEFB $71 ; cursor-down
+ DEFB ZX_QUOTE ; "
+ DEFB $11 ; )
+ DEFB $10 ; (
+ DEFB ZX_DOLLAR ; $
+ DEFB $DC ; >=
+ DEFB ZX_FUNCTION
+ DEFB ZX_EQUAL
+ DEFB ZX_PLUS
+ DEFB ZX_MINUS
+ DEFB ZX_POWER ; **
+ DEFB ZX_POUND ; £
+ DEFB ZX_COMMA ; ,
+ DEFB ZX_GREATER_THAN ; >
+ DEFB ZX_LESS_THAN ; <
+ DEFB ZX_STAR ; *
+
+------------------------------------------------------------------------
+
+; THE *'FUNCTION'* CHARACTER CODES
+------------------------------------------------------------------------
+
+
+
+mark_00CC:
+*K_FUNCT:*
+ DEFB ZX_LN
+ DEFB ZX_EXP
+ DEFB ZX_AT
+ DEFB ZX_KL
+ DEFB ZX_ASN
+ DEFB ZX_ACS
+ DEFB ZX_ATN
+ DEFB ZX_SGN
+ DEFB ZX_ABS
+ DEFB ZX_SIN
+ DEFB ZX_COS
+ DEFB ZX_TAN
+ DEFB ZX_INT
+ DEFB ZX_RND
+ DEFB ZX_KL
+ DEFB ZX_KL
+ DEFB ZX_KL
+ DEFB ZX_KL
+ DEFB ZX_KL
+ DEFB ZX_KL
+ DEFB ZX_KL
+ DEFB ZX_KL
+ DEFB ZX_KL
+ DEFB ZX_KL
+ DEFB ZX_TAB
+ DEFB ZX_PEEK
+ DEFB ZX_CODE
+ DEFB ZX_CHR_STR ; CHR$
+ DEFB ZX_STR_STR ; STR$
+ DEFB ZX_KL
+ DEFB ZX_USR
+ DEFB ZX_LEN
+ DEFB ZX_VAL
+ DEFB ZX_SQR
+ DEFB ZX_KL
+ DEFB ZX_KL
+ DEFB ZX_PI
+ DEFB ZX_NOT
+ DEFB ZX_INKEY_STR
+
+
+------------------------------------------------------------------------
+
+; THE *'GRAPHIC'* CHARACTER CODES
+------------------------------------------------------------------------
+
+
+
+mark_00F3:
+*K_GRAPH:*
+ DEFB $08 ; graphic
+ DEFB $0A ; graphic
+ DEFB $09 ; graphic
+ DEFB $8A ; graphic
+ DEFB $89 ; graphic
+
+ DEFB $81 ; graphic
+ DEFB $82 ; graphic
+ DEFB $07 ; graphic
+ DEFB $84 ; graphic
+ DEFB $06 ; graphic
+
+ DEFB $01 ; graphic
+ DEFB $02 ; graphic
+ DEFB $87 ; graphic
+ DEFB $04 ; graphic
+ DEFB $05 ; graphic
+
+ DEFB ZX_RUBOUT
+ DEFB ZX_KL
+ DEFB $85 ; graphic
+ DEFB $03 ; graphic
+ DEFB $83 ; graphic
+
+ DEFB $8B ; graphic
+ DEFB $91 ; inverse )
+ DEFB $90 ; inverse (
+ DEFB $8D ; inverse $
+ DEFB $86 ; graphic
+
+ DEFB ZX_KL
+ DEFB $92 ; inverse >
+ DEFB $95 ; inverse +
+ DEFB $96 ; inverse -
+ DEFB $88 ; graphic
+
+------------------------------------------------------------------------
+
+; THE *'TOKEN'* TABLES
+------------------------------------------------------------------------
+
+
+
+mark_0111:
+*TOKEN_TABLE:*
+ DEFB ZX_QUERY +$80; '?'
+ DEFB ZX_QUOTE, ZX_QUOTE +$80; ""
+ DEFB ZX_A, ZX_T +$80; AT
+ DEFB ZX_T, ZX_A, ZX_B +$80; TAB
+ DEFB ZX_QUERY +$80; '?'
+ DEFB ZX_C, ZX_O, ZX_D, ZX_E +$80; CODE
+ DEFB ZX_V, ZX_A, ZX_L +$80; VAL
+ DEFB ZX_L, ZX_E, ZX_N +$80; LEN
+ DEFB ZX_S, ZX_I, ZX_N +$80; SIN
+ DEFB ZX_C, ZX_O, ZX_S +$80; COS
+ DEFB ZX_T, ZX_A, ZX_N +$80; TAN
+ DEFB ZX_A, ZX_S, ZX_N +$80; ASN
+ DEFB ZX_A, ZX_C, ZX_S +$80; ACS
+ DEFB ZX_A, ZX_T, ZX_N +$80; ATN
+ DEFB ZX_L, ZX_N +$80; LN
+ DEFB ZX_E, ZX_X, ZX_P +$80; EXP
+ DEFB ZX_I, ZX_N, ZX_T +$80; INT
+ DEFB ZX_S, ZX_Q, ZX_R +$80; SQR
+ DEFB ZX_S, ZX_G, ZX_N +$80; SGN
+ DEFB ZX_A, ZX_B, ZX_S +$80; ABS
+ DEFB ZX_P, ZX_E, ZX_E, ZX_K +$80; PEEK
+ DEFB ZX_U, ZX_S, ZX_R +$80; USR
+ DEFB ZX_S, ZX_T, ZX_R, ZX_DOLLAR +$80; STR$
+ DEFB ZX_C, ZX_H, ZX_R, ZX_DOLLAR +$80; CHR$
+ DEFB ZX_N, ZX_O, ZX_T +$80; NOT
+ DEFB ZX_STAR, ZX_STAR +$80; **
+ DEFB ZX_O, ZX_R +$80; OR
+ DEFB ZX_A, ZX_N, ZX_D +$80; AND
+ DEFB ZX_LESS_THAN, ZX_EQUAL +$80; >=
+ DEFB ZX_GREATER_THAN, ZX_EQUAL +$80; <=
+ DEFB ZX_LESS_THAN, ZX_GREATER_THAN +$80; ><
+ DEFB ZX_T, ZX_H, ZX_E, ZX_N +$80; THEN
+ DEFB ZX_T, ZX_O +$80; TO
+ DEFB ZX_S, ZX_T, ZX_E, ZX_P +$80; STEP
+ DEFB ZX_L, ZX_P, ZX_R, ZX_I, ZX_N, ZX_T +$80; LPRINT
+ DEFB ZX_L, ZX_L, ZX_I, ZX_S, ZX_T +$80; LLIST
+ DEFB ZX_S, ZX_T, ZX_O, ZX_P +$80; STOP
+ DEFB ZX_S, ZX_L, ZX_O, ZX_W +$80; SLOW
+ DEFB ZX_F, ZX_A, ZX_S, ZX_T +$80; FAST
+ DEFB ZX_N, ZX_E, ZX_W +$80; NEW
+ DEFB ZX_S, ZX_C, ZX_R, ZX_O, ZX_L, ZX_L +$80; SCROLL
+ DEFB ZX_C, ZX_O, ZX_N, ZX_T +$80; CONT
+ DEFB ZX_D, ZX_I, ZX_M +$80; DIM
+ DEFB ZX_R, ZX_E, ZX_M +$80; REM
+ DEFB ZX_F, ZX_O, ZX_R +$80; FOR
+ DEFB ZX_G, ZX_O, ZX_T, ZX_O +$80; GOTO
+ DEFB ZX_G, ZX_O, ZX_S, ZX_U, ZX_B +$80; GOSUB
+ DEFB ZX_I, ZX_N, ZX_P, ZX_U, ZX_T +$80; INPUT
+ DEFB ZX_L, ZX_O, ZX_A, ZX_D +$80; LOAD
+ DEFB ZX_L, ZX_I, ZX_S, ZX_T +$80; LIST
+ DEFB ZX_L, ZX_E, ZX_T +$80; LET
+ DEFB ZX_P, ZX_A, ZX_U, ZX_S, ZX_E +$80; PAUSE
+ DEFB ZX_N, ZX_E, ZX_X, ZX_T +$80; NEXT
+ DEFB ZX_P, ZX_O, ZX_K, ZX_E +$80; POKE
+ DEFB ZX_P, ZX_R, ZX_I, ZX_N, ZX_T +$80; PRINT
+ DEFB ZX_P, ZX_L, ZX_O, ZX_T +$80; PLOT
+ DEFB ZX_R, ZX_U, ZX_N +$80; RUN
+ DEFB ZX_S, ZX_A, ZX_V, ZX_E +$80; SAVE
+ DEFB ZX_R, ZX_A, ZX_N, ZX_D +$80; RAND
+ DEFB ZX_I, ZX_F +$80; IF
+ DEFB ZX_C, ZX_L, ZX_S +$80; CLS
+ DEFB ZX_U, ZX_N, ZX_P, ZX_L, ZX_O, ZX_T +$80; UNPLOT
+ DEFB ZX_C, ZX_L, ZX_E, ZX_A, ZX_R +$80; CLEAR
+ DEFB ZX_R, ZX_E, ZX_T, ZX_U, ZX_R, ZX_N +$80; RETURN
+ DEFB ZX_C, ZX_O, ZX_P, ZX_Y +$80; COPY
+ DEFB ZX_R, ZX_N, ZX_D +$80; RND
+ DEFB ZX_I, ZX_N, ZX_K, ZX_E, ZX_Y, ZX_DOLLAR +$80; INKEY$
+ DEFB ZX_P, ZX_I +$80; PI
+
+------------------------------------------------------------------------
+
+; THE *'LOAD_SAVE UPDATE'* ROUTINE
+------------------------------------------------------------------------
+
+;
+;
+
+mark_01FC:
+*LOAD_SAVE:*
+ INC HL ;
+ EX DE,HL ;
+ LD HL,(E_LINE) ; system variable edit line E_LINE.
+ SCF ; set carry flag
+ SBC HL,DE ;
+ EX DE,HL ;
+ RET NC ; return if more bytes to LOAD_SAVE.
+
+ POP HL ; else drop return address
+
+------------------------------------------------------------------------
+
+; THE *'DISPLAY'* ROUTINES
+------------------------------------------------------------------------
+
+;
+;
+
+mark_0207:
+*SLOW_FAST:*
+ LD HL,CDFLAG ; Address the system variable CDFLAG.
+ LD A,(HL) ; Load value to the accumulator.
+ RLA ; rotate bit 6 to position 7.
+ XOR (HL) ; exclusive or with original bit 7.
+ RLA ; rotate result out to carry.
+ RET NC ; return if both bits were the same.
+
+; Now test if this really is a ZX81 or a ZX80 running the upgraded ROM.
+; The standard ZX80 did not have an NMI generator.
+
+ LD A,$7F ; Load accumulator with %011111111
+ EX AF,AF' ; save in AF'
+
+ LD B,17 ; A counter within which an NMI should occur
+ ; if this is a ZX81.
+ OUT (IO_PORT_NMI_GEN_ON),A ; start the NMI generator.
+
+; Note that if this is a ZX81 then the NMI will increment AF'.
+
+mark_0216:
+*LOOP_11:*
+
+ DJNZ LOOP_11 <#LOOP_11> ; self loop to give the NMI a chance to kick in.
+ ; = 16*13 clock cycles + 8 = 216 clock cycles.
+
+ OUT (IO_PORT_NMI_GEN_OFF),A ; Turn off the NMI generator.
+ EX AF,AF' ; bring back the AF' value.
+ RLA ; test bit 7.
+ JR NC,NO_SLOW <#NO_SLOW> ; forward, if bit 7 is still reset, to NO_SLOW.
+
+; If the AF' was incremented then the NMI generator works and SLOW mode can
+; be set.
+
+ SET 7,(HL) ; Indicate SLOW mode - Compute and Display.
+
+ PUSH AF ; * Save Main Registers
+ PUSH BC ; **
+ PUSH DE ; ***
+ PUSH HL ; ****
+
+ JR DISPLAY_1 <#DISPLAY_1> ; skip forward - to DISPLAY_1.
+
+; ___
+
+mark_0226:
+*NO_SLOW:*
+ RES 6,(HL) ; reset bit 6 of CDFLAG.
+ RET ; return.
+
+------------------------------------------------------------------------
+
+; THE *'MAIN DISPLAY'* LOOP
+------------------------------------------------------------------------
+
+; This routine is executed once for every frame displayed.
+
+mark_0229:
+*DISPLAY_1:*
+
+ LD HL,(FRAMES) ; fetch two-byte system variable FRAMES.
+ DEC HL ; decrement frames counter.
+mark_022D:
+*DISPLAY_P:*
+ LD A,$7F ; prepare a mask
+ AND H ; pick up bits 6-0 of H.
+ OR L ; and any bits of L.
+ LD A,H ; reload A with all bits of H for PAUSE test.
+
+; Note both branches must take the same time.
+
+ JR NZ,ANOTHER <#ANOTHER> ; (12/7) forward if bits 14-0 are not zero
+ ; to ANOTHER
+
+ RLA ; (4) test bit 15 of FRAMES.
+ JR OVER_NC <#OVER_NC> ; (12) forward with result to OVER_NC
+
+; ___
+
+mark_0237:
+*ANOTHER:*
+ LD B,(HL) ; (7) Note. Harmless Nonsensical Timing weight.
+ SCF ; (4) Set Carry Flag.
+
+; Note. the branch to here takes either (12)(7)(4) cyles or (7)(4)(12) cycles.
+
+mark_0239:
+*OVER_NC:*
+ LD H,A ; (4) set H to zero
+ LD (FRAMES),HL ; (16) update system variable FRAMES
+ RET NC ; (11/5) return if FRAMES is in use by PAUSE
+ ; command.
+
+mark_023E:
+*DISPLAY_2:*
+ CALL KEYBOARD <#KEYBOARD> ; gets the key row in H and the column in L.
+ ; Reading the ports also starts
+ ; the TV frame synchronization pulse. (VSYNC)
+
+ LD BC,(LAST_K) ; fetch the last key values
+ LD (LAST_K),HL ; update LAST_K with new values.
+
+ LD A,B ; load A with previous column - will be $FF if
+ ; there was no key.
+ ADD A,2 ; adding two will set carry if no previous key.
+
+ SBC HL,BC ; subtract with the carry the two key values.
+
+; If the same key value has been returned twice then HL will be zero.
+
+ LD A,(DEBOUNCE_VAR)
+ OR H ; and OR with both bytes of the difference
+ OR L ; setting the zero flag for the upcoming branch.
+
+ LD E,B ; transfer the column value to E
+ LD B,11 ; and load B with eleven
+
+ LD HL,CDFLAG ; address system variable CDFLAG
+ RES 0,(HL) ; reset the rightmost bit of CDFLAG
+ JR NZ,NO_KEY <#NO_KEY> ; skip forward if debounce/diff >0 to NO_KEY
+
+ BIT 7,(HL) ; test compute and display bit of CDFLAG
+ SET 0,(HL) ; set the rightmost bit of CDFLAG.
+ RET Z ; return if bit 7 indicated fast mode.
+
+ DEC B ; (4) decrement the counter.
+ NOP ; (4) Timing - 4 clock cycles. ??
+ SCF ; (4) Set Carry Flag
+
+mark_0264:
+*NO_KEY:*
+
+ LD HL,DEBOUNCE_VAR ;
+ CCF ; Complement Carry Flag
+ RL B ; rotate left B picking up carry
+ ; C<-76543210<-C
+
+
+
+
+
+
+mark_026A:
+*LOOP_B:*
+
+ DJNZ LOOP_B <#LOOP_B> ; self-loop while B>0 to LOOP_B
+
+ LD B,(HL) ; fetch value of DEBOUNCE_VAR to B
+ LD A,E ; transfer column value
+ CP $FE ;
+ SBC A,A ; A = A-A-C = 0-Carry
+#if 1
+; I think this truncating DEBOUNCE_VAR
+; which would explain why the VSYNC time didn't match
+; my calculations that assumed debouncing for 255 loops.
+;
+;
+ LD B,$1F ; binary 000 11111
+ OR (HL) ;
+ AND B ; truncate column, 0 to 31
+#endif
+ RRA ;
+ LD (HL),A ;
+
+ OUT (IO_PORT_SCREEN),A ; end the TV frame synchronization pulse.
+
+ LD HL,(D_FILE) ; (12) set HL to the Display File from D_FILE
+ SET 7,H ; (8) set bit 15 to address the echo display.
+
+ CALL DISPLAY_3 <#DISPLAY_3> ; (17) routine DISPLAY_3 displays the top set
+ ; of blank lines.
+
+------------------------------------------------------------------------
+
+; THE *'VIDEO_1'* ROUTINE
+------------------------------------------------------------------------
+
+
+mark_0281:
+*R_IX_1:*
+ LD A,R ; (9) Harmless Nonsensical Timing
+ ; or something very clever?
+ LD BC,25*256+1 ; (10) 25 lines, 1 scanline in first. ($1901)
+
+; 32 characters, use $F5 (i.e. minus 11)
+; 40 characters, use $ED (i.e. minus 19)
+;
+
+mark_0286:
+ LD A,277-CHARS_PER_LINE_WINDOW ; $F5 for 6.5MHz clocked machines
+ ; (7) This value will be loaded into R and
+ ; ensures that the cycle starts at the right
+ ; part of the display - after last character
+ ; position.
+
+ CALL DISPLAY_5 <#DISPLAY_5> ; (17) routine DISPLAY_5 completes the current
+ ; blank line and then generates the display of
+ ; the live picture using INT interrupts
+ ; The final interrupt returns to the next
+ ; address.
+*R_IX_1_LAST_NEWLINE:*
+ DEC HL ; point HL to the last NEWLINE/HALT.
+
+ CALL DISPLAY_3 <#DISPLAY_3> ; displays the bottom set of blank lines.
+
+; ___
+
+mark_028F:
+*R_IX_2:*
+ JP DISPLAY_1 <#DISPLAY_1> ; JUMP back to DISPLAY_1
+
+------------------------------------------------------------------------
+
+; THE *'DISPLAY BLANK LINES'* ROUTINE
+------------------------------------------------------------------------
+
+; This subroutine is called twice (see above) to generate first the blank
+; lines at the top of the television display and then the blank lines at the
+; bottom of the display.
+;
+; It is actually pretty bad.
+; PAL or NTSC = 312 or
+; 1 to 5 = 5 long and 5 short sync
+; 6 to 23 = blank
+; 24 to 309 = image
+; 310 to 312 = 6 short sync
+;
+; The ZX80 generates either 62 or 110 blank lines
+;
+; 262 - 31 - 31 = 200
+; 312 - 55 - 55 = 202
+;
+; This does not include 'VSYNC' line periods.
+;
+
+mark_0292:
+*DISPLAY_3:*
+ POP IX ; pop the return address to IX register.
+ ; will be either R_IX_1 or R_IX_2 - see above.
+
+ LD C,(IY+MARGIN-RAMBASE) ; load C with value of system constant MARGIN.
+ BIT 7,(IY+CDFLAG-RAMBASE) ; test CDFLAG for compute and display.
+ JR Z,DISPLAY_4 <#DISPLAY_4> ; forward, with FAST mode, to DISPLAY_4
+
+ LD A,C ; move MARGIN to A - 31d or 55d.
+ NEG ; Negate
+ INC A ;
+ EX AF,AF' ; place negative count of blank lines in A'
+
+ OUT (IO_PORT_NMI_GEN_ON),A ; enable the NMI generator.
+
+ POP HL ; ****
+ POP DE ; ***
+ POP BC ; **
+ POP AF ; * Restore Main Registers
+
+ RET ; return - end of interrupt. Return is to
+ ; user's program - BASIC or machine code.
+ ; which will be interrupted by every NMI.
+
+------------------------------------------------------------------------
+
+; THE *'FAST MODE'* ROUTINES
+------------------------------------------------------------------------
+
+
+mark_02A9:
+
+*DISPLAY_4:*
+
+ LD A,284-CHARS_PER_LINE_WINDOW ; $FC for 6.5MHz clocked machines
+ ; (7) load A with first R delay value
+
+ LD B,1 ; (7) one row only.
+
+ CALL DISPLAY_5 <#DISPLAY_5> ; (17) routine DISPLAY_5
+
+ DEC HL ; (6) point back to the HALT.
+ EX (SP),HL ; (19) Harmless Nonsensical Timing if paired.
+ EX (SP),HL ; (19) Harmless Nonsensical Timing.
+ JP (IX) ; (8) to R_IX_1 or R_IX_2
+
+------------------------------------------------------------------------
+
+; THE *'DISPLAY_5'* SUBROUTINE
+------------------------------------------------------------------------
+
+; This subroutine is called from SLOW mode and FAST mode to generate the
+; central TV picture. With SLOW mode the R register is incremented, with
+; each instruction, to $F7 by the time it completes. With fast mode, the
+; final R value will be $FF and an interrupt will occur as soon as the
+; Program Counter reaches the HALT. (24 clock cycles)
+
+mark_02B5:
+*DISPLAY_5:*
+ LD R,A ; (9) Load R from A. R = slow: $F5 fast: $FC
+
+;; Original, for 32 column display:
+;;
+;; LD A,$DD ; (7) load future R value. $F6 $FD
+;;
+;; For other display widths,
+;; need to count down three instructions then the number of characters
+;;
+ LD A,256-3-CHARS_PER_LINE_WINDOW ; (7) load future R value. $F6 $FD
+
+ EI ; (4) Enable Interrupts $F7 $FE
+
+ JP (HL) ; (4) jump to the echo display. $F8 $FF
+
+------------------------------------------------------------------------
+
+; THE *'KEYBOARD SCANNING'* SUBROUTINE
+------------------------------------------------------------------------
+
+; The keyboard is read during the vertical sync interval while no video is
+; being displayed. Reading a port with address bit 0 low i.e. $FE starts the
+; vertical sync pulse.
+
+mark_02BB:
+*KEYBOARD:*
+ LD HL,$FFFF ; (16) prepare a buffer to take key.
+ LD BC,$FEFE ; (20) set BC to port $FEFE. The B register,
+ ; with its single reset bit also acts as
+ ; an 8-counter.
+ IN A,(C) ; (11) read the port - all 16 bits are put on
+ ; the address bus. Start VSYNC pulse.
+ OR $01 ; (7) set the rightmost bit so as to ignore
+ ; the SHIFT key.
+
+mark_02C5:
+*EACH_LINE:*
+ OR $E0 ; [7] OR %11100000
+ LD D,A ; [4] transfer to D.
+ CPL ; [4] complement - only bits 4-0 meaningful now.
+ CP 1 ; [7] sets carry if A is zero.
+ SBC A,A ; [4] $FF if $00 else zero.
+ OR B ; [7] $FF or port FE,FD,FB....
+ AND L ; [4] unless more than one key, L will still be
+ ; $FF. if more than one key is pressed then A is
+ ; now invalid.
+ LD L,A ; [4] transfer to L.
+
+; now consider the column identifier.
+
+ LD A,H ; [4] will be $FF if no previous keys.
+ AND D ; [4] 111xxxxx
+ LD H,A ; [4] transfer A to H
+
+; since only one key may be pressed, H will, if valid, be one of
+; 11111110, 11111101, 11111011, 11110111, 11101111
+; reading from the outer column, say Q, to the inner column, say T.
+
+ RLC B ; [8] rotate the 8-counter/port address.
+ ; sets carry if more to do.
+ IN A,(C) ; [10] read another half-row.
+ ; all five bits this time.
+
+ JR C,EACH_LINE <#EACH_LINE> ; [12](7) loop back, until done, to EACH_LINE
+
+; The last row read is SHIFT,Z,X,C,V for the second time.
+
+ RRA ; (4) test the shift key - carry will be reset
+ ; if the key is pressed.
+ RL H ; (8) rotate left H picking up the carry giving
+ ; column values -
+ ; $FD, $FB, $F7, $EF, $DF.
+ ; or $FC, $FA, $F6, $EE, $DE if shifted.
+
+; We now have H identifying the column and L identifying the row in the
+; keyboard matrix.
+
+; This is a good time to test if this is an American or British machine.
+; The US machine has an extra diode that causes bit 6 of a byte read from
+; a port to be reset.
+
+ RLA ; (4) compensate for the shift test.
+ RLA ; (4) rotate bit 7 out.
+ RLA ; (4) test bit 6.
+
+ SBC A,A ; (4) $FF or 0 {USA}
+ AND $18 ; (7) 24 or 0
+ ADD A,31 ; (7) 55 or 31
+
+; result is either 31 (USA) or 55 (UK) blank lines above and below the TV
+; picture.
+
+ LD (MARGIN),A ; (13) update system variable MARGIN
+
+ RET ; (10) return
+
+------------------------------------------------------------------------
+
+; THE *'SET FAST MODE'* SUBROUTINE
+------------------------------------------------------------------------
+
+;
+;
+
+mark_02E7:
+*SET_FAST:*
+ BIT 7,(IY+CDFLAG-RAMBASE)
+ RET Z ;
+
+ HALT ; Wait for Interrupt
+ OUT (IO_PORT_NMI_GEN_OFF),A ;
+ RES 7,(IY+CDFLAG-RAMBASE)
+ RET ; return.
+
+
+------------------------------------------------------------------------
+
+; THE *'REPORT_F'*
+------------------------------------------------------------------------
+
+
+mark_02F4:
+*REPORT_F:*
+ RST _ERROR_1
+ DEFB $0E ; Error Report: No Program Name supplied.
+
+------------------------------------------------------------------------
+
+; THE *'SAVE COMMAND'* ROUTINE
+------------------------------------------------------------------------
+
+;
+;
+
+mark_02F6:
+*SAVE:*
+ CALL NAME <#NAME>
+ JR C,REPORT_F <#REPORT_F> ; back with null name
+
+ EX DE,HL ;
+
+
+
+
+;
+; The next 6 bytes differ
+;
+#if NOT_BODGED
+; what ZASM assembled:
+; 02FC: 11CB12
+ LD DE,$12CB ; five seconds timing value (4811 decimal)
+; 02FF: CD460F
+mark_02FF:
+*HEADER:*
+ CALL BREAK_1 <#BREAK_1>
+
+#else
+; what the SG ROM disassembled to:
+; 02FC ED;FD
+ LDIR ; Patch tape SAVE
+; 02FE C3;07;02
+ JP SLOW_FAST ; to $0207
+; 0301 0F
+ RRCA
+#endif
+
+
+mark_0302:
+ JR NC,BREAK_2 <#BREAK_2>
+
+mark_0304:
+*DELAY_1:*
+ DJNZ DELAY_1 <#DELAY_1>
+
+ DEC DE ;
+ LD A,D ;
+ OR E ;
+ JR NZ,HEADER <#HEADER> ; back for delay to HEADER
+
+mark_030B:
+*OUT_NAME:*
+ CALL OUT_BYTE <#OUT_BYTE>
+ BIT 7,(HL) ; test for inverted bit.
+ INC HL ; address next character of name.
+ JR Z,OUT_NAME <#OUT_NAME> ; back if not inverted to OUT_NAME
+
+; now start saving the system variables onwards.
+
+ LD HL,VERSN ; set start of area to VERSN thereby
+ ; preserving RAMTOP etc.
+
+mark_0316:
+*OUT_PROG:*
+ CALL OUT_BYTE <#OUT_BYTE>
+
+ CALL LOAD_SAVE <#LOAD_SAVE> ; >>
+ JR OUT_PROG <#OUT_PROG> ; loop back
+
+------------------------------------------------------------------------
+
+; THE *'OUT_BYTE'* SUBROUTINE
+------------------------------------------------------------------------
+
+; This subroutine outputs a byte a bit at a time to a domestic tape recorder.
+
+mark_031E:
+*OUT_BYTE:*
+ LD E,(HL) ; fetch byte to be saved.
+ SCF ; set carry flag - as a marker.
+
+mark_0320:
+*EACH_BIT:*
+ RL E ; C < 76543210 < C
+ RET Z ; return when the marker bit has passed
+ ; right through. >>
+
+ SBC A,A ; $FF if set bit or $00 with no carry.
+ AND $05 ; $05 " " " " $00
+ ADD A,$04 ; $09 " " " " $04
+ LD C,A ; transfer timer to C. a set bit has a longer
+ ; pulse than a reset bit.
+
+mark_0329:
+*PULSES:*
+ OUT (IO_PORT_TAPE),A ; pulse to cassette.
+ LD B,$23 ; set timing constant
+
+mark_032D:
+*DELAY_2:*
+ DJNZ DELAY_2 <#DELAY_2> ; self-loop
+
+ CALL BREAK_1 <#BREAK_1> ; test for BREAK key.
+
+mark_0332:
+*BREAK_2:*
+ JR NC,REPORT_D <#REPORT_D> ; forward with break to REPORT_D
+
+ LD B,$1E ; set timing value.
+
+mark_0336:
+*DELAY_3:*
+
+ DJNZ DELAY_3 <#DELAY_3> ; self-loop
+
+ DEC C ; decrement counter
+ JR NZ,PULSES <#PULSES> ; loop back
+
+mark_033B:
+*DELAY_4:*
+ AND A ; clear carry for next bit test.
+ DJNZ DELAY_4 <#DELAY_4> ; self loop (B is zero - 256)
+
+ JR EACH_BIT <#EACH_BIT> ; loop back
+
+------------------------------------------------------------------------
+
+; THE *'LOAD COMMAND'* ROUTINE
+------------------------------------------------------------------------
+
+
+mark_0340:
+*LOAD:*
+ CALL NAME <#NAME>
+
+; DE points to start of name in RAM.
+
+ RL D ; pick up carry
+ RRC D ; carry now in bit 7.
+
+mark_0347:
+
+
+
+#if NOT_BODGED
+
+*LNEXT_PROG:*
+ CALL IN_BYTE <#IN_BYTE>
+ JR LNEXT_PROG <#LNEXT_PROG> ; loop
+
+------------------------------------------------------------------------
+
+; THE *'IN_BYTE'* SUBROUTINE
+------------------------------------------------------------------------
+
+
+mark_034C:
+*IN_BYTE:*
+ LD C,$01 ; prepare an eight counter 00000001.
+
+mark_034E:
+*NEXT_BIT:*
+ LD B,$00 ; set counter to 256
+
+#else
+; what the SG ROM has:
+;0347 EB
+ EX DE,HL ; NEXT-PROG
+;0348 ED;FC
+ LDIR ; Patch tape LOAD
+;034A C3;07;02
+ JP SLOW_FAST
+;034D 01;06;00
+ LD BC,6
+#endif
+
+
+
+
+mark_0350:
+*BREAK_3:*
+ LD A,$7F ; read the keyboard row
+ IN A,(IO_PORT_KEYBOARD_RD) ; with the SPACE key.
+
+ OUT (IO_PORT_SCREEN),A ; output signal to screen.
+
+ RRA ; test for SPACE pressed.
+ JR NC,BREAK_4 <#BREAK_4> ; forward if so to BREAK_4
+
+ RLA ; reverse above rotation
+ RLA ; test tape bit.
+ JR C,GET_BIT <#GET_BIT> ; forward if set to GET_BIT
+
+ DJNZ BREAK_3 <#BREAK_3> ; loop back
+
+ POP AF ; drop the return address.
+ CP D ; ugh.
+
+mark_0361:
+*RESTART:*
+ JP NC,INITIAL <#INITIAL> ; jump forward to INITIAL if D is zero
+ ; to reset the system
+ ; if the tape signal has timed out for example
+ ; if the tape is stopped. Not just a simple
+ ; report as some system variables will have
+ ; been overwritten.
+
+ LD H,D ; else transfer the start of name
+ LD L,E ; to the HL register
+
+mark_0366:
+*IN_NAME:*
+ CALL IN_BYTE <#IN_BYTE> ; is sort of recursion for name
+ ; part. received byte in C.
+ BIT 7,D ; is name the null string ?
+ LD A,C ; transfer byte to A.
+ JR NZ,MATCHING <#MATCHING> ; forward with null string
+
+ CP (HL) ; else compare with string in memory.
+ JR NZ,LNEXT_PROG <#LNEXT_PROG> ; back with mis-match
+ ; (seemingly out of subroutine but return
+ ; address has been dropped).
+
+
+mark_0371:
+*MATCHING:*
+ INC HL ; address next character of name
+ RLA ; test for inverted bit.
+ JR NC,IN_NAME <#IN_NAME> ; back if not
+
+; the name has been matched in full.
+; proceed to load the data but first increment the high byte of E_LINE, which
+; is one of the system variables to be loaded in. Since the low byte is loaded
+; before the high byte, it is possible that, at the in-between stage, a false
+; value could cause the load to end prematurely - see LOAD_SAVE check.
+
+ INC (IY+E_LINE_hi-RAMBASE) ; increment E_LINE_hi.
+ LD HL,VERSN ; start loading at VERSN.
+
+mark_037B:
+*IN_PROG:*
+ LD D,B ; set D to zero as indicator.
+ CALL IN_BYTE <#IN_BYTE> ; loads a byte
+ LD (HL),C ; insert assembled byte in memory.
+ CALL LOAD_SAVE <#LOAD_SAVE> ; >>
+ JR IN_PROG <#IN_PROG> ; loop back
+
+; ___
+
+; this branch assembles a full byte before exiting normally
+; from the IN_BYTE subroutine.
+
+mark_0385:
+*GET_BIT:*
+ PUSH DE ; save the
+ LD E,$94 ; timing value.
+
+mark_0388:
+*TRAILER:*
+ LD B,26 ; counter to twenty six.
+
+mark_038A:
+*COUNTER:*
+ DEC E ; decrement the measuring timer.
+ IN A,(IO_PORT_KEYBOARD_RD) ; read the tape input
+ RLA ;
+ BIT 7,E ;
+ LD A,E ;
+ JR C,TRAILER <#TRAILER> ; loop back with carry to TRAILER
+
+ DJNZ COUNTER <#COUNTER>
+
+ POP DE ;
+ JR NZ,BIT_DONE <#BIT_DONE>
+
+ CP $56 ;
+ JR NC,NEXT_BIT <#NEXT_BIT>
+
+mark_039C:
+*BIT_DONE:*
+ CCF ; complement carry flag
+ RL C ;
+ JR NC,NEXT_BIT <#NEXT_BIT>
+
+ RET ; return with full byte.
+
+; ___
+
+; if break is pressed while loading data then perform a reset.
+; if break pressed while waiting for program on tape then OK to break.
+
+mark_03A2:
+*BREAK_4:*
+ LD A,D ; transfer indicator to A.
+ AND A ; test for zero.
+ JR Z,RESTART <#RESTART> ; back if so
+
+
+mark_03A6:
+*REPORT_D:*
+ RST _ERROR_1
+ DEFB $0C ; Error Report: BREAK - CONT repeats
+
+------------------------------------------------------------------------
+
+; THE *'PROGRAM NAME'* SUBROUTINE
+------------------------------------------------------------------------
+
+
+mark_03A8:
+*NAME:*
+ CALL SCANNING <#SCANNING>
+ LD A,(FLAGS) ; sv
+ ADD A,A ;
+ JP M,REPORT_C <#REPORT_C>
+
+ POP HL ;
+ RET NC ;
+
+ PUSH HL ;
+ CALL SET_FAST <#SET_FAST>
+ CALL STK_FETCH <#STK_FETCH>
+ LD H,D ;
+ LD L,E ;
+ DEC C ;
+ RET M ;
+
+ ADD HL,BC ;
+ SET 7,(HL) ;
+ RET ;
+
+------------------------------------------------------------------------
+
+; THE *'NEW'* COMMAND ROUTINE
+------------------------------------------------------------------------
+
+
+mark_03C3:
+*NEW:*
+ CALL SET_FAST <#SET_FAST>
+ LD BC,(RAMTOP) ; fetch value of system variable RAMTOP
+ DEC BC ; point to last system byte.
+
+------------------------------------------------------------------------
+
+; THE *'RAM CHECK'* ROUTINE
+------------------------------------------------------------------------
+
+
+mark_03CB:
+*RAM_CHECK:*
+ LD H,B ;
+ LD L,C ;
+ LD A,$3F ;
+
+mark_03CF:
+*RAM_FILL:*
+ LD (HL),$02 ;
+ DEC HL ;
+ CP H ;
+ JR NZ,RAM_FILL <#RAM_FILL>
+
+mark_03D5:
+*RAM_READ:*
+ AND A ;
+ SBC HL,BC ;
+ ADD HL,BC ;
+ INC HL ;
+ JR NC,SET_TOP <#SET_TOP>
+
+ DEC (HL) ;
+ JR Z,SET_TOP <#SET_TOP>
+
+ DEC (HL) ;
+ JR Z,RAM_READ <#RAM_READ>
+
+mark_03E2:
+*SET_TOP:*
+ LD (RAMTOP),HL ; set system variable RAMTOP to first byte
+ ; above the BASIC system area.
+
+------------------------------------------------------------------------
+
+; THE *'INITIALIZATION'* ROUTINE
+------------------------------------------------------------------------
+
+
+mark_03E5:
+*INITIAL:*
+ LD HL,(RAMTOP) ; fetch system variable RAMTOP.
+ DEC HL ; point to last system byte.
+ LD (HL),$3E ; make GO SUB end-marker $3E - too high for
+ ; high order byte of line number.
+ ; (was $3F on ZX80)
+ DEC HL ; point to unimportant low-order byte.
+ LD SP,HL ; and initialize the stack-pointer to this
+ ; location.
+ DEC HL ; point to first location on the machine stack
+ DEC HL ; which will be filled by next CALL/PUSH.
+ LD (ERR_SP),HL ; set the error stack pointer ERR_SP to
+ ; the base of the now empty machine stack.
+
+; Now set the I register so that the video hardware knows where to find the
+; character set. This ROM only uses the character set when printing to
+; the ZX Printer. The TV picture is formed by the external video hardware.
+; Consider also, that this 8K ROM can be retro-fitted to the ZX80 instead of
+; its original 4K ROM so the video hardware could be on the ZX80.
+
+ LD A,$1E ; address for this ROM is $1E00.
+ LD I,A ; set I register from A.
+ IM 1 ; select Z80 Interrupt Mode 1.
+
+ LD IY,ERR_NR ; set IY to the start of RAM so that the
+ ; system variables can be indexed.
+
+ LD (IY+CDFLAG-RAMBASE),%01000000
+ ; Bit 6 indicates Compute and Display required.
+
+ LD HL,USER_RAM ; The first location after System Variables -
+ ; 16509 decimal.
+ LD (D_FILE),HL ; set system variable D_FILE to this value.
+ LD B,$19 ; prepare minimal screen of 24 NEWLINEs
+ ; following an initial NEWLINE.
+
+mark_0408:
+*LINE:*
+ LD (HL),ZX_NEWLINE ; insert NEWLINE (HALT instruction)
+ INC HL ; point to next location.
+ DJNZ LINE <#LINE> ; loop back for all twenty five to LINE
+
+ LD (VARS),HL ; set system variable VARS to next location
+
+ CALL CLEAR <#CLEAR> ; sets $80 end-marker and the
+ ; dynamic memory pointers E_LINE, STKBOT and
+ ; STKEND.
+
+mark_0413:
+*N_L_ONLY:*
+ CALL CURSOR_IN <#CURSOR_IN> ; inserts the cursor and
+ ; end-marker in the Edit Line also setting
+ ; size of lower display to two lines.
+
+ CALL SLOW_FAST <#SLOW_FAST> ; selects COMPUTE and DISPLAY
+
+------------------------------------------------------------------------
+
+; THE *'BASIC LISTING'* SECTION
+------------------------------------------------------------------------
+
+
+mark_0419:
+*UPPER:*
+ CALL CLS <#CLS>
+ LD HL,(E_PPC) ; sv
+ LD DE,(S_TOP) ; sv
+ AND A ;
+ SBC HL,DE ;
+ EX DE,HL ;
+ JR NC,ADDR_TOP <#ADDR_TOP>
+
+ ADD HL,DE ;
+ LD (S_TOP),HL ; sv
+
+mark_042D:
+*ADDR_TOP:*
+ CALL LINE_ADDR <#LINE_ADDR>
+ JR Z,LIST_TOP <#LIST_TOP>
+
+ EX DE,HL ;
+
+mark_0433:
+*LIST_TOP:*
+ CALL LIST_PROG <#LIST_PROG>
+ DEC (IY+BERG-RAMBASE)
+ JR NZ,LOWER <#LOWER>
+
+ LD HL,(E_PPC) ; sv
+ CALL LINE_ADDR <#LINE_ADDR>
+ LD HL,(CH_ADD) ; sv
+ SCF ; Set Carry Flag
+ SBC HL,DE ;
+ LD HL,S_TOP ; sv
+ JR NC,INC_LINE <#INC_LINE>
+
+ EX DE,HL ;
+ LD A,(HL) ;
+ INC HL ;
+ LDI ;
+ LD (DE),A ;
+ JR UPPER <#UPPER>
+; ___
+
+mark_0454:
+*DOWN_KEY:*
+
+ LD HL,E_PPC ; sv
+
+mark_0457:
+*INC_LINE:*
+ LD E,(HL) ;
+ INC HL ;
+ LD D,(HL) ;
+ PUSH HL ;
+ EX DE,HL ;
+ INC HL ;
+ CALL LINE_ADDR <#LINE_ADDR>
+ CALL LINE_NUM <#LINE_NUM>
+ POP HL ;
+
+mark_0464:
+*KEY_INPUT:*
+ BIT 5,(IY+FLAGX-RAMBASE)
+ JR NZ,LOWER <#LOWER> ; forward
+
+ LD (HL),D ;
+ DEC HL ;
+ LD (HL),E ;
+ JR UPPER <#UPPER>
+
+------------------------------------------------------------------------
+
+; THE *'EDIT LINE COPY'* SECTION
+------------------------------------------------------------------------
+
+; This routine sets the edit line to just the cursor when
+; 1) There is not enough memory to edit a BASIC line.
+; 2) The edit key is used during input.
+; The entry point LOWER
+
+
+mark_046F:
+*EDIT_INP:*
+ CALL CURSOR_IN <#CURSOR_IN> ; sets cursor only edit line.
+
+; ->
+
+mark_0472:
+*LOWER:*
+ LD HL,(E_LINE) ; fetch edit line start from E_LINE.
+
+mark_0475:
+*EACH_CHAR:*
+ LD A,(HL) ; fetch a character from edit line.
+ CP $7E ; compare to the number marker.
+ JR NZ,END_LINE <#END_LINE> ; forward if not
+
+ LD BC,6 ; else six invisible bytes to be removed.
+ CALL RECLAIM_2 <#RECLAIM_2>
+ JR EACH_CHAR <#EACH_CHAR> ; back
+; ___
+
+mark_0482:
+*END_LINE:*
+ CP ZX_NEWLINE ;
+ INC HL ;
+ JR NZ,EACH_CHAR <#EACH_CHAR>
+
+mark_0487:
+*EDIT_LINE:*
+ CALL CURSOR <#CURSOR> ; sets cursor K or L.
+
+mark_048A:
+*EDIT_ROOM:*
+ CALL LINE_ENDS <#LINE_ENDS>
+ LD HL,(E_LINE) ; sv
+ LD (IY+ERR_NR-RAMBASE),$FF
+ CALL COPY_LINE <#COPY_LINE>
+ BIT 7,(IY+ERR_NR-RAMBASE)
+ JR NZ,DISPLAY_6 <#DISPLAY_6>
+
+ LD A,(DF_SZ) ;
+ CP CHARS_VERTICAL ; $18 = 24
+ JR NC,DISPLAY_6 <#DISPLAY_6>
+
+ INC A ;
+ LD (DF_SZ),A ;
+ LD B,A ;
+ LD C,1 ;
+ CALL LOC_ADDR <#LOC_ADDR>
+ LD D,H ;
+ LD E,L ;
+ LD A,(HL) ;
+
+mark_04B1:
+*FREE_LINE:*
+ DEC HL ;
+ CP (HL) ;
+ JR NZ,FREE_LINE <#FREE_LINE>
+
+ INC HL ;
+ EX DE,HL ;
+ LD A,(RAMTOP+1) ; sv RAMTOP_hi
+ CP $4D ;
+ CALL C,RECLAIM_1 <#RECLAIM_1>
+ JR EDIT_ROOM <#EDIT_ROOM>
+
+------------------------------------------------------------------------
+
+; THE *'WAIT FOR KEY'* SECTION
+------------------------------------------------------------------------
+
+
+mark_04C1:
+*DISPLAY_6:*
+ LD HL,$0000 ;
+ LD (X_PTR),HL ; sv
+
+ LD HL,CDFLAG ; system variable CDFLAG
+
+
+
+
+#if NOT_BODGED
+ BIT 7,(HL) ;
+
+ CALL Z,DISPLAY_1 <#DISPLAY_1>
+
+mark_04CF:
+*SLOW_DISP:*
+ BIT 0,(HL) ;
+ JR Z,SLOW_DISP <#SLOW_DISP>
+
+#else
+; 04CA D3;00
+ OUT ($00),A ; PORT 0
+; 04CC CB;46
+L04CC:
+ BIT 0,(HL)
+; 04CE 28;FC
+ JR Z,L04CC
+; 04D0 D3;01
+ OUT ($01),A ; PORT 1
+; 04D2 00
+ NOP
+
+
+#endif
+
+
+
+
+ LD BC,(LAST_K) ; sv
+ CALL DEBOUNCE <#DEBOUNCE>
+ CALL DECODE <#DECODE>
+
+ JR NC,LOWER <#LOWER> ; back
+
+------------------------------------------------------------------------
+
+; THE *'KEYBOARD DECODING'* SECTION
+------------------------------------------------------------------------
+
+; The decoded key value is in E and HL points to the position in the
+; key table. D contains zero.
+
+mark_04DF:
+*K_DECODE:*
+ LD A,(MODE) ; Fetch value of system variable MODE
+ DEC A ; test the three values together
+
+ JP M,FETCH_2 <#FETCH_2> ; forward, if was zero
+
+ JR NZ,FETCH_1 <#FETCH_1> ; forward, if was 2
+
+; The original value was one and is now zero.
+
+ LD (MODE),A ; update the system variable MODE
+
+ DEC E ; reduce E to range $00 - $7F
+ LD A,E ; place in A
+ SUB 39 ; subtract 39 setting carry if range 00 - 38
+ JR C,FUNC_BASE <#FUNC_BASE> ; forward, if so
+
+ LD E,A ; else set E to reduced value
+
+mark_04F2:
+*FUNC_BASE:*
+ LD HL,K_FUNCT <#K_FUNCT> ; address of K_FUNCT table for function keys.
+ JR TABLE_ADD <#TABLE_ADD> ; forward
+; ___
+mark_04F7:
+*FETCH_1:*
+ LD A,(HL) ;
+ CP ZX_NEWLINE ;
+ JR Z,K_L_KEY <#K_L_KEY>
+
+ CP ZX_RND ; $40
+ SET 7,A ;
+ JR C,ENTER <#ENTER>
+
+ LD HL,$00C7 ; (expr reqd)
+
+mark_0505:
+*TABLE_ADD:*
+ ADD HL,DE ;
+ JR FETCH_3 <#FETCH_3>
+
+; ___
+
+mark_0508:
+*FETCH_2:*
+ LD A,(HL) ;
+ BIT 2,(IY+FLAGS-RAMBASE) ; K or L mode ?
+ JR NZ,TEST_CURS <#TEST_CURS>
+
+ ADD A,$C0 ;
+ CP $E6 ;
+ JR NC,TEST_CURS <#TEST_CURS>
+
+mark_0515:
+*FETCH_3:*
+ LD A,(HL) ;
+
+mark_0516:
+*TEST_CURS:*
+ CP $F0 ;
+ JP PE,KEY_SORT <#KEY_SORT>
+
+mark_051B:
+*ENTER:*
+ LD E,A ;
+ CALL CURSOR <#CURSOR>
+
+ LD A,E ;
+ CALL ADD_CHAR <#ADD_CHAR>
+
+mark_0523:
+*BACK_NEXT:*
+ JP LOWER <#LOWER> ; back
+
+------------------------------------------------------------------------
+
+; THE *'ADD CHARACTER'* SUBROUTINE
+------------------------------------------------------------------------
+
+mark_0526:
+*ADD_CHAR:*
+ CALL ONE_SPACE <#ONE_SPACE>
+ LD (DE),A ;
+ RET ;
+
+------------------------------------------------------------------------
+
+; THE *'CURSOR KEYS'* ROUTINE
+------------------------------------------------------------------------
+
+mark_052B:
+*K_L_KEY:*
+ LD A,ZX_KL ;
+
+mark_052D:
+*KEY_SORT:*
+ LD E,A ;
+ LD HL,$0482 ; base address of ED_KEYS (exp reqd)
+ ADD HL,DE ;
+ ADD HL,DE ;
+ LD C,(HL) ;
+ INC HL ;
+ LD B,(HL) ;
+ PUSH BC ;
+
+mark_0537:
+*CURSOR:*
+ LD HL,(E_LINE) ; sv
+ BIT 5,(IY+FLAGX-RAMBASE)
+ JR NZ,L_MODE <#L_MODE>
+
+mark_0540:
+*K_MODE:*
+ RES 2,(IY+FLAGS-RAMBASE) ; Signal use K mode
+
+mark_0544:
+*TEST_CHAR:*
+ LD A,(HL) ;
+ CP ZX_CURSOR ;
+ RET Z ; return
+
+ INC HL ;
+ CALL NUMBER <#NUMBER>
+ JR Z,TEST_CHAR <#TEST_CHAR>
+
+ CP ZX_A ; $26
+ JR C,TEST_CHAR <#TEST_CHAR>
+
+ CP $DE ; ZX_THEN ??
+ JR Z,K_MODE <#K_MODE>
+
+mark_0556:
+*L_MODE:*
+ SET 2,(IY+FLAGS-RAMBASE) ; Signal use L mode
+ JR TEST_CHAR <#TEST_CHAR>
+
+------------------------------------------------------------------------
+
+; THE *'CLEAR_ONE'* SUBROUTINE
+------------------------------------------------------------------------
+
+mark_055C:
+*CLEAR_ONE:*
+ LD BC,$0001 ;
+ JP RECLAIM_2 <#RECLAIM_2>
+
+------------------------------------------------------------------------
+
+; THE *'EDITING KEYS'* TABLE
+------------------------------------------------------------------------
+
+mark_0562:
+*ED_KEYS:*
+ DEFW UP_KEY <#UP_KEY>
+ DEFW DOWN_KEY <#DOWN_KEY>
+ DEFW LEFT_KEY <#LEFT_KEY>
+ DEFW RIGHT_KEY <#RIGHT_KEY>
+ DEFW FUNCTION <#FUNCTION>
+ DEFW EDIT_KEY <#EDIT_KEY>
+ DEFW N_L_KEY <#N_L_KEY>
+ DEFW RUBOUT <#RUBOUT>
+ DEFW FUNCTION <#FUNCTION>
+ DEFW FUNCTION <#FUNCTION>
+
+
+------------------------------------------------------------------------
+
+; THE *'CURSOR LEFT'* ROUTINE
+------------------------------------------------------------------------
+
+;
+;
+
+mark_LEFT_KEY:
+*LEFT_KEY:*
+ CALL LEFT_EDGE <#LEFT_EDGE>
+ LD A,(HL) ;
+ LD (HL),ZX_CURSOR ;
+ INC HL ;
+ JR GET_CODE <#GET_CODE>
+
+------------------------------------------------------------------------
+
+; THE *'CURSOR RIGHT'* ROUTINE
+------------------------------------------------------------------------
+
+
+mark_RIGHT_KEY:
+*RIGHT_KEY:*
+ INC HL ;
+ LD A,(HL) ;
+ CP ZX_NEWLINE ;
+ JR Z,ENDED_2 <#ENDED_2>
+
+ LD (HL),ZX_CURSOR ;
+ DEC HL ;
+
+mark_0588:
+*GET_CODE:*
+ LD (HL),A ;
+
+mark_0589:
+*ENDED_1:*
+ JR BACK_NEXT <#BACK_NEXT>
+
+------------------------------------------------------------------------
+
+; THE *'RUBOUT'* ROUTINE
+------------------------------------------------------------------------
+
+
+mark_058B:
+*RUBOUT:*
+ CALL LEFT_EDGE <#LEFT_EDGE>
+ CALL CLEAR_ONE <#CLEAR_ONE>
+ JR ENDED_1 <#ENDED_1>
+
+------------------------------------------------------------------------
+
+; THE *'ED_EDGE'* SUBROUTINE
+------------------------------------------------------------------------
+
+;
+;
+
+mark_0593:
+*LEFT_EDGE:*
+ DEC HL ;
+ LD DE,(E_LINE) ; sv
+ LD A,(DE) ;
+ CP ZX_CURSOR ;
+ RET NZ ;
+
+ POP DE ;
+
+mark_059D:
+*ENDED_2:*
+ JR ENDED_1 <#ENDED_1>
+
+------------------------------------------------------------------------
+
+; THE *'CURSOR UP'* ROUTINE
+------------------------------------------------------------------------
+
+;
+;
+
+mark_059F:
+*UP_KEY:*
+ LD HL,(E_PPC) ; sv
+ CALL LINE_ADDR <#LINE_ADDR>
+ EX DE,HL ;
+ CALL LINE_NUM <#LINE_NUM>
+ LD HL,E_PPC+1 ; point to system variable E_PPC_hi
+ JP KEY_INPUT <#KEY_INPUT> ; jump back
+
+------------------------------------------------------------------------
+
+; THE *'FUNCTION KEY'* ROUTINE
+------------------------------------------------------------------------
+
+;
+;
+
+mark_FUNCTION:
+*FUNCTION:*
+ LD A,E ;
+ AND $07 ;
+ LD (MODE),A ; sv
+ JR ENDED_2 <#ENDED_2> ; back
+
+------------------------------------------------------------------------
+
+; THE *'COLLECT LINE NUMBER'* SUBROUTINE
+------------------------------------------------------------------------
+
+mark_05B7:
+*ZERO_DE:*
+ EX DE,HL ;
+ LD DE,DISPLAY_6 <#DISPLAY_6> + 1 ; $04C2 - a location addressing two zeros.
+
+; ->
+
+mark_05BB:
+*LINE_NUM:*
+ LD A,(HL) ;
+ AND $C0 ;
+ JR NZ,ZERO_DE <#ZERO_DE>
+
+ LD D,(HL) ;
+ INC HL ;
+ LD E,(HL) ;
+ RET ;
+
+------------------------------------------------------------------------
+
+; THE *'EDIT KEY'* ROUTINE
+------------------------------------------------------------------------
+
+
+mark_EDIT_KEY:
+*EDIT_KEY:*
+ CALL LINE_ENDS <#LINE_ENDS> ; clears lower display.
+
+ LD HL,EDIT_INP <#EDIT_INP> ; Address: EDIT_INP
+ PUSH HL ; ** is pushed as an error looping address.
+
+ BIT 5,(IY+FLAGX-RAMBASE) ; test FLAGX
+ RET NZ ; indirect jump if in input mode
+ ; to EDIT_INP <#EDIT_INP> (begin again).
+
+;
+
+ LD HL,(E_LINE) ; fetch E_LINE
+ LD (DF_CC),HL ; and use to update the screen cursor DF_CC
+
+; so now RST $10 will print the line numbers to the edit line instead of screen.
+; first make sure that no newline/out of screen can occur while sprinting the
+; line numbers to the edit line.
+
+ ; prepare line 0, column 0.
+
+ LD HL,256*CHARS_VERTICAL + CHARS_HORIZONTAL + 1
+;
+ LD (S_POSN),HL ; update S_POSN with these dummy values.
+
+ LD HL,(E_PPC) ; fetch current line from E_PPC may be a
+ ; non-existent line e.g. last line deleted.
+ CALL LINE_ADDR <#LINE_ADDR> ; gets address or that of
+ ; the following line.
+ CALL LINE_NUM <#LINE_NUM> ; gets line number if any in DE
+ ; leaving HL pointing at second low byte.
+
+ LD A,D ; test the line number for zero.
+ OR E ;
+ RET Z ; return if no line number - no program to edit.
+
+ DEC HL ; point to high byte.
+ CALL OUT_NO <#OUT_NO> ; writes number to edit line.
+
+ INC HL ; point to length bytes.
+ LD C,(HL) ; low byte to C.
+ INC HL ;
+ LD B,(HL) ; high byte to B.
+
+ INC HL ; point to first character in line.
+ LD DE,(DF_CC) ; fetch display file cursor DF_CC
+
+ LD A,ZX_CURSOR ; prepare the cursor character.
+ LD (DE),A ; and insert in edit line.
+ INC DE ; increment intended destination.
+
+ PUSH HL ; * save start of BASIC.
+
+ LD HL,29 ; set an overhead of 29 bytes.
+ ADD HL,DE ; add in the address of cursor.
+ ADD HL,BC ; add the length of the line.
+ SBC HL,SP ; subtract the stack pointer.
+
+ POP HL ; * restore pointer to start of BASIC.
+
+ RET NC ; return if not enough room to EDIT_INP EDIT_INP.
+ ; the edit key appears not to work.
+
+ LDIR ; else copy bytes from program to edit line.
+ ; Note. hidden floating point forms are also
+ ; copied to edit line.
+
+ EX DE,HL ; transfer free location pointer to HL
+
+ POP DE ; ** remove address EDIT_INP from stack.
+
+ CALL SET_STK_B <#SET_STK_B> ; sets STKEND from HL.
+
+ JR ENDED_2 <#ENDED_2> ; back to ENDED_2 and after 3 more jumps
+ ; to LOWER <#LOWER>, LOWER.
+ ; Note. The LOWER routine removes the hidden
+ ; floating-point numbers from the edit line.
+
+------------------------------------------------------------------------
+
+; THE *'NEWLINE KEY'* ROUTINE
+------------------------------------------------------------------------
+
+
+mark_060C:
+*N_L_KEY:*
+ CALL LINE_ENDS <#LINE_ENDS>
+
+ LD HL,LOWER <#LOWER> ; prepare address: LOWER
+
+ BIT 5,(IY+FLAGX-RAMBASE)
+ JR NZ,NOW_SCAN <#NOW_SCAN>
+
+ LD HL,(E_LINE) ; sv
+ LD A,(HL) ;
+ CP $FF ;
+ JR Z,STK_UPPER <#STK_UPPER>
+
+ CALL CLEAR_PRB <#CLEAR_PRB>
+ CALL CLS <#CLS>
+
+mark_0626:
+*STK_UPPER:*
+ LD HL,UPPER <#UPPER> ; Address: UPPER
+
+mark_0629:
+*NOW_SCAN:*
+ PUSH HL ; push routine address (LOWER or UPPER).
+ CALL LINE_SCAN <#LINE_SCAN>
+ POP HL ;
+ CALL CURSOR <#CURSOR>
+ CALL CLEAR_ONE <#CLEAR_ONE>
+ CALL E_LINE_NUM <#E_LINE_NUM>
+ JR NZ,N_L_INP <#N_L_INP>
+
+ LD A,B ;
+ OR C ;
+ JP NZ,N_L_LINE <#N_L_LINE>
+
+ DEC BC ;
+ DEC BC ;
+ LD (PPC),BC ; sv
+ LD (IY+DF_SZ-RAMBASE),2
+ LD DE,(D_FILE) ; sv
+
+ JR TEST_NULL <#TEST_NULL> ; forward
+
+; ___
+
+mark_064E:
+*N_L_INP:*
+ CP ZX_NEWLINE ;
+ JR Z,N_L_NULL <#N_L_NULL>
+
+ LD BC,(T_ADDR) ;
+ CALL LOC_ADDR <#LOC_ADDR>
+ LD DE,(NXTLIN) ;
+ LD (IY+DF_SZ-RAMBASE),2
+
+mark_0661:
+*TEST_NULL:*
+ RST _GET_CHAR
+ CP ZX_NEWLINE ;
+
+mark_0664:
+*N_L_NULL:*
+ JP Z,N_L_ONLY <#N_L_ONLY>
+
+ LD (IY+FLAGS-RAMBASE),$80
+ EX DE,HL ;
+
+mark_066C:
+*NEXT_LINE:*
+ LD (NXTLIN),HL ;
+ EX DE,HL ;
+ CALL TEMP_PTR2 <#TEMP_PTR2>
+ CALL LINE_RUN <#LINE_RUN>
+ RES 1,(IY+FLAGS-RAMBASE) ; Signal printer not in use
+ LD A,$C0 ;
+;; LD (IY+X_PTR_lo-RAMBASE),A ;; ERROR IN htm SOURCE! IY+$19 is X_PTR_hi
+ LD (IY+X_PTR_hi-RAMBASE),A
+ CALL X_TEMP <#X_TEMP>
+ RES 5,(IY+FLAGX-RAMBASE)
+ BIT 7,(IY+ERR_NR-RAMBASE)
+ JR Z,STOP_LINE <#STOP_LINE>
+
+ LD HL,(NXTLIN) ;
+ AND (HL) ;
+ JR NZ,STOP_LINE <#STOP_LINE>
+
+ LD D,(HL) ;
+ INC HL ;
+ LD E,(HL) ;
+ LD (PPC),DE ;
+ INC HL ;
+ LD E,(HL) ;
+ INC HL ;
+ LD D,(HL) ;
+ INC HL ;
+ EX DE,HL ;
+ ADD HL,DE ;
+ CALL BREAK_1 <#BREAK_1>
+ JR C,NEXT_LINE <#NEXT_LINE>
+
+ LD HL,ERR_NR
+ BIT 7,(HL)
+ JR Z,STOP_LINE <#STOP_LINE>
+
+ LD (HL),$0C
+
+mark_06AE:
+*STOP_LINE:*
+ BIT 7,(IY+PR_CC-RAMBASE)
+ CALL Z,COPY_BUFF <#COPY_BUFF>
+;
+#if 0
+ LD BC,$0121 ;
+#else
+ LD BC,256*1 + CHARS_HORIZONTAL + 1
+#endif
+;
+;
+ CALL LOC_ADDR <#LOC_ADDR>
+ LD A,(ERR_NR)
+ LD BC,(PPC)
+ INC A
+ JR Z,REPORT <#REPORT>
+
+ CP $09
+ JR NZ,CONTINUE <#CONTINUE>
+
+ INC BC
+
+mark_06CA:
+*CONTINUE:*
+ LD (OLDPPC),BC ;
+ JR NZ,REPORT <#REPORT>
+
+ DEC BC ;
+
+mark_06D1:
+*REPORT:*
+ CALL OUT_CODE <#OUT_CODE>
+ LD A,ZX_SLASH
+
+ RST _PRINT_A
+ CALL OUT_NUM <#OUT_NUM>
+ CALL CURSOR_IN <#CURSOR_IN>
+ JP DISPLAY_6 <#DISPLAY_6>
+
+; ___
+
+mark_06E0:
+*N_L_LINE:*
+ LD (E_PPC),BC ;
+ LD HL,(CH_ADD) ;
+ EX DE,HL ;
+ LD HL,N_L_ONLY <#N_L_ONLY>
+ PUSH HL ;
+ LD HL,(STKBOT) ;
+ SBC HL,DE ;
+ PUSH HL ;
+ PUSH BC ;
+ CALL SET_FAST <#SET_FAST>
+ CALL CLS <#CLS>
+ POP HL ;
+ CALL LINE_ADDR <#LINE_ADDR>
+ JR NZ,COPY_OVER <#COPY_OVER>
+
+ CALL NEXT_ONE <#NEXT_ONE>
+ CALL RECLAIM_2 <#RECLAIM_2>
+
+mark_0705:
+*COPY_OVER:*
+ POP BC ;
+ LD A,C ;
+ DEC A ;
+ OR B ;
+ RET Z ;
+
+ PUSH BC ;
+ INC BC ;
+ INC BC ;
+ INC BC ;
+ INC BC ;
+ DEC HL ;
+ CALL MAKE_ROOM <#MAKE_ROOM>
+ CALL SLOW_FAST <#SLOW_FAST>
+ POP BC ;
+ PUSH BC ;
+ INC DE ;
+ LD HL,(STKBOT) ;
+ DEC HL ;
+ LDDR ; copy bytes
+ LD HL,(E_PPC) ;
+ EX DE,HL ;
+ POP BC ;
+ LD (HL),B ;
+ DEC HL ;
+ LD (HL),C ;
+ DEC HL ;
+ LD (HL),E ;
+ DEC HL ;
+ LD (HL),D ;
+
+ RET ; return.
+
+------------------------------------------------------------------------
+
+; THE *'LIST'* AND 'LLIST' COMMAND ROUTINES
+------------------------------------------------------------------------
+
+
+mark_072C:
+*LLIST:*
+ SET 1,(IY+FLAGS-RAMBASE) ; signal printer in use
+
+mark_0730:
+*LIST:*
+ CALL FIND_INT <#FIND_INT>
+
+ LD A,B ; fetch high byte of user-supplied line number.
+ AND $3F ; and crudely limit to range 1-16383.
+
+ LD H,A ;
+ LD L,C ;
+ LD (E_PPC),HL ;
+ CALL LINE_ADDR <#LINE_ADDR>
+
+mark_073E:
+*LIST_PROG:*
+ LD E,$00 ;
+
+mark_0740:
+*UNTIL_END:*
+ CALL OUT_LINE <#OUT_LINE> ; lists one line of BASIC
+ ; making an early return when the screen is
+ ; full or the end of program is reached.
+ JR UNTIL_END <#UNTIL_END> ; loop back to UNTIL_END
+
+------------------------------------------------------------------------
+
+; THE *'PRINT A BASIC LINE'* SUBROUTINE
+------------------------------------------------------------------------
+
+
+mark_0745:
+*OUT_LINE:*
+ LD BC,(E_PPC) ; sv
+ CALL CP_LINES <#CP_LINES>
+ LD D,$92 ;
+ JR Z,TEST_END <#TEST_END>
+
+ LD DE,$0000 ;
+ RL E ;
+
+mark_0755:
+*TEST_END:*
+ LD (IY+BERG-RAMBASE),E
+ LD A,(HL) ;
+ CP $40 ;
+ POP BC ;
+ RET NC ;
+
+ PUSH BC ;
+ CALL OUT_NO <#OUT_NO>
+ INC HL ;
+ LD A,D ;
+
+ RST _PRINT_A
+ INC HL ;
+ INC HL ;
+
+mark_0766:
+*COPY_LINE:*
+ LD (CH_ADD),HL ;
+ SET 0,(IY+FLAGS-RAMBASE) ; Suppress leading space
+
+mark_076D:
+*MORE_LINE:*
+ LD BC,(X_PTR) ;
+ LD HL,(CH_ADD) ;
+ AND A ;
+ SBC HL,BC ;
+ JR NZ,TEST_NUM <#TEST_NUM>
+
+ LD A,ZX_INV_S ; $B8 ;
+
+ RST _PRINT_A
+
+mark_077C:
+*TEST_NUM:*
+ LD HL,(CH_ADD) ;
+ LD A,(HL) ;
+ INC HL ;
+ CALL NUMBER <#NUMBER>
+ LD (CH_ADD),HL ;
+ JR Z,MORE_LINE <#MORE_LINE>
+
+ CP ZX_CURSOR ;
+ JR Z,OUT_CURS <#OUT_CURS>
+
+ CP ZX_NEWLINE ;
+ JR Z,OUT_CH <#OUT_CH>
+
+ BIT 6,A ;
+ JR Z,NOT_TOKEN <#NOT_TOKEN>
+
+ CALL TOKENS <#TOKENS>
+ JR MORE_LINE <#MORE_LINE>
+; ___
+
+mark_079A:
+*NOT_TOKEN:*
+ RST _PRINT_A
+ JR MORE_LINE <#MORE_LINE>
+; ___
+
+mark_079D:
+*OUT_CURS:*
+ LD A,(MODE) ; Fetch value of system variable MODE
+ LD B,$AB ; Prepare an inverse [F] for function cursor.
+
+ AND A ; Test for zero -
+ JR NZ,FLAGS_2 <#FLAGS_2> ; forward if not to FLAGS_2
+
+ LD A,(FLAGS) ; Fetch system variable FLAGS.
+ LD B,ZX_INV_K ; Prepare an inverse [K] for keyword cursor.
+
+mark_07AA:
+*FLAGS_2:*
+ RRA ; 00000?00 -> 000000?0
+ RRA ; 000000?0 -> 0000000?
+ AND $01 ; 0000000? 0000000x
+
+ ADD A,B ; Possibly [F] -> [G] or [K] -> [L]
+
+ CALL PRINT_SP <#PRINT_SP>
+ JR MORE_LINE <#MORE_LINE>
+
+------------------------------------------------------------------------
+
+; THE *'NUMBER'* SUBROUTINE
+------------------------------------------------------------------------
+
+
+mark_07B4:
+*NUMBER:*
+ CP $7E ;
+ RET NZ ;
+
+ INC HL ;
+ INC HL ;
+ INC HL ;
+ INC HL ;
+ INC HL ;
+ RET ;
+
+------------------------------------------------------------------------
+
+; THE *'KEYBOARD DECODE'* SUBROUTINE
+------------------------------------------------------------------------
+
+
+mark_07BD:
+*DECODE:*
+ LD D,0 ;
+ SRA B ; shift bit from B to Carry
+ SBC A,A ; A = 0 - Carry
+ OR $26 ; %00100110
+ LD L,5 ;
+ SUB L ;
+
+mark_07C7:
+*KEY_LINE:*
+ ADD A,L ;
+ SCF ; Set Carry Flag
+ RR C ;
+ JR C,KEY_LINE <#KEY_LINE>
+
+ INC C ;
+ RET NZ ;
+
+ LD C,B ;
+ DEC L ;
+ LD L,1 ;
+ JR NZ,KEY_LINE <#KEY_LINE>
+
+ LD HL,$007D ; (expr reqd)
+ LD E,A ;
+ ADD HL,DE ;
+ SCF ; Set Carry Flag
+ RET ;
+
+------------------------------------------------------------------------
+
+; THE *'PRINTING'* SUBROUTINE
+------------------------------------------------------------------------
+
+
+mark_07DC:
+*LEAD_SP:*
+ LD A,E ;
+ AND A ;
+ RET M ;
+
+ JR PRINT_CH <#PRINT_CH>
+
+; ___
+; HL is typically -10000, -1000, -100, -10
+; and repeatedly subtracted from BC
+; i.e. it print
+;
+;
+mark_07E1:
+*OUT_DIGIT:*
+ XOR A ; assume the digit is zero to begin with
+
+mark_07E2:
+*DIGIT_INC:*
+ ADD HL,BC ; HL += -ve number
+ INC A ;
+ JR C,DIGIT_INC <#DIGIT_INC> ; loop
+
+ SBC HL,BC ; undo last iteration
+ DEC A ; undo last iteration
+ JR Z,LEAD_SP <#LEAD_SP> ; leading zeros shown as spaces
+
+mark_07EB:
+*OUT_CODE:*
+ LD E,ZX_0 ; $1C
+ ADD A,E ;
+
+mark_07EE:
+*OUT_CH:*
+ AND A ;
+ JR Z,PRINT_SP <#PRINT_SP>
+
+mark_07F1:
+*PRINT_CH:*
+ RES 0,(IY+FLAGS-RAMBASE) ; signal leading space permitted
+
+mark_07F5:
+*PRINT_SP:*
+ EXX ;
+ PUSH HL ;
+ BIT 1,(IY+FLAGS-RAMBASE) ; is printer in use ?
+ JR NZ,LPRINT_A <#LPRINT_A>
+
+ CALL ENTER_CH <#ENTER_CH>
+ JR PRINT_EXX <#PRINT_EXX>
+
+; ___
+
+mark_0802:
+*LPRINT_A:*
+ CALL LPRINT_CH <#LPRINT_CH>
+
+mark_0805:
+*PRINT_EXX:*
+ POP HL ;
+ EXX ;
+ RET ;
+
+; ___
+
+mark_0808:
+*ENTER_CH:*
+ LD D,A ;
+ LD BC,(S_POSN) ;
+ LD A,C ;
+ CP CHARS_HORIZONTAL+1 ;
+ JR Z,TEST_LOW <#TEST_LOW>
+
+mark_0812:
+*TEST_N_L:*
+ LD A,ZX_NEWLINE ;
+ CP D ;
+ JR Z,WRITE_N_L <#WRITE_N_L>
+
+ LD HL,(DF_CC) ;
+ CP (HL) ;
+ LD A,D ;
+ JR NZ,WRITE_CH <#WRITE_CH>
+
+ DEC C ;
+ JR NZ,EXPAND_1 <#EXPAND_1>
+
+ INC HL ;
+ LD (DF_CC),HL ;
+ LD C,CHARS_HORIZONTAL+1 ; $21 = 33 normally
+ DEC B ;
+ LD (S_POSN),BC ;
+
+mark_082C:
+*TEST_LOW:*
+ LD A,B ;
+ CP (IY+DF_SZ-RAMBASE)
+ JR Z,REPORT_5 <#REPORT_5>
+
+ AND A ;
+ JR NZ,TEST_N_L <#TEST_N_L>
+
+mark_0835:
+*REPORT_5:*
+ LD L,4 ; 'No more room on screen'
+ JP ERROR_3 <#ERROR_3>
+
+; ___
+
+mark_083A:
+*EXPAND_1:*
+ CALL ONE_SPACE <#ONE_SPACE>
+ EX DE,HL ;
+
+mark_083E:
+*WRITE_CH:*
+ LD (HL),A ;
+ INC HL ;
+ LD (DF_CC),HL ;
+ DEC (IY+S_POSN_x-RAMBASE)
+ RET ;
+
+; ___
+
+mark_0847:
+*WRITE_N_L:*
+ LD C,CHARS_HORIZONTAL+1 ; $21 = 33
+ DEC B ;
+ SET 0,(IY+FLAGS-RAMBASE) ; Suppress leading space
+ JP LOC_ADDR <#LOC_ADDR>
+
+------------------------------------------------------------------------
+
+; THE *'LPRINT_CH'* SUBROUTINE
+------------------------------------------------------------------------
+
+; This routine sends a character to the ZX-Printer placing the code for the
+; character in the Printer Buffer.
+; Note. PR_CC contains the low byte of the buffer address. The high order byte
+; is always constant.
+
+
+mark_0851:
+*LPRINT_CH:*
+ CP ZX_NEWLINE ; compare to NEWLINE.
+ JR Z,COPY_BUFF <#COPY_BUFF> ; forward if so
+
+ LD C,A ; take a copy of the character in C.
+ LD A,(PR_CC) ; fetch print location from PR_CC
+ AND $7F ; ignore bit 7 to form true position.
+ CP $5C ; compare to 33rd location
+
+ LD L,A ; form low-order byte.
+ LD H,$40 ; the high-order byte is fixed.
+
+ CALL Z,COPY_BUFF <#COPY_BUFF> ; to send full buffer to
+ ; the printer if first 32 bytes full.
+ ; (this will reset HL to start.)
+
+ LD (HL),C ; place character at location.
+ INC L ; increment - will not cross a 256 boundary.
+ LD (IY+PR_CC-RAMBASE),L ; update system variable PR_CC
+ ; automatically resetting bit 7 to show that
+ ; the buffer is not empty.
+ RET ; return.
+
+------------------------------------------------------------------------
+
+; THE *'COPY'* COMMAND ROUTINE
+------------------------------------------------------------------------
+
+; The full character-mapped screen is copied to the ZX-Printer.
+; All twenty-four text/graphic lines are printed.
+
+mark_0869:
+*COPY:*
+;
+; check - is this $16==22 or 24?
+;
+;; LD D,$16 ; prepare to copy twenty four text lines.
+ LD D,22 ; prepare to copy twenty four text lines.
+ LD HL,(D_FILE) ; set HL to start of display file from D_FILE.
+ INC HL ;
+ JR COPY_D <#COPY_D> ; forward
+
+; ___
+
+; A single character-mapped printer buffer is copied to the ZX-Printer.
+
+mark_0871:
+*COPY_BUFF:*
+ LD D,1 ; prepare to copy a single text line.
+ LD HL,PRBUFF ; set HL to start of printer buffer PRBUFF.
+
+; both paths converge here.
+
+mark_0876:
+*COPY_D:*
+ CALL SET_FAST <#SET_FAST>
+
+ PUSH BC ; *** preserve BC throughout.
+ ; a pending character may be present
+ ; in C from LPRINT_CH
+
+mark_087A:
+*COPY_LOOP:*
+ PUSH HL ; save first character of line pointer. (*)
+ XOR A ; clear accumulator.
+ LD E,A ; set pixel line count, range 0-7, to zero.
+
+; this inner loop deals with each horizontal pixel line.
+
+mark_087D:
+*COPY_TIME:*
+ OUT (IO_PORT_PRINTER),A ; bit 2 reset starts the printer motor
+ ; with an inactive stylus - bit 7 reset.
+ POP HL ; pick up first character of line pointer (*)
+ ; on inner loop.
+
+mark_0880:
+*COPY_BRK:*
+ CALL BREAK_1 <#BREAK_1>
+ JR C,COPY_CONT <#COPY_CONT> ; forward with no keypress to COPY_CONT
+
+; else A will hold 11111111 0
+
+ RRA ; 0111 1111
+ OUT (IO_PORT_PRINTER),A ; stop ZX printer motor, de-activate stylus.
+
+mark_0888:
+*REPORT_D2:*
+ RST _ERROR_1
+ DEFB $0C ; Error Report: BREAK - CONT repeats
+
+; ___
+
+mark_088A:
+*COPY_CONT:*
+ IN A,(IO_PORT_PRINTER) ; read from printer port.
+ ADD A,A ; test bit 6 and 7
+ JP M,COPY_END <#COPY_END> ; jump forward with no printer to COPY_END
+
+ JR NC,COPY_BRK <#COPY_BRK> ; back if stylus not in position to COPY_BRK
+
+ PUSH HL ; save first character of line pointer (*)
+ PUSH DE ; ** preserve character line and pixel line.
+
+ LD A,D ; text line count to A?
+ CP 2 ; sets carry if last line.
+ SBC A,A ; now $FF if last line else zero.
+
+; now cleverly prepare a printer control mask setting bit 2 (later moved to 1)
+; of D to slow printer for the last two pixel lines ( E = 6 and 7)
+
+ AND E ; and with pixel line offset 0-7
+ RLCA ; shift to left.
+ AND E ; and again.
+ LD D,A ; store control mask in D.
+
+mark_089C:
+*COPY_NEXT:*
+ LD C,(HL) ; load character from screen or buffer.
+ LD A,C ; save a copy in C for later inverse test.
+ INC HL ; update pointer for next time.
+ CP ZX_NEWLINE ; is character a NEWLINE ?
+ JR Z,COPY_N_L <#COPY_N_L> ; forward, if so, to COPY_N_L
+
+ PUSH HL ; * else preserve the character pointer.
+
+ SLA A ; (?) multiply by two
+ ADD A,A ; multiply by four
+ ADD A,A ; multiply by eight
+
+ LD H,$0F ; load H with half the address of character set.
+ RL H ; now $1E or $1F (with carry)
+ ADD A,E ; add byte offset 0-7
+ LD L,A ; now HL addresses character source byte
+
+ RL C ; test character, setting carry if inverse.
+ SBC A,A ; accumulator now $00 if normal, $FF if inverse.
+
+ XOR (HL) ; combine with bit pattern at end or ROM.
+ LD C,A ; transfer the byte to C.
+ LD B,8 ; count eight bits to output.
+
+mark_08B5:
+*COPY_BITS:*
+ LD A,D ; fetch speed control mask from D.
+ RLC C ; rotate a bit from output byte to carry.
+ RRA ; pick up in bit 7, speed bit to bit 1
+ LD H,A ; store aligned mask in H register.
+
+mark_08BA:
+*COPY_WAIT:*
+ IN A,(IO_PORT_PRINTER) ; read the printer port
+ RRA ; test for alignment signal from encoder.
+ JR NC,COPY_WAIT <#COPY_WAIT> ; loop if not present to COPY_WAIT
+
+ LD A,H ; control byte to A.
+ OUT (IO_PORT_PRINTER),A ; and output to printer port.
+ DJNZ COPY_BITS <#COPY_BITS> ; loop for all eight bits to COPY_BITS
+
+ POP HL ; * restore character pointer.
+ JR COPY_NEXT <#COPY_NEXT> ; back for adjacent character line to COPY_NEXT
+
+; ___
+
+; A NEWLINE has been encountered either following a text line or as the
+; first character of the screen or printer line.
+
+mark_08C7:
+*COPY_N_L:*
+ IN A,(IO_PORT_PRINTER) ; read printer port.
+ RRA ; wait for encoder signal.
+ JR NC,COPY_N_L <#COPY_N_L> ; loop back if not to COPY_N_L
+
+ LD A,D ; transfer speed mask to A.
+ RRCA ; rotate speed bit to bit 1.
+ ; bit 7, stylus control is reset.
+ OUT (IO_PORT_PRINTER),A ; set the printer speed.
+
+ POP DE ; ** restore character line and pixel line.
+ INC E ; increment pixel line 0-7.
+ BIT 3,E ; test if value eight reached.
+ JR Z,COPY_TIME <#COPY_TIME> ; back if not
+
+; eight pixel lines, a text line have been completed.
+
+ POP BC ; lose the now redundant first character
+ ; pointer
+ DEC D ; decrease text line count.
+ JR NZ,COPY_LOOP <#COPY_LOOP> ; back if not zero
+
+ LD A,$04 ; stop the already slowed printer motor.
+ OUT (IO_PORT_PRINTER),A ; output to printer port.
+
+mark_08DE:
+*COPY_END:*
+ CALL SLOW_FAST <#SLOW_FAST>
+ POP BC ; *** restore preserved BC.
+
+------------------------------------------------------------------------
+
+; THE *'CLEAR PRINTER BUFFER'* SUBROUTINE
+------------------------------------------------------------------------
+
+; This subroutine sets 32 bytes of the printer buffer to zero (space) and
+; the 33rd character is set to a NEWLINE.
+; This occurs after the printer buffer is sent to the printer but in addition
+; after the 24 lines of the screen are sent to the printer.
+; Note. This is a logic error as the last operation does not involve the
+; buffer at all. Logically one should be able to use
+; 10 LPRINT "HELLO ";
+; 20 COPY
+; 30 LPRINT ; "WORLD"
+; and expect to see the entire greeting emerge from the printer.
+; Surprisingly this logic error was never discovered and although one can argue
+; if the above is a bug, the repetition of this error on the Spectrum was most
+; definitely a bug.
+; Since the printer buffer is fixed at the end of the system variables, and
+; the print position is in the range $3C - $5C, then bit 7 of the system
+; variable is set to show the buffer is empty and automatically reset when
+; the variable is updated with any print position - neat.
+
+mark_08E2:
+*CLEAR_PRB:*
+ LD HL,PRBUFF_END ; address fixed end of PRBUFF
+ LD (HL),ZX_NEWLINE ; place a newline at last position.
+ LD B,32 ; prepare to blank 32 preceding characters.
+;
+; NB the printer is fixed at 32 characters, maybe it can be tweaked ???
+;
+mark_08E9:
+*PRB_BYTES:*
+ DEC HL ; decrement address - could be DEC L.
+ LD (HL),0 ; place a zero byte.
+ DJNZ PRB_BYTES <#PRB_BYTES> ; loop for all thirty-two
+
+ LD A,L ; fetch character print position.
+ SET 7,A ; signal the printer buffer is clear.
+ LD (PR_CC),A ; update one-byte system variable PR_CC
+ RET ; return.
+
+------------------------------------------------------------------------
+
+; THE *'PRINT AT'* SUBROUTINE
+------------------------------------------------------------------------
+
+;
+;
+;
+mark_08F5:
+*PRINT_AT:*
+
+ LD A,CHARS_VERTICAL-1 ; originally 23
+ SUB B ;
+ JR C,WRONG_VAL <#WRONG_VAL>
+
+mark_08FA:
+*TEST_VAL:*
+ CP (IY+DF_SZ-RAMBASE)
+ JP C,REPORT_5 <#REPORT_5>
+
+ INC A ;
+ LD B,A ;
+ LD A,CHARS_HORIZONTAL-1 ; originally 31
+
+ SUB C ;
+
+mark_0905:
+*WRONG_VAL:*
+ JP C,REPORT_B <#REPORT_B>
+
+ ADD A,2 ;
+ LD C,A ;
+
+mark_090B:
+*SET_FIELD:*
+ BIT 1,(IY+FLAGS-RAMBASE) ; Is printer in use?
+ JR Z,LOC_ADDR <#LOC_ADDR>
+
+ LD A,$5D ;
+ SUB C ;
+ LD (PR_CC),A ;
+ RET ;
+
+------------------------------------------------------------------------
+
+; THE *'LOCATE ADDRESS'* ROUTINE
+------------------------------------------------------------------------
+
+;
+; I'm guessing this locates the address of a character at X,Y
+; on the screen, with 0,0 being on the bottom left?
+; S_POSN_x equ $4039
+; S_POSN_y equ $403A
+; so when BC is stored there, B is Y and C is X
+;
+mark_0918:
+*LOC_ADDR:*
+ LD (S_POSN),BC ;
+ LD HL,(VARS) ;
+ LD D,C ;
+ LD A,CHARS_HORIZONTAL+2 ; $22 == 34 originally.
+ SUB C ;
+ LD C,A ;
+ LD A,ZX_NEWLINE ;
+ INC B ;
+
+mark_0927:
+*LOOK_BACK:*
+ DEC HL ;
+ CP (HL) ;
+ JR NZ,LOOK_BACK <#LOOK_BACK>
+
+ DJNZ LOOK_BACK <#LOOK_BACK>
+
+ INC HL ;
+ CPIR ;
+ DEC HL ;
+ LD (DF_CC),HL ;
+ SCF ; Set Carry Flag
+ RET PO ;
+
+ DEC D ;
+ RET Z ;
+
+ PUSH BC ;
+ CALL MAKE_ROOM <#MAKE_ROOM>
+ POP BC ;
+ LD B,C ;
+ LD H,D ; HL := DE
+ LD L,E ;
+
+mark_0940:
+*EXPAND_2:*
+;
+; Writes B spaces to HL--
+;
+ LD (HL),ZX_SPACE ;
+ DEC HL ;
+ DJNZ EXPAND_2 <#EXPAND_2>
+
+ EX DE,HL ; restore HL
+ INC HL ;
+ LD (DF_CC),HL ;
+ RET ;
+
+------------------------------------------------------------------------
+
+; THE *'EXPAND TOKENS'* SUBROUTINE
+------------------------------------------------------------------------
+
+
+mark_094B:
+*TOKENS:*
+ PUSH AF ;
+ CALL TOKEN_ADD <#TOKEN_ADD>
+ JR NC,ALL_CHARS <#ALL_CHARS>
+
+ BIT 0,(IY+FLAGS-RAMBASE) ; Leading space if set
+ JR NZ,ALL_CHARS <#ALL_CHARS>
+
+ XOR A ; A = 0 = ZX_SPACE
+
+ RST _PRINT_A
+
+mark_0959:
+*ALL_CHARS:*
+ LD A,(BC) ;
+ AND $3F ; truncate to printable values ???
+
+ RST _PRINT_A
+ LD A,(BC) ;
+ INC BC ;
+ ADD A,A ;
+ JR NC,ALL_CHARS <#ALL_CHARS>
+
+ POP BC ;
+ BIT 7,B ;
+ RET Z ;
+
+ CP ZX_COMMA ; $1A == 26
+ JR Z,TRAIL_SP <#TRAIL_SP>
+
+ CP ZX_S ; $38 == 56
+ RET C ;
+
+mark_096D:
+*TRAIL_SP:*
+ XOR A ;
+ SET 0,(IY+FLAGS-RAMBASE) ; Suppress leading space
+ JP PRINT_SP <#PRINT_SP>
+
+; ___
+
+mark_0975:
+*TOKEN_ADD:*
+ PUSH HL ;
+ LD HL,TOKEN_TABLE <#TOKEN_TABLE>
+ BIT 7,A ;
+ JR Z,TEST_HIGH <#TEST_HIGH>
+
+ AND $3F ;
+
+mark_097F:
+*TEST_HIGH:*
+ CP $43 ;
+ JR NC,FOUND <#FOUND>
+
+ LD B,A ;
+ INC B ;
+
+mark_0985:
+*WORDS:*
+ BIT 7,(HL) ;
+ INC HL ;
+ JR Z,WORDS <#WORDS>
+
+ DJNZ WORDS <#WORDS>
+
+ BIT 6,A ;
+ JR NZ,COMP_FLAG <#COMP_FLAG>
+
+ CP $18 ;
+
+mark_0992:
+*COMP_FLAG:*
+ CCF ; Complement Carry Flag
+
+mark_0993:
+*FOUND:*
+ LD B,H ;
+ LD C,L ;
+ POP HL ;
+ RET NC ;
+
+ LD A,(BC) ;
+ ADD A,$E4 ;
+ RET ;
+
+------------------------------------------------------------------------
+
+; THE *'ONE_SPACE'* SUBROUTINE
+------------------------------------------------------------------------
+
+
+mark_099B:
+*ONE_SPACE:*
+ LD BC,$0001 ;
+
+------------------------------------------------------------------------
+
+; THE *'MAKE ROOM'* SUBROUTINE
+------------------------------------------------------------------------
+
+;
+;
+
+mark_099E:
+*MAKE_ROOM:*
+ PUSH HL ;
+ CALL TEST_ROOM <#TEST_ROOM>
+ POP HL ;
+ CALL POINTERS <#POINTERS>
+ LD HL,(STKEND) ;
+ EX DE,HL ;
+ LDDR ; Copy Bytes
+ RET ;
+
+------------------------------------------------------------------------
+
+; THE *'POINTERS'* SUBROUTINE
+------------------------------------------------------------------------
+
+
+mark_09AD:
+*POINTERS:*
+ PUSH AF ;
+ PUSH HL ;
+ LD HL,D_FILE ;
+ LD A,$09 ;
+
+mark_09B4:
+*NEXT_PTR:*
+ LD E,(HL) ;
+ INC HL ;
+ LD D,(HL) ;
+ EX (SP),HL ;
+ AND A ;
+ SBC HL,DE ;
+ ADD HL,DE ;
+ EX (SP),HL ;
+ JR NC,PTR_DONE <#PTR_DONE>
+
+ PUSH DE ;
+ EX DE,HL ;
+ ADD HL,BC ;
+ EX DE,HL ;
+ LD (HL),D ;
+ DEC HL ;
+ LD (HL),E ;
+ INC HL ;
+ POP DE ;
+
+mark_09C8:
+*PTR_DONE:*
+ INC HL ;
+ DEC A ;
+ JR NZ,NEXT_PTR <#NEXT_PTR>
+
+ EX DE,HL ;
+ POP DE ;
+ POP AF ;
+ AND A ;
+ SBC HL,DE ;
+ LD B,H ;
+ LD C,L ;
+ INC BC ;
+ ADD HL,DE ;
+ EX DE,HL ;
+ RET ;
+
+------------------------------------------------------------------------
+
+; THE *'LINE ADDRESS'* SUBROUTINE
+------------------------------------------------------------------------
+
+
+mark_09D8:
+*LINE_ADDR:*
+ PUSH HL ;
+ LD HL,USER_RAM ;
+ LD D,H ;
+ LD E,L ;
+
+mark_09DE:
+*NEXT_TEST:*
+ POP BC ;
+ CALL CP_LINES <#CP_LINES>
+ RET NC ;
+
+ PUSH BC ;
+ CALL NEXT_ONE <#NEXT_ONE>
+ EX DE,HL ;
+ JR NEXT_TEST <#NEXT_TEST>
+
+------------------------------------------------------------------------
+
+; THE *'COMPARE LINE NUMBERS'* SUBROUTINE
+------------------------------------------------------------------------
+
+
+mark_09EA:
+*CP_LINES:*
+ LD A,(HL) ;
+ CP B ;
+ RET NZ ;
+
+ INC HL ;
+ LD A,(HL) ;
+ DEC HL ;
+ CP C ;
+ RET ;
+
+------------------------------------------------------------------------
+
+; THE *'NEXT LINE OR VARIABLE'* SUBROUTINE
+------------------------------------------------------------------------
+
+
+mark_09F2:
+*NEXT_ONE:*
+ PUSH HL ;
+ LD A,(HL) ;
+ CP $40 ;
+ JR C,LINES <#LINES>
+
+ BIT 5,A ;
+ JR Z,NEXT_0_4 <#NEXT_0_4> ; skip forward
+
+ ADD A,A ;
+ JP M,NEXT_PLUS_FIVE <#NEXT_PLUS_FIVE>
+
+ CCF ; Complement Carry Flag
+
+mark_0A01:
+*NEXT_PLUS_FIVE:*
+ LD BC,$0005 ;
+ JR NC,NEXT_LETT <#NEXT_LETT>
+
+ LD C,$11 ; 17
+
+mark_0A08:
+*NEXT_LETT:*
+ RLA ;
+ INC HL ;
+ LD A,(HL) ;
+ JR NC,NEXT_LETT <#NEXT_LETT> ; loop
+
+ JR NEXT_ADD <#NEXT_ADD>
+; ___
+
+mark_0A0F:
+*LINES:*
+ INC HL ;
+
+mark_0A10:
+*NEXT_0_4:*
+ INC HL ; BC = word at HL++
+ LD C,(HL) ;
+ INC HL ;
+ LD B,(HL) ;
+ INC HL ;
+
+mark_0A15:
+*NEXT_ADD:*
+ ADD HL,BC ;
+ POP DE ;
+
+------------------------------------------------------------------------
+
+; THE *'DIFFERENCE'* SUBROUTINE
+------------------------------------------------------------------------
+
+
+mark_0A17:
+*DIFFER:*
+ AND A ;
+ SBC HL,DE ;
+ LD B,H ; BC := (HL-DE)
+ LD C,L ;
+ ADD HL,DE ;
+ EX DE,HL ; DE := old HL ???
+ RET ;
+
+------------------------------------------------------------------------
+
+; THE *'LINE_ENDS'* SUBROUTINE
+------------------------------------------------------------------------
+
+
+mark_0A1F:
+*LINE_ENDS:*
+ LD B,(IY+DF_SZ-RAMBASE)
+ PUSH BC ;
+ CALL B_LINES <#B_LINES>
+ POP BC ;
+ DEC B ;
+ JR B_LINES <#B_LINES>
+
+------------------------------------------------------------------------
+
+; THE *'CLS'* COMMAND ROUTINE
+------------------------------------------------------------------------
+
+
+mark_0A2A:
+*CLS:*
+ LD B,CHARS_VERTICAL ; number of lines to clear. $18 = 24 originally.
+
+mark_0A2C:
+*B_LINES:*
+ RES 1,(IY+FLAGS-RAMBASE) ; Signal printer not in use
+ LD C,CHARS_HORIZONTAL+1 ; $21 ; extra 1 is for HALT opcode ?
+ PUSH BC ;
+ CALL LOC_ADDR <#LOC_ADDR>
+ POP BC ;
+ LD A,(RAMTOP+1) ; is RAMTOP_hi
+ CP $4D ;
+ JR C,COLLAPSED <#COLLAPSED>
+;
+; If RAMTOP less then 4D00, RAM less than D00 = 3.25 K,
+; uses collapsed display.
+;
+
+ SET 7,(IY+S_POSN_y-RAMBASE)
+
+mark_0A42:
+*CLEAR_LOC:*
+ XOR A ; prepare a space
+ CALL PRINT_SP <#PRINT_SP> ; prints a space
+ LD HL,(S_POSN) ;
+ LD A,L ;
+ OR H ;
+ AND $7E ;
+ JR NZ,CLEAR_LOC <#CLEAR_LOC>
+
+ JP LOC_ADDR <#LOC_ADDR>
+
+; ___
+
+mark_0A52:
+*COLLAPSED:*
+ LD D,H ; DE := HL
+ LD E,L ;
+ DEC HL ;
+ LD C,B ;
+ LD B,0 ; Will loop 256 times
+ LDIR ; Copy Bytes
+ LD HL,(VARS) ;
+
+------------------------------------------------------------------------
+
+; THE *'RECLAIMING'* SUBROUTINES
+------------------------------------------------------------------------
+
+
+mark_0A5D:
+*RECLAIM_1:*
+ CALL DIFFER <#DIFFER>
+
+mark_0A60:
+*RECLAIM_2:*
+ PUSH BC ;
+ LD A,B ;
+ CPL ;
+ LD B,A ;
+ LD A,C ;
+ CPL ;
+ LD C,A ;
+ INC BC ;
+ CALL POINTERS <#POINTERS>
+ EX DE,HL ;
+ POP HL ;
+ ADD HL,DE ;
+ PUSH DE ;
+ LDIR ; Copy Bytes
+ POP HL ;
+ RET ;
+
+------------------------------------------------------------------------
+
+; THE *'E_LINE NUMBER'* SUBROUTINE
+------------------------------------------------------------------------
+
+
+mark_0A73:
+*E_LINE_NUM:*
+ LD HL,(E_LINE) ;
+ CALL TEMP_PTR2 <#TEMP_PTR2>
+
+ RST _GET_CHAR
+ BIT 5,(IY+FLAGX-RAMBASE)
+ RET NZ ;
+
+ LD HL,MEM_0_1st ;
+ LD (STKEND),HL ;
+ CALL INT_TO_FP <#INT_TO_FP>
+ CALL FP_TO_BC <#FP_TO_BC>
+ JR C,NO_NUMBER <#NO_NUMBER> ; to NO_NUMBER
+
+ LD HL,-10000 ; $D8F0 ; value '-10000'
+ ADD HL,BC ;
+
+mark_0A91:
+*NO_NUMBER:*
+ JP C,REPORT_C <#REPORT_C> ; to REPORT_C
+
+ CP A ;
+ JP SET_MIN <#SET_MIN>
+
+------------------------------------------------------------------------
+
+; THE *'REPORT AND LINE NUMBER'* PRINTING SUBROUTINES
+------------------------------------------------------------------------
+
+
+mark_0A98:
+*OUT_NUM:*
+ PUSH DE ;
+ PUSH HL ;
+ XOR A ;
+ BIT 7,B ;
+ JR NZ,UNITS <#UNITS>
+
+ LD H,B ; HL := BC
+ LD L,C ;
+ LD E,$FF ;
+ JR THOUSAND <#THOUSAND>
+; ___
+
+mark_0AA5:
+*OUT_NO:*
+ PUSH DE ;
+ LD D,(HL) ;
+ INC HL ;
+ LD E,(HL) ;
+ PUSH HL ;
+ EX DE,HL ;
+ LD E,ZX_SPACE ; set E to leading space.
+
+mark_0AAD:
+*THOUSAND:*
+ LD BC,-1000 ; $FC18 ;
+ CALL OUT_DIGIT <#OUT_DIGIT>
+ LD BC,-100 ; $FF9C ;
+ CALL OUT_DIGIT <#OUT_DIGIT>
+ LD C,-10 ; $F6 ; B is already FF, so saves a byte.
+ CALL OUT_DIGIT <#OUT_DIGIT>
+ LD A,L ;
+
+mark_0ABF:
+*UNITS:*
+ CALL OUT_CODE <#OUT_CODE>
+ POP HL ;
+ POP DE ;
+ RET ;
+
+------------------------------------------------------------------------
+
+; THE *'UNSTACK_Z'* SUBROUTINE
+------------------------------------------------------------------------
+
+
+; This subroutine is used to return early from a routine when checking syntax.
+; On the ZX81 the same routines that execute commands also check the syntax
+; on line entry. This enables precise placement of the error marker in a line
+; that fails syntax.
+; The sequence CALL SYNTAX_Z ; RET Z can be replaced by a call to this routine
+; although it has not replaced every occurrence of the above two instructions.
+; Even on the ZX80 this routine was not fully utilized.
+
+mark_0AC5:
+*UNSTACK_Z:*
+ CALL SYNTAX_Z <#SYNTAX_Z> ; resets the ZERO flag if
+ ; checking syntax.
+ POP HL ; drop the return address.
+ RET Z ; return to previous calling routine if
+ ; checking syntax.
+
+ JP (HL) ; else jump to the continuation address in
+ ; the calling routine as RET would have done.
+
+------------------------------------------------------------------------
+
+; THE *'LPRINT'* COMMAND ROUTINE
+------------------------------------------------------------------------
+
+;
+;
+
+mark_0ACB:
+*LPRINT:*
+ SET 1,(IY+FLAGS-RAMBASE) ; Signal printer in use
+
+------------------------------------------------------------------------
+
+; THE *'PRINT'* COMMAND ROUTINE
+------------------------------------------------------------------------
+
+
+mark_0ACF:
+*PRINT:*
+ LD A,(HL) ;
+ CP ZX_NEWLINE ;
+ JP Z,PRINT_END <#PRINT_END> ; to PRINT_END
+
+mark_0AD5:
+*PRINT_1:*
+ SUB ZX_COMMA ; $1A == 26
+ ADC A,$00 ;
+ JR Z,SPACING <#SPACING> ; to SPACING
+ ;
+ ; Compare with AT,
+ ; less comma recently subtracted.
+ ;
+ CP ZX_AT-ZX_COMMA ; $A7 == 167
+ JR NZ,NOT_AT <#NOT_AT> ;
+
+
+ RST _NEXT_CHAR
+ CALL CLASS_6 <#CLASS_6>
+ CP ZX_COMMA ; $1A = 26
+ JP NZ,REPORT_C <#REPORT_C> ;
+
+ RST _NEXT_CHAR
+ CALL CLASS_6 <#CLASS_6>
+ CALL SYNTAX_ON <#SYNTAX_ON>
+
+ RST _FP_CALC ;;
+ DEFB __exchange ;;
+ DEFB __end_calc ;;
+
+ CALL STK_TO_BC <#STK_TO_BC>
+ CALL PRINT_AT <#PRINT_AT>
+ JR PRINT_ON <#PRINT_ON>
+; ___
+
+mark_0AFA:
+*NOT_AT:*
+ CP ZX_TAB-ZX_COMMA ; $A8 == 168
+ JR NZ,NOT_TAB <#NOT_TAB>
+
+
+ RST _NEXT_CHAR
+ CALL CLASS_6 <#CLASS_6>
+ CALL SYNTAX_ON <#SYNTAX_ON>
+ CALL STK_TO_A <#STK_TO_A>
+ JP NZ,REPORT_B <#REPORT_B>
+
+ AND $1F ; truncate to 0 to 31 characters ???
+ LD C,A ;
+ BIT 1,(IY+FLAGS-RAMBASE) ; Is printer in use
+ JR Z,TAB_TEST <#TAB_TEST>
+
+ SUB (IY+PR_CC-RAMBASE)
+ SET 7,A ;
+ ADD A,$3C ; 60
+ CALL NC,COPY_BUFF <#COPY_BUFF>
+
+mark_0B1E:
+*TAB_TEST:*
+ ADD A,(IY+S_POSN_x-RAMBASE) ; screen position X
+ CP CHARS_HORIZONTAL+1 ; 33 (characters horizontal plus newline ???)
+ LD A,(S_POSN_y) ; screen position Y
+ SBC A,1 ;
+ CALL TEST_VAL <#TEST_VAL>
+ SET 0,(IY+FLAGS-RAMBASE) ; sv FLAGS - Suppress leading space
+ JR PRINT_ON <#PRINT_ON>
+; ___
+
+mark_0B31:
+*NOT_TAB:*
+ CALL SCANNING <#SCANNING>
+ CALL PRINT_STK <#PRINT_STK>
+
+mark_0B37:
+*PRINT_ON:*
+ RST _GET_CHAR
+ SUB ZX_COMMA ; $1A
+ ADC A,0 ;
+ JR Z,SPACING <#SPACING>
+
+ CALL CHECK_END <#CHECK_END>
+ JP PRINT_END <#PRINT_END>
+; ___
+mark_0B44:
+*SPACING:*
+ CALL NC,FIELD <#FIELD>
+
+ RST _NEXT_CHAR
+ CP ZX_NEWLINE ;
+ RET Z ;
+
+ JP PRINT_1 <#PRINT_1>
+; ___
+mark_0B4E:
+*SYNTAX_ON:*
+ CALL SYNTAX_Z <#SYNTAX_Z>
+ RET NZ ;
+
+ POP HL ;
+ JR PRINT_ON <#PRINT_ON>
+; ___
+mark_0B55:
+*PRINT_STK:*
+ CALL UNSTACK_Z <#UNSTACK_Z>
+ BIT 6,(IY+FLAGS-RAMBASE) ; Numeric or string result?
+ CALL Z,STK_FETCH <#STK_FETCH>
+ JR Z,PR_STR_4 <#PR_STR_4>
+
+ JP PRINT_FP <#PRINT_FP> ; jump forward
+; ___
+
+mark_0B64:
+*PR_STR_1:*
+ LD A,ZX_QUOTE ; $0B
+
+mark_0B66:
+*PR_STR_2:*
+ RST _PRINT_A
+
+mark_0B67:
+*PR_STR_3:*
+ LD DE,(X_PTR) ;
+
+mark_0B6B:
+*PR_STR_4:*
+ LD A,B ;
+ OR C ;
+ DEC BC ;
+ RET Z ;
+
+ LD A,(DE) ;
+ INC DE ;
+ LD (X_PTR),DE ;
+ BIT 6,A ;
+ JR Z,PR_STR_2 <#PR_STR_2>
+
+ CP $C0 ;
+ JR Z,PR_STR_1 <#PR_STR_1>
+
+ PUSH BC ;
+ CALL TOKENS <#TOKENS>
+ POP BC ;
+ JR PR_STR_3 <#PR_STR_3>
+
+; ___
+
+mark_0B84:
+*PRINT_END:*
+ CALL UNSTACK_Z <#UNSTACK_Z>
+ LD A,ZX_NEWLINE ;
+
+ RST _PRINT_A
+ RET ;
+
+; ___
+
+mark_0B8B:
+*FIELD:*
+ CALL UNSTACK_Z <#UNSTACK_Z>
+ SET 0,(IY+FLAGS-RAMBASE) ; Suppress leading space
+ XOR A ;
+
+ RST _PRINT_A
+ LD BC,(S_POSN) ;
+ LD A,C ;
+ BIT 1,(IY+FLAGS-RAMBASE) ; Is printer in use
+ JR Z,CENTRE <#CENTRE>
+
+ LD A,$5D ;
+ SUB (IY+PR_CC-RAMBASE)
+
+mark_0BA4:
+*CENTRE:*
+ LD C,$11 ;
+ CP C ;
+ JR NC,RIGHT <#RIGHT>
+
+ LD C,$01 ;
+
+mark_0BAB:
+*RIGHT:*
+ CALL SET_FIELD <#SET_FIELD>
+ RET ;
+
+------------------------------------------------------------------------
+
+; THE *'PLOT AND UNPLOT'* COMMAND ROUTINES
+------------------------------------------------------------------------
+
+
+mark_0BAF:
+*PLOT_UNPLOT:*
+;
+; Of the 24 lines, only top 22 ar used for plotting.
+;
+ CALL STK_TO_BC <#STK_TO_BC>
+ LD (COORDS_x),BC ;
+;; LD A,$2B ; originally $2B == 32+11 = 43 = 2*22-1
+ LD A,2*(CHARS_VERTICAL-2)-1 ;
+ SUB B ;
+ JP C,REPORT_B <#REPORT_B>
+
+ LD B,A ;
+ LD A,$01 ;
+ SRA B ;
+ JR NC,COLUMNS <#COLUMNS>
+
+ LD A,$04 ;
+
+mark_0BC5:
+*COLUMNS:*
+ SRA C ;
+ JR NC,FIND_ADDR <#FIND_ADDR>
+
+ RLCA ;
+
+mark_0BCA:
+*FIND_ADDR:*
+ PUSH AF ;
+ CALL PRINT_AT <#PRINT_AT>
+ LD A,(HL) ;
+ RLCA ;
+ CP ZX_BRACKET_LEFT ; $10
+ JR NC,TABLE_PTR <#TABLE_PTR>
+
+ RRCA ;
+ JR NC,SQ_SAVED <#SQ_SAVED>
+
+ XOR $8F ;
+
+mark_0BD9:
+*SQ_SAVED:*
+ LD B,A ;
+
+mark_0BDA:
+*TABLE_PTR:*
+ LD DE,P_UNPLOT <#P_UNPLOT> ; Address: P_UNPLOT
+ LD A,(T_ADDR) ; get T_ADDR_lo
+ SUB E ;
+ JP M,PLOT <#PLOT>
+
+ POP AF ;
+ CPL ;
+ AND B ;
+ JR UNPLOT <#UNPLOT>
+
+; ___
+
+mark_0BE9:
+*PLOT:*
+ POP AF ;
+ OR B ;
+
+mark_0BEB:
+*UNPLOT:*
+ CP 8 ; Only apply to graphic characters (0 to 7)
+ JR C,PLOT_END <#PLOT_END>
+
+ XOR $8F ; binary 1000 1111
+
+mark_0BF1:
+*PLOT_END:*
+ EXX ;
+
+ RST _PRINT_A
+ EXX ;
+ RET ;
+
+------------------------------------------------------------------------
+
+; THE *'STACK_TO_BC'* SUBROUTINE
+------------------------------------------------------------------------
+
+mark_0BF5:
+*STK_TO_BC:*
+ CALL STK_TO_A <#STK_TO_A>
+ LD B,A ;
+ PUSH BC ;
+ CALL STK_TO_A <#STK_TO_A>
+ LD E,C ;
+ POP BC ;
+ LD D,C ;
+ LD C,A ;
+ RET ;
+
+------------------------------------------------------------------------
+
+; THE *'STACK_TO_A'* SUBROUTINE
+------------------------------------------------------------------------
+
+
+mark_0C02:
+*STK_TO_A:*
+ CALL FP_TO_A <#FP_TO_A>
+ JP C,REPORT_B <#REPORT_B>
+
+ LD C,$01 ;
+ RET Z ;
+
+ LD C,$FF ;
+ RET ;
+
+------------------------------------------------------------------------
+
+; THE *'SCROLL'* SUBROUTINE
+------------------------------------------------------------------------
+
+
+mark_0C0E:
+*SCROLL:*
+ LD B,(IY+DF_SZ-RAMBASE)
+ LD C,CHARS_HORIZONTAL+1 ;
+ CALL LOC_ADDR <#LOC_ADDR>
+ CALL ONE_SPACE <#ONE_SPACE>
+ LD A,(HL) ;
+ LD (DE),A ;
+ INC (IY+S_POSN_y-RAMBASE)
+ LD HL,(D_FILE) ;
+ INC HL ;
+ LD D,H ;
+ LD E,L ;
+ CPIR ;
+ JP RECLAIM_1 <#RECLAIM_1>
+
+------------------------------------------------------------------------
+
+; THE *'SYNTAX'* TABLES
+------------------------------------------------------------------------
+
+
+; i) The Offset table
+
+mark_0C29:
+*offset_t:*
+ DEFB P_LPRINT <#P_LPRINT> - $ ; 8B offset
+ DEFB P_LLIST <#P_LLIST> - $ ; 8D offset
+ DEFB P_STOP <#P_STOP> - $ ; 2D offset
+ DEFB P_SLOW <#P_SLOW> - $ ; 7F offset
+ DEFB P_FAST <#P_FAST> - $ ; 81 offset
+ DEFB P_NEW <#P_NEW> - $ ; 49 offset
+ DEFB P_SCROLL <#P_SCROLL> - $ ; 75 offset
+ DEFB P_CONT <#P_CONT> - $ ; 5F offset
+ DEFB P_DIM <#P_DIM> - $ ; 40 offset
+ DEFB P_REM <#P_REM> - $ ; 42 offset
+ DEFB P_FOR <#P_FOR> - $ ; 2B offset
+ DEFB P_GOTO <#P_GOTO> - $ ; 17 offset
+ DEFB P_GOSUB <#P_GOSUB> - $ ; 1F offset
+ DEFB P_INPUT <#P_INPUT> - $ ; 37 offset
+ DEFB P_LOAD <#P_LOAD> - $ ; 52 offset
+ DEFB P_LIST <#P_LIST> - $ ; 45 offset
+ DEFB P_LET <#P_LET> - $ ; 0F offset
+ DEFB P_PAUSE <#P_PAUSE> - $ ; 6D offset
+ DEFB P_NEXT <#P_NEXT> - $ ; 2B offset
+ DEFB P_POKE <#P_POKE> - $ ; 44 offset
+ DEFB P_PRINT <#P_PRINT> - $ ; 2D offset
+ DEFB P_PLOT <#P_PLOT> - $ ; 5A offset
+ DEFB P_RUN <#P_RUN> - $ ; 3B offset
+ DEFB P_SAVE <#P_SAVE> - $ ; 4C offset
+ DEFB P_RAND <#P_RAND> - $ ; 45 offset
+ DEFB P_IF <#P_IF> - $ ; 0D offset
+ DEFB P_CLS <#P_CLS> - $ ; 52 offset
+ DEFB P_UNPLOT <#P_UNPLOT> - $ ; 5A offset
+ DEFB P_CLEAR <#P_CLEAR> - $ ; 4D offset
+ DEFB P_RETURN <#P_RETURN> - $ ; 15 offset
+ DEFB P_COPY <#P_COPY> - $ ; 6A offset
+------------------------------------------------------------------------
+
+; ii) The parameter table.
+
+mark_0C48:
+*P_LET:*
+ DEFB _CLASS_01 ; A variable is required.
+ DEFB ZX_EQUAL ; Separator: '='
+ DEFB _CLASS_02 ; An expression, numeric or string,
+ ; must follow.
+
+mark_0C4B:
+*P_GOTO:*
+ DEFB _CLASS_06 ; A numeric expression must follow.
+ DEFB _CLASS_00 ; No further operands.
+ DEFW GOTO <#GOTO>
+
+mark_0C4F:
+*P_IF:*
+ DEFB _CLASS_06 ; A numeric expression must follow.
+ DEFB ZX_THEN ; Separator: 'THEN'
+ DEFB _CLASS_05 ; Variable syntax checked entirely
+ ; by routine.
+ DEFW IF <#IF>
+
+mark_0C54:
+*P_GOSUB:*
+ DEFB _CLASS_06 ; A numeric expression must follow.
+ DEFB _CLASS_00 ; No further operands.
+ DEFW GOSUB <#GOSUB>
+
+mark_0C58:
+*P_STOP:*
+ DEFB _CLASS_00 ; No further operands.
+ DEFW STOP <#STOP>
+
+mark_0C5B:
+*P_RETURN:*
+ DEFB _CLASS_00 ; No further operands.
+ DEFW RETURN <#RETURN>
+
+mark_0C5E:
+*P_FOR:*
+ DEFB _CLASS_04 ; A single character variable must
+ ; follow.
+ DEFB ZX_EQUAL ; Separator: '='
+ DEFB _CLASS_06 ; A numeric expression must follow.
+ DEFB ZX_TO ; Separator: 'TO'
+ DEFB _CLASS_06 ; A numeric expression must follow.
+ DEFB _CLASS_05 ; Variable syntax checked entirely
+ ; by routine.
+ DEFW FOR <#FOR>
+
+mark_0C66:
+*P_NEXT:*
+ DEFB _CLASS_04 ; A single character variable must
+ ; follow.
+ DEFB _CLASS_00 ; No further operands.
+ DEFW NEXT <#NEXT>
+
+mark_0C6A:
+*P_PRINT:*
+ DEFB _CLASS_05 ; Variable syntax checked entirely
+ ; by routine.
+ DEFW PRINT <#PRINT> ; not LPRINT ???
+
+mark_0C6D:
+*P_INPUT:*
+ DEFB _CLASS_01 ; A variable is required.
+ DEFB _CLASS_00 ; No further operands.
+ DEFW INPUT <#INPUT>
+
+mark_0C71:
+*P_DIM:*
+ DEFB _CLASS_05 ; Variable syntax checked entirely
+ ; by routine.
+ DEFW DIM <#DIM>
+
+mark_0C74:
+*P_REM:*
+ DEFB _CLASS_05 ; Variable syntax checked entirely
+ ; by routine.
+ DEFW REM <#REM>
+
+mark_0C77:
+*P_NEW:*
+ DEFB _CLASS_00 ; No further operands.
+ DEFW NEW <#NEW>
+
+mark_0C7A:
+*P_RUN:*
+ DEFB _CLASS_03 ; A numeric expression may follow
+ ; else default to zero.
+ DEFW RUN <#RUN>
+
+mark_0C7D:
+*P_LIST:*
+ DEFB _CLASS_03 ; A numeric expression may follow
+ ; else default to zero.
+ DEFW LIST <#LIST>
+
+mark_0C80:
+*P_POKE:*
+ DEFB _CLASS_06 ; A numeric expression must follow.
+ DEFB ZX_COMMA ; Separator: ','
+ DEFB _CLASS_06 ; A numeric expression must follow.
+ DEFB _CLASS_00 ; No further operands.
+ DEFW POKE <#POKE>
+
+mark_0C86:
+*P_RAND:*
+ DEFB _CLASS_03 ; A numeric expression may follow
+ ; else default to zero.
+ DEFW RAND <#RAND>
+
+mark_0C89:
+*P_LOAD:*
+ DEFB _CLASS_05 ; Variable syntax checked entirely
+ ; by routine.
+ DEFW LOAD <#LOAD>
+
+mark_0C8C:
+*P_SAVE:*
+ DEFB _CLASS_05 ; Variable syntax checked entirely
+ ; by routine.
+ DEFW SAVE <#SAVE>
+
+mark_0C8F:
+*P_CONT:*
+ DEFB _CLASS_00 ; No further operands.
+ DEFW CONT <#CONT>
+
+mark_0C92:
+*P_CLEAR:*
+ DEFB _CLASS_00 ; No further operands.
+ DEFW CLEAR <#CLEAR>
+
+mark_0C95:
+*P_CLS:*
+ DEFB _CLASS_00 ; No further operands.
+ DEFW CLS <#CLS>
+
+mark_0C98:
+*P_PLOT:*
+ DEFB _CLASS_06 ; A numeric expression must follow.
+ DEFB ZX_COMMA ; Separator: ','
+ DEFB _CLASS_06 ; A numeric expression must follow.
+ DEFB _CLASS_00 ; No further operands.
+ DEFW PLOT_UNPLOT <#PLOT_UNPLOT>
+
+mark_0C9E:
+*P_UNPLOT:*
+ DEFB _CLASS_06 ; A numeric expression must follow.
+ DEFB ZX_COMMA ; Separator: ','
+ DEFB _CLASS_06 ; A numeric expression must follow.
+ DEFB _CLASS_00 ; No further operands.
+ DEFW PLOT_UNPLOT <#PLOT_UNPLOT>
+
+mark_0CA4:
+*P_SCROLL:*
+ DEFB _CLASS_00 ; No further operands.
+ DEFW SCROLL <#SCROLL>
+
+mark_0CA7:
+*P_PAUSE:*
+ DEFB _CLASS_06 ; A numeric expression must follow.
+ DEFB _CLASS_00 ; No further operands.
+ DEFW PAUSE <#PAUSE>
+
+mark_0CAB:
+*P_SLOW:*
+ DEFB _CLASS_00 ; No further operands.
+ DEFW SLOW <#SLOW>
+
+mark_0CAE:
+*P_FAST:*
+ DEFB _CLASS_00 ; No further operands.
+ DEFW FAST <#FAST>
+
+mark_0CB1:
+*P_COPY:*
+ DEFB _CLASS_00 ; No further operands.
+ DEFW COPY <#COPY>
+
+mark_0CB4:
+*P_LPRINT:*
+ DEFB _CLASS_05 ; Variable syntax checked entirely
+ ; by routine.
+ DEFW LPRINT <#LPRINT>
+
+mark_0CB7:
+*P_LLIST:*
+ DEFB _CLASS_03 ; A numeric expression may follow
+ ; else default to zero.
+ DEFW LLIST <#LLIST>
+
+
+------------------------------------------------------------------------
+
+; THE *'LINE SCANNING'* ROUTINE
+------------------------------------------------------------------------
+
+
+mark_0CBA:
+*LINE_SCAN:*
+ LD (IY+FLAGS-RAMBASE),1
+ CALL E_LINE_NUM <#E_LINE_NUM>
+
+mark_0CC1:
+*LINE_RUN:*
+ CALL SET_MIN <#SET_MIN>
+ LD HL,ERR_NR ;
+ LD (HL),$FF ;
+ LD HL,FLAGX ;
+ BIT 5,(HL) ;
+ JR Z,LINE_NULL <#LINE_NULL>
+
+ CP $E3 ; 'STOP' ?
+ LD A,(HL) ;
+ JP NZ,INPUT_REP <#INPUT_REP>
+
+ CALL SYNTAX_Z <#SYNTAX_Z>
+ RET Z ;
+
+
+ RST _ERROR_1
+ DEFB $0C ; Error Report: BREAK - CONT repeats
+
+
+------------------------------------------------------------------------
+
+; THE *'STOP'* COMMAND ROUTINE
+------------------------------------------------------------------------
+
+;
+;
+
+mark_0CDC:
+*STOP:*
+ RST _ERROR_1
+ DEFB $08 ; Error Report: STOP statement
+; ___
+
+; the interpretation of a line continues with a check for just spaces
+; followed by a carriage return.
+; The IF command also branches here with a true value to execute the
+; statement after the THEN but the statement can be null so
+; 10 IF 1 = 1 THEN
+; passes syntax (on all ZX computers).
+
+mark_0CDE:
+*LINE_NULL:*
+ RST _GET_CHAR
+ LD B,$00 ; prepare to index - early.
+ CP ZX_NEWLINE ; compare to NEWLINE.
+ RET Z ; return if so.
+
+
+
+
+
+ LD C,A ; transfer character to C.
+
+ RST _NEXT_CHAR ; advances.
+ LD A,C ; character to A
+ SUB $E1 ; subtract 'LPRINT' - lowest command.
+ JR C,REPORT_C2 <#REPORT_C2> ; forward if less
+
+ LD C,A ; reduced token to C
+ LD HL,offset_t <#offset_t> ; set HL to address of offset table.
+ ADD HL,BC ; index into offset table.
+ LD C,(HL) ; fetch offset
+ ADD HL,BC ; index into parameter table.
+ JR GET_PARAM <#GET_PARAM>
+; ___
+
+mark_0CF4:
+*SCAN_LOOP:*
+ LD HL,(T_ADDR) ;
+
+; -> Entry Point to Scanning Loop
+
+mark_0CF7:
+*GET_PARAM:*
+ LD A,(HL) ;
+ INC HL ;
+ LD (T_ADDR),HL ;
+
+ LD BC,SCAN_LOOP <#SCAN_LOOP>
+ PUSH BC ; is pushed on machine stack.
+
+ LD C,A ;
+ CP ZX_QUOTE ; $0B
+ JR NC,SEPARATOR <#SEPARATOR>
+
+ LD HL,class_tbl <#class_tbl> ; class_tbl - the address of the class table.
+ LD B,$00 ;
+ ADD HL,BC ;
+ LD C,(HL) ;
+ ADD HL,BC ;
+ PUSH HL ;
+
+ RST _GET_CHAR
+ RET ; indirect jump to class routine and
+ ; by subsequent RET to SCAN_LOOP.
+
+------------------------------------------------------------------------
+
+; THE *'SEPARATOR'* ROUTINE
+------------------------------------------------------------------------
+
+
+mark_0D10:
+*SEPARATOR:*
+ RST _GET_CHAR
+ CP C ;
+ JR NZ,REPORT_C2 <#REPORT_C2>
+ ; 'Nonsense in BASIC'
+
+ RST _NEXT_CHAR
+ RET ; return
+
+
+------------------------------------------------------------------------
+
+; THE *'COMMAND CLASS'* TABLE
+------------------------------------------------------------------------
+
+;
+mark_0D16:
+*class_tbl:*
+ DEFB CLASS_0 <#CLASS_0> - $ ; 17 offset to; Address: CLASS_0
+ DEFB CLASS_1 <#CLASS_1> - $ ; 25 offset to; Address: CLASS_1
+ DEFB CLASS_2 <#CLASS_2> - $ ; 53 offset to; Address: CLASS_2
+ DEFB CLASS_3 <#CLASS_3> - $ ; 0F offset to; Address: CLASS_3
+ DEFB CLASS_4 <#CLASS_4> - $ ; 6B offset to; Address: CLASS_4
+ DEFB CLASS_5 <#CLASS_5> - $ ; 13 offset to; Address: CLASS_5
+ DEFB CLASS_6 <#CLASS_6> - $ ; 76 offset to; Address: CLASS_6
+
+------------------------------------------------------------------------
+
+; THE *'CHECK END'* SUBROUTINE
+------------------------------------------------------------------------
+
+; Check for end of statement and that no spurious characters occur after
+; a correctly parsed statement. Since only one statement is allowed on each
+; line, the only character that may follow a statement is a NEWLINE.
+;
+mark_0D1D:
+*CHECK_END:*
+ CALL SYNTAX_Z <#SYNTAX_Z>
+ RET NZ ; return in runtime.
+
+ POP BC ; else drop return address.
+
+mark_0D22:
+*CHECK_2:*
+ LD A,(HL) ; fetch character.
+ CP ZX_NEWLINE ; compare to NEWLINE.
+ RET Z ; return if so.
+
+mark_0D26:
+*REPORT_C2:*
+ JR REPORT_C <#REPORT_C>
+ ; 'Nonsense in BASIC'
+
+------------------------------------------------------------------------
+
+; COMMAND CLASSES 03, 00, 05
+------------------------------------------------------------------------
+
+
+mark_0D28:
+*CLASS_3:*
+ CP ZX_NEWLINE ;
+ CALL NUMBER_TO_STK <#NUMBER_TO_STK>
+
+mark_0D2D:
+*CLASS_0:*
+ CP A ;
+
+mark_0D2E:
+*CLASS_5:*
+ POP BC ;
+ CALL Z,CHECK_END <#CHECK_END>
+ EX DE,HL ;
+ LD HL,(T_ADDR) ;
+ LD C,(HL) ;
+ INC HL ;
+ LD B,(HL) ;
+ EX DE,HL ;
+
+mark_0D3A:
+*CLASS_END:*
+ PUSH BC ;
+ RET ;
+
+------------------------------------------------------------------------
+
+; COMMAND CLASSES 01, 02, 04, 06
+------------------------------------------------------------------------
+
+
+mark_0D3C:
+*CLASS_1:*
+ CALL LOOK_VARS <#LOOK_VARS>
+
+mark_0D3F:
+*CLASS_4_2:*
+ LD (IY+FLAGX-RAMBASE),$00
+ JR NC,SET_STK <#SET_STK>
+
+ SET 1,(IY+FLAGX-RAMBASE)
+ JR NZ,SET_STRLN <#SET_STRLN>
+
+
+mark_0D4B:
+*REPORT_2:*
+ RST _ERROR_1
+ DEFB $01 ; Error Report: Variable not found
+; ___
+
+mark_0D4D:
+*SET_STK:*
+ CALL Z,STK_VAR <#STK_VAR>
+ BIT 6,(IY+FLAGS-RAMBASE) ; Numeric or string result?
+ JR NZ,SET_STRLN <#SET_STRLN>
+
+ XOR A ;
+ CALL SYNTAX_Z <#SYNTAX_Z>
+ CALL NZ,STK_FETCH <#STK_FETCH>
+ LD HL,FLAGX ;
+ OR (HL) ;
+ LD (HL),A ;
+ EX DE,HL ;
+
+mark_0D63:
+*SET_STRLN:*
+ LD (STRLEN),BC ;
+ LD (DEST),HL ;
+
+; THE *'REM'* COMMAND ROUTINE
+
+mark_0D6A:
+*REM:*
+ RET ;
+
+; ___
+
+mark_0D6B:
+*CLASS_2:*
+ POP BC ;
+ LD A,(FLAGS) ; sv
+
+mark_0D6F:
+*INPUT_REP:*
+ PUSH AF ;
+ CALL SCANNING <#SCANNING>
+ POP AF ;
+ LD BC,LET <#LET> ; Address: LET
+ LD D,(IY+FLAGS-RAMBASE)
+ XOR D ;
+ AND $40 ;
+ JR NZ,REPORT_C <#REPORT_C> ; to REPORT_C
+
+ BIT 7,D ;
+ JR NZ,CLASS_END <#CLASS_END> ; to CLASS_END
+
+ JR CHECK_2 <#CHECK_2> ; to CHECK_2
+; ___
+
+mark_0D85:
+*CLASS_4:*
+ CALL LOOK_VARS <#LOOK_VARS>
+ PUSH AF ;
+ LD A,C ;
+ OR $9F ;
+ INC A ;
+ JR NZ,REPORT_C <#REPORT_C> ; to REPORT_C
+
+ POP AF ;
+ JR CLASS_4_2 <#CLASS_4_2> ; to CLASS_4_2
+
+; ___
+
+mark_0D92:
+*CLASS_6:*
+ CALL SCANNING <#SCANNING>
+ BIT 6,(IY+FLAGS-RAMBASE) ; Numeric or string result?
+ RET NZ ;
+
+
+mark_0D9A:
+*REPORT_C:*
+ RST _ERROR_1
+ DEFB $0B ; Error Report: Nonsense in BASIC
+
+------------------------------------------------------------------------
+
+; THE *'NUMBER TO STACK'* SUBROUTINE
+------------------------------------------------------------------------
+
+;
+;
+
+mark_0D9C:
+*NUMBER_TO_STK:*
+ JR NZ,CLASS_6 <#CLASS_6> ; back to CLASS_6 with a non-zero number.
+
+ CALL SYNTAX_Z <#SYNTAX_Z>
+ RET Z ; return if checking syntax.
+
+; in runtime a zero default is placed on the calculator stack.
+
+ RST _FP_CALC ;;
+ DEFB __stk_zero ;;
+ DEFB __end_calc ;;
+
+ RET ; return.
+
+------------------------------------------------------------------------
+
+; THE *'SYNTAX_Z'* SUBROUTINE
+------------------------------------------------------------------------
+
+; This routine returns with zero flag set if checking syntax.
+; Calling this routine uses three instruction bytes compared to four if the
+; bit test is implemented inline.
+
+mark_0DA6:
+*SYNTAX_Z:*
+ BIT 7,(IY+FLAGS-RAMBASE) ; checking syntax only?
+ RET ; return.
+
+------------------------------------------------------------------------
+
+; THE *'IF'* COMMAND ROUTINE
+------------------------------------------------------------------------
+
+; In runtime, the class routines have evaluated the test expression and
+; the result, true or false, is on the stack.
+
+mark_0DAB:
+*IF:*
+ CALL SYNTAX_Z <#SYNTAX_Z>
+ JR Z,IF_END <#IF_END> ; forward if checking syntax
+
+; else delete the Boolean value on the calculator stack.
+
+ RST _FP_CALC ;;
+ DEFB __delete ;;
+ DEFB __end_calc ;;
+
+; register DE points to exponent of floating point value.
+
+ LD A,(DE) ; fetch exponent.
+ AND A ; test for zero - FALSE.
+ RET Z ; return if so.
+
+mark_0DB6:
+*IF_END:*
+ JP LINE_NULL <#LINE_NULL> ; jump back
+
+------------------------------------------------------------------------
+
+; THE *'FOR'* COMMAND ROUTINE
+------------------------------------------------------------------------
+
+;
+;
+
+mark_0DB9:
+*FOR:*
+ CP ZX_STEP ; is current character 'STEP' ?
+ JR NZ,F_USE_ONE <#F_USE_ONE> ; forward if not
+
+
+ RST _NEXT_CHAR
+ CALL CLASS_6 <#CLASS_6> ; stacks the number
+ CALL CHECK_END <#CHECK_END>
+ JR F_REORDER <#F_REORDER> ; forward to F_REORDER
+; ___
+
+mark_0DC6:
+*F_USE_ONE:*
+ CALL CHECK_END <#CHECK_END>
+
+ RST _FP_CALC ;;
+ DEFB __stk_one ;;
+ DEFB __end_calc ;;
+
+
+
+mark_0DCC:
+*F_REORDER:*
+ RST _FP_CALC ;; v, l, s.
+ DEFB __st_mem_0 ;; v, l, s.
+ DEFB __delete ;; v, l.
+ DEFB __exchange ;; l, v.
+ DEFB __get_mem_0 ;; l, v, s.
+ DEFB __exchange ;; l, s, v.
+ DEFB __end_calc ;; l, s, v.
+
+ CALL LET <#LET>
+
+ LD (MEM),HL ; set MEM to address variable.
+ DEC HL ; point to letter.
+ LD A,(HL) ;
+ SET 7,(HL) ;
+ LD BC,$0006 ;
+ ADD HL,BC ;
+ RLCA ;
+ JR C,F_LMT_STP <#F_LMT_STP>
+
+ SLA C ;
+ CALL MAKE_ROOM <#MAKE_ROOM>
+ INC HL ;
+
+mark_0DEA:
+*F_LMT_STP:*
+ PUSH HL ;
+
+ RST _FP_CALC ;;
+ DEFB __delete ;;
+ DEFB __delete ;;
+ DEFB __end_calc ;;
+
+ POP HL ;
+ EX DE,HL ;
+
+ LD C,$0A ; ten bytes to be moved.
+ LDIR ; copy bytes
+
+ LD HL,(PPC) ; set HL to system variable PPC current line.
+ EX DE,HL ; transfer to DE, variable pointer to HL.
+ INC DE ; loop start will be this line + 1 at least.
+ LD (HL),E ;
+ INC HL ;
+ LD (HL),D ;
+ CALL NEXT_LOOP <#NEXT_LOOP> ; considers an initial pass.
+ RET NC ; return if possible.
+
+; else program continues from point following matching NEXT.
+
+ BIT 7,(IY+PPC_hi-RAMBASE)
+ RET NZ ; return if over 32767 ???
+
+ LD B,(IY+STRLEN_lo-RAMBASE) ; fetch variable name from STRLEN_lo
+ RES 6,B ; make a true letter.
+ LD HL,(NXTLIN) ; set HL from NXTLIN
+
+; now enter a loop to look for matching next.
+
+mark_0E0E:
+*NXTLIN_NO:*
+ LD A,(HL) ; fetch high byte of line number.
+ AND $C0 ; mask off low bits $3F
+ JR NZ,FOR_END <#FOR_END> ; forward at end of program
+
+ PUSH BC ; save letter
+ CALL NEXT_ONE <#NEXT_ONE> ; finds next line.
+ POP BC ; restore letter
+
+ INC HL ; step past low byte
+ INC HL ; past the
+ INC HL ; line length.
+ CALL TEMP_PTR1 <#TEMP_PTR1> ; sets CH_ADD
+
+ RST _GET_CHAR
+ CP ZX_NEXT ;
+ EX DE,HL ; next line to HL.
+ JR NZ,NXTLIN_NO <#NXTLIN_NO> ; back with no match
+
+;
+
+ EX DE,HL ; restore pointer.
+
+ RST _NEXT_CHAR ; advances and gets letter in A.
+ EX DE,HL ; save pointer
+ CP B ; compare to variable name.
+ JR NZ,NXTLIN_NO <#NXTLIN_NO> ; back with mismatch
+
+mark_0E2A:
+*FOR_END:*
+ LD (NXTLIN),HL ; update system variable NXTLIN
+ RET ; return.
+
+------------------------------------------------------------------------
+
+; THE *'NEXT'* COMMAND ROUTINE
+------------------------------------------------------------------------
+
+;
+;
+
+mark_0E2E:
+*NEXT:*
+ BIT 1,(IY+FLAGX-RAMBASE)
+ JP NZ,REPORT_2 <#REPORT_2>
+
+ LD HL,(DEST)
+ BIT 7,(HL)
+ JR Z,REPORT_1 <#REPORT_1>
+
+ INC HL ;
+ LD (MEM),HL ;
+
+ RST _FP_CALC ;;
+ DEFB __get_mem_0 ;;
+ DEFB __get_mem_2 ;;
+ DEFB __addition ;;
+ DEFB __st_mem_0 ;;
+ DEFB __delete ;;
+ DEFB __end_calc ;;
+
+ CALL NEXT_LOOP <#NEXT_LOOP>
+ RET C ;
+
+ LD HL,(MEM) ;
+ LD DE,$000F ;
+ ADD HL,DE ;
+ LD E,(HL) ;
+ INC HL ;
+ LD D,(HL) ;
+ EX DE,HL ;
+ JR GOTO_2 <#GOTO_2>
+; ___
+
+mark_0E58:
+*REPORT_1:*
+ RST _ERROR_1
+ DEFB $00 ; Error Report: NEXT without FOR
+
+
+------------------------------------------------------------------------
+
+; THE *'NEXT_LOOP'* SUBROUTINE
+------------------------------------------------------------------------
+
+;
+;
+
+mark_0E5A:
+*NEXT_LOOP:*
+ RST _FP_CALC ;;
+ DEFB __get_mem_1 ;;
+ DEFB __get_mem_0 ;;
+ DEFB __get_mem_2 ;;
+ DEFB __less_0 ;;
+ DEFB __jump_true ;;
+ DEFB LMT_V_VAL <#LMT_V_VAL> - $ ;;
+
+ DEFB __exchange ;;
+
+mark_0E62:
+*LMT_V_VAL:*
+ DEFB __subtract ;;
+ DEFB __greater_0 ;;
+ DEFB __jump_true ;;
+ DEFB IMPOSS <#IMPOSS> - $ ;;
+
+ DEFB __end_calc ;;
+
+ AND A ; clear carry flag
+ RET ; return.
+; ___
+
+mark_0E69:
+*IMPOSS:*
+ DEFB __end_calc ;;
+
+ SCF ; set carry flag
+ RET ; return.
+
+------------------------------------------------------------------------
+
+; THE *'RAND'* COMMAND ROUTINE
+------------------------------------------------------------------------
+
+; The keyword was *'RANDOMISE'* on the ZX80, is 'RAND' here on the ZX81 and
+; becomes 'RANDOMIZE' on the ZX Spectrum.
+; In all invocations the procedure is the same - to set the SEED system variable
+; with a supplied integer value or to use a time-based value if no number, or
+; zero, is supplied.
+
+mark_0E6C:
+*RAND:*
+ CALL FIND_INT <#FIND_INT>
+ LD A,B ; test value
+ OR C ; for zero
+ JR NZ,SET_SEED <#SET_SEED> ; forward if not zero
+
+ LD BC,(FRAMES) ; fetch value of FRAMES system variable.
+
+mark_0E77:
+*SET_SEED:*
+ LD (SEED),BC ; update the SEED system variable.
+ RET ; return.
+
+------------------------------------------------------------------------
+
+; THE *'CONT'* COMMAND ROUTINE
+------------------------------------------------------------------------
+
+; Another abbreviated command. ROM space was really tight.
+; CONTINUE at the line number that was set when break was pressed.
+; Sometimes the current line, sometimes the next line.
+
+mark_0E7C:
+*CONT:*
+ LD HL,(OLDPPC) ; set HL from system variable OLDPPC
+ JR GOTO_2 <#GOTO_2> ; forward
+
+------------------------------------------------------------------------
+
+; THE *'GOTO'* COMMAND ROUTINE
+------------------------------------------------------------------------
+
+; This token also suffered from the shortage of room and there is no space
+; getween GO and TO as there is on the ZX80 and ZX Spectrum. The same also
+; applies to the GOSUB keyword.
+
+mark_0E81:
+*GOTO:*
+ CALL FIND_INT <#FIND_INT>
+ LD H,B ;
+ LD L,C ;
+
+mark_0E86:
+*GOTO_2:*
+ LD A,H ;
+ CP $F0 ; ZX_LIST ???
+ JR NC,REPORT_B <#REPORT_B>
+
+ CALL LINE_ADDR <#LINE_ADDR>
+ LD (NXTLIN),HL ; sv
+ RET ;
+
+------------------------------------------------------------------------
+
+; THE *'POKE'* COMMAND ROUTINE
+------------------------------------------------------------------------
+
+
+mark_0E92:
+*POKE:*
+ CALL FP_TO_A <#FP_TO_A>
+ JR C,REPORT_B <#REPORT_B> ; forward, with overflow
+
+ JR Z,POKE_SAVE <#POKE_SAVE> ; forward, if positive
+
+ NEG ; negate
+
+mark_0E9B:
+*POKE_SAVE:*
+ PUSH AF ; preserve value.
+ CALL FIND_INT <#FIND_INT> ; gets address in BC
+ ; invoking the error routine with overflow
+ ; or a negative number.
+ POP AF ; restore value.
+
+; Note. the next two instructions are legacy code from the ZX80 and
+; inappropriate here.
+
+ BIT 7,(IY+ERR_NR-RAMBASE) ; test ERR_NR - is it still $FF ?
+ RET Z ; return with error.
+
+ LD (BC),A ; update the address contents.
+ RET ; return.
+
+------------------------------------------------------------------------
+
+; THE *'FIND INTEGER'* SUBROUTINE
+------------------------------------------------------------------------
+
+
+mark_0EA7:
+*FIND_INT:*
+ CALL FP_TO_BC <#FP_TO_BC>
+ JR C,REPORT_B <#REPORT_B> ; forward with overflow
+
+ RET Z ; return if positive (0-65535).
+
+
+mark_0EAD:
+*REPORT_B:*
+ RST _ERROR_1
+ DEFB $0A ; Error Report: Integer out of range
+;
+; Seems stupid, $0A is 10 but the ERROR_CODE_INTEGER_OUT_OF_RANGE is 11
+; maybe gets incremented ???
+
+------------------------------------------------------------------------
+
+; THE *'RUN'* COMMAND ROUTINE
+------------------------------------------------------------------------
+
+
+mark_0EAF:
+*RUN:*
+ CALL GOTO <#GOTO>
+ JP CLEAR <#CLEAR>
+
+------------------------------------------------------------------------
+
+; THE *'GOSUB'* COMMAND ROUTINE
+------------------------------------------------------------------------
+
+
+mark_0EB5:
+*GOSUB:*
+ LD HL,(PPC) ;
+ INC HL ;
+ EX (SP),HL ;
+ PUSH HL ;
+ LD (ERR_SP),SP ; set the error stack pointer - ERR_SP
+ CALL GOTO <#GOTO>
+ LD BC,6 ;
+
+------------------------------------------------------------------------
+
+; THE *'TEST ROOM'* SUBROUTINE
+------------------------------------------------------------------------
+
+;
+; checks ther is room for 36 bytes on the stack
+;
+mark_0EC5:
+*TEST_ROOM:*
+ LD HL,(STKEND) ;
+ ADD HL,BC ; HL = STKEND + BC
+ JR C,REPORT_4 <#REPORT_4>
+
+ EX DE,HL ; DE = STKEND + BC
+ LD HL,$0024 ; 36 decimal
+ ADD HL,DE ; HL = 36 + STKEND + BC
+ SBC HL,SP ; HL = 36 + STKEND + BC - SP
+ RET C ;
+
+mark_0ED3:
+*REPORT_4:*
+ LD L,3 ;
+ JP ERROR_3 <#ERROR_3>
+
+------------------------------------------------------------------------
+
+; THE *'RETURN'* COMMAND ROUTINE
+------------------------------------------------------------------------
+
+
+mark_0ED8:
+*RETURN:*
+ POP HL ;
+ EX (SP),HL ;
+ LD A,H ;
+ CP $3E ;
+ JR Z,REPORT_7 <#REPORT_7>
+
+ LD (ERR_SP),SP ;
+ JR GOTO_2 <#GOTO_2> ; back
+; ___
+
+mark_0EE5:
+*REPORT_7:*
+ EX (SP),HL ;
+ PUSH HL ;
+
+ RST _ERROR_1
+ DEFB 6 ; Error Report: RETURN without GOSUB
+
+;
+; Contradicts BASIC manual:
+; 7 is ERROR_CODE_RETURN_WITHOUT_GOSUB
+; 6 is ERROR_CODE_ARITHMETIC_OVERFLOW
+;
+
+------------------------------------------------------------------------
+
+; THE *'INPUT'* COMMAND ROUTINE
+------------------------------------------------------------------------
+
+
+mark_0EE9:
+*INPUT:*
+ BIT 7,(IY+PPC_hi-RAMBASE)
+ JR NZ,REPORT_8 <#REPORT_8> ; to REPORT_8
+
+ CALL X_TEMP <#X_TEMP>
+ LD HL,FLAGX ;
+ SET 5,(HL) ;
+ RES 6,(HL) ;
+ LD A,(FLAGS) ;
+ AND $40 ; 64
+ LD BC,2 ;
+ JR NZ,PROMPT <#PROMPT> ; to PROMPT
+
+ LD C,$04 ;
+
+mark_0F05:
+*PROMPT:*
+ OR (HL) ;
+ LD (HL),A ;
+
+ RST _BC_SPACES
+ LD (HL),ZX_NEWLINE
+ LD A,C ;
+ RRCA ;
+ RRCA ;
+ JR C,ENTER_CUR <#ENTER_CUR>
+
+ LD A,$0B ; ZX_QUOTE ???
+ LD (DE),A ;
+ DEC HL ;
+ LD (HL),A ;
+
+mark_0F14:
+*ENTER_CUR:*
+ DEC HL ;
+ LD (HL),ZX_CURSOR ;
+ LD HL,(S_POSN) ;
+ LD (T_ADDR),HL ;
+ POP HL ;
+ JP LOWER <#LOWER>
+
+; ___
+
+mark_0F21:
+*REPORT_8:*
+ RST _ERROR_1
+ DEFB 7 ; Error Report: End of file
+
+------------------------------------------------------------------------
+
+; THE *'PAUSE'* COMMAND ROUTINE
+------------------------------------------------------------------------
+
+
+mark_0F23:
+*FAST:*
+ CALL SET_FAST <#SET_FAST>
+ RES 6,(IY+CDFLAG-RAMBASE)
+ RET ; return.
+
+------------------------------------------------------------------------
+
+; THE *'SLOW'* COMMAND ROUTINE
+------------------------------------------------------------------------
+
+
+mark_0F2B:
+*SLOW:*
+ SET 6,(IY+CDFLAG-RAMBASE)
+ JP SLOW_FAST <#SLOW_FAST>
+
+------------------------------------------------------------------------
+
+; THE *'PAUSE'* COMMAND ROUTINE
+------------------------------------------------------------------------
+
+
+mark_0F32:
+*PAUSE:*
+ CALL FIND_INT <#FIND_INT>
+ CALL SET_FAST <#SET_FAST>
+ LD H,B ;
+ LD L,C ;
+ CALL DISPLAY_P <#DISPLAY_P>
+
+ LD (IY+FRAMES_hi-RAMBASE),$FF
+
+ CALL SLOW_FAST <#SLOW_FAST>
+ JR DEBOUNCE <#DEBOUNCE>
+
+------------------------------------------------------------------------
+
+; THE *'BREAK'* SUBROUTINE
+------------------------------------------------------------------------
+
+
+mark_0F46:
+*BREAK_1:*
+ LD A,$7F ; read port $7FFE - keys B,N,M,.,SPACE.
+ IN A,(IO_PORT_KEYBOARD_RD) ;
+ RRA ; carry will be set if space not pressed.
+
+------------------------------------------------------------------------
+
+; THE *'DEBOUNCE'* SUBROUTINE
+------------------------------------------------------------------------
+
+
+mark_0F4B:
+*DEBOUNCE:*
+ RES 0,(IY+CDFLAG-RAMBASE) ; update
+ LD A,$FF ;
+ LD (DEBOUNCE_VAR),A ; update
+ RET ; return.
+
+
+------------------------------------------------------------------------
+
+; THE *'SCANNING'* SUBROUTINE
+------------------------------------------------------------------------
+
+; This recursive routine is where the ZX81 gets its power.
+; Provided there is enough memory it can evaluate
+; an expression of unlimited complexity.
+; Note. there is no unary plus so, as on the ZX80, PRINT +1 gives a syntax error.
+; PRINT +1 works on the Spectrum but so too does PRINT + "STRING".
+
+mark_0F55:
+*SCANNING:*
+ RST _GET_CHAR
+ LD B,0 ; set B register to zero.
+ PUSH BC ; stack zero as a priority end-marker.
+
+mark_0F59:
+*S_LOOP_1:*
+ CP ZX_RND
+ JR NZ,S_TEST_PI <#S_TEST_PI> ; forward, if not, to S_TEST_PI
+
+------------------------------------------------------------------------
+
+; THE *'RND'* FUNCTION
+------------------------------------------------------------------------
+
+*RND:*
+
+ CALL SYNTAX_Z <#SYNTAX_Z>
+ JR Z,S_JPI_END <#S_JPI_END> ; forward if checking syntax to S_JPI_END
+
+ LD BC,(SEED) ; sv
+ CALL STACK_BC <#STACK_BC>
+
+ RST _FP_CALC ;;
+ DEFB __stk_one ;;
+ DEFB __addition ;;
+ DEFB __stk_data ;;
+ DEFB $37 ;;Exponent: $87, Bytes: 1
+ DEFB $16 ;;(+00,+00,+00)
+ DEFB __multiply ;;
+ DEFB __stk_data ;;
+ DEFB $80 ;;Bytes: 3
+ DEFB $41 ;;Exponent $91
+ DEFB $00,$00,$80 ;;(+00)
+ DEFB __n_mod_m ;;
+ DEFB __delete ;;
+ DEFB __stk_one ;;
+ DEFB __subtract ;;
+ DEFB __duplicate ;;
+ DEFB __end_calc ;;
+
+ CALL FP_TO_BC <#FP_TO_BC>
+ LD (SEED),BC ; update the SEED system variable.
+ LD A,(HL) ; HL addresses the exponent of the last value.
+ AND A ; test for zero
+ JR Z,S_JPI_END <#S_JPI_END> ; forward, if so
+
+ SUB $10 ; else reduce exponent by sixteen
+ LD (HL),A ; thus dividing by 65536 for last value.
+
+mark_0F8A:
+*S_JPI_END:*
+ JR S_PI_END <#S_PI_END> ; forward
+
+; ___
+
+mark_0F8C:
+*S_TEST_PI:*
+ CP ZX_PI ; the 'PI' character
+ JR NZ,S_TST_INK <#S_TST_INK> ; forward, if not
+
+------------------------------------------------------------------------
+
+; THE *'PI'* EVALUATION
+------------------------------------------------------------------------
+
+
+ CALL SYNTAX_Z <#SYNTAX_Z>
+ JR Z,S_PI_END <#S_PI_END> ; forward if checking syntax
+
+
+ RST _FP_CALC ;;
+ DEFB __stk_half_pi ;;
+ DEFB __end_calc ;;
+
+ INC (HL) ; double the exponent giving PI on the stack.
+
+mark_0F99:
+*S_PI_END:*
+ RST _NEXT_CHAR ; advances character pointer.
+
+ JP S_NUMERIC <#S_NUMERIC> ; jump forward to set the flag
+ ; to signal numeric result before advancing.
+
+; ___
+
+mark_0F9D:
+*S_TST_INK:*
+ CP ZX_INKEY_STR ;
+ JR NZ,S_ALPHANUM <#S_ALPHANUM> ; forward, if not
+
+------------------------------------------------------------------------
+
+; THE *'INKEY$'* EVALUATION
+------------------------------------------------------------------------
+
+
+ CALL KEYBOARD <#KEYBOARD>
+ LD B,H ;
+ LD C,L ;
+ LD D,C ;
+ INC D ;
+ CALL NZ,DECODE <#DECODE>
+ LD A,D ;
+ ADC A,D ;
+ LD B,D ;
+ LD C,A ;
+ EX DE,HL ;
+ JR S_STRING <#S_STRING> ; forward
+
+; ___
+
+mark_0FB2:
+*S_ALPHANUM:*
+ CALL ALPHANUM <#ALPHANUM>
+ JR C,S_LTR_DGT <#S_LTR_DGT> ; forward, if alphanumeric
+
+ CP ZX_PERIOD ; is character a '.' ?
+ JP Z,S_DECIMAL <#S_DECIMAL> ; jump forward if so
+
+ LD BC,$09D8 ; prepare priority 09, operation 'subtract'
+ CP ZX_MINUS ; is character unary minus '-' ?
+ JR Z,S_PUSH_PO <#S_PUSH_PO> ; forward, if so
+
+ CP ZX_BRACKET_LEFT ; is character a '(' ?
+ JR NZ,S_QUOTE <#S_QUOTE> ; forward if not
+
+ CALL CH_ADD_PLUS_1 <#CH_ADD_PLUS_1> ; advances character pointer.
+
+ CALL SCANNING <#SCANNING> ; recursively call to evaluate the sub_expression.
+
+ CP ZX_BRACKET_RIGHT; is subsequent character a ')' ?
+ JR NZ,S_RPT_C <#S_RPT_C> ; forward if not
+
+
+ CALL CH_ADD_PLUS_1 <#CH_ADD_PLUS_1> ; advances.
+ JR S_J_CONT_3 <#S_J_CONT_3> ; relative jump to S_JP_CONT3 and then S_CONT3
+
+; ___
+
+; consider a quoted string e.g. PRINT "Hooray!"
+; Note. quotes are not allowed within a string.
+
+mark_0FD6:
+*S_QUOTE:*
+ CP ZX_QUOTE ; is character a quote (") ?
+ JR NZ,S_FUNCTION <#S_FUNCTION> ; forward, if not
+
+ CALL CH_ADD_PLUS_1 <#CH_ADD_PLUS_1> ; advances
+ PUSH HL ; * save start of string.
+ JR S_QUOTE_S <#S_QUOTE_S> ; forward
+
+; ___
+
+
+mark_0FE0:
+*S_Q_AGAIN:*
+ CALL CH_ADD_PLUS_1 <#CH_ADD_PLUS_1>
+
+mark_0FE3:
+*S_QUOTE_S:*
+ CP ZX_QUOTE ; is character a '"' ?
+ JR NZ,S_Q_NL <#S_Q_NL> ; forward if not to S_Q_NL
+
+ POP DE ; * retrieve start of string
+ AND A ; prepare to subtract.
+ SBC HL,DE ; subtract start from current position.
+ LD B,H ; transfer this length
+ LD C,L ; to the BC register pair.
+
+mark_0FED:
+*S_STRING:*
+ LD HL,FLAGS ; address system variable FLAGS
+ RES 6,(HL) ; signal string result
+ BIT 7,(HL) ; test if checking syntax.
+
+ CALL NZ,STK_STO_STR <#STK_STO_STR> ; in run-time stacks the
+ ; string descriptor - start DE, length BC.
+
+ RST _NEXT_CHAR ; advances pointer.
+
+mark_0FF8:
+*S_J_CONT_3:*
+ JP S_CONT_3 <#S_CONT_3>
+
+; ___
+
+; A string with no terminating quote has to be considered.
+
+mark_0FFB:
+*S_Q_NL:*
+ CP ZX_NEWLINE
+ JR NZ,S_Q_AGAIN <#S_Q_AGAIN> ; loop back if not
+
+mark_0FFF:
+*S_RPT_C:*
+ JP REPORT_C <#REPORT_C>
+; ___
+
+mark_1002:
+*S_FUNCTION:*
+ SUB $C4 ; subtract 'CODE' reducing codes
+ ; CODE thru '<>' to range $00 - $XX
+ JR C,S_RPT_C <#S_RPT_C> ; back, if less
+
+; test for NOT the last function in character set.
+
+ LD BC,$04EC ; prepare priority $04, operation 'not'
+ CP $13 ; compare to 'NOT' ( - CODE)
+ JR Z,S_PUSH_PO <#S_PUSH_PO> ; forward, if so
+
+ JR NC,S_RPT_C <#S_RPT_C> ; back with anything higher
+
+; else is a function 'CODE' thru 'CHR$'
+
+ LD B,$10 ; priority sixteen binds all functions to
+ ; arguments removing the need for brackets.
+
+ ADD A,$D9 ; add $D9 to give range $D9 thru $EB
+ ; bit 6 is set to show numeric argument.
+ ; bit 7 is set to show numeric result.
+
+; now adjust these default argument/result indicators.
+
+ LD C,A ; save code in C
+
+ CP $DC ; separate 'CODE', 'VAL', 'LEN'
+ JR NC,S_NUMBER_TO_STRING <#S_NUMBER_TO_STRING> ; skip forward if string operand
+
+ RES 6,C ; signal string operand.
+
+mark_101A:
+*S_NUMBER_TO_STRING:*
+ CP $EA ; isolate top of range 'STR$' and 'CHR$'
+ JR C,S_PUSH_PO <#S_PUSH_PO> ; skip forward with others
+
+ RES 7,C ; signal string result.
+
+mark_1020:
+*S_PUSH_PO:*
+ PUSH BC ; push the priority/operation
+
+ RST _NEXT_CHAR
+ JP S_LOOP_1 <#S_LOOP_1> ; jump back
+; ___
+
+mark_1025:
+*S_LTR_DGT:*
+ CP ZX_A ; compare to 'A'.
+ JR C,S_DECIMAL <#S_DECIMAL> ; forward if less to S_DECIMAL
+
+ CALL LOOK_VARS <#LOOK_VARS>
+ JP C,REPORT_2 <#REPORT_2> ; back if not found
+ ; a variable is always 'found' when checking
+ ; syntax.
+
+ CALL Z,STK_VAR <#STK_VAR> ; stacks string parameters or
+ ; returns cell location if numeric.
+
+ LD A,(FLAGS) ; fetch FLAGS
+ CP $C0 ; compare to numeric result/numeric operand
+ JR C,S_CONT_2 <#S_CONT_2> ; forward if not numeric
+
+ INC HL ; address numeric contents of variable.
+ LD DE,(STKEND) ; set destination to STKEND
+ CALL MOVE_FP <#MOVE_FP> ; stacks the five bytes
+ EX DE,HL ; transfer new free location from DE to HL.
+ LD (STKEND),HL ; update STKEND system variable.
+ JR S_CONT_2 <#S_CONT_2> ; forward
+; ___
+
+; The Scanning Decimal routine is invoked when a decimal point or digit is
+; found in the expression.
+; When checking syntax, then the 'hidden floating point' form is placed
+; after the number in the BASIC line.
+; In run-time, the digits are skipped and the floating point number is picked
+; up.
+
+mark_1047:
+*S_DECIMAL:*
+ CALL SYNTAX_Z <#SYNTAX_Z>
+ JR NZ,S_STK_DEC <#S_STK_DEC> ; forward in run-time
+
+ CALL DEC_TO_FP <#DEC_TO_FP>
+
+ RST _GET_CHAR ; advances HL past digits
+ LD BC,$0006 ; six locations are required.
+ CALL MAKE_ROOM <#MAKE_ROOM>
+ INC HL ; point to first new location
+ LD (HL),$7E ; insert the number marker 126 decimal.
+ INC HL ; increment
+ EX DE,HL ; transfer destination to DE.
+ LD HL,(STKEND) ; set HL from STKEND which points to the
+ ; first location after the 'last value'
+ LD C,$05 ; five bytes to move.
+ AND A ; clear carry.
+ SBC HL,BC ; subtract five pointing to 'last value'.
+ LD (STKEND),HL ; update STKEND thereby 'deleting the value.
+
+ LDIR ; copy the five value bytes.
+
+ EX DE,HL ; basic pointer to HL which may be white-space
+ ; following the number.
+ DEC HL ; now points to last of five bytes.
+ CALL TEMP_PTR1 <#TEMP_PTR1> ; advances the character
+ ; address skipping any white-space.
+ JR S_NUMERIC <#S_NUMERIC> ; forward
+ ; to signal a numeric result.
+; ___
+; In run-time the branch is here when a digit or point is encountered.
+
+mark_106F:
+*S_STK_DEC:*
+ RST _NEXT_CHAR
+ CP $7E ; compare to 'number marker'
+ JR NZ,S_STK_DEC <#S_STK_DEC> ; loop back until found
+ ; skipping all the digits.
+
+ INC HL ; point to first of five hidden bytes.
+ LD DE,(STKEND) ; set destination from STKEND system variable
+ CALL MOVE_FP <#MOVE_FP> ; stacks the number.
+ LD (STKEND),DE ; update system variable STKEND.
+ LD (CH_ADD),HL ; update system variable CH_ADD.
+
+mark_1083:
+*S_NUMERIC:*
+ SET 6,(IY+FLAGS-RAMBASE) ; Signal numeric result
+
+mark_1087:
+*S_CONT_2:*
+ RST _GET_CHAR
+
+mark_1088:
+*S_CONT_3:*
+ CP ZX_BRACKET_LEFT ; compare to opening bracket '('
+ JR NZ,S_OPERTR <#S_OPERTR> ; forward if not
+
+ BIT 6,(IY+FLAGS-RAMBASE) ; Numeric or string result?
+ JR NZ,S_LOOP <#S_LOOP> ; forward if numeric
+
+; else is a string
+
+ CALL SLICING <#SLICING>
+
+ RST _NEXT_CHAR
+ JR S_CONT_3 <#S_CONT_3> ; back
+; ___
+; the character is now manipulated to form an equivalent in the table of
+; calculator literals. This is quite cumbersome and in the ZX Spectrum a
+; simple look-up table was introduced at this point.
+
+mark_1098:
+*S_OPERTR:*
+ LD BC,$00C3 ; prepare operator 'subtract' as default.
+ ; also set B to zero for later indexing.
+
+ CP ZX_GREATER_THAN ; is character '>' ?
+ JR C,S_LOOP <#S_LOOP> ; forward if less, as
+ ; we have reached end of meaningful expression
+
+ SUB ZX_MINUS ; is character '-' ?
+ JR NC,SUBMLTDIV <#SUBMLTDIV> ; forward with - * / and '**' '<>'
+
+ ADD A,13 ; increase others by thirteen
+ ; $09 '>' thru $0C '+'
+ JR GET_PRIO <#GET_PRIO> ; forward
+
+; ___
+
+mark_10A7:
+*SUBMLTDIV:*
+ CP $03 ; isolate $00 '-', $01 '*', $02 '/'
+ JR C,GET_PRIO <#GET_PRIO> ; forward if so
+
+; else possibly originally $D8 '**' thru $DD '<>' already reduced by $16
+
+ SUB $C2 ; giving range $00 to $05
+ JR C,S_LOOP <#S_LOOP> ; forward if less
+
+ CP $06 ; test the upper limit for nonsense also
+ JR NC,S_LOOP <#S_LOOP> ; forward if so
+
+ ADD A,$03 ; increase by 3 to give combined operators of
+
+ ; $00 '-'
+ ; $01 '*'
+ ; $02 '/'
+
+ ; $03 '**'
+ ; $04 'OR'
+ ; $05 'AND'
+ ; $06 '<='
+ ; $07 '>='
+ ; $08 '<>'
+
+ ; $09 '>'
+ ; $0A '<'
+ ; $0B '='
+ ; $0C '+'
+
+mark_10B5:
+*GET_PRIO:*
+ ADD A,C ; add to default operation 'sub' ($C3)
+ LD C,A ; and place in operator byte - C.
+
+ LD HL,tbl_pri <#tbl_pri> - $C3 ; theoretical base of the priorities table.
+ ADD HL,BC ; add C ( B is zero)
+ LD B,(HL) ; pick up the priority in B
+
+mark_10BC:
+*S_LOOP:*
+ POP DE ; restore previous
+ LD A,D ; load A with priority.
+ CP B ; is present priority higher
+ JR C,S_TIGHTER <#S_TIGHTER> ; forward if so to S_TIGHTER
+
+ AND A ; are both priorities zero
+ JP Z,GET_CHAR <#GET_CHAR> ; exit if zero via GET_CHAR
+
+ PUSH BC ; stack present values
+ PUSH DE ; stack last values
+ CALL SYNTAX_Z <#SYNTAX_Z>
+ JR Z,S_SYNTEST <#S_SYNTEST> ; forward is checking syntax
+
+ LD A,E ; fetch last operation
+ AND $3F ; mask off the indicator bits to give true
+ ; calculator literal.
+ LD B,A ; place in the B register for BERG
+
+; perform the single operation
+
+ RST _FP_CALC ;;
+ DEFB __fp_calc_2 ;;
+ DEFB __end_calc ;;
+
+ JR S_RUNTEST <#S_RUNTEST> ; forward
+
+; ___
+
+mark_10D5:
+*S_SYNTEST:*
+ LD A,E ; transfer masked operator to A
+ XOR (IY+FLAGS-RAMBASE) ; XOR with FLAGS like results will reset bit 6
+ AND $40 ; test bit 6
+
+mark_10DB:
+*S_RPORT_C:*
+ JP NZ,REPORT_C <#REPORT_C> ; back if results do not agree.
+
+; ___
+
+; in run-time impose bit 7 of the operator onto bit 6 of the FLAGS
+
+mark_10DE:
+*S_RUNTEST:*
+ POP DE ; restore last operation.
+ LD HL,FLAGS ; address system variable FLAGS
+ SET 6,(HL) ; presume a numeric result
+ BIT 7,E ; test expected result in operation
+ JR NZ,S_LOOPEND <#S_LOOPEND> ; forward if numeric
+
+ RES 6,(HL) ; reset to signal string result
+
+mark_10EA:
+*S_LOOPEND:*
+ POP BC ; restore present values
+ JR S_LOOP <#S_LOOP> ; back
+
+; ___
+
+mark_10ED:
+*S_TIGHTER:*
+ PUSH DE ; push last values and consider these
+
+ LD A,C ; get the present operator.
+ BIT 6,(IY+FLAGS-RAMBASE) ; Numeric or string result?
+ JR NZ,S_NEXT <#S_NEXT> ; forward if numeric to S_NEXT
+
+ AND $3F ; strip indicator bits to give clear literal.
+ ADD A,$08 ; add eight - augmenting numeric to equivalent
+ ; string literals.
+ LD C,A ; place plain literal back in C.
+ CP $10 ; compare to 'AND'
+ JR NZ,S_NOT_AND <#S_NOT_AND> ; forward if not
+
+ SET 6,C ; set the numeric operand required for 'AND'
+ JR S_NEXT <#S_NEXT> ; forward to S_NEXT
+
+; ___
+
+mark_1102:
+*S_NOT_AND:*
+ JR C,S_RPORT_C <#S_RPORT_C> ; back if less than 'AND'
+ ; Nonsense if '-', '*' etc.
+
+ CP __strs_add ; compare to 'strs_add' literal
+ JR Z,S_NEXT <#S_NEXT> ; forward if so signaling string result
+
+ SET 7,C ; set bit to numeric (Boolean) for others.
+
+mark_110A:
+*S_NEXT:*
+ PUSH BC ; stack 'present' values
+
+ RST _NEXT_CHAR
+ JP S_LOOP_1 <#S_LOOP_1> ; jump back
+
+
+
+------------------------------------------------------------------------
+
+; THE *'TABLE OF PRIORITIES'*
+------------------------------------------------------------------------
+
+
+mark_110F:
+*tbl_pri:*
+ DEFB 6 ; '-'
+ DEFB 8 ; '*'
+ DEFB 8 ; '/'
+ DEFB 10 ; '**'
+ DEFB 2 ; 'OR'
+ DEFB 3 ; 'AND'
+ DEFB 5 ; '<='
+ DEFB 5 ; '>='
+ DEFB 5 ; '<>'
+ DEFB 5 ; '>'
+ DEFB 5 ; '<'
+ DEFB 5 ; '='
+ DEFB 6 ; '+'
+
+------------------------------------------------------------------------
+
+; THE *'LOOK_VARS'* SUBROUTINE
+------------------------------------------------------------------------
+
+
+mark_111C:
+*LOOK_VARS:*
+ SET 6,(IY+FLAGS-RAMBASE) ; Signal numeric result
+
+ RST _GET_CHAR
+ CALL ALPHA <#ALPHA>
+ JP NC,REPORT_C <#REPORT_C> ; to REPORT_C
+
+ PUSH HL ;
+ LD C,A ;
+
+ RST _NEXT_CHAR
+ PUSH HL ;
+ RES 5,C ;
+ CP $10 ; $10
+ JR Z,V_RUN_SYN <#V_RUN_SYN>
+
+ SET 6,C ;
+ CP ZX_DOLLAR ; $0D
+ JR Z,V_STR_VAR <#V_STR_VAR> ; forward
+
+ SET 5,C ;
+
+mark_1139:
+*V_CHAR:*
+ CALL ALPHANUM <#ALPHANUM>
+ JR NC,V_RUN_SYN <#V_RUN_SYN> ; forward when not
+
+ RES 6,C ;
+
+ RST _NEXT_CHAR
+ JR V_CHAR <#V_CHAR> ; loop back
+
+; ___
+
+mark_1143:
+*V_STR_VAR:*
+ RST _NEXT_CHAR
+ RES 6,(IY+FLAGS-RAMBASE) ; Signal string result
+
+mark_1148:
+*V_RUN_SYN:*
+ LD B,C ;
+ CALL SYNTAX_Z <#SYNTAX_Z>
+ JR NZ,V_RUN <#V_RUN> ; forward
+
+ LD A,C ;
+ AND $E0 ;
+ SET 7,A ;
+ LD C,A ;
+ JR V_SYNTAX <#V_SYNTAX> ; forward
+
+; ___
+
+mark_1156:
+*V_RUN:*
+ LD HL,(VARS) ; sv
+
+mark_1159:
+*V_EACH:*
+ LD A,(HL) ;
+ AND $7F ;
+ JR Z,V_80_BYTE <#V_80_BYTE> ;
+
+ CP C ;
+ JR NZ,V_NEXT <#V_NEXT> ;
+
+ RLA ;
+ ADD A,A ;
+ JP P,V_FOUND_2 <#V_FOUND_2>
+
+ JR C,V_FOUND_2 <#V_FOUND_2>
+
+ POP DE ;
+ PUSH DE ;
+ PUSH HL ;
+
+mark_116B:
+*V_MATCHES:*
+ INC HL ;
+
+mark_116C:
+*V_SPACES:*
+ LD A,(DE) ;
+ INC DE ;
+ AND A ;
+ JR Z,V_SPACES <#V_SPACES> ; back
+
+ CP (HL) ;
+ JR Z,V_MATCHES <#V_MATCHES> ; back
+
+ OR $80 ;
+ CP (HL) ;
+ JR NZ,V_GET_PTR <#V_GET_PTR> ; forward
+
+ LD A,(DE) ;
+ CALL ALPHANUM <#ALPHANUM>
+ JR NC,V_FOUND_1 <#V_FOUND_1> ; forward
+
+mark_117F:
+*V_GET_PTR:*
+ POP HL ;
+
+mark_1180:
+*V_NEXT:*
+ PUSH BC ;
+ CALL NEXT_ONE <#NEXT_ONE>
+ EX DE,HL ;
+ POP BC ;
+ JR V_EACH <#V_EACH> ; back
+
+; ___
+
+mark_1188:
+*V_80_BYTE:*
+ SET 7,B ;
+
+mark_118A:
+*V_SYNTAX:*
+ POP DE ;
+
+ RST _GET_CHAR
+ CP $10 ;
+ JR Z,V_PASS <#V_PASS> ; forward
+
+ SET 5,B ;
+ JR V_END <#V_END> ; forward
+
+; ___
+
+mark_1194:
+*V_FOUND_1:*
+ POP DE ;
+
+mark_1195:
+*V_FOUND_2:*
+ POP DE ;
+ POP DE ;
+ PUSH HL ;
+
+ RST _GET_CHAR
+
+mark_1199:
+*V_PASS:*
+ CALL ALPHANUM <#ALPHANUM>
+ JR NC,V_END <#V_END> ; forward if not alphanumeric
+
+
+ RST _NEXT_CHAR
+ JR V_PASS <#V_PASS> ; back
+
+; ___
+
+mark_11A1:
+*V_END:*
+ POP HL ;
+ RL B ;
+ BIT 6,B ;
+ RET ;
+
+------------------------------------------------------------------------
+
+; THE *'STK_VAR'* SUBROUTINE
+------------------------------------------------------------------------
+
+
+mark_11A7:
+*STK_VAR:*
+ XOR A ;
+ LD B,A ;
+ BIT 7,C ;
+ JR NZ,SV_COUNT <#SV_COUNT> ; forward
+
+ BIT 7,(HL) ;
+ JR NZ,SV_ARRAYS <#SV_ARRAYS> ; forward
+
+ INC A ;
+
+mark_11B2:
+*SV_SIMPLE_STR:*
+ INC HL ;
+ LD C,(HL) ;
+ INC HL ;
+ LD B,(HL) ;
+ INC HL ;
+ EX DE,HL ;
+ CALL STK_STO_STR <#STK_STO_STR>
+
+ RST _GET_CHAR
+ JP SV_SLICE_QUERY <#SV_SLICE_QUERY> ; jump forward
+
+; ___
+
+mark_11BF:
+*SV_ARRAYS:*
+ INC HL ;
+ INC HL ;
+ INC HL ;
+ LD B,(HL) ;
+ BIT 6,C ;
+ JR Z,SV_PTR <#SV_PTR> ; forward
+
+ DEC B ;
+ JR Z,SV_SIMPLE_STR <#SV_SIMPLE_STR> ; forward
+
+ EX DE,HL ;
+
+ RST _GET_CHAR
+ CP $10 ;
+ JR NZ,REPORT_3 <#REPORT_3> ; forward
+
+ EX DE,HL ;
+
+mark_11D1:
+*SV_PTR:*
+ EX DE,HL ;
+ JR SV_COUNT <#SV_COUNT> ; forward
+; ___
+mark_11D4:
+*SV_COMMA:*
+ PUSH HL ;
+
+ RST _GET_CHAR
+ POP HL ;
+ CP ZX_COMMA ; $1A == 26
+ JR Z,SV_LOOP <#SV_LOOP> ; forward
+
+ BIT 7,C ;
+ JR Z,REPORT_3 <#REPORT_3> ; forward
+
+ BIT 6,C ;
+ JR NZ,SV_CLOSE <#SV_CLOSE> ; forward
+
+ CP ZX_BRACKET_RIGHT ; $11
+ JR NZ,SV_RPT_C <#SV_RPT_C> ; forward
+
+
+ RST _NEXT_CHAR
+ RET ;
+; ___
+mark_11E9:
+*SV_CLOSE:*
+ CP ZX_BRACKET_RIGHT ; $11
+ JR Z,SV_DIM <#SV_DIM> ; forward
+
+ CP $DF ;
+ JR NZ,SV_RPT_C <#SV_RPT_C> ; forward
+
+mark_11F1:
+*SV_CH_ADD:*
+ RST _GET_CHAR
+ DEC HL ;
+ LD (CH_ADD),HL ; sv
+ JR SV_SLICE <#SV_SLICE> ; forward
+
+; ___
+
+mark_11F8:
+*SV_COUNT:*
+ LD HL,$0000 ;
+
+mark_11FB:
+*SV_LOOP:*
+ PUSH HL ;
+
+ RST _NEXT_CHAR
+ POP HL ;
+ LD A,C ;
+ CP ZX_DOUBLE_QUOTE ;
+ JR NZ,SV_MULT <#SV_MULT> ; forward
+
+
+ RST _GET_CHAR
+ CP ZX_BRACKET_RIGHT
+ JR Z,SV_DIM <#SV_DIM> ; forward
+
+ CP ZX_TO ;
+ JR Z,SV_CH_ADD <#SV_CH_ADD> ; back
+
+mark_120C:
+*SV_MULT:*
+ PUSH BC ;
+ PUSH HL ;
+ CALL DE_DE_PLUS_ONE <#DE_DE_PLUS_ONE>
+ EX (SP),HL ;
+ EX DE,HL ;
+ CALL INT_EXP1 <#INT_EXP1>
+ JR C,REPORT_3 <#REPORT_3>
+
+ DEC BC ;
+ CALL GET_HL_TIMES_DE <#GET_HL_TIMES_DE>
+ ADD HL,BC ;
+ POP DE ;
+ POP BC ;
+ DJNZ SV_COMMA <#SV_COMMA> ; loop back
+
+ BIT 7,C ;
+
+mark_1223:
+*SV_RPT_C:*
+ JR NZ,SL_RPT_C <#SL_RPT_C>
+
+ PUSH HL ;
+ BIT 6,C ;
+ JR NZ,SV_ELEM_STR <#SV_ELEM_STR>
+
+ LD B,D ;
+ LD C,E ;
+
+ RST _GET_CHAR
+ CP ZX_BRACKET_RIGHT; is character a ')' ?
+ JR Z,SV_NUMBER <#SV_NUMBER> ; skip forward
+
+
+mark_1231:
+*REPORT_3:*
+ RST _ERROR_1
+ DEFB $02 ; Error Report: Subscript wrong
+
+
+mark_1233:
+*SV_NUMBER:*
+ RST _NEXT_CHAR
+ POP HL ;
+ LD DE,$0005 ;
+ CALL GET_HL_TIMES_DE <#GET_HL_TIMES_DE>
+ ADD HL,BC ;
+ RET ; return >>
+
+; ___
+
+mark_123D:
+*SV_ELEM_STR:*
+ CALL DE_DE_PLUS_ONE <#DE_DE_PLUS_ONE>
+ EX (SP),HL ;
+ CALL GET_HL_TIMES_DE <#GET_HL_TIMES_DE>
+ POP BC ;
+ ADD HL,BC ;
+ INC HL ;
+ LD B,D ;
+ LD C,E ;
+ EX DE,HL ;
+ CALL STK_ST_0 <#STK_ST_0>
+
+ RST _GET_CHAR
+ CP ZX_BRACKET_RIGHT ; is it ')' ?
+ JR Z,SV_DIM <#SV_DIM> ; forward if so
+
+ CP ZX_COMMA ; $1A == 26 ; is it ',' ?
+ JR NZ,REPORT_3 <#REPORT_3> ; back if not
+
+mark_1256:
+*SV_SLICE:*
+ CALL SLICING <#SLICING>
+
+mark_1259:
+*SV_DIM:*
+ RST _NEXT_CHAR
+
+mark_125A:
+*SV_SLICE_QUERY:*
+ CP $10 ;
+ JR Z,SV_SLICE <#SV_SLICE> ; back
+
+ RES 6,(IY+FLAGS-RAMBASE) ; Signal string result
+ RET ; return.
+
+------------------------------------------------------------------------
+
+; THE *'SLICING'* SUBROUTINE
+------------------------------------------------------------------------
+
+;
+;
+
+mark_1263:
+*SLICING:*
+ CALL SYNTAX_Z <#SYNTAX_Z>
+ CALL NZ,STK_FETCH <#STK_FETCH>
+
+ RST _NEXT_CHAR
+ CP ZX_BRACKET_RIGHT; is it ')' ?
+ JR Z,SL_STORE <#SL_STORE> ; forward if so
+
+ PUSH DE ;
+ XOR A ;
+ PUSH AF ;
+ PUSH BC ;
+ LD DE,$0001 ;
+
+ RST _GET_CHAR
+ POP HL ;
+ CP ZX_TO ; is it 'TO' ?
+ JR Z,SL_SECOND <#SL_SECOND> ; forward if so
+
+ POP AF ;
+ CALL INT_EXP2 <#INT_EXP2>
+ PUSH AF ;
+ LD D,B ;
+ LD E,C ;
+ PUSH HL ;
+
+ RST _GET_CHAR
+ POP HL ;
+ CP ZX_TO ; is it 'TO' ?
+ JR Z,SL_SECOND <#SL_SECOND> ; forward if so
+
+ CP ZX_BRACKET_RIGHT; $11
+
+mark_128B:
+*SL_RPT_C:*
+ JP NZ,REPORT_C <#REPORT_C>
+
+ LD H,D ;
+ LD L,E ;
+ JR SL_DEFINE <#SL_DEFINE> ; forward
+
+; ___
+
+mark_1292:
+*SL_SECOND:*
+ PUSH HL ;
+
+ RST _NEXT_CHAR
+ POP HL ;
+ CP ZX_BRACKET_RIGHT; is it ')' ?
+ JR Z,SL_DEFINE <#SL_DEFINE> ; forward if so
+
+ POP AF ;
+ CALL INT_EXP2 <#INT_EXP2>
+ PUSH AF ;
+
+ RST _GET_CHAR
+ LD H,B ;
+ LD L,C ;
+ CP ZX_BRACKET_RIGHT; is it ')' ?
+ JR NZ,SL_RPT_C <#SL_RPT_C> ; back if not
+
+mark_12A5:
+*SL_DEFINE:*
+ POP AF ;
+ EX (SP),HL ;
+ ADD HL,DE ;
+ DEC HL ;
+ EX (SP),HL ;
+ AND A ;
+ SBC HL,DE ;
+ LD BC,$0000 ;
+ JR C,SL_OVER <#SL_OVER> ; forward
+
+ INC HL ;
+ AND A ;
+ JP M,REPORT_3 <#REPORT_3> ; jump back
+
+ LD B,H ;
+ LD C,L ;
+
+mark_12B9:
+*SL_OVER:*
+ POP DE ;
+ RES 6,(IY+FLAGS-RAMBASE) ; Signal string result
+
+mark_12BE:
+*SL_STORE:*
+ CALL SYNTAX_Z <#SYNTAX_Z>
+ RET Z ; return if checking syntax.
+
+------------------------------------------------------------------------
+
+; THE *'STK_STORE'* SUBROUTINE
+------------------------------------------------------------------------
+
+;
+;
+
+mark_12C2:
+*STK_ST_0:*
+ XOR A ;
+
+mark_12C3:
+*STK_STO_STR:*
+ PUSH BC ;
+ CALL TEST_5_SP <#TEST_5_SP>
+ POP BC ;
+ LD HL,(STKEND) ; sv
+ LD (HL),A ;
+ INC HL ;
+ LD (HL),E ;
+ INC HL ;
+ LD (HL),D ;
+ INC HL ;
+ LD (HL),C ;
+ INC HL ;
+ LD (HL),B ;
+ INC HL ;
+ LD (STKEND),HL ; sv
+ RES 6,(IY+FLAGS-RAMBASE) ; Signal string result
+ RET ; return.
+
+------------------------------------------------------------------------
+
+; THE *'INT EXP'* SUBROUTINES
+------------------------------------------------------------------------
+
+;
+;
+
+mark_12DD:
+*INT_EXP1:*
+ XOR A ;
+
+mark_12DE:
+*INT_EXP2:*
+ PUSH DE ;
+ PUSH HL ;
+ PUSH AF ;
+ CALL CLASS_6 <#CLASS_6>
+ POP AF ;
+ CALL SYNTAX_Z <#SYNTAX_Z>
+ JR Z,I_RESTORE <#I_RESTORE> ; forward if checking syntax
+
+ PUSH AF ;
+ CALL FIND_INT <#FIND_INT>
+ POP DE ;
+ LD A,B ;
+ OR C ;
+ SCF ; Set Carry Flag
+ JR Z,I_CARRY <#I_CARRY> ; forward
+
+ POP HL ;
+ PUSH HL ;
+ AND A ;
+ SBC HL,BC ;
+
+mark_12F9:
+*I_CARRY:*
+ LD A,D ;
+ SBC A,$00 ;
+
+mark_12FC:
+*I_RESTORE:*
+ POP HL ;
+ POP DE ;
+ RET ;
+
+------------------------------------------------------------------------
+
+; THE *'DE,(DE+1)'* SUBROUTINE
+------------------------------------------------------------------------
+
+; INDEX and LOAD Z80 subroutine.
+; This emulates the 6800 processor instruction LDX 1,X which loads a two_byte
+; value from memory into the register indexing it. Often these are hardly worth
+; the bother of writing as subroutines and this one doesn't save any time or
+; memory. The timing and space overheads have to be offset against the ease of
+; writing and the greater program readability from using such toolkit routines.
+
+mark_12FF:
+*DE_DE_PLUS_ONE:*
+ EX DE,HL ; move index address into HL.
+ INC HL ; increment to address word.
+ LD E,(HL) ; pick up word low_order byte.
+ INC HL ; index high_order byte and
+ LD D,(HL) ; pick it up.
+ RET ; return with DE = word.
+
+------------------------------------------------------------------------
+
+; THE *'GET_HL_TIMES_DE'* SUBROUTINE
+------------------------------------------------------------------------
+
+;
+
+mark_1305:
+*GET_HL_TIMES_DE:*
+ CALL SYNTAX_Z <#SYNTAX_Z>
+ RET Z ;
+
+ PUSH BC ;
+ LD B,$10 ;
+ LD A,H ;
+ LD C,L ;
+ LD HL,$0000 ;
+
+mark_1311:
+*HL_LOOP:*
+ ADD HL,HL ;
+ JR C,HL_END <#HL_END> ; forward with carry
+
+ RL C ;
+ RLA ;
+ JR NC,HL_AGAIN <#HL_AGAIN> ; forward with no carry
+
+ ADD HL,DE ;
+
+mark_131A:
+*HL_END:*
+ JP C,REPORT_4 <#REPORT_4>
+
+mark_131D:
+*HL_AGAIN:*
+ DJNZ HL_LOOP <#HL_LOOP> ; loop back
+
+ POP BC ;
+ RET ; return.
+
+------------------------------------------------------------------------
+
+; THE *'LET'* SUBROUTINE
+------------------------------------------------------------------------
+
+;
+;
+
+mark_1321:
+*LET:*
+ LD HL,(DEST)
+ BIT 1,(IY+FLAGX-RAMBASE)
+ JR Z,L_EXISTS <#L_EXISTS> ; forward
+
+ LD BC,$0005 ;
+
+mark_132D:
+*L_EACH_CH:*
+ INC BC ;
+
+; check
+
+mark_132E:
+*L_NO_SP:*
+ INC HL ;
+ LD A,(HL) ;
+ AND A ;
+ JR Z,L_NO_SP <#L_NO_SP> ; back
+
+ CALL ALPHANUM <#ALPHANUM>
+ JR C,L_EACH_CH <#L_EACH_CH> ; back
+
+ CP ZX_DOLLAR ; is it '$' ?
+ JP Z,L_NEW_STR <#L_NEW_STR> ; forward if so
+
+
+ RST _BC_SPACES ; BC_SPACES
+ PUSH DE ;
+ LD HL,(DEST) ;
+ DEC DE ;
+ LD A,C ;
+ SUB $06 ;
+ LD B,A ;
+ LD A,$40 ;
+ JR Z,L_SINGLE <#L_SINGLE>
+
+mark_134B:
+*L_CHAR:*
+ INC HL ;
+ LD A,(HL) ;
+ AND A ; is it a space ?
+ JR Z,L_CHAR <#L_CHAR> ; back
+
+ INC DE ;
+ LD (DE),A ;
+ DJNZ L_CHAR <#L_CHAR> ; loop back
+
+ OR $80 ;
+ LD (DE),A ;
+ LD A,$80 ;
+
+mark_1359:
+*L_SINGLE:*
+ LD HL,(DEST) ;
+ XOR (HL) ;
+ POP HL ;
+ CALL L_FIRST <#L_FIRST>
+
+mark_1361:
+*L_NUMERIC:*
+ PUSH HL ;
+
+ RST _FP_CALC ;;
+ DEFB __delete ;;
+ DEFB __end_calc ;;
+
+ POP HL ;
+ LD BC,$0005 ;
+ AND A ;
+ SBC HL,BC ;
+ JR L_ENTER <#L_ENTER> ; forward
+
+; ___
+
+mark_136E:
+*L_EXISTS:*
+ BIT 6,(IY+FLAGS-RAMBASE) ; Numeric or string result?
+ JR Z,L_DELETE_STR <#L_DELETE_STR> ; forward
+
+ LD DE,$0006 ;
+ ADD HL,DE ;
+ JR L_NUMERIC <#L_NUMERIC> ; back
+
+; ___
+
+mark_137A:
+*L_DELETE_STR:*
+ LD HL,(DEST) ;
+ LD BC,(STRLEN) ;
+ BIT 0,(IY+FLAGX-RAMBASE)
+ JR NZ,L_ADD_STR <#L_ADD_STR> ; forward
+
+ LD A,B ;
+ OR C ;
+ RET Z ;
+
+ PUSH HL ;
+
+ RST _BC_SPACES
+ PUSH DE ;
+ PUSH BC ;
+ LD D,H ;
+ LD E,L ;
+ INC HL ;
+ LD (HL),$00 ;
+ LDDR ; Copy Bytes
+ PUSH HL ;
+ CALL STK_FETCH <#STK_FETCH>
+ POP HL ;
+ EX (SP),HL ;
+ AND A ;
+ SBC HL,BC ;
+ ADD HL,BC ;
+ JR NC,L_LENGTH <#L_LENGTH> ; forward
+
+ LD B,H ;
+ LD C,L ;
+
+mark_13A3:
+*L_LENGTH:*
+ EX (SP),HL ;
+ EX DE,HL ;
+ LD A,B ;
+ OR C ;
+ JR Z,L_IN_W_S <#L_IN_W_S> ; forward if zero
+
+ LDIR ; Copy Bytes
+
+mark_13AB:
+*L_IN_W_S:*
+ POP BC ;
+ POP DE ;
+ POP HL ;
+
+------------------------------------------------------------------------
+
+; 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.
+------------------------------------------------------------------------
+
+mark_13AE:
+*L_ENTER:*
+ EX DE,HL ;
+#if ORIGINAL
+#else
+*COND_MV*
+#endif
+ LD A,B ;
+ OR C ;
+ RET Z ;
+
+ PUSH DE ;
+ LDIR ; Copy Bytes
+ POP HL ;
+ RET ; return.
+------------------------------------------------------------------------
+
+mark_13B7:
+*L_ADD_STR:*
+ DEC HL ;
+ DEC HL ;
+ DEC HL ;
+ LD A,(HL) ;
+ PUSH HL ;
+ PUSH BC ;
+
+ CALL L_STRING <#L_STRING>
+
+ POP BC ;
+ POP HL ;
+ INC BC ;
+ INC BC ;
+ INC BC ;
+ JP RECLAIM_2 <#RECLAIM_2> ; jump back to exit via RECLAIM_2
+
+; ___
+
+mark_13C8:
+*L_NEW_STR:*
+ LD A,$60 ; prepare mask %01100000
+ LD HL,(DEST) ;
+ XOR (HL) ;
+
+------------------------------------------------------------------------
+
+; THE *'L_STRING'* SUBROUTINE
+------------------------------------------------------------------------
+
+;
+
+mark_13CE:
+*L_STRING:*
+ PUSH AF ;
+ CALL STK_FETCH <#STK_FETCH>
+ EX DE,HL ;
+ ADD HL,BC ;
+ PUSH HL ;
+ INC BC ;
+ INC BC ;
+ INC BC ;
+
+ RST _BC_SPACES
+ EX DE,HL ;
+ POP HL ;
+ DEC BC ;
+ DEC BC ;
+ PUSH BC ;
+ LDDR ; Copy Bytes
+ EX DE,HL ;
+ POP BC ;
+ DEC BC ;
+ LD (HL),B ;
+ DEC HL ;
+ LD (HL),C ;
+ POP AF ;
+
+mark_13E7:
+*L_FIRST:*
+ PUSH AF ;
+ CALL REC_V80 <#REC_V80>
+ POP AF ;
+ DEC HL ;
+ LD (HL),A ;
+ LD HL,(STKBOT) ; sv
+ LD (E_LINE),HL ; sv
+ DEC HL ;
+ LD (HL),$80 ;
+ RET ;
+
+------------------------------------------------------------------------
+
+; THE *'STK_FETCH'* SUBROUTINE
+------------------------------------------------------------------------
+
+; This routine fetches a five-byte value from the calculator stack
+; reducing the pointer to the end of the stack by five.
+; For a floating-point number the exponent is in A and the mantissa
+; is the thirty-two bits EDCB.
+; For strings, the start of the string is in DE and the length in BC.
+; A is unused.
+
+mark_13F8:
+*STK_FETCH:*
+ LD HL,(STKEND) ; load HL from system variable STKEND
+
+ DEC HL ;
+ LD B,(HL) ;
+ DEC HL ;
+ LD C,(HL) ;
+ DEC HL ;
+ LD D,(HL) ;
+ DEC HL ;
+ LD E,(HL) ;
+ DEC HL ;
+ LD A,(HL) ;
+
+ LD (STKEND),HL ; set system variable STKEND to lower value.
+ RET ; return.
+
+------------------------------------------------------------------------
+
+; THE *'DIM'* COMMAND ROUTINE
+------------------------------------------------------------------------
+
+; An array is created and initialized to zeros which is also the space
+; character on the ZX81.
+
+mark_1409:
+*DIM:*
+ CALL LOOK_VARS <#LOOK_VARS>
+
+mark_140C:
+*D_RPORT_C:*
+ JP NZ,REPORT_C <#REPORT_C>
+
+ CALL SYNTAX_Z <#SYNTAX_Z>
+ JR NZ,D_RUN <#D_RUN> ; forward
+
+ RES 6,C ;
+ CALL STK_VAR <#STK_VAR>
+ CALL CHECK_END <#CHECK_END>
+
+mark_141C:
+*D_RUN:*
+ JR C,D_LETTER <#D_LETTER> ; forward
+
+ PUSH BC ;
+ CALL NEXT_ONE <#NEXT_ONE>
+ CALL RECLAIM_2 <#RECLAIM_2>
+ POP BC ;
+
+mark_1426:
+*D_LETTER:*
+ SET 7,C ;
+ LD B,$00 ;
+ PUSH BC ;
+ LD HL,$0001 ;
+ BIT 6,C ;
+ JR NZ,D_SIZE <#D_SIZE> ; forward
+
+ LD L,$05 ;
+
+mark_1434:
+*D_SIZE:*
+ EX DE,HL ;
+
+mark_1435:
+*D_NO_LOOP:*
+ RST _NEXT_CHAR
+ LD H,$40 ;
+ CALL INT_EXP1 <#INT_EXP1>
+ JP C,REPORT_3 <#REPORT_3>
+
+ POP HL ;
+ PUSH BC ;
+ INC H ;
+ PUSH HL ;
+ LD H,B ;
+ LD L,C ;
+ CALL GET_HL_TIMES_DE <#GET_HL_TIMES_DE>
+ EX DE,HL ;
+
+ RST _GET_CHAR
+ CP ZX_COMMA ; $1A == 26
+ JR Z,D_NO_LOOP <#D_NO_LOOP> ; back
+
+ CP ZX_BRACKET_RIGHT; is it ')' ?
+ JR NZ,D_RPORT_C <#D_RPORT_C> ; back if not
+
+
+ RST _NEXT_CHAR
+ POP BC ;
+ LD A,C ;
+ LD L,B ;
+ LD H,$00 ;
+ INC HL ;
+ INC HL ;
+ ADD HL,HL ;
+ ADD HL,DE ;
+ JP C,REPORT_4 <#REPORT_4>
+
+ PUSH DE ;
+ PUSH BC ;
+ PUSH HL ;
+ LD B,H ;
+ LD C,L ;
+ LD HL,(E_LINE) ; sv
+ DEC HL ;
+ CALL MAKE_ROOM <#MAKE_ROOM>
+ INC HL ;
+ LD (HL),A ;
+ POP BC ;
+ DEC BC ;
+ DEC BC ;
+ DEC BC ;
+ INC HL ;
+ LD (HL),C ;
+ INC HL ;
+ LD (HL),B ;
+ POP AF ;
+ INC HL ;
+ LD (HL),A ;
+ LD H,D ;
+ LD L,E ;
+ DEC DE ;
+ LD (HL),0 ;
+ POP BC ;
+ LDDR ; Copy Bytes
+
+mark_147F:
+*DIM_SIZES:*
+ POP BC ;
+ LD (HL),B ;
+ DEC HL ;
+ LD (HL),C ;
+ DEC HL ;
+ DEC A ;
+ JR NZ,DIM_SIZES <#DIM_SIZES> ; back
+
+ RET ; return.
+
+------------------------------------------------------------------------
+
+; THE *'RESERVE'* ROUTINE
+------------------------------------------------------------------------
+
+;
+;
+
+mark_1488:
+*RESERVE:*
+ LD HL,(STKBOT) ; address STKBOT
+ DEC HL ; now last byte of workspace
+ CALL MAKE_ROOM <#MAKE_ROOM>
+ INC HL ;
+ INC HL ;
+ POP BC ;
+ LD (E_LINE),BC ; sv
+ POP BC ;
+ EX DE,HL ;
+ INC HL ;
+ RET ;
+
+------------------------------------------------------------------------
+
+; THE *'CLEAR'* COMMAND ROUTINE
+------------------------------------------------------------------------
+
+;
+;
+
+mark_149A:
+*CLEAR:*
+ LD HL,(VARS) ; sv
+ LD (HL),$80 ;
+ INC HL ;
+ LD (E_LINE),HL ; sv
+
+------------------------------------------------------------------------
+
+; THE *'X_TEMP'* SUBROUTINE
+------------------------------------------------------------------------
+
+;
+;
+
+mark_14A3:
+*X_TEMP:*
+ LD HL,(E_LINE) ; sv
+
+------------------------------------------------------------------------
+
+; THE *'SET_STK'* ROUTINES
+------------------------------------------------------------------------
+
+;
+;
+
+mark_14A6:
+*SET_STK_B:*
+ LD (STKBOT),HL ; sv
+
+;
+
+mark_14A9:
+*SET_STK_E:*
+ LD (STKEND),HL ; sv
+ RET ;
+
+------------------------------------------------------------------------
+
+; THE *'CURSOR_IN'* ROUTINE
+------------------------------------------------------------------------
+
+; This routine is called to set the edit line to the minimum cursor/newline
+; and to set STKEND, the start of free space, at the next position.
+
+mark_14AD:
+*CURSOR_IN:*
+ LD HL,(E_LINE) ; fetch start of edit line
+ LD (HL),ZX_CURSOR ; insert cursor character
+
+ INC HL ; point to next location.
+ LD (HL),ZX_NEWLINE ; insert NEWLINE character
+ INC HL ; point to next free location.
+
+ LD (IY+DF_SZ-RAMBASE),2 ; set lower screen display file size
+
+ JR SET_STK_B <#SET_STK_B> ; exit via SET_STK_B above
+
+------------------------------------------------------------------------
+
+; THE *'SET_MIN'* SUBROUTINE
+------------------------------------------------------------------------
+
+;
+;
+
+mark_14BC:
+*SET_MIN:*
+ LD HL,$405D ; normal location of calculator's memory area
+ LD (MEM),HL ; update system variable MEM
+ LD HL,(STKBOT) ;
+ JR SET_STK_E <#SET_STK_E> ; back
+
+
+------------------------------------------------------------------------
+
+; THE *'RECLAIM THE END_MARKER'* ROUTINE
+------------------------------------------------------------------------
+
+
+mark_14C7:
+*REC_V80:*
+ LD DE,(E_LINE) ; sv
+ JP RECLAIM_1 <#RECLAIM_1>
+
+------------------------------------------------------------------------
+
+; THE *'ALPHA'* SUBROUTINE
+------------------------------------------------------------------------
+
+
+mark_14CE:
+*ALPHA:*
+ CP ZX_A ; $26
+ JR ALPHA_2 <#ALPHA_2> ; skip forward
+
+
+------------------------------------------------------------------------
+
+; THE *'ALPHANUM'* SUBROUTINE
+------------------------------------------------------------------------
+
+
+mark_14D2:
+*ALPHANUM:*
+ CP ZX_0 ;
+
+
+mark_14D4:
+*ALPHA_2:*
+ CCF ; Complement Carry Flag
+ RET NC ;
+
+ CP $40 ;
+ RET ;
+
+
+------------------------------------------------------------------------
+
+; THE *'DECIMAL TO FLOATING POINT'* SUBROUTINE
+------------------------------------------------------------------------
+
+;
+
+mark_14D9:
+*DEC_TO_FP:*
+ CALL INT_TO_FP <#INT_TO_FP> ; gets first part
+ CP ZX_PERIOD ; is character a '.' ?
+ JR NZ,E_FORMAT <#E_FORMAT> ; forward if not
+
+
+ RST _FP_CALC ;;
+ DEFB __stk_one ;;
+ DEFB __st_mem_0 ;;
+ DEFB __delete ;;
+ DEFB __end_calc ;;
+
+
+mark_14E5:
+
+*NXT_DGT_1:*
+ RST _NEXT_CHAR
+ CALL STK_DIGIT <#STK_DIGIT>
+ JR C,E_FORMAT <#E_FORMAT> ; forward
+
+
+ RST _FP_CALC ;;
+ DEFB __get_mem_0 ;;
+ DEFB __stk_ten ;;
+#if ORIGINAL
+ DEFB __division ;
+ DEFB $C0 ;;st-mem-0
+ DEFB __multiply ;;
+#else
+ DEFB $04 ;;+multiply
+ DEFB $C0 ;;st-mem-0
+ DEFB $05 ;;+division
+#endif
+ DEFB __addition ;;
+ DEFB __end_calc ;;
+
+ JR NXT_DGT_1 <#NXT_DGT_1> ; loop back till exhausted
+
+; ___
+
+mark_14F5:
+*E_FORMAT:*
+ CP ZX_E ; is character 'E' ?
+ RET NZ ; return if not
+
+ LD (IY+MEM_0_1st-RAMBASE),$FF ; initialize sv MEM_0_1st to $FF TRUE
+
+ RST _NEXT_CHAR
+ CP ZX_PLUS ; is character a '+' ?
+ JR Z,SIGN_DONE <#SIGN_DONE> ; forward if so
+
+ CP ZX_MINUS ; is it a '-' ?
+ JR NZ,ST_E_PART <#ST_E_PART> ; forward if not
+
+ INC (IY+MEM_0_1st-RAMBASE) ; sv MEM_0_1st change to FALSE
+
+mark_1508:
+*SIGN_DONE:*
+ RST _NEXT_CHAR
+
+mark_1509:
+*ST_E_PART:*
+ CALL INT_TO_FP <#INT_TO_FP>
+
+ RST _FP_CALC ;; m, e.
+ DEFB __get_mem_0 ;; m, e, (1/0) TRUE/FALSE
+ DEFB __jump_true ;;
+ DEFB E_POSTVE <#E_POSTVE> - $ ;;
+ DEFB __negate ;; m, _e
+
+mark_1511:
+*E_POSTVE:*
+ DEFB __e_to_fp ;; x.
+ DEFB __end_calc ;; x.
+
+ RET ; return.
+
+
+------------------------------------------------------------------------
+
+; THE *'STK_DIGIT'* SUBROUTINE
+------------------------------------------------------------------------
+
+;
+
+mark_1514:
+*STK_DIGIT:*
+ CP ZX_0 ;
+ RET C ;
+
+ CP ZX_A ; $26
+ CCF ; Complement Carry Flag
+ RET C ;
+
+ SUB ZX_0 ;
+
+------------------------------------------------------------------------
+
+; THE *'STACK_A'* SUBROUTINE
+------------------------------------------------------------------------
+
+;
+
+
+mark_151D:
+*STACK_A:*
+ LD C,A ;
+ LD B,0 ;
+
+------------------------------------------------------------------------
+
+; THE *'STACK_BC'* SUBROUTINE
+------------------------------------------------------------------------
+
+; The ZX81 does not have an integer number format so the BC register contents
+; must be converted to their full floating-point form.
+
+mark_1520:
+*STACK_BC:*
+ LD IY,ERR_NR ; re-initialize the system variables pointer.
+ PUSH BC ; save the integer value.
+
+; now stack zero, five zero bytes as a starting point.
+
+ RST _FP_CALC ;;
+ DEFB __stk_zero ;; 0.
+ DEFB __end_calc ;;
+
+ POP BC ; restore integer value.
+
+ LD (HL),$91 ; place $91 in exponent 65536.
+ ; this is the maximum possible value
+
+ LD A,B ; fetch hi-byte.
+ AND A ; test for zero.
+ JR NZ,STK_BC_2 <#STK_BC_2> ; forward if not zero
+
+ LD (HL),A ; else make exponent zero again
+ OR C ; test lo-byte
+ RET Z ; return if BC was zero - done.
+
+; else there has to be a set bit if only the value one.
+
+ LD B,C ; save C in B.
+ LD C,(HL) ; fetch zero to C
+ LD (HL),$89 ; make exponent $89 256.
+
+mark_1536:
+*STK_BC_2:*
+ DEC (HL) ; decrement exponent - halving number
+ SLA C ; C<-76543210<-0
+ RL B ; C<-76543210<-C
+ JR NC,STK_BC_2 <#STK_BC_2> ; loop back if no carry
+
+ SRL B ; 0->76543210->C
+ RR C ; C->76543210->C
+
+ INC HL ; address first byte of mantissa
+ LD (HL),B ; insert B
+ INC HL ; address second byte of mantissa
+ LD (HL),C ; insert C
+
+ DEC HL ; point to the
+ DEC HL ; exponent again
+ RET ; return.
+
+------------------------------------------------------------------------
+
+; THE *'INTEGER TO FLOATING POINT'* SUBROUTINE
+------------------------------------------------------------------------
+
+;
+;
+
+mark_1548:
+*INT_TO_FP:*
+ PUSH AF ;
+
+ RST _FP_CALC ;;
+ DEFB __stk_zero ;;
+ DEFB __end_calc ;;
+
+ POP AF ;
+
+mark_154D:
+*NXT_DGT_2:*
+ CALL STK_DIGIT <#STK_DIGIT>
+ RET C ;
+
+ RST _FP_CALC ;;
+ DEFB __exchange ;;
+ DEFB __stk_ten ;;
+ DEFB __multiply ;;
+ DEFB __addition ;;
+ DEFB __end_calc ;;
+
+ RST _NEXT_CHAR
+ JR NXT_DGT_2 <#NXT_DGT_2>
+
+
+------------------------------------------------------------------------
+
+; THE *'E_FORMAT TO FLOATING POINT'* SUBROUTINE
+------------------------------------------------------------------------
+
+; (Offset $38: 'e_to_fp')
+; invoked from DEC_TO_FP and PRINT_FP.
+; e.g. 2.3E4 is 23000.
+; This subroutine evaluates xEm where m is a positive or negative integer.
+; At a simple level x is multiplied by ten for every unit of m.
+; If the decimal exponent m is negative then x is divided by ten for each unit.
+; A short-cut is taken if the exponent is greater than seven and in this
+; case the exponent is reduced by seven and the value is multiplied or divided
+; by ten million.
+; Note. for the ZX Spectrum an even cleverer method was adopted which involved
+; shifting the bits out of the exponent so the result was achieved with six
+; shifts at most. The routine below had to be completely re-written mostly
+; in Z80 machine code.
+; Although no longer operable, the calculator literal was retained for old
+; times sake, the routine being invoked directly from a machine code CALL.
+;
+; On entry in the ZX81, m, the exponent, is the 'last value', and the
+; floating-point decimal mantissa is beneath it.
+
+
+mark_155A:
+*e_to_fp:*
+ RST _FP_CALC ;; x, m.
+ DEFB __duplicate ;; x, m, m.
+ DEFB __less_0 ;; x, m, (1/0).
+ DEFB __st_mem_0 ;; x, m, (1/0).
+ DEFB __delete ;; x, m.
+ DEFB __abs ;; x, +m.
+
+mark_1560:
+*E_LOOP:*
+ DEFB __stk_one ;; x, m,1.
+ DEFB __subtract ;; x, m-1.
+ DEFB __duplicate ;; x, m-1,m-1.
+ DEFB __less_0 ;; x, m-1, (1/0).
+ DEFB __jump_true ;; x, m-1.
+ DEFB E_END <#E_END> - $ ;; x, m-1.
+
+ DEFB __duplicate ;; x, m-1, m-1.
+ DEFB __stk_data ;;
+ DEFB $33 ;;Exponent: $83, Bytes: 1
+
+ DEFB $40 ;;(+00,+00,+00) x, m-1, m-1, 6.
+ DEFB __subtract ;; x, m-1, m-7.
+ DEFB __duplicate ;; x, m-1, m-7, m-7.
+ DEFB __less_0 ;; x, m-1, m-7, (1/0).
+ DEFB __jump_true ;; x, m-1, m-7.
+ DEFB E_LOW <#E_LOW> - $ ;;
+
+; but if exponent m is higher than 7 do a bigger chunk.
+; multiplying (or dividing if negative) by 10 million - 1e7.
+
+ DEFB __exchange ;; x, m-7, m-1.
+ DEFB __delete ;; x, m-7.
+ DEFB __exchange ;; m-7, x.
+ DEFB __stk_data ;;
+ DEFB $80 ;;Bytes: 3
+ DEFB $48 ;;Exponent $98
+ DEFB $18,$96,$80 ;;(+00) m-7, x, 10,000,000 (=f)
+ DEFB __jump ;;
+ DEFB E_CHUNK <#E_CHUNK> - $ ;;
+
+; ___
+
+mark_157A:
+*E_LOW:*
+ DEFB __delete ;; x, m-1.
+ DEFB __exchange ;; m-1, x.
+ DEFB __stk_ten ;; m-1, x, 10 (=f).
+
+mark_157D:
+*E_CHUNK:*
+ DEFB __get_mem_0 ;; m-1, x, f, (1/0)
+ DEFB __jump_true ;; m-1, x, f
+ DEFB E_DIVSN <#E_DIVSN> - $ ;;
+
+ DEFB __multiply ;; m-1, x*f.
+ DEFB __jump ;;
+ DEFB E_SWAP <#E_SWAP> - $ ;;
+
+; ___
+
+mark_1583:
+*E_DIVSN:*
+ DEFB __division ;; m-1, x/f (= new x).
+
+mark_1584:
+*E_SWAP:*
+ DEFB __exchange ;; x, m-1 (= new m).
+ DEFB __jump ;; x, m.
+ DEFB E_LOOP <#E_LOOP> - $ ;;
+
+; ___
+
+mark_1587:
+*E_END:*
+ DEFB __delete ;; x. (-1)
+ DEFB __end_calc ;; x.
+
+ RET ; return.
+
+------------------------------------------------------------------------
+
+; THE *'FLOATING-POINT TO BC'* SUBROUTINE
+------------------------------------------------------------------------
+
+; The floating-point form on the calculator stack is compressed directly into
+; the BC register rounding up if necessary.
+; Valid range is 0 to 65535.4999
+
+mark_158A:
+*FP_TO_BC:*
+ CALL STK_FETCH <#STK_FETCH> ; exponent to A
+ ; mantissa to EDCB.
+ AND A ; test for value zero.
+ JR NZ,FPBC_NZRO <#FPBC_NZRO> ; forward if not
+
+; else value is zero
+
+ LD B,A ; zero to B
+ LD C,A ; also to C
+ PUSH AF ; save the flags on machine stack
+ JR FPBC_END <#FPBC_END> ; forward
+
+; ___
+
+; EDCB => BCE
+
+mark_1595:
+*FPBC_NZRO:*
+ LD B,E ; transfer the mantissa from EDCB
+ LD E,C ; to BCE. Bit 7 of E is the 17th bit which
+ LD C,D ; will be significant for rounding if the
+ ; number is already normalized.
+
+ SUB $91 ; subtract 65536
+ CCF ; complement carry flag
+ BIT 7,B ; test sign bit
+ PUSH AF ; push the result
+
+ SET 7,B ; set the implied bit
+ JR C,FPBC_END <#FPBC_END> ; forward with carry from SUB/CCF
+ ; number is too big.
+
+ INC A ; increment the exponent and
+ NEG ; negate to make range $00 - $0F
+
+ CP $08 ; test if one or two bytes
+ JR C,BIG_INT <#BIG_INT> ; forward with two
+
+ LD E,C ; shift mantissa
+ LD C,B ; 8 places right
+ LD B,$00 ; insert a zero in B
+ SUB $08 ; reduce exponent by eight
+
+mark_15AF:
+*BIG_INT:*
+ AND A ; test the exponent
+ LD D,A ; save exponent in D.
+
+ LD A,E ; fractional bits to A
+ RLCA ; rotate most significant bit to carry for
+ ; rounding of an already normal number.
+
+ JR Z,EXP_ZERO <#EXP_ZERO> ; forward if exponent zero
+ ; the number is normalized
+
+mark_15B5:
+*FPBC_NORM:*
+ SRL B ; 0->76543210->C
+ RR C ; C->76543210->C
+
+ DEC D ; decrement exponent
+
+ JR NZ,FPBC_NORM <#FPBC_NORM> ; loop back till zero
+
+mark_15BC:
+*EXP_ZERO:*
+ JR NC,FPBC_END <#FPBC_END> ; forward without carry to NO_ROUND ???
+
+ INC BC ; round up.
+ LD A,B ; test result
+ OR C ; for zero
+ JR NZ,FPBC_END <#FPBC_END> ; forward if not to GRE_ZERO ???
+
+ POP AF ; restore sign flag
+ SCF ; set carry flag to indicate overflow
+ PUSH AF ; save combined flags again
+
+mark_15C6:
+*FPBC_END:*
+ PUSH BC ; save BC value
+
+; set HL and DE to calculator stack pointers.
+
+ RST _FP_CALC ;;
+ DEFB __end_calc ;;
+
+
+ POP BC ; restore BC value
+ POP AF ; restore flags
+ LD A,C ; copy low byte to A also.
+ RET ; return
+
+------------------------------------------------------------------------
+
+; THE *'FLOATING-POINT TO A'* SUBROUTINE
+------------------------------------------------------------------------
+
+;
+;
+
+mark_15CD:
+*FP_TO_A:*
+ CALL FP_TO_BC <#FP_TO_BC>
+ RET C ;
+
+ PUSH AF ;
+ DEC B ;
+ INC B ;
+ JR Z,FP_A_END <#FP_A_END> ; forward if in range
+
+ POP AF ; fetch result
+ SCF ; set carry flag signaling overflow
+ RET ; return
+
+mark_15D9:
+*FP_A_END:*
+ POP AF ;
+ RET ;
+
+
+------------------------------------------------------------------------
+
+; THE *'PRINT A FLOATING-POINT NUMBER'* SUBROUTINE
+------------------------------------------------------------------------
+
+; prints 'last value' x on calculator stack.
+; There are a wide variety of formats see Chapter 4.
+; e.g.
+; PI prints as 3.1415927
+; .123 prints as 0.123
+; .0123 prints as .0123
+; 999999999999 prints as 1000000000000
+; 9876543210123 prints as 9876543200000
+
+; Begin by isolating zero and just printing the '0' character
+; for that case. For negative numbers print a leading '-' and
+; then form the absolute value of x.
+
+mark_15DB:
+*PRINT_FP:*
+ RST _FP_CALC ;; x.
+ DEFB __duplicate ;; x, x.
+ DEFB __less_0 ;; x, (1/0).
+ DEFB __jump_true ;;
+ DEFB PF_NEGTVE <#PF_NEGTVE> - $ ;; x.
+
+ DEFB __duplicate ;; x, x
+ DEFB __greater_0 ;; x, (1/0).
+ DEFB __jump_true ;;
+ DEFB PF_POSTVE <#PF_POSTVE> - $ ;; x.
+
+ DEFB __delete ;; .
+ DEFB __end_calc ;; .
+
+ LD A,ZX_0 ; load accumulator with character '0'
+
+ RST _PRINT_A
+ RET ; return. >>
+
+; ___
+
+mark_15EA:
+*PF_NEGTVE:*
+ DEFB __abs ;; +x.
+ DEFB __end_calc ;; x.
+
+ LD A,ZX_MINUS ; load accumulator with '-'
+
+ RST _PRINT_A
+
+ RST _FP_CALC ;; x.
+
+mark_15F0:
+*PF_POSTVE:*
+ DEFB __end_calc ;; x.
+
+; register HL addresses the exponent of the floating-point value.
+; if positive, and point floats to left, then bit 7 is set.
+
+ LD A,(HL) ; pick up the exponent byte
+ CALL STACK_A <#STACK_A> ; places on calculator stack.
+
+; now calculate roughly the number of digits, n, before the decimal point by
+; subtracting a half from true exponent and multiplying by log to
+; the base 10 of 2.
+; The true number could be one higher than n, the integer result.
+
+ RST _FP_CALC ;; x, e.
+ DEFB __stk_data ;;
+ DEFB $78 ;;Exponent: $88, Bytes: 2
+ DEFB $00,$80 ;;(+00,+00) x, e, 128.5.
+ DEFB __subtract ;; x, e -.5.
+ DEFB __stk_data ;;
+ DEFB $EF ;;Exponent: $7F, Bytes: 4
+ DEFB $1A,$20,$9A,$85 ;; .30103 (log10 2)
+ DEFB __multiply ;; x,
+ DEFB __int ;;
+ DEFB __st_mem_1 ;; x, n.
+
+
+ DEFB __stk_data ;;
+ DEFB $34 ;;Exponent: $84, Bytes: 1
+ DEFB $00 ;;(+00,+00,+00) x, n, 8.
+
+ DEFB __subtract ;; x, n-8.
+ DEFB __negate ;; x, 8-n.
+ DEFB __e_to_fp ;; x * (10^n)
+
+; finally the 8 or 9 digit decimal is rounded.
+; a ten-digit integer can arise in the case of, say, 999999999.5
+; which gives 1000000000.
+
+ DEFB __stk_half ;;
+ DEFB __addition ;;
+ DEFB __int ;; i.
+ DEFB __end_calc ;;
+
+; If there were 8 digits then final rounding will take place on the calculator
+; stack above and the next two instructions insert a masked zero so that
+; no further rounding occurs. If the result is a 9 digit integer then
+; rounding takes place within the buffer.
+
+ LD HL,$406B ; address system variable MEM_2_5th
+ ; which could be the 'ninth' digit.
+ LD (HL),$90 ; insert the value $90 10010000
+
+; now starting from lowest digit lay down the 8, 9 or 10 digit integer
+; which represents the significant portion of the number
+; e.g. PI will be the nine-digit integer 314159265
+
+ LD B,10 ; count is ten digits.
+
+mark_1615:
+*PF_LOOP:*
+ INC HL ; increase pointer
+
+ PUSH HL ; preserve buffer address.
+ PUSH BC ; preserve counter.
+
+ RST _FP_CALC ;; i.
+ DEFB __stk_ten ;; i, 10.
+ DEFB __n_mod_m ;; i mod 10, i/10
+ DEFB __exchange ;; i/10, remainder.
+ DEFB __end_calc ;;
+
+ CALL FP_TO_A <#FP_TO_A> ; $00-$09
+
+ OR $90 ; make left hand nibble 9
+
+ POP BC ; restore counter
+ POP HL ; restore buffer address.
+
+ LD (HL),A ; insert masked digit in buffer.
+ DJNZ PF_LOOP <#PF_LOOP> ; loop back for all ten
+
+; the most significant digit will be last but if the number is exhausted then
+; the last one or two positions will contain zero ($90).
+
+; e.g. for 'one' we have zero as estimate of leading digits.
+; 1*10^8 100000000 as integer value
+; 90 90 90 90 90 90 90 90 91 90 as buffer mem3/mem4 contents.
+
+
+ INC HL ; advance pointer to one past buffer
+ LD BC,$0008 ; set C to 8 ( B is already zero )
+ PUSH HL ; save pointer.
+
+mark_162C:
+*PF_NULL:*
+ DEC HL ; decrease pointer
+ LD A,(HL) ; fetch masked digit
+ CP $90 ; is it a leading zero ?
+ JR Z,PF_NULL <#PF_NULL> ; loop back if so
+
+; at this point a significant digit has been found. carry is reset.
+
+ SBC HL,BC ; subtract eight from the address.
+ PUSH HL ; ** save this pointer too
+ LD A,(HL) ; fetch addressed byte
+ ADD A,$6B ; add $6B - forcing a round up ripple
+ ; if $95 or over.
+ PUSH AF ; save the carry result.
+
+; now enter a loop to round the number. After rounding has been considered
+; a zero that has arisen from rounding or that was present at that position
+; originally is changed from $90 to $80.
+
+mark_1639:
+*PF_RND_LP:*
+ POP AF ; retrieve carry from machine stack.
+ INC HL ; increment address
+ LD A,(HL) ; fetch new byte
+ ADC A,0 ; add in any carry
+
+ DAA ; decimal adjust accumulator
+ ; carry will ripple through the '9'
+
+ PUSH AF ; save carry on machine stack.
+ AND $0F ; isolate character 0 - 9 AND set zero flag
+ ; if zero.
+ LD (HL),A ; place back in location.
+ SET 7,(HL) ; set bit 7 to show printable.
+ ; but not if trailing zero after decimal point.
+ JR Z,PF_RND_LP <#PF_RND_LP> ; back if a zero
+ ; to consider further rounding and/or trailing
+ ; zero identification.
+
+ POP AF ; balance stack
+ POP HL ; ** retrieve lower pointer
+
+; now insert 6 trailing zeros which are printed if before the decimal point
+; but mark the end of printing if after decimal point.
+; e.g. 9876543210123 is printed as 9876543200000
+; 123.456001 is printed as 123.456
+
+ LD B,6 ; the count is six.
+
+mark_164B:
+*PF_ZERO_6:*
+ LD (HL),$80 ; insert a masked zero
+ DEC HL ; decrease pointer.
+ DJNZ PF_ZERO_6 <#PF_ZERO_6> ; loop back for all six
+
+; n-mod-m reduced the number to zero and this is now deleted from the calculator
+; stack before fetching the original estimate of leading digits.
+
+
+ RST _FP_CALC ;; 0.
+ DEFB __delete ;; .
+ DEFB __get_mem_1 ;; n.
+ DEFB __end_calc ;; n.
+
+ CALL FP_TO_A <#FP_TO_A>
+ JR Z,PF_POS <#PF_POS> ; skip forward if positive
+
+ NEG ; negate makes positive
+
+mark_165B:
+*PF_POS:*
+ LD E,A ; transfer count of digits to E
+ INC E ; increment twice
+ INC E ;
+ POP HL ; * retrieve pointer to one past buffer.
+
+mark_165F:
+*GET_FIRST:*
+ DEC HL ; decrement address.
+ DEC E ; decrement digit counter.
+ LD A,(HL) ; fetch masked byte.
+ AND $0F ; isolate right-hand nibble.
+ JR Z,GET_FIRST <#GET_FIRST> ; back with leading zero
+
+; now determine if E-format printing is needed
+
+ LD A,E ; transfer now accurate number count to A.
+ SUB 5 ; subtract five
+ CP 8 ; compare with 8 as maximum digits is 13.
+ JP P,PF_E_FMT <#PF_E_FMT> ; forward if positive to PF_E_FMT
+
+ CP $F6 ; test for more than four zeros after point.
+ JP M,PF_E_FMT <#PF_E_FMT> ; forward if so to PF_E_FMT
+
+ ADD A,6 ; test for zero leading digits, e.g. 0.5
+ JR Z,PF_ZERO_1 <#PF_ZERO_1> ; forward if so to PF_ZERO_1
+
+ JP M,PF_ZEROS <#PF_ZEROS> ; forward if more than one zero to PF_ZEROS
+
+; else digits before the decimal point are to be printed
+
+ LD B,A ; count of leading characters to B.
+
+mark_167B:
+*PF_NIB_LP:*
+ CALL PF_NIBBLE <#PF_NIBBLE>
+ DJNZ PF_NIB_LP <#PF_NIB_LP> ; loop back for counted numbers
+
+ JR PF_DC_OUT <#PF_DC_OUT> ; forward to consider decimal part to PF_DC_OUT
+
+; ___
+
+mark_1682:
+*PF_E_FMT:*
+ LD B,E ; count to B
+ CALL PF_NIBBLE <#PF_NIBBLE> ; prints one digit.
+ CALL PF_DC_OUT <#PF_DC_OUT> ; considers fractional part.
+
+ LD A,ZX_E ;
+ RST _PRINT_A
+
+ LD A,B ; transfer exponent to A
+ AND A ; test the sign.
+ JP P,PF_E_POS <#PF_E_POS> ; forward if positive to PF_E_POS
+
+ NEG ; negate the negative exponent.
+ LD B,A ; save positive exponent in B.
+
+ LD A,ZX_MINUS ;
+ JR PF_E_SIGN <#PF_E_SIGN> ; skip forward to PF_E_SIGN
+
+; ___
+
+mark_1698:
+*PF_E_POS:*
+ LD A,ZX_PLUS ;
+
+mark_169A:
+*PF_E_SIGN:*
+ RST _PRINT_A
+
+; now convert the integer exponent in B to two characters.
+; it will be less than 99.
+
+ LD A,B ; fetch positive exponent.
+ LD B,$FF ; initialize left hand digit to minus one.
+
+mark_169E:
+*PF_E_TENS:*
+ INC B ; increment ten count
+ SUB 10 ; subtract ten from exponent
+ JR NC,PF_E_TENS <#PF_E_TENS> ; loop back if greater than ten
+
+ ADD A,10 ; reverse last subtraction
+ LD C,A ; transfer remainder to C
+
+ LD A,B ; transfer ten value to A.
+ AND A ; test for zero.
+ JR Z,PF_E_LOW <#PF_E_LOW> ; skip forward if so to PF_E_LOW
+
+ CALL OUT_CODE <#OUT_CODE> ; prints as digit '1' - '9'
+
+mark_16AD:
+*PF_E_LOW:*
+ LD A,C ; low byte to A
+ CALL OUT_CODE <#OUT_CODE> ; prints final digit of the
+ ; exponent.
+ RET ; return. >>
+
+------------------------------------------------------------------------
+
+; 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
+------------------------------------------------------------------------
+
+mark_16B2:
+*PF_ZEROS:*
+ NEG ; negate makes number positive 1 to 4.
+ LD B,A ; zero count to B.
+
+ LD A,ZX_PERIOD ; prepare character '.'
+ RST _PRINT_A
+
+
+#if ORIGINAL
+ LD A,ZX_0 ; prepare a '0'
+*PFZROLP*
+ RST _PRINT_A
+ DJNZ PFZROLP <#PFZROLP> ; obsolete loop back to PFZROLP
+#else
+*PF_ZRO_LP*
+ LD A,ZX_0 ; prepare a '0' in the accumulator each time.
+ RST _PRINT_A
+ DJNZ PF_ZRO_LP <#PF_ZRO_LP> ;+ New loop back to PF-ZRO-LP
+#endif
+
+ JR PF_FRAC_LP <#PF_FRAC_LP> ; forward
+
+; there is a need to print a leading zero e.g. 0.1 but not with .01
+
+mark_16BF:
+*PF_ZERO_1:*
+ LD A,ZX_0 ; prepare character '0'.
+ RST _PRINT_A
+
+; this subroutine considers the decimal point and any trailing digits.
+; if the next character is a marked zero, $80, then nothing more to print.
+
+mark_16C2:
+*PF_DC_OUT:*
+ DEC (HL) ; decrement addressed character
+ INC (HL) ; increment it again
+ RET PE ; return with overflow (was 128) >>
+ ; as no fractional part
+
+; else there is a fractional part so print the decimal point.
+
+ LD A,ZX_PERIOD ; prepare character '.'
+ RST _PRINT_A
+
+; now enter a loop to print trailing digits
+
+mark_16C8:
+*PF_FRAC_LP:*
+ DEC (HL) ; test for a marked zero.
+ INC (HL) ;
+ RET PE ; return when digits exhausted >>
+
+ CALL PF_NIBBLE <#PF_NIBBLE>
+ JR PF_FRAC_LP <#PF_FRAC_LP> ; back for all fractional digits
+
+; ___
+
+; subroutine to print right-hand nibble
+
+mark_16D0:
+*PF_NIBBLE:*
+ LD A,(HL) ; fetch addressed byte
+ AND $0F ; mask off lower 4 bits
+ CALL OUT_CODE <#OUT_CODE>
+ DEC HL ; decrement pointer.
+ RET ; return.
+
+
+------------------------------------------------------------------------
+
+; THE *'PREPARE TO ADD'* SUBROUTINE
+------------------------------------------------------------------------
+
+; This routine is called twice to prepare each floating point number for
+; addition, in situ, on the calculator stack.
+; The exponent is picked up from the first byte which is then cleared to act
+; as a sign byte and accept any overflow.
+; If the exponent is zero then the number is zero and an early return is made.
+; The now redundant sign bit of the mantissa is set and if the number is
+; negative then all five bytes of the number are twos-complemented to prepare
+; the number for addition.
+; On the second invocation the exponent of the first number is in B.
+
+
+mark_16D8:
+*PREP_ADD:*
+ LD A,(HL) ; fetch exponent.
+ LD (HL),0 ; make this byte zero to take any overflow and
+ ; default to positive.
+ AND A ; test stored exponent for zero.
+ RET Z ; return with zero flag set if number is zero.
+
+ INC HL ; point to first byte of mantissa.
+ BIT 7,(HL) ; test the sign bit.
+ SET 7,(HL) ; set it to its implied state.
+ DEC HL ; set pointer to first byte again.
+ RET Z ; return if bit indicated number is positive.>>
+
+; if negative then all five bytes are twos complemented starting at LSB.
+
+ PUSH BC ; save B register contents.
+ LD BC,$0005 ; set BC to five.
+ ADD HL,BC ; point to location after 5th byte.
+ LD B,C ; set the B counter to five.
+ LD C,A ; store original exponent in C.
+ SCF ; set carry flag so that one is added.
+
+; now enter a loop to twos_complement the number.
+; The first of the five bytes becomes $FF to denote a negative number.
+
+mark_16EC:
+*NEG_BYTE:*
+ DEC HL ; point to first or more significant byte.
+ LD A,(HL) ; fetch to accumulator.
+ CPL ; complement.
+ ADC A,0 ; add in initial carry or any subsequent carry.
+ LD (HL),A ; place number back.
+ DJNZ NEG_BYTE <#NEG_BYTE> ; loop back five times
+
+ LD A,C ; restore the exponent to accumulator.
+ POP BC ; restore B register contents.
+
+ RET ; return.
+
+------------------------------------------------------------------------
+
+; THE *'FETCH TWO NUMBERS'* SUBROUTINE
+------------------------------------------------------------------------
+
+; This routine is used by addition, multiplication and division to fetch
+; the two five_byte numbers addressed by HL and DE from the calculator stack
+; into the Z80 registers.
+; The HL register may no longer point to the first of the two numbers.
+; Since the 32-bit addition operation is accomplished using two Z80 16-bit
+; instructions, it is important that the lower two bytes of each mantissa are
+; in one set of registers and the other bytes all in the alternate set.
+;
+; In: HL = highest number, DE= lowest number
+;
+; : alt':
+; :
+; Out:
+; :H,B-C:C,B: num1
+; :L,D-E:D-E: num2
+
+mark_16F7:
+*FETCH_TWO:*
+ PUSH HL ; save HL
+ PUSH AF ; save A - result sign when used from division.
+
+ LD C,(HL) ;
+ INC HL ;
+ LD B,(HL) ;
+ LD (HL),A ; insert sign when used from multiplication.
+ INC HL ;
+ LD A,C ; m1
+ LD C,(HL) ;
+ PUSH BC ; PUSH m2 m3
+
+ INC HL ;
+ LD C,(HL) ; m4
+ INC HL ;
+ LD B,(HL) ; m5 BC holds m5 m4
+
+ EX DE,HL ; make HL point to start of second number.
+
+ LD D,A ; m1
+ LD E,(HL) ;
+ PUSH DE ; PUSH m1 n1
+
+ INC HL ;
+ LD D,(HL) ;
+ INC HL ;
+ LD E,(HL) ;
+ PUSH DE ; PUSH n2 n3
+
+ EXX ; - - - - - - -
+
+ POP DE ; POP n2 n3
+ POP HL ; POP m1 n1
+ POP BC ; POP m2 m3
+
+ EXX ; - - - - - - -
+
+ INC HL ;
+ LD D,(HL) ;
+ INC HL ;
+ LD E,(HL) ; DE holds n4 n5
+
+ POP AF ; restore saved
+ POP HL ; registers.
+ RET ; return.
+
+------------------------------------------------------------------------
+
+; THE *'SHIFT ADDEND'* SUBROUTINE
+------------------------------------------------------------------------
+
+; The accumulator A contains the difference between the two exponents.
+; This is the lowest of the two numbers to be added
+
+mark_171A:
+*SHIFT_FP:*
+ AND A ; test difference between exponents.
+ RET Z ; return if zero. both normal.
+
+ CP 33 ; compare with 33 bits.
+ JR NC,ADDEND_0 <#ADDEND_0> ; forward if greater than 32
+
+ PUSH BC ; preserve BC - part
+ LD B,A ; shift counter to B.
+
+; Now perform B right shifts on the addend L'D'E'D E
+; to bring it into line with the augend H'B'C'C B
+
+mark_1722:
+*ONE_SHIFT:*
+ EXX ; - - -
+ SRA L ; 76543210->C bit 7 unchanged.
+ RR D ; C->76543210->C
+ RR E ; C->76543210->C
+ EXX ; - - -
+ RR D ; C->76543210->C
+ RR E ; C->76543210->C
+ DJNZ ONE_SHIFT <#ONE_SHIFT> ; loop back B times
+
+ POP BC ; restore BC
+ RET NC ; return if last shift produced no carry. >>
+
+; if carry flag was set then accuracy is being lost so round up the addend.
+
+ CALL ADD_BACK <#ADD_BACK>
+ RET NZ ; return if not FF 00 00 00 00
+
+; this branch makes all five bytes of the addend zero and is made during
+; addition when the exponents are too far apart for the addend bits to
+; affect the result.
+
+mark_1736:
+*ADDEND_0:*
+ EXX ; select alternate set for more significant
+ ; bytes.
+ XOR A ; clear accumulator.
+
+
+; this entry point (from multiplication) sets four of the bytes to zero or if
+; continuing from above, during addition, then all five bytes are set to zero.
+
+mark_1738:
+*ZEROS_4_OR_5:*
+ LD L,0 ; set byte 1 to zero.
+ LD D,A ; set byte 2 to A.
+ LD E,L ; set byte 3 to zero.
+ EXX ; select main set
+ LD DE,$0000 ; set lower bytes 4 and 5 to zero.
+ RET ; return.
+
+------------------------------------------------------------------------
+
+; THE *'ADD_BACK'* SUBROUTINE
+------------------------------------------------------------------------
+
+; Called from SHIFT_FP above during addition and after normalization from
+; multiplication.
+; This is really a 32_bit increment routine which sets the zero flag according
+; to the 32-bit result.
+; During addition, only negative numbers like FF FF FF FF FF,
+; the twos-complement version of xx 80 00 00 01 say
+; will result in a full ripple FF 00 00 00 00.
+; FF FF FF FF FF when shifted right is unchanged by SHIFT_FP but sets the
+; carry invoking this routine.
+
+mark_1741:
+*ADD_BACK:*
+ INC E ;
+ RET NZ ;
+
+ INC D ;
+ RET NZ ;
+
+ EXX ;
+ INC E ;
+ JR NZ,ALL_ADDED <#ALL_ADDED> ; forward if no overflow
+
+ INC D ;
+
+mark_174A:
+*ALL_ADDED:*
+ EXX ;
+ RET ; return with zero flag set for zero mantissa.
+
+
+------------------------------------------------------------------------
+
+; THE *'SUBTRACTION'* OPERATION
+------------------------------------------------------------------------
+
+; just switch the sign of subtrahend and do an add.
+
+mark_174C:
+*SUBTRACT:*
+ LD A,(DE) ; fetch exponent byte of second number the
+ ; subtrahend.
+ AND A ; test for zero
+ RET Z ; return if zero - first number is result.
+
+ INC DE ; address the first mantissa byte.
+ LD A,(DE) ; fetch to accumulator.
+ XOR $80 ; toggle the sign bit.
+ LD (DE),A ; place back on calculator stack.
+ DEC DE ; point to exponent byte.
+ ; continue into addition routine.
+
+------------------------------------------------------------------------
+
+; THE *'ADDITION'* OPERATION
+------------------------------------------------------------------------
+
+; The addition operation pulls out all the stops and uses most of the Z80's
+; registers to add two floating-point numbers.
+; This is a binary operation and on entry, HL points to the first number
+; and DE to the second.
+
+mark_1755:
+*ADDITION:*
+ EXX ; - - -
+ PUSH HL ; save the pointer to the next literal.
+ EXX ; - - -
+
+ PUSH DE ; save pointer to second number
+ PUSH HL ; save pointer to first number - will be the
+ ; result pointer on calculator stack.
+
+ CALL PREP_ADD <#PREP_ADD>
+ LD B,A ; save first exponent byte in B.
+ EX DE,HL ; switch number pointers.
+ CALL PREP_ADD <#PREP_ADD>
+ LD C,A ; save second exponent byte in C.
+ CP B ; compare the exponent bytes.
+ JR NC,SHIFT_LEN <#SHIFT_LEN> ; forward if second higher
+
+ LD A,B ; else higher exponent to A
+ LD B,C ; lower exponent to B
+ EX DE,HL ; switch the number pointers.
+
+mark_1769:
+*SHIFT_LEN:*
+ PUSH AF ; save higher exponent
+ SUB B ; subtract lower exponent
+
+ CALL FETCH_TWO <#FETCH_TWO>
+ CALL SHIFT_FP <#SHIFT_FP>
+
+ POP AF ; restore higher exponent.
+ POP HL ; restore result pointer.
+ LD (HL),A ; insert exponent byte.
+ PUSH HL ; save result pointer again.
+
+; now perform the 32-bit addition using two 16-bit Z80 add instructions.
+
+ LD L,B ; transfer low bytes of mantissa individually
+ LD H,C ; to HL register
+
+ ADD HL,DE ; the actual binary addition of lower bytes
+
+; now the two higher byte pairs that are in the alternate register sets.
+
+ EXX ; switch in set
+ EX DE,HL ; transfer high mantissa bytes to HL register.
+
+ ADC HL,BC ; the actual addition of higher bytes with
+ ; any carry from first stage.
+
+ EX DE,HL ; result in DE, sign bytes ($FF or $00) to HL
+
+; now consider the two sign bytes
+
+ LD A,H ; fetch sign byte of num1
+
+ ADC A,L ; add including any carry from mantissa
+ ; addition. 00 or 01 or FE or FF
+
+ LD L,A ; result in L.
+
+; possible outcomes of signs and overflow from mantissa are
+;
+; H + L + carry = L RRA XOR L RRA
+------------------------------------------------------------------------
+
+; 00 + 00 = 00 00 00
+; 00 + 00 + carry = 01 00 01 carry
+; FF + FF = FE C FF 01 carry
+; FF + FF + carry = FF C FF 00
+; FF + 00 = FF FF 00
+; FF + 00 + carry = 00 C 80 80
+
+ RRA ; C->76543210->C
+ XOR L ; set bit 0 if shifting required.
+
+ EXX ; switch back to main set
+ EX DE,HL ; full mantissa result now in D'E'D E registers.
+ POP HL ; restore pointer to result exponent on
+ ; the calculator stack.
+
+ RRA ; has overflow occurred ?
+ JR NC,TEST_NEG <#TEST_NEG> ; skip forward if not
+
+; if the addition of two positive mantissas produced overflow or if the
+; addition of two negative mantissas did not then the result exponent has to
+; be incremented and the mantissa shifted one place to the right.
+
+ LD A,1 ; one shift required.
+ CALL SHIFT_FP <#SHIFT_FP> ; performs a single shift
+ ; rounding any lost bit
+ INC (HL) ; increment the exponent.
+ JR Z,ADD_REP_6 <#ADD_REP_6> ; forward to ADD_REP_6 if the exponent
+ ; wraps round from FF to zero as number is too
+ ; big for the system.
+
+; at this stage the exponent on the calculator stack is correct.
+
+mark_1790:
+*TEST_NEG:*
+ EXX ; switch in the alternate set.
+ LD A,L ; load result sign to accumulator.
+ AND $80 ; isolate bit 7 from sign byte setting zero
+ ; flag if positive.
+ EXX ; back to main set.
+
+ INC HL ; point to first byte of mantissa
+ LD (HL),A ; insert $00 positive or $80 negative at
+ ; position on calculator stack.
+
+ DEC HL ; point to exponent again.
+ JR Z,GO_NC_MLT <#GO_NC_MLT> ; forward if positive to GO_NC_MLT
+
+; a negative number has to be twos-complemented before being placed on stack.
+
+ LD A,E ; fetch lowest (rightmost) mantissa byte.
+ NEG ; Negate
+ CCF ; Complement Carry Flag
+ LD E,A ; place back in register
+
+ LD A,D ; ditto
+ CPL ;
+ ADC A,0 ;
+ LD D,A ;
+
+ EXX ; switch to higher (leftmost) 16 bits.
+
+ LD A,E ; ditto
+ CPL ;
+ ADC A,0 ;
+ LD E,A ;
+
+ LD A,D ; ditto
+ CPL ;
+ ADC A,0 ;
+ JR NC,END_COMPL <#END_COMPL> ; forward without overflow to END_COMPL
+
+; else entire mantissa is now zero. 00 00 00 00
+
+ RRA ; set mantissa to 80 00 00 00
+ EXX ; switch.
+ INC (HL) ; increment the exponent.
+
+mark_17B3:
+*ADD_REP_6:*
+ JP Z,REPORT_6 <#REPORT_6> ; jump forward if exponent now zero to REPORT_6
+ ; 'Number too big'
+
+ EXX ; switch back to alternate set.
+
+mark_17B7:
+*END_COMPL:*
+ LD D,A ; put first byte of mantissa back in DE.
+ EXX ; switch to main set.
+
+mark_17B9:
+*GO_NC_MLT:*
+ XOR A ; clear carry flag and
+ ; clear accumulator so no extra bits carried
+ ; forward as occurs in multiplication.
+
+ JR TEST_NORM <#TEST_NORM> ; forward to common code at TEST_NORM
+ ; but should go straight to NORMALIZE.
+
+
+------------------------------------------------------------------------
+
+; THE *'PREPARE TO MULTIPLY OR DIVIDE'* SUBROUTINE
+------------------------------------------------------------------------
+
+; this routine is called twice from multiplication and twice from division
+; to prepare each of the two numbers for the operation.
+; Initially the accumulator holds zero and after the second invocation bit 7
+; of the accumulator will be the sign bit of the result.
+
+mark_17BC:
+*PREP_MULTIPLY_OR_DIVIDE:*
+ SCF ; set carry flag to signal number is zero.
+ DEC (HL) ; test exponent
+ INC (HL) ; for zero.
+ RET Z ; return if zero with carry flag set.
+
+ INC HL ; address first mantissa byte.
+ XOR (HL) ; exclusive or the running sign bit.
+ SET 7,(HL) ; set the implied bit.
+ DEC HL ; point to exponent byte.
+ RET ; return.
+
+------------------------------------------------------------------------
+
+; THE *'MULTIPLICATION'* OPERATION
+------------------------------------------------------------------------
+
+;
+;
+
+mark_17C6:
+*MULTIPLY:*
+ XOR A ; reset bit 7 of running sign flag.
+ CALL PREP_MULTIPLY_OR_DIVIDE <#PREP_MULTIPLY_OR_DIVIDE>
+ RET C ; return if number is zero.
+ ; zero * anything = zero.
+
+ EXX ; - - -
+ PUSH HL ; save pointer to 'next literal'
+ EXX ; - - -
+
+ PUSH DE ; save pointer to second number
+
+ EX DE,HL ; make HL address second number.
+
+ CALL PREP_MULTIPLY_OR_DIVIDE <#PREP_MULTIPLY_OR_DIVIDE>
+
+ EX DE,HL ; HL first number, DE - second number
+ JR C,ZERO_RESULT <#ZERO_RESULT> ; forward with carry to ZERO_RESULT
+ ; anything * zero = zero.
+
+ PUSH HL ; save pointer to first number.
+
+ CALL FETCH_TWO <#FETCH_TWO> ; fetches two mantissas from
+ ; calc stack to B'C'C,B D'E'D E
+ ; (HL will be overwritten but the result sign
+ ; in A is inserted on the calculator stack)
+
+ LD A,B ; transfer low mantissa byte of first number
+ AND A ; clear carry.
+ SBC HL,HL ; a short form of LD HL,$0000 to take lower
+ ; two bytes of result. (2 program bytes)
+ EXX ; switch in alternate set
+ PUSH HL ; preserve HL
+ SBC HL,HL ; set HL to zero also to take higher two bytes
+ ; of the result and clear carry.
+ EXX ; switch back.
+
+ LD B,33 ; register B can now be used to count 33 shifts.
+ JR STRT_MLT <#STRT_MLT> ; forward to loop entry point STRT_MLT
+
+; ___
+
+; The multiplication loop is entered at STRT_LOOP.
+
+mark_17E7:
+*MLT_LOOP:*
+ JR NC,NO_ADD <#NO_ADD> ; forward if no carry
+
+ ; else add in the multiplicand.
+
+ ADD HL,DE ; add the two low bytes to result
+ EXX ; switch to more significant bytes.
+ ADC HL,DE ; add high bytes of multiplicand and any carry.
+ EXX ; switch to main set.
+
+; in either case shift result right into B'C'C A
+
+mark_17EE:
+*NO_ADD:*
+ EXX ; switch to alternate set
+ RR H ; C > 76543210 > C
+ RR L ; C > 76543210 > C
+ EXX ;
+ RR H ; C > 76543210 > C
+ RR L ; C > 76543210 > C
+
+mark_17F8:
+*STRT_MLT:*
+ EXX ; switch in alternate set.
+ RR B ; C > 76543210 > C
+ RR C ; C > 76543210 > C
+ EXX ; now main set
+ RR C ; C > 76543210 > C
+ RRA ; C > 76543210 > C
+ DJNZ MLT_LOOP <#MLT_LOOP> ; loop back 33 timeS
+
+;
+
+ EX DE,HL ;
+ EXX ;
+ EX DE,HL ;
+ EXX ;
+ POP BC ;
+ POP HL ;
+ LD A,B ;
+ ADD A,C ;
+ JR NZ,MAKE_EXPT <#MAKE_EXPT> ; forward
+
+ AND A ;
+
+mark_180E:
+*MAKE_EXPT:*
+ DEC A ;
+ CCF ; Complement Carry Flag
+
+mark_1810:
+*DIVN_EXPT:*
+ RLA ;
+ CCF ; Complement Carry Flag
+ RRA ;
+ JP P,OFLW1_CLR <#OFLW1_CLR>
+
+ JR NC,REPORT_6 <#REPORT_6>
+
+ AND A ;
+
+mark_1819:
+*OFLW1_CLR:*
+ INC A ;
+ JR NZ,OFLW2_CLR <#OFLW2_CLR>
+
+ JR C,OFLW2_CLR <#OFLW2_CLR>
+
+ EXX ;
+ BIT 7,D ;
+ EXX ;
+ JR NZ,REPORT_6 <#REPORT_6>
+
+mark_1824:
+*OFLW2_CLR:*
+ LD (HL),A ;
+ EXX ;
+ LD A,B ;
+ EXX ;
+
+; addition joins here with carry flag clear.
+
+mark_1828:
+*TEST_NORM:*
+ JR NC,NORMALIZE <#NORMALIZE> ; forward
+
+ LD A,(HL) ;
+ AND A ;
+
+mark_182C:
+*NEAR_ZERO:*
+ LD A,$80 ; prepare to rescue the most significant bit
+ ; of the mantissa if it is set.
+ JR Z,SKIP_ZERO <#SKIP_ZERO> ; skip forward
+
+mark_1830:
+*ZERO_RESULT:*
+ XOR A ; make mask byte zero signaling set five
+ ; bytes to zero.
+
+mark_1831:
+*SKIP_ZERO:*
+ EXX ; switch in alternate set
+ AND D ; isolate most significant bit (if A is $80).
+
+ CALL ZEROS_4_OR_5 <#ZEROS_4_OR_5> ; sets mantissa without
+ ; affecting any flags.
+
+ RLCA ; test if MSB set. bit 7 goes to bit 0.
+ ; either $00 -> $00 or $80 -> $01
+ LD (HL),A ; make exponent $01 (lowest) or $00 zero
+ JR C,OFLOW_CLR <#OFLOW_CLR> ; forward if first case
+
+ INC HL ; address first mantissa byte on the
+ ; calculator stack.
+ LD (HL),A ; insert a zero for the sign bit.
+ DEC HL ; point to zero exponent
+ JR OFLOW_CLR <#OFLOW_CLR> ; forward
+
+; ___
+
+; this branch is common to addition and multiplication with the mantissa
+; result still in registers D'E'D E .
+
+mark_183F:
+*NORMALIZE:*
+ LD B,32 ; a maximum of thirty-two left shifts will be
+ ; needed.
+
+mark_1841:
+*SHIFT_ONE:*
+ EXX ; address higher 16 bits.
+ BIT 7,D ; test the leftmost bit
+ EXX ; address lower 16 bits.
+
+ JR NZ,NORML_NOW <#NORML_NOW> ; forward if leftmost bit was set
+
+ RLCA ; this holds zero from addition, 33rd bit
+ ; from multiplication.
+
+ RL E ; C < 76543210 < C
+ RL D ; C < 76543210 < C
+
+ EXX ; address higher 16 bits.
+
+ RL E ; C < 76543210 < C
+ RL D ; C < 76543210 < C
+
+ EXX ; switch to main set.
+
+ DEC (HL) ; decrement the exponent byte on the calculator
+ ; stack.
+
+ JR Z,NEAR_ZERO <#NEAR_ZERO> ; back if exponent becomes zero
+ ; it's just possible that the last rotation
+ ; set bit 7 of D. We shall see.
+
+ DJNZ SHIFT_ONE <#SHIFT_ONE> ; loop back
+
+; if thirty-two left shifts were performed without setting the most significant
+; bit then the result is zero.
+
+ JR ZERO_RESULT <#ZERO_RESULT> ; back
+
+; ___
+
+mark_1859:
+*NORML_NOW:*
+ RLA ; for the addition path, A is always zero.
+ ; for the mult path, ...
+
+ JR NC,OFLOW_CLR <#OFLOW_CLR> ; forward
+
+; this branch is taken only with multiplication.
+
+ CALL ADD_BACK <#ADD_BACK>
+
+ JR NZ,OFLOW_CLR <#OFLOW_CLR> ; forward
+
+ EXX ;
+ LD D,$80 ;
+ EXX ;
+ INC (HL) ;
+ JR Z,REPORT_6 <#REPORT_6> ; forward
+
+; now transfer the mantissa from the register sets to the calculator stack
+; incorporating the sign bit already there.
+
+mark_1868:
+*OFLOW_CLR:*
+ PUSH HL ; save pointer to exponent on stack.
+ INC HL ; address first byte of mantissa which was
+ ; previously loaded with sign bit $00 or $80.
+
+ EXX ; - - -
+ PUSH DE ; push the most significant two bytes.
+ EXX ; - - -
+
+ POP BC ; pop - true mantissa is now BCDE.
+
+; now pick up the sign bit.
+
+ LD A,B ; first mantissa byte to A
+ RLA ; rotate out bit 7 which is set
+ RL (HL) ; rotate sign bit on stack into carry.
+ RRA ; rotate sign bit into bit 7 of mantissa.
+
+; and transfer mantissa from main registers to calculator stack.
+
+ LD (HL),A ;
+ INC HL ;
+ LD (HL),C ;
+ INC HL ;
+ LD (HL),D ;
+ INC HL ;
+ LD (HL),E ;
+
+ POP HL ; restore pointer to num1 now result.
+ POP DE ; restore pointer to num2 now STKEND.
+
+ EXX ; - - -
+ POP HL ; restore pointer to next calculator literal.
+ EXX ; - - -
+
+ RET ; return.
+
+; ___
+
+mark_1880:
+*REPORT_6:*
+ RST _ERROR_1
+ DEFB 5 ; Error Report: Arithmetic overflow.
+
+------------------------------------------------------------------------
+
+; THE *'DIVISION'* OPERATION
+------------------------------------------------------------------------
+
+; "Of all the arithmetic subroutines, division is the most complicated and
+; the least understood. It is particularly interesting to note that the
+; Sinclair programmer himself has made a mistake in his programming ( or has
+; copied over someone else's mistake!) for
+; PRINT PEEK 6352 [ $18D0 ] ('unimproved' ROM, 6351 [ $18CF ] )
+; should give 218 not 225."
+; - Dr. Ian Logan, Syntax magazine Jul/Aug 1982.
+; [ i.e. the jump should be made to div-34th ]
+
+; First check for division by zero.
+
+mark_1882:
+*DIVISION:*
+ EX DE,HL ; consider the second number first.
+ XOR A ; set the running sign flag.
+ CALL PREP_MULTIPLY_OR_DIVIDE <#PREP_MULTIPLY_OR_DIVIDE>
+ JR C,REPORT_6 <#REPORT_6> ; back if zero
+ ; 'Arithmetic overflow'
+
+ EX DE,HL ; now prepare first number and check for zero.
+ CALL PREP_MULTIPLY_OR_DIVIDE <#PREP_MULTIPLY_OR_DIVIDE>
+ RET C ; return if zero, 0/anything is zero.
+
+ EXX ; - - -
+ PUSH HL ; save pointer to the next calculator literal.
+ EXX ; - - -
+
+ PUSH DE ; save pointer to divisor - will be STKEND.
+ PUSH HL ; save pointer to dividend - will be result.
+
+ CALL FETCH_TWO <#FETCH_TWO> ; fetches the two numbers
+ ; into the registers H'B'C'C B
+ ; L'D'E'D E
+ EXX ; - - -
+ PUSH HL ; save the two exponents.
+
+ LD H,B ; transfer the dividend to H'L'H L
+ LD L,C ;
+ EXX ;
+ LD H,C ;
+ LD L,B ;
+
+ XOR A ; clear carry bit and accumulator.
+ LD B,$DF ; count upwards from -33 decimal
+ JR DIVISION_START <#DIVISION_START> ; forward to mid-loop entry point
+
+; ___
+
+mark_18A2:
+*DIV_LOOP:*
+ RLA ; multiply partial quotient by two
+ RL C ; setting result bit from carry.
+ EXX ;
+ RL C ;
+ RL B ;
+ EXX ;
+
+mark_18AB:
+*DIV_34TH:*
+ ADD HL,HL ;
+ EXX ;
+ ADC HL,HL ;
+ EXX ;
+ JR C,SUBN_ONLY <#SUBN_ONLY> ; forward
+
+mark_18B2:
+*DIVISION_START:*
+ SBC HL,DE ; subtract divisor part.
+ EXX ;
+ SBC HL,DE ;
+ EXX ;
+ JR NC,NUM_RESTORE <#NUM_RESTORE> ; forward if subtraction goes
+
+ ADD HL,DE ; else restore
+ EXX ;
+ ADC HL,DE ;
+ EXX ;
+ AND A ; clear carry
+ JR COUNT_ONE <#COUNT_ONE> ; forward
+
+; ___
+
+mark_18C2:
+*SUBN_ONLY:*
+ AND A ;
+ SBC HL,DE ;
+ EXX ;
+ SBC HL,DE ;
+ EXX ;
+
+mark_18C9:
+*NUM_RESTORE:*
+ SCF ; set carry flag
+
+mark_18CA:
+*COUNT_ONE:*
+ INC B ; increment the counter
+ JP M,DIV_LOOP <#DIV_LOOP> ; back while still minus to DIV_LOOP
+
+ PUSH AF ;
+ JR Z,DIVISION_START <#DIVISION_START> ; back to DIV_START
+
+; "This jump is made to the wrong place. No 34th bit will ever be obtained
+; without first shifting the dividend. Hence important results like 1/10 and
+; 1/1000 are not rounded up as they should be. Rounding up never occurs when
+; it depends on the 34th bit. The jump should be made to div_34th above."
+; - Dr. Frank O'Hara, "The Complete Spectrum ROM Disassembly", 1983,
+; published by Melbourne House.
+; (Note. on the ZX81 this would be JR Z,DIV_34TH)
+;
+; However if you make this change, then while (1/2=.5) will now evaluate as
+; true, (.25=1/4), which did evaluate as true, no longer does.
+
+ LD E,A ;
+ LD D,C ;
+ EXX ;
+ LD E,C ;
+ LD D,B ;
+
+ POP AF ;
+ RR B ;
+ POP AF ;
+ RR B ;
+
+ EXX ;
+ POP BC ;
+ POP HL ;
+ LD A,B ;
+ SUB C ;
+ JP DIVN_EXPT <#DIVN_EXPT> ; jump back
+
+------------------------------------------------------------------------
+
+; THE *'INTEGER TRUNCATION TOWARDS ZERO'* SUBROUTINE
+------------------------------------------------------------------------
+
+;
+
+mark_18E4:
+*TRUNCATE:*
+ LD A,(HL) ; fetch exponent
+ CP $81 ; compare to +1
+ JR NC,T_GR_ZERO <#T_GR_ZERO> ; forward, if 1 or more
+
+; else the number is smaller than plus or minus 1 and can be made zero.
+
+ LD (HL),$00 ; make exponent zero.
+ LD A,$20 ; prepare to set 32 bits of mantissa to zero.
+ JR NIL_BYTES <#NIL_BYTES> ; forward
+
+; ___
+
+mark_18EF:
+*T_GR_ZERO:*
+ SUB $A0 ; subtract +32 from exponent
+ RET P ; return if result is positive as all 32 bits
+ ; of the mantissa relate to the integer part.
+ ; The floating point is somewhere to the right
+ ; of the mantissa
+
+ NEG ; else negate to form number of rightmost bits
+ ; to be blanked.
+
+; for instance, disregarding the sign bit, the number 3.5 is held as
+; exponent $82 mantissa .11100000 00000000 00000000 00000000
+; we need to set $82 - $A0 = $E2 NEG = $1E (thirty) bits to zero to form the
+; integer.
+; The sign of the number is never considered as the first bit of the mantissa
+; must be part of the integer.
+
+mark_18F4:
+*NIL_BYTES:*
+ PUSH DE ; save pointer to STKEND
+ EX DE,HL ; HL points at STKEND
+ DEC HL ; now at last byte of mantissa.
+ LD B,A ; Transfer bit count to B register.
+ SRL B ; divide by
+ SRL B ; eight
+ SRL B ;
+ JR Z,BITS_ZERO <#BITS_ZERO> ; forward if zero
+
+; else the original count was eight or more and whole bytes can be blanked.
+
+mark_1900:
+*BYTE_ZERO:*
+ LD (HL),0 ; set eight bits to zero.
+ DEC HL ; point to more significant byte of mantissa.
+ DJNZ BYTE_ZERO <#BYTE_ZERO> ; loop back
+
+; now consider any residual bits.
+
+mark_1905:
+*BITS_ZERO:*
+ AND $07 ; isolate the remaining bits
+ JR Z,IX_END <#IX_END> ; forward if none
+
+ LD B,A ; transfer bit count to B counter.
+ LD A,$FF ; form a mask 11111111
+
+mark_190C:
+*LESS_MASK:*
+ SLA A ; 1 <- 76543210 <- o slide mask leftwards.
+ DJNZ LESS_MASK <#LESS_MASK> ; loop back for bit count
+
+ AND (HL) ; lose the unwanted rightmost bits
+ LD (HL),A ; and place in mantissa byte.
+
+mark_1912:
+*IX_END:*
+ EX DE,HL ; restore result pointer from DE.
+ POP DE ; restore STKEND from stack.
+ RET ; return.
+
+------------------------------------------------------------------------
+
+; 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.
+; Exceptions are val and str$.
+; 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.
+
+
+*TAB_CNST*
+
+#if ORIGINAL
+mark_1915:
+** ; 00 00 00 00 00
+stk_zero:
+ DEFB $00 ;;Bytes: 1
+ DEFB $B0 ;;Exponent $00
+ DEFB $00 ;;(+00,+00,+00)
+
+mark_1918:
+** ; 81 00 00 00 00
+stk_one:
+ DEFB $31 ;;Exponent $81, Bytes: 1
+ DEFB $00 ;;(+00,+00,+00)
+
+
+mark_191A:
+** ; 80 00 00 00 00
+stk_half:
+ DEFB $30 ;;Exponent: $80, Bytes: 1
+ DEFB $00 ;;(+00,+00,+00)
+
+
+mark_191C:
+** ; 81 49 0F DA A2
+stk_half_pi:
+ DEFB $F1 ;;Exponent: $81, Bytes: 4
+ DEFB $49,$0F,$DA,$A2 ;;
+
+mark_1921:
+** ; 84 20 00 00 00
+stk_ten:
+ DEFB $34 ;;Exponent: $84, Bytes: 1
+ DEFB $20 ;;(+00,+00,+00)
+#else
+; This table has been modified so that the constants are held in their
+; uncompressed, ready-to-use, 5-byte form.
+
+ 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 ;
+#endif
+
+------------------------------------------------------------------------
+
+; THE *'TABLE OF ADDRESSES'*
+------------------------------------------------------------------------
+
+;
+; starts with binary operations which have two operands and one result.
+; three pseudo binary operations first.
+
+#if ORIGINAL
+mark_1923:
+#else
+#endif
+
+*tbl_addrs:*
+
+ DEFW jump_true <#jump_true> ; $00 Address: $1C2F - jump_true
+ DEFW exchange <#exchange> ; $01 Address: $1A72 - exchange
+ DEFW delete <#delete> ; $02 Address: $19E3 - delete
+
+; true binary operations.
+
+ DEFW SUBTRACT <#SUBTRACT> ; $03 Address: $174C - subtract
+ DEFW MULTIPLY <#MULTIPLY> ; $04 Address: $176C - multiply
+ DEFW DIVISION <#DIVISION> ; $05 Address: $1882 - division
+ DEFW to_power <#to_power> ; $06 Address: $1DE2 - to_power
+ DEFW or <#or> ; $07 Address: $1AED - or
+
+ DEFW boolean_num_and_num <#boolean_num_and_num> ; $08 Address: $1AF3 - boolean_num_and_num
+ DEFW num_l_eql <#num_l_eql> ; $09 Address: $1B03 - num_l_eql
+ DEFW num_gr_eql <#num_gr_eql> ; $0A Address: $1B03 - num_gr_eql
+ DEFW nums_neql <#nums_neql> ; $0B Address: $1B03 - nums_neql
+ DEFW num_grtr <#num_grtr> ; $0C Address: $1B03 - num_grtr
+ DEFW num_less <#num_less> ; $0D Address: $1B03 - num_less
+ DEFW nums_eql <#nums_eql> ; $0E Address: $1B03 - nums_eql
+ DEFW ADDITION <#ADDITION> ; $0F Address: $1755 - addition
+
+ DEFW strs_and_num <#strs_and_num> ; $10 Address: $1AF8 - str_and_num
+ DEFW str_l_eql <#str_l_eql> ; $11 Address: $1B03 - str_l_eql
+ DEFW str_gr_eql <#str_gr_eql> ; $12 Address: $1B03 - str_gr_eql
+ DEFW strs_neql <#strs_neql> ; $13 Address: $1B03 - strs_neql
+ DEFW str_grtr <#str_grtr> ; $14 Address: $1B03 - str_grtr
+ DEFW str_less <#str_less> ; $15 Address: $1B03 - str_less
+ DEFW strs_eql <#strs_eql> ; $16 Address: $1B03 - strs_eql
+ DEFW strs_add <#strs_add> ; $17 Address: $1B62 - strs_add
+
+; unary follow
+
+ DEFW neg <#neg> ; $18
+ DEFW code <#code> ; $19
+ DEFW val <#val> ; $1A
+ DEFW len <#len> ; $1B
+ DEFW sin <#sin> ; $1C
+ DEFW cos <#cos> ; $1D
+ DEFW tan <#tan> ; $1E
+ DEFW asn <#asn> ; $1F
+ DEFW acs <#acs> ; $20
+ DEFW atn <#atn> ; $21
+ DEFW ln <#ln> ; $22
+ DEFW exp <#exp> ; $23
+ DEFW int <#int> ; $24
+ DEFW sqr <#sqr> ; $25
+ DEFW sgn <#sgn> ; $26
+ DEFW abs <#abs> ; $27
+ DEFW PEEK <#PEEK> ; $28 Address: $1A1B - peek !!!!
+ DEFW usr_num <#usr_num> ; $29
+ DEFW str_dollar <#str_dollar> ; $2A
+ DEFW chr_dollar <#chr_dollar> ; $2B
+ DEFW not <#not> ; $2C
+
+; end of true unary
+
+ DEFW duplicate <#duplicate> ; $2D
+ DEFW n_mod_m <#n_mod_m> ; $2E
+
+ DEFW jump <#jump> ; $2F
+ DEFW stk_data <#stk_data> ; $30
+
+ DEFW dec_jr_nz <#dec_jr_nz> ; $31
+ DEFW less_0 <#less_0> ; $32
+ DEFW greater_0 <#greater_0> ; $33
+ DEFW end_calc <#end_calc> ; $34
+ DEFW get_argt <#get_argt> ; $35
+ DEFW TRUNCATE <#TRUNCATE> ; $36
+ DEFW FP_CALC_2 <#FP_CALC_2> ; $37
+ DEFW e_to_fp <#e_to_fp> ; $38
+
+; the following are just the next available slots for the 128 compound literals
+; which are in range $80 - $FF.
+
+ DEFW series_xx <#series_xx> ; $39 : $80 - $9F.
+ DEFW stk_const_xx <#stk_const_xx> ; $3A : $A0 - $BF.
+ DEFW st_mem_xx <#st_mem_xx> ; $3B : $C0 - $DF.
+ DEFW get_mem_xx <#get_mem_xx> ; $3C : $E0 - $FF.
+
+; Aside: 3D - 7F are therefore unused calculator literals.
+; 39 - 7B would be available for expansion.
+
+------------------------------------------------------------------------
+
+; THE *'FLOATING POINT CALCULATOR'*
+------------------------------------------------------------------------
+
+;
+;
+
+mark_199D:
+*CALCULATE:*
+ CALL STACK_POINTERS <#STACK_POINTERS> ; 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...
+
+mark_19A0:
+*GEN_ENT_1:*
+ LD A,B ; fetch the Z80 B register to A
+ LD (BERG),A ; and store value in system variable BERG.
+ ; this will be the counter for dec_jr_nz
+ ; or if used from FP_CALC2 the calculator
+ ; instruction.
+
+; ... and again later at this point
+
+mark_19A4:
+*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.
+
+mark_19A7:
+*RE_ENTRY:*
+ LD (STKEND),DE ; save end of stack
+ EXX ; switch to alt
+ LD A,(HL) ; get next literal
+ INC HL ; increase pointer'
+
+; single operation jumps back to here
+
+mark_19AE:
+*SCAN_ENT:*
+ PUSH HL ; save pointer on stack *
+ AND A ; now test the literal
+ JP P,FIRST_3D <#FIRST_3D> ; forward 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 <#ENT_TABLE> ; forward
+
+; ___
+
+; the branch was here with simple literals.
+
+mark_19C2:
+*FIRST_3D:*
+ CP $18 ; compare with first unary operations.
+ JR NC,DOUBLE_A <#DOUBLE_A> ; with unary operations
+
+; it is binary so adjust pointers.
+
+ EXX ;
+ LD BC,-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 ;
+
+mark_19CE:
+*DOUBLE_A:*
+ RLCA ; double the literal
+ LD L,A ; and store in L for indexing
+
+mark_19D0:
+*ENT_TABLE:*
+ LD DE,tbl_addrs <#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 <#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,(STKEND+1) ; STKEND_hi
+ ; nothing much goes to C but BERG 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
+
+mark_19E3:
+*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.
+
+mark_19E4:
+*FP_CALC_2:*
+ POP AF ; drop return address.
+ LD A,(BERG) ; load accumulator from system variable BERG
+ ; value will be literal eg. 'tan'
+ EXX ; switch to alt
+ JR SCAN_ENT <#SCAN_ENT> ; back
+ ; 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.
+
+mark_19EB:
+*TEST_5_SP:*
+ PUSH DE ; save
+ PUSH HL ; registers
+ LD BC,5 ; an overhead of five bytes
+ CALL TEST_ROOM <#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
+
+mark_19F6:
+*duplicate:*
+*MOVE_FP:*
+ CALL TEST_5_SP <#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.
+
+mark_19FC:
+*stk_data:*
+ LD H,D ; transfer STKEND
+ LD L,E ; to HL for result.
+
+mark_19FE:
+*STK_CONST:*
+ CALL TEST_5_SP <#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.
+
+#if ORIGINAL
+ PUSH BC ; save BC - value 5 from test room ??.
+#else
+;; PUSH BC ; save BC - value 5 from test room. No need.
+#endif
+ 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 <#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).
+
+mark_1A14:
+*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
+
+
+#if ORIGINAL
+ LD B,$00 ; prepare to copy. Note. B is zero.
+ LDIR ; copy C bytes
+ POP BC ; restore 5 counter to BC.
+#else
+ LDIR ; copy C bytes
+#endif
+
+ 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
+
+mark_1A27:
+*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 <#STK_ZEROS> ; loop back until done.
+
+------------------------------------------------------------------------
+
+; THE *'SKIP CONSTANTS'* SUBROUTINE
+------------------------------------------------------------------------
+
+; This routine traverses 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.
+
+#if ORIGINAL
+mark_1A2D:
+*SKIP_CONS:*
+ AND A ; test if initially zero.
+
+mark_1A2E:
+*SKIP_NEXT:*
+ 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
+ ; Note. not a fault but this has to be
+ ; moved elsewhere when running in RAM.
+ ;
+ CALL STK_CONST <#STK_CONST> ; works through variable
+ ; length records.
+
+ POP DE ; restore real STKEND
+ POP AF ; restore count
+ DEC A ; decrease
+ JR SKIP_NEXT <#SKIP_NEXT> ; loop back
+#else
+; 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.
+#endif
+
+------------------------------------------------------------------------
+
+; 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.
+
+mark_1A3C:
+*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.
+
+mark_1A45:
+*get_mem_xx:*
+
+#if ORIGINAL
+ PUSH DE ; save STKEND
+ LD HL,(MEM) ; MEM is base address of the memory cells.
+#else
+ ; Note. first two instructions have been swapped to create a subroutine.
+ LD HL,(MEM) ; MEM is base address of the memory cells.
+*INDEX_5* ; new label
+ PUSH DE ; save STKEND
+#endif
+ CALL LOC_MEM <#LOC_MEM> ; so that HL = first byte
+ CALL MOVE_FP <#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
+------------------------------------------------------------------------
+
+
+*stk_const_xx:*
+#if ORIGINAL
+
+; offset $A0: 'stk_zero'
+; offset $A1: 'stk_one'
+; offset $A2: 'stk_half'
+; offset $A3: 'stk_half_pi'
+; 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 isn't very efficient and it would have been better to hold the
+; numbers in full, five byte form and stack them in a similar manner
+; to that which would be used later for semi-tone table values.
+
+mark_1A51:
+
+ LD H,D ; save STKEND - required for result
+ LD L,E ;
+ EXX ; swap
+ PUSH HL ; save pointer to next literal
+ LD HL,stk_zero <#stk_zero> ; Address: stk_zero - start of table of
+ ; constants
+ EXX ;
+ CALL SKIP_CONS <#SKIP_CONS>
+ CALL STK_CONST <#STK_CONST>
+ EXX ;
+ POP HL ; restore pointer to next literal.
+ EXX ;
+ RET ; return.
+#else
+*stk_con_x*
+ LD HL,TAB_CNST ; Address: Table of constants.
+
+ JR INDEX_5 <#INDEX_5> ; and join subroutine above.
+#endif
+
+
+
+------------------------------------------------------------------------
+
+; 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.
+
+mark_1A63:
+*st_mem_xx:*
+ PUSH HL ; save the result pointer.
+ EX DE,HL ; transfer to DE.
+ LD HL,(MEM) ; fetch MEM the base of memory area.
+ CALL LOC_MEM <#LOC_MEM> ; sets HL to the destination.
+ EX DE,HL ; swap - HL is start, DE is destination.
+
+#if ORIGINAL
+ CALL MOVE_FP <#MOVE_FP>
+ ; note. a short ld bc,5; ldir
+ ; the embedded memory check is not required
+ ; so these instructions would be faster!
+#else
+ LD C,5 ;+ one extra byte but
+ LDIR ;+ faster and no memory check.
+#endif
+
+
+ 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.
+
+mark_1A72:
+*exchange:*
+ LD B,$05 ; there are five bytes to be swapped
+
+; start of loop.
+
+mark_1A74:
+*SWAP_BYTE:*
+ LD A,(DE) ; each byte of second
+#if ORIGINAL
+ LD C,(HL) ; each byte of first
+ EX DE,HL ; swap pointers
+#else
+ LD C,A ;+
+ LD A,(HL)
+#endif
+ 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 <#SWAP_BYTE> ; loop back until all 5 done.
+
+#if ORIGINAL
+ EX DE,HL ; even up the exchanges so that DE addresses STKEND.
+#else
+; omit
+#endif
+ RET ; return.
+
+------------------------------------------------------------------------
+
+; THE *'SERIES GENERATOR'* SUBROUTINE
+------------------------------------------------------------------------
+
+
+; 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.
+
+mark_1A7F:
+*series_xx:*
+ LD B,A ; parameter $00 - $1F to B counter
+ CALL GEN_ENT_1 <#GEN_ENT_1>
+ ; A recursive call to a special entry point
+ ; in the calculator that puts the B register
+ ; in the system variable BERG. 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 __duplicate ;; x,x
+ DEFB __addition ;; x+x
+ DEFB __st_mem_0 ;; x+x
+ DEFB __delete ;; .
+ DEFB __stk_zero ;; 0
+ DEFB __st_mem_2 ;; 0
+
+; a loop is now entered to perform the algebraic calculation for each of
+; the numbers in the series
+
+mark_1A89:
+*G_LOOP:*
+ DEFB __duplicate ;; v,v.
+ DEFB __get_mem_0 ;; v,v,x+2
+ DEFB __multiply ;; v,v*x+2
+ DEFB __get_mem_2 ;; v,v*x+2,v
+ DEFB __st_mem_1 ;;
+ DEFB __subtract ;;
+ DEFB __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 <#stk_data> ; is called directly to
+ ; push a value and advance H'L'.
+ CALL GEN_ENT_2 <#GEN_ENT_2> ; recursively re-enters
+ ; the calculator without disturbing
+ ; system variable BERG
+ ; H'L' value goes on the machine stack and is
+ ; then loaded as usual with the next address.
+
+ DEFB __addition ;;
+ DEFB __exchange ;;
+ DEFB __st_mem_2 ;;
+ DEFB __delete ;;
+
+ DEFB __dec_jr_nz ;;
+ DEFB $EE ;;back to G_LOOP <#G_LOOP>, G_LOOP
+
+; when the counted loop is complete the final subtraction yields the result
+; for example SIN X.
+
+ DEFB __get_mem_1 ;;
+ DEFB __subtract ;;
+ DEFB __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.
+
+mark_1AA0:
+*mark_1AA0:*
+*neg:*
+ 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.
+
+mark_1AAA:
+*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 negative. If it is zero then it is left as such.
+
+mark_1AAF:
+*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_OR_1 <#FP_0_OR_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.
+
+mark_1ABE:
+*PEEK:*
+ CALL FIND_INT <#FIND_INT> ; puts address in BC.
+ LD A,(BC) ; load contents into A register.
+
+mark_1AC2:
+*IN_PK_STK:*
+ JP STACK_A <#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 ERR_NR if a user-written
+; program has altered it.
+
+mark_1AC5:
+*usr_num:*
+ CALL FIND_INT <#FIND_INT> ; to fetch the
+ ; supplied address into BC.
+
+ LD HL,STACK_BC <#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 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.
+
+mark_1ACE:
+*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 <#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.
+
+mark_1AD5:
+*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_OR_1 <#FP_0_OR_1> ; forward to FP_0_OR_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.
+
+mark_1ADB:
+*less_0:*
+ XOR A ; set xor mask to zero
+ ; (carry will become set if sign is negative).
+
+; transfer sign of mantissa to Carry Flag.
+
+mark_1ADC:
+*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.
+
+mark_1AE0:
+*FP_0_OR_1:*
+ PUSH HL ; save pointer to the first byte
+ LD B,$05 ; five bytes to do.
+
+mark_1AE3:
+*FP_loop:*
+ LD (HL),$00 ; insert a zero.
+ INC HL ;
+ DJNZ FP_loop <#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).
+
+mark_1AED:
+*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_OR_1 <#FP_0_OR_1> ; back to FP_0_OR_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.
+
+*boolean_num_and_num:*
+ LD A,(DE) ; fetch exponent of second number.
+ AND A ; test it.
+ RET NZ ; return if not zero.
+
+ JR FP_0_OR_1 <#FP_0_OR_1> ; back to FP_0_OR_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.
+
+*strs_and_num:*
+ 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
+; ========= ==== == ======== === ======== ======== ==== === = === === ===
+; num_l_eql x<=y 09 00000001 dec 00000000 00000000 ---- x-y ? --- >0? NOT
+; num_gr_eql x>=y 0A 00000010 dec 00000001 10000000c swap y-x ? --- >0? NOT
+; nums_neql x<>y 0B 00000011 dec 00000010 00000001 ---- x-y ? NOT --- NOT
+; num_grtr x>y 0C 00000100 - 00000100 00000010 ---- x-y ? --- >0? ---
+; num_less x<y 0D 00000101 - 00000101 10000010c swap y-x ? --- >0? ---
+; nums_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.
+
+; $1B03 SAME ADDRESS FOR MULTIPLE ROUTINES ???
+
+*num_l_eql:*
+*num_gr_eql:*
+*nums_neql:*
+*num_grtr:*
+*num_less:*
+*nums_eql:*
+*str_l_eql:*
+*str_gr_eql:*
+*strs_neql:*
+*str_grtr:*
+*str_less:*
+*strs_eql:*
+*num_lt_eql:*
+#if ORIGINAL
+mark_1B03:
+ LD A,B ; transfer literal to accumulator.
+ SUB $08 ; subtract eight - which is not useful.
+#else
+ LD A,B ; transfer literal to accumulator.
+;; SUB $08 ; subtract eight - which is not useful.
+#endif
+ BIT 2,A ; isolate '>', '<', '='.
+
+ JR NZ,EX_OR_NOT <#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:*
+#if ORIGINAL
+mark_1B0B:
+#endif
+ RRCA ; the first RRCA sets carry for a swap.
+ JR NC,NUM_OR_STR <#NUM_OR_STR> ; forward to NUM_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 <#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.
+
+*NUM_OR_STR:*
+#if ORIGINAL
+mark_1B16:
+
+ BIT 2,A ; test if a string comparison.
+ JR NZ,STRINGS <#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
+#else
+ RRCA ;+ causes 'eql/neql' to set carry.
+ PUSH AF ;+ save the carry flag.
+
+ BIT 2,A ; test if a string comparison.
+ JR NZ,STRINGS <#STRINGS> ; forward to STRINGS if so.
+
+#endif
+
+ CALL SUBTRACT <#SUBTRACT> ; leaves result on stack.
+ JR END_TESTS <#END_TESTS> ; forward to END_TESTS
+
+; ___
+
+
+*STRINGS:*
+#if ORIGINAL
+mark_1B21:
+ RRCA ; 2nd RRCA causes eql/neql to set carry.
+ PUSH AF ; save A and carry.
+#else
+;; RRCA ; 2nd RRCA causes eql/neql to set carry.
+;; PUSH AF ; save A and carry.
+#endif
+ CALL STK_FETCH <#STK_FETCH> ; gets 2nd string params
+ PUSH DE ; save start2 *.
+ PUSH BC ; and the length.
+
+ CALL STK_FETCH <#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.
+
+#if ORIGINAL
+mark_1B2C:
+#endif
+*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 <#SEC_PLUS> ; forward to SEC_PLUS if second not null.
+
+ OR C ; test length of first string.
+
+#if ORIGINAL
+mark_1B33:
+#endif
+
+*SECOND_LOW:*
+ POP BC ; pop the second length off stack.
+ JR Z,BOTH_NULL <#BOTH_NULL> ; forward if first string is also
+ ; of zero length.
+
+; the true condition - first is longer than second (SECOND_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 <#STR_TEST> ; forward to leave via STR_TEST
+
+; ___
+; the branch was here with a match
+
+#if ORIGINAL
+mark_1B3A:
+#endif
+
+*BOTH_NULL:*
+ POP AF ; restore carry - set for eql/neql
+ JR STR_TEST <#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.
+
+
+mark_1B3D:
+*SEC_PLUS:*
+ OR C ; test the length of first string.
+ JR Z,FRST_LESS <#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 <#FRST_LESS> ; forward to FRST_LESS if carry set
+
+ JR NZ,SECOND_LOW <#SECOND_LOW> ; back to SECOND_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 <#BYTE_COMP> ; back to BYTE_COMP
+
+; ___
+; the false condition.
+
+mark_1B4D:
+*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
+
+mark_1B50:
+*STR_TEST:*
+ PUSH AF ; save A and carry
+
+ RST _FP_CALC ;;
+ DEFB __stk_zero ;; an initial false value.
+ DEFB __end_calc ;;
+
+; both numeric and string paths converge here.
+
+mark_1B54:
+*END_TESTS:*
+ POP AF ; pop carry - will be set if eql/neql
+ PUSH AF ; save it again.
+
+ CALL C,not <#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 <#not> ; apply a terminal NOT if so.
+ RET ; return.
+------------------------------------------------------------------------
+
+; String concatenation ($17)
+------------------------------------------------------------------------
+
+; 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.
+
+mark_1B62:
+*strs_add:*
+ CALL STK_FETCH <#STK_FETCH> ; fetches string parameters
+ ; and deletes calculator stack entry.
+ PUSH DE ; save start address.
+ PUSH BC ; and length.
+
+ CALL STK_FETCH <#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 _BC_SPACES ; BC_SPACES in workspace.
+ ; DE points to start of space.
+
+ CALL STK_STO_STR <#STK_STO_STR> ; stores parameters
+ ; of new string updating STKEND.
+ POP BC ; length of first
+ POP HL ; address of start
+
+#if ORIGINAL
+ LD A,B ; test for
+ OR C ; zero length.
+ JR Z,OTHER_STR <#OTHER_STR> ; to OTHER_STR if null string
+ LDIR ; copy string to workspace.
+#else
+ CALL COND_MV <#COND_MV> ;+ a conditional (NZ) ldir routine.
+#endif
+
+mark_1B7D:
+*OTHER_STR:*
+ POP BC ; now second length
+ POP HL ; and start of string
+#if ORIGINAL
+ LD A,B ; test this one
+ OR C ; for zero length
+ JR Z,STACK_POINTERS <#STACK_POINTERS> ; skip forward to STACK_POINTERS if so as complete.
+
+ LDIR ; else copy the bytes.
+ ; and continue into next routine which
+ ; sets the calculator stack pointers.
+#else
+ CALL COND_MV <#COND_MV> ;+ a conditional (NZ) ldir routine.
+#endif
+
+------------------------------------------------------------------------
+
+; Check stack pointers
+------------------------------------------------------------------------
+
+; Register DE is set to STKEND and HL, the result pointer, is set to five
+; locations below this.
+; 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.
+
+mark_1B85:
+*STACK_POINTERS:*
+ LD HL,(STKEND) ; fetch STKEND value from system variable.
+ LD DE,-5
+ PUSH HL ; push STKEND value.
+
+ ADD HL,DE ; subtract 5 from HL.
+
+ POP DE ; pop STKEND to DE.
+ RET ; return.
+
+------------------------------------------------------------------------
+
+; Handle CHR$ (2B)
+------------------------------------------------------------------------
+
+; 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.
+
+mark_1B8F:
+*chr_dollar:*
+ CALL FP_TO_A <#FP_TO_A> ; puts the number in A.
+
+ JR C,REPORT_Bd <#REPORT_Bd> ; forward if overflow
+ JR NZ,REPORT_Bd <#REPORT_Bd> ; forward if negative
+#if ORIGINAL
+ PUSH AF ; save the argument.
+#endif
+ LD BC,1 ; one space required.
+ RST _BC_SPACES ; BC_SPACES makes DE point to start
+#if ORIGINAL
+ POP AF ; restore the number.
+#endif
+ LD (DE),A ; and store in workspace
+
+#if ORIGINAL
+ CALL STK_STO_STR <#STK_STO_STR> ; stacks descriptor.
+
+ EX DE,HL ; make HL point to result and DE to STKEND.
+ RET ; return.
+#else
+ JR str_STK ;+ relative jump to similar sequence in str$.
+#endif
+; ___
+
+mark_1BA2:
+*REPORT_Bd:*
+ RST _ERROR_1
+ DEFB $0A ; Error Report: Integer out of range
+
+------------------------------------------------------------------------
+
+; Handle VAL ($1A)
+------------------------------------------------------------------------
+
+; 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:*
+#if ORIGINAL
+mark_1BA4:
+ LD HL,(CH_ADD) ; fetch value of system variable CH_ADD
+#else
+ RST _GET_CHAR ;+ shorter way to fetch CH_ADD.
+#endif
+ PUSH HL ; and save on the machine stack.
+
+ CALL STK_FETCH <#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 _BC_SPACES ; BC_SPACES creates the space in workspace.
+ POP HL ; restore start of string to HL.
+ LD (CH_ADD),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),ZX_NEWLINE ; insert a carriage return at end.
+ ; ZX81 has a non-ASCII character set
+ RES 7,(IY+FLAGS-RAMBASE) ; signal checking syntax.
+ CALL CLASS_6 <#CLASS_6> ; evaluates string
+ ; expression and checks for integer result.
+
+ CALL CHECK_2 <#CHECK_2> ; checks for carriage return.
+
+
+ POP HL ; restore start of string in workspace.
+
+ LD (CH_ADD),HL ; set CH_ADD to the start of the string again.
+ SET 7,(IY+FLAGS-RAMBASE) ; signal running program.
+ CALL SCANNING <#SCANNING> ; evaluates the string
+ ; in full leaving result on calculator stack.
+
+ POP HL ; restore saved character address in program.
+ LD (CH_ADD),HL ; and reset the system variable CH_ADD.
+
+ JR STACK_POINTERS <#STACK_POINTERS> ; back to exit via STACK_POINTERS.
+ ; resetting the calculator stack pointers
+ ; HL and DE from STKEND as it wasn't possible
+ ; to preserve them during this routine.
+
+------------------------------------------------------------------------
+
+; Handle STR$ (2A)
+------------------------------------------------------------------------
+
+; 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.
+
+mark_1BD5:
+*str_dollar:*
+ LD BC,1 ; create an initial byte in workspace
+ RST _BC_SPACES ; using BC_SPACES restart.
+
+ LD (HL),ZX_NEWLINE ; place a carriage return there.
+
+ LD HL,(S_POSN) ; 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 (S_POSN),HL ; and store in system variable S_POSN.
+
+ LD HL,(DF_CC) ; fetch value of DF_CC
+ PUSH HL ; and preserve on stack also.
+
+ LD (DF_CC),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 <#PRINT_FP>
+
+ POP DE ; retrieve start of string.
+
+ LD HL,(DF_CC) ; 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 (DF_CC),HL ; DF_CC value
+
+ POP HL ; restore original
+ LD (S_POSN),HL ; S_POSN values.
+
+#if ORIGINAL
+#else
+str_STK: ; New entry-point to exploit similarities and save 3 bytes of code.
+#endif
+
+ CALL STK_STO_STR <#STK_STO_STR> ; 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).
+
+
+mark_1C06:
+*code:*
+ CALL STK_FETCH <#STK_FETCH> ; 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 <#STK_CODE> ; skip with zero if the null string.
+
+ LD A,(DE) ; else fetch the first character.
+
+mark_1C0E:
+*STK_CODE:*
+ JP STACK_A <#STACK_A> ; jump back (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
+
+mark_1C11:
+*len:*
+ CALL STK_FETCH <#STK_FETCH> ; fetch and delete the
+ ; string parameters from the calculator stack.
+ ; register BC now holds the length of string.
+
+ JP STACK_BC <#STACK_BC> ; jump back 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.
+
+mark_1C17:
+*dec_jr_nz:*
+ EXX ; switch in set that addresses code
+
+ PUSH HL ; save pointer to offset byte
+ LD HL,BERG ; address BERG in system variables
+ DEC (HL) ; decrement it
+ POP HL ; restore pointer
+
+ JR NZ,JUMP_2 <#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 to be polished for the ZX Spectrum.
+; See, without looking at the ZX Spectrum ROM, if you can get rid of the
+; relative jump.
+
+mark_1C23:
+*jump:*
+ EXX ;switch in pointer set
+*JUMP_2:*
+ LD E,(HL) ; the jump byte 0-127 forward, 128-255 back.
+
+#if ORIGINAL
+mark_1C24:
+ XOR A ; clear accumulator.
+ BIT 7,E ; test if negative jump
+ JR Z,JUMP_3 <#JUMP_3> ; skip, if positive
+ CPL ; else change to $FF.
+#else
+ ; Note. Elegance from the ZX Spectrum.
+ LD A,E ;+
+ RLA ;+
+ SBC A,A ;+
+#endif
+
+mark_1C2B:
+*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.
+
+mark_1C2F:
+*jump_true:*
+ LD A,(DE) ; collect exponent byte
+
+ AND A ; is result 0 or 1 ?
+ JR NZ,jump <#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.
+
+mark_1C37:
+*n_mod_m:*
+ RST _FP_CALC ;; 17, 3.
+ DEFB __st_mem_0 ;; 17, 3.
+ DEFB __delete ;; 17.
+ DEFB __duplicate ;; 17, 17.
+ DEFB __get_mem_0 ;; 17, 17, 3.
+ DEFB __division ;; 17, 17/3.
+ DEFB __int ;; 17, 5.
+ DEFB __get_mem_0 ;; 17, 5, 3.
+ DEFB __exchange ;; 17, 3, 5.
+ DEFB __st_mem_0 ;; 17, 3, 5.
+ DEFB __multiply ;; 17, 15.
+ DEFB __subtract ;; 2.
+ DEFB __get_mem_0 ;; 2, 5.
+ DEFB __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 or minus 3.4 as examples.
+
+mark_1C46:
+*int:*
+ RST _FP_CALC ;; x. (= 3.4 or -3.4).
+ DEFB __duplicate ;; x, x.
+ DEFB __less_0 ;; x, (1/0)
+ DEFB __jump_true ;; x, (1/0)
+ DEFB int <#int> - $ ;; X_NEG
+
+ DEFB __truncate ;; trunc 3.4 = 3.
+ DEFB __end_calc ;; 3.
+
+ RET ; return with + int x on stack.
+
+
+mark_1C4E:
+*X_NEG:*
+ DEFB __duplicate ;; -3.4, -3.4.
+ DEFB __truncate ;; -3.4, -3.
+ DEFB __st_mem_0 ;; -3.4, -3.
+ DEFB __subtract ;; -.4
+ DEFB __get_mem_0 ;; -.4, -3.
+ DEFB __exchange ;; -3, -.4.
+ DEFB __not ;; -3, (0).
+ DEFB __jump_true ;; -3.
+ DEFB EXIT <#EXIT> - $ ;; -3.
+
+ DEFB __stk_one ;; -3, 1.
+ DEFB __subtract ;; -4.
+
+mark_1C59:
+*EXIT:*
+ DEFB __end_calc ;; -4.
+
+ RET ; return.
+
+
+------------------------------------------------------------------------
+
+; Exponential (23)
+------------------------------------------------------------------------
+
+;
+;
+
+mark_1C5B:
+*exp:*
+ RST _FP_CALC ;;
+ DEFB __stk_data ;;
+ DEFB $F1 ;;Exponent: $81, Bytes: 4
+ DEFB $38,$AA,$3B,$29 ;;
+ DEFB __multiply ;;
+ DEFB __duplicate ;;
+ DEFB __int ;;
+ DEFB $C3 ;;st_mem_3
+ DEFB __subtract ;;
+ DEFB __duplicate ;;
+ DEFB __addition ;;
+ DEFB __stk_one ;;
+ DEFB __subtract ;;
+ DEFB __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 __end_calc ;;
+
+ CALL FP_TO_A <#FP_TO_A>
+ JR NZ,N_NEGTV <#N_NEGTV>
+
+ JR C,REPORT_6b <#REPORT_6b>
+
+ ADD A,(HL) ;
+ JR NC,RESULT_OK <#RESULT_OK>
+
+
+mark_1C99:
+*REPORT_6b:*
+ RST _ERROR_1
+ DEFB $05 ; Error Report: Number too big
+
+mark_1C9B:
+*N_NEGTV:*
+ JR C,RESULT_ZERO <#RESULT_ZERO>
+
+ SUB (HL) ;
+ JR NC,RESULT_ZERO <#RESULT_ZERO>
+
+ NEG ; Negate
+
+mark_1CA2:
+*RESULT_OK:*
+ LD (HL),A ;
+ RET ; return.
+
+
+mark_1CA4:
+*RESULT_ZERO:*
+ RST _FP_CALC ;;
+ DEFB __delete ;;
+ DEFB __stk_zero ;;
+ DEFB __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.
+
+mark_1CA9:
+*ln:*
+ RST _FP_CALC ;;
+ DEFB __duplicate ;;
+ DEFB __greater_0 ;;
+ DEFB __jump_true ;;
+ DEFB VALID - $ ;;to VALID <#VALID>
+
+ DEFB __end_calc ;;
+
+
+mark_1CAF:
+*REPORT_Ab:*
+ RST _ERROR_1
+ DEFB $09 ; Error Report: Invalid argument
+
+*VALID:*
+#if ORIGINAL
+mark_1CB1:
+ DEFB __stk_zero ;; Note. not necessary.
+ DEFB __delete ;;
+#endif
+ DEFB __end_calc ;;
+ LD A,(HL) ;
+
+ LD (HL),$80 ;
+ CALL STACK_A <#STACK_A>
+
+ RST _FP_CALC ;;
+ DEFB __stk_data ;;
+ DEFB $38 ;;Exponent: $88, Bytes: 1
+ DEFB $00 ;;(+00,+00,+00)
+ DEFB __subtract ;;
+ DEFB __exchange ;;
+ DEFB __duplicate ;;
+ DEFB __stk_data ;;
+ DEFB $F0 ;;Exponent: $80, Bytes: 4
+ DEFB $4C,$CC,$CC,$CD ;;
+ DEFB __subtract ;;
+ DEFB __greater_0 ;;
+ DEFB __jump_true ;;
+ DEFB GRE_8 <#GRE_8> - $ ;;
+
+ DEFB __exchange ;;
+ DEFB __stk_one ;;
+ DEFB __subtract ;;
+ DEFB __exchange ;;
+ DEFB __end_calc ;;
+
+ INC (HL) ;
+
+ RST _FP_CALC ;;
+
+mark_1CD2:
+*GRE_8:*
+ DEFB __exchange ;;
+ DEFB __stk_data ;;
+ DEFB $F0 ;;Exponent: $80, Bytes: 4
+ DEFB $31,$72,$17,$F8 ;;
+ DEFB __multiply ;;
+ DEFB __exchange ;;
+ DEFB __stk_half ;;
+ DEFB __subtract ;;
+ DEFB __stk_half ;;
+ DEFB __subtract ;;
+ DEFB __duplicate ;;
+ DEFB __stk_data ;;
+ DEFB $32 ;;Exponent: $82, Bytes: 1
+ DEFB $20 ;;(+00,+00,+00)
+ DEFB __multiply ;;
+ DEFB __stk_half ;;
+ DEFB __subtract ;;
+ DEFB __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 __multiply ;;
+ DEFB __addition ;;
+ DEFB __end_calc ;;
+
+ RET ; return.
+------------------------------------------------------------------------
+
+#if ORIGINAL
+#else
+; ------------------------------
+; 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 _FP_CALC ;; x
+ DEFB __st_mem_3 ;; x. (seed for guess)
+ DEFB __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 <#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 <#ASIS> ; forward with say .25 -> .5
+ JP P,ASIS <#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 __duplicate ;; x,x.
+ DEFB __get_mem_3 ;; x,x,guess
+ DEFB __st_mem_4 ;; x,x,guess
+ DEFB __division ;; x,x/guess.
+ DEFB __get_mem_3 ;; x,x/guess,guess
+ DEFB __addition ;; x,x/guess+guess
+ DEFB __stk_half ;; x,x/guess+guess,.5
+ DEFB __multiply ;; x,(x/guess+guess)*.5
+ DEFB __st_mem_3 ;; x,newguess
+ DEFB __get_mem_4 ;; x,newguess,oldguess
+ DEFB __subtract ;; x,newguess-oldguess
+ DEFB __abs ;; x,difference.
+ DEFB __greater_0 ;; x,(0/1).
+ DEFB __jump_true ;; x.
+
+ DEFB SLOOP <#SLOOP> - $ ;; x.
+
+ DEFB __delete ;; .
+ DEFB __get_mem_3 ;; retrieve final guess.
+ DEFB __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
+#endif
+
+------------------------------------------------------------------------
+
+; 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.
+
+mark_1D18:
+*get_argt:*
+ RST _FP_CALC ;; X.
+ DEFB __stk_data ;;
+ DEFB $EE ;;Exponent: $7E,
+ ;;Bytes: 4
+ DEFB $22,$F9,$83,$6E ;; X, 1/(2*PI)
+ DEFB __multiply ;; X/(2*PI) = fraction
+
+ DEFB __duplicate ;;
+ DEFB __stk_half ;;
+ DEFB __addition ;;
+ DEFB __int ;;
+
+ DEFB __subtract ;; now range -.5 to .5
+
+ DEFB __duplicate ;;
+ DEFB __addition ;; now range -1 to 1.
+ DEFB __duplicate ;;
+ DEFB __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 __duplicate ;; Y, Y.
+ DEFB __abs ;; Y, abs(Y). range 1 to 2
+ DEFB __stk_one ;; Y, abs(Y), 1.
+ DEFB __subtract ;; Y, abs(Y)-1. range 0 to 1
+ DEFB __duplicate ;; Y, Z, Z.
+ DEFB __greater_0 ;; Y, Z, (1/0).
+
+ DEFB __st_mem_0 ;; store as possible sign
+ ;; for cosine function.
+
+ DEFB __jump_true ;;
+ DEFB Z_PLUS <#Z_PLUS> - $ ;; with quadrants II and III
+
+; else the angle lies in quadrant I or IV and value Y is already correct.
+
+ DEFB __delete ;; Y delete test value.
+ DEFB __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.
+
+mark_1D35:
+*Z_PLUS:*
+ DEFB __stk_one ;; Y, Z, 1
+ DEFB __subtract ;; Y, Z-1. Q3 = 0 to -1
+ DEFB __exchange ;; Z-1, Y.
+ DEFB __less_0 ;; Z-1, (1/0).
+ DEFB __jump_true ;; Z-1.
+ DEFB YNEG <#YNEG> - $ ;;
+ ;;if angle in quadrant III
+
+; else angle is within quadrant II (-1 to 0)
+
+ DEFB __negate ; range +1 to 0
+
+
+mark_1D3C:
+*YNEG:*
+ DEFB __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.
+
+mark_1D3E:
+*cos:*
+ RST _FP_CALC ;; angle in radians.
+ DEFB __get_argt ;; X reduce -1 to +1
+
+ DEFB __abs ;; ABS X 0 to 1
+ DEFB __stk_one ;; ABS X, 1.
+ DEFB __subtract ;; now opposite angle
+ ;; though negative sign.
+ DEFB __get_mem_0 ;; fetch sign indicator.
+ DEFB __jump_true ;;
+ DEFB C_ENT <#C_ENT> - $ ;;fwd to C_ENT
+ ;;forward to common code if in QII or QIII
+
+
+ DEFB __negate ;; else make positive.
+ DEFB __jump ;;
+ DEFB C_ENT <#C_ENT> - $ ;;fwd to 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.
+
+mark_1D49:
+*sin:*
+ RST _FP_CALC ;; angle in radians
+ DEFB __get_argt ;; reduce - sign now correct.
+
+mark_1D4B:
+*C_ENT:*
+ DEFB __duplicate ;;
+ DEFB __duplicate ;;
+ DEFB __multiply ;;
+ DEFB __duplicate ;;
+ DEFB __addition ;;
+ DEFB __stk_one ;;
+ DEFB __subtract ;;
+
+ DEFB __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 __multiply ;;
+ DEFB __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.
+
+mark_1D6E:
+*tan:*
+ RST _FP_CALC ;; x.
+ DEFB __duplicate ;; x, x.
+ DEFB __sin ;; x, sin x.
+ DEFB __exchange ;; sin x, x.
+ DEFB __cos ;; sin x, cos x.
+ DEFB __division ;; sin x/cos x (= tan x).
+ DEFB __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.
+
+mark_1D76:
+*atn:*
+ LD A,(HL) ; fetch exponent
+ CP $81 ; compare to that for 'one'
+ JR C,SMALL <#SMALL> ; forward, if less
+
+ RST _FP_CALC ;; X.
+ DEFB __stk_one ;;
+ DEFB __negate ;;
+ DEFB __exchange ;;
+ DEFB __division ;;
+ DEFB __duplicate ;;
+ DEFB __less_0 ;;
+ DEFB __stk_half_pi ;;
+ DEFB __exchange ;;
+ DEFB __jump_true ;;
+ DEFB CASES <#CASES> - $ ;;
+
+ DEFB __negate ;;
+ DEFB __jump ;;
+ DEFB CASES <#CASES> - $ ;;
+
+; ___
+
+mark_1D89:
+*SMALL:*
+ RST _FP_CALC ;;
+ DEFB __stk_zero ;;
+
+mark_1D8B:
+*CASES:*
+ DEFB __exchange ;;
+ DEFB __duplicate ;;
+ DEFB __duplicate ;;
+ DEFB __multiply ;;
+ DEFB __duplicate ;;
+ DEFB __addition ;;
+ DEFB __stk_one ;;
+ DEFB __subtract ;;
+
+ DEFB __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 __multiply ;;
+ DEFB __addition ;;
+ DEFB __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+d = 180 degrees and b+a = 180 degrees then c=a/2.
+;
+; A value higher than 1 gives the required error as attempting to find the
+; square root of a negative number generates an error in Sinclair BASIC.
+
+mark_1DC4:
+*asn:*
+ RST _FP_CALC ;; x.
+ DEFB __duplicate ;; x, x.
+ DEFB __duplicate ;; x, x, x.
+ DEFB __multiply ;; x, x*x.
+ DEFB __stk_one ;; x, x*x, 1.
+ DEFB __subtract ;; x, x*x-1.
+ DEFB __negate ;; x, 1-x*x.
+ DEFB __sqr ;; x, sqr(1-x*x) = y.
+ DEFB __stk_one ;; x, y, 1.
+ DEFB __addition ;; x, y+1.
+ DEFB __division ;; x/y+1.
+ DEFB __atn ;; a/2 (half the angle)
+ DEFB __duplicate ;; a/2, a/2.
+ DEFB __addition ;; a.
+ DEFB __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
+
+mark_1DD4:
+*acs:*
+ RST _FP_CALC ;; x.
+ DEFB __asn ;; asn(x).
+ DEFB __stk_half_pi ;; asn(x), pi/2.
+ DEFB __subtract ;; asn(x) - pi/2.
+ DEFB __negate ;; pi/2 - asn(x) = acs(x).
+ DEFB __end_calc ;; acs(x)
+
+ RET ; return.
+
+#if ORIGINAL
+------------------------------------------------------------------------
+
+; THE *'SQUARE ROOT'* FUNCTION
+------------------------------------------------------------------------
+
+; (Offset $25: 'sqr')
+; Error A if argument is negative.
+; This routine is remarkable for its brevity - 7 bytes.
+;
+; The ZX81 code was originally 9K and various techniques had to be
+; used to shoe-horn it into an 8K Rom chip.
+
+; 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'.
+
+mark_1DDB:
+*sqr:*
+ RST _FP_CALC ;; x.
+ DEFB __duplicate ;; x, x.
+ DEFB __not ;; x, 1/0
+ DEFB __jump_true ;; x, (1/0).
+ DEFB LAST <#LAST> - $ ;; exit if argument zero
+ ;; with zero result.
+
+; else continue to calculate as x ** .5
+
+ DEFB __stk_half ;; x, .5.
+ DEFB __end_calc ;; x, .5.
+
+#endif
+
+------------------------------------------------------------------------
+
+; THE *'EXPONENTIATION'* OPERATION
+------------------------------------------------------------------------
+
+; (Offset $06: 'to_power')
+; This raises the first number X to the power of the second number Y.
+; As with the ZX80,
+; 0 ** 0 = 1
+; 0 ** +n = 0
+; 0 ** -n = arithmetic overflow.
+
+mark_1DE2:
+*to_power:*
+ RST _FP_CALC ;; X,Y.
+ DEFB __exchange ;; Y,X.
+ DEFB __duplicate ;; Y,X,X.
+ DEFB __not ;; Y,X,(1/0).
+ DEFB __jump_true ;;
+ DEFB XISO <#XISO> - $ ;;forward to XISO if X is zero.
+
+; else X is non-zero. function 'ln' will catch a negative value of X.
+
+ DEFB __ln ;; Y, LN X.
+ DEFB __multiply ;; Y * LN X
+ DEFB __end_calc ;;
+
+ JP exp <#exp> ; jump back to EXP routine. ->
+
+; ___
+
+; These routines form the three simple results when the number is zero.
+; begin by deleting the known zero to leave Y the power factor.
+
+mark_1DEE:
+*XISO:*
+ DEFB __delete ;; Y.
+ DEFB __duplicate ;; Y, Y.
+ DEFB __not ;; Y, (1/0).
+ DEFB __jump_true ;;
+ DEFB ONE <#ONE> - $ ;; if Y is zero.
+
+; the power factor is not zero. If negative then an error exists.
+
+ DEFB __stk_zero ;; Y, 0.
+ DEFB __exchange ;; 0, Y.
+ DEFB __greater_0 ;; 0, (1/0).
+ DEFB __jump_true ;; 0
+ DEFB LAST <#LAST> - $ ;; if Y was any positive
+ ;; number.
+
+; else force division by zero thereby raising an Arithmetic overflow error.
+; There are some one and two-byte alternatives but perhaps the most formal
+; might have been to use end_calc; rst 08; defb 05.
+
+; #if ORIGINAL
+
+; the SG ROM seems to want it the old way!
+#if 1
+ DEFB __stk_one ;; 0, 1.
+ DEFB __exchange ;; 1, 0.
+ DEFB __division ;; 1/0 >> error
+#else
+ DEFB $34 ;+ end-calc
+*REPORT_6c*
+ RST 08H ;+ ERROR-1
+ DEFB $05 ;+ Error Report: Number too big
+#endif
+
+
+; ___
+
+mark_1DFB:
+*ONE:*
+ DEFB __delete ;; .
+ DEFB __stk_one ;; 1.
+
+mark_1DFD:
+*LAST:*
+ DEFB __end_calc ;; last value 1 or 0.
+
+ RET ; return.
+
+------------------------------------------------------------------------
+
+; THE *'SPARE LOCATIONS'*
+------------------------------------------------------------------------
+
+*SPARE:*
+
+#if ORIGINAL
+mark_1DFF:
+ DEFB $FF ; That's all folks.
+#else
+mark_1DFE:
+*L1DFE:*
+
+;; DEFB $FF, $FF ; Two spare bytes.
+ DEFB $00, $00 ; Two spare bytes (as per the Shoulders of Giants ROM)
+#endif
+
+
+------------------------------------------------------------------------
+
+; THE *'ZX81 CHARACTER SET'*
+------------------------------------------------------------------------
+
+
+mark_1E00:
+*char_set* ; - begins with space character.
+
+; $00 - *Character: ' '* CHR$(0)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+
+; $01 - *Character: mosaic* CHR$(1)
+
+ DEFB %*1111*0000
+ DEFB %*1111*0000
+ DEFB %*1111*0000
+ DEFB %*1111*0000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+
+
+; $02 - *Character: mosaic* CHR$(2)
+
+ DEFB %0000*1111*
+ DEFB %0000*1111*
+ DEFB %0000*1111*
+ DEFB %0000*1111*
+ 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 %*1111*0000
+ DEFB %*1111*0000
+ DEFB %*1111*0000
+ DEFB %*1111*0000
+
+; $05 - *Character: mosaic* CHR$(5)
+
+ DEFB %*1111*0000
+ DEFB %*1111*0000
+ DEFB %*1111*0000
+ DEFB %*1111*0000
+ DEFB %*1111*0000
+ DEFB %*1111*0000
+ DEFB %*1111*0000
+ DEFB %*1111*0000
+
+; $06 - *Character: mosaic* CHR$(6)
+
+ DEFB %0000*1111*
+ DEFB %0000*1111*
+ DEFB %0000*1111*
+ DEFB %0000*1111*
+ DEFB %*1111*0000
+ DEFB %*1111*0000
+ DEFB %*1111*0000
+ DEFB %*1111*0000
+
+; $07 - *Character: mosaic* CHR$(7)
+
+ DEFB %*11111111*
+ DEFB %*11111111*
+ DEFB %*11111111*
+ DEFB %*11111111*
+ DEFB %*1111*0000
+ DEFB %*1111*0000
+ DEFB %*1111*0000
+ DEFB %*1111*0000
+
+; $08 - *Character: mosaic* CHR$(8)
+
+ DEFB %*1*0*1*0*1*0*1*0
+ DEFB %0*1*0*1*0*1*0*1*
+ DEFB %*1*0*1*0*1*0*1*0
+ DEFB %0*1*0*1*0*1*0*1*
+ DEFB %*1*0*1*0*1*0*1*0
+ DEFB %0*1*0*1*0*1*0*1*
+ DEFB %*1*0*1*0*1*0*1*0
+ DEFB %0*1*0*1*0*1*0*1*
+; $09 - *Character: mosaic* CHR$(9)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %*1*0*1*0*1*0*1*0
+ DEFB %0*1*0*1*0*1*0*1*
+ DEFB %*1*0*1*0*1*0*1*0
+ DEFB %0*1*0*1*0*1*0*1*
+; $0A - *Character: mosaic* CHR$(10)
+
+ DEFB %*1*0*1*0*1*0*1*0
+ DEFB %0*1*0*1*0*1*0*1*
+ DEFB %*1*0*1*0*1*0*1*0
+ DEFB %0*1*0*1*0*1*0*1*
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+
+; $0B - *Character: '"'* CHR$(11)
+
+ DEFB %00000000
+ DEFB %00*1*00*1*00
+ DEFB %00*1*00*1*00
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+
+; $0C - *Character:* £ CHR$(12)
+
+ DEFB %00000000
+ DEFB %000*111*00
+ DEFB %00*1*000*1*0
+ DEFB %0*1111*000
+ DEFB %00*1*00000
+ DEFB %00*1*00000
+ DEFB %0*111111*0
+ DEFB %00000000
+
+; $0D - *Character: '$'* CHR$(13)
+
+ DEFB %00000000
+ DEFB %0000*1*000
+ DEFB %00*11111*0
+ DEFB %00*1*0*1*000
+ DEFB %00*11111*0
+ DEFB %0000*1*0*1*0
+ DEFB %00*11111*0
+ DEFB %0000*1*000
+
+; $0E - *Character: ':'* CHR$(14)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %000*1*0000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %000*1*0000
+ DEFB %00000000
+
+; $0F - *Character: '?'* CHR$(15)
+
+ DEFB %00000000
+ DEFB %00*1111*00
+ DEFB %0*1*0000*1*0
+ DEFB %00000*1*00
+ DEFB %0000*1*000
+ DEFB %00000000
+ DEFB %0000*1*000
+ DEFB %00000000
+
+; $10 - *Character: '('* CHR$(16)
+
+ DEFB %00000000
+ DEFB %00000*1*00
+ DEFB %0000*1*000
+ DEFB %0000*1*000
+ DEFB %0000*1*000
+ DEFB %0000*1*000
+ DEFB %00000*1*00
+ DEFB %00000000
+
+; $11 - *Character: ')'* CHR$(17)
+
+ DEFB %00000000
+ DEFB %00*1*00000
+ DEFB %000*1*0000
+ DEFB %000*1*0000
+ DEFB %000*1*0000
+ DEFB %000*1*0000
+ DEFB %00*1*00000
+ DEFB %00000000
+
+; $12 - *Character: '>'* CHR$(18)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %000*1*0000
+ DEFB %0000*1*000
+ DEFB %00000*1*00
+ DEFB %0000*1*000
+ DEFB %000*1*0000
+ DEFB %00000000
+
+; $13 - *Character: '<'* CHR$(19)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000*1*00
+ DEFB %0000*1*000
+ DEFB %000*1*0000
+ DEFB %0000*1*000
+ DEFB %00000*1*00
+ DEFB %00000000
+
+; $14 - *Character: '='* CHR$(20)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00*11111*0
+ DEFB %00000000
+ DEFB %00*11111*0
+ DEFB %00000000
+ DEFB %00000000
+
+; $15 - *Character: '+'* CHR$(21)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %0000*1*000
+ DEFB %0000*1*000
+ DEFB %00*11111*0
+ DEFB %0000*1*000
+ DEFB %0000*1*000
+ DEFB %00000000
+
+; $16 - *Character: '-'* CHR$(22)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00*11111*0
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+
+; $17 - *Character: '*'* CHR$(23)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %000*1*0*1*00
+ DEFB %0000*1*000
+ DEFB %00*11111*0
+ DEFB %0000*1*000
+ DEFB %000*1*0*1*00
+ DEFB %00000000
+
+; $18 - *Character: '/'* CHR$(24)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %000000*1*0
+ DEFB %00000*1*00
+ DEFB %0000*1*000
+ DEFB %000*1*0000
+ DEFB %00*1*00000
+ DEFB %00000000
+
+; $19 - *Character: ';'* CHR$(25)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %000*1*0000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %000*1*0000
+ DEFB %000*1*0000
+ DEFB %00*1*00000
+
+; $1A - *Character: ','* CHR$(26)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %0000*1*000
+ DEFB %0000*1*000
+ DEFB %000*1*0000
+
+; $1B - *Character: '"'* CHR$(27)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %000*11*000
+ DEFB %000*11*000
+ DEFB %00000000
+
+; $1C - *Character: '0'* CHR$(28)
+
+ DEFB %00000000
+ DEFB %00*1111*00
+ DEFB %0*1*000*11*0
+ DEFB %0*1*00*1*0*1*0
+ DEFB %0*1*0*1*00*1*0
+ DEFB %0*11*000*1*0
+ DEFB %00*1111*00
+ DEFB %00000000
+
+; $1D - *Character: '1'* CHR$(29)
+
+ DEFB %00000000
+ DEFB %000*11*000
+ DEFB %00*1*0*1*000
+ DEFB %0000*1*000
+ DEFB %0000*1*000
+ DEFB %0000*1*000
+ DEFB %00*11111*0
+ DEFB %00000000
+
+; $1E - *Character: '2'* CHR$(30)
+
+ DEFB %00000000
+ DEFB %00*1111*00
+ DEFB %0*1*0000*1*0
+ DEFB %000000*1*0
+ DEFB %00*1111*00
+ DEFB %0*1*000000
+ DEFB %0*111111*0
+ DEFB %00000000
+
+; $1F - *Character: '3'* CHR$(31)
+
+ DEFB %00000000
+ DEFB %00*1111*00
+ DEFB %0*1*0000*1*0
+ DEFB %0000*11*00
+ DEFB %000000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %00*1111*00
+ DEFB %00000000
+
+; $20 - *Character: '4'* CHR$(32)
+
+ DEFB %00000000
+ DEFB %0000*1*000
+ DEFB %000*11*000
+ DEFB %00*1*0*1*000
+ DEFB %0*1*00*1*000
+ DEFB %0*111111*0
+ DEFB %0000*1*000
+ DEFB %00000000
+
+; $21 - *Character: '5'* CHR$(33)
+
+ DEFB %00000000
+ DEFB %0*111111*0
+ DEFB %0*1*000000
+ DEFB %0*11111*00
+ DEFB %000000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %00*1111*00
+ DEFB %00000000
+
+; $22 - *Character: '6'* CHR$(34)
+
+ DEFB %00000000
+ DEFB %00*1111*00
+ DEFB %0*1*000000
+ DEFB %0*11111*00
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %00*1111*00
+ DEFB %00000000
+
+; $23 - *Character: '7'* CHR$(35)
+
+ DEFB %00000000
+ DEFB %0*111111*0
+ DEFB %000000*1*0
+ DEFB %00000*1*00
+ DEFB %0000*1*000
+ DEFB %000*1*0000
+ DEFB %000*1*0000
+ DEFB %00000000
+
+; $24 - *Character: '8'* CHR$(36)
+
+ DEFB %00000000
+ DEFB %00*1111*00
+ DEFB %0*1*0000*1*0
+ DEFB %00*1111*00
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %00*1111*00
+ DEFB %00000000
+
+; $25 - *Character: '9'* CHR$(37)
+
+ DEFB %00000000
+ DEFB %00*1111*00
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %00*11111*0
+ DEFB %000000*1*0
+ DEFB %00*1111*00
+ DEFB %00000000
+
+; $26 - *Character: 'A'* CHR$(38)
+
+ DEFB %00000000
+ DEFB %00*1111*00
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %0*111111*0
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %00000000
+
+; $27 - *Character: 'B'* CHR$(39)
+
+ DEFB %00000000
+ DEFB %0*11111*00
+ DEFB %0*1*0000*1*0
+ DEFB %0*11111*00
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %0*11111*00
+ DEFB %00000000
+
+; $28 - *Character: 'C'* CHR$(40)
+
+ DEFB %00000000
+ DEFB %00*1111*00
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*000000
+ DEFB %0*1*000000
+ DEFB %0*1*0000*1*0
+ DEFB %00*1111*00
+ DEFB %00000000
+
+; $29 - *Character: 'D'* CHR$(41)
+
+ DEFB %00000000
+ DEFB %0*1111*000
+ DEFB %0*1*000*1*00
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*000*1*00
+ DEFB %0*1111*000
+ DEFB %00000000
+
+; $2A - *Character: 'E'* CHR$(42)
+
+ DEFB %00000000
+ DEFB %0*111111*0
+ DEFB %0*1*000000
+ DEFB %0*11111*00
+ DEFB %0*1*000000
+ DEFB %0*1*000000
+ DEFB %0*111111*0
+ DEFB %00000000
+
+; $2B - *Character: 'F'* CHR$(43)
+
+ DEFB %00000000
+ DEFB %0*111111*0
+ DEFB %0*1*000000
+ DEFB %0*11111*00
+ DEFB %0*1*000000
+ DEFB %0*1*000000
+ DEFB %0*1*000000
+ DEFB %00000000
+
+; $2C - *Character: 'G'* CHR$(44)
+
+ DEFB %00000000
+ DEFB %00*1111*00
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*000000
+ DEFB %0*1*00*111*0
+ DEFB %0*1*0000*1*0
+ DEFB %00*1111*00
+ DEFB %00000000
+
+; $2D - *Character: 'H'* CHR$(45)
+
+ DEFB %00000000
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %0*111111*0
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %00000000
+
+; $2E - *Character: 'I'* CHR$(46)
+
+ DEFB %00000000
+ DEFB %00*11111*0
+ DEFB %0000*1*000
+ DEFB %0000*1*000
+ DEFB %0000*1*000
+ DEFB %0000*1*000
+ DEFB %00*11111*0
+ DEFB %00000000
+
+; $2F - *Character: 'J'* CHR$(47)
+
+ DEFB %00000000
+ DEFB %000000*1*0
+ DEFB %000000*1*0
+ DEFB %000000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %00*1111*00
+ DEFB %00000000
+
+; $30 - *Character: 'K'* CHR$(48)
+
+ DEFB %00000000
+ DEFB %0*1*000*1*00
+ DEFB %0*1*00*1*000
+ DEFB %0*111*0000
+ DEFB %0*1*00*1*000
+ DEFB %0*1*000*1*00
+ DEFB %0*1*0000*1*0
+ DEFB %00000000
+
+; $31 - *Character: 'L'* CHR$(49)
+
+ DEFB %00000000
+ DEFB %0*1*000000
+ DEFB %0*1*000000
+ DEFB %0*1*000000
+ DEFB %0*1*000000
+ DEFB %0*1*000000
+ DEFB %0*111111*0
+ DEFB %00000000
+
+; $32 - *Character: 'M'* CHR$(50)
+
+ DEFB %00000000
+ DEFB %0*1*0000*1*0
+ DEFB %0*11*00*11*0
+ DEFB %0*1*0*11*0*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %00000000
+
+; $33 - *Character: 'N'* CHR$(51)
+
+ DEFB %00000000
+ DEFB %0*1*0000*1*0
+ DEFB %0*11*000*1*0
+ DEFB %0*1*0*1*00*1*0
+ DEFB %0*1*00*1*0*1*0
+ DEFB %0*1*000*11*0
+ DEFB %0*1*0000*1*0
+ DEFB %00000000
+
+; $34 - *Character: 'O'* CHR$(52)
+
+ DEFB %00000000
+ DEFB %00*1111*00
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %00*1111*00
+ DEFB %00000000
+
+; $35 - *Character: 'P'* CHR$(53)
+
+ DEFB %00000000
+ DEFB %0*11111*00
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %0*11111*00
+ DEFB %0*1*000000
+ DEFB %0*1*000000
+ DEFB %00000000
+
+; $36 - *Character: 'Q'* CHR$(54)
+
+ DEFB %00000000
+ DEFB %00*1111*00
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*0*1*00*1*0
+ DEFB %0*1*00*1*0*1*0
+ DEFB %00*1111*00
+ DEFB %00000000
+
+; $37 - *Character: 'R'* CHR$(55)
+
+ DEFB %00000000
+ DEFB %0*11111*00
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %0*11111*00
+ DEFB %0*1*000*1*00
+ DEFB %0*1*0000*1*0
+ DEFB %00000000
+
+; $38 - *Character: 'S'* CHR$(56)
+
+ DEFB %00000000
+ DEFB %00*1111*00
+ DEFB %0*1*000000
+ DEFB %00*1111*00
+ DEFB %000000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %00*1111*00
+ DEFB %00000000
+
+; $39 - *Character: 'T'* CHR$(57)
+
+ DEFB %00000000
+ DEFB %*1111111*0
+ DEFB %000*1*0000
+ DEFB %000*1*0000
+ DEFB %000*1*0000
+ DEFB %000*1*0000
+ DEFB %000*1*0000
+ DEFB %00000000
+
+; $3A - *Character: 'U'* CHR$(58)
+
+ DEFB %00000000
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %00*1111*00
+ DEFB %00000000
+
+; $3B - *Character: 'V'* CHR$(59)
+
+ DEFB %00000000
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %00*1*00*1*00
+ DEFB %000*11*000
+ DEFB %00000000
+
+; $3C - *Character: 'W'* CHR$(60)
+
+ DEFB %00000000
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*0000*1*0
+ DEFB %0*1*0*11*0*1*0
+ DEFB %00*1*00*1*00
+ DEFB %00000000
+
+; $3D - *Character: 'X'* CHR$(61)
+
+ DEFB %00000000
+ DEFB %0*1*0000*1*0
+ DEFB %00*1*00*1*00
+ DEFB %000*11*000
+ DEFB %000*11*000
+ DEFB %00*1*00*1*00
+ DEFB %0*1*0000*1*0
+ DEFB %00000000
+
+; $3E - *Character: 'Y'* CHR$(62)
+
+ DEFB %00000000
+ DEFB %*1*00000*1*0
+ DEFB %0*1*000*1*00
+ DEFB %00*1*0*1*000
+ DEFB %000*1*0000
+ DEFB %000*1*0000
+ DEFB %000*1*0000
+ DEFB %00000000
+
+; $3F - *Character: 'Z'* CHR$(63)
+
+ DEFB %00000000
+ DEFB %0*111111*0
+ DEFB %00000*1*00
+ DEFB %0000*1*000
+ DEFB %000*1*0000
+ DEFB %00*1*00000
+ DEFB %0*111111*0
+ DEFB %00000000
+
+
+; .END ;TASM assembler instruction.
+
+
+
+;
+; This marks the end of the ZX81 ROM.
+;
+; As a bonus feature, I will now include the code for
+; the G007 graphics board and
+; the ZX81 monitor
+;
+; The 8K space divides into four 2K spaces like so:
+;
+; 2000 RAM (1K or 2K) remapped
+; 2800 G007 ROM
+; 3000
+; 3800 ZX81 Monitor
+;
+; The G007 uses some RAM
+; 2300 G007 RAM variables
+;
+; 2000
+#code $2000,$0800
+; RAM_2K
+; 2300
+; xxxx data 4 ; reserves 4 bytes from the #data segment for variable "Toto"
+
+#code $2800,$0800
+
+#if 0
+;
+; just copy bytes. Provides a reference for comparing code output.
+;
+; G007 Graphics
+; Start = 2800H
+; End = 2FFFH
+mark_2800: DEFB $2A, $23, $23, $3A, $25, $23, $67, $EB
+mark_2808: DEFB $3A, $18, $23, $3D, $92, $57, $3E, $07
+mark_2810: DEFB $A3, $6F, $26, $2D, $7E, $2A, $08, $23
+mark_2818: DEFB $4A, $06, $00, $09, $09, $CB, $3A, $CB
+mark_2820: DEFB $1B, $CB, $3A, $CB, $1B, $CB, $3A, $CB
+mark_2828: DEFB $1B, $19, $47, $C9, $3A, $21, $23, $2A
+mark_2830: DEFB $27, $23, $ED, $5B, $23, $23, $D9, $2A
+mark_2838: DEFB $25, $23, $ED, $5B, $29, $23, $CB, $77
+mark_2840: DEFB $28, $1C, $CB, $7F, $C0, $3A, $21, $40
+mark_2848: DEFB $F5, $06, $08, $0F, $FD, $CB, $21, $16
+mark_2850: DEFB $10, $F9, $EB, $D9, $EB, $D9, $CD, $5E
+mark_2858: DEFB $28, $F1, $32, $21, $40, $C9, $01, $DE
+mark_2860: DEFB $FF, $3A, $18, $23, $3D, $93, $ED, $52
+mark_2868: DEFB $F2, $76, $28, $01, $22, $00, $7B, $D5
+mark_2870: DEFB $19, $EB, $B7, $ED, $52, $D1, $E5, $63
+mark_2878: DEFB $5F, $D9, $01, $FB, $28, $7D, $D9, $6F
+mark_2880: DEFB $D9, $B7, $ED, $52, $F2, $90, $28, $01
+mark_2888: DEFB $04, $29, $2F, $19, $EB, $B7, $ED, $52
+mark_2890: DEFB $ED, $43, $1A, $23, $D1, $B7, $ED, $52
+mark_2898: DEFB $19, $30, $09, $EB, $01, $F6, $28, $D9
+mark_28A0: DEFB $53, $5F, $7A, $D9, $ED, $43, $1C, $23
+mark_28A8: DEFB $D9, $57, $D5, $D9, $C1, $3A, $21, $23
+mark_28B0: DEFB $FE, $40, $30, $02, $45, $4B, $04, $0C
+mark_28B8: DEFB $CB, $3C, $38, $02, $28, $08, $CB, $1D
+mark_28C0: DEFB $CB, $3A, $CB, $1B, $18, $F2, $55, $CB
+mark_28C8: DEFB $3D, $D9, $C5, $CD, $07, $28, $D1, $3A
+mark_28D0: DEFB $1F, $23, $4F, $FD, $CB, $21, $06, $38
+mark_28D8: DEFB $08, $3A, $1E, $23, $AE, $B1, $A0, $AE
+mark_28E0: DEFB $77, $D9, $7D, $05, $C8, $93, $30, $0A
+mark_28E8: DEFB $0D, $C8, $82, $D9, $19, $D9, $2A, $1A
+mark_28F0: DEFB $23, $E9, $2A, $1C, $23, $E9, $6F, $D9
+mark_28F8: DEFB $19, $18, $D8, $6F, $D9, $CB, $00, $30
+mark_2900: DEFB $D2, $2B, $18, $CF, $6F, $D9, $CB, $08
+mark_2908: DEFB $30, $C9, $23, $18, $C6, $2A, $A8, $0E
+mark_2910: DEFB $E9, $CD, $0D, $29, $38, $0E, $21, $00
+mark_2918: DEFB $00, $28, $06, $ED, $42, $F8, $C8, $18
+mark_2920: DEFB $03, $ED, $4A, $F0, $E1, $C9, $CD, $11
+mark_2928: DEFB $29, $22, $25, $23, $CD, $11, $29, $E5
+mark_2930: DEFB $CD, $02, $0C, $C1, $ED, $5B, $25, $23
+mark_2938: DEFB $28, $0C, $3D, $C0, $21, $9C, $0C, $22
+mark_2940: DEFB $30, $40, $43, $C3, $B2, $0B
+; The Plot Routine:
+; A == the plot number N
+; BC == screen X
+; DE == screen Y
+mark_2946: DEFB $B7, $28
+mark_2948: DEFB $F9, $3D, $D5, $C5, $F5, $CD, $1F, $2E
+mark_2950: DEFB $F1, $D1, $C1, $FE, $81, $20, $0F, $ED
+mark_2958: DEFB $53, $30, $23, $ED, $43, $32, $23, $01
+mark_2960: DEFB $00, $00, $50, $58, $3E, $0B, $CB, $57
+mark_2968: DEFB $28, $09, $2A, $29, $23, $E5, $2A, $27
+mark_2970: DEFB $23, $18, $07, $2A, $32, $23, $E5, $2A
+mark_2978: DEFB $30, $23, $B7, $ED, $5A, $D1, $E8, $22
+mark_2980: DEFB $23, $23, $EB, $B7, $ED, $4A, $E8, $22
+mark_2988: DEFB $25, $23, $5F, $3E, $C0, $A4, $E0, $3E
+mark_2990: DEFB $C0, $A2, $E0, $D9, $E5, $D5, $C5, $CD
+mark_2998: DEFB $9F, $29, $C1, $D1, $E1, $D9, $C9, $D9
+mark_29A0: DEFB $7C, $B2, $37, $20, $05, $3A, $18, $23
+mark_29A8: DEFB $3D, $BD, $3A, $21, $23, $1F, $32, $21
+mark_29B0: DEFB $23, $7B, $CB, $7F, $28, $04, $2A, $10
+mark_29B8: DEFB $23, $E9, $F5, $E6, $03, $1F, $3D, $2F
+mark_29C0: DEFB $67, $9F, $6F, $22, $1E, $23, $E5, $CB
+mark_29C8: DEFB $5B, $20, $28, $3A, $20, $23, $AB, $E6
+mark_29D0: DEFB $FB, $28, $0F, $21, $34, $23, $7B, $07
+mark_29D8: DEFB $07, $07, $E6, $03, $85, $6F, $7E, $32
+mark_29E0: DEFB $21, $40, $CD, $2C, $28, $E1, $FD, $CB
+mark_29E8: DEFB $21, $0E, $38, $26, $7C, $A5, $28, $22
+mark_29F0: DEFB $2C, $5D, $E5, $3A, $21, $23, $CB, $6B
+mark_29F8: DEFB $20, $3D, $CB, $73, $20, $3E, $E1, $CB
+mark_2A00: DEFB $7F, $20, $0F, $E5, $CD, $00, $28, $A6
+mark_2A08: DEFB $32, $17, $23, $D1, $7E, $B2, $AB, $A0
+mark_2A10: DEFB $AE, $77, $F1, $32, $20, $23, $21, $2A
+mark_2A18: DEFB $23, $11, $2E, $23, $01, $08, $00, $CB
+mark_2A20: DEFB $67, $28, $11, $3A, $21, $23, $E6, $C0
+mark_2A28: DEFB $17, $30, $02, $CB, $F7, $32, $21, $23
+mark_2A30: DEFB $2E, $26, $0E, $04, $ED, $B8, $C9, $21
+mark_2A38: DEFB $FF, $FF, $18, $03, $2A, $0A, $23, $D9
+mark_2A40: DEFB $E6, $E0, $C2, $AD, $0E, $21, $21, $23
+mark_2A48: DEFB $FD, $36, $21, $55, $06, $03, $23, $23
+mark_2A50: DEFB $5E, $23, $23, $56, $D5, $10, $F7, $C1
+mark_2A58: DEFB $D1, $E1, $B7, $28, $02, $44, $4D, $78
+mark_2A60: DEFB $BC, $30, $03, $C5, $E3, $C1, $7C, $BA
+mark_2A68: DEFB $30, $01, $EB, $7A, $D9, $E6, $07, $3C
+mark_2A70: DEFB $47, $7C, $CB, $05, $07, $07, $07, $10
+mark_2A78: DEFB $F9, $67, $22, $38, $23, $D9, $7C, $B8
+mark_2A80: DEFB $3E, $07, $30, $03, $C5, $E3, $C1, $EB
+mark_2A88: DEFB $F5, $79, $D9, $67, $6F, $4F, $22, $1A
+mark_2A90: DEFB $23, $06, $FE, $D9, $93, $D9, $30, $04
+mark_2A98: DEFB $06, $00, $ED, $44, $57, $D9, $7A, $90
+mark_2AA0: DEFB $D9, $67, $BA, $30, $02, $EB, $04, $6C
+mark_2AA8: DEFB $2C, $5C, $CB, $3B, $F1, $0F, $30, $09
+mark_2AB0: DEFB $E5, $D5, $C5, $0F, $D9, $38, $CD, $18
+mark_2AB8: DEFB $CE, $D9, $60, $2E, $01, $C1, $D1, $E3
+mark_2AC0: DEFB $2D, $20, $16, $2A, $1A, $23, $79, $BC
+mark_2AC8: DEFB $38, $01, $67, $BD, $30, $01, $6F, $22
+mark_2AD0: DEFB $1A, $23, $E1, $2D, $28, $E7, $E5, $18
+mark_2AD8: DEFB $15, $7B, $92, $38, $0A, $5F, $CB, $40
+mark_2AE0: DEFB $28, $0C, $79, $80, $4F, $18, $D9, $84
+mark_2AE8: DEFB $5F, $3E, $01, $B0, $81, $4F, $79, $D9
+mark_2AF0: DEFB $FD, $CB, $21, $0E, $38, $CA, $E3, $D5
+mark_2AF8: DEFB $C5, $E5, $ED, $5B, $1A, $23, $47, $B9
+mark_2B00: DEFB $30, $02, $41, $4F, $ED, $43, $1A, $23
+mark_2B08: DEFB $79, $BB, $3C, $38, $01, $7B, $6F, $7A
+mark_2B10: DEFB $B8, $30, $02, $78, $3D, $95, $3C, $F5
+mark_2B18: DEFB $CD, $07, $28, $F1, $4F, $EB, $21, $39
+mark_2B20: DEFB $23, $7E, $07, $07, $07, $77, $2B, $CB
+mark_2B28: DEFB $06, $B6, $2A, $1E, $23, $AD, $2F, $6F
+mark_2B30: DEFB $EB, $7B, $AE, $B2, $A0, $AE, $77, $0D
+mark_2B38: DEFB $20, $14, $E1, $24, $CB, $7D, $28, $94
+mark_2B40: DEFB $E1, $E1, $E1, $E1, $7C, $A5, $CA, $12
+mark_2B48: DEFB $2A, $2C, $E5, $C3, $45, $2A, $CB, $18
+mark_2B50: DEFB $30, $DF, $23, $41, $79, $E6, $07, $4F
+mark_2B58: DEFB $CB, $38, $CB, $38, $CB, $38, $28, $08
+mark_2B60: DEFB $7B, $AE, $B2, $AE, $77, $23, $10, $F8
+mark_2B68: DEFB $37, $0C, $18, $CB, $2A, $13, $23, $2D
+mark_2B70: DEFB $B5, $C2, $09, $08, $25, $C2, $B8, $23
+mark_2B78: DEFB $3A, $21, $23, $17, $D8, $00, $00, $2A
+mark_2B80: DEFB $28, $23, $3A, $27, $23, $CB, $72, $20
+mark_2B88: DEFB $05, $6F, $C6, $08, $30, $0D, $7C, $D6
+mark_2B90: DEFB $08, $30, $04, $3A, $18, $23, $3D, $32
+mark_2B98: DEFB $29, $23, $AF, $32, $27, $23, $CB, $72
+mark_2BA0: DEFB $C0, $CD, $EF, $2E, $7D, $FE, $F9, $38
+mark_2BA8: DEFB $02, $1E, $02, $2F, $E6, $07, $3C, $57
+mark_2BB0: DEFB $7C, $3C, $D9, $57, $D9, $D5, $CD, $07
+mark_2BB8: DEFB $28, $D9, $79, $AE, $D9, $EB, $C1, $C5
+mark_2BC0: DEFB $6F, $26, $00, $29, $10, $FD, $EB, $06
+mark_2BC8: DEFB $02, $3A, $7B, $40, $AE, $FD, $B6, $7C
+mark_2BD0: DEFB $A2, $AE, $0D, $28, $01, $77, $53, $23
+mark_2BD8: DEFB $10, $EF, $0E, $20, $09, $D9, $23, $15
+mark_2BE0: DEFB $28, $02, $10, $D6, $E1, $C3, $9A, $29
+mark_2BE8: DEFB $ED, $57, $0F, $30, $08, $AF, $32, $22
+mark_2BF0: DEFB $40, $3C, $32, $13, $23, $CD, $CF, $0A
+mark_2BF8: DEFB $AF, $32, $13, $23, $FD, $36, $22, $02
+mark_2C00: DEFB $4F, $C9, $CD, $A0, $0C, $DA, $AD, $0E
+mark_2C08: DEFB $0E, $01, $C8, $0E, $FF, $C9, $FD, $46
+mark_2C10: DEFB $22, $0E, $21, $CD, $18, $09, $CD, $9B
+mark_2C18: DEFB $09, $7E, $12, $FD, $34, $3A, $2A, $0C
+mark_2C20: DEFB $40, $23, $54, $5D, $ED, $B1, $C3, $5D
+mark_2C28: DEFB $0A, $8B, $8D, $2D, $7F, $81, $49, $75
+mark_2C30: DEFB $5F, $40, $42, $2B, $17, $1F, $37, $52
+mark_2C38: DEFB $45, $0F, $6D, $2B, $44, $2D, $5A, $3B
+mark_2C40: DEFB $4C, $45, $0D, $52, $54, $4D, $15, $6A
+mark_2C48: DEFB $01, $14, $02, $06, $00, $81, $0E, $06
+mark_2C50: DEFB $DE, $05, $AB, $0D, $06, $00, $B5, $0E
+mark_2C58: DEFB $00, $DC, $0C, $00, $D8, $0E, $04, $14
+mark_2C60: DEFB $06, $DF, $06, $05, $B9, $0D, $04, $00
+mark_2C68: DEFB $2E, $0E, $05, $E8, $2B, $01, $00, $E9
+mark_2C70: DEFB $0E, $05, $A7, $2E, $05, $6A, $0D, $00
+mark_2C78: DEFB $C3, $03, $03, $AF, $0E, $03, $30, $07
+mark_2C80: DEFB $06, $1A, $06, $00, $92, $0E, $03, $6C
+mark_2C88: DEFB $0E, $05, $40, $03, $05, $4D, $2F, $00
+mark_2C90: DEFB $7C, $0E, $00, $B2, $0E, $03, $4E, $2E
+mark_2C98: DEFB $06, $1A, $06, $1A, $06, $00, $26, $29
+mark_2CA0: DEFB $2A, $A8, $0E, $E9, $00, $0E, $0C, $06
+mark_2CA8: DEFB $00, $AE, $2E, $03, $B2, $2E, $03, $B6
+mark_2CB0: DEFB $2E, $03, $53, $2F, $05, $CB, $0A, $03
+mark_2CB8: DEFB $2C, $07, $FD, $36, $01, $01, $CD, $73
+mark_2CC0: DEFB $0A, $CD, $95, $0A, $21, $00, $40, $36
+mark_2CC8: DEFB $FF, $21, $2D, $40, $CB, $6E, $28, $0E
+mark_2CD0: DEFB $FE, $E3, $7E, $C2, $6F, $0D, $CD, $A6
+mark_2CD8: DEFB $0D, $C8, $CF, $0C, $CF, $08, $DF, $06
+mark_2CE0: DEFB $00, $FE, $76, $C8, $4F, $E7, $79, $D6
+mark_2CE8: DEFB $E1, $38, $3B, $4F, $21, $29, $0C, $09
+mark_2CF0: DEFB $4E, $09, $18, $03, $2A, $30, $40, $7E
+mark_2CF8: DEFB $23, $22, $30, $40, $01, $F4, $0C, $C5
+;
+; bits shifting right within a byte:
+;
+mark_2D00: DEFB $80, $40, $20, $10, $08, $04, $02, $01
+;
+mark_2D08: DEFB $CD, $F7, $2B, $C3, $07, $02, $32, $28
+mark_2D10: DEFB $40, $EB, $21, $0A, $00, $39, $7E, $3C
+mark_2D18: DEFB $E6, $F0, $D6, $D0, $4F, $23, $7E, $D6
+mark_2D20: DEFB $04, $B1, $4F, $3A, $3B, $40, $07, $9F
+mark_2D28: DEFB $A1, $20, $10, $2A, $10, $40, $01, $DF
+mark_2D30: DEFB $FF, $09, $CB, $FC, $22, $04, $23, $3E
+mark_2D38: DEFB $01, $18, $0E, $2A, $0C, $40, $ED, $4B
+mark_2D40: DEFB $00, $23, $09, $CB, $FC, $22, $06, $23
+mark_2D48: DEFB $AF, $32, $19, $23, $2B, $7E, $EB, $C9
+mark_2D50: DEFB $3A, $19, $23, $3D, $C2, $73, $2D, $3E
+mark_2D58: DEFB $1E, $ED, $47, $ED, $6A, $2A, $04, $23
+mark_2D60: DEFB $01, $08, $01, $3E, $FE, $CD, $B5, $02
+mark_2D68: DEFB $3E, $1F, $ED, $47, $3A, $28, $40, $D6
+mark_2D70: DEFB $08, $18, $04, $2B, $3A, $28, $40, $4F
+mark_2D78: DEFB $DD, $E1, $FD, $CB, $3B, $7E, $C2, $9D
+mark_2D80: DEFB $02, $3E, $FE, $06, $01, $21, $9A, $2D
+mark_2D88: DEFB $CD, $95, $2D, $29, $00, $5F, $2A, $06
+mark_2D90: DEFB $23, $CB, $FC, $DD, $E9, $ED, $4F, $3E
+mark_2D98: DEFB $DD, $FB, $76, $21, $00, $22, $3E, $24
+mark_2DA0: DEFB $36, $01, $35, $28, $02, $CF, $1A, $23
+mark_2DA8: DEFB $BC, $20, $F5, $65, $11, $00, $20, $01
+mark_2DB0: DEFB $00, $01, $ED, $B0, $24, $14, $04, $ED
+mark_2DB8: DEFB $B0, $21, $F1, $07, $11, $A0, $23, $01
+mark_2DC0: DEFB $60, $00, $ED, $B0, $16, $20, $21, $B4
+mark_2DC8: DEFB $2F, $46, $18, $04, $5E, $23, $7E, $12
+mark_2DD0: DEFB $23, $10, $F9, $14, $CB, $52, $28, $F1
+mark_2DD8: DEFB $C9
+
+; Delete Display File:
+
+mark_2DD9: DEFB $CD, $E7, $02, $21, $87, $3E, $CD
+mark_2DE0: DEFB $D8, $09, $C0, $EB, $2A, $29, $40, $CB
+mark_2DE8: DEFB $76, $28, $04, $ED, $53, $29, $40, $2A
+mark_2DF0: DEFB $0C, $40, $C3, $5D, $0A, $CD, $D9, $2D
+mark_2DF8: DEFB $01, $92, $19, $2A, $0C, $40, $2B, $CD
+mark_2E00: DEFB $9E, $09, $3E, $76, $12, $13, $12, $23
+mark_2E08: DEFB $23, $36, $3E, $23, $36, $87, $23, $36
+mark_2E10: DEFB $8D, $23, $36, $19, $23, $77, $23, $77
+mark_2E18: DEFB $CD, $07, $02, $3E, $01, $18, $37
+; Check & Set Up Display File:
+mark_2E1F: DEFB $2A
+mark_2E20: DEFB $65, $22, $11, $D9, $BF, $19, $3A, $64
+mark_2E28: DEFB $22, $D6, $21, $B4, $B5, $C4, $9B, $2D
+mark_2E30: DEFB $2A, $0C, $40, $3E, $76, $2B, $2B, $BE
+mark_2E38: DEFB $C4, $F5, $2D, $2A, $0C, $40, $ED, $5B
+mark_2E40: DEFB $00, $23, $19, $22, $06, $23, $11, $09
+mark_2E48: DEFB $00, $19, $22, $08, $23, $C9, $CD, $02
+mark_2E50: DEFB $0C, $C0
+; Clear The Screen:
+mark_2E52: DEFB $3D, $FA, $2A, $0A, $F5, $CD
+mark_2E58: DEFB $1F, $2E, $F1, $FE, $02, $ED, $4B, $18
+mark_2E60: DEFB $23, $2A, $0C, $40, $30, $30, $3D, $23
+mark_2E68: DEFB $22, $0E, $40, $2B, $2B, $2B, $1E, $00
+mark_2E70: DEFB $06, $10, $2B, $73, $2B, $73, $2B, $77
+mark_2E78: DEFB $2B, $77, $10, $FA, $0D, $20, $F1, $06
+mark_2E80: DEFB $09, $3E, $01, $2B, $73, $10, $FC, $21
+mark_2E88: DEFB $34, $23, $06, $14, $3D, $28, $F4, $21
+mark_2E90: DEFB $21, $18, $22, $39, $40, $C9, $C0, $2B
+mark_2E98: DEFB $2B, $06, $20, $2B, $2B, $2B, $7E, $2F
+mark_2EA0: DEFB $77, $10, $FA, $0D, $20, $F3, $C9, $2A
+mark_2EA8: DEFB $96, $0A, $3E, $4D, $18, $35, $3E, $DD
+mark_2EB0: DEFB $18, $2E, $3E, $D6, $18, $02, $3E, $CE
+mark_2EB8: DEFB $F5, $CD, $02, $0C, $06, $1E, $3D, $FE
+mark_2EC0: DEFB $06, $30, $19, $CB, $3F, $67, $28, $02
+mark_2EC8: DEFB $3E, $01, $F5, $9F, $6F, $CB, $8C, $25
+mark_2ED0: DEFB $22, $7B, $40, $CD, $1F, $2E, $06, $1F
+mark_2ED8: DEFB $F1, $32, $14, $23, $78, $ED, $47, $F1
+mark_2EE0: DEFB $2A, $71, $0D, $85, $6F, $E9, $57, $3A
+mark_2EE8: DEFB $39, $40, $E6, $80, $C3, $6C, $2B, $7A
+mark_2EF0: DEFB $D1, $D9, $E5, $D5, $C5, $2A, $0C, $23
+mark_2EF8: DEFB $87, $30, $0B, $2A, $0E, $23, $CB, $77
+mark_2F00: DEFB $28, $04, $2A, $15, $23, $3F, $EB, $6F
+mark_2F08: DEFB $26, $00, $9F, $4F, $3A, $7B, $40, $2F
+mark_2F10: DEFB $FD, $A6, $7C, $A9, $4F, $29, $29, $19
+mark_2F18: DEFB $06, $08, $D9, $D5, $C9, $FD, $35, $39
+mark_2F20: DEFB $3E, $18, $90, $47, $87, $87, $87, $6F
+mark_2F28: DEFB $3A, $18, $23, $95, $D8, $3E, $21, $91
+mark_2F30: DEFB $4F, $26, $00, $29, $09, $ED, $4B, $08
+mark_2F38: DEFB $23, $09, $CD, $EF, $2E, $01, $22, $00
+mark_2F40: DEFB $D9, $79, $AE, $D9, $77, $09, $D9, $23
+mark_2F48: DEFB $10, $F7, $C3, $9A, $29, $CD, $D9, $2D
+mark_2F50: DEFB $C3, $F6, $02, $CD, $02, $0C, $C0, $3D
+mark_2F58: DEFB $FA, $69, $08, $CD, $1F, $2E, $CD, $E7
+mark_2F60: DEFB $02, $3A, $18, $23, $47, $2A, $08, $23
+mark_2F68: DEFB $AF, $5F, $D3, $FB, $3E, $7F, $DB, $FE
+mark_2F70: DEFB $0F, $D2, $86, $08, $DB, $FB, $87, $FA
+mark_2F78: DEFB $AD, $2F, $30, $F0, $0E, $20, $C5, $4E
+mark_2F80: DEFB $06, $08, $CB, $01, $1F, $B3, $57, $DB
+mark_2F88: DEFB $FB, $1F, $30, $FB, $7A, $D3, $FB, $10
+mark_2F90: DEFB $F1, $23, $C1, $0D, $20, $E8, $23, $23
+mark_2F98: DEFB $3E, $03, $B8, $38, $02, $5F, $1D, $DB
+mark_2FA0: DEFB $FB, $1F, $30, $FB, $7B, $D3, $FB, $10
+mark_2FA8: DEFB $C3, $3E, $04, $D3, $FB, $C3, $07, $02
+mark_2FB0: DEFB $FB, $10, $C3, $3E, $0A, $12, $A0, $13
+mark_2FB8: DEFB $23, $15, $A4, $16, $23, $40, $C1, $60
+mark_2FC0: DEFB $08, $61, $2D, $75, $06, $76, $23, $01
+mark_2FC8: DEFB $0A, $54, $02, $85, $C1, $7F, $73, $80
+mark_2FD0: DEFB $2D, $8D, $50, $8E, $2D, $E3, $C3, $E4
+mark_2FD8: DEFB $0E, $E5, $2D, $13, $00, $75, $01, $E6
+mark_2FE0: DEFB $0A, $55, $0D, $1E, $0F, $1E, $16, $20
+mark_2FE8: DEFB $10, $07, $11, $08, $18, $C0, $35, $EE
+mark_2FF0: DEFB $36, $55, $37, $C6, $AD, $E6, $AE, $2E
+mark_2FF8: DEFB $F2, $C3, $F3, $1D, $F4, $2F, $ED, $00
+;
+#else
+; G007 source code, reverse engineered.
+;
+; The ZX81 labels will have been generated already.
+; No need to include ZX definitions:
+;
+
+
+;===============================
+; G007 Hi-Res graphics board for the ZX81
+;
+; Source code partially reverse-engineered.
+; A work in progress...
+;
+; 2011-05-15 Assembles to create correct ROM image.
+;
+; Verifed by comparing the hex files,
+; the known-good reference generated by
+; 2048 defb statements containing the original bytes.
+;
+; The source code below is not guaranteed to create the ROM image
+; exactly the way the original author had in mind,
+; as I'm not him and I don't have the original source code.
+;
+; This file was created from a disassembly generated by the
+; impressive VB81 emulator program.
+;
+; It was already know that the G007 switched out pages of
+; the ZX81 BASIC ROM and patched in replacements from
+; the G007 ROM and the ZX81's internal 1K (or 2K) RAM.
+; This is a thrifty use of RAM that expansion RAM packs
+; usually just disabled.
+;
+; Memory map:
+;
+; 0000-0FFF
+; 2000-23FF 1K RAM inside ZX81, remapped here.
+; 2400-27FF 1K RAM more RAM if ZX81 has a 2K RAM chip
+; 2800-2FFF 2K G007
+; 3000-3FFF Empty
+; 4000-7FFF External RAM pack
+;
+; Patching:
+;
+; 2C00-2CFF (ROM) also appears at 0C00-0CFF
+; 2000-20FF (RAM) also appears at 0000-00FF
+; 2200-22FF (RAM) also appears at 0200-02FF
+;
+; The ROM patching is active all the time.
+; The RAM patching is active only in hi-res mode.
+;
+; The patches didn't cover every modification needed,
+; so one routine is copied from ROM to RAM and
+; then individual bytes are modified there
+; by the initialisation routine which also
+; initialises some variables.
+;
+; Graphics routines:
+;
+; These have not yet been analysed.
+; Bresenham's algorithm will be there is some form.
+;
+; Triangle-filling is a sophisticated feature,
+; using 8-bit maths for a practical speed.
+;
+; Future enhancements
+;
+; Programmers may like to try writing faster routines,
+; or adding more commands now that memory is cheap.
+;
+; The bit-mask array at $2D00 looks like this:
+; 10000000
+; 01000000
+; 00100000
+; 00010000
+; 00001000
+; 00000100
+; 00000010
+; 00000001
+;
+; and suggests that pixels
+; may be plotted one at a time.
+; When I wrote a triangle-filling algorithm for the Atom,
+; I made frequent use of horizontal lines.
+; I optimised these by working out the partially-filled
+; bytes at the left and right sides, and writing
+; whole bytes (of 8 pixels) between the partial bytes.
+; This used two overlapping tables like so:
+;
+; 10000000 ; BIT_MASK_R
+; 11000000
+; 11100000
+; 11110000
+; 11111000
+; 11111100
+; 11111110
+; 11111111 ; BIT_MASK_L
+; 01111111
+; 00111111
+; 00011111
+; 00001111
+; 00000111
+; 00000011
+; 00000001
+;
+; This technique might be able to increase the triangle filling speed.
+;
+; A proper ellipse algorithm would also be welcome,
+; not one of those approximations using polygons!
+;
+;===============================
+; Assembly was done by a very handy online tool:
+; http://k1.dyndns.org/cgi-bin/zasm.cgi
+; which avoids the chore of installing it
+; on one's own machine.
+; The online tool is limited to one source file and
+; one include file, but that's not a big deal
+; for a tiny 2K ROM like this one.
+;
+;===============================
+; Problems
+; The G007 ROM had several instances of opcdoes
+; beginning with FD CB
+; involving the Y register.
+; VB81 disassembled them into statements
+; that looked mangled and would not re-assemble.
+; So I've just added them with defb statements.
+;
+;
+;===============================
+; Global constants:
+;===============================
+FALSE equ 0
+NOT_G007 equ FALSE
+
+
+
+;===============================
+; ZX81 constants:
+;===============================
+; ZX characters (not the same as ASCII)
+;-------------------------------
+ZX_EQU equ $14
+ZX_COMMA equ $1A
+ZX_THEN equ $DE
+ZX_TO equ $DF
+ZX_INV_K equ $B0
+ZX_NEWLINE equ $76
+HRG_BYTES_PER_LINE equ 34
+ALL_BITS_SET equ -1
+;-------------------------------
+; tokens
+;-------------------------------
+_CLASS_00 equ 0
+_CLASS_01 equ 1
+_CLASS_02 equ 2
+_CLASS_03 equ 3
+_CLASS_04 equ 4
+_CLASS_05 equ 5
+_CLASS_06 equ 6
+;===============================
+; ZX81 I/O locations:
+;===============================
+IO_PORT_KEYBOARD_RD equ $FE ; A0 low
+ZX_NMI_GEN equ $FD ; A1 low
+ZX_PRINTER_PORT equ $FB ; A2 low
+;===============================
+; ZX81 RAM variables
+;===============================
+RAMBASE equ $4000
+ERR_NR equ $4000
+FLAGS equ $4001
+ERR_SP equ $4002
+RAMTOP equ $4004
+MODE equ $4006
+PPC equ $4007
+VERSN equ $4009
+E_PPC equ $400A
+D_FILE equ $400C
+DF_CC equ $400E
+VARS equ $4010
+DEST equ $4012
+E_LINE equ $4014
+CH_ADD equ $4016
+X_PTR equ $4018
+STKBOT equ $401A
+STKEND equ $401C
+BERG equ $401E
+MEM equ $401F
+UNUSED_8 equ $4021
+G007_FLAG_Y equ UNUSED_8 ;
+
+DF_SZ equ $4022
+S_TOP equ $4023
+LAST_K equ $4025
+DEBOUNCE equ $4027
+MARGIN equ $4028
+NXTLIN equ $4029
+NXT_LINE equ $4029
+OLDPPC equ $402B
+FLAGX equ $402D
+
+STRLEN equ $402E
+T_ADDR equ $4030
+SEED equ $4032
+FRAMES equ $4034
+COORDS equ $4036
+PR_CC equ $4038
+S_POSN equ $4039
+S_POSN_hi equ S_POSN+1
+CDFLAG equ $403B
+PRBUFF equ $403C
+MEMBOT equ $407B
+PROGRAM equ $407D
+UNUSED_16 equ $407B
+UNUSED_16_hi equ UNUSED_16+1
+
+G007_RESTART equ UNUSED_16
+
+; First byte after system variables:
+USER_RAM equ $407D
+MAX_RAM equ $7FFF
+
+;===============================
+; ZX BASIC ROM addresses
+;===============================
+; restart constants
+START equ $0000 ; = 0
+ERROR_1 equ $0008 ; = 8
+PRINT_A equ $0010 ; = 16
+GET_CHAR equ $0018 ; = 24
+TEST_SP equ $001C ; = 28
+NEXT_CH equ $0020 ; = 32
+FP_CALC equ $0028 ; = 40
+;-------------------------------
+L0108 equ $0108
+SLOW_FAST equ $0207 ;
+DISPLAY_5 equ $02B5 ;
+L029D equ $029D ; Inside the 'LOC_ADDR' subroutine
+SET_FAST equ $02E7
+LOAD equ $0340
+LIST equ $0730
+COPY equ $0869
+SAVE equ $02F6
+NEW equ $03C3
+LLIST equ $072C
+PRINT_CH equ $07F1 ; old, replaced
+L0809 equ $0809
+LOC_ADDR equ $0918
+ONE_SPACE equ $099B
+MAKE_ROOM equ $099E
+LINE_ADDR equ $09D8
+E_LINE_NO equ $0A73
+L0A95 equ $0A95 ; []*BIOS ROM*. Part way into 088A COPY_CONT
+PRINT equ $0ACF
+CLS equ $0A2A
+RECLAIM_1 equ $0A5D
+LPRINT equ $0ACB
+PRINT equ $0ACF
+PLOT_UNPLOT equ $0BAF
+L0BB2 equ $0BB2 ;
+STK_TO_A equ $0C02
+SCROLL equ $0C0E
+
+;-------------------------------
+; Parameter table addresses:
+; Checked in ROM disassembly book:
+;-------------------------------
+P_LET equ $0C48
+P_GOTO equ $0C4B
+P_IF equ $0C4F
+P_GOSUB equ $0C54
+P_STOP equ $0C58
+P_RETURN equ $0C5B
+P_FOR equ $0C5E
+P_NEXT equ $0C66
+P_PRINT equ $0C6A
+P_INPUT equ $0C6D
+P_DIM equ $0C71
+P_REM equ $0C74
+P_NEW equ $0C77
+P_RUN equ $0C7A
+P_LIST equ $0C7D
+P_POKE equ $0C80
+P_RAND equ $0C86
+P_LOAD equ $0C89
+P_SAVE equ $0C8C
+P_CONT equ $0C8F
+P_CLEAR equ $0C92
+P_CLS equ $0C95
+
+P_PLOT equ $0C98 ; redefined in G007 patch
+P_UNPLOT equ $0C9E ; redefined in G007 patch
+P_SCROLL equ $0CA4
+
+P_PAUSE equ $0CA7
+;P_SLOW equ $0CAB
+;P_FAST equ $0CAE
+;P_COPY equ $0CB1
+;P_LPRINT equ $0CB4
+;P_LLIST equ $0CB7
+;-------------------------------
+
+STOP equ $0CDC ; defined in this file
+
+REM equ $0D6A
+INPUT_RE equ $0D6F
+
+REPORT_C equ $0D9A ; according to the book
+REPORT_C_007 equ $0D26 ; seems the right one
+SYNTAX_Z equ $0DA6
+IF equ $0DAB
+FOR equ $0DB9
+
+NEXT equ $0E2E
+RAND equ $0E6C
+CONT equ $0E7C
+GOTO equ $0E81
+POKE equ $0E92
+L0EA8 equ $0EA8 ; Inside the 'FIND_INT.' subroutine
+REPORT_B equ $0EAD
+ ; check this!!!
+RUN equ $0EAF
+CLEAR_007 equ $0EB2 ; goes to JP CLEAR
+GOSUB equ $0EB5
+RETURN equ $0ED8
+INPUT equ $0EE9
+
+FAST equ $0F23
+SLOW equ $0F2B
+PAUSE equ $0F32
+
+DIM equ $1409
+CLEAR equ $149A
+SET_MEM equ $14BC
+
+
+;===============================
+; G007 Memory patch addresses
+;===============================
+; These patches only appear in hi-res mode
+;
+RAM_PATCH_0000 equ $0000
+RAM_PATCH_0200 equ $0200
+ROM_PATCH_0C00 equ $0C00
+;
+; Their aliases are always present:
+;
+RAM_PATCH_2000 equ $2000
+RAM_PATCH_2200 equ $2200
+;===============================
+; G007 Plot number notes (a work in prgress)
+;===============================
+; N-1
+;
+; 76543 210
+; ..... .00 Line in white.
+; ..... .01 Line black.
+; ..... .10 Line inverting.
+; ..... .11 Line inverting, omit last pixel.
+;
+; ..... 0.. Absolute co-ordinates
+; ..... 1.. Relative co-ordinates
+;
+; ....0 ... Line
+; ....1 ... Single pixel
+;
+; .0100 ... Coarse dotted line
+; .0101 ... Add 40: triangle, plain
+; .1001.... Add 72: triangle, textured (not available in invert mode)
+; .1000 ... Add 64: fine dotted line
+; .1100 ... Add 96: chain dotted line
+;
+; Note that PLOT 12 and PLOT 16 miss out the pixel, so simply move the PLOT position.
+;
+
+
+
+
+
+;===============================
+; G007 Byte constants
+;===============================
+
+
+
+
+
+G007_INT_REG_VALUE_FOR_HI_RES_GRAPHICS equ $1F
+G007_INT_REG_VALUE_FOR_LO_RES_GRAPHICS equ $1E
+
+; Dec. Hex. Bytes System Variables: G007
+
+; 8448 2100 Reserved for user defined characters
+; 8703 21FF
+;
+; 8704 2200 Another page, possibly
+; 8959 22FF
+
+;===============================
+; G007 Byte constants
+;===============================
+MEM_PATCH_SIZE equ $0100
+;===============================
+; G007 RAM variables
+;===============================
+
+V2265 equ $2265
+V2264 equ $2264
+
+; Dec. Hex. Bytes System Variables: G007
+; 8960 2300 2 Offset of hi-res display file, less 9, from the D-FILE variable
+; 8962 2302 2 Not used
+; 8964 2304 2 Start address of last line of lo-res display file
+
+G007_DISPLAY_OFFSET_FROM_DFILE_LESS_9 equ $2300 ; conflict!
+
+G007_UNUSED_2302 equ $2302
+G007_DISPLAY_ADDRESS_LO_RES_LAST_LINE equ $2304
+G007_DISPLAY_ADDRESS_LESS_9 equ $2306 ; 8966 2306 2 Start address of hi-res display file, less 9 (used for video)
+G007_DISPLAY_ADDRESS equ $2308 ; 8968 2308 2 Start address of hi-res display file
+G007_TRIANGLE_TEXTURE equ $230A ; 8970 230A 2 Bytes defining triangle texture
+G007_CHAR_TABLE_ADDR_0_63 equ $230C ; 8972 230C 2 Character table address for CHR$0-63
+G007_CHAR_TABLE_ADDR_128_159 equ $230E ; 8974 230E 2 Character table address for CHR$128-159
+G007_PLOT_ROUTINES_VECTOR equ $2310 ; 8976 2310 2 Vector for additional plot routines
+G007_FLAG_0 equ $2312 ; 8978 2312 3 * Various flags
+G007_FLAG_1 equ $2313
+G007_FLAG_2 equ $2314
+G007_USR_DEF_CHR_TAB_LESS_256 equ $2315 ; 8981 2315 2 Address of user-defined character table, less 256
+G007_READ_POINT_BYTE equ $2317 ; 8983 2317 1 Read-point byte. Non-zero if pixel is set.
+G007_DISPLAY_HEIGHT equ $2318 ; 8984 2318 1 * Display height, normally 192
+G007_FLAG_3 equ $2319 ; 8985 2319 1 Flags
+
+;G007_TEMP_WORD_0 equ $231C ;
+G007_TEMP_BYTE_0 equ $231A ; 9886 231A 7 Temporary variables for PLOT routine.
+G007_TEMP_BYTE_1 equ $231B
+
+G007_TEMP_WORD_1 equ $231C ;
+;G007_TEMP_BYTE_2 equ $231C
+;G007_TEMP_BYTE_3 equ $231D
+
+G007_TEMP_WORD_2 equ $231E ;
+G007_TEMP_BYTE_4 equ $231E
+G007_TEMP_BYTE_5 equ $231F
+
+G007_TEMP_BYTE_6 equ $2320
+G007_OUT_OF_RANGE_FLAGS equ $2321 ; 8993 2321 1 Plot out of range flags. Bit 7 = latest statement
+G007_UNUSED equ $2322 ; 8994 2322 1 Not used
+G007_PLOT_X equ $2323 ; 8995 2323 2 X co-ordinate for PLOT. Signed 16-bit
+G007_PLOT_Y equ $2325 ; 8997 2325 2 Y co-ordinate for PLOT. Signed 16-bit
+G007_PLOT_X_PREVIOUS_N1 equ $2327 ; 8999 2327 8 X and Y co-ordinates for previous two statements
+G007_PLOT_Y_PREVIOUS_N1 equ $2329
+G007_PLOT_X_PREVIOUS_N2 equ $232B
+G007_PLOT_Y_PREVIOUS_N2 equ $232D
+G007_FLAG_4 equ $232F ; 9007 232F 1 Flags
+G007_ORIGIN_Y equ $2330 ; 9008 2330 2 Y co-ordinate of graphics origin
+G007_ORIGIN_X equ $2332 ; 9010 2332 2 X co-ordinate of graphics origin
+G007_LINE_TYPE equ $2334 ; 9012 2334 4 Bytes defining four line types
+G007_TEMP_BYTE_7 equ $2338 ; 9016 2338 2 Temporary variable for PLOT
+G007_TEMP_BYTE_8 equ $2339 ;
+G007_V23A0 equ $23A0
+;===============================
+; G007 RAM routines
+;===============================
+; Yes, there is such a thing!
+L23B8 equ $23B8
+;===============================
+; G007 ROM routines
+;===============================
+L2BF7 equ $2BF7 ; invalid opcode address
+SLOW_FAST_007 equ $2D08 ; new!
+;===============================
+; Needed by zasm:
+#target rom ; declare target file format
+#code $2800,$0800 ; declare code segment start and size
+;
+;===============================
+; G007 ROM assembly code
+;===============================
+; Start of ROM contents;
+; 10240/12287 : 2800/2FFF
+;===============================
+G007_GET_PIXEL_ADDRESS_AND_MASK: ;
+ LD HL,(G007_PLOT_X) ; X co-ordinate for PLOT. Signed 16-bit. Fetch 16 bits
+L2803:
+ LD A,(G007_PLOT_Y) ; Y co-ordinate for PLOT. Signed 16-bit. Fetch 8 bits
+ LD H,A ; and store in H
+L2807:
+ EX DE,HL ; then swap it into D
+ LD A,(G007_DISPLAY_HEIGHT)
+ DEC A;
+ SUB D ;
+ LD D,A
+ LD A,7 ; A must be 0 to 7
+ AND A,E ; E is the LS byte of the X co-ordinate
+ LD L,A ; L = A = selects bit-mask
+ LD H,$2D ; bit-mask array is at $2D00
+ LD A,(HL) ; get byte with bit set at appropriate position
+
+ LD HL,(G007_DISPLAY_ADDRESS) ; Start address of hi-res display file
+;-------------------------------
+; Add two bytes for every line
+;
+ LD C,D ; BC = Y coordinate
+ LD B,0
+ ADD HL,BC ; HL += BC*2
+ ADD HL,BC
+;-------------------------------
+; DE /= 8 gets byte offset from left of screen
+;
+ SRL D ; shift DE right
+ RR E
+ SRL D ; shift DE right
+ RR E
+ SRL D ; shift DE right
+ RR E
+
+ ADD HL,DE ; HL = G007_DISPLAY_ADDRESS + byte offset of XY-co-ordinates
+ LD B,A ; A and B hold the bitmask
+ RET ; return
+;===============================
+L282C:
+ LD A,(G007_OUT_OF_RANGE_FLAGS) ; Plot out of range flags. Bit 7 = latest statement
+
+ LD HL,(G007_PLOT_X_PREVIOUS_N1)
+ LD DE,(G007_PLOT_X) ; X co-ordinate for PLOT. Signed 16-bit
+
+ EXX
+
+ LD HL,(G007_PLOT_Y)
+ LD DE,(G007_PLOT_Y_PREVIOUS_N1)
+
+ ; test two most recent out-of-range flag bits:
+ BIT 6,A
+ JR Z,L285E
+ BIT 7,A
+ RET NZ
+
+ LD A,(G007_FLAG_Y);
+ PUSH AF
+ LD B,8
+;-------------------------------
+loop_284B:
+ RRCA
+
+;284C FD;CB;21;16 LD C,SLA (IY+CH_ADD-RAMBASE) output from VB81 disassembler
+
+#if 1
+ RL (IY+G007_FLAG_Y-RAMBASE) ; makes FD CB 21 16 - correct!
+#else ; force bytes
+ defb $FD
+ defb $CB
+ defb $21
+ defb $16
+#endif
+ DJNZ loop_284B
+;-------------------------------
+ EX DE,HL
+ EXX
+ EX DE,HL
+ EXX
+ CALL L285E
+ POP AF
+ LD (G007_FLAG_Y),A;
+ RET
+;===============================
+L285E:
+ LD BC,-HRG_BYTES_PER_LINE ; NB negative number = $FFDE
+ LD A,(G007_DISPLAY_HEIGHT)
+ DEC A
+ SUB E
+ SBC HL,DE
+ JP P,L2876
+;-------------------------------
+ LD BC,HRG_BYTES_PER_LINE
+ LD A,E
+
+ PUSH DE
+ ADD HL,DE
+ EX DE,HL
+ OR A,A
+ SBC HL,DE
+ POP DE
+;-------------------------------
+L2876:
+ PUSH HL
+ LD H,E
+ LD E,A
+ EXX
+
+ LD BC,L28FB
+ LD A,L
+ EXX
+ LD L,A
+ EXX
+ OR A,A
+ SBC HL,DE
+ JP P,L2890 ; [10384]
+;-------------------------------
+ LD BC,L2904
+ CPL
+ ADD HL,DE
+ EX DE,HL
+ OR A,A
+ SBC HL,DE
+L2890:
+ LD (G007_TEMP_BYTE_0),BC ; Temporary variables for PLOT routine.
+ POP DE
+ OR A,A
+ SBC HL,DE
+ ADD HL,DE
+ JR NC,L28A4
+;-------------------------------
+ EX DE,HL
+
+ LD BC,L28F6
+ EXX
+ LD D,E
+ LD E,A
+ LD A,D
+ EXX
+L28A4:
+ LD (G007_TEMP_WORD_1),BC
+ EXX
+ LD D,A
+;-------------------------------
+ PUSH DE
+ EXX
+ POP BC
+;-------------------------------
+ LD A,(G007_OUT_OF_RANGE_FLAGS) ; Plot out of range flags. Bit 7 = latest statement
+ CP $40
+ JR NC,L28B6
+;-------------------------------
+ LD B,L ; BC = LE
+ LD C,E
+;-------------------------------
+L28B6:
+ INC B
+ INC C
+;-------------------------------
+L28B8:
+ SRL H
+ JR C,L28BE
+
+ JR Z,L28C6
+;-------------------------------
+L28BE:
+ RR L
+ SRL D
+ RR E
+ JR L28B8
+;-------------------------------
+L28C6:
+ LD D,L
+ SRL L
+ EXX
+ PUSH BC
+ CALL L2807
+ POP DE
+ LD A,(G007_TEMP_BYTE_5)
+ LD C,A
+;-------------------------------
+L28D3:
+; Bytes from disassembler:
+;28D3 FD;CB;21;06 LD C,SLA (IY+6)
+#if 1
+ RLC (IY+G007_FLAG_Y-RAMBASE) ; makes FD CB 21 06
+#else
+; force bytes
+ defb $FD
+ defb $CB
+ defb $21
+ defb $06
+#endif
+
+ JR C,L28E1
+;-------------------------------
+ LD A,(G007_TEMP_BYTE_4)
+ XOR A,(HL)
+ OR A,C
+ AND A,B
+ XOR A,(HL)
+ LD (HL),A
+;-------------------------------
+L28E1:
+ EXX
+
+ LD A,L
+ DEC B
+ RET Z
+;-------------------------------
+ SUB E
+ JR NC,L28F2
+;-------------------------------
+ DEC C
+ RET Z
+;-------------------------------
+ ADD A,D
+ EXX
+ ADD HL,DE
+ EXX
+ LD HL,(G007_TEMP_BYTE_0) ; Temporary variables for PLOT routine.
+ JP (HL)
+;-------------------------------
+L28F2:
+ LD HL,(G007_TEMP_WORD_1)
+ JP (HL)
+;-------------------------------
+L28F6:
+ LD L,A
+ EXX
+ ADD HL,DE
+ JR L28D3
+;-------------------------------
+L28FB:
+ LD L,A
+ EXX
+ RLC B
+ JR NC,L28D3
+;-------------------------------
+ DEC HL
+ JR L28D3
+;-------------------------------
+L2904:
+ LD L,A
+ EXX
+
+ RRC B
+ JR NC,L28D3
+;-------------------------------
+ INC HL
+ JR L28D3
+;-------------------------------
+G007_INTEGER_FROM_STACK_TO_BC:
+ LD HL,(L0EA8) ; Inside the 'FIND_INT.' subroutine
+ ; 0EA7 FIND_INT CALL 158A,FP_TO_BC
+ ; so 0EA8 should contain $158A, pointing to the FP_TO_BC routine
+ JP (HL) ; exit
+;===============================
+G007_INTEGER_FROM_STACK_TO_HL:
+ CALL G007_INTEGER_FROM_STACK_TO_BC ; get an integer
+ JR C,L2924
+;-------------------------------
+ LD HL,0
+ JR Z,L2921
+;-------------------------------
+ SBC HL,BC ; HL = 0 - BC
+ RET M
+ RET Z
+;-------------------------------
+ JR L2924
+;-------------------------------
+L2921:
+ ADC HL,BC
+ RET P
+;-------------------------------
+L2924:
+ POP HL
+ RET
+;===============================
+;
+G007_PLOT_UNPLOT_N_X_Y: ; takes parameters from the stack
+;
+ CALL G007_INTEGER_FROM_STACK_TO_HL ; get Y
+ LD (G007_PLOT_Y),HL
+ CALL G007_INTEGER_FROM_STACK_TO_HL ; get X
+ PUSH HL
+ CALL STK_TO_A ; get N
+ ;
+ POP BC ; BC = X
+ LD DE,(G007_PLOT_Y) ; DE = Y
+ JR Z,G007_PLOT ;
+ DEC A ; if (N-1) is zero
+ RET NZ
+;-------------------------------
+ LD HL,L0C9C ; then T_ADDR = HL = L0C9C
+ LD (T_ADDR),HL ;
+;-------------------------------
+L2942:
+ LD B,E
+ JP L0BB2 ; []*BIOS ROM*
+;===============================
+; The Plot Routine:
+; A == the plot number N
+; BC == screen X
+; DE == screen Y
+;-------------------------------
+G007_PLOT:
+ OR A,A
+ JR Z,L2942
+;-------------------------------
+ DEC A ; when PLOT N value is decremented, bits make more sense
+;-------------------------------
+ PUSH DE ; push screen Y
+ PUSH BC ; push screen X
+ PUSH AF ; push plot number-1
+
+ CALL G007_CHECK_AND_SET_UP_DISPLAY_FILE
+
+ POP AF
+ POP DE
+ POP BC ; NB BC and DE are swapped
+;-------------------------------
+ CP $81 ; Was A=129 (PLOT 130 sets graphics origin)
+ JR NZ,G007_PLOT_XY_TO_HISTORY
+
+;-------------------------------
+G007_ORIGIN_SET:
+ ;
+ LD (G007_ORIGIN_Y),DE ; Y co-ordinate of graphics origin
+ LD (G007_ORIGIN_X),BC ; X co-ordinate of graphics origin
+ LD BC,$0000 ; BC = 0
+ LD D,B ; DE = BC = 0
+ LD E,B
+ LD A,$0B
+
+;-------------------------------
+G007_PLOT_XY_TO_HISTORY:
+ ;
+ BIT 2,A; ; the (PLOT_N -1)
+ JR Z,G007_PLOT_ABSOLUTE
+
+;-------------------------------
+G007_PLOT_RELATIVE: ;
+ LD HL,(G007_PLOT_Y_PREVIOUS_N1)
+ PUSH HL
+ LD HL,(G007_PLOT_X_PREVIOUS_N1)
+ JR G007_PLOT_ABSOLUTE_OR_RELATIVE
+;===============================
+G007_PLOT_ABSOLUTE:
+ LD HL,(G007_ORIGIN_X) ; X co-ordinate of graphics origin
+ PUSH HL
+ LD HL,(G007_ORIGIN_Y) ; Y co-ordinate of graphics origin
+;------------------------------- ;
+ ;
+G007_PLOT_ABSOLUTE_OR_RELATIVE: ;
+ OR A,A ; update flags
+ ADC HL,DE ; HL = G007_PLOT_Y + G007_ORIGIN_Y
+ POP DE ; DE = G007_PLOT_X co-ordinate of graphics origin
+ RET PE
+;-------------------------------
+ LD (G007_PLOT_X),HL ; record plot X
+ EX DE,HL
+ OR A,A ; restore Flags
+ ADC HL,BC ; HL = G007_PLOT_X + G007_ORIGIN_X
+ RET PE
+;-------------------------------
+ LD (G007_PLOT_Y),HL ; record plot Y
+ LD E,A
+ LD A,%11000000 ; $C0
+ AND A,H ;
+ RET PO;
+;-------------------------------
+ LD A,%11000000 ; $C0
+ AND A,D
+ RET PO
+;------------------------------- ; push alternate HL,DE,BC
+ EXX
+ PUSH HL
+ PUSH DE
+ PUSH BC
+ CALL G007_RANGE_CHECK
+;------------------------------- ; pop alternate BC,DE,HL
+L299A:
+ POP BC
+ POP DE
+ POP HL
+ EXX
+ RET
+;===============================
+G007_RANGE_CHECK:
+ EXX
+ LD A,H ; A = H OR D
+ OR A,D
+ SCF
+ JR NZ,G007_RANGE_CHECK_UPDATE ; if not zero, out of range already so ignore display height
+;-------------------------------
+ LD A,(G007_DISPLAY_HEIGHT) ; check Y coordinate
+ DEC A ; if (G007_DISPLAY_HEIGHT - Y ) is less than zero,
+ CP L ; set the Carry flag
+;-------------------------------
+G007_RANGE_CHECK_UPDATE: ; latest flag in Carry is shifted into history flags
+ LD A,(G007_OUT_OF_RANGE_FLAGS) ;
+ RRA ; shift bits right. Bit 7 = latest statement
+ LD (G007_OUT_OF_RANGE_FLAGS),A ;
+ LD A,E ; E bit 7
+ BIT 7,A
+ JR Z,L29BA
+;------------------------------- ; gets here if PLOT_N is over 130
+ LD HL,(G007_PLOT_ROUTINES_VECTOR) ; Vector for additional plot routines
+ JP (HL) ; This looks very promising,
+ ; makes it easy to bolt-on extra graphics routines
+;===============================
+L29BA:
+ PUSH AF
+ AND 3
+ RRA
+ DEC A
+ CPL
+ LD H,A
+ SBC A,A
+ LD L,A
+ LD (G007_TEMP_WORD_2),HL
+ PUSH HL
+ BIT 3,E
+ JR NZ,L29F3
+;-------------------------------
+ LD A,(G007_TEMP_BYTE_6)
+ XOR A,E
+ AND %11111011 ; $FB. Bit 2 ignored
+ JR Z,L29E2
+;-------------------------------
+ LD HL,G007_LINE_TYPE
+ LD A,E
+;------------------------------- ; A *= 8
+ RLCA
+ RLCA
+ RLCA
+;-------------------------------
+ AND $03
+ ADD A,L
+ LD L,A
+ LD A,(HL)
+ LD (G007_FLAG_Y),A;
+;-------------------------------
+L29E2:
+ CALL L282C;
+ POP HL
+
+;-------------------------------
+; Bytes from disassembler: 29E6 FD;CB;21;0E LD C,SLA (IY+14)
+
+ RRC (IY+G007_FLAG_Y-RAMBASE) ; makes FD CB 21 0E
+ JR C,L2A12
+;===============================
+ LD A,H ; A = H and L
+ AND A,L
+ JR Z,L2A12
+;-------------------------------
+ INC L
+ LD E,L
+ PUSH HL
+;-------------------------------
+L29F3:
+ LD A,(G007_OUT_OF_RANGE_FLAGS) ; Bit 7 = latest statement
+ BIT 5,E
+ JR NZ,G007_TRIANGLE_PLAIN
+;-------------------------------
+ BIT 6,E
+ JR NZ,G007_TRIANGLE_TEXTURED
+;-------------------------------
+ POP HL
+ BIT 7,A
+ JR NZ,L2A12
+;-------------------------------
+ PUSH HL
+ CALL G007_GET_PIXEL_ADDRESS_AND_MASK
+ AND A,(HL)
+ LD (G007_READ_POINT_BYTE),A ; "Read-point" byte. Non-zero if pixel is set.
+ POP DE
+ LD A,(HL)
+ OR A,D
+ XOR A,E
+ AND A,B
+ XOR A,(HL)
+ LD (HL),A
+;-------------------------------
+L2A12:
+ POP AF
+ LD (G007_TEMP_BYTE_6),A
+ LD HL,G007_PLOT_Y_PREVIOUS_N1+1
+ LD DE,G007_PLOT_Y_PREVIOUS_N2+1
+ LD BC,8
+ BIT 4,A
+ JR Z,L2A34
+;-------------------------------
+ LD A,(G007_OUT_OF_RANGE_FLAGS) ; Plot out of range flags. Bit 7 = latest statement
+ AND $C0 ; only want most recent two bits
+ RLA
+ JR NC,L2A2D
+ SET 6,A ; carry flag into bit 6 of A
+;-------------------------------
+L2A2D:
+ LD (G007_OUT_OF_RANGE_FLAGS),A
+ LD L,$26
+ LD C,4
+;-------------------------------
+L2A34:
+ LDDR ; (DE--)=(HL--); BC-- UNTIL BC IS ZERO
+ RET
+;===============================
+G007_TRIANGLE_PLAIN:
+ LD HL,ALL_BITS_SET
+ JR G007_TRIANGLE_TEXTURED_BY_HL
+;===============================
+G007_TRIANGLE_TEXTURED:
+ LD HL,(G007_TRIANGLE_TEXTURE) ; Bytes defining triangle texture
+;-------------------------------
+G007_TRIANGLE_TEXTURED_BY_HL:
+ EXX
+ AND $E0 ; = 224
+ JP NZ,REPORT_B;
+;-------------------------------
+L2A45:
+ LD HL,G007_OUT_OF_RANGE_FLAGS
+ LD (IY+G007_FLAG_Y-RAMBASE),$55
+ LD B,3
+;-------------------------------
+loop_2A4E:
+ INC HL ; HL+=2
+ INC HL
+ LD E,(HL)
+ INC HL ; HL+=2
+ INC HL
+ LD D,(HL)
+ PUSH DE
+ DJNZ loop_2A4E ; BC--
+;-------------------------------
+ POP BC ; restore register pairs
+ POP DE
+ POP HL
+;-------------------------------
+ OR A,A
+ JR Z,L2A5F
+;-------------------------------
+ LD B,H ; BC=HL
+ LD C,L
+;-------------------------------
+L2A5F:
+ LD A,B
+ CP H
+ JR NC,L2A66
+;-------------------------------
+ PUSH BC
+ EX (SP),HL
+ POP BC
+;-------------------------------
+L2A66:
+ LD A,H
+ CP D
+ JR NC,L2A6B
+;-------------------------------
+ EX DE,HL
+;-------------------------------
+L2A6B:
+ LD A,D
+ EXX
+ AND 7
+ INC A
+ LD B,A
+ LD A,H
+;-------------------------------
+loop_2A72:
+ RLC L ; L *= 2
+;-------------------------------
+ RLCA ; A *= 8
+ RLCA
+ RLCA
+;-------------------------------
+ DJNZ loop_2A72 ; loop while BC-- is not zero
+;-------------------------------
+ LD H,A
+ LD (G007_TEMP_BYTE_7),HL ; Temporary variable for PLOT
+ EXX
+;-------------------------------
+ LD A,H
+ CP B
+ LD A,7
+ JR NC,L2A87
+;-------------------------------
+loop_2A84:
+ PUSH BC
+ EX (SP),HL
+ POP BC
+;-------------------------------
+L2A87:
+ EX DE,HL
+ PUSH AF
+ LD A,C
+ EXX
+ LD H,A
+ LD L,A
+ LD C,A
+ LD (G007_TEMP_BYTE_0),HL ; Temporary variables for PLOT routine.
+ LD B,$FE
+ EXX
+ SUB E
+ EXX
+;-------------------------------
+ JR NC,skip_2A9C
+ LD B,$00
+ NEG
+skip_2A9C:
+;-------------------------------
+ LD D,A
+ EXX
+ LD A,D
+ SUB B
+ EXX
+ LD H,A
+;-------------------------------
+ CP D
+ JR NC,skip_2AA7
+ EX DE,HL
+ INC B
+skip_2AA7:
+;-------------------------------
+ LD L,H
+ INC L
+ LD E,H
+ SRL E
+
+ POP AF
+ RRCA
+ JR NC,L2AB9
+;-------------------------------
+ PUSH HL
+;-------------------------------
+ PUSH DE
+ PUSH BC
+;-------------------------------
+ RRCA
+ EXX
+;-------------------------------
+ JR C,loop_2A84
+ JR L2A87
+;===============================
+L2AB9:
+ EXX
+ LD H,B
+ LD L,1
+;-------------------------------
+loop_2ABD:
+ POP BC
+ POP DE
+;-------------------------------
+ EX (SP),HL
+;-------------------------------
+L2AC0:
+ DEC L
+ JR NZ,L2AD9
+;-------------------------------
+ LD HL,(G007_TEMP_BYTE_0) ; Temporary variables for PLOT routine.
+ LD A,C
+;-------------------------------
+ CP H
+ JR C,skip_2ACB
+ LD H,A
+skip_2ACB:
+;-------------------------------
+ CP L
+ JR NC,skip_2ACF
+ LD L,A
+skip_2ACF:
+;-------------------------------
+ LD (G007_TEMP_BYTE_0),HL ; Temporary variables for PLOT routine.
+ POP HL
+ DEC L
+;-------------------------------
+L2AD4:
+
+ JR Z,loop_2ABD
+
+ PUSH HL
+ JR L2AEE
+;-------------------------------
+L2AD9:
+ LD A,E
+ SUB D
+ JR C,L2AE7
+;-------------------------------
+ LD E,A
+ BIT 0,B
+ JR Z,L2AEE
+;-------------------------------
+ LD A,C
+ ADD A,B
+ LD C,A
+ JR L2AC0
+;-------------------------------
+L2AE7:
+ ADD A,H
+ LD E,A
+ LD A,1
+ OR A,B
+ ADD A,C
+ LD C,A
+;-------------------------------
+L2AEE:
+ LD A,C
+ EXX
+;-------------------------------
+ RRC (IY+G007_FLAG_Y-RAMBASE) ; makes FD CB 21 0E
+
+ JR C,L2AC0 ; if
+;-------------------------------
+ EX (SP),HL
+;-------------------------------
+ PUSH DE
+ PUSH BC
+ PUSH HL
+;-------------------------------
+ LD DE,(G007_TEMP_BYTE_0)
+ LD B,A
+;-------------------------------
+ CP C
+ JR NC,skip_2B04
+ LD B,C
+ LD C,A
+skip_2B04:
+;-------------------------------
+ LD (G007_TEMP_BYTE_0),BC
+ LD A,C
+;-------------------------------
+ CP E
+ INC A
+ JR C,skip_2B0E
+ LD A,E
+skip_2B0E:
+;-------------------------------
+ LD L,A
+ LD A,D
+;-------------------------------
+ CP B
+ JR NC,skip_2B15
+ LD A,B
+ DEC A
+skip_2B15:
+;-------------------------------
+ SUB L
+ INC A
+ PUSH AF
+ CALL L2807
+
+ POP AF
+ LD C,A
+ EX DE,HL
+ LD HL,G007_TEMP_BYTE_8
+ LD A,(HL)
+;-------------------------------
+ RLCA
+ RLCA
+ RLCA
+;-------------------------------
+ LD (HL),A
+ DEC HL
+ RLC (HL)
+ OR A,(HL)
+ LD HL,(G007_TEMP_WORD_2)
+ XOR A,L
+ CPL
+ LD L,A
+ EX DE,HL
+;-------------------------------
+loop_2B31:
+ LD A,E
+ XOR A,(HL)
+ OR A,D
+ AND A,B
+ XOR A,(HL)
+ LD (HL),A
+;-------------------------------
+loop_2B37:
+ DEC C
+ JR NZ,L2B4E
+;-------------------------------
+ POP HL
+ INC H
+ BIT 7,L
+ JR Z,L2AD4
+;------------------------------- ; discard 3 words from stack
+ POP HL
+ POP HL
+ POP HL
+;-------------------------------
+ POP HL
+ LD A,H
+ AND A,L
+ JP Z,L2A12
+;-------------------------------
+ INC L
+ PUSH HL
+ JP L2A45
+;-------------------------------
+L2B4E:
+ RR B
+ JR NC,loop_2B31
+;-------------------------------
+ INC HL
+ LD B,C
+ LD A,C
+ AND $07
+ LD C,A
+;-------------------------------
+ SRL B ; B *= 8
+ SRL B
+ SRL B
+;-------------------------------
+ JR Z,L2B68
+;-------------------------------
+loop_2B60:
+ LD A,E
+ XOR A,(HL)
+ OR A,D
+ XOR A,(HL)
+ LD (HL),A
+ INC HL
+ DJNZ loop_2B60
+;-------------------------------
+L2B68:
+ SCF
+ INC C
+ JR loop_2B37
+;-------------------------------
+L2B6C:
+ LD HL,(G007_FLAG_1)
+ DEC L
+ OR A,L
+ JP NZ,L0809 ; iii) Testing S-POSN: 0808 ENTER-CH LD D,A
+ ; G007 skips that first instruction though.
+;-------------------------------
+
+ DEC H ; if --H,
+ JP NZ,L23B8 ; then jump to modified copy of a ZX81 routine, in RAM.
+;------------------------------- ;
+ LD A,(G007_OUT_OF_RANGE_FLAGS) ; Plot out of range flags. Bit 7 = latest statement
+ RLA
+ RET C
+;-------------------------------
+ NOP
+ NOP
+ LD HL,(G007_PLOT_X_PREVIOUS_N1+1)
+ LD A, (G007_PLOT_X_PREVIOUS_N1)
+ BIT 6,D
+;-------------------------------
+mark_2B87:
+ JR NZ,L2B8E ; 20;05
+
+ LD L,A
+ ADD A,8
+ JR NC,L2B9B
+;-------------------------------
+L2B8E:
+ LD A,H
+;-------------------------------
+ SUB 8
+ JR NC,skip_2B97
+ LD A,(G007_DISPLAY_HEIGHT)
+ DEC A
+skip_2B97:
+;-------------------------------
+ LD (G007_PLOT_Y_PREVIOUS_N1),A;
+ XOR A,A ; A = 0
+;-------------------------------
+L2B9B:
+ LD (G007_PLOT_X_PREVIOUS_N1),A
+ BIT 6,D
+ RET NZ
+;-------------------------------
+ CALL L2EEF
+
+ LD A,L
+;-------------------------------
+ CP $F9
+ JR C,skip_2BAB
+ LD E,2
+skip_2BAB:
+;-------------------------------
+ CPL
+ AND 7
+ INC A
+ LD D,A
+ LD A,H
+ INC A
+ EXX
+
+ LD D,A
+ EXX
+ PUSH DE
+ CALL L2807
+ EXX
+;-------------------------------
+L2BBA:
+ LD A,C
+ XOR A,(HL)
+ EXX
+ EX DE,HL
+LXXX:
+ POP BC
+ PUSH BC
+ LD L,A
+ LD H,$00
+;-------------------------------
+loop_2BC3:
+ ADD HL,HL
+ DJNZ loop_2BC3
+;-------------------------------
+ EX DE,HL
+ LD B,2
+;-------------------------------
+L2BC9:
+ LD A,(G007_RESTART);
+ XOR A,(HL)
+ OR A,(IY+G007_RESTART+1-RAMBASE)
+ AND A,D
+ XOR A,(HL)
+;-------------------------------
+ DEC C
+ JR Z,skip_2BD6
+ LD (HL),A
+skip_2BD6:
+;-------------------------------
+ LD D,E
+ INC HL
+ DJNZ L2BC9
+;-------------------------------
+ LD C,$20
+ ADD HL,BC
+ EXX
+ INC HL
+;-------------------------------
+ DEC D
+ JR Z,skip_2BE4
+;-------------------------------
+ DJNZ L2BBA
+;-------------------------------
+skip_2BE4:
+;-------------------------------
+ POP HL
+ JP L299A
+;===============================
+PRINT_007:
+ LD A,I ; read graphics mode
+ RRCA
+;-------------------------------
+ JR NC,skip_2BF5
+ XOR A,A ; A = 0
+ LD (DF_SZ),A ; SET DF_SZ
+ INC A
+ LD (G007_FLAG_1),A
+skip_2BF5:
+;-------------------------------
+ CALL PRINT ; [PRINT]
+ XOR A,A ; A = 0
+ LD (G007_FLAG_1),A
+ LD (IY+DF_SZ-RAMBASE),2 ; The lines above have no return or jump statements
+; They will fall into copy of the end of the STK_TO_BC routine
+; Perhaps a cunning trick to save two bytes? :-)
+;===============================
+;===============================
+;===============================
+
+ org ROM_PATCH_0C00
+;
+; 256-byte block patched in to ZX81 BASIC at 0C00 hex
+; This cleverly alters the language syntax,
+; adding parameters and/or pointing to new routines.
+;
+ LD C,A ; from the end of the STK_TO_BC routine
+ RET
+;===============================
+; THE 'STK_TO_A' SUBROUTINE (duplicated for the paged ROM)
+
+; This subroutine 'loads' the A register with the floating point number held at the top of the calculator stack. The number must be in the range 00-FF.
+;-------------------------------
+STK_TO_A:
+
+#if NOT_G007
+; CALL 15CD,FP_TO_A ; This is what the disassembly book says
+#else
+ CALL L0CA0 ; new in G007
+#endif
+ JP C,REPORT_B
+;-------------------------------
+ LD C,$01
+ RET Z
+ LD C,$FF
+ RET
+;===============================
+
+; THE 'SCROLL' COMMAND ROUTINE (duplicated for the paged ROM)
+
+; The first part of the routine sets the correct values of DF_CC and S_POSN to allow for the next printing to occur at the start of the bottom line + 1.
+
+; Next the end address of the first line in the display file is identified and the whole of the display file moved to overwrite this line.
+;-------------------------------
+
+SCROLL:
+mark_0C0E:
+ LD B,(IY+DF_SZ-RAMBASE) ; ??? Disassembly book says LD B,(DF_SZ)
+ LD C,CHARS_HORIZONTAL + 1 ;change here, $21 originally
+ CALL LOC_ADDR
+ CALL ONE_SPACE
+ LD A,(HL)
+ LD (DE),A
+
+ INC (IY+S_POSN_hi-RAMBASE) ;
+
+ LD HL,(D_FILE);
+ INC HL
+ LD D,H
+ LD E,L
+ CPIR
+ JP RECLAIM_1
+;===============================
+;
+; THE SYNTAX TABLES
+;
+; i) The offset table
+;
+; There is an offset value for each of the BASIC commands and by
+; adding this offset to the value of the address where it is found,
+; the correct address for the command in the parameter table is
+; obtained.
+
+; 2C29: 8B 8D 2D 7F 81 49 75
+
+mark_0C29:
+offset_t:
+
+; expression address byte offset expected
+ defb P_LPRINT-$ ; 0CB4 $8B
+ defb P_LLIST-$ ; 0CB1 ; $8D ;
+ defb P_STOP-$ ; 0C58 ; $2D ;
+ defb P_SLOW-$ ; 0CAB ; $7F ;
+ defb P_FAST-$ ; 0CAE ; $81 ;
+ defb P_NEW-$ ; 0C77 ; $49 ;
+ defb P_SCROLL_007-$ ; 0CA4 ; $75 ;
+ ; 2C30: 5F 40 42 2B 17 1F 37 52
+ defb P_CONT-$ ; 0CBF ; $5F ;
+ defb P_DIM-$ ; 0C71 ; $40 ;
+ defb P_REM-$ ; 0C74 ; $42 ;
+ defb P_FOR-$ ; 0C5E ; $2B ;
+ defb P_GOTO-$ ; 0C4B ; $17 ;
+ defb P_GOSUB-$ ; 0C54 ; $1F ;
+ defb P_INPUT-$ ; 0C6D ; $37 ;
+ defb P_LOAD-$ ; 0C89 ; $52 ;
+ ; 2C38: 45 0F 6D 2B 44 2D 5A 3B
+ defb P_LIST-$ ; 0C7D ; $45 ;
+ defb P_LET-$ ; 0C48 ; $0F ;
+ defb P_PAUSE-$ ; 0CA7 ; $6D ;
+ defb P_NEXT-$ ; 0C66 ; $2B ;
+ defb P_POKE-$ ; 0C80 ; $44 ;
+ defb P_PRINT-$ ; 0C6A ; $2D ; Same as normal ZX81
+ defb P_PLOT_007-$ ; 0C98 ; $5A ;
+ defb P_RUN-$ ; 0C7A ; $3B ;
+ ; 2C40: 4C 45 0D 52 54 4D 15 6A
+ defb P_SAVE-$ ; 0C8C ; $4C ;
+ defb P_RAND-$ ; 0C86 ; $45 ;
+ defb P_IF-$ ; 0C4F ; $0D ;
+ defb P_CLS-$ ; 0C95 ; $52 ;
+ defb P_UNPLOT_007-$ ; 0C98 ; $54 ; This byte is 5A, an offset to 0C9E, in normal ZX81
+ ; NB same address as PLOT
+ defb P_CLEAR-$ ; 0C92 ; $4D ;
+ defb P_RETURN-$ ; 0C5B ; $15 ;
+ defb P_COPY-$ ; 0CB1 ; $6A ;
+;
+; ii) The parameter table.
+;
+; For each of the BASIC commands there are between 3 and 8
+; entries in the parameter table. The command classes for each of
+; the commands are given, together with the required separators and
+; these are followed by the address of the appropriate routine.
+
+
+ ; 2C48: 01 14 02
+mark_0C48:
+P_LET:
+ defb _CLASS_01 ; A variable is required.
+ defb ZX_EQU ; Separator: '='
+ defb _CLASS_02 ; An expression, numeric or string,
+ ; must follow.
+
+; 244B: 06 00 81 0E 06
+
+P_GOTO:
+mark_0C4B:
+ defb _CLASS_06 ; A numeric expression must follow.
+ defb _CLASS_00 ; No further operands.
+ defw GOTO
+
+
+P_IF:
+mark_0C4F:
+ defb _CLASS_06 ; A numeric expression must follow.
+mark_0C50 ; 2C50: DE 05 AB 0D
+ defb ZX_THEN ; Separator: 'THEN'
+ defb _CLASS_05 ; Variable syntax checked entirely
+ ; by routine.
+ defw IF
+
+
+P_GOSUB: ; mark_0C54:
+ defb _CLASS_06 ; A numeric expression must follow.
+ defb _CLASS_00 ; No further operands.
+ defw GOSUB ; 0EB5
+
+ ; 2C58: 00 DC 0C
+
+P_STOP: ; mark_0C58:
+ ; 2C54: 06 00 B5 0E
+ defb _CLASS_00 ; No further operands.
+ defw STOP
+
+ ; 2c5B: 00 D8 0E
+
+P_RETURN
+ defb _CLASS_00 ; No further operands.
+ defw RETURN
+
+
+ ; 2c5e: 04 14
+
+
+P_FOR:
+#if 0
+mark_0C5E:
+#else
+#endif
+ defb _CLASS_04 ; A single character variable must
+ ; follow.
+ defb ZX_EQU ; Separator: '='
+ defb _CLASS_06 ; A numeric expression must follow.
+ defb ZX_TO ; Separator: 'TO'
+ defb _CLASS_06 ; A numeric expression must follow.
+ defb _CLASS_05 ; Variable syntax checked entirely
+ ; by routine.
+ defw FOR
+
+ ; 0C66: 04 00 2E 0E
+
+P_NEXT: ; at $0C66:
+
+ defb _CLASS_04 ; A single character variable must follow.
+ defb _CLASS_00 ; No further operands.
+ defw NEXT
+
+
+; 2C6A: 05 E8 2B
+
+P_PRINT:
+
+mark_0C6A:
+
+
+
+ defb _CLASS_05 ; Variable syntax checked entirely
+ ; by routine.
+#if NOT_G007
+ defw PRINT ; This is the original routine
+#else
+ defw PRINT_007 ; 2BE8 This is the new routine!
+#endif
+
+; ; 2C6D: 01 00 E9 0E
+
+
+mark_0C6D:
+
+
+P_INPUT:
+ defb _CLASS_01 ; A variable is required.
+ defb _CLASS_00 ; No further operands.
+ defw INPUT
+
+P_DIM:
+mark_0C71:
+ defb _CLASS_05 ; Variable syntax checked entirely
+ ; by routine.
+#if NOT_G007
+ defw DIM ; 1409 Original ZX81 replaced by
+#else
+ defw DIM_007 ; 2EA7 new routine
+#endif
+;
+; ; 2c74: 05 6A 0D
+;
+mark_0C74:
+P_REM:
+
+ defb _CLASS_05 ; Variable syntax checked entirely by routine.
+ defw REM
+
+mark_0C77:
+P_NEW:
+ defb _CLASS_00 ; No further operands.
+ defw NEW
+
+P_RUN:
+mark_0C7A:
+
+P_RUN:
+ defb _CLASS_03 ; A numeric expression may follow
+ ; else default to zero.
+ defw RUN
+
+P_LIST:
+#if 0
+mark_0C7D:
+#else
+#endif
+ defb _CLASS_03 ; A numeric expression may follow
+ ; else default to zero.
+ defw LIST
+
+P_POKE: ; mark_0C80:
+ defb _CLASS_06 ; A numeric expression must follow.
+ defb ZX_COMMA ; Separator: ','
+ defb _CLASS_06 ; A numeric expression must follow.
+ defb _CLASS_00 ; No further operands.
+ defw POKE
+
+P_RAND: ; mark_0C86:
+ defb _CLASS_03 ; A numeric expression may follow
+ ; else default to zero.
+ defw RAND
+
+P_LOAD: ; mark_0C89:
+ defb _CLASS_05 ; Variable syntax checked entirely
+ ; by routine.
+ defw LOAD
+
+P_SAVE: ; mark_0C8C:
+ defb _CLASS_05 ; Variable syntax checked entirely
+ ; by routine.
+#if NOT_G007
+ defw SAVE ; 02F6 original
+#else
+ defw SAVE_007 ; 2F4D new!
+#endif
+;
+P_CONT:
+#if 0
+#else
+#endif
+mark_0C8F:
+ defb _CLASS_00 ; No further operands.
+ defw CONT ; 0E7C
+;
+P_CLEAR: ; mark_0C92:
+ defb _CLASS_00
+#if NOT_G007
+ defw CLEAR ; 149A original
+#else
+ defw CLEAR_007 ; 0EB2 new. this points outside the G007 ROM
+#endif
+
+
+P_CLS: ; mark_0C95:
+#if NOT_G007
+ defb _CLASS_00 ; No further operands.
+;; defw CLS ; original
+#else
+ defb _CLASS_03 ; A numeric expression may follow
+ ; else default to zero.
+ defw G007_CLS_N ; 2E4E new!
+#endif
+;
+;
+;
+mark_0C98:
+
+#if NOT_G007
+ ; originally the ZX81 does this:
+P_PLOT: ; original: plot X,Y
+ defb _CLASS_06 ; A numeric expression must follow. X
+ defb ZX_COMMA ; Separator: ','
+ defb _CLASS_06 ; A numeric expression must follow. Y
+ defb _CLASS_00 ; No further operands.
+ defw PLOT_UNPLOT ; 0BAF
+
+P_UNPLOT: ; original: plot X,Y
+ defb _CLASS_06 ; A numeric expression must follow. X
+ defb ZX_COMMA ; Separator: ','
+ defb _CLASS_06 ; A numeric expression must follow. Y
+ defb _CLASS_00 ; No further operands.
+ defw PLOT_UNPLOT ; 0BAF
+
+ ; These are identical, so they could overlap
+#else
+ ; The G007 saves bytes by having these
+ ; identical parameter table entries overlapping:
+ ; 0C98 / 2C98: 06 1A 06 1A 06 00 26 29
+P_PLOT_007:
+P_UNPLOT_007:
+ ; new: plot N,X,Y
+ defb _CLASS_06 ; A numeric expression must follow. N
+ defb ZX_COMMA ; Separator: ','
+ defb _CLASS_06 ; A numeric expression must follow. X
+ defb ZX_COMMA ; Separator: ','
+L0C9C:
+ defb _CLASS_06 ; A numeric expression must follow. Y
+ defb _CLASS_00 ; No further operands.
+ defw G007_PLOT_UNPLOT_N_X_Y ; 2926 new!
+ ;
+ ; by saving those bytes, can put a tiny routine here:
+L0CA0:
+ LD HL,(L0EA8) ; Inside the 'FIND_INT.' subroutine
+ JP (HL)
+#endif
+;
+; Now back to the table!
+;
+P_SCROLL_007
+mark_0CA4:
+ defb _CLASS_00
+ defw SCROLL ; 0C0E
+
+
+
+P_PAUSE:
+ defb _CLASS_06
+ defb _CLASS_00
+#if NOT_G007
+mark_0CA9:
+; 2CA9: 0F 32
+ defw PAUSE ; 0F32 original
+#else
+mark_0CA9:
+; 2CA9: AE 2E
+; 0CA7
+ defw PAUSE_007 ; 2EAE new!
+#endif
+;
+; 2CAB: 03 B2 2E
+; 0CAB
+
+
+
+P_SLOW:
+mark_0CAB:
+#if NOT_G007
+ defb _CLASS_00 ; No further operands.
+ defw SLOW ; 2B 0F SLOW,0F2B original
+#else
+ defb _CLASS_03 ; new! Extra parameter sets graphic mode
+ defw SLOW_007 ; 2EB2 new!
+#endif
+
+
+P_FAST:
+mark_0CAE:
+#if NOT_G007
+ defb _CLASS_00 ; No further operands.
+ defw FAST ; 23 0F FAST,0F23 original
+#else
+ defb _CLASS_03 ; new! Extra parameter sets graphic mode
+ defw FAST_007 ; 2EB6 new!
+#endif
+
+
+
+
+
+
+
+
+;
+; 2CB1: 03 53 2F
+;
+; 0CB1
+
+P_COPY:
+#if NOT_G007
+ defb _CLASS_00 ; original
+ defw COPY ; 0869 original
+#else
+ defb _CLASS_03 ; new! Extra parameter sets graphic mode
+;;; defw COPY_007 ; 2EB6 new but wrong ???!
+ defw L2F53 ; what the disassembler said
+#endif
+
+mark_0CB4:
+
+
+P_LPRINT:
+
+ defb _CLASS_05
+ defw LPRINT ; 0ACB
+;
+; ; 2CB7: 03 2C 07
+; 0CB7
+P_LLIST:
+ defb _CLASS_03
+ defw LLIST ; 072C
+;
+;
+; the rest of this page is just a copy of the usual ZX81 code
+
+LINE_SCAN:
+L0CBA:
+; 2CBA: FD 36 01 01 CD 73
+; 2CC0: 0A CD 95 0A 21 00 40 36
+; 2CC8: FF 21 2D 40 CB 6E 28 0E
+; 2CD0: FE E3 7E C2 6F 0D CD A6
+; 2CD8: 0D C8 CF 0C CF 08 DF 06
+; 2CE0: 00 FE 76 C8 4F E7 79 D6
+; 2CE8: E1 38 3B 4F 21 29 0C 09
+; 2CF0: 4E 09 18 03 2A 30 40 7E
+; 2CF8: 23 22 30 40 01 F4 0C C5
+;
+; which is described as follows in the ZX81 disassembly book:
+;
+; THE 'LINE SCANNING' ROUTINE
+;
+; The BASIC interpreter scans each line for BASIC commands and as each one is found
+; the appropriate command routine is followed.
+; The different parts of the routine are:
+;-------------------------------
+; i) The LINE_SCAN entry point leads to the line number being checked for validity.
+
+ LD (IY+FLAGS-RAMBASE),$01 ; FLAGS = 1
+ CALL E_LINE_NO ;
+;-------------------------------
+; ii) The LINE_RUN entry point is used when replying to an INPUT prompt
+; and this fact has be identified.
+;-------------------------------
+LINE_RUN
+#if NOT_G007
+ CALL SET_MEM ; at $14BC
+#else
+ CALL L0A95 ; JP 14BC,SET_MEM then return
+ ; why it does this I do not know - KH
+#endif
+ LD HL,ERR_NR
+ LD (HL),$FF
+ LD HL,FLAGX
+ BIT 5,(HL)
+ JR Z,LINE_NULL
+;-------------------------------
+; iii) The INPUT reply is tested to see if STOP was entered.
+
+ CP $E3
+ LD A,(HL)
+ JP NZ,INPUT_RE
+ CALL SYNTAX_Z
+ RET Z
+
+; iv) If appropriate, report D is given.
+
+ RST ERROR_1
+ defb $0C
+;-------------------------------
+ ; THE 'STOP' COMMAND ROUTINE
+ ; The only action is to give report 9.
+;-------------------------------
+STOP
+ RST ERROR_1
+ defb 8
+;-------------------------------
+ ; v) A return is made if the line is 'null'.
+;-------------------------------
+LINE_NULL:
+
+mark_0CDE:
+
+; old software from disassembly book:
+
+ RST GET_CHAR
+ LD B,0
+ CP ZX_NEWLINE
+ RET Z
+
+ ; vi) The first character is tested so as to check that it is a command.
+
+ LD C,A
+ RST NEXT_CH
+ LD A,C
+ SUB $E1
+#if NOT_G007
+ JR C,REPORT_C ; $0D9A as per the book
+#else
+ JR C,REPORT_C_007 ; $0D26 in G007 systems
+#endif
+;-------------------------------
+ ; vii) The offset for the command is found from the offset table.
+
+ LD C,A
+ LD HL,offset_t ; $0C29
+ ADD HL,BC
+ LD C,(HL)
+ ADD HL,BC
+ JR GET_PARAM
+
+ ; viii) The parameters are fetched in turn by a loc that returns to 0CF4.
+ ; The separators are identitied by the test against +0B.
+;-------------------------------
+mark_0CF4:
+
+SCAN_LOOP:
+ LD HL,(T_ADDR)
+GET_PARAM:
+ LD A,(HL)
+ INC HL
+ LD (T_ADDR),HL
+ LD BC,SCAN_LOOP
+ PUSH BC
+
+;
+; there is more to this routine but the patch ends here
+;
+; end of patch
+;-------------------------------
+ org $2D00
+
+
+; bits shifting right within a byte:
+;
+ defb %10000000 ; $80
+ defb %01000000 ; $40
+ defb %00100000 ; $20
+ defb %00010000 ; $10
+ defb %00001000 ; $08
+ defb %00000100 ; $04
+ defb %00000010 ; $02
+ defb %00000001 ; $01
+;-------------------------------
+L2D08:
+
+SLOW_FAST_007: ; at $2D08
+
+ CALL L2BF7 ; ??? not a valid opcode address?
+ JP SLOW_FAST ; old routine
+;-------------------------------
+L2D0E:
+ LD (MARGIN),A ;
+ EX DE,HL
+ LD HL,10
+ ADD HL,SP
+ LD A,(HL)
+ INC A
+ AND $F0
+ SUB $D0
+ LD C,A
+ INC HL
+ LD A,(HL)
+ SUB 4
+ OR A,C
+ LD C,A
+ LD A,(CDFLAG) ;
+ RLCA
+ SBC A,A
+ AND A,C
+ JR NZ,L2D3B
+;-------------------------------
+ LD HL,(VARS) ;
+ LD BC,-(CHARS_HORIZONTAL+1) ; $FFDF
+ ADD HL,BC
+ SET 7,H
+ LD (G007_DISPLAY_ADDRESS_LO_RES_LAST_LINE),HL
+ LD A,1
+ JR L2D49
+;-------------------------------
+L2D3B:
+ LD HL,(D_FILE) ; GET D_FILE
+ LD BC,(G007_DISPLAY_OFFSET_FROM_DFILE_LESS_9)
+ ADD HL,BC
+ SET 7,H
+ LD (G007_DISPLAY_ADDRESS_LESS_9),HL
+ XOR A,A ; A = 0
+;-------------------------------
+L2D49:
+ LD (G007_FLAG_3),A
+ DEC HL
+ LD A,(HL)
+ EX DE,HL
+ RET
+;-------------------------------
+ LD A,(G007_FLAG_3)
+ DEC A
+ JP NZ,L2D73 ; [11635]
+;-------------------------------
+ LD A,G007_INT_REG_VALUE_FOR_LO_RES_GRAPHICS
+ LD I,A ; Interrupt register = 0x1E sets low res mode
+ ADC HL,HL
+ LD HL,(G007_DISPLAY_ADDRESS_LO_RES_LAST_LINE)
+
+ LD BC,$0108
+ LD A,$FE
+ CALL DISPLAY_5;
+ LD A,G007_INT_REG_VALUE_FOR_HI_RES_GRAPHICS
+ LD I,A ; Interrupt register = 0x1F sets high res mode
+ LD A,(MARGIN) ; GET MARGIN
+ SUB 8
+ JR L2D77
+;-------------------------------
+L2D73:
+ DEC HL
+ LD A,(MARGIN) ; GET MARGIN
+;-------------------------------
+L2D77:
+ LD C,A
+ POP IX
+;-------------------------------
+ BIT 7,(IY+CDFLAG-RAMBASE) ; makes FD CB 3B 7E
+ JP NZ,L029D ; []*BIOS ROM*
+;-------------------------------
+
+; This jumps into the middle of the DISPLAY_3, where it meets this code:
+;029D 79 LD A,C
+;029E ED;44 NEG
+;02A0 3C INC A
+;02A1 08 EX AF,AF'
+;02A2 D3;FE OUT (IO_PORT_KEYBOARD_RD),A ; ZX81 NMI GENERATOR on, for SLOW mode
+;02A4 E1 POP HL ; restore registers
+;02A5 D1 POP DE
+;02A6 C1 POP BC
+;02A7 F1 POP AF
+;02A8 C9 RET
+;-------------------------------
+ LD A,$FE
+ LD B,$01
+ LD HL,HALT_AT_2D9A
+ CALL LOAD_R_AND_WAIT_FOR_INT
+ ADD HL,HL ; double HL
+ NOP
+ LD E,A
+ LD HL,(G007_DISPLAY_ADDRESS_LESS_9)
+ SET 7,H
+ JP (IX)
+;===============================
+LOAD_R_AND_WAIT_FOR_INT ; mark_2D95
+ ; Sets up the refresh register and waits for an interrupt:
+ LD R,A ; Refresh row counter := accumulator
+ LD A,221 ; 221 = $DD
+ EI ; Enable interrupts, then drop into HALT
+;-------------------------------
+HALT_AT_2D9A:
+
+ HALT
+;===============================
+; Copy two pages of ZX81 BASIC ROM to RAM patch areas,
+; then modify bytes as controlled by a table of offsets and values
+; This uses less memory than two whole pages of ROM
+; If the RAM patches are not changed thereafter,
+; one might be able to use a 16K ROM to hold two different versions of the ZX81 BASIC space,
+; one with the 'RAM' patch and one without.
+; The RAM testing would have to be disabled, because the ROM would cause it to fail.
+;-------------------------------
+#if NOT_G007
+ZX81_COPY:
+#else
+G007_COPY:
+#endif
+;-------------------------------
+; First check RAM by loading 512 bytes RAM from $2200 to $23FF with value 1,
+; and decrement them to zero to test they are working RAM
+;
+ LD HL,RAM_PATCH_2200 ; destination RAM patch 2
+ LD A,$24
+;-------------------------------
+mark_2DA0:
+G007_COPY_LOOP:
+ LD (HL),1 ; try writing the value 1 to a byte in RAM patch 2
+ DEC (HL) ; try decrementing it to zero
+ JR Z,G007_COPY_SKIP ; if result is zero, skip error code report
+;-------------------------------
+ RST $08
+ defb $1A ; RST8 Arg ; Error Code:'R' (ZX81 on-board 1K/2K RAM not found)
+;-------------------------------
+G007_COPY_SKIP
+mark_2DA7:
+ INC HL ; next address
+ CP H ; has HL reached $2400 (yet?
+ JR NZ,G007_COPY_LOOP ; no, repeat.
+;-------------------------------
+ ; HL is now $2400
+ ; Copy 256 bytes from BASIC ROM to RAM patch 1:
+ LD H,L ; HL is now $0000 (data source is start of ZX81 BASIC ROM)
+ LD DE,RAM_PATCH_2000 ; data destination
+ LD BC,$0100 ; 256 bytes
+ LDIR ; (HL) -> (DE) 256 times
+;-------------------------------
+ ; HL is now $0100
+ ; DE is now $2100
+ ; BC is now $0000
+;-------------------------------
+ INC H ; HL is now $0200
+ INC D ; DE is now $2200
+ INC B ; BC is now $0100
+ LDIR ; copy another 256 bytes to RAM patch 2
+;-------------------------------
+ LD HL,PRINT_CH ; src = the original ZX81 print character routine
+ LD DE,G007_V23A0 ; dst =
+ LD BC,$0060 ; 60hex = 96 bytes, so stops just before 0851 (the 'LPRINT_CH routine)
+ LDIR ; move
+ ; BC is now $0000
+ LD D,$20 ; DE is now $20??
+ LD HL,TABLE_ONE ; copy data from this table:
+LOOP_2DC9:
+ LD B,(HL) ; BC is now $0A00 (seems way too many bytes)
+ JR G007_TABLE_END_TEST ; branch to end-of-table test
+;-------------------------------
+G007_TABLE_ONE_LOOP:
+ LD E,(HL) ; Get the offset
+ INC HL ; next byte is data
+ LD A,(HL) ; Get the data byte
+ LD (DE),A ; ($2000+offset) = data byte
+;-------------------------------
+G007_TABLE_END_TEST:
+mark_2DD0:
+ INC HL ; next offset
+ DJNZ G007_TABLE_ONE_LOOP ; repeat until BC is zero
+;-------------------------------
+ INC D ; D becomes $21
+ BIT 2,D ; test bit 2 (has D incremented to $23 ?)
+ JR Z,LOOP_2DC9
+
+ RET
+;===============================
+; Delete Display File:
+;
+G007_DELETE_DISPLAY_FILE:
+ CALL SET_FAST;
+ LD HL,$3E87
+ CALL LINE_ADDR;
+ RET NZ
+;-------------------------------
+ EX DE,HL
+ LD HL,(NXT_LINE) ;
+ BIT 6,(HL)
+ JR Z,L2DEF
+;-------------------------------
+ LD (NXT_LINE),DE ;
+;-------------------------------
+L2DEF:
+ LD HL,(D_FILE) ;
+ JP RECLAIM_1 ; exit
+;-------------------------------
+L2DF5:
+ CALL G007_DELETE_DISPLAY_FILE
+ LD BC,$1992 ; 6546 decimal =
+ LD HL,(D_FILE) ;
+ DEC HL
+ CALL MAKE_ROOM
+
+ LD A,ZX_NEWLINE
+ LD (DE),A ; to DE
+ INC DE
+ LD (DE),A ; and DE+1
+
+ INC HL ; HL += 2
+ INC HL
+
+ LD (HL),$3E ; (HL++) = $3E 'Y'
+ INC HL
+ LD (HL),$87 ; (HL++) = $87
+ INC HL
+ LD (HL),$8D ; (HL++) = $8D
+ INC HL
+ LD (HL),$19 ; (HL++) = $19 ';'
+ INC HL
+ LD (HL),A ; (HL++) = ZX_NEWLINE
+ INC HL
+ LD (HL),A ; (HL++) = ZX_NEWLINE
+;-------------------------------
+L2E18:
+ CALL SLOW_FAST ;
+ LD A,1
+ JR G007_CLS_HI_RES
+;-------------------------------
+G007_CHECK_AND_SET_UP_DISPLAY_FILE:
+ ;
+ LD HL,(V2265) ; in the RAM page 2 area ! Usually $4027
+ LD DE,$BFD9 ; 3FD9 + 32K, or -16423 = 16K - 39 bytes
+ ADD HL,DE
+
+ LD A,(V2264)
+ SUB CHARS_HORIZONTAL+1 ; bytes per screen line
+
+ OR A,H
+ OR A,L
+
+ CALL NZ,G007_COPY
+ LD HL,(D_FILE) ; GET D_FILE
+ LD A,ZX_NEWLINE
+
+ DEC HL
+ DEC HL
+ CP (HL) ; pointing to a HALT?
+
+ CALL NZ,L2DF5 ; no,
+ ; else yes
+ LD HL,(D_FILE) ; GET D_FILE
+ LD DE,(G007_DISPLAY_OFFSET_FROM_DFILE_LESS_9)
+ ADD HL,DE
+ LD (G007_DISPLAY_ADDRESS_LESS_9),HL
+
+ LD DE,9 ; HL += 9
+ ADD HL,DE
+
+ LD (G007_DISPLAY_ADDRESS),HL
+ RET
+;-------------------------------
+G007_CLS_N:
+;
+ CALL STK_TO_A
+ RET NZ ; drop through if zero flag set
+;-------------------------------
+; Clear The Screen:
+;
+G007_CLS_A:
+ DEC A
+ JP M,CLS ; if A was zero
+;-------------------------------
+G007_CLS_HI_RES:
+ PUSH AF
+ CALL G007_CHECK_AND_SET_UP_DISPLAY_FILE
+ POP AF
+;-------------------------------
+ CP 2
+ LD BC,(G007_DISPLAY_HEIGHT)
+ LD HL,(D_FILE)
+ JR NC,L2E96
+;-------------------------------
+ DEC A
+
+ INC HL
+ LD (DF_CC),HL ; Addr. for PRINT AT position
+ DEC HL
+
+ DEC HL
+ DEC HL
+
+ LD E,0 ; E = 0 is byte to fill screen
+;-------------------------------
+loop_2E70:
+ LD B,16 ; 16
+ DEC HL
+ LD (HL),E ; but writes 2 bytes for each loop
+ DEC HL
+ LD (HL),E ; perhaps to reduce loop overhead?
+;-------------------------------
+loop_2E76:
+ DEC HL
+ LD (HL),A ; writes A twice over
+ DEC HL
+ LD (HL),A
+ DJNZ loop_2E76
+;-------------------------------
+ DEC C
+ JR NZ,loop_2E70
+;------------------------------- ; C is now zero
+ LD B,9 ; BC is now $0900
+ LD A,1
+;-------------------------------
+loop_2E83:
+ DEC HL
+ LD (HL),E ; (--HL) = E while --BC
+ DJNZ loop_2E83
+;------------------------------- ; BC is now zero
+ LD HL,G007_LINE_TYPE
+ LD B,20 ; decimal 20
+ DEC A
+ JR Z,loop_2E83
+;-------------------------------
+ LD HL,256*CHARS_VERTICAL + CHARS_HORIZONTAL + 1
+ LD (S_POSN),HL ; SET S_POSN
+ RET
+;===============================
+L2E96:
+ RET NZ
+
+ DEC HL ; HL -= 2
+ DEC HL
+;------------------------------- outer loop
+loop_2E99:
+ LD B,32 ; 32 bytes to invert
+ DEC HL ; HL -= 2
+ DEC HL ; skips past 2 HALT characters
+
+;------------------------------- inner loop
+loop_2E9D:
+ DEC HL
+
+ LD A,(HL) ; read
+ CPL ; invert (one's complement)
+ LD (HL),A ; write
+
+ DJNZ loop_2E9D
+;-------------------------------
+ DEC C ; C is obviously the number of horizontal lines to invert
+ JR NZ,loop_2E99
+;-------------------------------
+ RET
+;-------------------------------
+mark_2EA7:
+DIM_007: ; 2EA7
+ LD HL,($0A96)
+ LD A,$4D
+ JR L2EE3
+;-------------------------------
+PAUSE_007:
+ LD A,$DD
+ JR skip_2EE0
+;-------------------------------
+SLOW_007: ; 2EB2
+ LD A,$D6
+ JR skip_2EB8
+;-------------------------------
+FAST_007: ; 2EB6
+ LD A,$CE
+;-------------------------------
+skip_2EB8:
+ PUSH AF
+
+ CALL STK_TO_A
+ LD B,$1E
+ DEC A
+ CP 6
+ JR NC,skip_2EDC
+;-------------------------------
+ SRL A
+ LD H,A
+;-------------------------------
+ JR Z,skip_2ECA
+ LD A,1
+skip_2ECA:
+;-------------------------------
+ PUSH AF
+ SBC A,A
+ LD L,A
+ RES 1,H
+ DEC H
+ LD (G007_RESTART),HL
+ CALL G007_CHECK_AND_SET_UP_DISPLAY_FILE
+ LD B,G007_INT_REG_VALUE_FOR_HI_RES_GRAPHICS
+ POP AF
+
+ LD (G007_FLAG_2),A
+;-------------------------------
+skip_2EDC:
+ LD A,B ;
+ LD I,A ; I = B = G007_INT_REG_VALUE_FOR_HI_RES_GRAPHICS = set video mode
+ POP AF
+;-------------------------------
+skip_2EE0:
+ LD HL,($0D71) ; inside the 'COMMAND CLASS 2' routine of the ZX BASIC ROM
+;-------------------------------
+L2EE3:
+ ADD A,L
+ LD L,A
+ JP (HL)
+
+ LD D,A
+ LD A,(S_POSN) ;
+ AND $80
+ JP L2B6C
+;===============================
+L2EEF: ; print a character on high-res screen perhaps?
+ LD A,D
+ POP DE
+
+ EXX
+;-------------------------------
+ PUSH HL
+ PUSH DE
+ PUSH BC
+;-------------------------------
+ LD HL,(G007_CHAR_TABLE_ADDR_0_63)
+ ADD A,A ; if (A < 128) goto
+ JR NC,skip_2F06
+;------------------------------- ;
+ LD HL,(G007_CHAR_TABLE_ADDR_128_159)
+ BIT 6,A
+ JR Z,skip_2F06
+;-------------------------------
+ LD HL,(G007_USR_DEF_CHR_TAB_LESS_256)
+ CCF
+;-------------------------------
+skip_2F06:
+ EX DE,HL ; DE now points to the selected charater table
+
+ LD L,A ; HL = A
+ LD H,0
+
+ SBC A,A ; A = carry
+ LD C,A ;
+;-------------------------------
+ LD A,(G007_RESTART) ; invert this variable
+ CPL
+ AND A,(IY+G007_RESTART+1-RAMBASE)
+;-------------------------------
+ XOR A,C
+ LD C,A
+
+ ADD HL,HL ; HL *= 4
+ ADD HL,HL
+ ADD HL,DE ; HD += DE
+
+ LD B,8
+
+ EXX
+ PUSH DE
+ RET
+;===============================
+L2F1D: ; The copied and modified PRINT_CH / WRITE_CH jumps here from $23FF
+ DEC (IY+S_POSN-RAMBASE) ; Line and Column for PRINT AT
+ LD A,24 ; 24 character rows per screen
+ SUB B
+ LD B,A
+;
+ ADD A,A
+ ADD A,A
+ ADD A,A
+ LD L,A ; L = A * 8
+;
+ LD A,(G007_DISPLAY_HEIGHT)
+ SUB L ; A = G007_DISPLAY_HEIGHT - something
+ RET C ; return if beyond limit
+;-------------------------------
+ LD A,CHARS_HORIZONTAL+1 ; 32 video bytes plus HALT ?
+ SUB C
+ LD C,A
+;-------------------------------
+ LD H,0
+ ADD HL,HL
+ ADD HL,BC
+ LD BC,(G007_DISPLAY_ADDRESS)
+ ADD HL,BC ; HL = L*2 + BC + G007_DISPLAY_ADDRESS:
+;-------------------------------
+ CALL L2EEF
+ LD BC,$0022 ; B = 0, C = 34
+ EXX
+;-------------------------------
+COPY_007:
+loop_2F41: ;
+ LD A,C
+ XOR A,(HL)
+ EXX
+ LD (HL),A
+ ADD HL,BC
+ EXX
+ INC HL
+ DJNZ loop_2F41
+;-------------------------------
+ JP L299A
+;-------------------------------
+SAVE_007: ; 2F4D
+ CALL G007_DELETE_DISPLAY_FILE
+ JP SAVE
+;-------------------------------
+L2F53:
+ CALL STK_TO_A
+ RET NZ
+
+ DEC A
+ JP M,COPY ; A was zero, jump to low-res copy-to-printer
+;-------------------------------
+ CALL G007_CHECK_AND_SET_UP_DISPLAY_FILE ; check we have a display
+ CALL SET_FAST ; print in FAST mode!
+ LD A,(G007_DISPLAY_HEIGHT)
+ LD B,A ; B = (G007_DISPLAY_HEIGHT)
+ LD HL,(G007_DISPLAY_ADDRESS)
+ XOR A,A ; A = 0
+ LD E,A ; E = 0
+ OUT (ZX_PRINTER_PORT),A
+;-------------------------------
+loop_2F6C:
+ LD A,$7F ; looks pointless
+ IN A,(IO_PORT_KEYBOARD_RD)
+ RRCA
+ JP NC,$0886 ; []*BIOS ROM* between 0880 COPY-BRK and 0888 REPORT-D2
+ IN A,(ZX_PRINTER_PORT)
+;-------------------------------
+loop_2F76:
+ ADD A,A
+ JP M,L2FAD
+;-------------------------------
+ JR NC,loop_2F6C
+;-------------------------------
+ LD C,$20 ; 32
+;-------------------------------
+loop_2F7E:
+ PUSH BC
+ LD C,(HL)
+ LD B,8
+;------------------------------- ; outer loop
+loop_2F82:
+ RLC C
+ RRA
+ OR A,E
+ LD D,A
+;------------------------------- ; inner loop
+loop_2F87:
+ IN A,(ZX_PRINTER_PORT) ; read printer port bit 0 into carry flag
+ RRA
+ JR NC,loop_2F87 ; repeat while bit is zero
+;-------------------------------
+ LD A,D
+ OUT (ZX_PRINTER_PORT),A ; D register to printer
+ DJNZ loop_2F82
+;-------------------------------
+ INC HL
+ POP BC
+ DEC C
+ JR NZ,loop_2F7E
+;-------------------------------
+ INC HL
+ INC HL
+ LD A,3
+;-------------------------------
+ CP B
+ JR C,skip_2F9F
+;-------------------------------
+ LD E,A
+ DEC E
+skip_2F9F:
+;-------------------------------
+loop_2F9F:
+ IN A,(ZX_PRINTER_PORT) ; read printer port bit 0 into carry flag
+ RRA
+ JR NC,loop_2F9F ; repeat while bit is zero
+;-------------------------------
+ LD A,E
+ OUT (ZX_PRINTER_PORT),A ; E register to printer
+ DJNZ loop_2F6C
+;-------------------------------
+ LD A,4
+ OUT (ZX_PRINTER_PORT),A
+;-------------------------------
+L2FAD:
+ JP SLOW_FAST ; normal
+ EI
+ DJNZ loop_2F76
+;-------------------------------
+ defb $3E ; something to do with the table below?
+;-------------------------------
+TABLE_ONE:
+;
+; this table is copied to offsets from $2000, $2200, and $2300
+;-------------------------------
+; Modify 9 bytes in page 20xx / 00xx:
+;-------------------------------
+L2FB4 :
+ defb (9+1) ; Modify 9 bytes in page 20xx / 00xx:
+;-------------------------------
+; Original routine Modifications made in page 00xx
+;
+L2FB5:
+; 0011 C2;F1;07 JP NZ,PRINT_CH ; old
+; 0011 C2;A0;23 JP NZ,L23A0 ; new, jump somewhere in RAM ???
+
+ defb $12
+ defb $A0
+ defb $13
+ defb $23
+;-------------------------------
+L2FB9
+; 0014 F5 07 JP PRINT_SP ; $07F5
+; 2014 A4 23 JP L23A4 ; new, jumps to somewhere in RAM
+
+ defb $15
+ defb $A4
+ defb $16
+ defb $23
+;-------------------------------
+; 003F CB;D9 SET 3,C ; old
+; 003F CB;C1 SET 0,C ; new
+
+L2FBD: defb $40
+L2FBE: defb $C1
+;-------------------------------
+; 005F CD;07;02 CALL SLOW_FAST ; 0207 is old
+; 005F CD;08;2D CALL SLOW_FAST_007 ; 2D08 is new
+
+L2FBF: defb $60
+L2FC0: defb $08
+L2FC1: defb $61
+L2FC2: defb $2D
+;-------------------------------
+; 0074 2A;0C;40 LD HL,(D_FILE)
+; 0074 2A;06:23 LD HL,(G007_DISPLAY_ADDRESS_LESS_9) ; new
+
+L2FC3: defb $75
+L2FC4: defb $06
+ defb $76
+L2FC6: defb $23
+;-------------------------------
+; Modifications made in page 02xx
+;-------------------------------
+L2FC7: defb $01
+ defb $0A
+
+; 0253 06;0B LD B,$0B
+; 2253 06;02 LD B,$02
+
+L2FC9 defb $54
+ defb $02
+;-------------------------------
+; 0283 01;01;19 LD BC,$1901
+; 2283 01;01;C1 LD BC,$C101
+L2FCB: defb $85
+ defb $C1
+;-------------------------------
+; 027E CD;92;02 CALL DISPLAY_3
+; 227E CD;73;2D CALL L2D73
+;
+L2FCD: defb $7F
+ defb $73
+L2FCF: defb $80
+ defb $2D
+;-------------------------------
+; 028C CD;92;02 CALL $0292 ; [DISPLAY-3]
+; 228C CD;50;2D CALL $2D50
+L2FD1: defb $8D
+L2FD2: defb $50
+L2FD3: defb $8E
+L2FD4: defb $2D
+;-------------------------------
+; 02E3 32;28;40 LD ($4028),A ; SET MARGIN
+; 22E3 C3;0E;2D JP L2D0E
+L2FD5: defb $E3
+L2FD6: defb $C3
+ defb $E4
+ defb $0E
+L2FD9: defb $E5
+L2FDA: defb $2D
+;-------------------------------
+L2FDB: defb $13 ; some kind of marker?
+;-------------------------------
+;
+; Original routine Modifications made to the copy of the PRINT-CH routines:
+;
+;
+; Variables in the sparsely populated G007 RAM areas
+;
+; Initialise 18 (decimal, = 12 in hex)RAM variables at $23xx:
+;
+; 2FE0 E6 0A ; 23E6 0A
+; 2FE2 55 0D ; 2355 0D
+; 2FE4 1E 0F ; 231E 0F
+; 2FE6 16 20 ; 2316 20
+; 2FE8 10 07 ; 2310 07
+; 2FEA 11 08 ; 2311 08
+; 2FEC 18 C0 ; 2318 C0
+;
+;------------------ ; G007_DISPLAY_OFFSET_FROM_DFILE_LESS_9 = E675 (6675 echoed in top 32K ???)
+L2FDC: defb $00 ; 2300 75
+ defb $75
+;------------------
+L2FDE: defb $01 ; 2301 E6
+ defb $E6
+;------------------
+L2FE0: defb $0A
+;------------------
+L2FE1: defb $55 ; 2355 0D
+L2FE2: defb $0D
+;------------------
+L2FE3: defb $1E ; 231E 0F
+L2FE4: defb $0F
+;------------------
+ defb $1E ; some kind of marker?
+;------------------
+L2FE6: defb $16 ; 2316 20
+ defb $20
+;------------------ ; G007_PLOT_ROUTINES_VECTOR = G007_PLOT_ROUTINES_VECTOR_DEFAULT = $0807
+L2FE8: defb $10 ; 2310 07
+ defb $07
+;------------------
+L2FEA: defb $11 ; 2311 08
+ defb $08
+;------------------
+L2FEC: defb $18 ; 2318 C0
+ defb $C0
+;------------------
+L2FEE: defb $35 ; 2335 EE
+ defb $EE
+;------------------
+L2FF0: defb $36 ; 2336 55
+ defb $55
+;------------------
+L2FF2: defb $37 ; 2337 C6
+ defb $C6
+;------------------
+; The values below were seen in RAM but are not initialised from this table:
+;
+; 2306 84 4084 G007_DISPLAY_ADDRESS_LESS_9
+; 2307 40
+; 2308 8D 408D G007_DISPLAY_ADDRESS
+; 2309 40
+; 230A 55 00 55,00 G007_TRIANGLE_TEXTURE
+; 230C 00 1E00 Character table address for CHR$0-63
+; 230D 1E
+; 230E 00 1E00 Character table address for CHR$128-159
+; 230F 1E
+;------------------
+; Modding offsets in copied code:
+;------------------
+; 07FD CD 08 08 CALL $0808 ; [ENTER-CH]
+; 23AC CD E6 2E CALL $2EE6
+;
+L2FF4: defb $AD
+ defb $E6
+ defb $AE
+ defb $2E
+;------------------
+; 0843 FD 35 39 DEC (IY+SPOSN-RAMBASE)
+; 23F2 C3 1D 2F JP $2F1D
+;
+L2FF8: defb $F2
+ defb $C3
+
+ defb $F3
+ defb $1D
+
+ defb $F4
+ defb $2F
+;------------------
+;
+; 083E 77 LD (HL),A ; WRITE-CH
+; 23ED 00 NOP ; WRITE-CH modified!
+;
+L2FFE: defb $ED
+ defb $00
+;------------------
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+; end of g007 stuff
+;
+#endif
+
+
+
+#code $3800,$0800
+
+
+
+
+#if 1
+
+mark_3800: DEFB $21, $00, $38 ; ld hl,Source
+mark_3803: DEFB $11, $00, $78 ; ld de,Destination
+mark_3806: DEFB $01, $00, $08 ; ld bc,MonitorSize
+mark_3809: DEFB $ED, $B0 ; ldir
+mark_380B: DEFB $3E, $74 ; ld a,74
+mark_380D: DEFB $32, $05, $40 ; ld (RamtopHi),a
+mark_3810: DEFB $CD, $C3, $03 ; call New
+
+mark_3813: DEFB $3E, $01 ; ld a,01
+mark_3815: DEFB $01, $93, $7B ; ld bc,Input_Prompt_Data
+mark_3818: DEFB $CD, $02, $79 ; call Get_A_addresses
+mark_381B: DEFB $CD, $2A, $0A ; call Cls
+mark_381E: DEFB $ED, $6B, $F8, $7F ; ld hl,Next_Address
+mark_3822: DEFB $E9 ; jp (hl)
+
+; Routine 0 the disassembler
+mark_3823: DEFB $CD, $FD, $78, $CD, $09
+mark_3828: DEFB $79, $18, $05, $16, $13, $CD, $53, $7B
+mark_3830: DEFB $CD, $81, $7B, $30, $05, $AF, $32, $21
+mark_3838: DEFB $40, $C9
+mark_383A: DEFB $21, $7B, $40, $36, $30, $23
+mark_3840: DEFB $36, $78, $CD, $2E, $7A, $CD, $72, $7A
+mark_3848: DEFB $78, $FE, $76, $20, $08, $21, $F5, $7C
+mark_3850: DEFB $CD, $A4, $7B, $18, $D6, $FE, $CB, $28
+mark_3858: DEFB $32, $FE, $ED, $28, $38, $FE, $DD, $28
+mark_3860: DEFB $44, $FE, $FD, $28, $44, $CD, $4F, $7A
+mark_3868: DEFB $2E, $24, $FE, $00, $28, $11, $2E, $2C
+mark_3870: DEFB $FE, $01, $28, $0E, $2C, $FE, $02, $28
+mark_3878: DEFB $09, $3E, $5E, $32, $42, $7D, $2C, $7D
+mark_3880: DEFB $81, $6F, $26, $7D, $6E, $EB, $CD, $D4
+mark_3888: DEFB $7A, $18, $A0, $CD, $72, $7A, $CD, $4F
+mark_3890: DEFB $7A, $C6, $36, $18, $EC, $CD, $72, $7A
+mark_3898: DEFB $CD, $4F, $7A, $2E, $18, $FE, $01, $28
+mark_38A0: DEFB $DE, $2E, $20, $18, $DA, $3E, $0B, $18
+mark_38A8: DEFB $02, $3E, $0D, $32, $41, $7D, $3E, $12
+mark_38B0: DEFB $32, $40, $7D, $2A, $F8, $7F, $23, $7E
+mark_38B8: DEFB $21, $16, $7D, $CD, $89, $7A, $23, $CB
+mark_38C0: DEFB $BE, $CD, $72, $7A, $78, $FE, $CB, $20
+mark_38C8: DEFB $05, $CD, $72, $7A, $18, $BD, $CD, $4F
+mark_38D0: DEFB $7A, $FE, $03, $28, $93, $F5, $C5, $FE
+mark_38D8: DEFB $00, $20, $05, $3E, $06, $B8, $20, $03
+mark_38E0: DEFB $CD, $72, $7A, $C1, $F1, $18, $81
+
+; Spare bytes
+mark_38E7: DEFB $00
+mark_38E8: DEFB $00, $00, $00, $00, $00, $00, $00, $00
+mark_38F0: DEFB $00, $00, $00, $00, $00, $00, $00, $00
+mark_38F8: DEFB $00, $00, $00, $00, $00
+
+; Start/Finish Addresses
+; prints request for input then calls input address routine
+mark_38FD: DEFB $01, $93, $7B
+mark_3900: DEFB $3E, $02
+
+; Get_A_addresses
+mark_3902: DEFB $11, $F8, $7F, $CD, $14, $7A, $C9
+
+; Check printer
+mark_3909: DEFB $11, $E1, $10, $CD, $3B, $7B, $2B
+mark_3910: DEFB $7E, $21, $21, $40, $36, $83, $FD, $CB
+mark_3918: DEFB $01, $CE, $FE, $1D, $C4, $2A, $0A, $C9
+
+; routine 1: DEFB hex dump
+mark_3920: DEFB $CD, $FD, $78, $CD, $09, $79, $21, $F9
+mark_3928: DEFB $7C, $CD, $9F, $7B, $21, $7B, $40, $36
+mark_3930: DEFB $33, $23, $36, $79, $CD, $81, $7B, $F8
+mark_3938: DEFB $CD, $3D, $7A, $0E, $08, $CD, $72, $7A
+mark_3940: DEFB $0D, $28, $0B, $CD, $81, $7B, $30, $F5
+mark_3948: DEFB $16, $0B, $CD, $53, $7B, $C9
+mark_394E: DEFB $CD, $48, $79, $18, $E1
+
+; routine 2: DEFB write
+mark_3953: DEFB $3E, $01, $01, $93, $7B
+mark_3958: DEFB $CD, $02, $79, $CD, $2A, $0A, $CD, $0E
+mark_3960: DEFB $0C, $CD, $3D, $7A, $11, $E8, $1C, $CD
+mark_3968: DEFB $A5, $7F, $CB, $45, $28, $05, $CD, $AD
+mark_3970: DEFB $7F, $18, $F7, $ED, $5B, $F8, $7F, $CD
+mark_3978: DEFB $0B, $7B, $ED, $53, $F8, $7F, $3E, $76
+mark_3980: DEFB $D7, $18, $DB
+
+; prints data associated with RST 08 or RST 28
+mark_3983: DEFB $0D, $20, $24, $0E, $04
+mark_3988: DEFB $16, $13, $CD, $53, $7B, $18, $18, $78
+mark_3990: DEFB $FE, $01, $20, $1A, $21, $7B, $40, $36
+mark_3998: DEFB $62, $23, $36, $7A, $16, $13, $CD, $53
+mark_39A0: DEFB $7B, $21, $F9, $7C, $CD, $9F, $7B, $CD
+mark_39A8: DEFB $3D, $7A, $CD, $72, $7A, $C9
+mark_39AE: DEFB $FE, $05
+mark_39B0: DEFB $C0, $21, $7B, $40, $36, $68, $23, $36
+mark_39B8: DEFB $7A, $0E, $04, $CD, $9C, $79, $78, $FE
+mark_39C0: DEFB $34, $C8, $CD, $83, $79, $18, $F7
+
+; Data Calculates absolute address for JR instructions
+; and adds number and addresses to mnemonic
+mark_39C7: DEFB $CD
+mark_39C8: DEFB $72, $7A, $AF, $CB, $78, $28, $01, $2F
+mark_39D0: DEFB $48, $47, $2A, $F8, $7F, $09, $EB, $21
+mark_39D8: DEFB $00, $7D, $7B, $CD, $89, $7A, $2B, $7A
+mark_39E0: DEFB $CD, $89, $7A, $23, $CB, $BE, $2B, $18
+mark_39E8: DEFB $1B, $CD, $72, $7A, $2B, $18, $15, $CD
+mark_39F0: DEFB $07, $7A, $18, $10, $CD, $72, $7A, $2B
+mark_39F8: DEFB $18, $03, $CD, $07, $7A, $2B, $CD, $A4
+mark_3A00: DEFB $7B, $21, $0D, $7C, $C3, $A4, $7B, $21
+mark_3A08: DEFB $00, $7D, $CD, $75, $7A, $CD, $72, $7A
+mark_3A10: DEFB $CB, $BE, $2B, $C9
+
+; Input_Address
+mark_3A14: DEFB $F5, $D5, $11, $E4
+mark_3A18: DEFB $10, $CD, $3B, $7B, $7D, $BB, $28, $05
+mark_3A20: DEFB $CD, $AD, $7F, $18, $F7, $D1, $CD, $21
+mark_3A28: DEFB $7B, $F1, $3D, $20, $E7, $C9
+
+; Initial
+mark_3A2E: DEFB $21, $42
+mark_3A30: DEFB $7D, $36, $5C, $2B, $36, $09, $2B, $36
+mark_3A38: DEFB $0F, $2B, $2B, $36, $E0
+
+; Next_Address
+mark_3A3D: DEFB $11, $F9, $7F
+mark_3A40: DEFB $21, $FE, $7C, $1A, $CD, $7F, $7A, $1B
+mark_3A48: DEFB $1A, $CD, $7F, $7A, $AF, $D7, $C9
+
+; Octal
+mark_3A4F: DEFB $78
+mark_3A50: DEFB $E6, $07, $4F, $78, $F5, $E6, $38, $0F
+mark_3A58: DEFB $0F, $0F, $47, $F1, $E6, $C0, $07, $CB
+mark_3A60: DEFB $07, $C9
+
+; Cont, $RST
+mark_3A62: DEFB $CD, $A1, $79, $C3, $2B, $78
+mark_3A68: DEFB $0E, $04, $CD, $A1, $79, $CD, $BE, $79
+mark_3A70: DEFB $18, $F3
+
+; Next_Byte
+mark_3A72: DEFB $21, $FE, $7C, $ED, $5B, $F8
+mark_3A78: DEFB $7F, $1A, $13, $ED, $53, $F8, $7F, $CD
+mark_3A80: DEFB $89, $7A, $D7, $23, $7E, $CB, $BF, $D7
+mark_3A88: DEFB $C9
+mark_3A89: DEFB $47, $E6, $0F, $C6, $1C, $CB, $FF
+mark_3A90: DEFB $77, $2B, $78, $E6, $F0, $1F, $1F, $1F
+mark_3A98: DEFB $1F, $C6, $1C, $77, $C9
+
+; Offsets
+mark_3A9D: DEFB $CB, $40, $20
+mark_3AA0: DEFB $19, $CD, $B3, $7A, $13, $C9
+mark_3AA6: DEFB $3E, $01
+mark_3AA8: DEFB $CB, $40, $20, $1E, $18, $05, $CB, $40
+mark_3AB0: DEFB $28, $0F, $13, $AF, $18, $14, $CB, $40
+mark_3AB8: DEFB $28, $07, $13, $78, $CB, $87, $0F, $18
+mark_3AC0: DEFB $09, $CD, $BB, $7A, $13, $C9
+mark_3AC7: DEFB $78, $18
+mark_3AC8: DEFB $01, $79, $13, $62, $6B, $6E, $26, $7C
+mark_3AD0: DEFB $CD, $A5, $7B, $C9
+
+; Control
+mark_3AD4: DEFB $1A, $F5, $E6, $07
+mark_3AD8: DEFB $21, $DD, $7A, $18, $1E, $F1, $F5, $CB
+mark_3AE0: DEFB $77, $28, $0D, $2A, $3E, $7D, $CB, $55
+mark_3AE8: DEFB $36, $00, $23, $28, $F9, $22, $3E, $7D
+mark_3AF0: DEFB $E6, $38, $0F, $0F, $CB, $0F, $28, $0C
+mark_3AF8: DEFB $21, $04, $7B, $E5, $3C, $26, $7D, $6F
+mark_3B00: DEFB $6E, $26, $7A, $E9, $F1, $CB, $7F, $C0
+mark_3B08: DEFB $13, $18, $C9
+
+; Transfer
+mark_3B0B: DEFB $C5, $7D, $2E, $E0, $95
+mark_3B10: DEFB $0F, $47, $7E, $CD, $8C, $7B, $23, $86
+mark_3B18: DEFB $D6, $1C, $12, $13, $23, $10, $F3, $C1
+mark_3B20: DEFB $C9
+mark_3B21: DEFB $C5, $06, $02, $2B, $4E, $2B, $7E
+mark_3B28: DEFB $CD, $8C, $7B, $81, $D6, $1C, $12, $13
+mark_3B30: DEFB $10, $F2, $C1, $3E, $76, $D7, $D7, $C9
+
+mark_3B38: DEFB $11, $EF, $10, $CD, $33, $78, $26, $7E
+mark_3B40: DEFB $0A, $6F, $CD, $9F, $7B, $03, $0A, $6F
+mark_3B48: DEFB $03, $CD, $A4, $7B, $CD, $53, $7B, $CD
+mark_3B50: DEFB $A5, $7F, $C9
+
+; Print_String
+mark_3B53: DEFB $C5, $D5, $E5, $FD, $CB
+mark_3B58: DEFB $21, $46, $28, $08, $ED, $4B, $39, $40
+mark_3B60: DEFB $4A, $CD, $0B, $09, $11, $E0, $7F, $ED
+mark_3B68: DEFB $4B, $3E, $7D, $06, $00, $79, $D6, $E0
+mark_3B70: DEFB $4F, $CD, $6B, $0B, $FD, $CB, $21, $4E
+mark_3B78: DEFB $28, $03, $3E, $76, $D7, $E1, $D1, $C1
+mark_3B80: DEFB $C9
+
+; Check_Finish
+mark_3B81: DEFB $2A, $FA, $7F, $ED, $5B, $F8, $7F
+mark_3B88: DEFB $A7, $ED, $52, $C9
+mark_3B8C: DEFB $D6, $1C, $07, $07
+mark_3B90: DEFB $07, $07, $C9
+
+; data for input prompt messages
+mark_3B93: DEFB $DD, $E3, $EB, $E3, $F2
+mark_3B98: DEFB $F8, $D5, $DC, $30, $E3, $D0, $E3
+
+; Add_String, for building mnemonic
+mark_3B9F: DEFB $3E
+mark_3BA0: DEFB $E0, $32, $3E, $7D, $AF, $C5, $D5, $A7
+mark_3BA8: DEFB $28, $0B, $CB, $7E, $20, $03, $23, $18
+mark_3BB0: DEFB $F9, $3D, $23, $18, $F2, $CD, $BB, $7B
+mark_3BB8: DEFB $D1, $C1, $C9
+mark_3BBB: DEFB $ED, $5B, $3E, $7D, $7E
+mark_3BC0: DEFB $CB, $7F, $20, $05, $CD, $CB, $7B, $18
+mark_3BC8: DEFB $F2, $CB, $BF, $FE, $40, $30, $08, $12
+mark_3BD0: DEFB $13, $ED, $53, $3E, $7D, $23, $C9
+mark_3BD7: DEFB $23
+mark_3BD8: DEFB $ED, $53, $3E, $7D, $E5, $FE, $43, $30
+mark_3BE0: DEFB $06, $26, $7D, $6F, $6E, $18, $14, $FE
+mark_3BE8: DEFB $64, $30, $05, $26, $7D, $6F, $18, $0B
+mark_3BF0: DEFB $21, $FE, $7B, $E5, $26, $7D, $6F, $6E
+mark_3BF8: DEFB $26, $79, $E9, $CD, $BB, $7B, $E1, $C9
+;
+; data for mnemonics
+; lower case are ZX inverse characters
+;
+mark_3C00: DEFB $A7, $A8, $A9, $AA, $AD, $B1, $C0, $A6
+mark_3C08: DEFB $A6, $C1, $A6, $10, $43, $91, $10, $45
+mark_3C10: DEFB $91, $E7, $E7, $C3, $C5, $C1, $C2, $62
+mark_3C18: DEFB $A8, $45, $A8, $47, $D6, $49, $D6, $47
+mark_3C20: DEFB $A6, $49, $A6, $29, $26, $A6, $28, $35
+mark_3C28: DEFB $B1, $38, $28, $AB, $28, $28, $AB, $9C
+mark_3C30: DEFB $9D, $9E, $9F, $A0, $A1, $A2, $A3, $35
+mark_3C38: DEFB $3A, $38, $AD, $56, $31, $B1, $37, $2A
+mark_3C40: DEFB $B9, $4B, $BD, $2F, $B5, $CD, $58, $A9
+mark_3C48: DEFB $58, $A8, $38, $3A, $A7, $38, $C3, $26
+mark_3C50: DEFB $33, $A9, $3D, $34, $B7, $34, $B7, $28
+mark_3C58: DEFB $B5, $35, $34, $B5, $37, $38, $B9, $2F
+mark_3C60: DEFB $B5, $80, $D3, $E2, $CB, $CB, $29, $AE
+mark_3C68: DEFB $2A, $AE, $E5, $E8, $66, $1A, $A6, $4F
+mark_3C70: DEFB $E6, $10, $5C, $11, $DA, $45, $DA, $80
+mark_3C78: DEFB $80, $10, $41, $91, $5C, $DA, $CF, $CF
+mark_3C80: DEFB $80, $CF, $80, $80, $80, $80, $33, $BF
+mark_3C88: DEFB $BF, $33, $A8, $A8, $35, $B4, $35, $AA
+mark_3C90: DEFB $B5, $B2, $47, $A8, $49, $A8, $C7, $C9
+mark_3C98: DEFB $38, $31, $A6, $38, $37, $A6, $E4, $38
+mark_3CA0: DEFB $C7, $27, $2E, $B9, $37, $2A, $B8, $38
+mark_3CA8: DEFB $2A, $B9, $10, $28, $91, $33, $2A, $AC
+mark_3CB0: DEFB $B3, $AE, $38, $C3, $58, $A8, $2E, $B2
+mark_3CB8: DEFB $9C, $A4, $9D, $9E, $CD, $CD, $CD, $CD
+mark_3CC0: DEFB $49, $A9, $47, $A9, $2E, $9A, $37, $9A
+mark_3CC8: DEFB $CF, $CF, $80, $80, $5E, $1A, $DE, $E9
+mark_3CD0: DEFB $E9, $E0, $E0, $E0, $E0, $A6, $A6, $AE
+mark_3CD8: DEFB $B7, $80, $80, $AE, $A9, $2E, $B7, $29
+mark_3CE0: DEFB $B7, $CD, $28, $B5, $E2, $D3, $34, $B9
+mark_3CE8: DEFB $33, $34, $B5, $CB, $29, $2F, $33, $BF
+mark_3CF0: DEFB $D1, $D1, $D1, $D1, $D1, $2D, $26, $31
+mark_3CF8: DEFB $B9, $45, $2B, $A7, $10, $1D, $A9, $27
+;
+; data and data pointers for disassembler
+;
+mark_3D00: DEFB $9F, $A6, $C9, $B3, $AE, $BB, $9D, $B6
+mark_3D08: DEFB $C6, $2D, $B1, $2E, $BD, $2E, $BE, $10
+mark_3D10: DEFB $41, $91, $10, $41, $15, $2A, $27, $91
+mark_3D18: DEFB $CD, $D3, $D9, $DF, $3C, $E8, $EB, $EE
+mark_3D20: DEFB $F3, $F3, $F3, $F5, $91, $81, $89, $6D
+mark_3D28: DEFB $6A, $F9, $7B, $3A, $70, $9C, $A1, $76
+mark_3D30: DEFB $A4, $AA, $AD, $FC, $B3, $96, $B8, $BB
+mark_3D38: DEFB $C1, $C7, $87, $1B, $81, $A1, $E4, $7F
+mark_3D40: DEFB $0F, $09, $5C, $27, $A8, $29, $AA, $37
+mark_3D48: DEFB $B1, $37, $B7, $2A, $BD, $31, $A9, $26
+mark_3D50: DEFB $9A, $2F, $B7, $34, $3A, $B9, $28, $A6
+mark_3D58: DEFB $26, $A9, $1A, $C1, $38, $B5, $26, $AB
+mark_3D60: DEFB $1A, $E9, $2E, $B3, $E9, $EF, $F4, $FA
+mark_3D68: DEFB $8F, $C7, $FA, $17, $00, $E0, $17, $13
+mark_3D70: DEFB $7A, $45, $00, $8A, $C5, $00, $F5, $59
+mark_3D78: DEFB $3E, $13, $77, $7A, $45, $00, $92, $C5
+mark_3D80: DEFB $9E, $58, $45, $13, $15, $AA, $C5, $6A
+mark_3D88: DEFB $13, $72, $45, $0B, $07, $B2, $C5, $07
+mark_3D90: DEFB $0B, $7F, $E8, $82, $87, $CB, $62, $5C
+mark_3D98: DEFB $2F, $90, $B8, $6B, $7F, $46, $7E, $81
+mark_3DA0: DEFB $00, $FA, $3E, $86, $7A, $43, $86, $92
+mark_3DA8: DEFB $C5, $6A, $FF, $5F, $6A, $7A, $3B, $86
+mark_3DB0: DEFB $92, $C5, $6A, $7F, $46, $7E, $82, $9E
+mark_3DB8: DEFB $CF, $92, $00, $7A, $A1, $2F, $8A, $C5
+mark_3DC0: DEFB $00, $7A, $A4, $2F, $8A, $C5, $00, $7A
+mark_3DC8: DEFB $A7, $2F, $8A, $C5, $00, $7A, $63, $00
+mark_3DD0: DEFB $92, $C5, $AA, $52, $62, $AA, $BA, $C5
+mark_3DD8: DEFB $00, $50, $B2, $15, $A2, $C5, $13, $6A
+mark_3DE0: DEFB $45, $11, $13, $9A, $C5, $13, $11, $00
+mark_3DE8: DEFB $BA, $3E, $B0, $FA, $B6, $B8, $7F, $BC
+mark_3DF0: DEFB $C4, $87, $D5, $B9, $E1, $D7, $BC, $E2
+mark_3DF8: DEFB $D7, $FA, $19, $00, $D8, $37, $13, $6A
+;
+; print data for menu & routines
+;
+mark_3E00: DEFB $00, $00, $00, $00, $32, $2A, $33, $BA
+mark_3E08: DEFB $00, $00, $00, $00, $14, $14, $14, $94
+mark_3E10: DEFB $1C, $00, $35, $37, $2E, $33, $39, $00
+mark_3E18: DEFB $28, $34, $29, $AA, $1D, $00, $2D, $2A
+mark_3E20: DEFB $3D, $00, $29, $3A, $32, $B5, $1E, $00
+mark_3E28: DEFB $3C, $37, $2E, $39, $AA, $1F, $00, $2E
+mark_3E30: DEFB $33, $38, $2A, $37, $B9, $20, $00, $29
+mark_3E38: DEFB $2A, $31, $2A, $39, $AA, $21, $00, $39
+mark_3E40: DEFB $37, $26, $33, $38, $2B, $2A, $B7, $22
+mark_3E48: DEFB $00, $38, $2A, $26, $37, $28, $AD, $23
+mark_3E50: DEFB $00, $37, $2A, $35, $31, $26, $28, $AA
+mark_3E58: DEFB $24, $00, $26, $38, $38, $2A, $32, $27
+mark_3E60: DEFB $31, $2A, $B7, $25, $00, $37, $3A, $33
+mark_3E68: DEFB $00, $28, $34, $29, $AA, $26, $00, $28
+mark_3E70: DEFB $26, $31, $28, $3A, $31, $26, $39, $34
+mark_3E78: DEFB $B7, $27, $00, $28, $2D, $37, $0D, $00
+mark_3E80: DEFB $29, $3A, $32, $B5, $28, $00, $26, $38
+mark_3E88: DEFB $28, $2E, $2E, $00, $29, $3A, $32, $B5
+mark_3E90: DEFB $29, $00, $37, $2A, $33, $3A, $32, $27
+mark_3E98: DEFB $2A, $B7, $2A, $00, $2E, $32, $26, $2C
+mark_3EA0: DEFB $2A, $B7, $2B, $00, $32, $2A, $33, $3A
+mark_3EA8: DEFB $00, $9E, $80, $80, $00, $00, $00, $00
+mark_3EB0: DEFB $00, $00, $00, $00, $00, $00, $00, $00
+mark_3EB8: DEFB $00, $00, $00, $00, $00, $00, $00, $00
+mark_3EC0: DEFB $00, $00, $00, $00, $00, $00, $00, $00
+mark_3EC8: DEFB $00, $00, $00, $00, $00, $00, $00, $00
+mark_3ED0: DEFB $31, $2E, $32, $2E, $B9, $37, $34, $3A
+mark_3ED8: DEFB $39, $2E, $33, $2A, $80, $38, $39, $26
+mark_3EE0: DEFB $37, $39, $80, $26, $29, $29, $37, $2A
+mark_3EE8: DEFB $38, $38, $80, $2B, $2E, $33, $2E, $38
+mark_3EF0: DEFB $2D, $80, $1D, $00, $2B, $34, $37, $80
+mark_3EF8: DEFB $35, $37, $2E, $33, $39, $2A, $37, $80
+
+; addresses of routines
+mark_3F00: DEFB $23, $78 ;, $.dw, $7823
+mark_3F02: DEFB $20, $79 ; , $.dw, $7902
+mark_3F04: DEFB $53, $79 ;, $.dw, $7953
+mark_3F06: DEFB $FF, $FF
+mark_3F08: DEFB $FF, $FF
+mark_3F0A: DEFB $FF, $FF
+mark_3F0C: DEFB $FF, $FF
+mark_3F0E: DEFB $FF, $FF
+mark_3F10: DEFB $FF, $FF
+mark_3F12: DEFB $13, $38 ;, $.dw, $3813
+mark_3F14: DEFB $FF, $FF
+mark_3F16: DEFB $FF, $FF
+mark_3F18: DEFB $FF, $FF
+mark_3F1A: DEFB $FF, $FF
+mark_3F1C: DEFB $FF, $FF
+mark_3F1E: DEFB $FF, $FF
+
+; Read_Keyboard
+mark_3F20: DEFB $D5, $C5, $E5, $2A, $25, $40, $E5, $ED
+mark_3F28: DEFB $4B, $25, $40, $E1, $C5, $A7, $ED, $42
+mark_3F30: DEFB $28, $F5, $79, $3C, $28, $F1, $E1, $CD
+mark_3F38: DEFB $BD, $07, $7E, $E1, $C1, $D1, $FE, $76
+mark_3F40: DEFB $C8, $FE, $77, $C8, $FE, $00, $20, $05
+mark_3F48: DEFB $CD, $2A, $0A, $CF, $0C, $FE, $1C, $38
+mark_3F50: DEFB $CF, $FE, $2C, $30, $CB, $C9
+;
+; Menu
+;
+mark_3F56: DEFB $21, $21
+mark_3F58: DEFB $40, $CB, $7E, $28, $04, $2A, $7B, $40
+mark_3F60: DEFB $E9, $21, $00, $7E, $06, $14, $3E, $03
+mark_3F68: DEFB $32, $21, $40, $11, $E1, $18, $CD, $9F
+mark_3F70: DEFB $7B, $CD, $53, $7B, $10, $F8, $01, $99
+mark_3F78: DEFB $7B, $CD, $3B, $7B, $2B, $7E, $D6, $1C
+mark_3F80: DEFB $47, $07, $6F, $26, $7F, $7E, $23, $66
+mark_3F88: DEFB $6F, $E5, $C5, $CD, $2A, $0A, $C1, $3E
+mark_3F90: DEFB $E0, $32, $3E, $7D, $78, $21, $10, $7E
+mark_3F98: DEFB $CD, $A5, $7B, $16, $1A, $CD, $53, $7B
+mark_3FA0: DEFB $3E, $76, $D7, $D7, $C9
+;
+; InputString, the heart of all routines
+;
+mark_3FA5: DEFB $3E, $01, $32
+mark_3FA8: DEFB $21, $40, $21, $E0, $7F, $36, $17, $CD
+mark_3FB0: DEFB $C0, $7F, $CD, $20, $7F, $FE, $76, $20
+mark_3FB8: DEFB $10, $3E, $E0, $BD, $28, $F4, $36, $00
+mark_3FC0: DEFB $23, $22, $3E, $7D, $CD, $53, $7B, $2B
+mark_3FC8: DEFB $C9
+mark_3FC9: DEFB $FE, $77, $20, $0B, $3E, $E0, $BD
+mark_3FD0: DEFB $28, $DB, $CD, $BE, $7F, $2B, $18, $D5
+mark_3FD8: DEFB $77, $7B, $BD, $28, $D0, $23, $18, $CD
+;
+; mnemonic string, ram area for mnemonic to be built up
+;
+mark_3FE0: DEFB $00, $00, $00, $00, $00, $00, $00, $00
+mark_3FE8: DEFB $00, $00, $00, $00, $00, $00, $00, $00
+;
+; spare bytes
+;
+mark_3FF0: DEFB $00, $00, $00, $00, $00, $00, $00, $00
+;
+; next address for routine
+;
+mark_3FF8: DEFB $00, $00
+;
+; finish address for routine
+;
+mark_3FFA: DEFB $00, $00
+;
+; spare bytes
+;
+mark_3FFC: DEFB $00, $00, $00
+mark_3FFF: DEFB $C9 ; ret
+
+
+#else
+;
+;
+;
+Input_Prompt_Data equ $7B93
+Get_A_addresses equ $7902
+Next_Address equ $7FFB
+#endif
+
+
+
+
+
+
+
+
+#end ; required by zasm
+
+
diff --git a/mirror/tablix.org/sg81.html b/mirror/tablix.org/sg81.html
@@ -0,0 +1,3224 @@
+<HTML>
+<HEAD>
+<TITLE>
+Assembly Listing of the Shoulders of Giants ZX81 ROM.
+</TITLE>
+</HEAD>
+<BODY>
+<font face = "Courier"> </font>
+<PRE>
+; =========================================================
+; 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
+
+<a name="subtract"></a><b>subtract</b> EQU $174C ; SUBTRACT
+<a name="multiply"></a><b>multiply</b> EQU $176C ; multiply
+<a name="division"></a><b>division</b> EQU $1882 ; division
+<a name="addition"></a><b>addition</b> EQU $1755 ; addition
+<a name="truncate"></a><b>truncate</b> EQU $18E4 ; truncate
+<a name="e_to_fp"></a><b>e_to_fp</b> EQU $155A ; e-to-fp
+<a name="TEST_ROOM"></a><b>TEST_ROOM</b> EQU $0EC5 ; TEST-ROOM
+<a name="FIND_INT"></a><b>FIND_INT</b> EQU $0EA7 ; FIND-INT
+<a name="STACK_A"></a><b>STACK_A</b> EQU $151D ; STACK-A
+<a name="STACK_BC"></a><b>STACK_BC</b> EQU $1520 ; STACK-BC
+<a name="STK_FETCH"></a><b>STK_FETCH</b> EQU $13F8 ; STK-FETCH
+<a name="STK_STO_s"></a><b>STK_STO_s</b> EQU $12C3 ; STK-STO-$
+<a name="FP_TO_A"></a><b>FP_TO_A</b> EQU $15CD ; FP-TO-A
+<a name="CLASS_06"></a><b>CLASS_06</b> EQU $0D92 ; CLASS-06
+<a name="CHECK_2"></a><b>CHECK_2</b> EQU $0D22 ; CHECK-2
+<a name="SCANNING"></a><b>SCANNING</b> EQU $0F55 ; SCANNING
+<a name="PRINT_FP"></a><b>PRINT_FP</b> EQU $15DB ; PRINT-FP
+
+; -----------------------------------------------------------------------------
+
+<a name="ORG"></a><b>ORG</b> $0010
+
+;--------------------------------
+; THE <b><font color=#333388>'PRINT A CHARACTER'</font></b> 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.
+; <font color=#9900FF>Note.</font> the accumulator is preserved only when printing to the screen.
+
+<a name="PRINT_A"></a><b>PRINT_A</b> AND A ; test for zero - space.
+ JP NZ,<A href="#PRINT_CH">PRINT_CH</a> ; jump forward if not to PRINT-CH.
+
+ JP <A href="#PRINT_SP">PRINT_SP</a> ; jump forward to PRINT-SP.
+
+; ---
+
+ <font color=#3333FF> DEFB $01 ;+ unused location. Version. PRINT PEEK 23</font>
+
+; -----------------------------------------------------------------------------
+
+<a name="ORG"></a><b>ORG</b> $0028
+
+
+; -----------------------
+; THE <b><font color=#333388>'CALCULATE'</font></b> RESTART
+; -----------------------
+; An immediate jump is made to the CALCULATE routine the address of which
+; has changed.
+
+<a name="FP_CALC"></a><b>FP_CALC</b> <font color=#3333FF> JP <A href="#CALCULATE">CALCULATE</a> ;+ jump to the NEW calculate routine address.</font>
+
+<a name="end_calc"></a><b>end_calc</b> 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.
+
+; -----------------------------------------------------------------------------
+
+<a name="ORG"></a><b>ORG</b> $13AE
+
+; ------------------------
+; THE <b><font color=#333388>'L-ENTER'</font></b> SUBROUTINE
+; ------------------------
+; Part of the LET command contains a natural subroutine which is a
+; conditional LDIR. The copy only occurs of BC is non-zero.
+
+<a name="L_ENTER"></a><b>L_ENTER</b> EX DE,HL ;
+
+
+<a name="COND_MV"></a><b>COND_MV</b> LD A,B ;
+ OR C ;
+ RET Z ;
+
+ PUSH DE ;
+
+ LDIR ; Copy Bytes
+
+ POP HL ;
+ RET ; Return.
+
+; -----------------------------------------------------------------------------
+
+<a name="ORG"></a><b>ORG</b> $14E5
+
+; ---------------------
+; THE <b><font color=#333388>'NEXT DIGIT'</font></b> 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.
+
+<a name="NXT_DGT_1"></a><b>NXT_DGT_1</b> 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
+<font color=#FF3333>;;; DEFB $05 ;;division</font>
+ <font color=#3333FF> DEFB $04 ;;+multiply</font>
+ DEFB $C0 ;;st-mem-0
+<font color=#FF3333>;;; DEFB $04 ;;multiply</font>
+ <font color=#3333FF> DEFB $05 ;;+division</font>
+ DEFB $0F ;;addition
+ DEFB $34 ;;end-calc
+
+ JR NXT_DGT_1 ; loop back till exhausted to NXT-DGT-1
+
+; -----------------------------------------------------------------------------
+
+<a name="ORG"></a><b>ORG</b> $16B2
+
+; -------------------------------------
+; THE <b><font color=#333388>'FLOATING POINT PRINT ZEROS'</font></b> LOOP
+; -------------------------------------
+
+; This branch deals with zeros after decimal point.
+; e.g. .01 or .0000999
+; <font color=#9900FF>Note.</font> 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
+
+<a name="PF_ZEROS"></a><b>PF_ZEROS</b> NEG ; negate makes number positive 1 to 4.
+ LD B,A ; zero count to B.
+
+ LD A,$1B ; prepare character '.'
+ RST 10H ; PRINT-A
+
+<a name="PF_ZRO_LP"></a><b>PF_ZRO_LP</b> LD A,$1C ; prepare a '0' in the accumulator each time.
+
+<a name="PFZROLP"></a><b>PFZROLP</b> RST 10H ; PRINT-A
+
+ <font color=#3333FF> DJNZ <A href="#PF_ZRO_LP">PF_ZRO_LP</a> ;+ New loop back to PF-ZRO-LP</font>
+
+<font color=#FF3333>;;; DJNZ <A href="#PFZROLP">PFZROLP</a> ; obsolete loop back to PFZROLP</font>
+
+
+; and continue with trailing fractional digits...
+
+; -----------------------------------------------------------------------------
+
+<a name="ORG"></a><b>ORG</b> $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 <b><font color=#333388>'TABLE OF CONSTANTS'</font></b>
+; ------------------------
+; 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.
+
+<font color=#FF3333>;;; L1915: DEFB $00 ;;Bytes: 1</font>
+<font color=#FF3333>;;; DEFB $B0 ;;Exponent $00</font>
+<font color=#FF3333>;;; DEFB $00 ;;(+00,+00,+00)</font>
+<font color=#FF3333>;;; L1918: DEFB $31 ;;Exponent $81, Bytes: 1</font>
+<font color=#FF3333>;;; DEFB $00 ;;(+00,+00,+00)</font>
+<font color=#FF3333>;;; L191A: DEFB $30 ;;Exponent: $80, Bytes: 1</font>
+<font color=#FF3333>;;; DEFB $00 ;;(+00,+00,+00)</font>
+<font color=#FF3333>;;; L191C: DEFB $F1 ;;Exponent: $81, Bytes: 4</font>
+<font color=#FF3333>;;; DEFB $49,$0F,$DA,$A2 ;;</font>
+<font color=#FF3333>;;; L1921: DEFB $34 ;;Exponent: $84, Bytes: 1</font>
+<font color=#FF3333>;;; DEFB $20 ;;(+00,+00,+00)</font>
+
+<a name="TAB_CNST"></a><b>TAB_CNST</b> 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 <b><font color=#333388>'TABLE OF ADDRESSES'</font></b>
+; ------------------------
+;
+; Starts with binary operations which have two operands and one result.
+; three pseudo binary operations first.
+
+<a name="tbl_addrs"></a><b>tbl_addrs</b> 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 <b><font color=#333388>'FLOATING POINT CALCULATOR'</font></b>
+; -------------------------------
+;
+;
+
+<a name="CALCULATE"></a><b>CALCULATE</b> CALL <A href="#STK_PNTRS">STK_PNTRS</a> ; 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...
+
+<a name="GEN_ENT_1"></a><b>GEN_ENT_1</b> 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
+
+<a name="GEN_ENT_2"></a><b>GEN_ENT_2</b> 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.
+
+<a name="RE_ENTRY"></a><b>RE_ENTRY</b> 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
+
+<a name="SCAN_ENT"></a><b>SCAN_ENT</b> PUSH HL ; save pointer on stack *
+ AND A ; now test the literal
+ JP P,<A href="#FIRST_3D">FIRST_3D</a> ; 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 <A href="#ENT_TABLE">ENT_TABLE</a> ; forward to ENT-TABLE
+
+; ---
+
+; the branch was here with simple literals.
+
+<a name="FIRST_3D"></a><b>FIRST_3D</b> CP $18 ; compare with first unary operations.
+ JR NC,<A href="#DOUBLE_A">DOUBLE_A</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 ;
+
+<a name="DOUBLE_A"></a><b>DOUBLE_A</b> RLCA ; double the literal
+ LD L,A ; and store in L for indexing
+
+<a name="ENT_TABLE"></a><b>ENT_TABLE</b> 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 <b><font color=#333388>'DELETE'</font></b> SUBROUTINE
+; -----------------------
+; <font color=#339933>(offset $02: 'delete')</font>
+; 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
+
+<a name="delete"></a><b>delete</b> RET ; return - indirect jump if from above.
+
+; ---------------------------------
+; THE <b><font color=#333388>'SINGLE OPERATION'</font></b> 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.
+
+<a name="fp_calc_2"></a><b>fp_calc_2</b> 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 <A href="#SCAN_ENT">SCAN_ENT</a> ; back to SCAN-ENT
+ ; next literal will be end-calc in scanning
+
+; ------------------------------
+; THE <b><font color=#333388>'TEST 5 SPACES'</font></b> 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.
+
+<a name="TEST_5_SP"></a><b>TEST_5_SP</b> PUSH DE ; save
+ PUSH HL ; registers
+ LD BC,$0005 ; an overhead of five bytes
+ CALL <A href="#TEST_ROOM">TEST_ROOM</a> ; 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 <b><font color=#333388>'MOVE A FLOATING POINT NUMBER'</font></b> 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
+
+<a name="MOVE_FP"></a><b>MOVE_FP</b> CALL <A href="#TEST_5_SP">TEST_5_SP</a> ; 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 <b><font color=#333388>'STACK LITERALS'</font></b> 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.
+
+<a name="stk_data"></a><b>stk_data</b> LD H,D ; transfer STKEND
+ LD L,E ; to HL for result.
+
+<a name="STK_CONST"></a><b>STK_CONST</b> CALL <A href="#TEST_5_SP">TEST_5_SP</a> ; 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.
+
+<font color=#FF3333>;;; PUSH BC ; save BC - value 5 from test room. No need.</font>
+
+ 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,<A href="#FORM_EXP">FORM_EXP</a> ; 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).
+
+<a name="FORM_EXP"></a><b>FORM_EXP</b> 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
+<font color=#FF3333>;;; LD B,$00 ; prepare to copy. <font color=#9900FF>Note.</font> B is zero.</font>
+ LDIR ; copy C bytes
+
+<font color=#FF3333>;;; POP BC ; restore 5 counter to BC.</font>
+
+ 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
+
+<a name="STK_ZEROS"></a><b>STK_ZEROS</b> 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 <A href="#STK_ZEROS">STK_ZEROS</a> ; loop back to STK-ZEROS until done.
+
+; -------------------------------
+; THE <b><font color=#333388>'SKIP CONSTANTS'</font></b> 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.
+
+<font color=#FF3333>;;; L1A2D: AND A ; test if initially zero.</font>
+<font color=#FF3333>;;; L1A2E: RET Z ; return if zero. >></font>
+<font color=#FF3333>;;; PUSH AF ; save count.</font>
+<font color=#FF3333>;;; PUSH DE ; and normal STKEND</font>
+<font color=#FF3333>;;; LD DE,$0000 ; dummy value for STKEND at start of ROM</font>
+<font color=#FF3333>;;; CALL STK_CONST ; routine STK-CONST works through variable</font>
+<font color=#FF3333>;;; ; length records.</font>
+<font color=#FF3333>;;; POP DE ; restore real STKEND</font>
+<font color=#FF3333>;;; POP AF ; restore count</font>
+<font color=#FF3333>;;; DEC A ; decrease</font>
+<font color=#FF3333>;;; JR L1A2E ; loop back to SKIP-NEXT</font>
+
+; --------------------------------
+; THE <b><font color=#333388>'MEMORY LOCATION'</font></b> 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.
+
+<a name="LOC_MEM"></a><b>LOC_MEM</b> 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 <b><font color=#333388>'GET FROM MEMORY AREA'</font></b> SUBROUTINE
+; -------------------------------------
+; offsets $E0 to $FF: 'get-mem-0', 'get-mem-1' etc.
+; A holds $00-$1F offset.
+; The calculator stack increases by 5 bytes.
+; <font color=#9900FF>Note.</font> first two instructions have been swapped to create a subroutine.
+
+<a name="get_mem_x"></a><b>get_mem_x</b> LD HL,($401F) ; MEM is base address of the memory cells.
+
+<a name="INDEX_5"></a><b>INDEX_5</b> PUSH DE ; save STKEND
+
+ CALL <A href="#LOC_MEM">LOC_MEM</a> ; routine LOC-MEM so that HL = first byte
+ CALL <A href="#MOVE_FP">MOVE_FP</a> ; 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 <b><font color=#333388>'STACK A CONSTANT'</font></b> SUBROUTINE
+; ---------------------------------
+; <font color=#339933>(offset $A0: 'stk-zero')</font>
+; <font color=#339933>(offset $A1: 'stk-one')</font>
+; <font color=#339933>(offset $A2: 'stk-half')</font>
+; <font color=#339933>(offset $A3: 'stk-pi/2')</font>
+; <font color=#339933>(offset $A4: 'stk-ten')</font>
+; 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.
+
+<a name="stk_con_x"></a><b>stk_con_x</b> LD HL,TAB_CNST ; Address: Table of constants.
+
+ JR <A href="#INDEX_5">INDEX_5</a> ; and join subsroutine above.
+
+; ---
+
+<font color=#FF3333>;;; LD H,D ; save STKEND - required for result</font>
+<font color=#FF3333>;;; LD L,E ;</font>
+<font color=#FF3333>;;; EXX ; swap</font>
+<font color=#FF3333>;;; PUSH HL ; save pointer to next literal</font>
+<font color=#FF3333>;;; LD HL,L1515 ; Address: stk-zero - start of table of</font>
+<font color=#FF3333>;;; ; constants</font>
+<font color=#FF3333>;;; EXX ;</font>
+<font color=#FF3333>;;; CALL SKIP_CONS ; routine SKIP-CONS</font>
+<font color=#FF3333>;;; CALL STK_CONST ; routine STK-CONST</font>
+<font color=#FF3333>;;; EXX ;</font>
+<font color=#FF3333>;;; POP HL ; restore pointer to next literal.</font>
+<font color=#FF3333>;;; EXX ;</font>
+<font color=#FF3333>;;; RET ; return.</font>
+
+; ---------------------------------------
+; THE <b><font color=#333388>'STORE IN A MEMORY AREA'</font></b> 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.
+
+<a name="sto_mem_x"></a><b>sto_mem_x</b> PUSH HL ; save the result pointer.
+ EX DE,HL ; transfer to DE.
+ LD HL,($401F) ; fetch MEM the base of memory area.
+ CALL <A href="#LOC_MEM">LOC_MEM</a> ; routine LOC-MEM sets HL to the destination.
+ EX DE,HL ; swap - HL is start, DE is destination.
+
+<font color=#FF3333>;;; CALL <A href="#MOVE_FP">MOVE_FP</a> ; routine MOVE-FP.</font>
+<font color=#FF3333>;;; ; <font color=#9900FF>Note.</font> a short ld bc,5; ldir</font>
+<font color=#FF3333>;;; ; the embedded memory check is not required</font>
+<font color=#FF3333>;;; ; so these instructions would be faster!</font>
+
+ <font color=#3333FF> LD C,$05 ;+ one extra byte but </font>
+ <font color=#3333FF> LDIR ;+ faster and no memory check.</font>
+
+ EX DE,HL ; DE = STKEND
+ POP HL ; restore original result pointer
+ RET ; return.
+
+; -------------------------
+; THE <b><font color=#333388>'EXCHANGE'</font></b> 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.
+
+<a name="exchange"></a><b>exchange</b> LD B,$05 ; there are five bytes to be swapped
+
+; start of loop.
+
+<a name="SWAP_BYTE"></a><b>SWAP_BYTE</b> LD A,(DE) ; each byte of second
+<font color=#FF3333>;;; LD C,(HL) ; each byte of first</font>
+<font color=#FF3333>;;; EX DE,HL ; swap pointers</font>
+ <font color=#3333FF> ld c,a ;+</font>
+ <font color=#3333FF> ld a,(hl) ;+</font>
+ LD (DE),A ; store each byte of first
+ LD (HL),C ; store each byte of second
+ INC HL ; advance both
+ INC DE ; pointers.
+ DJNZ <A href="#SWAP_BYTE">SWAP_BYTE</a> ; loop back to SWAP-BYTE until all 5 done.
+
+<font color=#FF3333>;;; EX DE,HL ; even up the exchanges (one byte saved)</font>
+
+ RET ; return.
+
+; ---------------------------------
+; THE <b><font color=#333388>'SERIES GENERATOR'</font></b> 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.
+
+<a name="seriesg_x"></a><b>seriesg_x</b> LD B,A ; parameter $00 - $1F to B counter
+ CALL <A href="#GEN_ENT_1">GEN_ENT_1</a> ; 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
+
+<a name="G_LOOP"></a><b>G_LOOP</b> 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 <A href="#GEN_ENT_2">GEN_ENT_2</a> ; 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.
+
+<a name="negate"></a><b>negate</b> 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.
+
+<a name="abs"></a><b>abs</b> 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.
+
+<a name="sgn"></a><b>sgn</b> 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,<A href="#FP_0_1">FP_0_1</a> ; 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.
+
+<a name="peek"></a><b>peek</b> CALL <A href="#FIND_INT">FIND_INT</a> ; routine FIND-INT puts address in BC.
+ LD A,(BC) ; load contents into A register.
+
+<a name="IN_PK_STK"></a><b>IN_PK_STK</b> JP <A href="#STACK_A">STACK_A</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.
+; <font color=#9900FF>Note.</font> that STACK-BC re-initializes the IY register to $4000 if a user-written
+; program has altered it.
+
+<a name="usr_no"></a><b>usr_no</b> CALL <A href="#FIND_INT">FIND_INT</a> ; 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.
+
+<a name="greater_0"></a><b>greater_0</b> 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 <A href="#SIGN_TO_C">SIGN_TO_C</a> ; 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.
+
+<a name="not"></a><b>not</b> LD A,(HL) ; get exponent byte.
+ NEG ; negate - sets carry if non-zero.
+ CCF ; complement so carry set if zero, else reset.
+ JR <A href="#FP_0_1">FP_0_1</a> ; 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.
+
+<a name="less_0"></a><b>less_0</b> XOR A ; set xor mask to zero
+ ; (carry will become set if sign is negative).
+
+; transfer sign of mantissa to Carry Flag.
+
+<a name="SIGN_TO_C"></a><b>SIGN_TO_C</b> 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.
+
+<a name="FP_0_1"></a><b>FP_0_1</b> PUSH HL ; save pointer to the first byte
+ LD B,$05 ; five bytes to do.
+
+<a name="FP_loop"></a><b>FP_loop</b> LD (HL),$00 ; insert a zero.
+ INC HL ;
+ DJNZ <A href="#FP_loop">FP_loop</a> ; 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).
+
+<a name="or"></a><b>or</b> LD A,(DE) ; fetch exponent of second number
+ AND A ; test it.
+ RET Z ; return if zero.
+
+ SCF ; set carry flag
+ JR <A href="#FP_0_1">FP_0_1</a> ; 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.
+
+<a name="no_v_no"></a><b>no_v_no</b> LD A,(DE) ; fetch exponent of second number.
+ AND A ; test it.
+ RET NZ ; return if not zero.
+
+ JR <A href="#FP_0_1">FP_0_1</a> ; 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.
+
+<a name="str_v_no"></a><b>str_v_no</b> 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 <b><font color=#333388>'x>y'</font></b> 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.
+
+<a name="no_l_eql"></a><b>no_l_eql</b> LD A,B ; transfer literal to accumulator.
+
+<font color=#FF3333>;;; SUB $08 ; subtract eight - which is not useful.</font>
+
+ BIT 2,A ; isolate '>', '<', '='.
+
+ JR NZ,<A href="#EX_OR_NOT">EX_OR_NOT</a> ; skip to EX-OR-NOT with these.
+
+ DEC A ; else make $00-$02, $08-$0A to match bits 0-2.
+
+<a name="EX_OR_NOT"></a><b>EX_OR_NOT</b> RRCA ; the first RRCA sets carry for a swap.
+ JR NC,<A href="#NU_OR_STR">NU_OR_STR</a> ; 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.
+
+; <font color=#9900FF>Note.</font> 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.
+
+<a name="NU_OR_STR"></a><b>NU_OR_STR</b> <font color=#3333FF>RRCA ;+ causes 'eql/neql' to set carry.</font>
+ <font color=#3333FF> PUSH AF ;+ save the carry flag.</font>
+ BIT 2,A ; test if a string comparison.
+ JR NZ,<A href="#STRINGS">STRINGS</a> ; forward to STRINGS if so.
+
+; continue with numeric comparisons.
+
+<font color=#FF3333>;;; RRCA ; 2nd RRCA causes eql/neql to set carry.</font>
+<font color=#FF3333>;;; PUSH AF ; save A and carry</font>
+
+ CALL subtract ; routine subtract leaves result on stack.
+ JR <A href="#END_TESTS">END_TESTS</a> ; forward to END-TESTS
+
+; ---
+
+<a name="STRINGS"></a><b>STRINGS</b>
+<font color=#FF3333>;;; RRCA ; 2nd RRCA causes eql/neql to set carry.</font>
+<font color=#FF3333>;;; PUSH AF ; save A and carry.</font>
+
+ CALL <A href="#STK_FETCH">STK_FETCH</a> ; routine STK-FETCH gets 2nd string params
+ PUSH DE ; save start2 *.
+ PUSH BC ; and the length.
+
+ CALL <A href="#STK_FETCH">STK_FETCH</a> ; 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.
+
+<a name="BYTE_COMP"></a><b>BYTE_COMP</b> 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,<A href="#SEC_PLUS">SEC_PLUS</a> ; forward to SEC-PLUS if second not null.
+
+ OR C ; test length of first string.
+
+<a name="SECND_LOW"></a><b>SECND_LOW</b> POP BC ; pop the second length off stack.
+ JR Z,<A href="#BOTH_NULL">BOTH_NULL</a> ; 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.
+ ; <font color=#9900FF>Note.</font> 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 <A href="#STR_TEST">STR_TEST</a> ; forward to leave via STR-TEST
+
+; ---
+; the branch was here with a match
+
+<a name="BOTH_NULL"></a><b>BOTH_NULL</b> POP AF ; restore carry - set for eql/neql
+ JR <A href="#STR_TEST">STR_TEST</a> ; forward to STR-TEST
+
+; ---
+; the branch was here when 2nd string not null and low byte of first is yet
+; to be tested.
+
+
+<a name="SEC_PLUS"></a><b>SEC_PLUS</b> OR C ; test the length of first string.
+ JR Z,<A href="#FRST_LESS">FRST_LESS</a> ; 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,<A href="#FRST_LESS">FRST_LESS</a> ; forward to FRST-LESS if carry set
+
+ JR NZ,<A href="#SECND_LOW">SECND_LOW</a> ; 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 <A href="#BYTE_COMP">BYTE_COMP</a> ; back to BYTE-COMP
+
+; ---
+; the false condition.
+
+<a name="FRST_LESS"></a><b>FRST_LESS</b> POP BC ; discard length
+ POP AF ; pop A
+ AND A ; clear the carry for false result.
+
+; ---
+; exact match and x$>y$ rejoin here
+
+<a name="STR_TEST"></a><b>STR_TEST</b> 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.
+
+<a name="END_TESTS"></a><b>END_TESTS</b> 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 <b><font color=#333388>'STRING CONCATENATION'</font></b> OPERATOR
+; -----------------------------------
+; <font color=#339933>(offset $17: 'strs_add')</font>
+; 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.
+
+<a name="strs_add"></a><b>strs_add</b>
+ CALL <A href="#STK_FETCH">STK_FETCH</a> ; routine STK-FETCH fetches string parameters
+ ; and deletes calculator stack entry.
+ PUSH DE ; save start address.
+ PUSH BC ; and length.
+
+ CALL <A href="#STK_FETCH">STK_FETCH</a> ; 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 <A href="#STK_STO_s">STK_STO_s</a> ; routine STK-STO-$ stores parameters
+ ; of new string updating STKEND.
+
+ POP BC ; length of first
+ POP HL ; address of start
+
+<font color=#FF3333>;;; LD A,B ; test for</font>
+<font color=#FF3333>;;; OR C ; zero length.</font>
+<font color=#FF3333>;;; JR Z,OTHER_STR ; to OTHER-STR if null string</font>
+<font color=#FF3333>;;; LDIR ; copy string to workspace.</font>
+
+ <font color=#3333FF> CALL <A href="#COND_MV">COND_MV</a> ;+ a conditional (NZ) ldir routine. </font>
+
+<a name="OTHER_STR"></a><b>OTHER_STR</b> POP BC ; now second length
+ POP HL ; and start of string
+
+<font color=#FF3333>;;; LD A,B ; test this one</font>
+<font color=#FF3333>;;; OR C ; for zero length</font>
+<font color=#FF3333>;;; JR Z,<A href="#STK_PNTRS">STK_PNTRS</a> ; skip forward to STK-PNTRS if so as complete.</font>
+<font color=#FF3333>;;; LDIR ; else copy the bytes.</font>
+
+ <font color=#3333FF> CALL <A href="#COND_MV">COND_MV</a> ;+ a conditional (NZ) ldir routine. </font>
+
+; Continue into next routine which sets the calculator stack pointers.
+
+; ----------------------------
+; THE <b><font color=#333388>'STACK POINTERS'</font></b> 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.
+
+<a name="STK_PNTRS"></a><b>STK_PNTRS</b> 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 <b><font color=#333388>'CHR$'</font></b> FUNCTION
+; -------------------
+; <font color=#339933>(offset $2B: 'chr$')</font>
+; 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".
+; <font color=#9900FF>Note.</font> the ZX81 does not have an ASCII character set.
+
+<a name="chrS"></a><b>chrS</b> CALL <A href="#FP_TO_A">FP_TO_A</a> ; routine FP-TO-A puts the number in A.
+
+ JR C,<A href="#REPORT_Bd">REPORT_Bd</a> ; forward to REPORT-Bd if overflow
+ JR NZ,<A href="#REPORT_Bd">REPORT_Bd</a> ; forward to REPORT-Bd if negative
+
+<font color=#FF3333>;;; PUSH AF ; save the argument.</font>
+
+ LD BC,$0001 ; one space required.
+ RST 30H ; BC-SPACES makes DE point to start
+
+<font color=#FF3333>;;; POP AF ; restore the number.</font>
+
+ LD (DE),A ; and store in workspace
+
+ <font color=#3333FF> JR str_STK ;+ relative jump to similar sequence in str$.</font>
+
+<font color=#FF3333>;;; CALL <A href="#STK_STO_s">STK_STO_s</a> ; routine STK-STO-$ stacks descriptor.</font>
+<font color=#FF3333>;;; EX DE,HL ; make HL point to result and DE to STKEND.</font>
+<font color=#FF3333>;;; RET ; return.</font>
+
+; ---
+
+<a name="REPORT_Bd"></a><b>REPORT_Bd</b> RST 08H ; ERROR-1
+ DEFB $0A ; Error Report: Integer out of range
+
+; ------------------
+; THE <b><font color=#333388>'VAL'</font></b> FUNCTION
+; ------------------
+; <font color=#339933>(offset $1A: 'val')</font>
+; 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.
+
+<a name="val"></a><b>val</b> <font color=#3333FF> RST 18H ;+ shorter way to fetch CH_ADD.</font>
+
+<font color=#FF3333>;;; LD HL,($4016) ; fetch value of system variable CH_ADD</font>
+ PUSH HL ; and save on the machine stack.
+
+ CALL <A href="#STK_FETCH">STK_FETCH</a> ; 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 <A href="#CLASS_06">CLASS_06</a> ; routine CLASS-06 - SCANNING evaluates string
+ ; expression and checks for integer result.
+
+ CALL <A href="#CHECK_2">CHECK_2</a> ; 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 <A href="#SCANNING">SCANNING</a> ; 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 <A href="#STK_PNTRS">STK_PNTRS</a> ; 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 <b><font color=#333388>'STR$'</font></b> FUNCTION
+; -------------------
+; <font color=#339933>(offset $2A: 'str$')</font>
+; 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.
+
+<a name="strS"></a><b>strS</b> 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 <A href="#PRINT_FP">PRINT_FP</a> ; 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.
+
+<a name="str_STK"></a><b>str_STK</b> 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 <b><font color=#333388>'CODE'</font></b> FUNCTION
+; -------------------
+; <font color=#339933>(offset $19: 'code')</font>
+; 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).
+
+
+<a name="code"></a><b>code</b> CALL <A href="#STK_FETCH">STK_FETCH</a> ; 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,<A href="#STK_CODE">STK_CODE</a> ; skip to STK-CODE with zero if the null string.
+
+ LD A,(DE) ; else fetch the first character.
+
+<a name="STK_CODE"></a><b>STK_CODE</b> JP <A href="#STACK_A">STACK_A</a> ; jump back to STACK-A (with memory check)
+
+; --------------------
+; THE <b><font color=#333388>'LEN'</font></b> SUBROUTINE
+; --------------------
+; <font color=#339933>(offset $1b: 'len')</font>
+; 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
+
+<a name="len"></a><b>len</b> CALL <A href="#STK_FETCH">STK_FETCH</a> ; routine STK-FETCH to fetch and delete the
+ ; string parameters from the calculator stack.
+ ; register BC now holds the length of string.
+
+ JP <A href="#STACK_BC">STACK_BC</a> ; jump back to STACK-BC to save result on the
+ ; calculator stack (with memory check).
+
+; -------------------------------------
+; THE <b><font color=#333388>'DECREASE THE COUNTER'</font></b> SUBROUTINE
+; -------------------------------------
+; <font color=#339933>(offset $31: 'dec-jr-nz')</font>
+; The calculator has an instruction that decrements a single-byte
+; pseudo-register and makes consequential relative jumps just like
+; the Z80's DJNZ instruction.
+
+<a name="dec_jr_nz"></a><b>dec_jr_nz</b> 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,<A href="#JUMP_2">JUMP_2</a> ; to JUMP-2 if not zero
+
+ INC HL ; step past the jump length.
+ EXX ; switch in the main set.
+ RET ; return.
+
+; <font color=#9900FF>Note.</font> 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 <b><font color=#333388>'JUMP'</font></b> SUBROUTINE
+; ---------------------
+; <font color=#339933>(Offset $2F; 'jump')</font>
+; 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.
+
+<a name="JUMP"></a><b>JUMP</b> EXX ;switch in pointer set
+
+<a name="JUMP_2"></a><b>JUMP_2</b> LD E,(HL) ; the jump byte 0-127 forward, 128-255 back.
+
+; <font color=#9900FF>Note.</font> Elegance from the ZX Spectrum.
+
+ <font color=#3333FF> LD A,E ;+</font>
+ <font color=#3333FF> RLA ;+</font>
+ <font color=#3333FF> SBC A,A ;+</font>
+
+; The original ZX81 code.
+
+<font color=#FF3333>;;; XOR A ; clear accumulator.</font>
+<font color=#FF3333>;;; BIT 7,E ; test if negative jump</font>
+<font color=#FF3333>;;; JR Z,<A href="#JUMP_3">JUMP_3</a> ; skip, if positive, to JUMP-3.</font>
+<font color=#FF3333>;;; CPL ; else change to $FF.</font>
+
+<a name="JUMP_3"></a><b>JUMP_3</b> LD D,A ; transfer to high byte.
+ ADD HL,DE ; advance calculator pointer forward or back.
+
+ EXX ; switch out pointer set.
+ RET ; return.
+
+; -----------------------------
+; THE <b><font color=#333388>'JUMP ON TRUE'</font></b> SUBROUTINE
+; -----------------------------
+; <font color=#339933>(Offset $00; 'jump-true')</font>
+; 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.
+
+<a name="jump_true"></a><b>jump_true</b> LD A,(DE) ; collect exponent byte
+
+ AND A ; is result 0 or 1 ?
+ JR NZ,<A href="#JUMP">JUMP</a> ; 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 <b><font color=#333388>'MODULUS'</font></b> SUBROUTINE
+; ------------------------
+; ( Offset $2E: 'n-mod-m' )
+; <font color=#CC00FF>( i1, i2 -- i3, i4 )</font>
+; 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.
+
+<a name="n_mod_m"></a><b>n_mod_m</b> 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 <b><font color=#333388>'INTEGER'</font></b> FUNCTION
+; ----------------------
+; <font color=#339933>(offset $24: 'int')</font>
+; 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.
+
+<a name="int"></a><b>int</b> 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.
+
+
+<a name="X_NEG"></a><b>X_NEG</b> 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.
+
+<a name="EXIT"></a><b>EXIT</b> DEFB $34 ;;end-calc -4.
+
+ RET ; return.
+
+
+; --------------------------
+; THE <b><font color=#333388>'EXPONENTIAL'</font></b> FUNCTION
+; --------------------------
+; <font color=#339933>(Offset $23: 'exp')</font>
+; 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.
+
+<a name="exp"></a><b>exp</b> 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 <A href="#FP_TO_A">FP_TO_A</a> ; routine FP-TO-A
+ JR NZ,<A href="#N_NEGTV">N_NEGTV</a> ; to N-NEGTV
+
+ JR C,<A href="#REPORT_6b">REPORT_6b</a> ; to REPORT-6b
+
+ ADD A,(HL) ;
+ JR NC,<A href="#RESULT_OK">RESULT_OK</a> ; to RESULT-OK
+
+
+<a name="REPORT_6b"></a><b>REPORT_6b</b> RST 08H ; ERROR-1
+ DEFB $05 ; Error Report: Number too big
+
+<a name="N_NEGTV"></a><b>N_NEGTV</b> JR C,<A href="#RSLT_ZERO">RSLT_ZERO</a> ; to RSLT-ZERO
+
+ SUB (HL) ;
+ JR NC,<A href="#RSLT_ZERO">RSLT_ZERO</a> ; to RSLT-ZERO
+
+ NEG ; Negate
+
+<a name="RESULT_OK"></a><b>RESULT_OK</b> LD (HL),A ;
+ RET ; return.
+
+
+<a name="RSLT_ZERO"></a><b>RSLT_ZERO</b> RST 28H ;; FP-CALC
+ DEFB $02 ;;delete
+ DEFB $A0 ;;stk-zero
+ DEFB $34 ;;end-calc
+
+ RET ; return.
+
+
+; --------------------------------
+; THE <b><font color=#333388>'NATURAL LOGARITHM'</font></b> FUNCTION
+; --------------------------------
+; <font color=#339933>(offset $22: 'ln')</font>
+; 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.
+
+
+<a name="ln"></a><b>ln</b> 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.
+
+
+<a name="REPORT_Ab"></a><b>REPORT_Ab</b> RST 08H ; ERROR-1
+ DEFB $09 ; Error Report: Invalid argument
+
+<a name="VALID"></a><b>VALID</b>
+<font color=#FF3333>;;; DEFB $A0 ;;stk-zero </font>
+<font color=#FF3333>;;; DEFB $02 ;;delete</font>
+ 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 <A href="#STACK_A">STACK_A</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
+
+<a name="GRE_8"></a><b>GRE_8</b> 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 <b><font color=#333388>'SQUARE ROOT'</font></b> FUNCTION
+; ------------------------------
+; <font color=#339933>(Offset $25: 'sqr')</font>
+; "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.
+
+<a name="sqr"></a><b>sqr</b> 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,<A href="#REPORT_Ab">REPORT_Ab</a> ; 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,<A href="#ASIS">ASIS</a> ; forward with say .25 -> .5
+ JP P,<A href="#ASIS">ASIS</a> ; leave increment if value > .5
+ DEC A ; restore to shift only.
+<a name="ASIS"></a><b>ASIS</b> XOR $80 ; restore sign.
+ LD (HL),A ; and put back 'halved' exponent.
+
+; Now re-enter the calculator.
+
+ RST 28H ;; FP-CALC x
+
+<a name="SLOOP"></a><b>SLOOP</b> 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 <b><font color=#333388>'TRIGONOMETRIC'</font></b> 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 <b><font color=#333388>'REDUCE ARGUMENT'</font></b> SUBROUTINE
+; --------------------------------
+; <font color=#339933>(offset $35: 'get-argt')</font>
+;
+; 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
+; |
+;<font color=#339933> sin+ |\ | /| sin+</font>
+;<font color=#339933> cos- | \ | / | cos+</font>
+;<font color=#339933> tan- | \ | / | tan+</font>
+;<font color=#339933> | \|/) | </font>
+;<font color=#339933> 180 deg. (pi) 0 -|----+----|-- 0 (0) 0 degrees</font>
+;<font color=#339933> | /|\ |</font>
+;<font color=#339933> sin- | / | \ | sin-</font>
+;<font color=#339933> cos- | / | \ | cos+</font>
+;<font color=#339933> tan+ |/ | \| tan-</font>
+;<font color=#339933> |</font>
+;<font color=#339933> III -1 IV</font>
+;<font color=#339933> (3pi/2)</font>
+;<font color=#339933></font>
+; 270 deg.
+
+
+<a name="get_argt"></a><b>get_argt</b> 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.
+
+<a name="ZPLUS"></a><b>ZPLUS</b> 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
+
+
+<a name="YNEG"></a><b>YNEG</b> DEFB $34 ;;end-calc quadrants II and III correct.
+
+ RET ; return.
+
+
+; ---------------------
+; THE <b><font color=#333388>'COSINE'</font></b> FUNCTION
+; ---------------------
+; <font color=#339933>(offset $1D: 'cos')</font>
+; Cosines are calculated as the sine of the opposite angle rectifying the
+; sign depending on the quadrant rules.
+;
+;
+;<font color=#339933> /|</font>
+;<font color=#339933> h /y|</font>
+;<font color=#339933> / |o</font>
+;<font color=#339933> /x |</font>
+;<font color=#339933> /----| </font>
+;<font color=#339933> a</font>
+;<font color=#339933></font>
+; 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.
+
+<a name="cos"></a><b>cos</b> 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 <b><font color=#333388>'SINE'</font></b> FUNCTION
+; -------------------
+; <font color=#339933>(offset $1C: 'sin')</font>
+; 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.
+;
+;
+;<font color=#339933> /|</font>
+;<font color=#339933> 1 / |</font>
+;<font color=#339933> / |x</font>
+;<font color=#339933> /a |</font>
+;<font color=#339933> /----| </font>
+;<font color=#339933> y</font>
+;<font color=#339933></font>
+; 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.
+
+<a name="sin"></a><b>sin</b> RST 28H ;; FP-CALC angle in radians
+ DEFB $35 ;;get-argt reduce - sign now correct.
+
+<a name="C_ENT"></a><b>C_ENT</b> 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 <b><font color=#333388>'TANGENT'</font></b> FUNCTION
+; ----------------------
+; <font color=#339933>(offset $1E: 'tan')</font>
+;
+; Evaluates tangent x as sin(x) / cos(x).
+;
+;
+;<font color=#339933> /|</font>
+;<font color=#339933> h / |</font>
+;<font color=#339933> / |o</font>
+;<font color=#339933> /x |</font>
+;<font color=#339933> /----| </font>
+;<font color=#339933> a</font>
+;<font color=#339933></font>
+; 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.
+
+<a name="tan"></a><b>tan</b> 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 <b><font color=#333388>'ARCTAN'</font></b> FUNCTION
+; ---------------------
+; <font color=#339933>(Offset $21: 'atn')</font>
+; 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.
+
+<a name="atn"></a><b>atn</b> LD A,(HL) ; fetch exponent
+ CP $81 ; compare to that for 'one'
+ JR C,<A href="#SMALL">SMALL</a> ; 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
+
+; ---
+
+<a name="SMALL"></a><b>SMALL</b> RST 28H ;; FP-CALC
+ DEFB $A0 ;;stk-zero
+
+<a name="CASES"></a><b>CASES</b> 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 <b><font color=#333388>'ARCSIN'</font></b> FUNCTION
+; ---------------------
+; <font color=#339933>(Offset $1F: 'asn')</font>
+; 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))
+;
+;
+;<font color=#339933> /|</font>
+;<font color=#339933> / |</font>
+;<font color=#339933> 1/ |x</font>
+;<font color=#339933> /a |</font>
+;<font color=#339933> /----| </font>
+;<font color=#339933> y</font>
+;<font color=#339933></font>
+; 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
+;
+;
+;<font color=#339933> . /|</font>
+;<font color=#339933> . c/ |</font>
+;<font color=#339933> . /1 |x</font>
+;<font color=#339933> . c b /a |</font>
+;<font color=#339933> ---------/----| </font>
+;<font color=#339933> 1 y</font>
+;<font color=#339933></font>
+; 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.
+
+<a name="asn"></a><b>asn</b> 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 <b><font color=#333388>'ARCCOS'</font></b> FUNCTION
+; ------------------------
+; <font color=#339933>(Offset $20: 'acs')</font>
+; 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).
+;
+;
+;<font color=#339933> /|</font>
+;<font color=#339933> 1 /b|</font>
+;<font color=#339933> / |x</font>
+;<font color=#339933> /a |</font>
+;<font color=#339933> /----| </font>
+;<font color=#339933> y</font>
+;<font color=#339933></font>
+
+<a name="acs"></a><b>acs</b> 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 <b><font color=#333388>'SQUARE ROOT'</font></b> FUNCTION
+; --------------------------
+; <font color=#339933>(Offset $25: 'sqr')</font>
+; 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'.
+;
+<font color=#FF3333>;;; L1DDB: RST 28H ;; FP-CALC x.</font>
+<font color=#FF3333>;;; DEFB $2D ;;duplicate x, x.</font>
+<font color=#FF3333>;;; DEFB $2C ;;not x, 1/0</font>
+<font color=#FF3333>;;; DEFB $00 ;;jump-true x, (1/0).</font>
+<font color=#FF3333>;;; DEFB $1E ;;to L1DFD, LAST exit if argument zero</font>
+<font color=#FF3333>;;; ;; with zero result.</font>
+;;;
+<font color=#FF3333>;;; else continue to calculate as x ** .5</font>
+;;;
+<font color=#FF3333>;;; DEFB $A2 ;;stk-half x, .5.</font>
+<font color=#FF3333>;;; DEFB $34 ;;end-calc x, .5.</font>
+
+
+; ------------------------
+; THE <b><font color=#333388>'TO POWER'</font></b> OPERATION
+; ------------------------
+; <font color=#339933>(Offset $06: 'to-power')</font>
+; 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.
+
+<a name="to_power"></a><b>to_power</b> 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.
+
+<a name="XISO"></a><b>XISO</b> 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.
+
+<font color=#FF3333>;;; DEFB $A1 ;;stk-one 0, 1.</font>
+<font color=#FF3333>;;; DEFB $01 ;;exchange 1, 0.</font>
+<font color=#FF3333>;;; DEFB $05 ;;division 1/0 >> error </font>
+
+ <font color=#3333FF> DEFB $34 ;+ end-calc</font>
+<a name="REPORT_6c"></a><b>REPORT_6c</b> <font color=#3333FF>RST 08H ;+ ERROR-1</font>
+ <font color=#3333FF> DEFB $05 ;+ Error Report: Number too big</font>
+
+; ---
+
+<a name="ONE"></a><b>ONE</b> DEFB $02 ;;delete .
+ DEFB $A1 ;;stk-one 1.
+
+<a name="LAST"></a><b>LAST</b> DEFB $34 ;;end-calc last value 1 or 0.
+
+ RET ; return.
+
+; ---------------------
+; THE <b><font color=#333388>'SPARE LOCATIONS'</font></b>
+; ---------------------
+
+<a name="L1DFE:"></a><b>L1DFE:</b>
+
+ DEFB $FF, $FF ; Two spare bytes.
+
+
+<a name="ORG"></a><b>ORG</b> $1E00
+
+; ------------------------
+; THE <b><font color=#333388>'ZX81 CHARACTER SET'</font></b>
+; ------------------------
+
+
+; $00 - <b>Character: ' ' </b>CHR$(0)
+
+<a name="char_set"></a><b>char_set</b> DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+
+; $01 - <b>Character: mosaic </b>CHR$(1)
+
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+
+
+; $02 - <b>Character: mosaic </b>CHR$(2)
+
+ DEFB %0000<B>1</B><B>1</B><B>1</B><B>1</B>
+ DEFB %0000<B>1</B><B>1</B><B>1</B><B>1</B>
+ DEFB %0000<B>1</B><B>1</B><B>1</B><B>1</B>
+ DEFB %0000<B>1</B><B>1</B><B>1</B><B>1</B>
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+
+
+; $03 - <b>Character: mosaic </b>CHR$(3)
+
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+
+; $04 - <b>Character: mosaic </b>CHR$(4)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+
+; $05 - <b>Character: mosaic </b>CHR$(1)
+
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+
+; $06 - <b>Character: mosaic </b>CHR$(1)
+
+ DEFB %0000<B>1</B><B>1</B><B>1</B><B>1</B>
+ DEFB %0000<B>1</B><B>1</B><B>1</B><B>1</B>
+ DEFB %0000<B>1</B><B>1</B><B>1</B><B>1</B>
+ DEFB %0000<B>1</B><B>1</B><B>1</B><B>1</B>
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+
+; $07 - <b>Character: mosaic </b>CHR$(1)
+
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+
+; $08 - <b>Character: mosaic </b>CHR$(1)
+
+ DEFB %<B>1</B>0<B>1</B>0<B>1</B>0<B>1</B>0
+ DEFB %0<B>1</B>0<B>1</B>0<B>1</B>0<B>1</B>
+ DEFB %<B>1</B>0<B>1</B>0<B>1</B>0<B>1</B>0
+ DEFB %0<B>1</B>0<B>1</B>0<B>1</B>0<B>1</B>
+ DEFB %<B>1</B>0<B>1</B>0<B>1</B>0<B>1</B>0
+ DEFB %0<B>1</B>0<B>1</B>0<B>1</B>0<B>1</B>
+ DEFB %<B>1</B>0<B>1</B>0<B>1</B>0<B>1</B>0
+ DEFB %0<B>1</B>0<B>1</B>0<B>1</B>0<B>1</B>
+
+; $09 - <b>Character: mosaic </b>CHR$(1)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %<B>1</B>0<B>1</B>0<B>1</B>0<B>1</B>0
+ DEFB %0<B>1</B>0<B>1</B>0<B>1</B>0<B>1</B>
+ DEFB %<B>1</B>0<B>1</B>0<B>1</B>0<B>1</B>0
+ DEFB %0<B>1</B>0<B>1</B>0<B>1</B>0<B>1</B>
+
+; $0A - <b>Character: mosaic </b>CHR$(10)
+
+ DEFB %<B>1</B>0<B>1</B>0<B>1</B>0<B>1</B>0
+ DEFB %0<B>1</B>0<B>1</B>0<B>1</B>0<B>1</B>
+ DEFB %<B>1</B>0<B>1</B>0<B>1</B>0<B>1</B>0
+ DEFB %0<B>1</B>0<B>1</B>0<B>1</B>0<B>1</B>
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+
+; $0B - <b>Character: '"' </b>CHR$(11)
+
+ DEFB %00000000
+ DEFB %00<B>1</B>00<B>1</B>00
+ DEFB %00<B>1</B>00<B>1</B>00
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+
+; $0B - <b>Character: 'œ' </b>CHR$(12)
+
+ DEFB %00000000
+ DEFB %000<B>1</B><B>1</B><B>1</B>00
+ DEFB %00<B>1</B>000<B>1</B>0
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B>000
+ DEFB %00<B>1</B>00000
+ DEFB %00<B>1</B>00000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %00000000
+
+; $0B - <b>Character: '$' </b>CHR$(13)
+
+ DEFB %00000000
+ DEFB %0000<B>1</B>000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %00<B>1</B>0<B>1</B>000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %0000<B>1</B>0<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %0000<B>1</B>000
+
+; $0B - <b>Character: ':' </b>CHR$(14)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %000<B>1</B>0000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %000<B>1</B>0000
+ DEFB %00000000
+
+; $0B - <b>Character: '?' </b>CHR$(15)
+
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00000<B>1</B>00
+ DEFB %0000<B>1</B>000
+ DEFB %00000000
+ DEFB %0000<B>1</B>000
+ DEFB %00000000
+
+; $10 - <b>Character: '(' </b>CHR$(16)
+
+ DEFB %00000000
+ DEFB %00000<B>1</B>00
+ DEFB %0000<B>1</B>000
+ DEFB %0000<B>1</B>000
+ DEFB %0000<B>1</B>000
+ DEFB %0000<B>1</B>000
+ DEFB %00000<B>1</B>00
+ DEFB %00000000
+
+; $11 - <b>Character: ')' </b>CHR$(17)
+
+ DEFB %00000000
+ DEFB %00<B>1</B>00000
+ DEFB %000<B>1</B>0000
+ DEFB %000<B>1</B>0000
+ DEFB %000<B>1</B>0000
+ DEFB %000<B>1</B>0000
+ DEFB %00<B>1</B>00000
+ DEFB %00000000
+
+; $12 - <b>Character: '>' </b>CHR$(18)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %000<B>1</B>0000
+ DEFB %0000<B>1</B>000
+ DEFB %00000<B>1</B>00
+ DEFB %0000<B>1</B>000
+ DEFB %000<B>1</B>0000
+ DEFB %00000000
+
+; $13 - <b>Character: '<' </b>CHR$(19)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000<B>1</B>00
+ DEFB %0000<B>1</B>000
+ DEFB %000<B>1</B>0000
+ DEFB %0000<B>1</B>000
+ DEFB %00000<B>1</B>00
+ DEFB %00000000
+
+; $14 - <b>Character: '=' </b>CHR$(20)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %00000000
+ DEFB %00000000
+
+; $15 - <b>Character: '+' </b>CHR$(21)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %0000<B>1</B>000
+ DEFB %0000<B>1</B>000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %0000<B>1</B>000
+ DEFB %0000<B>1</B>000
+ DEFB %00000000
+
+; $16 - <b>Character: '-' </b>CHR$(22)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+
+; $17 - <b>Character: '*' </b>CHR$(23)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %000<B>1</B>0<B>1</B>00
+ DEFB %0000<B>1</B>000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %0000<B>1</B>000
+ DEFB %000<B>1</B>0<B>1</B>00
+ DEFB %00000000
+
+; $18 - <b>Character: '/' </b>CHR$(24)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %000000<B>1</B>0
+ DEFB %00000<B>1</B>00
+ DEFB %0000<B>1</B>000
+ DEFB %000<B>1</B>0000
+ DEFB %00<B>1</B>00000
+ DEFB %00000000
+
+; $19 - <b>Character: ';' </b>CHR$(25)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %000<B>1</B>0000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %000<B>1</B>0000
+ DEFB %000<B>1</B>0000
+ DEFB %00<B>1</B>00000
+
+; $1A - <b>Character: ',' </b>CHR$(26)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %0000<B>1</B>000
+ DEFB %0000<B>1</B>000
+ DEFB %000<B>1</B>0000
+
+; $1B - <b>Character: '"' </b>CHR$(27)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %000<B>1</B><B>1</B>000
+ DEFB %000<B>1</B><B>1</B>000
+ DEFB %00000000
+
+; $1C - <b>Character: '0' </b>CHR$(28)
+
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>000<B>1</B><B>1</B>0
+ DEFB %0<B>1</B>00<B>1</B>0<B>1</B>0
+ DEFB %0<B>1</B>0<B>1</B>00<B>1</B>0
+ DEFB %0<B>1</B><B>1</B>000<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %00000000
+
+; $1D - <b>Character: '1' </b>CHR$(29)
+
+ DEFB %00000000
+ DEFB %000<B>1</B><B>1</B>000
+ DEFB %00<B>1</B>0<B>1</B>000
+ DEFB %0000<B>1</B>000
+ DEFB %0000<B>1</B>000
+ DEFB %0000<B>1</B>000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %00000000
+
+; $1E - <b>Character: '2' </b>CHR$(30)
+
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %000000<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %00000000
+
+; $1F - <b>Character: '3' </b>CHR$(31)
+
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0000<B>1</B><B>1</B>00
+ DEFB %000000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %00000000
+
+; $20 - <b>Character: '4' </b>CHR$(32)
+
+ DEFB %00000000
+ DEFB %0000<B>1</B>000
+ DEFB %000<B>1</B><B>1</B>000
+ DEFB %00<B>1</B>0<B>1</B>000
+ DEFB %0<B>1</B>00<B>1</B>000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %0000<B>1</B>000
+ DEFB %00000000
+
+; $21 - <b>Character: '5' </b>CHR$(33)
+
+ DEFB %00000000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %000000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %00000000
+
+; $22 - <b>Character: '6' </b>CHR$(34)
+
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %00000000
+
+; $23 - <b>Character: '7' </b>CHR$(35)
+
+ DEFB %00000000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %000000<B>1</B>0
+ DEFB %00000<B>1</B>00
+ DEFB %0000<B>1</B>000
+ DEFB %000<B>1</B>0000
+ DEFB %000<B>1</B>0000
+ DEFB %00000000
+
+; $24 - <b>Character: '8' </b>CHR$(36)
+
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %00000000
+
+; $25 - <b>Character: '9' </b>CHR$(37)
+
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %000000<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %00000000
+
+; $26 - <b>Character: 'A' </b>CHR$(38)
+
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00000000
+
+; $27 - <b>Character: 'B' </b>CHR$(39)
+
+ DEFB %00000000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %00000000
+
+; $28 - <b>Character: 'C' </b>CHR$(40)
+
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %00000000
+
+; $29 - <b>Character: 'D' </b>CHR$(41)
+
+ DEFB %00000000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B>000
+ DEFB %0<B>1</B>000<B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>000<B>1</B>00
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B>000
+ DEFB %00000000
+
+; $2A - <b>Character: 'E' </b>CHR$(42)
+
+ DEFB %00000000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %00000000
+
+; $2B - <b>Character: 'F' </b>CHR$(43)
+
+ DEFB %00000000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B>000000
+ DEFB %00000000
+
+; $2C - <b>Character: 'G' </b>CHR$(44)
+
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B>00<B>1</B><B>1</B><B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %00000000
+
+; $2D - <b>Character: 'H' </b>CHR$(45)
+
+ DEFB %00000000
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00000000
+
+; $2E - <b>Character: 'I' </b>CHR$(46)
+
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %0000<B>1</B>000
+ DEFB %0000<B>1</B>000
+ DEFB %0000<B>1</B>000
+ DEFB %0000<B>1</B>000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %00000000
+
+; $2F - <b>Character: 'J' </b>CHR$(47)
+
+ DEFB %00000000
+ DEFB %000000<B>1</B>0
+ DEFB %000000<B>1</B>0
+ DEFB %000000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %00000000
+
+; $30 - <b>Character: 'K' </b>CHR$(48)
+
+ DEFB %00000000
+ DEFB %0<B>1</B>000<B>1</B>00
+ DEFB %0<B>1</B>00<B>1</B>000
+ DEFB %0<B>1</B><B>1</B><B>1</B>0000
+ DEFB %0<B>1</B>00<B>1</B>000
+ DEFB %0<B>1</B>000<B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00000000
+
+; $31 - <b>Character: 'L' </b>CHR$(49)
+
+ DEFB %00000000
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %00000000
+
+; $32 - <b>Character: 'M' </b>CHR$(50)
+
+ DEFB %00000000
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B><B>1</B>00<B>1</B><B>1</B>0
+ DEFB %0<B>1</B>0<B>1</B><B>1</B>0<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00000000
+
+; $33 - <b>Character: 'N' </b>CHR$(51)
+
+ DEFB %00000000
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B><B>1</B>000<B>1</B>0
+ DEFB %0<B>1</B>0<B>1</B>00<B>1</B>0
+ DEFB %0<B>1</B>00<B>1</B>0<B>1</B>0
+ DEFB %0<B>1</B>000<B>1</B><B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00000000
+
+; $34 - <b>Character: 'O' </b>CHR$(52)
+
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %00000000
+
+; $35 - <b>Character: 'P' </b>CHR$(53)
+
+ DEFB %00000000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B>000000
+ DEFB %00000000
+
+; $36 - <b>Character: 'Q' </b>CHR$(54)
+
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0<B>1</B>00<B>1</B>0
+ DEFB %0<B>1</B>00<B>1</B>0<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %00000000
+
+; $37 - <b>Character: 'R' </b>CHR$(55)
+
+ DEFB %00000000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>000<B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00000000
+
+; $38 - <b>Character: 'S' </b>CHR$(56)
+
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %000000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %00000000
+
+; $39 - <b>Character: 'T' </b>CHR$(57)
+
+ DEFB %00000000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %000<B>1</B>0000
+ DEFB %000<B>1</B>0000
+ DEFB %000<B>1</B>0000
+ DEFB %000<B>1</B>0000
+ DEFB %000<B>1</B>0000
+ DEFB %00000000
+
+; $3A - <b>Character: 'U' </b>CHR$(58)
+
+ DEFB %00000000
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %00000000
+
+; $3B - <b>Character: 'V' </b>CHR$(59)
+
+ DEFB %00000000
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00<B>1</B>00<B>1</B>00
+ DEFB %000<B>1</B><B>1</B>000
+ DEFB %00000000
+
+; $3C - <b>Character: 'W' </b>CHR$(60)
+
+ DEFB %00000000
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0<B>1</B><B>1</B>0<B>1</B>0
+ DEFB %00<B>1</B>00<B>1</B>00
+ DEFB %00000000
+
+; $3D - <b>Character: 'X' </b>CHR$(61)
+
+ DEFB %00000000
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00<B>1</B>00<B>1</B>00
+ DEFB %000<B>1</B><B>1</B>000
+ DEFB %000<B>1</B><B>1</B>000
+ DEFB %00<B>1</B>00<B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00000000
+
+; $3E - <b>Character: 'Y' </b>CHR$(62)
+
+ DEFB %00000000
+ DEFB %<B>1</B>00000<B>1</B>0
+ DEFB %0<B>1</B>000<B>1</B>00
+ DEFB %00<B>1</B>0<B>1</B>000
+ DEFB %000<B>1</B>0000
+ DEFB %000<B>1</B>0000
+ DEFB %000<B>1</B>0000
+ DEFB %00000000
+
+; $3F - <b>Character: 'Z' </b>CHR$(63)
+
+ DEFB %00000000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %00000<B>1</B>00
+ DEFB %0000<B>1</B>000
+ DEFB %000<B>1</B>0000
+ DEFB %00<B>1</B>00000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %00000000
+
+.END ;TASM assembler instruction.
+
+
+
+</PRE>
+</BODY>
+</HTML>
diff --git a/mirror/tablix.org/zx81.html b/mirror/tablix.org/zx81.html
@@ -0,0 +1,10568 @@
+<HTML>
+<HEAD>
+<TITLE>
+Assembly Listing of the Operating System of the Sinclair ZX81.
+</TITLE>
+</HEAD>
+<BODY>
+<font face = "Courier"> </font>
+<PRE>
+; ===========================================================
+; An Assembly Listing of the Operating System of the ZX81 ROM
+; ===========================================================
+; -------------------------
+; Last updated: 13-DEC-2004
+; -------------------------
+;
+; Work in progress.
+; This file will cross-assemble an original version of the "Improved"
+; ZX81 ROM. The file can be modified to change the behaviour of the ROM
+; when used in emulators although there is no spare space available.
+;
+; The documentation is incomplete and if you can find a copy
+; of "The Complete Spectrum ROM Disassembly" then many routines
+; such as POINTERS and most of the mathematical routines are
+; similar and often identical.
+;
+; I've used the labels from the above book in this file and also
+; some from the more elusive Complete ZX81 ROM Disassembly
+; by the same publishers, Melbourne House.
+
+
+#define DEFB .BYTE ; TASM cross-assembler definitions
+#define DEFW .WORD
+#define EQU .EQU
+
+
+;*****************************************
+;** Part 1. RESTART ROUTINES AND TABLES **
+;*****************************************
+
+; -----------
+; THE <b><font color=#333388>'START'</font></b>
+; -----------
+; All Z80 chips start at location zero.
+; At start-up the Interrupt Mode is 0, ZX computers use Interrupt Mode 1.
+; Interrupts are disabled .
+
+<a name="L0000"></a>;; <b>START</b>
+L0000: OUT ($FD),A ; Turn off the NMI generator if this ROM is
+ ; running in ZX81 hardware. This does nothing
+ ; if this ROM is running within an upgraded
+ ; ZX80.
+ LD BC,$7FFF ; Set BC to the top of possible RAM.
+ ; The higher unpopulated addresses are used for
+ ; video generation.
+ JP <A href="#L03CB">L03CB</a> ; Jump forward to RAM-CHECK.
+
+; -------------------
+; THE <b><font color=#333388>'ERROR'</font></b> RESTART
+; -------------------
+; The error restart deals immediately with an error. ZX computers execute the
+; same code in runtime as when checking syntax. If the error occurred while
+; running a program then a brief report is produced. If the error occurred
+; while entering a BASIC line or in input etc., then the error marker indicates
+; the exact point at which the error lies.
+
+<a name="L0008"></a>;; <b>ERROR-1</b>
+L0008: LD HL,($4016) ; fetch character address from CH_ADD.
+ LD ($4018),HL ; and set the error pointer X_PTR.
+ JR <A href="#L0056">L0056</a> ; forward to continue at ERROR-2.
+
+; -------------------------------
+; THE <b><font color=#333388>'PRINT A CHARACTER'</font></b> 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.
+
+<a name="L0010"></a>;; <b>PRINT-A</b>
+L0010: AND A ; test for zero - space.
+ JP NZ,<A href="#L07F1">L07F1</a> ; jump forward if not to PRINT-CH.
+
+ JP <A href="#L07F5">L07F5</a> ; jump forward to PRINT-SP.
+
+; ---
+
+ DEFB $FF ; unused location.
+
+; ---------------------------------
+; THE <b><font color=#333388>'COLLECT A CHARACTER'</font></b> RESTART
+; ---------------------------------
+; The character addressed by the system variable CH_ADD is fetched and if it
+; is a non-space, non-cursor character it is returned else CH_ADD is
+; incremented and the new addressed character tested until it is not a space.
+
+<a name="L0018"></a>;; <b>GET-CHAR</b>
+L0018: LD HL,($4016) ; set HL to character address CH_ADD.
+ LD A,(HL) ; fetch addressed character to A.
+
+<a name="L001C"></a>;; <b>TEST-SP</b>
+L001C: AND A ; test for space.
+ RET NZ ; return if not a space
+
+ NOP ; else trickle through
+ NOP ; to the next routine.
+
+; ------------------------------------
+; THE <b><font color=#333388>'COLLECT NEXT CHARACTER'</font></b> RESTART
+; ------------------------------------
+; The character address in incremented and the new addressed character is
+; returned if not a space, or cursor, else the process is repeated.
+
+<a name="L0020"></a>;; <b>NEXT-CHAR</b>
+L0020: CALL <A href="#L0049">L0049</a> ; routine CH-ADD+1 gets next immediate
+ ; character.
+ JR <A href="#L001C">L001C</a> ; back to TEST-SP.
+
+; ---
+
+ DEFB $FF, $FF, $FF ; unused locations.
+
+; ---------------------------------------
+; THE <b><font color=#333388>'FLOATING POINT CALCULATOR'</font></b> RESTART
+; ---------------------------------------
+; this restart jumps to the recursive floating-point calculator.
+; the ZX81's internal, FORTH-like, stack-based language.
+;
+; In the five remaining bytes there is, appropriately, enough room for the
+; end-calc literal - the instruction which exits the calculator.
+
+<a name="L0028"></a>;; <b>FP-CALC</b>
+L0028: JP <A href="#L199D">L199D</a> ; jump immediately to the CALCULATE routine.
+
+; ---
+
+<a name="L002B"></a>;; <b>end-calc</b>
+L002B: 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.
+
+
+; -----------------------------
+; THE <b><font color=#333388>'MAKE BC SPACES'</font></b> RESTART
+; -----------------------------
+; This restart is used eight times to create, in workspace, the number of
+; spaces passed in the BC register.
+
+<a name="L0030"></a>;; <b>BC-SPACES</b>
+L0030: PUSH BC ; push number of spaces on stack.
+ LD HL,($4014) ; fetch edit line location from E_LINE.
+ PUSH HL ; save this value on stack.
+ JP <A href="#L1488">L1488</a> ; jump forward to continue at RESERVE.
+
+; -----------------------
+; THE <b><font color=#333388>'INTERRUPT'</font></b> RESTART
+; -----------------------
+; The Mode 1 Interrupt routine is concerned solely with generating the central
+; television picture.
+; On the ZX81 interrupts are enabled only during the interrupt routine,
+; although the interrupt
+; This Interrupt Service Routine automatically disables interrupts at the
+; outset and the last interrupt in a cascade exits before the interrupts are
+; enabled.
+; There is no DI instruction in the ZX81 ROM.
+; An maskable interrupt is triggered when bit 6 of the Z80's Refresh register
+; changes from set to reset.
+; The Z80 will always be executing a HALT (NEWLINE) when the interrupt occurs.
+; A HALT instruction repeatedly executes NOPS but the seven lower bits
+; of the Refresh register are incremented each time as they are when any
+; simple instruction is executed. (The lower 7 bits are incremented twice for
+; a prefixed instruction)
+; This is controlled by the Sinclair Computer Logic Chip - manufactured from
+; a Ferranti Uncommitted Logic Array.
+;
+; When a Mode 1 Interrupt occurs the Program Counter, which is the address in
+; the upper echo display following the NEWLINE/HALT instruction, goes on the
+; machine stack. 193 interrupts are required to generate the last part of
+; the 56th border line and then the 192 lines of the central TV picture and,
+; although each interrupt interrupts the previous one, there are no stack
+; problems as the 'return address' is discarded each time.
+;
+; The scan line counter in C counts down from 8 to 1 within the generation of
+; each text line. For the first interrupt in a cascade the initial value of
+; C is set to 1 for the last border line.
+; Timing is of the utmost importance as the RH border, horizontal retrace
+; and LH border are mostly generated in the 58 clock cycles this routine
+; takes .
+
+<a name="L0038"></a>;; <b>INTERRUPT</b>
+L0038: DEC C ; (4) decrement C - the scan line counter.
+ JP NZ,<A href="#L0045">L0045</a> ; (10/10) JUMP forward if not zero to SCAN-LINE
+
+ POP HL ; (10) point to start of next row in display
+ ; file.
+
+ DEC B ; (4) decrement the row counter. (4)
+ RET Z ; (11/5) return when picture complete to L028B
+ ; with interrupts disabled.
+
+ SET 3,C ; (8) Load the scan line counter with eight.
+ ; <font color=#9900FF>Note.</font> LD C,$08 is 7 clock cycles which
+ ; is way too fast.
+
+; ->
+
+<a name="L0041"></a>;; <b>WAIT-INT</b>
+L0041: LD R,A ; (9) Load R with initial rising value $DD.
+
+ EI ; (4) Enable Interrupts. [ R is now $DE ].
+
+ JP (HL) ; (4) jump to the echo display file in upper
+ ; memory and execute characters $00 - $3F
+ ; as NOP instructions. The video hardware
+ ; is able to read these characters and,
+ ; with the I register is able to convert
+ ; the character bitmaps in this ROM into a
+ ; line of bytes. Eventually the NEWLINE/HALT
+ ; will be encountered before R reaches $FF.
+ ; It is however the transition from $FF to
+ ; $80 that triggers the next interrupt.
+ ; [ The Refresh register is now $DF ]
+
+; ---
+
+<a name="L0045"></a>;; <b>SCAN-LINE</b>
+L0045: POP DE ; (10) discard the address after NEWLINE as the
+ ; same text line has to be done again
+ ; eight times.
+
+ RET Z ; (5) Harmless Nonsensical Timing.
+ ; (condition never met)
+
+ JR <A href="#L0041">L0041</a> ; (12) back to WAIT-INT
+
+; <font color=#9900FF>Note.</font> that a computer with less than 4K or RAM will have a collapsed
+; display file and the above mechanism deals with both types of display.
+;
+; With a full display, the 32 characters in the line are treated as NOPS
+; and the Refresh register rises from $E0 to $FF and, at the next instruction
+; - HALT, the interrupt occurs.
+; With a collapsed display and an initial NEWLINE/HALT, it is the NOPs
+; generated by the HALT that cause the Refresh value to rise from $E0 to $FF,
+; triggering an Interrupt on the next transition.
+; This works happily for all display lines between these extremes and the
+; generation of the 32 character, 1 pixel high, line will always take 128
+; clock cycles.
+
+; ---------------------------------
+; THE <b><font color=#333388>'INCREMENT CH-ADD'</font></b> SUBROUTINE
+; ---------------------------------
+; This is the subroutine that increments the character address system variable
+; and returns if it is not the cursor character. The ZX81 has an actual
+; character at the cursor position rather than a pointer system variable
+; as is the case with prior and subsequent ZX computers.
+
+<a name="L0049"></a>;; <b>CH-ADD+1</b>
+L0049: LD HL,($4016) ; fetch character address to CH_ADD.
+
+<a name="L004C"></a>;; <b>TEMP-PTR1</b>
+L004C: INC HL ; address next immediate location.
+
+<a name="L004D"></a>;; <b>TEMP-PTR2</b>
+L004D: LD ($4016),HL ; update system variable CH_ADD.
+
+ LD A,(HL) ; fetch the character.
+ CP $7F ; compare to cursor character.
+ RET NZ ; return if not the cursor.
+
+ JR <A href="#L004C">L004C</a> ; back for next character to TEMP-PTR1.
+
+; --------------------
+; THE <b><font color=#333388>'ERROR-2'</font></b> BRANCH
+; --------------------
+; This is a continuation of the error restart.
+; If the error occurred in runtime then the error stack pointer will probably
+; lead to an error report being printed unless it occurred during input.
+; If the error occurred when checking syntax then the error stack pointer
+; will be an editing routine and the position of the error will be shown
+; when the lower screen is reprinted.
+
+<a name="L0056"></a>;; <b>ERROR-2</b>
+L0056: POP HL ; pop the return address which points to the
+ ; DEFB, error code, after the RST 08.
+ LD L,(HL) ; load L with the error code. HL is not needed
+ ; anymore.
+
+<a name="L0058"></a>;; <b>ERROR-3</b>
+L0058: LD (IY+$00),L ; place error code in system variable ERR_NR
+ LD SP,($4002) ; set the stack pointer from ERR_SP
+ CALL <A href="#L0207">L0207</a> ; routine SLOW/FAST selects slow mode.
+ JP <A href="#L14BC">L14BC</a> ; exit to address on stack via routine SET-MIN.
+
+; ---
+
+ DEFB $FF ; unused.
+
+; ------------------------------------
+; THE <b><font color=#333388>'NON MASKABLE INTERRUPT'</font></b> ROUTINE
+; ------------------------------------
+; Jim Westwood's technical dodge using Non-Maskable Interrupts solved the
+; flicker problem of the ZX80 and gave the ZX81 a multi-tasking SLOW mode
+; with a steady display. Note that the AF' register is reserved for this
+; function and its interaction with the display routines. When counting
+; TV lines, the NMI makes no use of the main registers.
+; The circuitry for the NMI generator is contained within the SCL (Sinclair
+; Computer Logic) chip.
+; ( It takes 32 clock cycles while incrementing towards zero ).
+
+<a name="L0066"></a>;; <b>NMI</b>
+L0066: EX AF,AF' ; (4) switch in the NMI's copy of the
+ ; accumulator.
+ INC A ; (4) increment.
+ JP M,<A href="#L006D">L006D</a> ; (10/10) jump, if minus, to NMI-RET as this is
+ ; part of a test to see if the NMI
+ ; generation is working or an intermediate
+ ; value for the ascending negated blank
+ ; line counter.
+
+ JR Z,<A href="#L006F">L006F</a> ; (12) forward to NMI-CONT
+ ; when line count has incremented to zero.
+
+; <font color=#9900FF>Note.</font> the synchronizing NMI when A increments from zero to one takes this
+; 7 clock cycle route making 39 clock cycles in all.
+
+<a name="L006D"></a>;; <b>NMI-RET</b>
+L006D: EX AF,AF' ; (4) switch out the incremented line counter
+ ; or test result $80
+ RET ; (10) return to User application for a while.
+
+; ---
+
+; This branch is taken when the 55 (or 31) lines have been drawn.
+
+<a name="L006F"></a>;; <b>NMI-CONT</b>
+L006F: EX AF,AF' ; (4) restore the main accumulator.
+
+ PUSH AF ; (11) * Save Main Registers
+ PUSH BC ; (11) **
+ PUSH DE ; (11) ***
+ PUSH HL ; (11) ****
+
+; the next set-up procedure is only really applicable when the top set of
+; blank lines have been generated.
+
+ LD HL,($400C) ; (16) fetch start of Display File from D_FILE
+ ; points to the HALT at beginning.
+ SET 7,H ; (8) point to upper 32K 'echo display file'
+
+ HALT ; (1) HALT synchronizes with NMI.
+ ; Used with special hardware connected to the
+ ; Z80 HALT and WAIT lines to take 1 clock cycle.
+
+; ----------------------------------------------------------------------------
+; the NMI has been generated - start counting. The cathode ray is at the RH
+; side of the TV.
+; First the NMI servicing, similar to CALL = 17 clock cycles.
+; Then the time taken by the NMI for zero-to-one path = 39 cycles
+; The HALT above = 01 cycles.
+; The two instructions below = 19 cycles.
+; The code at <A href="#L0281">L0281</a> up to and including the CALL = 43 cycles.
+; The Called routine at <A href="#L02B5">L02B5</a> = 24 cycles.
+; -------------------------------------- ---
+; Total Z80 instructions = 143 cycles.
+;
+; Meanwhile in TV world,
+; Horizontal retrace = 15 cycles.
+; Left blanking border 8 character positions = 32 cycles
+; Generation of 75% scanline from the first NEWLINE = 96 cycles
+; --------------------------------------- ---
+; 143 cycles
+;
+; Since at the time the first JP (HL) is encountered to execute the echo
+; display another 8 character positions have to be put out, then the
+; Refresh register need to hold $F8. Working back and counteracting
+; the fact that every instruction increments the Refresh register then
+; the value that is loaded into R needs to be $F5. :-)
+;
+;
+ OUT ($FD),A ; (11) Stop the NMI generator.
+
+ JP (IX) ; (8) forward to L0281 (after top) or L028F
+
+; ****************
+; ** KEY TABLES **
+; ****************
+
+; -------------------------------
+; THE <b><font color=#333388>'UNSHIFTED'</font></b> CHARACTER CODES
+; -------------------------------
+
+<a name="L007E"></a>;; <b>K-UNSHIFT</b>
+L007E: DEFB $3F ; Z
+ DEFB $3D ; X
+ DEFB $28 ; C
+ DEFB $3B ; V
+ DEFB $26 ; A
+ DEFB $38 ; S
+ DEFB $29 ; D
+ DEFB $2B ; F
+ DEFB $2C ; G
+ DEFB $36 ; Q
+ DEFB $3C ; W
+ DEFB $2A ; E
+ DEFB $37 ; R
+ DEFB $39 ; T
+ DEFB $1D ; 1
+ DEFB $1E ; 2
+ DEFB $1F ; 3
+ DEFB $20 ; 4
+ DEFB $21 ; 5
+ DEFB $1C ; 0
+ DEFB $25 ; 9
+ DEFB $24 ; 8
+ DEFB $23 ; 7
+ DEFB $22 ; 6
+ DEFB $35 ; P
+ DEFB $34 ; O
+ DEFB $2E ; I
+ DEFB $3A ; U
+ DEFB $3E ; Y
+ DEFB $76 ; NEWLINE
+ DEFB $31 ; L
+ DEFB $30 ; K
+ DEFB $2F ; J
+ DEFB $2D ; H
+ DEFB $00 ; SPACE
+ DEFB $1B ; .
+ DEFB $32 ; M
+ DEFB $33 ; N
+ DEFB $27 ; B
+
+; -----------------------------
+; THE <b><font color=#333388>'SHIFTED'</font></b> CHARACTER CODES
+; -----------------------------
+
+
+<a name="L00A5"></a>;; <b>K-SHIFT</b>
+L00A5: DEFB $0E ; :
+ DEFB $19 ; ;
+ DEFB $0F ; ?
+ DEFB $18 ; /
+ DEFB $E3 ; STOP
+ DEFB $E1 ; LPRINT
+ DEFB $E4 ; SLOW
+ DEFB $E5 ; FAST
+ DEFB $E2 ; LLIST
+ DEFB $C0 ; ""
+ DEFB $D9 ; OR
+ DEFB $E0 ; STEP
+ DEFB $DB ; <=
+ DEFB $DD ; <>
+ DEFB $75 ; EDIT
+ DEFB $DA ; AND
+ DEFB $DE ; THEN
+ DEFB $DF ; TO
+ DEFB $72 ; cursor-left
+ DEFB $77 ; RUBOUT
+ DEFB $74 ; GRAPHICS
+ DEFB $73 ; cursor-right
+ DEFB $70 ; cursor-up
+ DEFB $71 ; cursor-down
+ DEFB $0B ; "
+ DEFB $11 ; )
+ DEFB $10 ; (
+ DEFB $0D ; $
+ DEFB $DC ; >=
+ DEFB $79 ; FUNCTION
+ DEFB $14 ; =
+ DEFB $15 ; +
+ DEFB $16 ; -
+ DEFB $D8 ; **
+ DEFB $0C ; £
+ DEFB $1A ; ,
+ DEFB $12 ; >
+ DEFB $13 ; <
+ DEFB $17 ; *
+
+; ------------------------------
+; THE <b><font color=#333388>'FUNCTION'</font></b> CHARACTER CODES
+; ------------------------------
+
+
+<a name="L00CC"></a>;; <b>K-FUNCT</b>
+L00CC: DEFB $CD ; LN
+ DEFB $CE ; EXP
+ DEFB $C1 ; AT
+ DEFB $78 ; KL
+ DEFB $CA ; ASN
+ DEFB $CB ; ACS
+ DEFB $CC ; ATN
+ DEFB $D1 ; SGN
+ DEFB $D2 ; ABS
+ DEFB $C7 ; SIN
+ DEFB $C8 ; COS
+ DEFB $C9 ; TAN
+ DEFB $CF ; INT
+ DEFB $40 ; RND
+ DEFB $78 ; KL
+ DEFB $78 ; KL
+ DEFB $78 ; KL
+ DEFB $78 ; KL
+ DEFB $78 ; KL
+ DEFB $78 ; KL
+ DEFB $78 ; KL
+ DEFB $78 ; KL
+ DEFB $78 ; KL
+ DEFB $78 ; KL
+ DEFB $C2 ; TAB
+ DEFB $D3 ; PEEK
+ DEFB $C4 ; CODE
+ DEFB $D6 ; CHR$
+ DEFB $D5 ; STR$
+ DEFB $78 ; KL
+ DEFB $D4 ; USR
+ DEFB $C6 ; LEN
+ DEFB $C5 ; VAL
+ DEFB $D0 ; SQR
+ DEFB $78 ; KL
+ DEFB $78 ; KL
+ DEFB $42 ; PI
+ DEFB $D7 ; NOT
+ DEFB $41 ; INKEY$
+
+; -----------------------------
+; THE <b><font color=#333388>'GRAPHIC'</font></b> CHARACTER CODES
+; -----------------------------
+
+
+<a name="L00F3"></a>;; <b>K-GRAPH</b>
+L00F3: DEFB $08 ; graphic
+ DEFB $0A ; graphic
+ DEFB $09 ; graphic
+ DEFB $8A ; graphic
+ DEFB $89 ; graphic
+ DEFB $81 ; graphic
+ DEFB $82 ; graphic
+ DEFB $07 ; graphic
+ DEFB $84 ; graphic
+ DEFB $06 ; graphic
+ DEFB $01 ; graphic
+ DEFB $02 ; graphic
+ DEFB $87 ; graphic
+ DEFB $04 ; graphic
+ DEFB $05 ; graphic
+ DEFB $77 ; RUBOUT
+ DEFB $78 ; KL
+ DEFB $85 ; graphic
+ DEFB $03 ; graphic
+ DEFB $83 ; graphic
+ DEFB $8B ; graphic
+ DEFB $91 ; inverse )
+ DEFB $90 ; inverse (
+ DEFB $8D ; inverse $
+ DEFB $86 ; graphic
+ DEFB $78 ; KL
+ DEFB $92 ; inverse >
+ DEFB $95 ; inverse +
+ DEFB $96 ; inverse -
+ DEFB $88 ; graphic
+
+; ------------------
+; THE <b><font color=#333388>'TOKEN'</font></b> TABLES
+; ------------------
+
+
+<a name="L094B"></a>;; <b>TOKENS</b>
+L0111: DEFB $0F+$80 ; '?'+$80
+ DEFB $0B,$0B+$80 ; ""
+ DEFB $26,$39+$80 ; AT
+ DEFB $39,$26,$27+$80 ; TAB
+ DEFB $0F+$80 ; '?'+$80
+ DEFB $28,$34,$29,$2A+$80 ; CODE
+ DEFB $3B,$26,$31+$80 ; VAL
+ DEFB $31,$2A,$33+$80 ; LEN
+ DEFB $38,$2E,$33+$80 ; SIN
+ DEFB $28,$34,$38+$80 ; COS
+ DEFB $39,$26,$33+$80 ; TAN
+ DEFB $26,$38,$33+$80 ; ASN
+ DEFB $26,$28,$38+$80 ; ACS
+ DEFB $26,$39,$33+$80 ; ATN
+ DEFB $31,$33+$80 ; LN
+ DEFB $2A,$3D,$35+$80 ; EXP
+ DEFB $2E,$33,$39+$80 ; INT
+ DEFB $38,$36,$37+$80 ; SQR
+ DEFB $38,$2C,$33+$80 ; SGN
+ DEFB $26,$27,$38+$80 ; ABS
+ DEFB $35,$2A,$2A,$30+$80 ; PEEK
+ DEFB $3A,$38,$37+$80 ; USR
+ DEFB $38,$39,$37,$0D+$80 ; STR$
+ DEFB $28,$2D,$37,$0D+$80 ; CHR$
+ DEFB $33,$34,$39+$80 ; NOT
+ DEFB $17,$17+$80 ; **
+ DEFB $34,$37+$80 ; OR
+ DEFB $26,$33,$29+$80 ; AND
+ DEFB $13,$14+$80 ; <=
+ DEFB $12,$14+$80 ; >=
+ DEFB $13,$12+$80 ; <>
+ DEFB $39,$2D,$2A,$33+$80 ; THEN
+ DEFB $39,$34+$80 ; TO
+ DEFB $38,$39,$2A,$35+$80 ; STEP
+ DEFB $31,$35,$37,$2E,$33,$39+$80 ; LPRINT
+ DEFB $31,$31,$2E,$38,$39+$80 ; LLIST
+ DEFB $38,$39,$34,$35+$80 ; STOP
+ DEFB $38,$31,$34,$3C+$80 ; SLOW
+ DEFB $2B,$26,$38,$39+$80 ; FAST
+ DEFB $33,$2A,$3C+$80 ; NEW
+ DEFB $38,$28,$37,$34,$31,$31+$80 ; SCROLL
+ DEFB $28,$34,$33,$39+$80 ; CONT
+ DEFB $29,$2E,$32+$80 ; DIM
+ DEFB $37,$2A,$32+$80 ; REM
+ DEFB $2B,$34,$37+$80 ; FOR
+ DEFB $2C,$34,$39,$34+$80 ; GOTO
+ DEFB $2C,$34,$38,$3A,$27+$80 ; GOSUB
+ DEFB $2E,$33,$35,$3A,$39+$80 ; INPUT
+ DEFB $31,$34,$26,$29+$80 ; LOAD
+ DEFB $31,$2E,$38,$39+$80 ; LIST
+ DEFB $31,$2A,$39+$80 ; LET
+ DEFB $35,$26,$3A,$38,$2A+$80 ; PAUSE
+ DEFB $33,$2A,$3D,$39+$80 ; NEXT
+ DEFB $35,$34,$30,$2A+$80 ; POKE
+ DEFB $35,$37,$2E,$33,$39+$80 ; PRINT
+ DEFB $35,$31,$34,$39+$80 ; PLOT
+ DEFB $37,$3A,$33+$80 ; RUN
+ DEFB $38,$26,$3B,$2A+$80 ; SAVE
+ DEFB $37,$26,$33,$29+$80 ; RAND
+ DEFB $2E,$2B+$80 ; IF
+ DEFB $28,$31,$38+$80 ; CLS
+ DEFB $3A,$33,$35,$31,$34,$39+$80 ; UNPLOT
+ DEFB $28,$31,$2A,$26,$37+$80 ; CLEAR
+ DEFB $37,$2A,$39,$3A,$37,$33+$80 ; RETURN
+ DEFB $28,$34,$35,$3E+$80 ; COPY
+ DEFB $37,$33,$29+$80 ; RND
+ DEFB $2E,$33,$30,$2A,$3E,$0D+$80 ; INKEY$
+ DEFB $35,$2E+$80 ; PI
+
+
+; ------------------------------
+; THE <b><font color=#333388>'LOAD-SAVE UPDATE'</font></b> ROUTINE
+; ------------------------------
+;
+;
+
+<a name="L01FC"></a>;; <b>LOAD/SAVE</b>
+L01FC: INC HL ;
+ EX DE,HL ;
+ LD HL,($4014) ; system variable edit line E_LINE.
+ SCF ; set carry flag
+ SBC HL,DE ;
+ EX DE,HL ;
+ RET NC ; return if more bytes to load/save.
+
+ POP HL ; else drop return address
+
+; ----------------------
+; THE <b><font color=#333388>'DISPLAY'</font></b> ROUTINES
+; ----------------------
+;
+;
+
+<a name="L0207"></a>;; <b>SLOW/FAST</b>
+L0207: LD HL,$403B ; Address the system variable CDFLAG.
+ LD A,(HL) ; Load value to the accumulator.
+ RLA ; rotate bit 6 to position 7.
+ XOR (HL) ; exclusive or with original bit 7.
+ RLA ; rotate result out to carry.
+ RET NC ; return if both bits were the same.
+
+; Now test if this really is a ZX81 or a ZX80 running the upgraded ROM.
+; The standard ZX80 did not have an NMI generator.
+
+ LD A,$7F ; Load accumulator with %011111111
+ EX AF,AF' ; save in AF'
+
+ LD B,$11 ; A counter within which an NMI should occur
+ ; if this is a ZX81.
+ OUT ($FE),A ; start the NMI generator.
+
+; Note that if this is a ZX81 then the NMI will increment AF'.
+
+<a name="L0216"></a>;; <b>LOOP-11</b>
+L0216: DJNZ <A href="#L0216">L0216</a> ; self loop to give the NMI a chance to kick in.
+ ; = 16*13 clock cycles + 8 = 216 clock cycles.
+
+ OUT ($FD),A ; Turn off the NMI generator.
+ EX AF,AF' ; bring back the AF' value.
+ RLA ; test bit 7.
+ JR NC,<A href="#L0226">L0226</a> ; forward, if bit 7 is still reset, to NO-SLOW.
+
+; If the AF' was incremented then the NMI generator works and SLOW mode can
+; be set.
+
+ SET 7,(HL) ; Indicate SLOW mode - Compute and Display.
+
+ PUSH AF ; * Save Main Registers
+ PUSH BC ; **
+ PUSH DE ; ***
+ PUSH HL ; ****
+
+ JR <A href="#L0229">L0229</a> ; skip forward - to DISPLAY-1.
+
+; ---
+
+<a name="L0226"></a>;; <b>NO-SLOW</b>
+L0226: RES 6,(HL) ; reset bit 6 of CDFLAG.
+ RET ; return.
+
+; -----------------------
+; THE <b><font color=#333388>'MAIN DISPLAY'</font></b> LOOP
+; -----------------------
+; This routine is executed once for every frame displayed.
+
+<a name="L0229"></a>;; <b>DISPLAY-1</b>
+L0229: LD HL,($4034) ; fetch two-byte system variable FRAMES.
+ DEC HL ; decrement frames counter.
+
+<a name="L022D"></a>;; <b>DISPLAY-P</b>
+L022D: LD A,$7F ; prepare a mask
+ AND H ; pick up bits 6-0 of H.
+ OR L ; and any bits of L.
+ LD A,H ; reload A with all bits of H for PAUSE test.
+
+; Note both branches must take the same time.
+
+ JR NZ,<A href="#L0237">L0237</a> ; (12/7) forward if bits 14-0 are not zero
+ ; to ANOTHER
+
+ RLA ; (4) test bit 15 of FRAMES.
+ JR <A href="#L0239">L0239</a> ; (12) forward with result to OVER-NC
+
+; ---
+
+<a name="L0237"></a>;; <b>ANOTHER</b>
+L0237: LD B,(HL) ; (7) <font color=#9900FF>Note.</font> Harmless Nonsensical Timing weight.
+ SCF ; (4) Set Carry Flag.
+
+; <font color=#9900FF>Note.</font> the branch to here takes either (12)(7)(4) cyles or (7)(4)(12) cycles.
+
+<a name="L0239"></a>;; <b>OVER-NC</b>
+L0239: LD H,A ; (4) set H to zero
+ LD ($4034),HL ; (16) update system variable FRAMES
+ RET NC ; (11/5) return if FRAMES is in use by PAUSE
+ ; command.
+
+<a name="L023E"></a>;; <b>DISPLAY-2</b>
+L023E: CALL <A href="#L02BB">L02BB</a> ; routine KEYBOARD gets the key row in H and
+ ; the column in L. Reading the ports also starts
+ ; the TV frame synchronization pulse. (VSYNC)
+
+ LD BC,($4025) ; fetch the last key values read from LAST_K
+ LD ($4025),HL ; update LAST_K with new values.
+
+ LD A,B ; load A with previous column - will be $FF if
+ ; there was no key.
+ ADD A,$02 ; adding two will set carry if no previous key.
+
+ SBC HL,BC ; subtract with the carry the two key values.
+
+; If the same key value has been returned twice then HL will be zero.
+
+ LD A,($4027) ; fetch system variable DEBOUNCE
+ OR H ; and OR with both bytes of the difference
+ OR L ; setting the zero flag for the upcoming branch.
+
+ LD E,B ; transfer the column value to E
+ LD B,$0B ; and load B with eleven
+
+ LD HL,$403B ; address system variable CDFLAG
+ RES 0,(HL) ; reset the rightmost bit of CDFLAG
+ JR NZ,<A href="#L0264">L0264</a> ; skip forward if debounce/diff >0 to NO-KEY
+
+ BIT 7,(HL) ; test compute and display bit of CDFLAG
+ SET 0,(HL) ; set the rightmost bit of CDFLAG.
+ RET Z ; return if bit 7 indicated fast mode.
+
+ DEC B ; (4) decrement the counter.
+ NOP ; (4) Timing - 4 clock cycles. ??
+ SCF ; (4) Set Carry Flag
+
+<a name="L0264"></a>;; <b>NO-KEY</b>
+L0264: LD HL,$4027 ; sv DEBOUNCE
+ CCF ; Complement Carry Flag
+ RL B ; rotate left B picking up carry
+ ; C<-76543210<-C
+
+<a name="L026A"></a>;; <b>LOOP-B</b>
+L026A: DJNZ <A href="#L026A">L026A</a> ; self-loop while B>0 to LOOP-B
+
+ LD B,(HL) ; fetch value of DEBOUNCE to B
+ LD A,E ; transfer column value
+ CP $FE ;
+ SBC A,A ;
+ LD B,$1F ;
+ OR (HL) ;
+ AND B ;
+ RRA ;
+ LD (HL),A ;
+
+ OUT ($FF),A ; end the TV frame synchronization pulse.
+
+ LD HL,($400C) ; (12) set HL to the Display File from D_FILE
+ SET 7,H ; (8) set bit 15 to address the echo display.
+
+ CALL <A href="#L0292">L0292</a> ; (17) routine DISPLAY-3 displays the top set
+ ; of blank lines.
+
+; ---------------------
+; THE <b><font color=#333388>'VIDEO-1'</font></b> ROUTINE
+; ---------------------
+
+<a name="L0281"></a>;; <b>R-IX-1</b>
+L0281: LD A,R ; (9) Harmless Nonsensical Timing or something
+ ; very clever?
+ LD BC,$1901 ; (10) 25 lines, 1 scanline in first.
+ LD A,$F5 ; (7) This value will be loaded into R and
+ ; ensures that the cycle starts at the right
+ ; part of the display - after 32nd character
+ ; position.
+
+ CALL <A href="#L02B5">L02B5</a> ; (17) routine DISPLAY-5 completes the current
+ ; blank line and then generates the display of
+ ; the live picture using INT interrupts
+ ; The final interrupt returns to the next
+ ; address.
+
+L028B: DEC HL ; point HL to the last NEWLINE/HALT.
+
+ CALL <A href="#L0292">L0292</a> ; routine DISPLAY-3 displays the bottom set of
+ ; blank lines.
+
+; ---
+
+<a name="L028F"></a>;; <b>R-IX-2</b>
+L028F: JP <A href="#L0229">L0229</a> ; JUMP back to DISPLAY-1
+
+; ---------------------------------
+; THE <b><font color=#333388>'DISPLAY BLANK LINES'</font></b> ROUTINE
+; ---------------------------------
+; This subroutine is called twice (see above) to generate first the blank
+; lines at the top of the television display and then the blank lines at the
+; bottom of the display.
+
+<a name="L0292"></a>;; <b>DISPLAY-3</b>
+L0292: POP IX ; pop the return address to IX register.
+ ; will be either L0281 or L028F - see above.
+
+ LD C,(IY+$28) ; load C with value of system constant MARGIN.
+ BIT 7,(IY+$3B) ; test CDFLAG for compute and display.
+ JR Z,<A href="#L02A9">L02A9</a> ; forward, with FAST mode, to DISPLAY-4
+
+ LD A,C ; move MARGIN to A - 31d or 55d.
+ NEG ; Negate
+ INC A ;
+ EX AF,AF' ; place negative count of blank lines in A'
+
+ OUT ($FE),A ; enable the NMI generator.
+
+ POP HL ; ****
+ POP DE ; ***
+ POP BC ; **
+ POP AF ; * Restore Main Registers
+
+ RET ; return - end of interrupt. Return is to
+ ; user's program - BASIC or machine code.
+ ; which will be interrupted by every NMI.
+
+; ------------------------
+; THE <b><font color=#333388>'FAST MODE'</font></b> ROUTINES
+; ------------------------
+
+<a name="L02A9"></a>;; <b>DISPLAY-4</b>
+L02A9: LD A,$FC ; (7) load A with first R delay value
+ LD B,$01 ; (7) one row only.
+
+ CALL <A href="#L02B5">L02B5</a> ; (17) routine DISPLAY-5
+
+ DEC HL ; (6) point back to the HALT.
+ EX (SP),HL ; (19) Harmless Nonsensical Timing if paired.
+ EX (SP),HL ; (19) Harmless Nonsensical Timing.
+ JP (IX) ; (8) to L0281 or L028F
+
+; --------------------------
+; THE <b><font color=#333388>'DISPLAY-5'</font></b> SUBROUTINE
+; --------------------------
+; This subroutine is called from SLOW mode and FAST mode to generate the
+; central TV picture. With SLOW mode the R register is incremented, with
+; each instruction, to $F7 by the time it completes. With fast mode, the
+; final R value will be $FF and an interrupt will occur as soon as the
+; Program Counter reaches the HALT. (24 clock cycles)
+
+<a name="L02B5"></a>;; <b>DISPLAY-5</b>
+L02B5: LD R,A ; (9) Load R from A. R = slow: $F5 fast: $FC
+ LD A,$DD ; (7) load future R value. $F6 $FD
+
+ EI ; (4) Enable Interrupts $F7 $FE
+
+ JP (HL) ; (4) jump to the echo display. $F8 $FF
+
+; ----------------------------------
+; THE <b><font color=#333388>'KEYBOARD SCANNING'</font></b> SUBROUTINE
+; ----------------------------------
+; The keyboard is read during the vertical sync interval while no video is
+; being displayed. Reading a port with address bit 0 low i.e. $FE starts the
+; vertical sync pulse.
+
+<a name="L02BB"></a>;; <b>KEYBOARD</b>
+L02BB: LD HL,$FFFF ; (16) prepare a buffer to take key.
+ LD BC,$FEFE ; (20) set BC to port $FEFE. The B register,
+ ; with its single reset bit also acts as
+ ; an 8-counter.
+ IN A,(C) ; (11) read the port - all 16 bits are put on
+ ; the address bus. Start VSYNC pulse.
+ OR $01 ; (7) set the rightmost bit so as to ignore
+ ; the SHIFT key.
+
+<a name="L02C5"></a>;; <b>EACH-LINE</b>
+L02C5: OR $E0 ; [7] OR %11100000
+ LD D,A ; [4] transfer to D.
+ CPL ; [4] complement - only bits 4-0 meaningful now.
+ CP $01 ; [7] sets carry if A is zero.
+ SBC A,A ; [4] $FF if $00 else zero.
+ OR B ; [7] $FF or port FE,FD,FB....
+ AND L ; [4] unless more than one key, L will still be
+ ; $FF. if more than one key is pressed then A is
+ ; now invalid.
+ LD L,A ; [4] transfer to L.
+
+; now consider the column identifier.
+
+ LD A,H ; [4] will be $FF if no previous keys.
+ AND D ; [4] 111xxxxx
+ LD H,A ; [4] transfer A to H
+
+; since only one key may be pressed, H will, if valid, be one of
+; 11111110, 11111101, 11111011, 11110111, 11101111
+; reading from the outer column, say Q, to the inner column, say T.
+
+ RLC B ; [8] rotate the 8-counter/port address.
+ ; sets carry if more to do.
+ IN A,(C) ; [10] read another half-row.
+ ; all five bits this time.
+
+ JR C,<A href="#L02C5">L02C5</a> ; [12](7) loop back, until done, to EACH-LINE
+
+; The last row read is SHIFT,Z,X,C,V for the second time.
+
+ RRA ; (4) test the shift key - carry will be reset
+ ; if the key is pressed.
+ RL H ; (8) rotate left H picking up the carry giving
+ ; column values -
+ ; $FD, $FB, $F7, $EF, $DF.
+ ; or $FC, $FA, $F6, $EE, $DE if shifted.
+
+; We now have H identifying the column and L identifying the row in the
+; keyboard matrix.
+
+; This is a good time to test if this is an American or British machine.
+; The US machine has an extra diode that causes bit 6 of a byte read from
+; a port to be reset.
+
+ RLA ; (4) compensate for the shift test.
+ RLA ; (4) rotate bit 7 out.
+ RLA ; (4) test bit 6.
+
+ SBC A,A ; (4) $FF or $00 {USA}
+ AND $18 ; (7) $18 or $00
+ ADD A,$1F ; (7) $37 or $1F
+
+; result is either 31 (USA) or 55 (UK) blank lines above and below the TV
+; picture.
+
+ LD ($4028),A ; (13) update system variable MARGIN
+
+ RET ; (10) return
+
+; ------------------------------
+; THE <b><font color=#333388>'SET FAST MODE'</font></b> SUBROUTINE
+; ------------------------------
+;
+;
+
+<a name="L02E7"></a>;; <b>SET-FAST</b>
+L02E7: BIT 7,(IY+$3B) ; sv CDFLAG
+ RET Z ;
+
+ HALT ; Wait for Interrupt
+ OUT ($FD),A ;
+ RES 7,(IY+$3B) ; sv CDFLAG
+ RET ; return.
+
+
+; --------------
+; THE <b><font color=#333388>'REPORT-F'</font></b>
+; --------------
+
+<a name="L02F4"></a>;; <b>REPORT-F</b>
+L02F4: RST 08H ; ERROR-1
+ DEFB $0E ; Error Report: No Program Name supplied.
+
+; --------------------------
+; THE <b><font color=#333388>'SAVE COMMAND'</font></b> ROUTINE
+; --------------------------
+;
+;
+
+<a name="L02F6"></a>;; <b>SAVE</b>
+L02F6: CALL <A href="#L03A8">L03A8</a> ; routine NAME
+ JR C,<A href="#L02F4">L02F4</a> ; back with null name to REPORT-F above.
+
+ EX DE,HL ;
+ LD DE,$12CB ; five seconds timing value
+
+<a name="L02FF"></a>;; <b>HEADER</b>
+L02FF: CALL <A href="#L0F46">L0F46</a> ; routine BREAK-1
+ JR NC,<A href="#L0332">L0332</a> ; to BREAK-2
+
+<a name="L0304"></a>;; <b>DELAY-1</b>
+L0304: DJNZ <A href="#L0304">L0304</a> ; to DELAY-1
+
+ DEC DE ;
+ LD A,D ;
+ OR E ;
+ JR NZ,<A href="#L02FF">L02FF</a> ; back for delay to HEADER
+
+<a name="L030B"></a>;; <b>OUT-NAME</b>
+L030B: CALL <A href="#L031E">L031E</a> ; routine OUT-BYTE
+ BIT 7,(HL) ; test for inverted bit.
+ INC HL ; address next character of name.
+ JR Z,<A href="#L030B">L030B</a> ; back if not inverted to OUT-NAME
+
+; now start saving the system variables onwards.
+
+ LD HL,$4009 ; set start of area to VERSN thereby
+ ; preserving RAMTOP etc.
+
+<a name="L0316"></a>;; <b>OUT-PROG</b>
+L0316: CALL <A href="#L031E">L031E</a> ; routine OUT-BYTE
+
+ CALL <A href="#L01FC">L01FC</a> ; routine LOAD/SAVE >>
+ JR <A href="#L0316">L0316</a> ; loop back to OUT-PROG
+
+; -------------------------
+; THE <b><font color=#333388>'OUT-BYTE'</font></b> SUBROUTINE
+; -------------------------
+; This subroutine outputs a byte a bit at a time to a domestic tape recorder.
+
+<a name="L031E"></a>;; <b>OUT-BYTE</b>
+L031E: LD E,(HL) ; fetch byte to be saved.
+ SCF ; set carry flag - as a marker.
+
+<a name="L0320"></a>;; <b>EACH-BIT</b>
+L0320: RL E ; C < 76543210 < C
+ RET Z ; return when the marker bit has passed
+ ; right through. >>
+
+ SBC A,A ; $FF if set bit or $00 with no carry.
+ AND $05 ; $05 $00
+ ADD A,$04 ; $09 $04
+ LD C,A ; transfer timer to C. a set bit has a longer
+ ; pulse than a reset bit.
+
+<a name="L0329"></a>;; <b>PULSES</b>
+L0329: OUT ($FF),A ; pulse to cassette.
+ LD B,$23 ; set timing constant
+
+<a name="L032D"></a>;; <b>DELAY-2</b>
+L032D: DJNZ <A href="#L032D">L032D</a> ; self-loop to DELAY-2
+
+ CALL <A href="#L0F46">L0F46</a> ; routine BREAK-1 test for BREAK key.
+
+<a name="L0332"></a>;; <b>BREAK-2</b>
+L0332: JR NC,<A href="#L03A6">L03A6</a> ; forward with break to REPORT-D
+
+ LD B,$1E ; set timing value.
+
+<a name="L0336"></a>;; <b>DELAY-3</b>
+L0336: DJNZ <A href="#L0336">L0336</a> ; self-loop to DELAY-3
+
+ DEC C ; decrement counter
+ JR NZ,<A href="#L0329">L0329</a> ; loop back to PULSES
+
+<a name="L033B"></a>;; <b>DELAY-4</b>
+L033B: AND A ; clear carry for next bit test.
+ DJNZ <A href="#L033B">L033B</a> ; self loop to DELAY-4 (B is zero - 256)
+
+ JR <A href="#L0320">L0320</a> ; loop back to EACH-BIT
+
+; --------------------------
+; THE <b><font color=#333388>'LOAD COMMAND'</font></b> ROUTINE
+; --------------------------
+;
+;
+
+<a name="L0340"></a>;; <b>LOAD</b>
+L0340: CALL <A href="#L03A8">L03A8</a> ; routine NAME
+
+; DE points to start of name in RAM.
+
+ RL D ; pick up carry
+ RRC D ; carry now in bit 7.
+
+<a name="L0347"></a>;; <b>NEXT-PROG</b>
+L0347: CALL <A href="#L034C">L034C</a> ; routine IN-BYTE
+ JR <A href="#L0347">L0347</a> ; loop to NEXT-PROG
+
+; ------------------------
+; THE <b><font color=#333388>'IN-BYTE'</font></b> SUBROUTINE
+; ------------------------
+
+<a name="L034C"></a>;; <b>IN-BYTE</b>
+L034C: LD C,$01 ; prepare an eight counter 00000001.
+
+<a name="L034E"></a>;; <b>NEXT-BIT</b>
+L034E: LD B,$00 ; set counter to 256
+
+<a name="L0350"></a>;; <b>BREAK-3</b>
+L0350: LD A,$7F ; read the keyboard row
+ IN A,($FE) ; with the SPACE key.
+
+ OUT ($FF),A ; output signal to screen.
+
+ RRA ; test for SPACE pressed.
+ JR NC,<A href="#L03A2">L03A2</a> ; forward if so to BREAK-4
+
+ RLA ; reverse above rotation
+ RLA ; test tape bit.
+ JR C,<A href="#L0385">L0385</a> ; forward if set to GET-BIT
+
+ DJNZ <A href="#L0350">L0350</a> ; loop back to BREAK-3
+
+ POP AF ; drop the return address.
+ CP D ; ugh.
+
+<a name="L0361"></a>;; <b>RESTART</b>
+L0361: JP NC,<A href="#L03E5">L03E5</a> ; jump forward to INITIAL if D is zero
+ ; to reset the system
+ ; if the tape signal has timed out for example
+ ; if the tape is stopped. Not just a simple
+ ; report as some system variables will have
+ ; been overwritten.
+
+ LD H,D ; else transfer the start of name
+ LD L,E ; to the HL register
+
+<a name="L0366"></a>;; <b>IN-NAME</b>
+L0366: CALL <A href="#L034C">L034C</a> ; routine IN-BYTE is sort of recursion for name
+ ; part. received byte in C.
+ BIT 7,D ; is name the null string ?
+ LD A,C ; transfer byte to A.
+ JR NZ,<A href="#L0371">L0371</a> ; forward with null string to MATCHING
+
+ CP (HL) ; else compare with string in memory.
+ JR NZ,<A href="#L0347">L0347</a> ; back with mis-match to NEXT-PROG
+ ; (seemingly out of subroutine but return
+ ; address has been dropped).
+
+
+<a name="L0371"></a>;; <b>MATCHING</b>
+L0371: INC HL ; address next character of name
+ RLA ; test for inverted bit.
+ JR NC,<A href="#L0366">L0366</a> ; back if not to IN-NAME
+
+; the name has been matched in full.
+; proceed to load the data but first increment the high byte of E_LINE, which
+; is one of the system variables to be loaded in. Since the low byte is loaded
+; before the high byte, it is possible that, at the in-between stage, a false
+; value could cause the load to end prematurely - see LOAD/SAVE check.
+
+ INC (IY+$15) ; increment system variable E_LINE_hi.
+ LD HL,$4009 ; start loading at system variable VERSN.
+
+<a name="L037B"></a>;; <b>IN-PROG</b>
+L037B: LD D,B ; set D to zero as indicator.
+ CALL <A href="#L034C">L034C</a> ; routine IN-BYTE loads a byte
+ LD (HL),C ; insert assembled byte in memory.
+ CALL <A href="#L01FC">L01FC</a> ; routine LOAD/SAVE >>
+ JR <A href="#L037B">L037B</a> ; loop back to IN-PROG
+
+; ---
+
+; this branch assembles a full byte before exiting normally
+; from the IN-BYTE subroutine.
+
+<a name="L0385"></a>;; <b>GET-BIT</b>
+L0385: PUSH DE ; save the
+ LD E,$94 ; timing value.
+
+<a name="L0388"></a>;; <b>TRAILER</b>
+L0388: LD B,$1A ; counter to twenty six.
+
+<a name="L038A"></a>;; <b>COUNTER</b>
+L038A: DEC E ; decrement the measuring timer.
+ IN A,($FE) ; read the
+ RLA ;
+ BIT 7,E ;
+ LD A,E ;
+ JR C,<A href="#L0388">L0388</a> ; loop back with carry to TRAILER
+
+ DJNZ <A href="#L038A">L038A</a> ; to COUNTER
+
+ POP DE ;
+ JR NZ,<A href="#L039C">L039C</a> ; to BIT-DONE
+
+ CP $56 ;
+ JR NC,<A href="#L034E">L034E</a> ; to NEXT-BIT
+
+<a name="L039C"></a>;; <b>BIT-DONE</b>
+L039C: CCF ; complement carry flag
+ RL C ;
+ JR NC,<A href="#L034E">L034E</a> ; to NEXT-BIT
+
+ RET ; return with full byte.
+
+; ---
+
+; if break is pressed while loading data then perform a reset.
+; if break pressed while waiting for program on tape then OK to break.
+
+<a name="L03A2"></a>;; <b>BREAK-4</b>
+L03A2: LD A,D ; transfer indicator to A.
+ AND A ; test for zero.
+ JR Z,<A href="#L0361">L0361</a> ; back if so to RESTART
+
+
+<a name="L03A6"></a>;; <b>REPORT-D</b>
+L03A6: RST 08H ; ERROR-1
+ DEFB $0C ; Error Report: BREAK - CONT repeats
+
+; -----------------------------
+; THE <b><font color=#333388>'PROGRAM NAME'</font></b> SUBROUTINE
+; -----------------------------
+;
+;
+
+<a name="L03A8"></a>;; <b>NAME</b>
+L03A8: CALL <A href="#L0F55">L0F55</a> ; routine SCANNING
+ LD A,($4001) ; sv FLAGS
+ ADD A,A ;
+ JP M,<A href="#L0D9A">L0D9A</a> ; to REPORT-C
+
+ POP HL ;
+ RET NC ;
+
+ PUSH HL ;
+ CALL <A href="#L02E7">L02E7</a> ; routine SET-FAST
+ CALL <A href="#L13F8">L13F8</a> ; routine STK-FETCH
+ LD H,D ;
+ LD L,E ;
+ DEC C ;
+ RET M ;
+
+ ADD HL,BC ;
+ SET 7,(HL) ;
+ RET ;
+
+; -------------------------
+; THE <b><font color=#333388>'NEW'</font></b> COMMAND ROUTINE
+; -------------------------
+;
+;
+
+<a name="L03C3"></a>;; <b>NEW</b>
+L03C3: CALL <A href="#L02E7">L02E7</a> ; routine SET-FAST
+ LD BC,($4004) ; fetch value of system variable RAMTOP
+ DEC BC ; point to last system byte.
+
+; -----------------------
+; THE <b><font color=#333388>'RAM CHECK'</font></b> ROUTINE
+; -----------------------
+;
+;
+
+<a name="L03CB"></a>;; <b>RAM-CHECK</b>
+L03CB: LD H,B ;
+ LD L,C ;
+ LD A,$3F ;
+
+<a name="L03CF"></a>;; <b>RAM-FILL</b>
+L03CF: LD (HL),$02 ;
+ DEC HL ;
+ CP H ;
+ JR NZ,<A href="#L03CF">L03CF</a> ; to RAM-FILL
+
+<a name="L03D5"></a>;; <b>RAM-READ</b>
+L03D5: AND A ;
+ SBC HL,BC ;
+ ADD HL,BC ;
+ INC HL ;
+ JR NC,<A href="#L03E2">L03E2</a> ; to SET-TOP
+
+ DEC (HL) ;
+ JR Z,<A href="#L03E2">L03E2</a> ; to SET-TOP
+
+ DEC (HL) ;
+ JR Z,<A href="#L03D5">L03D5</a> ; to RAM-READ
+
+<a name="L03E2"></a>;; <b>SET-TOP</b>
+L03E2: LD ($4004),HL ; set system variable RAMTOP to first byte
+ ; above the BASIC system area.
+
+; ----------------------------
+; THE <b><font color=#333388>'INITIALIZATION'</font></b> ROUTINE
+; ----------------------------
+;
+;
+
+<a name="L03E5"></a>;; <b>INITIAL</b>
+L03E5: LD HL,($4004) ; fetch system variable RAMTOP.
+ DEC HL ; point to last system byte.
+ LD (HL),$3E ; make GO SUB end-marker $3E - too high for
+ ; high order byte of line number.
+ ; (was $3F on ZX80)
+ DEC HL ; point to unimportant low-order byte.
+ LD SP,HL ; and initialize the stack-pointer to this
+ ; location.
+ DEC HL ; point to first location on the machine stack
+ DEC HL ; which will be filled by next CALL/PUSH.
+ LD ($4002),HL ; set the error stack pointer ERR_SP to
+ ; the base of the now empty machine stack.
+
+; Now set the I register so that the video hardware knows where to find the
+; character set. This ROM only uses the character set when printing to
+; the ZX Printer. The TV picture is formed by the external video hardware.
+; Consider also, that this 8K ROM can be retro-fitted to the ZX80 instead of
+; its original 4K ROM so the video hardware could be on the ZX80.
+
+ LD A,$1E ; address for this ROM is $1E00.
+ LD I,A ; set I register from A.
+ IM 1 ; select Z80 Interrupt Mode 1.
+
+ LD IY,$4000 ; set IY to the start of RAM so that the
+ ; system variables can be indexed.
+ LD (IY+$3B),$40 ; set CDFLAG 0100 0000. Bit 6 indicates
+ ; Compute nad Display required.
+
+ LD HL,$407D ; The first location after System Variables -
+ ; 16509 decimal.
+ LD ($400C),HL ; set system variable D_FILE to this value.
+ LD B,$19 ; prepare minimal screen of 24 NEWLINEs
+ ; following an initial NEWLINE.
+
+<a name="L0408"></a>;; <b>LINE</b>
+L0408: LD (HL),$76 ; insert NEWLINE (HALT instruction)
+ INC HL ; point to next location.
+ DJNZ <A href="#L0408">L0408</a> ; loop back for all twenty five to LINE
+
+ LD ($4010),HL ; set system variable VARS to next location
+
+ CALL <A href="#L149A">L149A</a> ; routine CLEAR sets $80 end-marker and the
+ ; dynamic memory pointers E_LINE, STKBOT and
+ ; STKEND.
+
+<a name="L0413"></a>;; <b>N/L-ONLY</b>
+L0413: CALL <A href="#L14AD">L14AD</a> ; routine CURSOR-IN inserts the cursor and
+ ; end-marker in the Edit Line also setting
+ ; size of lower display to two lines.
+
+ CALL <A href="#L0207">L0207</a> ; routine SLOW/FAST selects COMPUTE and DISPLAY
+
+; ---------------------------
+; THE <b><font color=#333388>'BASIC LISTING'</font></b> SECTION
+; ---------------------------
+;
+;
+
+<a name="L0419"></a>;; <b>UPPER</b>
+L0419: CALL <A href="#L0A2A">L0A2A</a> ; routine CLS
+ LD HL,($400A) ; sv E_PPC_lo
+ LD DE,($4023) ; sv S_TOP_lo
+ AND A ;
+ SBC HL,DE ;
+ EX DE,HL ;
+ JR NC,<A href="#L042D">L042D</a> ; to ADDR-TOP
+
+ ADD HL,DE ;
+ LD ($4023),HL ; sv S_TOP_lo
+
+<a name="L042D"></a>;; <b>ADDR-TOP</b>
+L042D: CALL <A href="#L09D8">L09D8</a> ; routine LINE-ADDR
+ JR Z,<A href="#L0433">L0433</a> ; to LIST-TOP
+
+ EX DE,HL ;
+
+<a name="L0433"></a>;; <b>LIST-TOP</b>
+L0433: CALL <A href="#L073E">L073E</a> ; routine LIST-PROG
+ DEC (IY+$1E) ; sv BERG
+ JR NZ,<A href="#L0472">L0472</a> ; to LOWER
+
+ LD HL,($400A) ; sv E_PPC_lo
+ CALL <A href="#L09D8">L09D8</a> ; routine LINE-ADDR
+ LD HL,($4016) ; sv CH_ADD_lo
+ SCF ; Set Carry Flag
+ SBC HL,DE ;
+ LD HL,$4023 ; sv S_TOP_lo
+ JR NC,<A href="#L0457">L0457</a> ; to INC-LINE
+
+ EX DE,HL ;
+ LD A,(HL) ;
+ INC HL ;
+ LDI ;
+ LD (DE),A ;
+ JR <A href="#L0419">L0419</a> ; to UPPER
+
+; ---
+
+<a name="L0454"></a>;; <b>DOWN-KEY</b>
+L0454: LD HL,$400A ; sv E_PPC_lo
+
+<a name="L0457"></a>;; <b>INC-LINE</b>
+L0457: LD E,(HL) ;
+ INC HL ;
+ LD D,(HL) ;
+ PUSH HL ;
+ EX DE,HL ;
+ INC HL ;
+ CALL <A href="#L09D8">L09D8</a> ; routine LINE-ADDR
+ CALL <A href="#L05BB">L05BB</a> ; routine LINE-NO
+ POP HL ;
+
+<a name="L0464"></a>;; <b>KEY-INPUT</b>
+L0464: BIT 5,(IY+$2D) ; sv FLAGX
+ JR NZ,<A href="#L0472">L0472</a> ; forward to LOWER
+
+ LD (HL),D ;
+ DEC HL ;
+ LD (HL),E ;
+ JR <A href="#L0419">L0419</a> ; to UPPER
+
+; ----------------------------
+; THE <b><font color=#333388>'EDIT LINE COPY'</font></b> SECTION
+; ----------------------------
+; This routine sets the edit line to just the cursor when
+; 1) There is not enough memory to edit a BASIC line.
+; 2) The edit key is used during input.
+; The entry point LOWER
+
+
+<a name="L046F"></a>;; <b>EDIT-INP</b>
+L046F: CALL <A href="#L14AD">L14AD</a> ; routine CURSOR-IN sets cursor only edit line.
+
+; ->
+
+<a name="L0472"></a>;; <b>LOWER</b>
+L0472: LD HL,($4014) ; fetch edit line start from E_LINE.
+
+<a name="L0475"></a>;; <b>EACH-CHAR</b>
+L0475: LD A,(HL) ; fetch a character from edit line.
+ CP $7E ; compare to the number marker.
+ JR NZ,<A href="#L0482">L0482</a> ; forward if not to END-LINE
+
+ LD BC,$0006 ; else six invisible bytes to be removed.
+ CALL <A href="#L0A60">L0A60</a> ; routine RECLAIM-2
+ JR <A href="#L0475">L0475</a> ; back to EACH-CHAR
+
+; ---
+
+<a name="L0482"></a>;; <b>END-LINE</b>
+L0482: CP $76 ;
+ INC HL ;
+ JR NZ,<A href="#L0475">L0475</a> ; to EACH-CHAR
+
+<a name="L0487"></a>;; <b>EDIT-LINE</b>
+L0487: CALL <A href="#L0537">L0537</a> ; routine CURSOR sets cursor K or L.
+
+<a name="L048A"></a>;; <b>EDIT-ROOM</b>
+L048A: CALL <A href="#L0A1F">L0A1F</a> ; routine LINE-ENDS
+ LD HL,($4014) ; sv E_LINE_lo
+ LD (IY+$00),$FF ; sv ERR_NR
+ CALL <A href="#L0766">L0766</a> ; routine COPY-LINE
+ BIT 7,(IY+$00) ; sv ERR_NR
+ JR NZ,<A href="#L04C1">L04C1</a> ; to DISPLAY-6
+
+ LD A,($4022) ; sv DF_SZ
+ CP $18 ;
+ JR NC,<A href="#L04C1">L04C1</a> ; to DISPLAY-6
+
+ INC A ;
+ LD ($4022),A ; sv DF_SZ
+ LD B,A ;
+ LD C,$01 ;
+ CALL <A href="#L0918">L0918</a> ; routine LOC-ADDR
+ LD D,H ;
+ LD E,L ;
+ LD A,(HL) ;
+
+<a name="L04B1"></a>;; <b>FREE-LINE</b>
+L04B1: DEC HL ;
+ CP (HL) ;
+ JR NZ,<A href="#L04B1">L04B1</a> ; to FREE-LINE
+
+ INC HL ;
+ EX DE,HL ;
+ LD A,($4005) ; sv RAMTOP_hi
+ CP $4D ;
+ CALL C,<A href="#L0A5D">L0A5D</a> ; routine RECLAIM-1
+ JR <A href="#L048A">L048A</a> ; to EDIT-ROOM
+
+; --------------------------
+; THE <b><font color=#333388>'WAIT FOR KEY'</font></b> SECTION
+; --------------------------
+;
+;
+
+<a name="L04C1"></a>;; <b>DISPLAY-6</b>
+L04C1: LD HL,$0000 ;
+ LD ($4018),HL ; sv X_PTR_lo
+
+ LD HL,$403B ; system variable CDFLAG
+ BIT 7,(HL) ;
+
+ CALL Z,<A href="#L0229">L0229</a> ; routine DISPLAY-1
+
+<a name="L04CF"></a>;; <b>SLOW-DISP</b>
+L04CF: BIT 0,(HL) ;
+ JR Z,<A href="#L04CF">L04CF</a> ; to SLOW-DISP
+
+ LD BC,($4025) ; sv LAST_K
+ CALL <A href="#L0F4B">L0F4B</a> ; routine DEBOUNCE
+ CALL <A href="#L07BD">L07BD</a> ; routine DECODE
+
+ JR NC,<A href="#L0472">L0472</a> ; back to LOWER
+
+; -------------------------------
+; THE <b><font color=#333388>'KEYBOARD DECODING'</font></b> SECTION
+; -------------------------------
+; The decoded key value is in E and HL points to the position in the
+; key table. D contains zero.
+
+<a name="L04DF"></a>;; <b>K-DECODE</b>
+L04DF: LD A,($4006) ; Fetch value of system variable MODE
+ DEC A ; test the three values together
+
+ JP M,<A href="#L0508">L0508</a> ; forward, if was zero, to FETCH-2
+
+ JR NZ,<A href="#L04F7">L04F7</a> ; forward, if was 2, to FETCH-1
+
+; The original value was one and is now zero.
+
+ LD ($4006),A ; update the system variable MODE
+
+ DEC E ; reduce E to range $00 - $7F
+ LD A,E ; place in A
+ SUB $27 ; subtract 39 setting carry if range 00 - 38
+ JR C,<A href="#L04F2">L04F2</a> ; forward, if so, to FUNC-BASE
+
+ LD E,A ; else set E to reduced value
+
+<a name="L04F2"></a>;; <b>FUNC-BASE</b>
+L04F2: LD HL,<A href="#L00CC">L00CC</a> ; address of K-FUNCT table for function keys.
+ JR <A href="#L0505">L0505</a> ; forward to TABLE-ADD
+
+; ---
+
+<a name="L04F7"></a>;; <b>FETCH-1</b>
+L04F7: LD A,(HL) ;
+ CP $76 ;
+ JR Z,<A href="#L052B">L052B</a> ; to K/L-KEY
+
+ CP $40 ;
+ SET 7,A ;
+ JR C,<A href="#L051B">L051B</a> ; to ENTER
+
+ LD HL,$00C7 ; (expr reqd)
+
+<a name="L0505"></a>;; <b>TABLE-ADD</b>
+L0505: ADD HL,DE ;
+ JR <A href="#L0515">L0515</a> ; to FETCH-3
+
+; ---
+
+<a name="L0508"></a>;; <b>FETCH-2</b>
+L0508: LD A,(HL) ;
+ BIT 2,(IY+$01) ; sv FLAGS - K or L mode ?
+ JR NZ,<A href="#L0516">L0516</a> ; to TEST-CURS
+
+ ADD A,$C0 ;
+ CP $E6 ;
+ JR NC,<A href="#L0516">L0516</a> ; to TEST-CURS
+
+<a name="L0515"></a>;; <b>FETCH-3</b>
+L0515: LD A,(HL) ;
+
+<a name="L0516"></a>;; <b>TEST-CURS</b>
+L0516: CP $F0 ;
+ JP PE,<A href="#L052D">L052D</a> ; to KEY-SORT
+
+<a name="L051B"></a>;; <b>ENTER</b>
+L051B: LD E,A ;
+ CALL <A href="#L0537">L0537</a> ; routine CURSOR
+
+ LD A,E ;
+ CALL <A href="#L0526">L0526</a> ; routine ADD-CHAR
+
+<a name="L0523"></a>;; <b>BACK-NEXT</b>
+L0523: JP <A href="#L0472">L0472</a> ; back to LOWER
+
+; ------------------------------
+; THE <b><font color=#333388>'ADD CHARACTER'</font></b> SUBROUTINE
+; ------------------------------
+;
+;
+
+<a name="L0526"></a>;; <b>ADD-CHAR</b>
+L0526: CALL <A href="#L099B">L099B</a> ; routine ONE-SPACE
+ LD (DE),A ;
+ RET ;
+
+; -------------------------
+; THE <b><font color=#333388>'CURSOR KEYS'</font></b> ROUTINE
+; -------------------------
+;
+;
+
+<a name="L052B"></a>;; <b>K/L-KEY</b>
+L052B: LD A,$78 ;
+
+<a name="L052D"></a>;; <b>KEY-SORT</b>
+L052D: LD E,A ;
+ LD HL,$0482 ; base address of ED-KEYS (exp reqd)
+ ADD HL,DE ;
+ ADD HL,DE ;
+ LD C,(HL) ;
+ INC HL ;
+ LD B,(HL) ;
+ PUSH BC ;
+
+<a name="L0537"></a>;; <b>CURSOR</b>
+L0537: LD HL,($4014) ; sv E_LINE_lo
+ BIT 5,(IY+$2D) ; sv FLAGX
+ JR NZ,<A href="#L0556">L0556</a> ; to L-MODE
+
+<a name="L0540"></a>;; <b>K-MODE</b>
+L0540: RES 2,(IY+$01) ; sv FLAGS - Signal use K mode
+
+<a name="L0544"></a>;; <b>TEST-CHAR</b>
+L0544: LD A,(HL) ;
+ CP $7F ;
+ RET Z ; return
+
+ INC HL ;
+ CALL <A href="#L07B4">L07B4</a> ; routine NUMBER
+ JR Z,<A href="#L0544">L0544</a> ; to TEST-CHAR
+
+ CP $26 ;
+ JR C,<A href="#L0544">L0544</a> ; to TEST-CHAR
+
+ CP $DE ;
+ JR Z,<A href="#L0540">L0540</a> ; to K-MODE
+
+<a name="L0556"></a>;; <b>L-MODE</b>
+L0556: SET 2,(IY+$01) ; sv FLAGS - Signal use L mode
+ JR <A href="#L0544">L0544</a> ; to TEST-CHAR
+
+; --------------------------
+; THE <b><font color=#333388>'CLEAR-ONE'</font></b> SUBROUTINE
+; --------------------------
+;
+;
+
+<a name="L055C"></a>;; <b>CLEAR-ONE</b>
+L055C: LD BC,$0001 ;
+ JP <A href="#L0A60">L0A60</a> ; to RECLAIM-2
+
+
+
+; ------------------------
+; THE <b><font color=#333388>'EDITING KEYS'</font></b> TABLE
+; ------------------------
+;
+;
+
+<a name="L0562"></a>;; <b>ED-KEYS</b>
+L0562: DEFW <A href="#L059F">L059F</a> ; Address: $059F; Address: UP-KEY
+ DEFW <A href="#L0454">L0454</a> ; Address: $0454; Address: DOWN-KEY
+ DEFW <A href="#L0576">L0576</a> ; Address: $0576; Address: LEFT-KEY
+ DEFW <A href="#L057F">L057F</a> ; Address: $057F; Address: RIGHT-KEY
+ DEFW <A href="#L05AF">L05AF</a> ; Address: $05AF; Address: FUNCTION
+ DEFW <A href="#L05C4">L05C4</a> ; Address: $05C4; Address: EDIT-KEY
+ DEFW <A href="#L060C">L060C</a> ; Address: $060C; Address: N/L-KEY
+ DEFW <A href="#L058B">L058B</a> ; Address: $058B; Address: RUBOUT
+ DEFW <A href="#L05AF">L05AF</a> ; Address: $05AF; Address: FUNCTION
+ DEFW <A href="#L05AF">L05AF</a> ; Address: $05AF; Address: FUNCTION
+
+
+; -------------------------
+; THE <b><font color=#333388>'CURSOR LEFT'</font></b> ROUTINE
+; -------------------------
+;
+;
+
+<a name="L0576"></a>;; <b>LEFT-KEY</b>
+L0576: CALL <A href="#L0593">L0593</a> ; routine LEFT-EDGE
+ LD A,(HL) ;
+ LD (HL),$7F ;
+ INC HL ;
+ JR <A href="#L0588">L0588</a> ; to GET-CODE
+
+; --------------------------
+; THE <b><font color=#333388>'CURSOR RIGHT'</font></b> ROUTINE
+; --------------------------
+;
+;
+
+<a name="L057F"></a>;; <b>RIGHT-KEY</b>
+L057F: INC HL ;
+ LD A,(HL) ;
+ CP $76 ;
+ JR Z,<A href="#L059D">L059D</a> ; to ENDED-2
+
+ LD (HL),$7F ;
+ DEC HL ;
+
+<a name="L0588"></a>;; <b>GET-CODE</b>
+L0588: LD (HL),A ;
+
+<a name="L0589"></a>;; <b>ENDED-1</b>
+L0589: JR <A href="#L0523">L0523</a> ; to BACK-NEXT
+
+; --------------------
+; THE <b><font color=#333388>'RUBOUT'</font></b> ROUTINE
+; --------------------
+;
+;
+
+<a name="L058B"></a>;; <b>RUBOUT</b>
+L058B: CALL <A href="#L0593">L0593</a> ; routine LEFT-EDGE
+ CALL <A href="#L055C">L055C</a> ; routine CLEAR-ONE
+ JR <A href="#L0589">L0589</a> ; to ENDED-1
+
+; ------------------------
+; THE <b><font color=#333388>'ED-EDGE'</font></b> SUBROUTINE
+; ------------------------
+;
+;
+
+<a name="L0593"></a>;; <b>LEFT-EDGE</b>
+L0593: DEC HL ;
+ LD DE,($4014) ; sv E_LINE_lo
+ LD A,(DE) ;
+ CP $7F ;
+ RET NZ ;
+
+ POP DE ;
+
+<a name="L059D"></a>;; <b>ENDED-2</b>
+L059D: JR <A href="#L0589">L0589</a> ; to ENDED-1
+
+; -----------------------
+; THE <b><font color=#333388>'CURSOR UP'</font></b> ROUTINE
+; -----------------------
+;
+;
+
+<a name="L059F"></a>;; <b>UP-KEY</b>
+L059F: LD HL,($400A) ; sv E_PPC_lo
+ CALL <A href="#L09D8">L09D8</a> ; routine LINE-ADDR
+ EX DE,HL ;
+ CALL <A href="#L05BB">L05BB</a> ; routine LINE-NO
+ LD HL,$400B ; point to system variable E_PPC_hi
+ JP <A href="#L0464">L0464</a> ; jump back to KEY-INPUT
+
+; --------------------------
+; THE <b><font color=#333388>'FUNCTION KEY'</font></b> ROUTINE
+; --------------------------
+;
+;
+
+<a name="L05AF"></a>;; <b>FUNCTION</b>
+L05AF: LD A,E ;
+ AND $07 ;
+ LD ($4006),A ; sv MODE
+ JR <A href="#L059D">L059D</a> ; back to ENDED-2
+
+; ------------------------------------
+; THE <b><font color=#333388>'COLLECT LINE NUMBER'</font></b> SUBROUTINE
+; ------------------------------------
+;
+;
+
+<a name="L05B7"></a>;; <b>ZERO-DE</b>
+L05B7: EX DE,HL ;
+ LD DE,<A href="#L04C1">L04C1</a> + 1 ; $04C2 - a location addressing two zeros.
+
+; ->
+
+<a name="L05BB"></a>;; <b>LINE-NO</b>
+L05BB: LD A,(HL) ;
+ AND $C0 ;
+ JR NZ,<A href="#L05B7">L05B7</a> ; to ZERO-DE
+
+ LD D,(HL) ;
+ INC HL ;
+ LD E,(HL) ;
+ RET ;
+
+; ----------------------
+; THE <b><font color=#333388>'EDIT KEY'</font></b> ROUTINE
+; ----------------------
+;
+;
+
+<a name="L05C4"></a>;; <b>EDIT-KEY</b>
+L05C4: CALL <A href="#L0A1F">L0A1F</a> ; routine LINE-ENDS clears lower display.
+
+ LD HL,<A href="#L046F">L046F</a> ; Address: EDIT-INP
+ PUSH HL ; ** is pushed as an error looping address.
+
+ BIT 5,(IY+$2D) ; test FLAGX
+ RET NZ ; indirect jump if in input mode
+ ; to <A href="#L046F">L046F</a>, EDIT-INP (begin again).
+
+;
+
+ LD HL,($4014) ; fetch E_LINE
+ LD ($400E),HL ; and use to update the screen cursor DF_CC
+
+; so now RST $10 will print the line numbers to the edit line instead of screen.
+; first make sure that no newline/out of screen can occur while sprinting the
+; line numbers to the edit line.
+
+ LD HL,$1821 ; prepare line 0, column 0.
+ LD ($4039),HL ; update S_POSN with these dummy values.
+
+ LD HL,($400A) ; fetch current line from E_PPC may be a
+ ; non-existent line e.g. last line deleted.
+ CALL <A href="#L09D8">L09D8</a> ; routine LINE-ADDR gets address or that of
+ ; the following line.
+ CALL <A href="#L05BB">L05BB</a> ; routine LINE-NO gets line number if any in DE
+ ; leaving HL pointing at second low byte.
+
+ LD A,D ; test the line number for zero.
+ OR E ;
+ RET Z ; return if no line number - no program to edit.
+
+ DEC HL ; point to high byte.
+ CALL <A href="#L0AA5">L0AA5</a> ; routine OUT-NO writes number to edit line.
+
+ INC HL ; point to length bytes.
+ LD C,(HL) ; low byte to C.
+ INC HL ;
+ LD B,(HL) ; high byte to B.
+
+ INC HL ; point to first character in line.
+ LD DE,($400E) ; fetch display file cursor DF_CC
+
+ LD A,$7F ; prepare the cursor character.
+ LD (DE),A ; and insert in edit line.
+ INC DE ; increment intended destination.
+
+ PUSH HL ; * save start of BASIC.
+
+ LD HL,$001D ; set an overhead of 29 bytes.
+ ADD HL,DE ; add in the address of cursor.
+ ADD HL,BC ; add the length of the line.
+ SBC HL,SP ; subtract the stack pointer.
+
+ POP HL ; * restore pointer to start of BASIC.
+
+ RET NC ; return if not enough room to L046F EDIT-INP.
+ ; the edit key appears not to work.
+
+ LDIR ; else copy bytes from program to edit line.
+ ; <font color=#9900FF>Note.</font> hidden floating point forms are also
+ ; copied to edit line.
+
+ EX DE,HL ; transfer free location pointer to HL
+
+ POP DE ; ** remove address EDIT-INP from stack.
+
+ CALL <A href="#L14A6">L14A6</a> ; routine SET-STK-B sets STKEND from HL.
+
+ JR <A href="#L059D">L059D</a> ; back to ENDED-2 and after 3 more jumps
+ ; to <A href="#L0472">L0472</a>, LOWER.
+ ; <font color=#9900FF>Note.</font> The LOWER routine removes the hidden
+ ; floating-point numbers from the edit line.
+
+; -------------------------
+; THE <b><font color=#333388>'NEWLINE KEY'</font></b> ROUTINE
+; -------------------------
+;
+;
+
+<a name="L060C"></a>;; <b>N/L-KEY</b>
+L060C: CALL <A href="#L0A1F">L0A1F</a> ; routine LINE-ENDS
+
+ LD HL,<A href="#L0472">L0472</a> ; prepare address: LOWER
+
+ BIT 5,(IY+$2D) ; sv FLAGX
+ JR NZ,<A href="#L0629">L0629</a> ; to NOW-SCAN
+
+ LD HL,($4014) ; sv E_LINE_lo
+ LD A,(HL) ;
+ CP $FF ;
+ JR Z,<A href="#L0626">L0626</a> ; to STK-UPPER
+
+ CALL <A href="#L08E2">L08E2</a> ; routine CLEAR-PRB
+ CALL <A href="#L0A2A">L0A2A</a> ; routine CLS
+
+<a name="L0626"></a>;; <b>STK-UPPER</b>
+L0626: LD HL,<A href="#L0419">L0419</a> ; Address: UPPER
+
+<a name="L0629"></a>;; <b>NOW-SCAN</b>
+L0629: PUSH HL ; push routine address (LOWER or UPPER).
+ CALL <A href="#L0CBA">L0CBA</a> ; routine LINE-SCAN
+ POP HL ;
+ CALL <A href="#L0537">L0537</a> ; routine CURSOR
+ CALL <A href="#L055C">L055C</a> ; routine CLEAR-ONE
+ CALL <A href="#L0A73">L0A73</a> ; routine E-LINE-NO
+ JR NZ,<A href="#L064E">L064E</a> ; to N/L-INP
+
+ LD A,B ;
+ OR C ;
+ JP NZ,<A href="#L06E0">L06E0</a> ; to N/L-LINE
+
+ DEC BC ;
+ DEC BC ;
+ LD ($4007),BC ; sv PPC_lo
+ LD (IY+$22),$02 ; sv DF_SZ
+ LD DE,($400C) ; sv D_FILE_lo
+
+ JR <A href="#L0661">L0661</a> ; forward to TEST-NULL
+
+; ---
+
+<a name="L064E"></a>;; <b>N/L-INP</b>
+L064E: CP $76 ;
+ JR Z,<A href="#L0664">L0664</a> ; to N/L-NULL
+
+ LD BC,($4030) ; sv T_ADDR_lo
+ CALL <A href="#L0918">L0918</a> ; routine LOC-ADDR
+ LD DE,($4029) ; sv NXTLIN_lo
+ LD (IY+$22),$02 ; sv DF_SZ
+
+<a name="L0661"></a>;; <b>TEST-NULL</b>
+L0661: RST 18H ; GET-CHAR
+ CP $76 ;
+
+<a name="L0664"></a>;; <b>N/L-NULL</b>
+L0664: JP Z,<A href="#L0413">L0413</a> ; to N/L-ONLY
+
+ LD (IY+$01),$80 ; sv FLAGS
+ EX DE,HL ;
+
+<a name="L066C"></a>;; <b>NEXT-LINE</b>
+L066C: LD ($4029),HL ; sv NXTLIN_lo
+ EX DE,HL ;
+ CALL <A href="#L004D">L004D</a> ; routine TEMP-PTR-2
+ CALL <A href="#L0CC1">L0CC1</a> ; routine LINE-RUN
+ RES 1,(IY+$01) ; sv FLAGS - Signal printer not in use
+ LD A,$C0 ;
+ LD (IY+$19),A ; sv X_PTR_lo
+ CALL <A href="#L14A3">L14A3</a> ; routine X-TEMP
+ RES 5,(IY+$2D) ; sv FLAGX
+ BIT 7,(IY+$00) ; sv ERR_NR
+ JR Z,<A href="#L06AE">L06AE</a> ; to STOP-LINE
+
+ LD HL,($4029) ; sv NXTLIN_lo
+ AND (HL) ;
+ JR NZ,<A href="#L06AE">L06AE</a> ; to STOP-LINE
+
+ LD D,(HL) ;
+ INC HL ;
+ LD E,(HL) ;
+ LD ($4007),DE ; sv PPC_lo
+ INC HL ;
+ LD E,(HL) ;
+ INC HL ;
+ LD D,(HL) ;
+ INC HL ;
+ EX DE,HL ;
+ ADD HL,DE ;
+ CALL <A href="#L0F46">L0F46</a> ; routine BREAK-1
+ JR C,<A href="#L066C">L066C</a> ; to NEXT-LINE
+
+ LD HL,$4000 ; sv ERR_NR
+ BIT 7,(HL) ;
+ JR Z,<A href="#L06AE">L06AE</a> ; to STOP-LINE
+
+ LD (HL),$0C ;
+
+<a name="L06AE"></a>;; <b>STOP-LINE</b>
+L06AE: BIT 7,(IY+$38) ; sv PR_CC
+ CALL Z,<A href="#L0871">L0871</a> ; routine COPY-BUFF
+ LD BC,$0121 ;
+ CALL <A href="#L0918">L0918</a> ; routine LOC-ADDR
+ LD A,($4000) ; sv ERR_NR
+ LD BC,($4007) ; sv PPC_lo
+ INC A ;
+ JR Z,<A href="#L06D1">L06D1</a> ; to REPORT
+
+ CP $09 ;
+ JR NZ,<A href="#L06CA">L06CA</a> ; to CONTINUE
+
+ INC BC ;
+
+<a name="L06CA"></a>;; <b>CONTINUE</b>
+L06CA: LD ($402B),BC ; sv OLDPPC_lo
+ JR NZ,<A href="#L06D1">L06D1</a> ; to REPORT
+
+ DEC BC ;
+
+<a name="L06D1"></a>;; <b>REPORT</b>
+L06D1: CALL <A href="#L07EB">L07EB</a> ; routine OUT-CODE
+ LD A,$18 ;
+
+ RST 10H ; PRINT-A
+ CALL <A href="#L0A98">L0A98</a> ; routine OUT-NUM
+ CALL <A href="#L14AD">L14AD</a> ; routine CURSOR-IN
+ JP <A href="#L04C1">L04C1</a> ; to DISPLAY-6
+
+; ---
+
+<a name="L06E0"></a>;; <b>N/L-LINE</b>
+L06E0: LD ($400A),BC ; sv E_PPC_lo
+ LD HL,($4016) ; sv CH_ADD_lo
+ EX DE,HL ;
+ LD HL,<A href="#L0413">L0413</a> ; Address: N/L-ONLY
+ PUSH HL ;
+ LD HL,($401A) ; sv STKBOT_lo
+ SBC HL,DE ;
+ PUSH HL ;
+ PUSH BC ;
+ CALL <A href="#L02E7">L02E7</a> ; routine SET-FAST
+ CALL <A href="#L0A2A">L0A2A</a> ; routine CLS
+ POP HL ;
+ CALL <A href="#L09D8">L09D8</a> ; routine LINE-ADDR
+ JR NZ,<A href="#L0705">L0705</a> ; to COPY-OVER
+
+ CALL <A href="#L09F2">L09F2</a> ; routine NEXT-ONE
+ CALL <A href="#L0A60">L0A60</a> ; routine RECLAIM-2
+
+<a name="L0705"></a>;; <b>COPY-OVER</b>
+L0705: POP BC ;
+ LD A,C ;
+ DEC A ;
+ OR B ;
+ RET Z ;
+
+ PUSH BC ;
+ INC BC ;
+ INC BC ;
+ INC BC ;
+ INC BC ;
+ DEC HL ;
+ CALL <A href="#L099E">L099E</a> ; routine MAKE-ROOM
+ CALL <A href="#L0207">L0207</a> ; routine SLOW/FAST
+ POP BC ;
+ PUSH BC ;
+ INC DE ;
+ LD HL,($401A) ; sv STKBOT_lo
+ DEC HL ;
+ LDDR ; copy bytes
+ LD HL,($400A) ; sv E_PPC_lo
+ EX DE,HL ;
+ POP BC ;
+ LD (HL),B ;
+ DEC HL ;
+ LD (HL),C ;
+ DEC HL ;
+ LD (HL),E ;
+ DEC HL ;
+ LD (HL),D ;
+
+ RET ; return.
+
+; ---------------------------------------
+; THE <b><font color=#333388>'LIST'</font></b> AND 'LLIST' COMMAND ROUTINES
+; ---------------------------------------
+;
+;
+
+<a name="L072C"></a>;; <b>LLIST</b>
+L072C: SET 1,(IY+$01) ; sv FLAGS - signal printer in use
+
+<a name="L0730"></a>;; <b>LIST</b>
+L0730: CALL <A href="#L0EA7">L0EA7</a> ; routine FIND-INT
+
+ LD A,B ; fetch high byte of user-supplied line number.
+ AND $3F ; and crudely limit to range 1-16383.
+
+ LD H,A ;
+ LD L,C ;
+ LD ($400A),HL ; sv E_PPC_lo
+ CALL <A href="#L09D8">L09D8</a> ; routine LINE-ADDR
+
+<a name="L073E"></a>;; <b>LIST-PROG</b>
+L073E: LD E,$00 ;
+
+<a name="L0740"></a>;; <b>UNTIL-END</b>
+L0740: CALL <A href="#L0745">L0745</a> ; routine OUT-LINE lists one line of BASIC
+ ; making an early return when the screen is
+ ; full or the end of program is reached. >>
+ JR <A href="#L0740">L0740</a> ; loop back to UNTIL-END
+
+; -----------------------------------
+; THE <b><font color=#333388>'PRINT A BASIC LINE'</font></b> SUBROUTINE
+; -----------------------------------
+;
+;
+
+<a name="L0745"></a>;; <b>OUT-LINE</b>
+L0745: LD BC,($400A) ; sv E_PPC_lo
+ CALL <A href="#L09EA">L09EA</a> ; routine CP-LINES
+ LD D,$92 ;
+ JR Z,<A href="#L0755">L0755</a> ; to TEST-END
+
+ LD DE,$0000 ;
+ RL E ;
+
+<a name="L0755"></a>;; <b>TEST-END</b>
+L0755: LD (IY+$1E),E ; sv BERG
+ LD A,(HL) ;
+ CP $40 ;
+ POP BC ;
+ RET NC ;
+
+ PUSH BC ;
+ CALL <A href="#L0AA5">L0AA5</a> ; routine OUT-NO
+ INC HL ;
+ LD A,D ;
+
+ RST 10H ; PRINT-A
+ INC HL ;
+ INC HL ;
+
+<a name="L0766"></a>;; <b>COPY-LINE</b>
+L0766: LD ($4016),HL ; sv CH_ADD_lo
+ SET 0,(IY+$01) ; sv FLAGS - Suppress leading space
+
+<a name="L076D"></a>;; <b>MORE-LINE</b>
+L076D: LD BC,($4018) ; sv X_PTR_lo
+ LD HL,($4016) ; sv CH_ADD_lo
+ AND A ;
+ SBC HL,BC ;
+ JR NZ,<A href="#L077C">L077C</a> ; to TEST-NUM
+
+ LD A,$B8 ;
+
+ RST 10H ; PRINT-A
+
+<a name="L077C"></a>;; <b>TEST-NUM</b>
+L077C: LD HL,($4016) ; sv CH_ADD_lo
+ LD A,(HL) ;
+ INC HL ;
+ CALL <A href="#L07B4">L07B4</a> ; routine NUMBER
+ LD ($4016),HL ; sv CH_ADD_lo
+ JR Z,<A href="#L076D">L076D</a> ; to MORE-LINE
+
+ CP $7F ;
+ JR Z,<A href="#L079D">L079D</a> ; to OUT-CURS
+
+ CP $76 ;
+ JR Z,<A href="#L07EE">L07EE</a> ; to OUT-CH
+
+ BIT 6,A ;
+ JR Z,<A href="#L079A">L079A</a> ; to NOT-TOKEN
+
+ CALL <A href="#L094B">L094B</a> ; routine TOKENS
+ JR <A href="#L076D">L076D</a> ; to MORE-LINE
+
+; ---
+
+
+<a name="L079A"></a>;; <b>NOT-TOKEN</b>
+L079A: RST 10H ; PRINT-A
+ JR <A href="#L076D">L076D</a> ; to MORE-LINE
+
+; ---
+
+<a name="L079D"></a>;; <b>OUT-CURS</b>
+L079D: LD A,($4006) ; Fetch value of system variable MODE
+ LD B,$AB ; Prepare an inverse [F] for function cursor.
+
+ AND A ; Test for zero -
+ JR NZ,<A href="#L07AA">L07AA</a> ; forward if not to FLAGS-2
+
+ LD A,($4001) ; Fetch system variable FLAGS.
+ LD B,$B0 ; Prepare an inverse [K] for keyword cursor.
+
+<a name="L07AA"></a>;; <b>FLAGS-2</b>
+L07AA: RRA ; 00000?00 -> 000000?0
+ RRA ; 000000?0 -> 0000000?
+ AND $01 ; 0000000? 0000000x
+
+ ADD A,B ; Possibly [F] -> [G] or [K] -> [L]
+
+ CALL <A href="#L07F5">L07F5</a> ; routine PRINT-SP prints character
+ JR <A href="#L076D">L076D</a> ; back to MORE-LINE
+
+; -----------------------
+; THE <b><font color=#333388>'NUMBER'</font></b> SUBROUTINE
+; -----------------------
+;
+;
+
+<a name="L07B4"></a>;; <b>NUMBER</b>
+L07B4: CP $7E ;
+ RET NZ ;
+
+ INC HL ;
+ INC HL ;
+ INC HL ;
+ INC HL ;
+ INC HL ;
+ RET ;
+
+; --------------------------------
+; THE <b><font color=#333388>'KEYBOARD DECODE'</font></b> SUBROUTINE
+; --------------------------------
+;
+;
+
+<a name="L07BD"></a>;; <b>DECODE</b>
+L07BD: LD D,$00 ;
+ SRA B ;
+ SBC A,A ;
+ OR $26 ;
+ LD L,$05 ;
+ SUB L ;
+
+<a name="L07C7"></a>;; <b>KEY-LINE</b>
+L07C7: ADD A,L ;
+ SCF ; Set Carry Flag
+ RR C ;
+ JR C,<A href="#L07C7">L07C7</a> ; to KEY-LINE
+
+ INC C ;
+ RET NZ ;
+
+ LD C,B ;
+ DEC L ;
+ LD L,$01 ;
+ JR NZ,<A href="#L07C7">L07C7</a> ; to KEY-LINE
+
+ LD HL,$007D ; (expr reqd)
+ LD E,A ;
+ ADD HL,DE ;
+ SCF ; Set Carry Flag
+ RET ;
+
+; -------------------------
+; THE <b><font color=#333388>'PRINTING'</font></b> SUBROUTINE
+; -------------------------
+;
+;
+
+<a name="L07DC"></a>;; <b>LEAD-SP</b>
+L07DC: LD A,E ;
+ AND A ;
+ RET M ;
+
+ JR <A href="#L07F1">L07F1</a> ; to PRINT-CH
+
+; ---
+
+<a name="L07E1"></a>;; <b>OUT-DIGIT</b>
+L07E1: XOR A ;
+
+<a name="L07E2"></a>;; <b>DIGIT-INC</b>
+L07E2: ADD HL,BC ;
+ INC A ;
+ JR C,<A href="#L07E2">L07E2</a> ; to DIGIT-INC
+
+ SBC HL,BC ;
+ DEC A ;
+ JR Z,<A href="#L07DC">L07DC</a> ; to LEAD-SP
+
+<a name="L07EB"></a>;; <b>OUT-CODE</b>
+L07EB: LD E,$1C ;
+ ADD A,E ;
+
+<a name="L07EE"></a>;; <b>OUT-CH</b>
+L07EE: AND A ;
+ JR Z,<A href="#L07F5">L07F5</a> ; to PRINT-SP
+
+<a name="L07F1"></a>;; <b>PRINT-CH</b>
+L07F1: RES 0,(IY+$01) ; update FLAGS - signal leading space permitted
+
+<a name="L07F5"></a>;; <b>PRINT-SP</b>
+L07F5: EXX ;
+ PUSH HL ;
+ BIT 1,(IY+$01) ; test FLAGS - is printer in use ?
+ JR NZ,<A href="#L0802">L0802</a> ; to LPRINT-A
+
+ CALL <A href="#L0808">L0808</a> ; routine ENTER-CH
+ JR <A href="#L0805">L0805</a> ; to PRINT-EXX
+
+; ---
+
+<a name="L0802"></a>;; <b>LPRINT-A</b>
+L0802: CALL <A href="#L0851">L0851</a> ; routine LPRINT-CH
+
+<a name="L0805"></a>;; <b>PRINT-EXX</b>
+L0805: POP HL ;
+ EXX ;
+ RET ;
+
+; ---
+
+<a name="L0808"></a>;; <b>ENTER-CH</b>
+L0808: LD D,A ;
+ LD BC,($4039) ; sv S_POSN_x
+ LD A,C ;
+ CP $21 ;
+ JR Z,<A href="#L082C">L082C</a> ; to TEST-LOW
+
+<a name="L0812"></a>;; <b>TEST-N/L</b>
+L0812: LD A,$76 ;
+ CP D ;
+ JR Z,<A href="#L0847">L0847</a> ; to WRITE-N/L
+
+ LD HL,($400E) ; sv DF_CC_lo
+ CP (HL) ;
+ LD A,D ;
+ JR NZ,<A href="#L083E">L083E</a> ; to WRITE-CH
+
+ DEC C ;
+ JR NZ,<A href="#L083A">L083A</a> ; to EXPAND-1
+
+ INC HL ;
+ LD ($400E),HL ; sv DF_CC_lo
+ LD C,$21 ;
+ DEC B ;
+ LD ($4039),BC ; sv S_POSN_x
+
+<a name="L082C"></a>;; <b>TEST-LOW</b>
+L082C: LD A,B ;
+ CP (IY+$22) ; sv DF_SZ
+ JR Z,<A href="#L0835">L0835</a> ; to REPORT-5
+
+ AND A ;
+ JR NZ,<A href="#L0812">L0812</a> ; to TEST-N/L
+
+<a name="L0835"></a>;; <b>REPORT-5</b>
+L0835: LD L,$04 ; 'No more room on screen'
+ JP <A href="#L0058">L0058</a> ; to ERROR-3
+
+; ---
+
+<a name="L083A"></a>;; <b>EXPAND-1</b>
+L083A: CALL <A href="#L099B">L099B</a> ; routine ONE-SPACE
+ EX DE,HL ;
+
+<a name="L083E"></a>;; <b>WRITE-CH</b>
+L083E: LD (HL),A ;
+ INC HL ;
+ LD ($400E),HL ; sv DF_CC_lo
+ DEC (IY+$39) ; sv S_POSN_x
+ RET ;
+
+; ---
+
+<a name="L0847"></a>;; <b>WRITE-N/L</b>
+L0847: LD C,$21 ;
+ DEC B ;
+ SET 0,(IY+$01) ; sv FLAGS - Suppress leading space
+ JP <A href="#L0918">L0918</a> ; to LOC-ADDR
+
+; --------------------------
+; THE <b><font color=#333388>'LPRINT-CH'</font></b> SUBROUTINE
+; --------------------------
+; This routine sends a character to the ZX-Printer placing the code for the
+; character in the Printer Buffer.
+; <font color=#9900FF>Note.</font> PR-CC contains the low byte of the buffer address. The high order byte
+; is always constant.
+
+
+<a name="L0851"></a>;; <b>LPRINT-CH</b>
+L0851: CP $76 ; compare to NEWLINE.
+ JR Z,<A href="#L0871">L0871</a> ; forward if so to COPY-BUFF
+
+ LD C,A ; take a copy of the character in C.
+ LD A,($4038) ; fetch print location from PR_CC
+ AND $7F ; ignore bit 7 to form true position.
+ CP $5C ; compare to 33rd location
+
+ LD L,A ; form low-order byte.
+ LD H,$40 ; the high-order byte is fixed.
+
+ CALL Z,<A href="#L0871">L0871</a> ; routine COPY-BUFF to send full buffer to
+ ; the printer if first 32 bytes full.
+ ; (this will reset HL to start.)
+
+ LD (HL),C ; place character at location.
+ INC L ; increment - will not cross a 256 boundary.
+ LD (IY+$38),L ; update system variable PR_CC
+ ; automatically resetting bit 7 to show that
+ ; the buffer is not empty.
+ RET ; return.
+
+; --------------------------
+; THE <b><font color=#333388>'COPY'</font></b> COMMAND ROUTINE
+; --------------------------
+; The full character-mapped screen is copied to the ZX-Printer.
+; All twenty-four text/graphic lines are printed.
+
+<a name="L0869"></a>;; <b>COPY</b>
+L0869: LD D,$16 ; prepare to copy twenty four text lines.
+ LD HL,($400C) ; set HL to start of display file from D_FILE.
+ INC HL ;
+ JR <A href="#L0876">L0876</a> ; forward to COPY*D
+
+; ---
+
+; A single character-mapped printer buffer is copied to the ZX-Printer.
+
+<a name="L0871"></a>;; <b>COPY-BUFF</b>
+L0871: LD D,$01 ; prepare to copy a single text line.
+ LD HL,$403C ; set HL to start of printer buffer PRBUFF.
+
+; both paths converge here.
+
+<a name="L0876"></a>;; <b>COPY*D</b>
+L0876: CALL <A href="#L02E7">L02E7</a> ; routine SET-FAST
+
+ PUSH BC ; *** preserve BC throughout.
+ ; a pending character may be present
+ ; in C from LPRINT-CH
+
+<a name="L087A"></a>;; <b>COPY-LOOP</b>
+L087A: PUSH HL ; save first character of line pointer. (*)
+ XOR A ; clear accumulator.
+ LD E,A ; set pixel line count, range 0-7, to zero.
+
+; this inner loop deals with each horizontal pixel line.
+
+<a name="L087D"></a>;; <b>COPY-TIME</b>
+L087D: OUT ($FB),A ; bit 2 reset starts the printer motor
+ ; with an inactive stylus - bit 7 reset.
+ POP HL ; pick up first character of line pointer (*)
+ ; on inner loop.
+
+<a name="L0880"></a>;; <b>COPY-BRK</b>
+L0880: CALL <A href="#L0F46">L0F46</a> ; routine BREAK-1
+ JR C,<A href="#L088A">L088A</a> ; forward with no keypress to COPY-CONT
+
+; else A will hold 11111111 0
+
+ RRA ; 0111 1111
+ OUT ($FB),A ; stop ZX printer motor, de-activate stylus.
+
+<a name="L0888"></a>;; <b>REPORT-D2</b>
+L0888: RST 08H ; ERROR-1
+ DEFB $0C ; Error Report: BREAK - CONT repeats
+
+; ---
+
+<a name="L088A"></a>;; <b>COPY-CONT</b>
+L088A: IN A,($FB) ; read from printer port.
+ ADD A,A ; test bit 6 and 7
+ JP M,<A href="#L08DE">L08DE</a> ; jump forward with no printer to COPY-END
+
+ JR NC,<A href="#L0880">L0880</a> ; back if stylus not in position to COPY-BRK
+
+ PUSH HL ; save first character of line pointer (*)
+ PUSH DE ; ** preserve character line and pixel line.
+
+ LD A,D ; text line count to A?
+ CP $02 ; sets carry if last line.
+ SBC A,A ; now $FF if last line else zero.
+
+; now cleverly prepare a printer control mask setting bit 2 (later moved to 1)
+; of D to slow printer for the last two pixel lines ( E = 6 and 7)
+
+ AND E ; and with pixel line offset 0-7
+ RLCA ; shift to left.
+ AND E ; and again.
+ LD D,A ; store control mask in D.
+
+<a name="L089C"></a>;; <b>COPY-NEXT</b>
+L089C: LD C,(HL) ; load character from screen or buffer.
+ LD A,C ; save a copy in C for later inverse test.
+ INC HL ; update pointer for next time.
+ CP $76 ; is character a NEWLINE ?
+ JR Z,<A href="#L08C7">L08C7</a> ; forward, if so, to COPY-N/L
+
+ PUSH HL ; * else preserve the character pointer.
+
+ SLA A ; (?) multiply by two
+ ADD A,A ; multiply by four
+ ADD A,A ; multiply by eight
+
+ LD H,$0F ; load H with half the address of character set.
+ RL H ; now $1E or $1F (with carry)
+ ADD A,E ; add byte offset 0-7
+ LD L,A ; now HL addresses character source byte
+
+ RL C ; test character, setting carry if inverse.
+ SBC A,A ; accumulator now $00 if normal, $FF if inverse.
+
+ XOR (HL) ; combine with bit pattern at end or ROM.
+ LD C,A ; transfer the byte to C.
+ LD B,$08 ; count eight bits to output.
+
+<a name="L08B5"></a>;; <b>COPY-BITS</b>
+L08B5: LD A,D ; fetch speed control mask from D.
+ RLC C ; rotate a bit from output byte to carry.
+ RRA ; pick up in bit 7, speed bit to bit 1
+ LD H,A ; store aligned mask in H register.
+
+<a name="L08BA"></a>;; <b>COPY-WAIT</b>
+L08BA: IN A,($FB) ; read the printer port
+ RRA ; test for alignment signal from encoder.
+ JR NC,<A href="#L08BA">L08BA</a> ; loop if not present to COPY-WAIT
+
+ LD A,H ; control byte to A.
+ OUT ($FB),A ; and output to printer port.
+ DJNZ <A href="#L08B5">L08B5</a> ; loop for all eight bits to COPY-BITS
+
+ POP HL ; * restore character pointer.
+ JR <A href="#L089C">L089C</a> ; back for adjacent character line to COPY-NEXT
+
+; ---
+
+; A NEWLINE has been encountered either following a text line or as the
+; first character of the screen or printer line.
+
+<a name="L08C7"></a>;; <b>COPY-N/L</b>
+L08C7: IN A,($FB) ; read printer port.
+ RRA ; wait for encoder signal.
+ JR NC,<A href="#L08C7">L08C7</a> ; loop back if not to COPY-N/L
+
+ LD A,D ; transfer speed mask to A.
+ RRCA ; rotate speed bit to bit 1.
+ ; bit 7, stylus control is reset.
+ OUT ($FB),A ; set the printer speed.
+
+ POP DE ; ** restore character line and pixel line.
+ INC E ; increment pixel line 0-7.
+ BIT 3,E ; test if value eight reached.
+ JR Z,<A href="#L087D">L087D</a> ; back if not to COPY-TIME
+
+; eight pixel lines, a text line have been completed.
+
+ POP BC ; lose the now redundant first character
+ ; pointer
+ DEC D ; decrease text line count.
+ JR NZ,<A href="#L087A">L087A</a> ; back if not zero to COPY-LOOP
+
+ LD A,$04 ; stop the already slowed printer motor.
+ OUT ($FB),A ; output to printer port.
+
+<a name="L08DE"></a>;; <b>COPY-END</b>
+L08DE: CALL <A href="#L0207">L0207</a> ; routine SLOW/FAST
+ POP BC ; *** restore preserved BC.
+
+; -------------------------------------
+; THE <b><font color=#333388>'CLEAR PRINTER BUFFER'</font></b> SUBROUTINE
+; -------------------------------------
+; This subroutine sets 32 bytes of the printer buffer to zero (space) and
+; the 33rd character is set to a NEWLINE.
+; This occurs after the printer buffer is sent to the printer but in addition
+; after the 24 lines of the screen are sent to the printer.
+; <font color=#9900FF>Note.</font> This is a logic error as the last operation does not involve the
+; buffer at all. Logically one should be able to use
+; 10 LPRINT "HELLO ";
+; 20 COPY
+; 30 LPRINT ; "WORLD"
+; and expect to see the entire greeting emerge from the printer.
+; Surprisingly this logic error was never discovered and although one can argue
+; if the above is a bug, the repetition of this error on the Spectrum was most
+; definitely a bug.
+; Since the printer buffer is fixed at the end of the system variables, and
+; the print position is in the range $3C - $5C, then bit 7 of the system
+; variable is set to show the buffer is empty and automatically reset when
+; the variable is updated with any print position - neat.
+
+<a name="L08E2"></a>;; <b>CLEAR-PRB</b>
+L08E2: LD HL,$405C ; address fixed end of PRBUFF
+ LD (HL),$76 ; place a newline at last position.
+ LD B,$20 ; prepare to blank 32 preceding characters.
+
+<a name="L08E9"></a>;; <b>PRB-BYTES</b>
+L08E9: DEC HL ; decrement address - could be DEC L.
+ LD (HL),$00 ; place a zero byte.
+ DJNZ <A href="#L08E9">L08E9</a> ; loop for all thirty-two to PRB-BYTES
+
+ LD A,L ; fetch character print position.
+ SET 7,A ; signal the printer buffer is clear.
+ LD ($4038),A ; update one-byte system variable PR_CC
+ RET ; return.
+
+; -------------------------
+; THE <b><font color=#333388>'PRINT AT'</font></b> SUBROUTINE
+; -------------------------
+;
+;
+
+<a name="L08F5"></a>;; <b>PRINT-AT</b>
+L08F5: LD A,$17 ;
+ SUB B ;
+ JR C,<A href="#L0905">L0905</a> ; to WRONG-VAL
+
+<a name="L08FA"></a>;; <b>TEST-VAL</b>
+L08FA: CP (IY+$22) ; sv DF_SZ
+ JP C,<A href="#L0835">L0835</a> ; to REPORT-5
+
+ INC A ;
+ LD B,A ;
+ LD A,$1F ;
+ SUB C ;
+
+<a name="L0905"></a>;; <b>WRONG-VAL</b>
+L0905: JP C,<A href="#L0EAD">L0EAD</a> ; to REPORT-B
+
+ ADD A,$02 ;
+ LD C,A ;
+
+<a name="L090B"></a>;; <b>SET-FIELD</b>
+L090B: BIT 1,(IY+$01) ; sv FLAGS - Is printer in use
+ JR Z,<A href="#L0918">L0918</a> ; to LOC-ADDR
+
+ LD A,$5D ;
+ SUB C ;
+ LD ($4038),A ; sv PR_CC
+ RET ;
+
+; ----------------------------
+; THE <b><font color=#333388>'LOCATE ADDRESS'</font></b> ROUTINE
+; ----------------------------
+;
+;
+
+<a name="L0918"></a>;; <b>LOC-ADDR</b>
+L0918: LD ($4039),BC ; sv S_POSN_x
+ LD HL,($4010) ; sv VARS_lo
+ LD D,C ;
+ LD A,$22 ;
+ SUB C ;
+ LD C,A ;
+ LD A,$76 ;
+ INC B ;
+
+<a name="L0927"></a>;; <b>LOOK-BACK</b>
+L0927: DEC HL ;
+ CP (HL) ;
+ JR NZ,<A href="#L0927">L0927</a> ; to LOOK-BACK
+
+ DJNZ <A href="#L0927">L0927</a> ; to LOOK-BACK
+
+ INC HL ;
+ CPIR ;
+ DEC HL ;
+ LD ($400E),HL ; sv DF_CC_lo
+ SCF ; Set Carry Flag
+ RET PO ;
+
+ DEC D ;
+ RET Z ;
+
+ PUSH BC ;
+ CALL <A href="#L099E">L099E</a> ; routine MAKE-ROOM
+ POP BC ;
+ LD B,C ;
+ LD H,D ;
+ LD L,E ;
+
+<a name="L0940"></a>;; <b>EXPAND-2</b>
+L0940: LD (HL),$00 ;
+ DEC HL ;
+ DJNZ <A href="#L0940">L0940</a> ; to EXPAND-2
+
+ EX DE,HL ;
+ INC HL ;
+ LD ($400E),HL ; sv DF_CC_lo
+ RET ;
+
+; ------------------------------
+; THE <b><font color=#333388>'EXPAND TOKENS'</font></b> SUBROUTINE
+; ------------------------------
+;
+;
+
+<a name="L094B"></a>;; <b>TOKENS</b>
+L094B: PUSH AF ;
+ CALL <A href="#L0975">L0975</a> ; routine TOKEN-ADD
+ JR NC,<A href="#L0959">L0959</a> ; to ALL-CHARS
+
+ BIT 0,(IY+$01) ; sv FLAGS - Leading space if set
+ JR NZ,<A href="#L0959">L0959</a> ; to ALL-CHARS
+
+ XOR A ;
+
+ RST 10H ; PRINT-A
+
+<a name="L0959"></a>;; <b>ALL-CHARS</b>
+L0959: LD A,(BC) ;
+ AND $3F ;
+
+ RST 10H ; PRINT-A
+ LD A,(BC) ;
+ INC BC ;
+ ADD A,A ;
+ JR NC,<A href="#L0959">L0959</a> ; to ALL-CHARS
+
+ POP BC ;
+ BIT 7,B ;
+ RET Z ;
+
+ CP $1A ;
+ JR Z,<A href="#L096D">L096D</a> ; to TRAIL-SP
+
+ CP $38 ;
+ RET C ;
+
+<a name="L096D"></a>;; <b>TRAIL-SP</b>
+L096D: XOR A ;
+ SET 0,(IY+$01) ; sv FLAGS - Suppress leading space
+ JP <A href="#L07F5">L07F5</a> ; to PRINT-SP
+
+; ---
+
+<a name="L0975"></a>;; <b>TOKEN-ADD</b>
+L0975: PUSH HL ;
+ LD HL,<A href="#L0111">L0111</a> ; Address of TOKENS
+ BIT 7,A ;
+ JR Z,<A href="#L097F">L097F</a> ; to TEST-HIGH
+
+ AND $3F ;
+
+<a name="L097F"></a>;; <b>TEST-HIGH</b>
+L097F: CP $43 ;
+ JR NC,<A href="#L0993">L0993</a> ; to FOUND
+
+ LD B,A ;
+ INC B ;
+
+<a name="L0985"></a>;; <b>WORDS</b>
+L0985: BIT 7,(HL) ;
+ INC HL ;
+ JR Z,<A href="#L0985">L0985</a> ; to WORDS
+
+ DJNZ <A href="#L0985">L0985</a> ; to WORDS
+
+ BIT 6,A ;
+ JR NZ,<A href="#L0992">L0992</a> ; to COMP-FLAG
+
+ CP $18 ;
+
+<a name="L0992"></a>;; <b>COMP-FLAG</b>
+L0992: CCF ; Complement Carry Flag
+
+<a name="L0993"></a>;; <b>FOUND</b>
+L0993: LD B,H ;
+ LD C,L ;
+ POP HL ;
+ RET NC ;
+
+ LD A,(BC) ;
+ ADD A,$E4 ;
+ RET ;
+
+; --------------------------
+; THE <b><font color=#333388>'ONE SPACE'</font></b> SUBROUTINE
+; --------------------------
+;
+;
+
+<a name="L099B"></a>;; <b>ONE-SPACE</b>
+L099B: LD BC,$0001 ;
+
+; --------------------------
+; THE <b><font color=#333388>'MAKE ROOM'</font></b> SUBROUTINE
+; --------------------------
+;
+;
+
+<a name="L099E"></a>;; <b>MAKE-ROOM</b>
+L099E: PUSH HL ;
+ CALL <A href="#L0EC5">L0EC5</a> ; routine TEST-ROOM
+ POP HL ;
+ CALL <A href="#L09AD">L09AD</a> ; routine POINTERS
+ LD HL,($401C) ; sv STKEND_lo
+ EX DE,HL ;
+ LDDR ; Copy Bytes
+ RET ;
+
+; -------------------------
+; THE <b><font color=#333388>'POINTERS'</font></b> SUBROUTINE
+; -------------------------
+;
+;
+
+<a name="L09AD"></a>;; <b>POINTERS</b>
+L09AD: PUSH AF ;
+ PUSH HL ;
+ LD HL,$400C ; sv D_FILE_lo
+ LD A,$09 ;
+
+<a name="L09B4"></a>;; <b>NEXT-PTR</b>
+L09B4: LD E,(HL) ;
+ INC HL ;
+ LD D,(HL) ;
+ EX (SP),HL ;
+ AND A ;
+ SBC HL,DE ;
+ ADD HL,DE ;
+ EX (SP),HL ;
+ JR NC,<A href="#L09C8">L09C8</a> ; to PTR-DONE
+
+ PUSH DE ;
+ EX DE,HL ;
+ ADD HL,BC ;
+ EX DE,HL ;
+ LD (HL),D ;
+ DEC HL ;
+ LD (HL),E ;
+ INC HL ;
+ POP DE ;
+
+<a name="L09C8"></a>;; <b>PTR-DONE</b>
+L09C8: INC HL ;
+ DEC A ;
+ JR NZ,<A href="#L09B4">L09B4</a> ; to NEXT-PTR
+
+ EX DE,HL ;
+ POP DE ;
+ POP AF ;
+ AND A ;
+ SBC HL,DE ;
+ LD B,H ;
+ LD C,L ;
+ INC BC ;
+ ADD HL,DE ;
+ EX DE,HL ;
+ RET ;
+
+; -----------------------------
+; THE <b><font color=#333388>'LINE ADDRESS'</font></b> SUBROUTINE
+; -----------------------------
+;
+;
+
+<a name="L09D8"></a>;; <b>LINE-ADDR</b>
+L09D8: PUSH HL ;
+ LD HL,$407D ;
+ LD D,H ;
+ LD E,L ;
+
+<a name="L09DE"></a>;; <b>NEXT-TEST</b>
+L09DE: POP BC ;
+ CALL <A href="#L09EA">L09EA</a> ; routine CP-LINES
+ RET NC ;
+
+ PUSH BC ;
+ CALL <A href="#L09F2">L09F2</a> ; routine NEXT-ONE
+ EX DE,HL ;
+ JR <A href="#L09DE">L09DE</a> ; to NEXT-TEST
+
+; -------------------------------------
+; THE <b><font color=#333388>'COMPARE LINE NUMBERS'</font></b> SUBROUTINE
+; -------------------------------------
+;
+;
+
+<a name="L09EA"></a>;; <b>CP-LINES</b>
+L09EA: LD A,(HL) ;
+ CP B ;
+ RET NZ ;
+
+ INC HL ;
+ LD A,(HL) ;
+ DEC HL ;
+ CP C ;
+ RET ;
+
+; --------------------------------------
+; THE <b><font color=#333388>'NEXT LINE OR VARIABLE'</font></b> SUBROUTINE
+; --------------------------------------
+;
+;
+
+<a name="L09F2"></a>;; <b>NEXT-ONE</b>
+L09F2: PUSH HL ;
+ LD A,(HL) ;
+ CP $40 ;
+ JR C,<A href="#L0A0F">L0A0F</a> ; to LINES
+
+ BIT 5,A ;
+ JR Z,<A href="#L0A10">L0A10</a> ; forward to NEXT-O-4
+
+ ADD A,A ;
+ JP M,<A href="#L0A01">L0A01</a> ; to NEXT+FIVE
+
+ CCF ; Complement Carry Flag
+
+<a name="L0A01"></a>;; <b>NEXT+FIVE</b>
+L0A01: LD BC,$0005 ;
+ JR NC,<A href="#L0A08">L0A08</a> ; to NEXT-LETT
+
+ LD C,$11 ;
+
+<a name="L0A08"></a>;; <b>NEXT-LETT</b>
+L0A08: RLA ;
+ INC HL ;
+ LD A,(HL) ;
+ JR NC,<A href="#L0A08">L0A08</a> ; to NEXT-LETT
+
+ JR <A href="#L0A15">L0A15</a> ; to NEXT-ADD
+
+; ---
+
+<a name="L0A0F"></a>;; <b>LINES</b>
+L0A0F: INC HL ;
+
+<a name="L0A10"></a>;; <b>NEXT-O-4</b>
+L0A10: INC HL ;
+ LD C,(HL) ;
+ INC HL ;
+ LD B,(HL) ;
+ INC HL ;
+
+<a name="L0A15"></a>;; <b>NEXT-ADD</b>
+L0A15: ADD HL,BC ;
+ POP DE ;
+
+; ---------------------------
+; THE <b><font color=#333388>'DIFFERENCE'</font></b> SUBROUTINE
+; ---------------------------
+;
+;
+
+<a name="L0A17"></a>;; <b>DIFFER</b>
+L0A17: AND A ;
+ SBC HL,DE ;
+ LD B,H ;
+ LD C,L ;
+ ADD HL,DE ;
+ EX DE,HL ;
+ RET ;
+
+; --------------------------
+; THE <b><font color=#333388>'LINE-ENDS'</font></b> SUBROUTINE
+; --------------------------
+;
+;
+
+<a name="L0A1F"></a>;; <b>LINE-ENDS</b>
+L0A1F: LD B,(IY+$22) ; sv DF_SZ
+ PUSH BC ;
+ CALL <A href="#L0A2C">L0A2C</a> ; routine B-LINES
+ POP BC ;
+ DEC B ;
+ JR <A href="#L0A2C">L0A2C</a> ; to B-LINES
+
+; -------------------------
+; THE <b><font color=#333388>'CLS'</font></b> COMMAND ROUTINE
+; -------------------------
+;
+;
+
+<a name="L0A2A"></a>;; <b>CLS</b>
+L0A2A: LD B,$18 ;
+
+<a name="L0A2C"></a>;; <b>B-LINES</b>
+L0A2C: RES 1,(IY+$01) ; sv FLAGS - Signal printer not in use
+ LD C,$21 ;
+ PUSH BC ;
+ CALL <A href="#L0918">L0918</a> ; routine LOC-ADDR
+ POP BC ;
+ LD A,($4005) ; sv RAMTOP_hi
+ CP $4D ;
+ JR C,<A href="#L0A52">L0A52</a> ; to COLLAPSED
+
+ SET 7,(IY+$3A) ; sv S_POSN_y
+
+<a name="L0A42"></a>;; <b>CLEAR-LOC</b>
+L0A42: XOR A ; prepare a space
+ CALL <A href="#L07F5">L07F5</a> ; routine PRINT-SP prints a space
+ LD HL,($4039) ; sv S_POSN_x
+ LD A,L ;
+ OR H ;
+ AND $7E ;
+ JR NZ,<A href="#L0A42">L0A42</a> ; to CLEAR-LOC
+
+ JP <A href="#L0918">L0918</a> ; to LOC-ADDR
+
+; ---
+
+<a name="L0A52"></a>;; <b>COLLAPSED</b>
+L0A52: LD D,H ;
+ LD E,L ;
+ DEC HL ;
+ LD C,B ;
+ LD B,$00 ;
+ LDIR ; Copy Bytes
+ LD HL,($4010) ; sv VARS_lo
+
+; ----------------------------
+; THE <b><font color=#333388>'RECLAIMING'</font></b> SUBROUTINES
+; ----------------------------
+;
+;
+
+<a name="L0A5D"></a>;; <b>RECLAIM-1</b>
+L0A5D: CALL <A href="#L0A17">L0A17</a> ; routine DIFFER
+
+<a name="L0A60"></a>;; <b>RECLAIM-2</b>
+L0A60: PUSH BC ;
+ LD A,B ;
+ CPL ;
+ LD B,A ;
+ LD A,C ;
+ CPL ;
+ LD C,A ;
+ INC BC ;
+ CALL <A href="#L09AD">L09AD</a> ; routine POINTERS
+ EX DE,HL ;
+ POP HL ;
+ ADD HL,DE ;
+ PUSH DE ;
+ LDIR ; Copy Bytes
+ POP HL ;
+ RET ;
+
+; ------------------------------
+; THE <b><font color=#333388>'E-LINE NUMBER'</font></b> SUBROUTINE
+; ------------------------------
+;
+;
+
+<a name="L0A73"></a>;; <b>E-LINE-NO</b>
+L0A73: LD HL,($4014) ; sv E_LINE_lo
+ CALL <A href="#L004D">L004D</a> ; routine TEMP-PTR-2
+
+ RST 18H ; GET-CHAR
+ BIT 5,(IY+$2D) ; sv FLAGX
+ RET NZ ;
+
+ LD HL,$405D ; sv MEM-0-1st
+ LD ($401C),HL ; sv STKEND_lo
+ CALL <A href="#L1548">L1548</a> ; routine INT-TO-FP
+ CALL <A href="#L158A">L158A</a> ; routine FP-TO-BC
+ JR C,<A href="#L0A91">L0A91</a> ; to NO-NUMBER
+
+ LD HL,$D8F0 ; value '-10000'
+ ADD HL,BC ;
+
+<a name="L0A91"></a>;; <b>NO-NUMBER</b>
+L0A91: JP C,<A href="#L0D9A">L0D9A</a> ; to REPORT-C
+
+ CP A ;
+ JP <A href="#L14BC">L14BC</a> ; routine SET-MIN
+
+; -------------------------------------------------
+; THE <b><font color=#333388>'REPORT AND LINE NUMBER'</font></b> PRINTING SUBROUTINES
+; -------------------------------------------------
+;
+;
+
+<a name="L0A98"></a>;; <b>OUT-NUM</b>
+L0A98: PUSH DE ;
+ PUSH HL ;
+ XOR A ;
+ BIT 7,B ;
+ JR NZ,<A href="#L0ABF">L0ABF</a> ; to UNITS
+
+ LD H,B ;
+ LD L,C ;
+ LD E,$FF ;
+ JR <A href="#L0AAD">L0AAD</a> ; to THOUSAND
+
+; ---
+
+<a name="L0AA5"></a>;; <b>OUT-NO</b>
+L0AA5: PUSH DE ;
+ LD D,(HL) ;
+ INC HL ;
+ LD E,(HL) ;
+ PUSH HL ;
+ EX DE,HL ;
+ LD E,$00 ; set E to leading space.
+
+<a name="L0AAD"></a>;; <b>THOUSAND</b>
+L0AAD: LD BC,$FC18 ;
+ CALL <A href="#L07E1">L07E1</a> ; routine OUT-DIGIT
+ LD BC,$FF9C ;
+ CALL <A href="#L07E1">L07E1</a> ; routine OUT-DIGIT
+ LD C,$F6 ;
+ CALL <A href="#L07E1">L07E1</a> ; routine OUT-DIGIT
+ LD A,L ;
+
+<a name="L0ABF"></a>;; <b>UNITS</b>
+L0ABF: CALL <A href="#L07EB">L07EB</a> ; routine OUT-CODE
+ POP HL ;
+ POP DE ;
+ RET ;
+
+; --------------------------
+; THE <b><font color=#333388>'UNSTACK-Z'</font></b> SUBROUTINE
+; --------------------------
+; This subroutine is used to return early from a routine when checking syntax.
+; On the ZX81 the same routines that execute commands also check the syntax
+; on line entry. This enables precise placement of the error marker in a line
+; that fails syntax.
+; The sequence CALL SYNTAX-Z ; RET Z can be replaced by a call to this routine
+; although it has not replaced every occurrence of the above two instructions.
+; Even on the ZX-80 this routine was not fully utilized.
+
+<a name="L0AC5"></a>;; <b>UNSTACK-Z</b>
+L0AC5: CALL <A href="#L0DA6">L0DA6</a> ; routine SYNTAX-Z resets the ZERO flag if
+ ; checking syntax.
+ POP HL ; drop the return address.
+ RET Z ; return to previous calling routine if
+ ; checking syntax.
+
+ JP (HL) ; else jump to the continuation address in
+ ; the calling routine as RET would have done.
+
+; ----------------------------
+; THE <b><font color=#333388>'LPRINT'</font></b> COMMAND ROUTINE
+; ----------------------------
+;
+;
+
+<a name="L0ACB"></a>;; <b>LPRINT</b>
+L0ACB: SET 1,(IY+$01) ; sv FLAGS - Signal printer in use
+
+; ---------------------------
+; THE <b><font color=#333388>'PRINT'</font></b> COMMAND ROUTINE
+; ---------------------------
+;
+;
+
+<a name="L0ACF"></a>;; <b>PRINT</b>
+L0ACF: LD A,(HL) ;
+ CP $76 ;
+ JP Z,<A href="#L0B84">L0B84</a> ; to PRINT-END
+
+<a name="L0AD5"></a>;; <b>PRINT-1</b>
+L0AD5: SUB $1A ;
+ ADC A,$00 ;
+ JR Z,<A href="#L0B44">L0B44</a> ; to SPACING
+
+ CP $A7 ;
+ JR NZ,<A href="#L0AFA">L0AFA</a> ; to NOT-AT
+
+
+ RST 20H ; NEXT-CHAR
+ CALL <A href="#L0D92">L0D92</a> ; routine CLASS-6
+ CP $1A ;
+ JP NZ,<A href="#L0D9A">L0D9A</a> ; to REPORT-C
+
+
+ RST 20H ; NEXT-CHAR
+ CALL <A href="#L0D92">L0D92</a> ; routine CLASS-6
+ CALL <A href="#L0B4E">L0B4E</a> ; routine SYNTAX-ON
+
+ RST 28H ;; FP-CALC
+ DEFB $01 ;;exchange
+ DEFB $34 ;;end-calc
+
+ CALL <A href="#L0BF5">L0BF5</a> ; routine STK-TO-BC
+ CALL <A href="#L08F5">L08F5</a> ; routine PRINT-AT
+ JR <A href="#L0B37">L0B37</a> ; to PRINT-ON
+
+; ---
+
+<a name="L0AFA"></a>;; <b>NOT-AT</b>
+L0AFA: CP $A8 ;
+ JR NZ,<A href="#L0B31">L0B31</a> ; to NOT-TAB
+
+
+ RST 20H ; NEXT-CHAR
+ CALL <A href="#L0D92">L0D92</a> ; routine CLASS-6
+ CALL <A href="#L0B4E">L0B4E</a> ; routine SYNTAX-ON
+ CALL <A href="#L0C02">L0C02</a> ; routine STK-TO-A
+ JP NZ,<A href="#L0EAD">L0EAD</a> ; to REPORT-B
+
+ AND $1F ;
+ LD C,A ;
+ BIT 1,(IY+$01) ; sv FLAGS - Is printer in use
+ JR Z,<A href="#L0B1E">L0B1E</a> ; to TAB-TEST
+
+ SUB (IY+$38) ; sv PR_CC
+ SET 7,A ;
+ ADD A,$3C ;
+ CALL NC,<A href="#L0871">L0871</a> ; routine COPY-BUFF
+
+<a name="L0B1E"></a>;; <b>TAB-TEST</b>
+L0B1E: ADD A,(IY+$39) ; sv S_POSN_x
+ CP $21 ;
+ LD A,($403A) ; sv S_POSN_y
+ SBC A,$01 ;
+ CALL <A href="#L08FA">L08FA</a> ; routine TEST-VAL
+ SET 0,(IY+$01) ; sv FLAGS - Suppress leading space
+ JR <A href="#L0B37">L0B37</a> ; to PRINT-ON
+
+; ---
+
+<a name="L0B31"></a>;; <b>NOT-TAB</b>
+L0B31: CALL <A href="#L0F55">L0F55</a> ; routine SCANNING
+ CALL <A href="#L0B55">L0B55</a> ; routine PRINT-STK
+
+<a name="L0B37"></a>;; <b>PRINT-ON</b>
+L0B37: RST 18H ; GET-CHAR
+ SUB $1A ;
+ ADC A,$00 ;
+ JR Z,<A href="#L0B44">L0B44</a> ; to SPACING
+
+ CALL <A href="#L0D1D">L0D1D</a> ; routine CHECK-END
+ JP <A href="#L0B84">L0B84</a> ;;; to PRINT-END
+
+; ---
+
+<a name="L0B44"></a>;; <b>SPACING</b>
+L0B44: CALL NC,<A href="#L0B8B">L0B8B</a> ; routine FIELD
+
+ RST 20H ; NEXT-CHAR
+ CP $76 ;
+ RET Z ;
+
+ JP <A href="#L0AD5">L0AD5</a> ;;; to PRINT-1
+
+; ---
+
+<a name="L0B4E"></a>;; <b>SYNTAX-ON</b>
+L0B4E: CALL <A href="#L0DA6">L0DA6</a> ; routine SYNTAX-Z
+ RET NZ ;
+
+ POP HL ;
+ JR <A href="#L0B37">L0B37</a> ; to PRINT-ON
+
+; ---
+
+<a name="L0B55"></a>;; <b>PRINT-STK</b>
+L0B55: CALL <A href="#L0AC5">L0AC5</a> ; routine UNSTACK-Z
+ BIT 6,(IY+$01) ; sv FLAGS - Numeric or string result?
+ CALL Z,<A href="#L13F8">L13F8</a> ; routine STK-FETCH
+ JR Z,<A href="#L0B6B">L0B6B</a> ; to PR-STR-4
+
+ JP <A href="#L15DB">L15DB</a> ; jump forward to PRINT-FP
+
+; ---
+
+<a name="L0B64"></a>;; <b>PR-STR-1</b>
+L0B64: LD A,$0B ;
+
+<a name="L0B66"></a>;; <b>PR-STR-2</b>
+L0B66: RST 10H ; PRINT-A
+
+<a name="L0B67"></a>;; <b>PR-STR-3</b>
+L0B67: LD DE,($4018) ; sv X_PTR_lo
+
+<a name="L0B6B"></a>;; <b>PR-STR-4</b>
+L0B6B: LD A,B ;
+ OR C ;
+ DEC BC ;
+ RET Z ;
+
+ LD A,(DE) ;
+ INC DE ;
+ LD ($4018),DE ; sv X_PTR_lo
+ BIT 6,A ;
+ JR Z,<A href="#L0B66">L0B66</a> ; to PR-STR-2
+
+ CP $C0 ;
+ JR Z,<A href="#L0B64">L0B64</a> ; to PR-STR-1
+
+ PUSH BC ;
+ CALL <A href="#L094B">L094B</a> ; routine TOKENS
+ POP BC ;
+ JR <A href="#L0B67">L0B67</a> ; to PR-STR-3
+
+; ---
+
+<a name="L0B84"></a>;; <b>PRINT-END</b>
+L0B84: CALL <A href="#L0AC5">L0AC5</a> ; routine UNSTACK-Z
+ LD A,$76 ;
+
+ RST 10H ; PRINT-A
+ RET ;
+
+; ---
+
+<a name="L0B8B"></a>;; <b>FIELD</b>
+L0B8B: CALL <A href="#L0AC5">L0AC5</a> ; routine UNSTACK-Z
+ SET 0,(IY+$01) ; sv FLAGS - Suppress leading space
+ XOR A ;
+
+ RST 10H ; PRINT-A
+ LD BC,($4039) ; sv S_POSN_x
+ LD A,C ;
+ BIT 1,(IY+$01) ; sv FLAGS - Is printer in use
+ JR Z,<A href="#L0BA4">L0BA4</a> ; to CENTRE
+
+ LD A,$5D ;
+ SUB (IY+$38) ; sv PR_CC
+
+<a name="L0BA4"></a>;; <b>CENTRE</b>
+L0BA4: LD C,$11 ;
+ CP C ;
+ JR NC,<A href="#L0BAB">L0BAB</a> ; to RIGHT
+
+ LD C,$01 ;
+
+<a name="L0BAB"></a>;; <b>RIGHT</b>
+L0BAB: CALL <A href="#L090B">L090B</a> ; routine SET-FIELD
+ RET ;
+
+; --------------------------------------
+; THE <b><font color=#333388>'PLOT AND UNPLOT'</font></b> COMMAND ROUTINES
+; --------------------------------------
+;
+;
+
+<a name="L0BAF"></a>;; <b>PLOT/UNP</b>
+L0BAF: CALL <A href="#L0BF5">L0BF5</a> ; routine STK-TO-BC
+ LD ($4036),BC ; sv COORDS_x
+ LD A,$2B ;
+ SUB B ;
+ JP C,<A href="#L0EAD">L0EAD</a> ; to REPORT-B
+
+ LD B,A ;
+ LD A,$01 ;
+ SRA B ;
+ JR NC,<A href="#L0BC5">L0BC5</a> ; to COLUMNS
+
+ LD A,$04 ;
+
+<a name="L0BC5"></a>;; <b>COLUMNS</b>
+L0BC5: SRA C ;
+ JR NC,<A href="#L0BCA">L0BCA</a> ; to FIND-ADDR
+
+ RLCA ;
+
+<a name="L0BCA"></a>;; <b>FIND-ADDR</b>
+L0BCA: PUSH AF ;
+ CALL <A href="#L08F5">L08F5</a> ; routine PRINT-AT
+ LD A,(HL) ;
+ RLCA ;
+ CP $10 ;
+ JR NC,<A href="#L0BDA">L0BDA</a> ; to TABLE-PTR
+
+ RRCA ;
+ JR NC,<A href="#L0BD9">L0BD9</a> ; to SQ-SAVED
+
+ XOR $8F ;
+
+<a name="L0BD9"></a>;; <b>SQ-SAVED</b>
+L0BD9: LD B,A ;
+
+<a name="L0BDA"></a>;; <b>TABLE-PTR</b>
+L0BDA: LD DE,<A href="#L0C9E">L0C9E</a> ; Address: P-UNPLOT
+ LD A,($4030) ; sv T_ADDR_lo
+ SUB E ;
+ JP M,<A href="#L0BE9">L0BE9</a> ; to PLOT
+
+ POP AF ;
+ CPL ;
+ AND B ;
+ JR <A href="#L0BEB">L0BEB</a> ; to UNPLOT
+
+; ---
+
+<a name="L0BE9"></a>;; <b>PLOT</b>
+L0BE9: POP AF ;
+ OR B ;
+
+<a name="L0BEB"></a>;; <b>UNPLOT</b>
+L0BEB: CP $08 ;
+ JR C,<A href="#L0BF1">L0BF1</a> ; to PLOT-END
+
+ XOR $8F ;
+
+<a name="L0BF1"></a>;; <b>PLOT-END</b>
+L0BF1: EXX ;
+
+ RST 10H ; PRINT-A
+ EXX ;
+ RET ;
+
+; ----------------------------
+; THE <b><font color=#333388>'STACK-TO-BC'</font></b> SUBROUTINE
+; ----------------------------
+;
+;
+
+<a name="L0BF5"></a>;; <b>STK-TO-BC</b>
+L0BF5: CALL <A href="#L0C02">L0C02</a> ; routine STK-TO-A
+ LD B,A ;
+ PUSH BC ;
+ CALL <A href="#L0C02">L0C02</a> ; routine STK-TO-A
+ LD E,C ;
+ POP BC ;
+ LD D,C ;
+ LD C,A ;
+ RET ;
+
+; ---------------------------
+; THE <b><font color=#333388>'STACK-TO-A'</font></b> SUBROUTINE
+; ---------------------------
+;
+;
+
+<a name="L0C02"></a>;; <b>STK-TO-A</b>
+L0C02: CALL <A href="#L15CD">L15CD</a> ; routine FP-TO-A
+ JP C,<A href="#L0EAD">L0EAD</a> ; to REPORT-B
+
+ LD C,$01 ;
+ RET Z ;
+
+ LD C,$FF ;
+ RET ;
+
+; -----------------------
+; THE <b><font color=#333388>'SCROLL'</font></b> SUBROUTINE
+; -----------------------
+;
+;
+
+<a name="L0C0E"></a>;; <b>SCROLL</b>
+L0C0E: LD B,(IY+$22) ; sv DF_SZ
+ LD C,$21 ;
+ CALL <A href="#L0918">L0918</a> ; routine LOC-ADDR
+ CALL <A href="#L099B">L099B</a> ; routine ONE-SPACE
+ LD A,(HL) ;
+ LD (DE),A ;
+ INC (IY+$3A) ; sv S_POSN_y
+ LD HL,($400C) ; sv D_FILE_lo
+ INC HL ;
+ LD D,H ;
+ LD E,L ;
+ CPIR ;
+ JP <A href="#L0A5D">L0A5D</a> ; to RECLAIM-1
+
+; -------------------
+; THE <b><font color=#333388>'SYNTAX'</font></b> TABLES
+; -------------------
+
+; i) The Offset table
+
+<a name="L0C29"></a>;; <b>offset-t</b>
+L0C29: DEFB <A href="#L0CB4">L0CB4</a> - $ ; 8B offset to; Address: P-LPRINT
+ DEFB <A href="#L0CB7">L0CB7</a> - $ ; 8D offset to; Address: P-LLIST
+ DEFB <A href="#L0C58">L0C58</a> - $ ; 2D offset to; Address: P-STOP
+ DEFB <A href="#L0CAB">L0CAB</a> - $ ; 7F offset to; Address: P-SLOW
+ DEFB <A href="#L0CAE">L0CAE</a> - $ ; 81 offset to; Address: P-FAST
+ DEFB <A href="#L0C77">L0C77</a> - $ ; 49 offset to; Address: P-NEW
+ DEFB <A href="#L0CA4">L0CA4</a> - $ ; 75 offset to; Address: P-SCROLL
+ DEFB <A href="#L0C8F">L0C8F</a> - $ ; 5F offset to; Address: P-CONT
+ DEFB <A href="#L0C71">L0C71</a> - $ ; 40 offset to; Address: P-DIM
+ DEFB <A href="#L0C74">L0C74</a> - $ ; 42 offset to; Address: P-REM
+ DEFB <A href="#L0C5E">L0C5E</a> - $ ; 2B offset to; Address: P-FOR
+ DEFB <A href="#L0C4B">L0C4B</a> - $ ; 17 offset to; Address: P-GOTO
+ DEFB <A href="#L0C54">L0C54</a> - $ ; 1F offset to; Address: P-GOSUB
+ DEFB <A href="#L0C6D">L0C6D</a> - $ ; 37 offset to; Address: P-INPUT
+ DEFB <A href="#L0C89">L0C89</a> - $ ; 52 offset to; Address: P-LOAD
+ DEFB <A href="#L0C7D">L0C7D</a> - $ ; 45 offset to; Address: P-LIST
+ DEFB <A href="#L0C48">L0C48</a> - $ ; 0F offset to; Address: P-LET
+ DEFB <A href="#L0CA7">L0CA7</a> - $ ; 6D offset to; Address: P-PAUSE
+ DEFB <A href="#L0C66">L0C66</a> - $ ; 2B offset to; Address: P-NEXT
+ DEFB <A href="#L0C80">L0C80</a> - $ ; 44 offset to; Address: P-POKE
+ DEFB <A href="#L0C6A">L0C6A</a> - $ ; 2D offset to; Address: P-PRINT
+ DEFB <A href="#L0C98">L0C98</a> - $ ; 5A offset to; Address: P-PLOT
+ DEFB <A href="#L0C7A">L0C7A</a> - $ ; 3B offset to; Address: P-RUN
+ DEFB <A href="#L0C8C">L0C8C</a> - $ ; 4C offset to; Address: P-SAVE
+ DEFB <A href="#L0C86">L0C86</a> - $ ; 45 offset to; Address: P-RAND
+ DEFB <A href="#L0C4F">L0C4F</a> - $ ; 0D offset to; Address: P-IF
+ DEFB <A href="#L0C95">L0C95</a> - $ ; 52 offset to; Address: P-CLS
+ DEFB <A href="#L0C9E">L0C9E</a> - $ ; 5A offset to; Address: P-UNPLOT
+ DEFB <A href="#L0C92">L0C92</a> - $ ; 4D offset to; Address: P-CLEAR
+ DEFB <A href="#L0C5B">L0C5B</a> - $ ; 15 offset to; Address: P-RETURN
+ DEFB <A href="#L0CB1">L0CB1</a> - $ ; 6A offset to; Address: P-COPY
+
+; ii) The parameter table.
+
+
+<a name="L0C48"></a>;; <b>P-LET</b>
+L0C48: DEFB $01 ; Class-01 - A variable is required.
+ DEFB $14 ; Separator: '='
+ DEFB $02 ; Class-02 - An expression, numeric or string,
+ ; must follow.
+
+<a name="L0C4B"></a>;; <b>P-GOTO</b>
+L0C4B: DEFB $06 ; Class-06 - A numeric expression must follow.
+ DEFB $00 ; Class-00 - No further operands.
+ DEFW <A href="#L0E81">L0E81</a> ; Address: $0E81; Address: GOTO
+
+<a name="L0C4F"></a>;; <b>P-IF</b>
+L0C4F: DEFB $06 ; Class-06 - A numeric expression must follow.
+ DEFB $DE ; Separator: 'THEN'
+ DEFB $05 ; Class-05 - Variable syntax checked entirely
+ ; by routine.
+ DEFW <A href="#L0DAB">L0DAB</a> ; Address: $0DAB; Address: IF
+
+<a name="L0C54"></a>;; <b>P-GOSUB</b>
+L0C54: DEFB $06 ; Class-06 - A numeric expression must follow.
+ DEFB $00 ; Class-00 - No further operands.
+ DEFW <A href="#L0EB5">L0EB5</a> ; Address: $0EB5; Address: GOSUB
+
+<a name="L0C58"></a>;; <b>P-STOP</b>
+L0C58: DEFB $00 ; Class-00 - No further operands.
+ DEFW <A href="#L0CDC">L0CDC</a> ; Address: $0CDC; Address: STOP
+
+<a name="L0C5B"></a>;; <b>P-RETURN</b>
+L0C5B: DEFB $00 ; Class-00 - No further operands.
+ DEFW <A href="#L0ED8">L0ED8</a> ; Address: $0ED8; Address: RETURN
+
+<a name="L0C5E"></a>;; <b>P-FOR</b>
+L0C5E: DEFB $04 ; Class-04 - A single character variable must
+ ; follow.
+ DEFB $14 ; Separator: '='
+ DEFB $06 ; Class-06 - A numeric expression must follow.
+ DEFB $DF ; Separator: 'TO'
+ DEFB $06 ; Class-06 - A numeric expression must follow.
+ DEFB $05 ; Class-05 - Variable syntax checked entirely
+ ; by routine.
+ DEFW <A href="#L0DB9">L0DB9</a> ; Address: $0DB9; Address: FOR
+
+<a name="L0C66"></a>;; <b>P-NEXT</b>
+L0C66: DEFB $04 ; Class-04 - A single character variable must
+ ; follow.
+ DEFB $00 ; Class-00 - No further operands.
+ DEFW <A href="#L0E2E">L0E2E</a> ; Address: $0E2E; Address: NEXT
+
+<a name="L0C6A"></a>;; <b>P-PRINT</b>
+L0C6A: DEFB $05 ; Class-05 - Variable syntax checked entirely
+ ; by routine.
+ DEFW <A href="#L0ACF">L0ACF</a> ; Address: $0ACF; Address: PRINT
+
+<a name="L0C6D"></a>;; <b>P-INPUT</b>
+L0C6D: DEFB $01 ; Class-01 - A variable is required.
+ DEFB $00 ; Class-00 - No further operands.
+ DEFW <A href="#L0EE9">L0EE9</a> ; Address: $0EE9; Address: INPUT
+
+<a name="L0C71"></a>;; <b>P-DIM</b>
+L0C71: DEFB $05 ; Class-05 - Variable syntax checked entirely
+ ; by routine.
+ DEFW <A href="#L1409">L1409</a> ; Address: $1409; Address: DIM
+
+<a name="L0C74"></a>;; <b>P-REM</b>
+L0C74: DEFB $05 ; Class-05 - Variable syntax checked entirely
+ ; by routine.
+ DEFW <A href="#L0D6A">L0D6A</a> ; Address: $0D6A; Address: REM
+
+<a name="L0C77"></a>;; <b>P-NEW</b>
+L0C77: DEFB $00 ; Class-00 - No further operands.
+ DEFW <A href="#L03C3">L03C3</a> ; Address: $03C3; Address: NEW
+
+<a name="L0C7A"></a>;; <b>P-RUN</b>
+L0C7A: DEFB $03 ; Class-03 - A numeric expression may follow
+ ; else default to zero.
+ DEFW <A href="#L0EAF">L0EAF</a> ; Address: $0EAF; Address: RUN
+
+<a name="L0C7D"></a>;; <b>P-LIST</b>
+L0C7D: DEFB $03 ; Class-03 - A numeric expression may follow
+ ; else default to zero.
+ DEFW <A href="#L0730">L0730</a> ; Address: $0730; Address: LIST
+
+<a name="L0C80"></a>;; <b>P-POKE</b>
+L0C80: DEFB $06 ; Class-06 - A numeric expression must follow.
+ DEFB $1A ; Separator: ','
+ DEFB $06 ; Class-06 - A numeric expression must follow.
+ DEFB $00 ; Class-00 - No further operands.
+ DEFW <A href="#L0E92">L0E92</a> ; Address: $0E92; Address: POKE
+
+<a name="L0C86"></a>;; <b>P-RAND</b>
+L0C86: DEFB $03 ; Class-03 - A numeric expression may follow
+ ; else default to zero.
+ DEFW <A href="#L0E6C">L0E6C</a> ; Address: $0E6C; Address: RAND
+
+<a name="L0C89"></a>;; <b>P-LOAD</b>
+L0C89: DEFB $05 ; Class-05 - Variable syntax checked entirely
+ ; by routine.
+ DEFW <A href="#L0340">L0340</a> ; Address: $0340; Address: LOAD
+
+<a name="L0C8C"></a>;; <b>P-SAVE</b>
+L0C8C: DEFB $05 ; Class-05 - Variable syntax checked entirely
+ ; by routine.
+ DEFW <A href="#L02F6">L02F6</a> ; Address: $02F6; Address: SAVE
+
+<a name="L0C8F"></a>;; <b>P-CONT</b>
+L0C8F: DEFB $00 ; Class-00 - No further operands.
+ DEFW <A href="#L0E7C">L0E7C</a> ; Address: $0E7C; Address: CONT
+
+<a name="L0C92"></a>;; <b>P-CLEAR</b>
+L0C92: DEFB $00 ; Class-00 - No further operands.
+ DEFW <A href="#L149A">L149A</a> ; Address: $149A; Address: CLEAR
+
+<a name="L0C95"></a>;; <b>P-CLS</b>
+L0C95: DEFB $00 ; Class-00 - No further operands.
+ DEFW <A href="#L0A2A">L0A2A</a> ; Address: $0A2A; Address: CLS
+
+<a name="L0C98"></a>;; <b>P-PLOT</b>
+L0C98: DEFB $06 ; Class-06 - A numeric expression must follow.
+ DEFB $1A ; Separator: ','
+ DEFB $06 ; Class-06 - A numeric expression must follow.
+ DEFB $00 ; Class-00 - No further operands.
+ DEFW <A href="#L0BAF">L0BAF</a> ; Address: $0BAF; Address: PLOT/UNP
+
+<a name="L0C9E"></a>;; <b>P-UNPLOT</b>
+L0C9E: DEFB $06 ; Class-06 - A numeric expression must follow.
+ DEFB $1A ; Separator: ','
+ DEFB $06 ; Class-06 - A numeric expression must follow.
+ DEFB $00 ; Class-00 - No further operands.
+ DEFW <A href="#L0BAF">L0BAF</a> ; Address: $0BAF; Address: PLOT/UNP
+
+<a name="L0CA4"></a>;; <b>P-SCROLL</b>
+L0CA4: DEFB $00 ; Class-00 - No further operands.
+ DEFW <A href="#L0C0E">L0C0E</a> ; Address: $0C0E; Address: SCROLL
+
+<a name="L0CA7"></a>;; <b>P-PAUSE</b>
+L0CA7: DEFB $06 ; Class-06 - A numeric expression must follow.
+ DEFB $00 ; Class-00 - No further operands.
+ DEFW <A href="#L0F32">L0F32</a> ; Address: $0F32; Address: PAUSE
+
+<a name="L0CAB"></a>;; <b>P-SLOW</b>
+L0CAB: DEFB $00 ; Class-00 - No further operands.
+ DEFW <A href="#L0F2B">L0F2B</a> ; Address: $0F2B; Address: SLOW
+
+<a name="L0CAE"></a>;; <b>P-FAST</b>
+L0CAE: DEFB $00 ; Class-00 - No further operands.
+ DEFW <A href="#L0F23">L0F23</a> ; Address: $0F23; Address: FAST
+
+<a name="L0CB1"></a>;; <b>P-COPY</b>
+L0CB1: DEFB $00 ; Class-00 - No further operands.
+ DEFW <A href="#L0869">L0869</a> ; Address: $0869; Address: COPY
+
+<a name="L0CB4"></a>;; <b>P-LPRINT</b>
+L0CB4: DEFB $05 ; Class-05 - Variable syntax checked entirely
+ ; by routine.
+ DEFW <A href="#L0ACB">L0ACB</a> ; Address: $0ACB; Address: LPRINT
+
+<a name="L0CB7"></a>;; <b>P-LLIST</b>
+L0CB7: DEFB $03 ; Class-03 - A numeric expression may follow
+ ; else default to zero.
+ DEFW <A href="#L072C">L072C</a> ; Address: $072C; Address: LLIST
+
+
+; ---------------------------
+; THE <b><font color=#333388>'LINE SCANNING'</font></b> ROUTINE
+; ---------------------------
+;
+;
+
+<a name="L0CBA"></a>;; <b>LINE-SCAN</b>
+L0CBA: LD (IY+$01),$01 ; sv FLAGS
+ CALL <A href="#L0A73">L0A73</a> ; routine E-LINE-NO
+
+<a name="L0CC1"></a>;; <b>LINE-RUN</b>
+L0CC1: CALL <A href="#L14BC">L14BC</a> ; routine SET-MIN
+ LD HL,$4000 ; sv ERR_NR
+ LD (HL),$FF ;
+ LD HL,$402D ; sv FLAGX
+ BIT 5,(HL) ;
+ JR Z,<A href="#L0CDE">L0CDE</a> ; to LINE-NULL
+
+ CP $E3 ; 'STOP' ?
+ LD A,(HL) ;
+ JP NZ,<A href="#L0D6F">L0D6F</a> ; to INPUT-REP
+
+ CALL <A href="#L0DA6">L0DA6</a> ; routine SYNTAX-Z
+ RET Z ;
+
+
+ RST 08H ; ERROR-1
+ DEFB $0C ; Error Report: BREAK - CONT repeats
+
+
+; --------------------------
+; THE <b><font color=#333388>'STOP'</font></b> COMMAND ROUTINE
+; --------------------------
+;
+;
+
+<a name="L0CDC"></a>;; <b>STOP</b>
+L0CDC: RST 08H ; ERROR-1
+ DEFB $08 ; Error Report: STOP statement
+
+; ---
+
+; the interpretation of a line continues with a check for just spaces
+; followed by a carriage return.
+; The IF command also branches here with a true value to execute the
+; statement after the THEN but the statement can be null so
+; 10 IF 1 = 1 THEN
+; passes syntax (on all ZX computers).
+
+<a name="L0CDE"></a>;; <b>LINE-NULL</b>
+L0CDE: RST 18H ; GET-CHAR
+ LD B,$00 ; prepare to index - early.
+ CP $76 ; compare to NEWLINE.
+ RET Z ; return if so.
+
+ LD C,A ; transfer character to C.
+
+ RST 20H ; NEXT-CHAR advances.
+ LD A,C ; character to A
+ SUB $E1 ; subtract 'LPRINT' - lowest command.
+ JR C,<A href="#L0D26">L0D26</a> ; forward if less to REPORT-C2
+
+ LD C,A ; reduced token to C
+ LD HL,<A href="#L0C29">L0C29</a> ; set HL to address of offset table.
+ ADD HL,BC ; index into offset table.
+ LD C,(HL) ; fetch offset
+ ADD HL,BC ; index into parameter table.
+ JR <A href="#L0CF7">L0CF7</a> ; to GET-PARAM
+
+; ---
+
+<a name="L0CF4"></a>;; <b>SCAN-LOOP</b>
+L0CF4: LD HL,($4030) ; sv T_ADDR_lo
+
+; -> Entry Point to Scanning Loop
+
+<a name="L0CF7"></a>;; <b>GET-PARAM</b>
+L0CF7: LD A,(HL) ;
+ INC HL ;
+ LD ($4030),HL ; sv T_ADDR_lo
+
+ LD BC,<A href="#L0CF4">L0CF4</a> ; Address: SCAN-LOOP
+ PUSH BC ; is pushed on machine stack.
+
+ LD C,A ;
+ CP $0B ;
+ JR NC,<A href="#L0D10">L0D10</a> ; to SEPARATOR
+
+ LD HL,<A href="#L0D16">L0D16</a> ; class-tbl - the address of the class table.
+ LD B,$00 ;
+ ADD HL,BC ;
+ LD C,(HL) ;
+ ADD HL,BC ;
+ PUSH HL ;
+
+ RST 18H ; GET-CHAR
+ RET ; indirect jump to class routine and
+ ; by subsequent RET to SCAN-LOOP.
+
+; -----------------------
+; THE <b><font color=#333388>'SEPARATOR'</font></b> ROUTINE
+; -----------------------
+
+<a name="L0D10"></a>;; <b>SEPARATOR</b>
+L0D10: RST 18H ; GET-CHAR
+ CP C ;
+ JR NZ,<A href="#L0D26">L0D26</a> ; to REPORT-C2
+ ; 'Nonsense in BASIC'
+
+ RST 20H ; NEXT-CHAR
+ RET ; return
+
+
+; -------------------------
+; THE <b><font color=#333388>'COMMAND CLASS'</font></b> TABLE
+; -------------------------
+;
+
+<a name="L0D16"></a>;; <b>class-tbl</b>
+L0D16: DEFB <A href="#L0D2D">L0D2D</a> - $ ; 17 offset to; Address: CLASS-0
+ DEFB <A href="#L0D3C">L0D3C</a> - $ ; 25 offset to; Address: CLASS-1
+ DEFB <A href="#L0D6B">L0D6B</a> - $ ; 53 offset to; Address: CLASS-2
+ DEFB <A href="#L0D28">L0D28</a> - $ ; 0F offset to; Address: CLASS-3
+ DEFB <A href="#L0D85">L0D85</a> - $ ; 6B offset to; Address: CLASS-4
+ DEFB <A href="#L0D2E">L0D2E</a> - $ ; 13 offset to; Address: CLASS-5
+ DEFB <A href="#L0D92">L0D92</a> - $ ; 76 offset to; Address: CLASS-6
+
+
+; --------------------------
+; THE <b><font color=#333388>'CHECK END'</font></b> SUBROUTINE
+; --------------------------
+; Check for end of statement and that no spurious characters occur after
+; a correctly parsed statement. Since only one statement is allowed on each
+; line, the only character that may follow a statement is a NEWLINE.
+;
+
+<a name="L0D1D"></a>;; <b>CHECK-END</b>
+L0D1D: CALL <A href="#L0DA6">L0DA6</a> ; routine SYNTAX-Z
+ RET NZ ; return in runtime.
+
+ POP BC ; else drop return address.
+
+<a name="L0D22"></a>;; <b>CHECK-2</b>
+L0D22: LD A,(HL) ; fetch character.
+ CP $76 ; compare to NEWLINE.
+ RET Z ; return if so.
+
+<a name="L0D26"></a>;; <b>REPORT-C2</b>
+L0D26: JR <A href="#L0D9A">L0D9A</a> ; to REPORT-C
+ ; 'Nonsense in BASIC'
+
+; --------------------------
+; COMMAND CLASSES 03, 00, 05
+; --------------------------
+;
+;
+
+<a name="L0D28"></a>;; <b>CLASS-3</b>
+L0D28: CP $76 ;
+ CALL <A href="#L0D9C">L0D9C</a> ; routine NO-TO-STK
+
+<a name="L0D2D"></a>;; <b>CLASS-0</b>
+L0D2D: CP A ;
+
+<a name="L0D2E"></a>;; <b>CLASS-5</b>
+L0D2E: POP BC ;
+ CALL Z,<A href="#L0D1D">L0D1D</a> ; routine CHECK-END
+ EX DE,HL ;
+ LD HL,($4030) ; sv T_ADDR_lo
+ LD C,(HL) ;
+ INC HL ;
+ LD B,(HL) ;
+ EX DE,HL ;
+
+<a name="L0D3A"></a>;; <b>CLASS-END</b>
+L0D3A: PUSH BC ;
+ RET ;
+
+; ------------------------------
+; COMMAND CLASSES 01, 02, 04, 06
+; ------------------------------
+;
+;
+
+<a name="L0D3C"></a>;; <b>CLASS-1</b>
+L0D3C: CALL <A href="#L111C">L111C</a> ; routine LOOK-VARS
+
+<a name="L0D3F"></a>;; <b>CLASS-4-2</b>
+L0D3F: LD (IY+$2D),$00 ; sv FLAGX
+ JR NC,<A href="#L0D4D">L0D4D</a> ; to SET-STK
+
+ SET 1,(IY+$2D) ; sv FLAGX
+ JR NZ,<A href="#L0D63">L0D63</a> ; to SET-STRLN
+
+
+<a name="L0D4B"></a>;; <b>REPORT-2</b>
+L0D4B: RST 08H ; ERROR-1
+ DEFB $01 ; Error Report: Variable not found
+
+; ---
+
+<a name="L0D4D"></a>;; <b>SET-STK</b>
+L0D4D: CALL Z,<A href="#L11A7">L11A7</a> ; routine STK-VAR
+ BIT 6,(IY+$01) ; sv FLAGS - Numeric or string result?
+ JR NZ,<A href="#L0D63">L0D63</a> ; to SET-STRLN
+
+ XOR A ;
+ CALL <A href="#L0DA6">L0DA6</a> ; routine SYNTAX-Z
+ CALL NZ,<A href="#L13F8">L13F8</a> ; routine STK-FETCH
+ LD HL,$402D ; sv FLAGX
+ OR (HL) ;
+ LD (HL),A ;
+ EX DE,HL ;
+
+<a name="L0D63"></a>;; <b>SET-STRLN</b>
+L0D63: LD ($402E),BC ; sv STRLEN_lo
+ LD ($4012),HL ; sv DEST-lo
+
+; THE <b><font color=#333388>'REM'</font></b> COMMAND ROUTINE
+
+<a name="L0D6A"></a>;; <b>REM</b>
+L0D6A: RET ;
+
+; ---
+
+<a name="L0D6B"></a>;; <b>CLASS-2</b>
+L0D6B: POP BC ;
+ LD A,($4001) ; sv FLAGS
+
+<a name="L0D6F"></a>;; <b>INPUT-REP</b>
+L0D6F: PUSH AF ;
+ CALL <A href="#L0F55">L0F55</a> ; routine SCANNING
+ POP AF ;
+ LD BC,<A href="#L1321">L1321</a> ; Address: LET
+ LD D,(IY+$01) ; sv FLAGS
+ XOR D ;
+ AND $40 ;
+ JR NZ,<A href="#L0D9A">L0D9A</a> ; to REPORT-C
+
+ BIT 7,D ;
+ JR NZ,<A href="#L0D3A">L0D3A</a> ; to CLASS-END
+
+ JR <A href="#L0D22">L0D22</a> ; to CHECK-2
+
+; ---
+
+<a name="L0D85"></a>;; <b>CLASS-4</b>
+L0D85: CALL <A href="#L111C">L111C</a> ; routine LOOK-VARS
+ PUSH AF ;
+ LD A,C ;
+ OR $9F ;
+ INC A ;
+ JR NZ,<A href="#L0D9A">L0D9A</a> ; to REPORT-C
+
+ POP AF ;
+ JR <A href="#L0D3F">L0D3F</a> ; to CLASS-4-2
+
+; ---
+
+<a name="L0D92"></a>;; <b>CLASS-6</b>
+L0D92: CALL <A href="#L0F55">L0F55</a> ; routine SCANNING
+ BIT 6,(IY+$01) ; sv FLAGS - Numeric or string result?
+ RET NZ ;
+
+
+<a name="L0D9A"></a>;; <b>REPORT-C</b>
+L0D9A: RST 08H ; ERROR-1
+ DEFB $0B ; Error Report: Nonsense in BASIC
+
+; --------------------------------
+; THE <b><font color=#333388>'NUMBER TO STACK'</font></b> SUBROUTINE
+; --------------------------------
+;
+;
+
+<a name="L0D9C"></a>;; <b>NO-TO-STK</b>
+L0D9C: JR NZ,<A href="#L0D92">L0D92</a> ; back to CLASS-6 with a non-zero number.
+
+ CALL <A href="#L0DA6">L0DA6</a> ; routine SYNTAX-Z
+ RET Z ; return if checking syntax.
+
+; in runtime a zero default is placed on the calculator stack.
+
+ RST 28H ;; FP-CALC
+ DEFB $A0 ;;stk-zero
+ DEFB $34 ;;end-calc
+
+ RET ; return.
+
+; -------------------------
+; THE <b><font color=#333388>'SYNTAX-Z'</font></b> SUBROUTINE
+; -------------------------
+; This routine returns with zero flag set if checking syntax.
+; Calling this routine uses three instruction bytes compared to four if the
+; bit test is implemented inline.
+
+<a name="L0DA6"></a>;; <b>SYNTAX-Z</b>
+L0DA6: BIT 7,(IY+$01) ; test FLAGS - checking syntax only?
+ RET ; return.
+
+; ------------------------
+; THE <b><font color=#333388>'IF'</font></b> COMMAND ROUTINE
+; ------------------------
+; In runtime, the class routines have evaluated the test expression and
+; the result, true or false, is on the stack.
+
+<a name="L0DAB"></a>;; <b>IF</b>
+L0DAB: CALL <A href="#L0DA6">L0DA6</a> ; routine SYNTAX-Z
+ JR Z,<A href="#L0DB6">L0DB6</a> ; forward if checking syntax to IF-END
+
+; else delete the Boolean value on the calculator stack.
+
+ RST 28H ;; FP-CALC
+ DEFB $02 ;;delete
+ DEFB $34 ;;end-calc
+
+; register DE points to exponent of floating point value.
+
+ LD A,(DE) ; fetch exponent.
+ AND A ; test for zero - FALSE.
+ RET Z ; return if so.
+
+<a name="L0DB6"></a>;; <b>IF-END</b>
+L0DB6: JP <A href="#L0CDE">L0CDE</a> ; jump back to LINE-NULL
+
+; -------------------------
+; THE <b><font color=#333388>'FOR'</font></b> COMMAND ROUTINE
+; -------------------------
+;
+;
+
+<a name="L0DB9"></a>;; <b>FOR</b>
+L0DB9: CP $E0 ; is current character 'STEP' ?
+ JR NZ,<A href="#L0DC6">L0DC6</a> ; forward if not to F-USE-ONE
+
+
+ RST 20H ; NEXT-CHAR
+ CALL <A href="#L0D92">L0D92</a> ; routine CLASS-6 stacks the number
+ CALL <A href="#L0D1D">L0D1D</a> ; routine CHECK-END
+ JR <A href="#L0DCC">L0DCC</a> ; forward to F-REORDER
+
+; ---
+
+<a name="L0DC6"></a>;; <b>F-USE-ONE</b>
+L0DC6: CALL <A href="#L0D1D">L0D1D</a> ; routine CHECK-END
+
+ RST 28H ;; FP-CALC
+ DEFB $A1 ;;stk-one
+ DEFB $34 ;;end-calc
+
+
+
+<a name="L0DCC"></a>;; <b>F-REORDER</b>
+L0DCC: RST 28H ;; FP-CALC v, l, s.
+ DEFB $C0 ;;st-mem-0 v, l, s.
+ DEFB $02 ;;delete v, l.
+ DEFB $01 ;;exchange l, v.
+ DEFB $E0 ;;get-mem-0 l, v, s.
+ DEFB $01 ;;exchange l, s, v.
+ DEFB $34 ;;end-calc l, s, v.
+
+ CALL <A href="#L1321">L1321</a> ; routine LET
+
+ LD ($401F),HL ; set MEM to address variable.
+ DEC HL ; point to letter.
+ LD A,(HL) ;
+ SET 7,(HL) ;
+ LD BC,$0006 ;
+ ADD HL,BC ;
+ RLCA ;
+ JR C,<A href="#L0DEA">L0DEA</a> ; to F-LMT-STP
+
+ SLA C ;
+ CALL <A href="#L099E">L099E</a> ; routine MAKE-ROOM
+ INC HL ;
+
+<a name="L0DEA"></a>;; <b>F-LMT-STP</b>
+L0DEA: PUSH HL ;
+
+ RST 28H ;; FP-CALC
+ DEFB $02 ;;delete
+ DEFB $02 ;;delete
+ DEFB $34 ;;end-calc
+
+ POP HL ;
+ EX DE,HL ;
+
+ LD C,$0A ; ten bytes to be moved.
+ LDIR ; copy bytes
+
+ LD HL,($4007) ; set HL to system variable PPC current line.
+ EX DE,HL ; transfer to DE, variable pointer to HL.
+ INC DE ; loop start will be this line + 1 at least.
+ LD (HL),E ;
+ INC HL ;
+ LD (HL),D ;
+ CALL <A href="#L0E5A">L0E5A</a> ; routine NEXT-LOOP considers an initial pass.
+ RET NC ; return if possible.
+
+; else program continues from point following matching NEXT.
+
+ BIT 7,(IY+$08) ; test PPC_hi
+ RET NZ ; return if over 32767 ???
+
+ LD B,(IY+$2E) ; fetch variable name from STRLEN_lo
+ RES 6,B ; make a true letter.
+ LD HL,($4029) ; set HL from NXTLIN
+
+; now enter a loop to look for matching next.
+
+<a name="L0E0E"></a>;; <b>NXTLIN-NO</b>
+L0E0E: LD A,(HL) ; fetch high byte of line number.
+ AND $C0 ; mask off low bits $3F
+ JR NZ,<A href="#L0E2A">L0E2A</a> ; forward at end of program to FOR-END
+
+ PUSH BC ; save letter
+ CALL <A href="#L09F2">L09F2</a> ; routine NEXT-ONE finds next line.
+ POP BC ; restore letter
+
+ INC HL ; step past low byte
+ INC HL ; past the
+ INC HL ; line length.
+ CALL <A href="#L004C">L004C</a> ; routine TEMP-PTR1 sets CH_ADD
+
+ RST 18H ; GET-CHAR
+ CP $F3 ; compare to 'NEXT'.
+ EX DE,HL ; next line to HL.
+ JR NZ,<A href="#L0E0E">L0E0E</a> ; back with no match to NXTLIN-NO
+
+;
+
+ EX DE,HL ; restore pointer.
+
+ RST 20H ; NEXT-CHAR advances and gets letter in A.
+ EX DE,HL ; save pointer
+ CP B ; compare to variable name.
+ JR NZ,<A href="#L0E0E">L0E0E</a> ; back with mismatch to NXTLIN-NO
+
+<a name="L0E2A"></a>;; <b>FOR-END</b>
+L0E2A: LD ($4029),HL ; update system variable NXTLIN
+ RET ; return.
+
+; --------------------------
+; THE <b><font color=#333388>'NEXT'</font></b> COMMAND ROUTINE
+; --------------------------
+;
+;
+
+<a name="L0E2E"></a>;; <b>NEXT</b>
+L0E2E: BIT 1,(IY+$2D) ; sv FLAGX
+ JP NZ,<A href="#L0D4B">L0D4B</a> ; to REPORT-2
+
+ LD HL,($4012) ; DEST
+ BIT 7,(HL) ;
+ JR Z,<A href="#L0E58">L0E58</a> ; to REPORT-1
+
+ INC HL ;
+ LD ($401F),HL ; sv MEM_lo
+
+ RST 28H ;; FP-CALC
+ DEFB $E0 ;;get-mem-0
+ DEFB $E2 ;;get-mem-2
+ DEFB $0F ;;addition
+ DEFB $C0 ;;st-mem-0
+ DEFB $02 ;;delete
+ DEFB $34 ;;end-calc
+
+ CALL <A href="#L0E5A">L0E5A</a> ; routine NEXT-LOOP
+ RET C ;
+
+ LD HL,($401F) ; sv MEM_lo
+ LD DE,$000F ;
+ ADD HL,DE ;
+ LD E,(HL) ;
+ INC HL ;
+ LD D,(HL) ;
+ EX DE,HL ;
+ JR <A href="#L0E86">L0E86</a> ; to GOTO-2
+
+; ---
+
+
+<a name="L0E58"></a>;; <b>REPORT-1</b>
+L0E58: RST 08H ; ERROR-1
+ DEFB $00 ; Error Report: NEXT without FOR
+
+
+; --------------------------
+; THE <b><font color=#333388>'NEXT-LOOP'</font></b> SUBROUTINE
+; --------------------------
+;
+;
+
+<a name="L0E5A"></a>;; <b>NEXT-LOOP</b>
+L0E5A: RST 28H ;; FP-CALC
+ DEFB $E1 ;;get-mem-1
+ DEFB $E0 ;;get-mem-0
+ DEFB $E2 ;;get-mem-2
+ DEFB $32 ;;less-0
+ DEFB $00 ;;jump-true
+ DEFB $02 ;;to <A href="#L0E62">L0E62</a>, LMT-V-VAL
+
+ DEFB $01 ;;exchange
+
+<a name="L0E62"></a>;; <b>LMT-V-VAL</b>
+L0E62: DEFB $03 ;;subtract
+ DEFB $33 ;;greater-0
+ DEFB $00 ;;jump-true
+ DEFB $04 ;;to <A href="#L0E69">L0E69</a>, IMPOSS
+
+ DEFB $34 ;;end-calc
+
+ AND A ; clear carry flag
+ RET ; return.
+
+; ---
+
+
+<a name="L0E69"></a>;; <b>IMPOSS</b>
+L0E69: DEFB $34 ;;end-calc
+
+ SCF ; set carry flag
+ RET ; return.
+
+; --------------------------
+; THE <b><font color=#333388>'RAND'</font></b> COMMAND ROUTINE
+; --------------------------
+; The keyword was <b><font color=#333388>'RANDOMISE'</font></b> on the ZX80, is 'RAND' here on the ZX81 and
+; becomes 'RANDOMIZE' on the ZX Spectrum.
+; In all invocations the procedure is the same - to set the SEED system variable
+; with a supplied integer value or to use a time-based value if no number, or
+; zero, is supplied.
+
+<a name="L0E6C"></a>;; <b>RAND</b>
+L0E6C: CALL <A href="#L0EA7">L0EA7</a> ; routine FIND-INT
+ LD A,B ; test value
+ OR C ; for zero
+ JR NZ,<A href="#L0E77">L0E77</a> ; forward if not zero to SET-SEED
+
+ LD BC,($4034) ; fetch value of FRAMES system variable.
+
+<a name="L0E77"></a>;; <b>SET-SEED</b>
+L0E77: LD ($4032),BC ; update the SEED system variable.
+ RET ; return.
+
+; --------------------------
+; THE <b><font color=#333388>'CONT'</font></b> COMMAND ROUTINE
+; --------------------------
+; Another abbreviated command. ROM space was really tight.
+; CONTINUE at the line number that was set when break was pressed.
+; Sometimes the current line, sometimes the next line.
+
+<a name="L0E7C"></a>;; <b>CONT</b>
+L0E7C: LD HL,($402B) ; set HL from system variable OLDPPC
+ JR <A href="#L0E86">L0E86</a> ; forward to GOTO-2
+
+; --------------------------
+; THE <b><font color=#333388>'GOTO'</font></b> COMMAND ROUTINE
+; --------------------------
+; This token also suffered from the shortage of room and there is no space
+; getween GO and TO as there is on the ZX80 and ZX Spectrum. The same also
+; applies to the GOSUB keyword.
+
+<a name="L0E81"></a>;; <b>GOTO</b>
+L0E81: CALL <A href="#L0EA7">L0EA7</a> ; routine FIND-INT
+ LD H,B ;
+ LD L,C ;
+
+<a name="L0E86"></a>;; <b>GOTO-2</b>
+L0E86: LD A,H ;
+ CP $F0 ;
+ JR NC,<A href="#L0EAD">L0EAD</a> ; to REPORT-B
+
+ CALL <A href="#L09D8">L09D8</a> ; routine LINE-ADDR
+ LD ($4029),HL ; sv NXTLIN_lo
+ RET ;
+
+; --------------------------
+; THE <b><font color=#333388>'POKE'</font></b> COMMAND ROUTINE
+; --------------------------
+;
+;
+
+<a name="L0E92"></a>;; <b>POKE</b>
+L0E92: CALL <A href="#L15CD">L15CD</a> ; routine FP-TO-A
+ JR C,<A href="#L0EAD">L0EAD</a> ; forward, with overflow, to REPORT-B
+
+ JR Z,<A href="#L0E9B">L0E9B</a> ; forward, if positive, to POKE-SAVE
+
+ NEG ; negate
+
+<a name="L0E9B"></a>;; <b>POKE-SAVE</b>
+L0E9B: PUSH AF ; preserve value.
+ CALL <A href="#L0EA7">L0EA7</a> ; routine FIND-INT gets address in BC
+ ; invoking the error routine with overflow
+ ; or a negative number.
+ POP AF ; restore value.
+
+; <font color=#9900FF>Note.</font> the next two instructions are legacy code from the ZX80 and
+; inappropriate here.
+
+ BIT 7,(IY+$00) ; test ERR_NR - is it still $FF ?
+ RET Z ; return with error.
+
+ LD (BC),A ; update the address contents.
+ RET ; return.
+
+; -----------------------------
+; THE <b><font color=#333388>'FIND INTEGER'</font></b> SUBROUTINE
+; -----------------------------
+;
+;
+
+<a name="L0EA7"></a>;; <b>FIND-INT</b>
+L0EA7: CALL <A href="#L158A">L158A</a> ; routine FP-TO-BC
+ JR C,<A href="#L0EAD">L0EAD</a> ; forward with overflow to REPORT-B
+
+ RET Z ; return if positive (0-65535).
+
+
+<a name="L0EAD"></a>;; <b>REPORT-B</b>
+L0EAD: RST 08H ; ERROR-1
+ DEFB $0A ; Error Report: Integer out of range
+
+; -------------------------
+; THE <b><font color=#333388>'RUN'</font></b> COMMAND ROUTINE
+; -------------------------
+;
+;
+
+<a name="L0EAF"></a>;; <b>RUN</b>
+L0EAF: CALL <A href="#L0E81">L0E81</a> ; routine GOTO
+ JP <A href="#L149A">L149A</a> ; to CLEAR
+
+; ---------------------------
+; THE <b><font color=#333388>'GOSUB'</font></b> COMMAND ROUTINE
+; ---------------------------
+;
+;
+
+<a name="L0EB5"></a>;; <b>GOSUB</b>
+L0EB5: LD HL,($4007) ; sv PPC_lo
+ INC HL ;
+ EX (SP),HL ;
+ PUSH HL ;
+ LD ($4002),SP ; set the error stack pointer - ERR_SP
+ CALL <A href="#L0E81">L0E81</a> ; routine GOTO
+ LD BC,$0006 ;
+
+; --------------------------
+; THE <b><font color=#333388>'TEST ROOM'</font></b> SUBROUTINE
+; --------------------------
+;
+;
+
+<a name="L0EC5"></a>;; <b>TEST-ROOM</b>
+L0EC5: LD HL,($401C) ; sv STKEND_lo
+ ADD HL,BC ;
+ JR C,<A href="#L0ED3">L0ED3</a> ; to REPORT-4
+
+ EX DE,HL ;
+ LD HL,$0024 ;
+ ADD HL,DE ;
+ SBC HL,SP ;
+ RET C ;
+
+<a name="L0ED3"></a>;; <b>REPORT-4</b>
+L0ED3: LD L,$03 ;
+ JP <A href="#L0058">L0058</a> ; to ERROR-3
+
+; ----------------------------
+; THE <b><font color=#333388>'RETURN'</font></b> COMMAND ROUTINE
+; ----------------------------
+;
+;
+
+<a name="L0ED8"></a>;; <b>RETURN</b>
+L0ED8: POP HL ;
+ EX (SP),HL ;
+ LD A,H ;
+ CP $3E ;
+ JR Z,<A href="#L0EE5">L0EE5</a> ; to REPORT-7
+
+ LD ($4002),SP ; sv ERR_SP_lo
+ JR <A href="#L0E86">L0E86</a> ; back to GOTO-2
+
+; ---
+
+<a name="L0EE5"></a>;; <b>REPORT-7</b>
+L0EE5: EX (SP),HL ;
+ PUSH HL ;
+
+ RST 08H ; ERROR-1
+ DEFB $06 ; Error Report: RETURN without GOSUB
+
+; ---------------------------
+; THE <b><font color=#333388>'INPUT'</font></b> COMMAND ROUTINE
+; ---------------------------
+;
+;
+
+<a name="L0EE9"></a>;; <b>INPUT</b>
+L0EE9: BIT 7,(IY+$08) ; sv PPC_hi
+ JR NZ,<A href="#L0F21">L0F21</a> ; to REPORT-8
+
+ CALL <A href="#L14A3">L14A3</a> ; routine X-TEMP
+ LD HL,$402D ; sv FLAGX
+ SET 5,(HL) ;
+ RES 6,(HL) ;
+ LD A,($4001) ; sv FLAGS
+ AND $40 ;
+ LD BC,$0002 ;
+ JR NZ,<A href="#L0F05">L0F05</a> ; to PROMPT
+
+ LD C,$04 ;
+
+<a name="L0F05"></a>;; <b>PROMPT</b>
+L0F05: OR (HL) ;
+ LD (HL),A ;
+
+ RST 30H ; BC-SPACES
+ LD (HL),$76 ;
+ LD A,C ;
+ RRCA ;
+ RRCA ;
+ JR C,<A href="#L0F14">L0F14</a> ; to ENTER-CUR
+
+ LD A,$0B ;
+ LD (DE),A ;
+ DEC HL ;
+ LD (HL),A ;
+
+<a name="L0F14"></a>;; <b>ENTER-CUR</b>
+L0F14: DEC HL ;
+ LD (HL),$7F ;
+ LD HL,($4039) ; sv S_POSN_x
+ LD ($4030),HL ; sv T_ADDR_lo
+ POP HL ;
+ JP <A href="#L0472">L0472</a> ; to LOWER
+
+; ---
+
+<a name="L0F21"></a>;; <b>REPORT-8</b>
+L0F21: RST 08H ; ERROR-1
+ DEFB $07 ; Error Report: End of file
+
+; ---------------------------
+; THE <b><font color=#333388>'PAUSE'</font></b> COMMAND ROUTINE
+; ---------------------------
+;
+;
+
+<a name="L0F23"></a>;; <b>FAST</b>
+L0F23: CALL <A href="#L02E7">L02E7</a> ; routine SET-FAST
+ RES 6,(IY+$3B) ; sv CDFLAG
+ RET ; return.
+
+; --------------------------
+; THE <b><font color=#333388>'SLOW'</font></b> COMMAND ROUTINE
+; --------------------------
+;
+;
+
+<a name="L0F2B"></a>;; <b>SLOW</b>
+L0F2B: SET 6,(IY+$3B) ; sv CDFLAG
+ JP <A href="#L0207">L0207</a> ; to SLOW/FAST
+
+; ---------------------------
+; THE <b><font color=#333388>'PAUSE'</font></b> COMMAND ROUTINE
+; ---------------------------
+
+<a name="L0F32"></a>;; <b>PAUSE</b>
+L0F32: CALL <A href="#L0EA7">L0EA7</a> ; routine FIND-INT
+ CALL <A href="#L02E7">L02E7</a> ; routine SET-FAST
+ LD H,B ;
+ LD L,C ;
+ CALL <A href="#L022D">L022D</a> ; routine DISPLAY-P
+
+ LD (IY+$35),$FF ; sv FRAMES_hi
+
+ CALL <A href="#L0207">L0207</a> ; routine SLOW/FAST
+ JR <A href="#L0F4B">L0F4B</a> ; routine DEBOUNCE
+
+; ----------------------
+; THE <b><font color=#333388>'BREAK'</font></b> SUBROUTINE
+; ----------------------
+;
+;
+
+<a name="L0F46"></a>;; <b>BREAK-1</b>
+L0F46: LD A,$7F ; read port $7FFE - keys B,N,M,.,SPACE.
+ IN A,($FE) ;
+ RRA ; carry will be set if space not pressed.
+
+; -------------------------
+; THE <b><font color=#333388>'DEBOUNCE'</font></b> SUBROUTINE
+; -------------------------
+;
+;
+
+<a name="L0F4B"></a>;; <b>DEBOUNCE</b>
+L0F4B: RES 0,(IY+$3B) ; update system variable CDFLAG
+ LD A,$FF ;
+ LD ($4027),A ; update system variable DEBOUNCE
+ RET ; return.
+
+
+; -------------------------
+; THE <b><font color=#333388>'SCANNING'</font></b> SUBROUTINE
+; -------------------------
+; This recursive routine is where the ZX81 gets its power. Provided there is
+; enough memory it can evaluate an expression of unlimited complexity.
+; <font color=#9900FF>Note.</font> there is no unary plus so, as on the ZX80, PRINT +1 gives a syntax error.
+; PRINT +1 works on the Spectrum but so too does PRINT + "STRING".
+
+<a name="L0F55"></a>;; <b>SCANNING</b>
+L0F55: RST 18H ; GET-CHAR
+ LD B,$00 ; set B register to zero.
+ PUSH BC ; stack zero as a priority end-marker.
+
+<a name="L0F59"></a>;; <b>S-LOOP-1</b>
+L0F59: CP $40 ; compare to the 'RND' character
+ JR NZ,<A href="#L0F8C">L0F8C</a> ; forward, if not, to S-TEST-PI
+
+; ------------------
+; THE <b><font color=#333388>'RND'</font></b> FUNCTION
+; ------------------
+
+ CALL <A href="#L0DA6">L0DA6</a> ; routine SYNTAX-Z
+ JR Z,<A href="#L0F8A">L0F8A</a> ; forward if checking syntax to S-JPI-END
+
+ LD BC,($4032) ; sv SEED_lo
+ CALL <A href="#L1520">L1520</a> ; routine STACK-BC
+
+ RST 28H ;; FP-CALC
+ DEFB $A1 ;;stk-one
+ DEFB $0F ;;addition
+ DEFB $30 ;;stk-data
+ DEFB $37 ;;Exponent: $87, Bytes: 1
+ DEFB $16 ;;(+00,+00,+00)
+ DEFB $04 ;;multiply
+ DEFB $30 ;;stk-data
+ DEFB $80 ;;Bytes: 3
+ DEFB $41 ;;Exponent $91
+ DEFB $00,$00,$80 ;;(+00)
+ DEFB $2E ;;n-mod-m
+ DEFB $02 ;;delete
+ DEFB $A1 ;;stk-one
+ DEFB $03 ;;subtract
+ DEFB $2D ;;duplicate
+ DEFB $34 ;;end-calc
+
+ CALL <A href="#L158A">L158A</a> ; routine FP-TO-BC
+ LD ($4032),BC ; update the SEED system variable.
+ LD A,(HL) ; HL addresses the exponent of the last value.
+ AND A ; test for zero
+ JR Z,<A href="#L0F8A">L0F8A</a> ; forward, if so, to S-JPI-END
+
+ SUB $10 ; else reduce exponent by sixteen
+ LD (HL),A ; thus dividing by 65536 for last value.
+
+<a name="L0F8A"></a>;; <b>S-JPI-END</b>
+L0F8A: JR <A href="#L0F99">L0F99</a> ; forward to S-PI-END
+
+; ---
+
+<a name="L0F8C"></a>;; <b>S-TEST-PI</b>
+L0F8C: CP $42 ; the 'PI' character
+ JR NZ,<A href="#L0F9D">L0F9D</a> ; forward, if not, to S-TST-INK
+
+; -------------------
+; THE <b><font color=#333388>'PI'</font></b> EVALUATION
+; -------------------
+
+ CALL <A href="#L0DA6">L0DA6</a> ; routine SYNTAX-Z
+ JR Z,<A href="#L0F99">L0F99</a> ; forward if checking syntax to S-PI-END
+
+
+ RST 28H ;; FP-CALC
+ DEFB $A3 ;;stk-pi/2
+ DEFB $34 ;;end-calc
+
+ INC (HL) ; double the exponent giving PI on the stack.
+
+<a name="L0F99"></a>;; <b>S-PI-END</b>
+L0F99: RST 20H ; NEXT-CHAR advances character pointer.
+
+ JP <A href="#L1083">L1083</a> ; jump forward to S-NUMERIC to set the flag
+ ; to signal numeric result before advancing.
+
+; ---
+
+<a name="L0F9D"></a>;; <b>S-TST-INK</b>
+L0F9D: CP $41 ; compare to character 'INKEY$'
+ JR NZ,<A href="#L0FB2">L0FB2</a> ; forward, if not, to S-ALPHANUM
+
+; -----------------------
+; THE <b><font color=#333388>'INKEY$'</font></b> EVALUATION
+; -----------------------
+
+ CALL <A href="#L02BB">L02BB</a> ; routine KEYBOARD
+ LD B,H ;
+ LD C,L ;
+ LD D,C ;
+ INC D ;
+ CALL NZ,<A href="#L07BD">L07BD</a> ; routine DECODE
+ LD A,D ;
+ ADC A,D ;
+ LD B,D ;
+ LD C,A ;
+ EX DE,HL ;
+ JR <A href="#L0FED">L0FED</a> ; forward to S-STRING
+
+; ---
+
+<a name="L0FB2"></a>;; <b>S-ALPHANUM</b>
+L0FB2: CALL <A href="#L14D2">L14D2</a> ; routine ALPHANUM
+ JR C,<A href="#L1025">L1025</a> ; forward, if alphanumeric to S-LTR-DGT
+
+ CP $1B ; is character a '.' ?
+ JP Z,<A href="#L1047">L1047</a> ; jump forward if so to S-DECIMAL
+
+ LD BC,$09D8 ; prepare priority 09, operation 'subtract'
+ CP $16 ; is character unary minus '-' ?
+ JR Z,<A href="#L1020">L1020</a> ; forward, if so, to S-PUSH-PO
+
+ CP $10 ; is character a '(' ?
+ JR NZ,<A href="#L0FD6">L0FD6</a> ; forward if not to S-QUOTE
+
+ CALL <A href="#L0049">L0049</a> ; routine CH-ADD+1 advances character pointer.
+
+ CALL <A href="#L0F55">L0F55</a> ; recursively call routine SCANNING to
+ ; evaluate the sub-expression.
+
+ CP $11 ; is subsequent character a ')' ?
+ JR NZ,<A href="#L0FFF">L0FFF</a> ; forward if not to S-RPT-C
+
+
+ CALL <A href="#L0049">L0049</a> ; routine CH-ADD+1 advances.
+ JR <A href="#L0FF8">L0FF8</a> ; relative jump to S-JP-CONT3 and then S-CONT3
+
+; ---
+
+; consider a quoted string e.g. PRINT "Hooray!"
+; <font color=#9900FF>Note.</font> quotes are not allowed within a string.
+
+<a name="L0FD6"></a>;; <b>S-QUOTE</b>
+L0FD6: CP $0B ; is character a quote (") ?
+ JR NZ,<A href="#L1002">L1002</a> ; forward, if not, to S-FUNCTION
+
+ CALL <A href="#L0049">L0049</a> ; routine CH-ADD+1 advances
+ PUSH HL ; * save start of string.
+ JR <A href="#L0FE3">L0FE3</a> ; forward to S-QUOTE-S
+
+; ---
+
+
+<a name="L0FE0"></a>;; <b>S-Q-AGAIN</b>
+L0FE0: CALL <A href="#L0049">L0049</a> ; routine CH-ADD+1
+
+<a name="L0FE3"></a>;; <b>S-QUOTE-S</b>
+L0FE3: CP $0B ; is character a '"' ?
+ JR NZ,<A href="#L0FFB">L0FFB</a> ; forward if not to S-Q-NL
+
+ POP DE ; * retrieve start of string
+ AND A ; prepare to subtract.
+ SBC HL,DE ; subtract start from current position.
+ LD B,H ; transfer this length
+ LD C,L ; to the BC register pair.
+
+<a name="L0FED"></a>;; <b>S-STRING</b>
+L0FED: LD HL,$4001 ; address system variable FLAGS
+ RES 6,(HL) ; signal string result
+ BIT 7,(HL) ; test if checking syntax.
+
+ CALL NZ,<A href="#L12C3">L12C3</a> ; in run-time routine STK-STO-$ stacks the
+ ; string descriptor - start DE, length BC.
+
+ RST 20H ; NEXT-CHAR advances pointer.
+
+<a name="L0FF8"></a>;; <b>S-J-CONT-3</b>
+L0FF8: JP <A href="#L1088">L1088</a> ; jump to S-CONT-3
+
+; ---
+
+; A string with no terminating quote has to be considered.
+
+<a name="L0FFB"></a>;; <b>S-Q-NL</b>
+L0FFB: CP $76 ; compare to NEWLINE
+ JR NZ,<A href="#L0FE0">L0FE0</a> ; loop back if not to S-Q-AGAIN
+
+<a name="L0FFF"></a>;; <b>S-RPT-C</b>
+L0FFF: JP <A href="#L0D9A">L0D9A</a> ; to REPORT-C
+
+; ---
+
+<a name="L1002"></a>;; <b>S-FUNCTION</b>
+L1002: SUB $C4 ; subtract 'CODE' reducing codes
+ ; CODE thru '<>' to range $00 - $XX
+ JR C,<A href="#L0FFF">L0FFF</a> ; back, if less, to S-RPT-C
+
+; test for NOT the last function in character set.
+
+ LD BC,$04EC ; prepare priority $04, operation 'not'
+ CP $13 ; compare to 'NOT' ( - CODE)
+ JR Z,<A href="#L1020">L1020</a> ; forward, if so, to S-PUSH-PO
+
+ JR NC,<A href="#L0FFF">L0FFF</a> ; back with anything higher to S-RPT-C
+
+; else is a function 'CODE' thru 'CHR$'
+
+ LD B,$10 ; priority sixteen binds all functions to
+ ; arguments removing the need for brackets.
+
+ ADD A,$D9 ; add $D9 to give range $D9 thru $EB
+ ; bit 6 is set to show numeric argument.
+ ; bit 7 is set to show numeric result.
+
+; now adjust these default argument/result indicators.
+
+ LD C,A ; save code in C
+
+ CP $DC ; separate 'CODE', 'VAL', 'LEN'
+ JR NC,<A href="#L101A">L101A</a> ; skip forward if string operand to S-NO-TO-$
+
+ RES 6,C ; signal string operand.
+
+<a name="L101A"></a>;; <b>S-NO-TO-$</b>
+L101A: CP $EA ; isolate top of range 'STR$' and 'CHR$'
+ JR C,<A href="#L1020">L1020</a> ; skip forward with others to S-PUSH-PO
+
+ RES 7,C ; signal string result.
+
+<a name="L1020"></a>;; <b>S-PUSH-PO</b>
+L1020: PUSH BC ; push the priority/operation
+
+ RST 20H ; NEXT-CHAR
+ JP <A href="#L0F59">L0F59</a> ; jump back to S-LOOP-1
+
+; ---
+
+<a name="L1025"></a>;; <b>S-LTR-DGT</b>
+L1025: CP $26 ; compare to 'A'.
+ JR C,<A href="#L1047">L1047</a> ; forward if less to S-DECIMAL
+
+ CALL <A href="#L111C">L111C</a> ; routine LOOK-VARS
+ JP C,<A href="#L0D4B">L0D4B</a> ; back if not found to REPORT-2
+ ; a variable is always 'found' when checking
+ ; syntax.
+
+ CALL Z,<A href="#L11A7">L11A7</a> ; routine STK-VAR stacks string parameters or
+ ; returns cell location if numeric.
+
+ LD A,($4001) ; fetch FLAGS
+ CP $C0 ; compare to numeric result/numeric operand
+ JR C,<A href="#L1087">L1087</a> ; forward if not numeric to S-CONT-2
+
+ INC HL ; address numeric contents of variable.
+ LD DE,($401C) ; set destination to STKEND
+ CALL <A href="#L19F6">L19F6</a> ; routine MOVE-FP stacks the five bytes
+ EX DE,HL ; transfer new free location from DE to HL.
+ LD ($401C),HL ; update STKEND system variable.
+ JR <A href="#L1087">L1087</a> ; forward to S-CONT-2
+
+; ---
+
+; The Scanning Decimal routine is invoked when a decimal point or digit is
+; found in the expression.
+; When checking syntax, then the 'hidden floating point' form is placed
+; after the number in the BASIC line.
+; In run-time, the digits are skipped and the floating point number is picked
+; up.
+
+<a name="L1047"></a>;; <b>S-DECIMAL</b>
+L1047: CALL <A href="#L0DA6">L0DA6</a> ; routine SYNTAX-Z
+ JR NZ,<A href="#L106F">L106F</a> ; forward in run-time to S-STK-DEC
+
+ CALL <A href="#L14D9">L14D9</a> ; routine DEC-TO-FP
+
+ RST 18H ; GET-CHAR advances HL past digits
+ LD BC,$0006 ; six locations are required.
+ CALL <A href="#L099E">L099E</a> ; routine MAKE-ROOM
+ INC HL ; point to first new location
+ LD (HL),$7E ; insert the number marker 126 decimal.
+ INC HL ; increment
+ EX DE,HL ; transfer destination to DE.
+ LD HL,($401C) ; set HL from STKEND which points to the
+ ; first location after the 'last value'
+ LD C,$05 ; five bytes to move.
+ AND A ; clear carry.
+ SBC HL,BC ; subtract five pointing to 'last value'.
+ LD ($401C),HL ; update STKEND thereby 'deleting the value.
+
+ LDIR ; copy the five value bytes.
+
+ EX DE,HL ; basic pointer to HL which may be white-space
+ ; following the number.
+ DEC HL ; now points to last of five bytes.
+ CALL <A href="#L004C">L004C</a> ; routine TEMP-PTR1 advances the character
+ ; address skipping any white-space.
+ JR <A href="#L1083">L1083</a> ; forward to S-NUMERIC
+ ; to signal a numeric result.
+
+; ---
+
+; In run-time the branch is here when a digit or point is encountered.
+
+<a name="L106F"></a>;; <b>S-STK-DEC</b>
+L106F: RST 20H ; NEXT-CHAR
+ CP $7E ; compare to 'number marker'
+ JR NZ,<A href="#L106F">L106F</a> ; loop back until found to S-STK-DEC
+ ; skipping all the digits.
+
+ INC HL ; point to first of five hidden bytes.
+ LD DE,($401C) ; set destination from STKEND system variable
+ CALL <A href="#L19F6">L19F6</a> ; routine MOVE-FP stacks the number.
+ LD ($401C),DE ; update system variable STKEND.
+ LD ($4016),HL ; update system variable CH_ADD.
+
+<a name="L1083"></a>;; <b>S-NUMERIC</b>
+L1083: SET 6,(IY+$01) ; update FLAGS - Signal numeric result
+
+<a name="L1087"></a>;; <b>S-CONT-2</b>
+L1087: RST 18H ; GET-CHAR
+
+<a name="L1088"></a>;; <b>S-CONT-3</b>
+L1088: CP $10 ; compare to opening bracket '('
+ JR NZ,<A href="#L1098">L1098</a> ; forward if not to S-OPERTR
+
+ BIT 6,(IY+$01) ; test FLAGS - Numeric or string result?
+ JR NZ,<A href="#L10BC">L10BC</a> ; forward if numeric to S-LOOP
+
+; else is a string
+
+ CALL <A href="#L1263">L1263</a> ; routine SLICING
+
+ RST 20H ; NEXT-CHAR
+ JR <A href="#L1088">L1088</a> ; back to S-CONT-3
+
+; ---
+
+; the character is now manipulated to form an equivalent in the table of
+; calculator literals. This is quite cumbersome and in the ZX Spectrum a
+; simple look-up table was introduced at this point.
+
+<a name="L1098"></a>;; <b>S-OPERTR</b>
+L1098: LD BC,$00C3 ; prepare operator 'subtract' as default.
+ ; also set B to zero for later indexing.
+
+ CP $12 ; is character '>' ?
+ JR C,<A href="#L10BC">L10BC</a> ; forward if less to S-LOOP as
+ ; we have reached end of meaningful expression
+
+ SUB $16 ; is character '-' ?
+ JR NC,<A href="#L10A7">L10A7</a> ; forward with - * / and '**' '<>' to SUBMLTDIV
+
+ ADD A,$0D ; increase others by thirteen
+ ; $09 '>' thru $0C '+'
+ JR <A href="#L10B5">L10B5</a> ; forward to GET-PRIO
+
+; ---
+
+<a name="L10A7"></a>;; <b>SUBMLTDIV</b>
+L10A7: CP $03 ; isolate $00 '-', $01 '*', $02 '/'
+ JR C,<A href="#L10B5">L10B5</a> ; forward if so to GET-PRIO
+
+; else possibly originally $D8 '**' thru $DD '<>' already reduced by $16
+
+ SUB $C2 ; giving range $00 to $05
+ JR C,<A href="#L10BC">L10BC</a> ; forward if less to S-LOOP
+
+ CP $06 ; test the upper limit for nonsense also
+ JR NC,<A href="#L10BC">L10BC</a> ; forward if so to S-LOOP
+
+ ADD A,$03 ; increase by 3 to give combined operators of
+
+ ; $00 '-'
+ ; $01 '*'
+ ; $02 '/'
+
+ ; $03 '**'
+ ; $04 'OR'
+ ; $05 'AND'
+ ; $06 '<='
+ ; $07 '>='
+ ; $08 '<>'
+
+ ; $09 '>'
+ ; $0A '<'
+ ; $0B '='
+ ; $0C '+'
+
+<a name="L10B5"></a>;; <b>GET-PRIO</b>
+L10B5: ADD A,C ; add to default operation 'sub' ($C3)
+ LD C,A ; and place in operator byte - C.
+
+ LD HL,<A href="#L110F">L110F</a> - $C3 ; theoretical base of the priorities table.
+ ADD HL,BC ; add C ( B is zero)
+ LD B,(HL) ; pick up the priority in B
+
+<a name="L10BC"></a>;; <b>S-LOOP</b>
+L10BC: POP DE ; restore previous
+ LD A,D ; load A with priority.
+ CP B ; is present priority higher
+ JR C,<A href="#L10ED">L10ED</a> ; forward if so to S-TIGHTER
+
+ AND A ; are both priorities zero
+ JP Z,<A href="#L0018">L0018</a> ; exit if zero via GET-CHAR
+
+ PUSH BC ; stack present values
+ PUSH DE ; stack last values
+ CALL <A href="#L0DA6">L0DA6</a> ; routine SYNTAX-Z
+ JR Z,<A href="#L10D5">L10D5</a> ; forward is checking syntax to S-SYNTEST
+
+ LD A,E ; fetch last operation
+ AND $3F ; mask off the indicator bits to give true
+ ; calculator literal.
+ LD B,A ; place in the B register for BREG
+
+; perform the single operation
+
+ RST 28H ;; FP-CALC
+ DEFB $37 ;;fp-calc-2
+ DEFB $34 ;;end-calc
+
+ JR <A href="#L10DE">L10DE</a> ; forward to S-RUNTEST
+
+; ---
+
+<a name="L10D5"></a>;; <b>S-SYNTEST</b>
+L10D5: LD A,E ; transfer masked operator to A
+ XOR (IY+$01) ; XOR with FLAGS like results will reset bit 6
+ AND $40 ; test bit 6
+
+<a name="L10DB"></a>;; <b>S-RPORT-C</b>
+L10DB: JP NZ,<A href="#L0D9A">L0D9A</a> ; back to REPORT-C if results do not agree.
+
+; ---
+
+; in run-time impose bit 7 of the operator onto bit 6 of the FLAGS
+
+<a name="L10DE"></a>;; <b>S-RUNTEST</b>
+L10DE: POP DE ; restore last operation.
+ LD HL,$4001 ; address system variable FLAGS
+ SET 6,(HL) ; presume a numeric result
+ BIT 7,E ; test expected result in operation
+ JR NZ,<A href="#L10EA">L10EA</a> ; forward if numeric to S-LOOPEND
+
+ RES 6,(HL) ; reset to signal string result
+
+<a name="L10EA"></a>;; <b>S-LOOPEND</b>
+L10EA: POP BC ; restore present values
+ JR <A href="#L10BC">L10BC</a> ; back to S-LOOP
+
+; ---
+
+<a name="L10ED"></a>;; <b>S-TIGHTER</b>
+L10ED: PUSH DE ; push last values and consider these
+
+ LD A,C ; get the present operator.
+ BIT 6,(IY+$01) ; test FLAGS - Numeric or string result?
+ JR NZ,<A href="#L110A">L110A</a> ; forward if numeric to S-NEXT
+
+ AND $3F ; strip indicator bits to give clear literal.
+ ADD A,$08 ; add eight - augmenting numeric to equivalent
+ ; string literals.
+ LD C,A ; place plain literal back in C.
+ CP $10 ; compare to 'AND'
+ JR NZ,<A href="#L1102">L1102</a> ; forward if not to S-NOT-AND
+
+ SET 6,C ; set the numeric operand required for 'AND'
+ JR <A href="#L110A">L110A</a> ; forward to S-NEXT
+
+; ---
+
+<a name="L1102"></a>;; <b>S-NOT-AND</b>
+L1102: JR C,<A href="#L10DB">L10DB</a> ; back if less than 'AND' to S-RPORT-C
+ ; Nonsense if '-', '*' etc.
+
+ CP $17 ; compare to 'strs-add' literal
+ JR Z,<A href="#L110A">L110A</a> ; forward if so signaling string result
+
+ SET 7,C ; set bit to numeric (Boolean) for others.
+
+<a name="L110A"></a>;; <b>S-NEXT</b>
+L110A: PUSH BC ; stack 'present' values
+
+ RST 20H ; NEXT-CHAR
+ JP <A href="#L0F59">L0F59</a> ; jump back to S-LOOP-1
+
+
+
+; -------------------------
+; THE <b><font color=#333388>'TABLE OF PRIORITIES'</font></b>
+; -------------------------
+;
+;
+
+<a name="L110F"></a>;; <b>tbl-pri</b>
+L110F: DEFB $06 ; '-'
+ DEFB $08 ; '*'
+ DEFB $08 ; '/'
+ DEFB $0A ; '**'
+ DEFB $02 ; 'OR'
+ DEFB $03 ; 'AND'
+ DEFB $05 ; '<='
+ DEFB $05 ; '>='
+ DEFB $05 ; '<>'
+ DEFB $05 ; '>'
+ DEFB $05 ; '<'
+ DEFB $05 ; '='
+ DEFB $06 ; '+'
+
+
+; --------------------------
+; THE <b><font color=#333388>'LOOK-VARS'</font></b> SUBROUTINE
+; --------------------------
+;
+;
+
+<a name="L111C"></a>;; <b>LOOK-VARS</b>
+L111C: SET 6,(IY+$01) ; sv FLAGS - Signal numeric result
+
+ RST 18H ; GET-CHAR
+ CALL <A href="#L14CE">L14CE</a> ; routine ALPHA
+ JP NC,<A href="#L0D9A">L0D9A</a> ; to REPORT-C
+
+ PUSH HL ;
+ LD C,A ;
+
+ RST 20H ; NEXT-CHAR
+ PUSH HL ;
+ RES 5,C ;
+ CP $10 ;
+ JR Z,<A href="#L1148">L1148</a> ; to V-SYN/RUN
+
+ SET 6,C ;
+ CP $0D ;
+ JR Z,<A href="#L1143">L1143</a> ; forward to V-STR-VAR
+
+ SET 5,C ;
+
+<a name="L1139"></a>;; <b>V-CHAR</b>
+L1139: CALL <A href="#L14D2">L14D2</a> ; routine ALPHANUM
+ JR NC,<A href="#L1148">L1148</a> ; forward when not to V-RUN/SYN
+
+ RES 6,C ;
+
+ RST 20H ; NEXT-CHAR
+ JR <A href="#L1139">L1139</a> ; loop back to V-CHAR
+
+; ---
+
+<a name="L1143"></a>;; <b>V-STR-VAR</b>
+L1143: RST 20H ; NEXT-CHAR
+ RES 6,(IY+$01) ; sv FLAGS - Signal string result
+
+<a name="L1148"></a>;; <b>V-RUN/SYN</b>
+L1148: LD B,C ;
+ CALL <A href="#L0DA6">L0DA6</a> ; routine SYNTAX-Z
+ JR NZ,<A href="#L1156">L1156</a> ; forward to V-RUN
+
+ LD A,C ;
+ AND $E0 ;
+ SET 7,A ;
+ LD C,A ;
+ JR <A href="#L118A">L118A</a> ; forward to V-SYNTAX
+
+; ---
+
+<a name="L1156"></a>;; <b>V-RUN</b>
+L1156: LD HL,($4010) ; sv VARS
+
+<a name="L1159"></a>;; <b>V-EACH</b>
+L1159: LD A,(HL) ;
+ AND $7F ;
+ JR Z,<A href="#L1188">L1188</a> ; to V-80-BYTE
+
+ CP C ;
+ JR NZ,<A href="#L1180">L1180</a> ; to V-NEXT
+
+ RLA ;
+ ADD A,A ;
+ JP P,<A href="#L1195">L1195</a> ; to V-FOUND-2
+
+ JR C,<A href="#L1195">L1195</a> ; to V-FOUND-2
+
+ POP DE ;
+ PUSH DE ;
+ PUSH HL ;
+
+<a name="L116B"></a>;; <b>V-MATCHES</b>
+L116B: INC HL ;
+
+<a name="L116C"></a>;; <b>V-SPACES</b>
+L116C: LD A,(DE) ;
+ INC DE ;
+ AND A ;
+ JR Z,<A href="#L116C">L116C</a> ; back to V-SPACES
+
+ CP (HL) ;
+ JR Z,<A href="#L116B">L116B</a> ; back to V-MATCHES
+
+ OR $80 ;
+ CP (HL) ;
+ JR NZ,<A href="#L117F">L117F</a> ; forward to V-GET-PTR
+
+ LD A,(DE) ;
+ CALL <A href="#L14D2">L14D2</a> ; routine ALPHANUM
+ JR NC,<A href="#L1194">L1194</a> ; forward to V-FOUND-1
+
+<a name="L117F"></a>;; <b>V-GET-PTR</b>
+L117F: POP HL ;
+
+<a name="L1180"></a>;; <b>V-NEXT</b>
+L1180: PUSH BC ;
+ CALL <A href="#L09F2">L09F2</a> ; routine NEXT-ONE
+ EX DE,HL ;
+ POP BC ;
+ JR <A href="#L1159">L1159</a> ; back to V-EACH
+
+; ---
+
+<a name="L1188"></a>;; <b>V-80-BYTE</b>
+L1188: SET 7,B ;
+
+<a name="L118A"></a>;; <b>V-SYNTAX</b>
+L118A: POP DE ;
+
+ RST 18H ; GET-CHAR
+ CP $10 ;
+ JR Z,<A href="#L1199">L1199</a> ; forward to V-PASS
+
+ SET 5,B ;
+ JR <A href="#L11A1">L11A1</a> ; forward to V-END
+
+; ---
+
+<a name="L1194"></a>;; <b>V-FOUND-1</b>
+L1194: POP DE ;
+
+<a name="L1195"></a>;; <b>V-FOUND-2</b>
+L1195: POP DE ;
+ POP DE ;
+ PUSH HL ;
+
+ RST 18H ; GET-CHAR
+
+<a name="L1199"></a>;; <b>V-PASS</b>
+L1199: CALL <A href="#L14D2">L14D2</a> ; routine ALPHANUM
+ JR NC,<A href="#L11A1">L11A1</a> ; forward if not alphanumeric to V-END
+
+
+ RST 20H ; NEXT-CHAR
+ JR <A href="#L1199">L1199</a> ; back to V-PASS
+
+; ---
+
+<a name="L11A1"></a>;; <b>V-END</b>
+L11A1: POP HL ;
+ RL B ;
+ BIT 6,B ;
+ RET ;
+
+; ------------------------
+; THE <b><font color=#333388>'STK-VAR'</font></b> SUBROUTINE
+; ------------------------
+;
+;
+
+<a name="L11A7"></a>;; <b>STK-VAR</b>
+L11A7: XOR A ;
+ LD B,A ;
+ BIT 7,C ;
+ JR NZ,<A href="#L11F8">L11F8</a> ; forward to SV-COUNT
+
+ BIT 7,(HL) ;
+ JR NZ,<A href="#L11BF">L11BF</a> ; forward to SV-ARRAYS
+
+ INC A ;
+
+<a name="L11B2"></a>;; <b>SV-SIMPLE$</b>
+L11B2: INC HL ;
+ LD C,(HL) ;
+ INC HL ;
+ LD B,(HL) ;
+ INC HL ;
+ EX DE,HL ;
+ CALL <A href="#L12C3">L12C3</a> ; routine STK-STO-$
+
+ RST 18H ; GET-CHAR
+ JP <A href="#L125A">L125A</a> ; jump forward to SV-SLICE?
+
+; ---
+
+<a name="L11BF"></a>;; <b>SV-ARRAYS</b>
+L11BF: INC HL ;
+ INC HL ;
+ INC HL ;
+ LD B,(HL) ;
+ BIT 6,C ;
+ JR Z,<A href="#L11D1">L11D1</a> ; forward to SV-PTR
+
+ DEC B ;
+ JR Z,<A href="#L11B2">L11B2</a> ; forward to SV-SIMPLE$
+
+ EX DE,HL ;
+
+ RST 18H ; GET-CHAR
+ CP $10 ;
+ JR NZ,<A href="#L1231">L1231</a> ; forward to REPORT-3
+
+ EX DE,HL ;
+
+<a name="L11D1"></a>;; <b>SV-PTR</b>
+L11D1: EX DE,HL ;
+ JR <A href="#L11F8">L11F8</a> ; forward to SV-COUNT
+
+; ---
+
+<a name="L11D4"></a>;; <b>SV-COMMA</b>
+L11D4: PUSH HL ;
+
+ RST 18H ; GET-CHAR
+ POP HL ;
+ CP $1A ;
+ JR Z,<A href="#L11FB">L11FB</a> ; forward to SV-LOOP
+
+ BIT 7,C ;
+ JR Z,<A href="#L1231">L1231</a> ; forward to REPORT-3
+
+ BIT 6,C ;
+ JR NZ,<A href="#L11E9">L11E9</a> ; forward to SV-CLOSE
+
+ CP $11 ;
+ JR NZ,<A href="#L1223">L1223</a> ; forward to SV-RPT-C
+
+
+ RST 20H ; NEXT-CHAR
+ RET ;
+
+; ---
+
+<a name="L11E9"></a>;; <b>SV-CLOSE</b>
+L11E9: CP $11 ;
+ JR Z,<A href="#L1259">L1259</a> ; forward to SV-DIM
+
+ CP $DF ;
+ JR NZ,<A href="#L1223">L1223</a> ; forward to SV-RPT-C
+
+
+<a name="L11F1"></a>;; <b>SV-CH-ADD</b>
+L11F1: RST 18H ; GET-CHAR
+ DEC HL ;
+ LD ($4016),HL ; sv CH_ADD
+ JR <A href="#L1256">L1256</a> ; forward to SV-SLICE
+
+; ---
+
+<a name="L11F8"></a>;; <b>SV-COUNT</b>
+L11F8: LD HL,$0000 ;
+
+<a name="L11FB"></a>;; <b>SV-LOOP</b>
+L11FB: PUSH HL ;
+
+ RST 20H ; NEXT-CHAR
+ POP HL ;
+ LD A,C ;
+ CP $C0 ;
+ JR NZ,<A href="#L120C">L120C</a> ; forward to SV-MULT
+
+
+ RST 18H ; GET-CHAR
+ CP $11 ;
+ JR Z,<A href="#L1259">L1259</a> ; forward to SV-DIM
+
+ CP $DF ;
+ JR Z,<A href="#L11F1">L11F1</a> ; back to SV-CH-ADD
+
+<a name="L120C"></a>;; <b>SV-MULT</b>
+L120C: PUSH BC ;
+ PUSH HL ;
+ CALL <A href="#L12FF">L12FF</a> ; routine DE,(DE+1)
+ EX (SP),HL ;
+ EX DE,HL ;
+ CALL <A href="#L12DD">L12DD</a> ; routine INT-EXP1
+ JR C,<A href="#L1231">L1231</a> ; forward to REPORT-3
+
+ DEC BC ;
+ CALL <A href="#L1305">L1305</a> ; routine GET-HL*DE
+ ADD HL,BC ;
+ POP DE ;
+ POP BC ;
+ DJNZ <A href="#L11D4">L11D4</a> ; loop back to SV-COMMA
+
+ BIT 7,C ;
+
+<a name="L1223"></a>;; <b>SV-RPT-C</b>
+L1223: JR NZ,<A href="#L128B">L128B</a> ; relative jump to SL-RPT-C
+
+ PUSH HL ;
+ BIT 6,C ;
+ JR NZ,<A href="#L123D">L123D</a> ; forward to SV-ELEM$
+
+ LD B,D ;
+ LD C,E ;
+
+ RST 18H ; GET-CHAR
+ CP $11 ; is character a ')' ?
+ JR Z,<A href="#L1233">L1233</a> ; skip forward to SV-NUMBER
+
+
+<a name="L1231"></a>;; <b>REPORT-3</b>
+L1231: RST 08H ; ERROR-1
+ DEFB $02 ; Error Report: Subscript wrong
+
+
+<a name="L1233"></a>;; <b>SV-NUMBER</b>
+L1233: RST 20H ; NEXT-CHAR
+ POP HL ;
+ LD DE,$0005 ;
+ CALL <A href="#L1305">L1305</a> ; routine GET-HL*DE
+ ADD HL,BC ;
+ RET ; return >>
+
+; ---
+
+<a name="L123D"></a>;; <b>SV-ELEM$</b>
+L123D: CALL <A href="#L12FF">L12FF</a> ; routine DE,(DE+1)
+ EX (SP),HL ;
+ CALL <A href="#L1305">L1305</a> ; routine GET-HL*DE
+ POP BC ;
+ ADD HL,BC ;
+ INC HL ;
+ LD B,D ;
+ LD C,E ;
+ EX DE,HL ;
+ CALL <A href="#L12C2">L12C2</a> ; routine STK-ST-0
+
+ RST 18H ; GET-CHAR
+ CP $11 ; is it ')' ?
+ JR Z,<A href="#L1259">L1259</a> ; forward if so to SV-DIM
+
+ CP $1A ; is it ',' ?
+ JR NZ,<A href="#L1231">L1231</a> ; back if not to REPORT-3
+
+<a name="L1256"></a>;; <b>SV-SLICE</b>
+L1256: CALL <A href="#L1263">L1263</a> ; routine SLICING
+
+<a name="L1259"></a>;; <b>SV-DIM</b>
+L1259: RST 20H ; NEXT-CHAR
+
+<a name="L125A"></a>;; <b>SV-SLICE?</b>
+L125A: CP $10 ;
+ JR Z,<A href="#L1256">L1256</a> ; back to SV-SLICE
+
+ RES 6,(IY+$01) ; sv FLAGS - Signal string result
+ RET ; return.
+
+; ------------------------
+; THE <b><font color=#333388>'SLICING'</font></b> SUBROUTINE
+; ------------------------
+;
+;
+
+<a name="L1263"></a>;; <b>SLICING</b>
+L1263: CALL <A href="#L0DA6">L0DA6</a> ; routine SYNTAX-Z
+ CALL NZ,<A href="#L13F8">L13F8</a> ; routine STK-FETCH
+
+ RST 20H ; NEXT-CHAR
+ CP $11 ; is it ')' ?
+ JR Z,<A href="#L12BE">L12BE</a> ; forward if so to SL-STORE
+
+ PUSH DE ;
+ XOR A ;
+ PUSH AF ;
+ PUSH BC ;
+ LD DE,$0001 ;
+
+ RST 18H ; GET-CHAR
+ POP HL ;
+ CP $DF ; is it 'TO' ?
+ JR Z,<A href="#L1292">L1292</a> ; forward if so to SL-SECOND
+
+ POP AF ;
+ CALL <A href="#L12DE">L12DE</a> ; routine INT-EXP2
+ PUSH AF ;
+ LD D,B ;
+ LD E,C ;
+ PUSH HL ;
+
+ RST 18H ; GET-CHAR
+ POP HL ;
+ CP $DF ; is it 'TO' ?
+ JR Z,<A href="#L1292">L1292</a> ; forward if so to SL-SECOND
+
+ CP $11 ;
+
+<a name="L128B"></a>;; <b>SL-RPT-C</b>
+L128B: JP NZ,<A href="#L0D9A">L0D9A</a> ; to REPORT-C
+
+ LD H,D ;
+ LD L,E ;
+ JR <A href="#L12A5">L12A5</a> ; forward to SL-DEFINE
+
+; ---
+
+<a name="L1292"></a>;; <b>SL-SECOND</b>
+L1292: PUSH HL ;
+
+ RST 20H ; NEXT-CHAR
+ POP HL ;
+ CP $11 ; is it ')' ?
+ JR Z,<A href="#L12A5">L12A5</a> ; forward if so to SL-DEFINE
+
+ POP AF ;
+ CALL <A href="#L12DE">L12DE</a> ; routine INT-EXP2
+ PUSH AF ;
+
+ RST 18H ; GET-CHAR
+ LD H,B ;
+ LD L,C ;
+ CP $11 ; is it ')' ?
+ JR NZ,<A href="#L128B">L128B</a> ; back if not to SL-RPT-C
+
+<a name="L12A5"></a>;; <b>SL-DEFINE</b>
+L12A5: POP AF ;
+ EX (SP),HL ;
+ ADD HL,DE ;
+ DEC HL ;
+ EX (SP),HL ;
+ AND A ;
+ SBC HL,DE ;
+ LD BC,$0000 ;
+ JR C,<A href="#L12B9">L12B9</a> ; forward to SL-OVER
+
+ INC HL ;
+ AND A ;
+ JP M,<A href="#L1231">L1231</a> ; jump back to REPORT-3
+
+ LD B,H ;
+ LD C,L ;
+
+<a name="L12B9"></a>;; <b>SL-OVER</b>
+L12B9: POP DE ;
+ RES 6,(IY+$01) ; sv FLAGS - Signal string result
+
+<a name="L12BE"></a>;; <b>SL-STORE</b>
+L12BE: CALL <A href="#L0DA6">L0DA6</a> ; routine SYNTAX-Z
+ RET Z ; return if checking syntax.
+
+; --------------------------
+; THE <b><font color=#333388>'STK-STORE'</font></b> SUBROUTINE
+; --------------------------
+;
+;
+
+<a name="L12C2"></a>;; <b>STK-ST-0</b>
+L12C2: XOR A ;
+
+<a name="L12C3"></a>;; <b>STK-STO-$</b>
+L12C3: PUSH BC ;
+ CALL <A href="#L19EB">L19EB</a> ; routine TEST-5-SP
+ POP BC ;
+ LD HL,($401C) ; sv STKEND
+ LD (HL),A ;
+ INC HL ;
+ LD (HL),E ;
+ INC HL ;
+ LD (HL),D ;
+ INC HL ;
+ LD (HL),C ;
+ INC HL ;
+ LD (HL),B ;
+ INC HL ;
+ LD ($401C),HL ; sv STKEND
+ RES 6,(IY+$01) ; update FLAGS - signal string result
+ RET ; return.
+
+; -------------------------
+; THE <b><font color=#333388>'INT EXP'</font></b> SUBROUTINES
+; -------------------------
+;
+;
+
+<a name="L12DD"></a>;; <b>INT-EXP1</b>
+L12DD: XOR A ;
+
+<a name="L12DE"></a>;; <b>INT-EXP2</b>
+L12DE: PUSH DE ;
+ PUSH HL ;
+ PUSH AF ;
+ CALL <A href="#L0D92">L0D92</a> ; routine CLASS-6
+ POP AF ;
+ CALL <A href="#L0DA6">L0DA6</a> ; routine SYNTAX-Z
+ JR Z,<A href="#L12FC">L12FC</a> ; forward if checking syntax to I-RESTORE
+
+ PUSH AF ;
+ CALL <A href="#L0EA7">L0EA7</a> ; routine FIND-INT
+ POP DE ;
+ LD A,B ;
+ OR C ;
+ SCF ; Set Carry Flag
+ JR Z,<A href="#L12F9">L12F9</a> ; forward to I-CARRY
+
+ POP HL ;
+ PUSH HL ;
+ AND A ;
+ SBC HL,BC ;
+
+<a name="L12F9"></a>;; <b>I-CARRY</b>
+L12F9: LD A,D ;
+ SBC A,$00 ;
+
+<a name="L12FC"></a>;; <b>I-RESTORE</b>
+L12FC: POP HL ;
+ POP DE ;
+ RET ;
+
+; --------------------------
+; THE <b><font color=#333388>'DE,(DE+1)'</font></b> SUBROUTINE
+; --------------------------
+; INDEX and LOAD Z80 subroutine.
+; This emulates the 6800 processor instruction LDX 1,X which loads a two-byte
+; value from memory into the register indexing it. Often these are hardly worth
+; the bother of writing as subroutines and this one doesn't save any time or
+; memory. The timing and space overheads have to be offset against the ease of
+; writing and the greater program readability from using such toolkit routines.
+
+<a name="L12FF"></a>;; <b>DE,(DE+1)</b>
+L12FF: EX DE,HL ; move index address into HL.
+ INC HL ; increment to address word.
+ LD E,(HL) ; pick up word low-order byte.
+ INC HL ; index high-order byte and
+ LD D,(HL) ; pick it up.
+ RET ; return with DE = word.
+
+; --------------------------
+; THE <b><font color=#333388>'GET-HL*DE'</font></b> SUBROUTINE
+; --------------------------
+;
+
+<a name="L1305"></a>;; <b>GET-HL*DE</b>
+L1305: CALL <A href="#L0DA6">L0DA6</a> ; routine SYNTAX-Z
+ RET Z ;
+
+ PUSH BC ;
+ LD B,$10 ;
+ LD A,H ;
+ LD C,L ;
+ LD HL,$0000 ;
+
+<a name="L1311"></a>;; <b>HL-LOOP</b>
+L1311: ADD HL,HL ;
+ JR C,<A href="#L131A">L131A</a> ; forward with carry to HL-END
+
+ RL C ;
+ RLA ;
+ JR NC,<A href="#L131D">L131D</a> ; forward with no carry to HL-AGAIN
+
+ ADD HL,DE ;
+
+<a name="L131A"></a>;; <b>HL-END</b>
+L131A: JP C,<A href="#L0ED3">L0ED3</a> ; to REPORT-4
+
+<a name="L131D"></a>;; <b>HL-AGAIN</b>
+L131D: DJNZ <A href="#L1311">L1311</a> ; loop back to HL-LOOP
+
+ POP BC ;
+ RET ; return.
+
+; --------------------
+; THE <b><font color=#333388>'LET'</font></b> SUBROUTINE
+; --------------------
+;
+;
+
+<a name="L1321"></a>;; <b>LET</b>
+L1321: LD HL,($4012) ; sv DEST-lo
+ BIT 1,(IY+$2D) ; sv FLAGX
+ JR Z,<A href="#L136E">L136E</a> ; forward to L-EXISTS
+
+ LD BC,$0005 ;
+
+<a name="L132D"></a>;; <b>L-EACH-CH</b>
+L132D: INC BC ;
+
+; check
+
+<a name="L132E"></a>;; <b>L-NO-SP</b>
+L132E: INC HL ;
+ LD A,(HL) ;
+ AND A ;
+ JR Z,<A href="#L132E">L132E</a> ; back to L-NO-SP
+
+ CALL <A href="#L14D2">L14D2</a> ; routine ALPHANUM
+ JR C,<A href="#L132D">L132D</a> ; back to L-EACH-CH
+
+ CP $0D ; is it '$' ?
+ JP Z,<A href="#L13C8">L13C8</a> ; forward if so to L-NEW$
+
+
+ RST 30H ; BC-SPACES
+ PUSH DE ;
+ LD HL,($4012) ; sv DEST
+ DEC DE ;
+ LD A,C ;
+ SUB $06 ;
+ LD B,A ;
+ LD A,$40 ;
+ JR Z,<A href="#L1359">L1359</a> ; forward to L-SINGLE
+
+<a name="L134B"></a>;; <b>L-CHAR</b>
+L134B: INC HL ;
+ LD A,(HL) ;
+ AND A ; is it a space ?
+ JR Z,<A href="#L134B">L134B</a> ; back to L-CHAR
+
+ INC DE ;
+ LD (DE),A ;
+ DJNZ <A href="#L134B">L134B</a> ; loop back to L-CHAR
+
+ OR $80 ;
+ LD (DE),A ;
+ LD A,$80 ;
+
+<a name="L1359"></a>;; <b>L-SINGLE</b>
+L1359: LD HL,($4012) ; sv DEST-lo
+ XOR (HL) ;
+ POP HL ;
+ CALL <A href="#L13E7">L13E7</a> ; routine L-FIRST
+
+<a name="L1361"></a>;; <b>L-NUMERIC</b>
+L1361: PUSH HL ;
+
+ RST 28H ;; FP-CALC
+ DEFB $02 ;;delete
+ DEFB $34 ;;end-calc
+
+ POP HL ;
+ LD BC,$0005 ;
+ AND A ;
+ SBC HL,BC ;
+ JR <A href="#L13AE">L13AE</a> ; forward to L-ENTER
+
+; ---
+
+<a name="L136E"></a>;; <b>L-EXISTS</b>
+L136E: BIT 6,(IY+$01) ; sv FLAGS - Numeric or string result?
+ JR Z,<A href="#L137A">L137A</a> ; forward to L-DELETE$
+
+ LD DE,$0006 ;
+ ADD HL,DE ;
+ JR <A href="#L1361">L1361</a> ; back to L-NUMERIC
+
+; ---
+
+<a name="L137A"></a>;; <b>L-DELETE$</b>
+L137A: LD HL,($4012) ; sv DEST-lo
+ LD BC,($402E) ; sv STRLEN_lo
+ BIT 0,(IY+$2D) ; sv FLAGX
+ JR NZ,<A href="#L13B7">L13B7</a> ; forward to L-ADD$
+
+ LD A,B ;
+ OR C ;
+ RET Z ;
+
+ PUSH HL ;
+
+ RST 30H ; BC-SPACES
+ PUSH DE ;
+ PUSH BC ;
+ LD D,H ;
+ LD E,L ;
+ INC HL ;
+ LD (HL),$00 ;
+ LDDR ; Copy Bytes
+ PUSH HL ;
+ CALL <A href="#L13F8">L13F8</a> ; routine STK-FETCH
+ POP HL ;
+ EX (SP),HL ;
+ AND A ;
+ SBC HL,BC ;
+ ADD HL,BC ;
+ JR NC,<A href="#L13A3">L13A3</a> ; forward to L-LENGTH
+
+ LD B,H ;
+ LD C,L ;
+
+<a name="L13A3"></a>;; <b>L-LENGTH</b>
+L13A3: EX (SP),HL ;
+ EX DE,HL ;
+ LD A,B ;
+ OR C ;
+ JR Z,<A href="#L13AB">L13AB</a> ; forward if zero to L-IN-W/S
+
+ LDIR ; Copy Bytes
+
+<a name="L13AB"></a>;; <b>L-IN-W/S</b>
+L13AB: POP BC ;
+ POP DE ;
+ POP HL ;
+
+; ------------------------
+; THE <b><font color=#333388>'L-ENTER'</font></b> SUBROUTINE
+; ------------------------
+;
+
+<a name="L13AE"></a>;; <b>L-ENTER</b>
+L13AE: EX DE,HL ;
+ LD A,B ;
+ OR C ;
+ RET Z ;
+
+ PUSH DE ;
+ LDIR ; Copy Bytes
+ POP HL ;
+ RET ; return.
+
+; ---
+
+<a name="L13B7"></a>;; <b>L-ADD$</b>
+L13B7: DEC HL ;
+ DEC HL ;
+ DEC HL ;
+ LD A,(HL) ;
+ PUSH HL ;
+ PUSH BC ;
+
+ CALL <A href="#L13CE">L13CE</a> ; routine L-STRING
+
+ POP BC ;
+ POP HL ;
+ INC BC ;
+ INC BC ;
+ INC BC ;
+ JP <A href="#L0A60">L0A60</a> ; jump back to exit via RECLAIM-2
+
+; ---
+
+<a name="L13C8"></a>;; <b>L-NEW$</b>
+L13C8: LD A,$60 ; prepare mask %01100000
+ LD HL,($4012) ; sv DEST-lo
+ XOR (HL) ;
+
+; -------------------------
+; THE <b><font color=#333388>'L-STRING'</font></b> SUBROUTINE
+; -------------------------
+;
+
+<a name="L13CE"></a>;; <b>L-STRING</b>
+L13CE: PUSH AF ;
+ CALL <A href="#L13F8">L13F8</a> ; routine STK-FETCH
+ EX DE,HL ;
+ ADD HL,BC ;
+ PUSH HL ;
+ INC BC ;
+ INC BC ;
+ INC BC ;
+
+ RST 30H ; BC-SPACES
+ EX DE,HL ;
+ POP HL ;
+ DEC BC ;
+ DEC BC ;
+ PUSH BC ;
+ LDDR ; Copy Bytes
+ EX DE,HL ;
+ POP BC ;
+ DEC BC ;
+ LD (HL),B ;
+ DEC HL ;
+ LD (HL),C ;
+ POP AF ;
+
+<a name="L13E7"></a>;; <b>L-FIRST</b>
+L13E7: PUSH AF ;
+ CALL <A href="#L14C7">L14C7</a> ; routine REC-V80
+ POP AF ;
+ DEC HL ;
+ LD (HL),A ;
+ LD HL,($401A) ; sv STKBOT_lo
+ LD ($4014),HL ; sv E_LINE_lo
+ DEC HL ;
+ LD (HL),$80 ;
+ RET ;
+
+; --------------------------
+; THE <b><font color=#333388>'STK-FETCH'</font></b> SUBROUTINE
+; --------------------------
+; This routine fetches a five-byte value from the calculator stack
+; reducing the pointer to the end of the stack by five.
+; For a floating-point number the exponent is in A and the mantissa
+; is the thirty-two bits EDCB.
+; For strings, the start of the string is in DE and the length in BC.
+; A is unused.
+
+<a name="L13F8"></a>;; <b>STK-FETCH</b>
+L13F8: LD HL,($401C) ; load HL from system variable STKEND
+
+ DEC HL ;
+ LD B,(HL) ;
+ DEC HL ;
+ LD C,(HL) ;
+ DEC HL ;
+ LD D,(HL) ;
+ DEC HL ;
+ LD E,(HL) ;
+ DEC HL ;
+ LD A,(HL) ;
+
+ LD ($401C),HL ; set system variable STKEND to lower value.
+ RET ; return.
+
+; -------------------------
+; THE <b><font color=#333388>'DIM'</font></b> COMMAND ROUTINE
+; -------------------------
+; An array is created and initialized to zeros which is also the space
+; character on the ZX81.
+
+<a name="L1409"></a>;; <b>DIM</b>
+L1409: CALL <A href="#L111C">L111C</a> ; routine LOOK-VARS
+
+<a name="L140C"></a>;; <b>D-RPORT-C</b>
+L140C: JP NZ,<A href="#L0D9A">L0D9A</a> ; to REPORT-C
+
+ CALL <A href="#L0DA6">L0DA6</a> ; routine SYNTAX-Z
+ JR NZ,<A href="#L141C">L141C</a> ; forward to D-RUN
+
+ RES 6,C ;
+ CALL <A href="#L11A7">L11A7</a> ; routine STK-VAR
+ CALL <A href="#L0D1D">L0D1D</a> ; routine CHECK-END
+
+<a name="L141C"></a>;; <b>D-RUN</b>
+L141C: JR C,<A href="#L1426">L1426</a> ; forward to D-LETTER
+
+ PUSH BC ;
+ CALL <A href="#L09F2">L09F2</a> ; routine NEXT-ONE
+ CALL <A href="#L0A60">L0A60</a> ; routine RECLAIM-2
+ POP BC ;
+
+<a name="L1426"></a>;; <b>D-LETTER</b>
+L1426: SET 7,C ;
+ LD B,$00 ;
+ PUSH BC ;
+ LD HL,$0001 ;
+ BIT 6,C ;
+ JR NZ,<A href="#L1434">L1434</a> ; forward to D-SIZE
+
+ LD L,$05 ;
+
+<a name="L1434"></a>;; <b>D-SIZE</b>
+L1434: EX DE,HL ;
+
+<a name="L1435"></a>;; <b>D-NO-LOOP</b>
+L1435: RST 20H ; NEXT-CHAR
+ LD H,$40 ;
+ CALL <A href="#L12DD">L12DD</a> ; routine INT-EXP1
+ JP C,<A href="#L1231">L1231</a> ; jump back to REPORT-3
+
+ POP HL ;
+ PUSH BC ;
+ INC H ;
+ PUSH HL ;
+ LD H,B ;
+ LD L,C ;
+ CALL <A href="#L1305">L1305</a> ; routine GET-HL*DE
+ EX DE,HL ;
+
+ RST 18H ; GET-CHAR
+ CP $1A ;
+ JR Z,<A href="#L1435">L1435</a> ; back to D-NO-LOOP
+
+ CP $11 ; is it ')' ?
+ JR NZ,<A href="#L140C">L140C</a> ; back if not to D-RPORT-C
+
+
+ RST 20H ; NEXT-CHAR
+ POP BC ;
+ LD A,C ;
+ LD L,B ;
+ LD H,$00 ;
+ INC HL ;
+ INC HL ;
+ ADD HL,HL ;
+ ADD HL,DE ;
+ JP C,<A href="#L0ED3">L0ED3</a> ; jump to REPORT-4
+
+ PUSH DE ;
+ PUSH BC ;
+ PUSH HL ;
+ LD B,H ;
+ LD C,L ;
+ LD HL,($4014) ; sv E_LINE_lo
+ DEC HL ;
+ CALL <A href="#L099E">L099E</a> ; routine MAKE-ROOM
+ INC HL ;
+ LD (HL),A ;
+ POP BC ;
+ DEC BC ;
+ DEC BC ;
+ DEC BC ;
+ INC HL ;
+ LD (HL),C ;
+ INC HL ;
+ LD (HL),B ;
+ POP AF ;
+ INC HL ;
+ LD (HL),A ;
+ LD H,D ;
+ LD L,E ;
+ DEC DE ;
+ LD (HL),$00 ;
+ POP BC ;
+ LDDR ; Copy Bytes
+
+<a name="L147F"></a>;; <b>DIM-SIZES</b>
+L147F: POP BC ;
+ LD (HL),B ;
+ DEC HL ;
+ LD (HL),C ;
+ DEC HL ;
+ DEC A ;
+ JR NZ,<A href="#L147F">L147F</a> ; back to DIM-SIZES
+
+ RET ; return.
+
+; ---------------------
+; THE <b><font color=#333388>'RESERVE'</font></b> ROUTINE
+; ---------------------
+;
+;
+
+<a name="L1488"></a>;; <b>RESERVE</b>
+L1488: LD HL,($401A) ; address STKBOT
+ DEC HL ; now last byte of workspace
+ CALL <A href="#L099E">L099E</a> ; routine MAKE-ROOM
+ INC HL ;
+ INC HL ;
+ POP BC ;
+ LD ($4014),BC ; sv E_LINE_lo
+ POP BC ;
+ EX DE,HL ;
+ INC HL ;
+ RET ;
+
+; ---------------------------
+; THE <b><font color=#333388>'CLEAR'</font></b> COMMAND ROUTINE
+; ---------------------------
+;
+;
+
+<a name="L149A"></a>;; <b>CLEAR</b>
+L149A: LD HL,($4010) ; sv VARS_lo
+ LD (HL),$80 ;
+ INC HL ;
+ LD ($4014),HL ; sv E_LINE_lo
+
+; -----------------------
+; THE <b><font color=#333388>'X-TEMP'</font></b> SUBROUTINE
+; -----------------------
+;
+;
+
+<a name="L14A3"></a>;; <b>X-TEMP</b>
+L14A3: LD HL,($4014) ; sv E_LINE_lo
+
+; ----------------------
+; THE <b><font color=#333388>'SET-STK'</font></b> ROUTINES
+; ----------------------
+;
+;
+
+<a name="L14A6"></a>;; <b>SET-STK-B</b>
+L14A6: LD ($401A),HL ; sv STKBOT
+
+;
+
+<a name="L14A9"></a>;; <b>SET-STK-E</b>
+L14A9: LD ($401C),HL ; sv STKEND
+ RET ;
+
+; -----------------------
+; THE <b><font color=#333388>'CURSOR-IN'</font></b> ROUTINE
+; -----------------------
+; This routine is called to set the edit line to the minimum cursor/newline
+; and to set STKEND, the start of free space, at the next position.
+
+<a name="L14AD"></a>;; <b>CURSOR-IN</b>
+L14AD: LD HL,($4014) ; fetch start of edit line from E_LINE
+ LD (HL),$7F ; insert cursor character
+
+ INC HL ; point to next location.
+ LD (HL),$76 ; insert NEWLINE character
+ INC HL ; point to next free location.
+
+ LD (IY+$22),$02 ; set lower screen display file size DF_SZ
+
+ JR <A href="#L14A6">L14A6</a> ; exit via SET-STK-B above
+
+; ------------------------
+; THE <b><font color=#333388>'SET-MIN'</font></b> SUBROUTINE
+; ------------------------
+;
+;
+
+<a name="L14BC"></a>;; <b>SET-MIN</b>
+L14BC: LD HL,$405D ; normal location of calculator's memory area
+ LD ($401F),HL ; update system variable MEM
+ LD HL,($401A) ; fetch STKBOT
+ JR <A href="#L14A9">L14A9</a> ; back to SET-STK-E
+
+
+; ------------------------------------
+; THE <b><font color=#333388>'RECLAIM THE END-MARKER'</font></b> ROUTINE
+; ------------------------------------
+
+<a name="L14C7"></a>;; <b>REC-V80</b>
+L14C7: LD DE,($4014) ; sv E_LINE_lo
+ JP <A href="#L0A5D">L0A5D</a> ; to RECLAIM-1
+
+; ----------------------
+; THE <b><font color=#333388>'ALPHA'</font></b> SUBROUTINE
+; ----------------------
+
+<a name="L14CE"></a>;; <b>ALPHA</b>
+L14CE: CP $26 ;
+ JR <A href="#L14D4">L14D4</a> ; skip forward to ALPHA-2
+
+
+; -------------------------
+; THE <b><font color=#333388>'ALPHANUM'</font></b> SUBROUTINE
+; -------------------------
+
+<a name="L14D2"></a>;; <b>ALPHANUM</b>
+L14D2: CP $1C ;
+
+
+<a name="L14D4"></a>;; <b>ALPHA-2</b>
+L14D4: CCF ; Complement Carry Flag
+ RET NC ;
+
+ CP $40 ;
+ RET ;
+
+
+; ------------------------------------------
+; THE <b><font color=#333388>'DECIMAL TO FLOATING POINT'</font></b> SUBROUTINE
+; ------------------------------------------
+;
+
+<a name="L14D9"></a>;; <b>DEC-TO-FP</b>
+L14D9: CALL <A href="#L1548">L1548</a> ; routine INT-TO-FP gets first part
+ CP $1B ; is character a '.' ?
+ JR NZ,<A href="#L14F5">L14F5</a> ; forward if not to E-FORMAT
+
+
+ RST 28H ;; FP-CALC
+ DEFB $A1 ;;stk-one
+ DEFB $C0 ;;st-mem-0
+ DEFB $02 ;;delete
+ DEFB $34 ;;end-calc
+
+
+<a name="L14E5"></a>;; <b>NXT-DGT-1</b>
+L14E5: RST 20H ; NEXT-CHAR
+ CALL <A href="#L1514">L1514</a> ; routine STK-DIGIT
+ JR C,<A href="#L14F5">L14F5</a> ; forward to E-FORMAT
+
+
+ RST 28H ;; FP-CALC
+ DEFB $E0 ;;get-mem-0
+ DEFB $A4 ;;stk-ten
+ DEFB $05 ;;division
+ DEFB $C0 ;;st-mem-0
+ DEFB $04 ;;multiply
+ DEFB $0F ;;addition
+ DEFB $34 ;;end-calc
+
+ JR <A href="#L14E5">L14E5</a> ; loop back till exhausted to NXT-DGT-1
+
+; ---
+
+<a name="L14F5"></a>;; <b>E-FORMAT</b>
+L14F5: CP $2A ; is character 'E' ?
+ RET NZ ; return if not
+
+ LD (IY+$5D),$FF ; initialize sv MEM-0-1st to $FF TRUE
+
+ RST 20H ; NEXT-CHAR
+ CP $15 ; is character a '+' ?
+ JR Z,<A href="#L1508">L1508</a> ; forward if so to SIGN-DONE
+
+ CP $16 ; is it a '-' ?
+ JR NZ,<A href="#L1509">L1509</a> ; forward if not to ST-E-PART
+
+ INC (IY+$5D) ; sv MEM-0-1st change to FALSE
+
+<a name="L1508"></a>;; <b>SIGN-DONE</b>
+L1508: RST 20H ; NEXT-CHAR
+
+<a name="L1509"></a>;; <b>ST-E-PART</b>
+L1509: CALL <A href="#L1548">L1548</a> ; routine INT-TO-FP
+
+ RST 28H ;; FP-CALC m, e.
+ DEFB $E0 ;;get-mem-0 m, e, (1/0) TRUE/FALSE
+ DEFB $00 ;;jump-true
+ DEFB $02 ;;to <A href="#L1511">L1511</a>, E-POSTVE
+ DEFB $18 ;;neg m, -e
+
+<a name="L1511"></a>;; <b>E-POSTVE</b>
+L1511: DEFB $38 ;;e-to-fp x.
+ DEFB $34 ;;end-calc x.
+
+ RET ; return.
+
+
+; --------------------------
+; THE <b><font color=#333388>'STK-DIGIT'</font></b> SUBROUTINE
+; --------------------------
+;
+
+<a name="L1514"></a>;; <b>STK-DIGIT</b>
+L1514: CP $1C ;
+ RET C ;
+
+ CP $26 ;
+ CCF ; Complement Carry Flag
+ RET C ;
+
+ SUB $1C ;
+
+; ------------------------
+; THE <b><font color=#333388>'STACK-A'</font></b> SUBROUTINE
+; ------------------------
+;
+
+
+<a name="L151D"></a>;; <b>STACK-A</b>
+L151D: LD C,A ;
+ LD B,$00 ;
+
+; -------------------------
+; THE <b><font color=#333388>'STACK-BC'</font></b> SUBROUTINE
+; -------------------------
+; The ZX81 does not have an integer number format so the BC register contents
+; must be converted to their full floating-point form.
+
+<a name="L1520"></a>;; <b>STACK-BC</b>
+L1520: LD IY,$4000 ; re-initialize the system variables pointer.
+ PUSH BC ; save the integer value.
+
+; now stack zero, five zero bytes as a starting point.
+
+ RST 28H ;; FP-CALC
+ DEFB $A0 ;;stk-zero 0.
+ DEFB $34 ;;end-calc
+
+ POP BC ; restore integer value.
+
+ LD (HL),$91 ; place $91 in exponent 65536.
+ ; this is the maximum possible value
+
+ LD A,B ; fetch hi-byte.
+ AND A ; test for zero.
+ JR NZ,<A href="#L1536">L1536</a> ; forward if not zero to STK-BC-2
+
+ LD (HL),A ; else make exponent zero again
+ OR C ; test lo-byte
+ RET Z ; return if BC was zero - done.
+
+; else there has to be a set bit if only the value one.
+
+ LD B,C ; save C in B.
+ LD C,(HL) ; fetch zero to C
+ LD (HL),$89 ; make exponent $89 256.
+
+<a name="L1536"></a>;; <b>STK-BC-2</b>
+L1536: DEC (HL) ; decrement exponent - halving number
+ SLA C ; C<-76543210<-0
+ RL B ; C<-76543210<-C
+ JR NC,<A href="#L1536">L1536</a> ; loop back if no carry to STK-BC-2
+
+ SRL B ; 0->76543210->C
+ RR C ; C->76543210->C
+
+ INC HL ; address first byte of mantissa
+ LD (HL),B ; insert B
+ INC HL ; address second byte of mantissa
+ LD (HL),C ; insert C
+
+ DEC HL ; point to the
+ DEC HL ; exponent again
+ RET ; return.
+
+; ------------------------------------------
+; THE <b><font color=#333388>'INTEGER TO FLOATING POINT'</font></b> SUBROUTINE
+; ------------------------------------------
+;
+;
+
+<a name="L1548"></a>;; <b>INT-TO-FP</b>
+L1548: PUSH AF ;
+
+ RST 28H ;; FP-CALC
+ DEFB $A0 ;;stk-zero
+ DEFB $34 ;;end-calc
+
+ POP AF ;
+
+<a name="L154D"></a>;; <b>NXT-DGT-2</b>
+L154D: CALL <A href="#L1514">L1514</a> ; routine STK-DIGIT
+ RET C ;
+
+
+ RST 28H ;; FP-CALC
+ DEFB $01 ;;exchange
+ DEFB $A4 ;;stk-ten
+ DEFB $04 ;;multiply
+ DEFB $0F ;;addition
+ DEFB $34 ;;end-calc
+
+
+ RST 20H ; NEXT-CHAR
+ JR <A href="#L154D">L154D</a> ; to NXT-DGT-2
+
+
+; -------------------------------------------
+; THE <b><font color=#333388>'E-FORMAT TO FLOATING POINT'</font></b> SUBROUTINE
+; -------------------------------------------
+; <font color=#339933>(Offset $38: 'e-to-fp')</font>
+; invoked from DEC-TO-FP and PRINT-FP.
+; e.g. 2.3E4 is 23000.
+; This subroutine evaluates xEm where m is a positive or negative integer.
+; At a simple level x is multiplied by ten for every unit of m.
+; If the decimal exponent m is negative then x is divided by ten for each unit.
+; A short-cut is taken if the exponent is greater than seven and in this
+; case the exponent is reduced by seven and the value is multiplied or divided
+; by ten million.
+; <font color=#9900FF>Note.</font> for the ZX Spectrum an even cleverer method was adopted which involved
+; shifting the bits out of the exponent so the result was achieved with six
+; shifts at most. The routine below had to be completely re-written mostly
+; in Z80 machine code.
+; Although no longer operable, the calculator literal was retained for old
+; times sake, the routine being invoked directly from a machine code CALL.
+;
+; On entry in the ZX81, m, the exponent, is the 'last value', and the
+; floating-point decimal mantissa is beneath it.
+
+
+<a name="L155A"></a>;; <b>e-to-fp</b>
+L155A: RST 28H ;; FP-CALC x, m.
+ DEFB $2D ;;duplicate x, m, m.
+ DEFB $32 ;;less-0 x, m, (1/0).
+ DEFB $C0 ;;st-mem-0 x, m, (1/0).
+ DEFB $02 ;;delete x, m.
+ DEFB $27 ;;abs x, +m.
+
+<a name="L1560"></a>;; <b>E-LOOP</b>
+L1560: DEFB $A1 ;;stk-one x, m,1.
+ DEFB $03 ;;subtract x, m-1.
+ DEFB $2D ;;duplicate x, m-1,m-1.
+ DEFB $32 ;;less-0 x, m-1, (1/0).
+ DEFB $00 ;;jump-true x, m-1.
+ DEFB $22 ;;to <A href="#L1587">L1587</a>, E-END x, m-1.
+
+ DEFB $2D ;;duplicate x, m-1, m-1.
+ DEFB $30 ;;stk-data
+ DEFB $33 ;;Exponent: $83, Bytes: 1
+
+ DEFB $40 ;;(+00,+00,+00) x, m-1, m-1, 6.
+ DEFB $03 ;;subtract x, m-1, m-7.
+ DEFB $2D ;;duplicate x, m-1, m-7, m-7.
+ DEFB $32 ;;less-0 x, m-1, m-7, (1/0).
+ DEFB $00 ;;jump-true x, m-1, m-7.
+ DEFB $0C ;;to <A href="#L157A">L157A</a>, E-LOW
+
+; but if exponent m is higher than 7 do a bigger chunk.
+; multiplying (or dividing if negative) by 10 million - 1e7.
+
+ DEFB $01 ;;exchange x, m-7, m-1.
+ DEFB $02 ;;delete x, m-7.
+ DEFB $01 ;;exchange m-7, x.
+ DEFB $30 ;;stk-data
+ DEFB $80 ;;Bytes: 3
+ DEFB $48 ;;Exponent $98
+ DEFB $18,$96,$80 ;;(+00) m-7, x, 10,000,000 (=f)
+ DEFB $2F ;;jump
+ DEFB $04 ;;to <A href="#L157D">L157D</a>, E-CHUNK
+
+; ---
+
+<a name="L157A"></a>;; <b>E-LOW</b>
+L157A: DEFB $02 ;;delete x, m-1.
+ DEFB $01 ;;exchange m-1, x.
+ DEFB $A4 ;;stk-ten m-1, x, 10 (=f).
+
+<a name="L157D"></a>;; <b>E-CHUNK</b>
+L157D: DEFB $E0 ;;get-mem-0 m-1, x, f, (1/0)
+ DEFB $00 ;;jump-true m-1, x, f
+ DEFB $04 ;;to <A href="#L1583">L1583</a>, E-DIVSN
+
+ DEFB $04 ;;multiply m-1, x*f.
+ DEFB $2F ;;jump
+ DEFB $02 ;;to <A href="#L1584">L1584</a>, E-SWAP
+
+; ---
+
+<a name="L1583"></a>;; <b>E-DIVSN</b>
+L1583: DEFB $05 ;;division m-1, x/f (= new x).
+
+<a name="L1584"></a>;; <b>E-SWAP</b>
+L1584: DEFB $01 ;;exchange x, m-1 (= new m).
+ DEFB $2F ;;jump x, m.
+ DEFB $DA ;;to <A href="#L1560">L1560</a>, E-LOOP
+
+; ---
+
+<a name="L1587"></a>;; <b>E-END</b>
+L1587: DEFB $02 ;;delete x. (-1)
+ DEFB $34 ;;end-calc x.
+
+ RET ; return.
+
+; -------------------------------------
+; THE <b><font color=#333388>'FLOATING-POINT TO BC'</font></b> SUBROUTINE
+; -------------------------------------
+; The floating-point form on the calculator stack is compressed directly into
+; the BC register rounding up if necessary.
+; Valid range is 0 to 65535.4999
+
+<a name="L158A"></a>;; <b>FP-TO-BC</b>
+L158A: CALL <A href="#L13F8">L13F8</a> ; routine STK-FETCH - exponent to A
+ ; mantissa to EDCB.
+ AND A ; test for value zero.
+ JR NZ,<A href="#L1595">L1595</a> ; forward if not to FPBC-NZRO
+
+; else value is zero
+
+ LD B,A ; zero to B
+ LD C,A ; also to C
+ PUSH AF ; save the flags on machine stack
+ JR <A href="#L15C6">L15C6</a> ; forward to FPBC-END
+
+; ---
+
+; EDCB => BCE
+
+<a name="L1595"></a>;; <b>FPBC-NZRO</b>
+L1595: LD B,E ; transfer the mantissa from EDCB
+ LD E,C ; to BCE. Bit 7 of E is the 17th bit which
+ LD C,D ; will be significant for rounding if the
+ ; number is already normalized.
+
+ SUB $91 ; subtract 65536
+ CCF ; complement carry flag
+ BIT 7,B ; test sign bit
+ PUSH AF ; push the result
+
+ SET 7,B ; set the implied bit
+ JR C,<A href="#L15C6">L15C6</a> ; forward with carry from SUB/CCF to FPBC-END
+ ; number is too big.
+
+ INC A ; increment the exponent and
+ NEG ; negate to make range $00 - $0F
+
+ CP $08 ; test if one or two bytes
+ JR C,<A href="#L15AF">L15AF</a> ; forward with two to BIG-INT
+
+ LD E,C ; shift mantissa
+ LD C,B ; 8 places right
+ LD B,$00 ; insert a zero in B
+ SUB $08 ; reduce exponent by eight
+
+<a name="L15AF"></a>;; <b>BIG-INT</b>
+L15AF: AND A ; test the exponent
+ LD D,A ; save exponent in D.
+
+ LD A,E ; fractional bits to A
+ RLCA ; rotate most significant bit to carry for
+ ; rounding of an already normal number.
+
+ JR Z,<A href="#L15BC">L15BC</a> ; forward if exponent zero to EXP-ZERO
+ ; the number is normalized
+
+<a name="L15B5"></a>;; <b>FPBC-NORM</b>
+L15B5: SRL B ; 0->76543210->C
+ RR C ; C->76543210->C
+
+ DEC D ; decrement exponent
+
+ JR NZ,<A href="#L15B5">L15B5</a> ; loop back till zero to FPBC-NORM
+
+<a name="L15BC"></a>;; <b>EXP-ZERO</b>
+L15BC: JR NC,<A href="#L15C6">L15C6</a> ; forward without carry to NO-ROUND
+
+ INC BC ; round up.
+ LD A,B ; test result
+ OR C ; for zero
+ JR NZ,<A href="#L15C6">L15C6</a> ; forward if not to GRE-ZERO
+
+ POP AF ; restore sign flag
+ SCF ; set carry flag to indicate overflow
+ PUSH AF ; save combined flags again
+
+<a name="L15C6"></a>;; <b>FPBC-END</b>
+L15C6: PUSH BC ; save BC value
+
+; set HL and DE to calculator stack pointers.
+
+ RST 28H ;; FP-CALC
+ DEFB $34 ;;end-calc
+
+
+ POP BC ; restore BC value
+ POP AF ; restore flags
+ LD A,C ; copy low byte to A also.
+ RET ; return
+
+; ------------------------------------
+; THE <b><font color=#333388>'FLOATING-POINT TO A'</font></b> SUBROUTINE
+; ------------------------------------
+;
+;
+
+<a name="L15CD"></a>;; <b>FP-TO-A</b>
+L15CD: CALL <A href="#L158A">L158A</a> ; routine FP-TO-BC
+ RET C ;
+
+ PUSH AF ;
+ DEC B ;
+ INC B ;
+ JR Z,<A href="#L15D9">L15D9</a> ; forward if in range to FP-A-END
+
+ POP AF ; fetch result
+ SCF ; set carry flag signaling overflow
+ RET ; return
+
+<a name="L15D9"></a>;; <b>FP-A-END</b>
+L15D9: POP AF ;
+ RET ;
+
+
+; ----------------------------------------------
+; THE <b><font color=#333388>'PRINT A FLOATING-POINT NUMBER'</font></b> SUBROUTINE
+; ----------------------------------------------
+; prints 'last value' x on calculator stack.
+; There are a wide variety of formats see Chapter 4.
+; e.g.
+; PI prints as 3.1415927
+; .123 prints as 0.123
+; .0123 prints as .0123
+; 999999999999 prints as 1000000000000
+; 9876543210123 prints as 9876543200000
+
+; Begin by isolating zero and just printing the '0' character
+; for that case. For negative numbers print a leading '-' and
+; then form the absolute value of x.
+
+<a name="L15DB"></a>;; <b>PRINT-FP</b>
+L15DB: RST 28H ;; FP-CALC x.
+ DEFB $2D ;;duplicate x, x.
+ DEFB $32 ;;less-0 x, (1/0).
+ DEFB $00 ;;jump-true
+ DEFB $0B ;;to <A href="#L15EA">L15EA</a>, PF-NGTVE x.
+
+ DEFB $2D ;;duplicate x, x
+ DEFB $33 ;;greater-0 x, (1/0).
+ DEFB $00 ;;jump-true
+ DEFB $0D ;;to <A href="#L15F0">L15F0</a>, PF-POSTVE x.
+
+ DEFB $02 ;;delete .
+ DEFB $34 ;;end-calc .
+
+ LD A,$1C ; load accumulator with character '0'
+
+ RST 10H ; PRINT-A
+ RET ; return. >>
+
+; ---
+
+<a name="L15EA"></a>;; <b>PF-NEGTVE</b>
+L15EA: DEFB $27 ; abs +x.
+ DEFB $34 ;;end-calc x.
+
+ LD A,$16 ; load accumulator with '-'
+
+ RST 10H ; PRINT-A
+
+ RST 28H ;; FP-CALC x.
+
+<a name="L15F0"></a>;; <b>PF-POSTVE</b>
+L15F0: DEFB $34 ;;end-calc x.
+
+; register HL addresses the exponent of the floating-point value.
+; if positive, and point floats to left, then bit 7 is set.
+
+ LD A,(HL) ; pick up the exponent byte
+ CALL <A href="#L151D">L151D</a> ; routine STACK-A places on calculator stack.
+
+; now calculate roughly the number of digits, n, before the decimal point by
+; subtracting a half from true exponent and multiplying by log to
+; the base 10 of 2.
+; The true number could be one higher than n, the integer result.
+
+ RST 28H ;; FP-CALC x, e.
+ DEFB $30 ;;stk-data
+ DEFB $78 ;;Exponent: $88, Bytes: 2
+ DEFB $00,$80 ;;(+00,+00) x, e, 128.5.
+ DEFB $03 ;;subtract x, e -.5.
+ DEFB $30 ;;stk-data
+ DEFB $EF ;;Exponent: $7F, Bytes: 4
+ DEFB $1A,$20,$9A,$85 ;; .30103 (log10 2)
+ DEFB $04 ;;multiply x,
+ DEFB $24 ;;int
+ DEFB $C1 ;;st-mem-1 x, n.
+
+
+ DEFB $30 ;;stk-data
+ DEFB $34 ;;Exponent: $84, Bytes: 1
+ DEFB $00 ;;(+00,+00,+00) x, n, 8.
+
+ DEFB $03 ;;subtract x, n-8.
+ DEFB $18 ;;neg x, 8-n.
+ DEFB $38 ;;e-to-fp x * (10^n)
+
+; finally the 8 or 9 digit decimal is rounded.
+; a ten-digit integer can arise in the case of, say, 999999999.5
+; which gives 1000000000.
+
+ DEFB $A2 ;;stk-half
+ DEFB $0F ;;addition
+ DEFB $24 ;;int i.
+ DEFB $34 ;;end-calc
+
+; If there were 8 digits then final rounding will take place on the calculator
+; stack above and the next two instructions insert a masked zero so that
+; no further rounding occurs. If the result is a 9 digit integer then
+; rounding takes place within the buffer.
+
+ LD HL,$406B ; address system variable MEM-2-5th
+ ; which could be the 'ninth' digit.
+ LD (HL),$90 ; insert the value $90 10010000
+
+; now starting from lowest digit lay down the 8, 9 or 10 digit integer
+; which represents the significant portion of the number
+; e.g. PI will be the nine-digit integer 314159265
+
+ LD B,$0A ; count is ten digits.
+
+<a name="L1615"></a>;; <b>PF-LOOP</b>
+L1615: INC HL ; increase pointer
+
+ PUSH HL ; preserve buffer address.
+ PUSH BC ; preserve counter.
+
+ RST 28H ;; FP-CALC i.
+ DEFB $A4 ;;stk-ten i, 10.
+ DEFB $2E ;;n-mod-m i mod 10, i/10
+ DEFB $01 ;;exchange i/10, remainder.
+ DEFB $34 ;;end-calc
+
+ CALL <A href="#L15CD">L15CD</a> ; routine FP-TO-A $00-$09
+
+ OR $90 ; make left hand nibble 9
+
+ POP BC ; restore counter
+ POP HL ; restore buffer address.
+
+ LD (HL),A ; insert masked digit in buffer.
+ DJNZ <A href="#L1615">L1615</a> ; loop back for all ten to PF-LOOP
+
+; the most significant digit will be last but if the number is exhausted then
+; the last one or two positions will contain zero ($90).
+
+; e.g. for 'one' we have zero as estimate of leading digits.
+; 1*10^8 100000000 as integer value
+; 90 90 90 90 90 90 90 90 91 90 as buffer mem3/mem4 contents.
+
+
+ INC HL ; advance pointer to one past buffer
+ LD BC,$0008 ; set C to 8 ( B is already zero )
+ PUSH HL ; save pointer.
+
+<a name="L162C"></a>;; <b>PF-NULL</b>
+L162C: DEC HL ; decrease pointer
+ LD A,(HL) ; fetch masked digit
+ CP $90 ; is it a leading zero ?
+ JR Z,<A href="#L162C">L162C</a> ; loop back if so to PF-NULL
+
+; at this point a significant digit has been found. carry is reset.
+
+ SBC HL,BC ; subtract eight from the address.
+ PUSH HL ; ** save this pointer too
+ LD A,(HL) ; fetch addressed byte
+ ADD A,$6B ; add $6B - forcing a round up ripple
+ ; if $95 or over.
+ PUSH AF ; save the carry result.
+
+; now enter a loop to round the number. After rounding has been considered
+; a zero that has arisen from rounding or that was present at that position
+; originally is changed from $90 to $80.
+
+<a name="L1639"></a>;; <b>PF-RND-LP</b>
+L1639: POP AF ; retrieve carry from machine stack.
+ INC HL ; increment address
+ LD A,(HL) ; fetch new byte
+ ADC A,$00 ; add in any carry
+
+ DAA ; decimal adjust accumulator
+ ; carry will ripple through the '9'
+
+ PUSH AF ; save carry on machine stack.
+ AND $0F ; isolate character 0 - 9 AND set zero flag
+ ; if zero.
+ LD (HL),A ; place back in location.
+ SET 7,(HL) ; set bit 7 to show printable.
+ ; but not if trailing zero after decimal point.
+ JR Z,<A href="#L1639">L1639</a> ; back if a zero to PF-RND-LP
+ ; to consider further rounding and/or trailing
+ ; zero identification.
+
+ POP AF ; balance stack
+ POP HL ; ** retrieve lower pointer
+
+; now insert 6 trailing zeros which are printed if before the decimal point
+; but mark the end of printing if after decimal point.
+; e.g. 9876543210123 is printed as 9876543200000
+; 123.456001 is printed as 123.456
+
+ LD B,$06 ; the count is six.
+
+<a name="L164B"></a>;; <b>PF-ZERO-6</b>
+L164B: LD (HL),$80 ; insert a masked zero
+ DEC HL ; decrease pointer.
+ DJNZ <A href="#L164B">L164B</a> ; loop back for all six to PF-ZERO-6
+
+; n-mod-m reduced the number to zero and this is now deleted from the calculator
+; stack before fetching the original estimate of leading digits.
+
+
+ RST 28H ;; FP-CALC 0.
+ DEFB $02 ;;delete .
+ DEFB $E1 ;;get-mem-1 n.
+ DEFB $34 ;;end-calc n.
+
+ CALL <A href="#L15CD">L15CD</a> ; routine FP-TO-A
+ JR Z,<A href="#L165B">L165B</a> ; skip forward if positive to PF-POS
+
+ NEG ; negate makes positive
+
+<a name="L165B"></a>;; <b>PF-POS</b>
+L165B: LD E,A ; transfer count of digits to E
+ INC E ; increment twice
+ INC E ;
+ POP HL ; * retrieve pointer to one past buffer.
+
+<a name="L165F"></a>;; <b>GET-FIRST</b>
+L165F: DEC HL ; decrement address.
+ DEC E ; decrement digit counter.
+ LD A,(HL) ; fetch masked byte.
+ AND $0F ; isolate right-hand nibble.
+ JR Z,<A href="#L165F">L165F</a> ; back with leading zero to GET-FIRST
+
+; now determine if E-format printing is needed
+
+ LD A,E ; transfer now accurate number count to A.
+ SUB $05 ; subtract five
+ CP $08 ; compare with 8 as maximum digits is 13.
+ JP P,<A href="#L1682">L1682</a> ; forward if positive to PF-E-FMT
+
+ CP $F6 ; test for more than four zeros after point.
+ JP M,<A href="#L1682">L1682</a> ; forward if so to PF-E-FMT
+
+ ADD A,$06 ; test for zero leading digits, e.g. 0.5
+ JR Z,<A href="#L16BF">L16BF</a> ; forward if so to PF-ZERO-1
+
+ JP M,<A href="#L16B2">L16B2</a> ; forward if more than one zero to PF-ZEROS
+
+; else digits before the decimal point are to be printed
+
+ LD B,A ; count of leading characters to B.
+
+<a name="L167B"></a>;; <b>PF-NIB-LP</b>
+L167B: CALL <A href="#L16D0">L16D0</a> ; routine PF-NIBBLE
+ DJNZ <A href="#L167B">L167B</a> ; loop back for counted numbers to PF-NIB-LP
+
+ JR <A href="#L16C2">L16C2</a> ; forward to consider decimal part to PF-DC-OUT
+
+; ---
+
+<a name="L1682"></a>;; <b>PF-E-FMT</b>
+L1682: LD B,E ; count to B
+ CALL <A href="#L16D0">L16D0</a> ; routine PF-NIBBLE prints one digit.
+ CALL <A href="#L16C2">L16C2</a> ; routine PF-DC-OUT considers fractional part.
+
+ LD A,$2A ; prepare character 'E'
+ RST 10H ; PRINT-A
+
+ LD A,B ; transfer exponent to A
+ AND A ; test the sign.
+ JP P,<A href="#L1698">L1698</a> ; forward if positive to PF-E-POS
+
+ NEG ; negate the negative exponent.
+ LD B,A ; save positive exponent in B.
+
+ LD A,$16 ; prepare character '-'
+ JR <A href="#L169A">L169A</a> ; skip forward to PF-E-SIGN
+
+; ---
+
+<a name="L1698"></a>;; <b>PF-E-POS</b>
+L1698: LD A,$15 ; prepare character '+'
+
+<a name="L169A"></a>;; <b>PF-E-SIGN</b>
+L169A: RST 10H ; PRINT-A
+
+; now convert the integer exponent in B to two characters.
+; it will be less than 99.
+
+ LD A,B ; fetch positive exponent.
+ LD B,$FF ; initialize left hand digit to minus one.
+
+<a name="L169E"></a>;; <b>PF-E-TENS</b>
+L169E: INC B ; increment ten count
+ SUB $0A ; subtract ten from exponent
+ JR NC,<A href="#L169E">L169E</a> ; loop back if greater than ten to PF-E-TENS
+
+ ADD A,$0A ; reverse last subtraction
+ LD C,A ; transfer remainder to C
+
+ LD A,B ; transfer ten value to A.
+ AND A ; test for zero.
+ JR Z,<A href="#L16AD">L16AD</a> ; skip forward if so to PF-E-LOW
+
+ CALL <A href="#L07EB">L07EB</a> ; routine OUT-CODE prints as digit '1' - '9'
+
+<a name="L16AD"></a>;; <b>PF-E-LOW</b>
+L16AD: LD A,C ; low byte to A
+ CALL <A href="#L07EB">L07EB</a> ; routine OUT-CODE prints final digit of the
+ ; exponent.
+ RET ; return. >>
+
+; ---
+
+; this branch deals with zeros after decimal point.
+; e.g. .01 or .0000999
+
+<a name="L16B2"></a>;; <b>PF-ZEROS</b>
+L16B2: NEG ; negate makes number positive 1 to 4.
+ LD B,A ; zero count to B.
+
+ LD A,$1B ; prepare character '.'
+ RST 10H ; PRINT-A
+
+ LD A,$1C ; prepare a '0'
+
+<a name="L16BA"></a>;; <b>PF-ZRO-LP</b>
+L16BA: RST 10H ; PRINT-A
+ DJNZ <A href="#L16BA">L16BA</a> ; loop back to PF-ZRO-LP
+
+ JR <A href="#L16C8">L16C8</a> ; forward to PF-FRAC-LP
+
+; ---
+
+; there is a need to print a leading zero e.g. 0.1 but not with .01
+
+<a name="L16BF"></a>;; <b>PF-ZERO-1</b>
+L16BF: LD A,$1C ; prepare character '0'.
+ RST 10H ; PRINT-A
+
+; this subroutine considers the decimal point and any trailing digits.
+; if the next character is a marked zero, $80, then nothing more to print.
+
+<a name="L16C2"></a>;; <b>PF-DC-OUT</b>
+L16C2: DEC (HL) ; decrement addressed character
+ INC (HL) ; increment it again
+ RET PE ; return with overflow (was 128) >>
+ ; as no fractional part
+
+; else there is a fractional part so print the decimal point.
+
+ LD A,$1B ; prepare character '.'
+ RST 10H ; PRINT-A
+
+; now enter a loop to print trailing digits
+
+<a name="L16C8"></a>;; <b>PF-FRAC-LP</b>
+L16C8: DEC (HL) ; test for a marked zero.
+ INC (HL) ;
+ RET PE ; return when digits exhausted >>
+
+ CALL <A href="#L16D0">L16D0</a> ; routine PF-NIBBLE
+ JR <A href="#L16C8">L16C8</a> ; back for all fractional digits to PF-FRAC-LP.
+
+; ---
+
+; subroutine to print right-hand nibble
+
+<a name="L16D0"></a>;; <b>PF-NIBBLE</b>
+L16D0: LD A,(HL) ; fetch addressed byte
+ AND $0F ; mask off lower 4 bits
+ CALL <A href="#L07EB">L07EB</a> ; routine OUT-CODE
+ DEC HL ; decrement pointer.
+ RET ; return.
+
+
+; -------------------------------
+; THE <b><font color=#333388>'PREPARE TO ADD'</font></b> SUBROUTINE
+; -------------------------------
+; This routine is called twice to prepare each floating point number for
+; addition, in situ, on the calculator stack.
+; The exponent is picked up from the first byte which is then cleared to act
+; as a sign byte and accept any overflow.
+; If the exponent is zero then the number is zero and an early return is made.
+; The now redundant sign bit of the mantissa is set and if the number is
+; negative then all five bytes of the number are twos-complemented to prepare
+; the number for addition.
+; On the second invocation the exponent of the first number is in B.
+
+
+<a name="L16D8"></a>;; <b>PREP-ADD</b>
+L16D8: LD A,(HL) ; fetch exponent.
+ LD (HL),$00 ; make this byte zero to take any overflow and
+ ; default to positive.
+ AND A ; test stored exponent for zero.
+ RET Z ; return with zero flag set if number is zero.
+
+ INC HL ; point to first byte of mantissa.
+ BIT 7,(HL) ; test the sign bit.
+ SET 7,(HL) ; set it to its implied state.
+ DEC HL ; set pointer to first byte again.
+ RET Z ; return if bit indicated number is positive.>>
+
+; if negative then all five bytes are twos complemented starting at LSB.
+
+ PUSH BC ; save B register contents.
+ LD BC,$0005 ; set BC to five.
+ ADD HL,BC ; point to location after 5th byte.
+ LD B,C ; set the B counter to five.
+ LD C,A ; store original exponent in C.
+ SCF ; set carry flag so that one is added.
+
+; now enter a loop to twos-complement the number.
+; The first of the five bytes becomes $FF to denote a negative number.
+
+<a name="L16EC"></a>;; <b>NEG-BYTE</b>
+L16EC: DEC HL ; point to first or more significant byte.
+ LD A,(HL) ; fetch to accumulator.
+ CPL ; complement.
+ ADC A,$00 ; add in initial carry or any subsequent carry.
+ LD (HL),A ; place number back.
+ DJNZ <A href="#L16EC">L16EC</a> ; loop back five times to NEG-BYTE
+
+ LD A,C ; restore the exponent to accumulator.
+ POP BC ; restore B register contents.
+
+ RET ; return.
+
+; ----------------------------------
+; THE <b><font color=#333388>'FETCH TWO NUMBERS'</font></b> SUBROUTINE
+; ----------------------------------
+; This routine is used by addition, multiplication and division to fetch
+; the two five-byte numbers addressed by HL and DE from the calculator stack
+; into the Z80 registers.
+; The HL register may no longer point to the first of the two numbers.
+; Since the 32-bit addition operation is accomplished using two Z80 16-bit
+; instructions, it is important that the lower two bytes of each mantissa are
+; in one set of registers and the other bytes all in the alternate set.
+;
+; In: HL = highest number, DE= lowest number
+;
+; : alt': :
+; Out: :H,B-C:C,B: num1
+; :L,D-E:D-E: num2
+
+<a name="L16F7"></a>;; <b>FETCH-TWO</b>
+L16F7: PUSH HL ; save HL
+ PUSH AF ; save A - result sign when used from division.
+
+ LD C,(HL) ;
+ INC HL ;
+ LD B,(HL) ;
+ LD (HL),A ; insert sign when used from multiplication.
+ INC HL ;
+ LD A,C ; m1
+ LD C,(HL) ;
+ PUSH BC ; PUSH m2 m3
+
+ INC HL ;
+ LD C,(HL) ; m4
+ INC HL ;
+ LD B,(HL) ; m5 BC holds m5 m4
+
+ EX DE,HL ; make HL point to start of second number.
+
+ LD D,A ; m1
+ LD E,(HL) ;
+ PUSH DE ; PUSH m1 n1
+
+ INC HL ;
+ LD D,(HL) ;
+ INC HL ;
+ LD E,(HL) ;
+ PUSH DE ; PUSH n2 n3
+
+ EXX ; - - - - - - -
+
+ POP DE ; POP n2 n3
+ POP HL ; POP m1 n1
+ POP BC ; POP m2 m3
+
+ EXX ; - - - - - - -
+
+ INC HL ;
+ LD D,(HL) ;
+ INC HL ;
+ LD E,(HL) ; DE holds n4 n5
+
+ POP AF ; restore saved
+ POP HL ; registers.
+ RET ; return.
+
+; -----------------------------
+; THE <b><font color=#333388>'SHIFT ADDEND'</font></b> SUBROUTINE
+; -----------------------------
+; The accumulator A contains the difference between the two exponents.
+; This is the lowest of the two numbers to be added
+
+<a name="L171A"></a>;; <b>SHIFT-FP</b>
+L171A: AND A ; test difference between exponents.
+ RET Z ; return if zero. both normal.
+
+ CP $21 ; compare with 33 bits.
+ JR NC,<A href="#L1736">L1736</a> ; forward if greater than 32 to ADDEND-0
+
+ PUSH BC ; preserve BC - part
+ LD B,A ; shift counter to B.
+
+; Now perform B right shifts on the addend L'D'E'D E
+; to bring it into line with the augend H'B'C'C B
+
+<a name="L1722"></a>;; <b>ONE-SHIFT</b>
+L1722: EXX ; - - -
+ SRA L ; 76543210->C bit 7 unchanged.
+ RR D ; C->76543210->C
+ RR E ; C->76543210->C
+ EXX ; - - -
+ RR D ; C->76543210->C
+ RR E ; C->76543210->C
+ DJNZ <A href="#L1722">L1722</a> ; loop back B times to ONE-SHIFT
+
+ POP BC ; restore BC
+ RET NC ; return if last shift produced no carry. >>
+
+; if carry flag was set then accuracy is being lost so round up the addend.
+
+ CALL <A href="#L1741">L1741</a> ; routine ADD-BACK
+ RET NZ ; return if not FF 00 00 00 00
+
+; this branch makes all five bytes of the addend zero and is made during
+; addition when the exponents are too far apart for the addend bits to
+; affect the result.
+
+<a name="L1736"></a>;; <b>ADDEND-0</b>
+L1736: EXX ; select alternate set for more significant
+ ; bytes.
+ XOR A ; clear accumulator.
+
+
+; this entry point (from multiplication) sets four of the bytes to zero or if
+; continuing from above, during addition, then all five bytes are set to zero.
+
+<a name="L1738"></a>;; <b>ZEROS-4/5</b>
+L1738: LD L,$00 ; set byte 1 to zero.
+ LD D,A ; set byte 2 to A.
+ LD E,L ; set byte 3 to zero.
+ EXX ; select main set
+ LD DE,$0000 ; set lower bytes 4 and 5 to zero.
+ RET ; return.
+
+; -------------------------
+; THE <b><font color=#333388>'ADD-BACK'</font></b> SUBROUTINE
+; -------------------------
+; Called from SHIFT-FP above during addition and after normalization from
+; multiplication.
+; This is really a 32-bit increment routine which sets the zero flag according
+; to the 32-bit result.
+; During addition, only negative numbers like FF FF FF FF FF,
+; the twos-complement version of xx 80 00 00 01 say
+; will result in a full ripple FF 00 00 00 00.
+; FF FF FF FF FF when shifted right is unchanged by SHIFT-FP but sets the
+; carry invoking this routine.
+
+<a name="L1741"></a>;; <b>ADD-BACK</b>
+L1741: INC E ;
+ RET NZ ;
+
+ INC D ;
+ RET NZ ;
+
+ EXX ;
+ INC E ;
+ JR NZ,<A href="#L174A">L174A</a> ; forward if no overflow to ALL-ADDED
+
+ INC D ;
+
+<a name="L174A"></a>;; <b>ALL-ADDED</b>
+L174A: EXX ;
+ RET ; return with zero flag set for zero mantissa.
+
+
+; ---------------------------
+; THE <b><font color=#333388>'SUBTRACTION'</font></b> OPERATION
+; ---------------------------
+; just switch the sign of subtrahend and do an add.
+
+<a name="L174C"></a>;; <b>subtract</b>
+L174C: LD A,(DE) ; fetch exponent byte of second number the
+ ; subtrahend.
+ AND A ; test for zero
+ RET Z ; return if zero - first number is result.
+
+ INC DE ; address the first mantissa byte.
+ LD A,(DE) ; fetch to accumulator.
+ XOR $80 ; toggle the sign bit.
+ LD (DE),A ; place back on calculator stack.
+ DEC DE ; point to exponent byte.
+ ; continue into addition routine.
+
+; ------------------------
+; THE <b><font color=#333388>'ADDITION'</font></b> OPERATION
+; ------------------------
+; The addition operation pulls out all the stops and uses most of the Z80's
+; registers to add two floating-point numbers.
+; This is a binary operation and on entry, HL points to the first number
+; and DE to the second.
+
+<a name="L1755"></a>;; <b>addition</b>
+L1755: EXX ; - - -
+ PUSH HL ; save the pointer to the next literal.
+ EXX ; - - -
+
+ PUSH DE ; save pointer to second number
+ PUSH HL ; save pointer to first number - will be the
+ ; result pointer on calculator stack.
+
+ CALL <A href="#L16D8">L16D8</a> ; routine PREP-ADD
+ LD B,A ; save first exponent byte in B.
+ EX DE,HL ; switch number pointers.
+ CALL <A href="#L16D8">L16D8</a> ; routine PREP-ADD
+ LD C,A ; save second exponent byte in C.
+ CP B ; compare the exponent bytes.
+ JR NC,<A href="#L1769">L1769</a> ; forward if second higher to SHIFT-LEN
+
+ LD A,B ; else higher exponent to A
+ LD B,C ; lower exponent to B
+ EX DE,HL ; switch the number pointers.
+
+<a name="L1769"></a>;; <b>SHIFT-LEN</b>
+L1769: PUSH AF ; save higher exponent
+ SUB B ; subtract lower exponent
+
+ CALL <A href="#L16F7">L16F7</a> ; routine FETCH-TWO
+ CALL <A href="#L171A">L171A</a> ; routine SHIFT-FP
+
+ POP AF ; restore higher exponent.
+ POP HL ; restore result pointer.
+ LD (HL),A ; insert exponent byte.
+ PUSH HL ; save result pointer again.
+
+; now perform the 32-bit addition using two 16-bit Z80 add instructions.
+
+ LD L,B ; transfer low bytes of mantissa individually
+ LD H,C ; to HL register
+
+ ADD HL,DE ; the actual binary addition of lower bytes
+
+; now the two higher byte pairs that are in the alternate register sets.
+
+ EXX ; switch in set
+ EX DE,HL ; transfer high mantissa bytes to HL register.
+
+ ADC HL,BC ; the actual addition of higher bytes with
+ ; any carry from first stage.
+
+ EX DE,HL ; result in DE, sign bytes ($FF or $00) to HL
+
+; now consider the two sign bytes
+
+ LD A,H ; fetch sign byte of num1
+
+ ADC A,L ; add including any carry from mantissa
+ ; addition. 00 or 01 or FE or FF
+
+ LD L,A ; result in L.
+
+; possible outcomes of signs and overflow from mantissa are
+;
+; H + L + carry = L RRA XOR L RRA
+; ------------------------------------------------------------
+; 00 + 00 = 00 00 00
+; 00 + 00 + carry = 01 00 01 carry
+; FF + FF = FE C FF 01 carry
+; FF + FF + carry = FF C FF 00
+; FF + 00 = FF FF 00
+; FF + 00 + carry = 00 C 80 80
+
+ RRA ; C->76543210->C
+ XOR L ; set bit 0 if shifting required.
+
+ EXX ; switch back to main set
+ EX DE,HL ; full mantissa result now in D'E'D E registers.
+ POP HL ; restore pointer to result exponent on
+ ; the calculator stack.
+
+ RRA ; has overflow occurred ?
+ JR NC,<A href="#L1790">L1790</a> ; skip forward if not to TEST-NEG
+
+; if the addition of two positive mantissas produced overflow or if the
+; addition of two negative mantissas did not then the result exponent has to
+; be incremented and the mantissa shifted one place to the right.
+
+ LD A,$01 ; one shift required.
+ CALL <A href="#L171A">L171A</a> ; routine SHIFT-FP performs a single shift
+ ; rounding any lost bit
+ INC (HL) ; increment the exponent.
+ JR Z,<A href="#L17B3">L17B3</a> ; forward to ADD-REP-6 if the exponent
+ ; wraps round from FF to zero as number is too
+ ; big for the system.
+
+; at this stage the exponent on the calculator stack is correct.
+
+<a name="L1790"></a>;; <b>TEST-NEG</b>
+L1790: EXX ; switch in the alternate set.
+ LD A,L ; load result sign to accumulator.
+ AND $80 ; isolate bit 7 from sign byte setting zero
+ ; flag if positive.
+ EXX ; back to main set.
+
+ INC HL ; point to first byte of mantissa
+ LD (HL),A ; insert $00 positive or $80 negative at
+ ; position on calculator stack.
+
+ DEC HL ; point to exponent again.
+ JR Z,<A href="#L17B9">L17B9</a> ; forward if positive to GO-NC-MLT
+
+; a negative number has to be twos-complemented before being placed on stack.
+
+ LD A,E ; fetch lowest (rightmost) mantissa byte.
+ NEG ; Negate
+ CCF ; Complement Carry Flag
+ LD E,A ; place back in register
+
+ LD A,D ; ditto
+ CPL ;
+ ADC A,$00 ;
+ LD D,A ;
+
+ EXX ; switch to higher (leftmost) 16 bits.
+
+ LD A,E ; ditto
+ CPL ;
+ ADC A,$00 ;
+ LD E,A ;
+
+ LD A,D ; ditto
+ CPL ;
+ ADC A,$00 ;
+ JR NC,<A href="#L17B7">L17B7</a> ; forward without overflow to END-COMPL
+
+; else entire mantissa is now zero. 00 00 00 00
+
+ RRA ; set mantissa to 80 00 00 00
+ EXX ; switch.
+ INC (HL) ; increment the exponent.
+
+<a name="L17B3"></a>;; <b>ADD-REP-6</b>
+L17B3: JP Z,<A href="#L1880">L1880</a> ; jump forward if exponent now zero to REPORT-6
+ ; 'Number too big'
+
+ EXX ; switch back to alternate set.
+
+<a name="L17B7"></a>;; <b>END-COMPL</b>
+L17B7: LD D,A ; put first byte of mantissa back in DE.
+ EXX ; switch to main set.
+
+<a name="L17B9"></a>;; <b>GO-NC-MLT</b>
+L17B9: XOR A ; clear carry flag and
+ ; clear accumulator so no extra bits carried
+ ; forward as occurs in multiplication.
+
+ JR <A href="#L1828">L1828</a> ; forward to common code at TEST-NORM
+ ; but should go straight to NORMALIZE.
+
+
+; ----------------------------------------------
+; THE <b><font color=#333388>'PREPARE TO MULTIPLY OR DIVIDE'</font></b> SUBROUTINE
+; ----------------------------------------------
+; this routine is called twice from multiplication and twice from division
+; to prepare each of the two numbers for the operation.
+; Initially the accumulator holds zero and after the second invocation bit 7
+; of the accumulator will be the sign bit of the result.
+
+<a name="L17BC"></a>;; <b>PREP-M/D</b>
+L17BC: SCF ; set carry flag to signal number is zero.
+ DEC (HL) ; test exponent
+ INC (HL) ; for zero.
+ RET Z ; return if zero with carry flag set.
+
+ INC HL ; address first mantissa byte.
+ XOR (HL) ; exclusive or the running sign bit.
+ SET 7,(HL) ; set the implied bit.
+ DEC HL ; point to exponent byte.
+ RET ; return.
+
+; ------------------------------
+; THE <b><font color=#333388>'MULTIPLICATION'</font></b> OPERATION
+; ------------------------------
+;
+;
+
+<a name="L17C6"></a>;; <b>multiply</b>
+L17C6: XOR A ; reset bit 7 of running sign flag.
+ CALL <A href="#L17BC">L17BC</a> ; routine PREP-M/D
+ RET C ; return if number is zero.
+ ; zero * anything = zero.
+
+ EXX ; - - -
+ PUSH HL ; save pointer to 'next literal'
+ EXX ; - - -
+
+ PUSH DE ; save pointer to second number
+
+ EX DE,HL ; make HL address second number.
+
+ CALL <A href="#L17BC">L17BC</a> ; routine PREP-M/D
+
+ EX DE,HL ; HL first number, DE - second number
+ JR C,<A href="#L1830">L1830</a> ; forward with carry to ZERO-RSLT
+ ; anything * zero = zero.
+
+ PUSH HL ; save pointer to first number.
+
+ CALL <A href="#L16F7">L16F7</a> ; routine FETCH-TWO fetches two mantissas from
+ ; calc stack to B'C'C,B D'E'D E
+ ; (HL will be overwritten but the result sign
+ ; in A is inserted on the calculator stack)
+
+ LD A,B ; transfer low mantissa byte of first number
+ AND A ; clear carry.
+ SBC HL,HL ; a short form of LD HL,$0000 to take lower
+ ; two bytes of result. (2 program bytes)
+ EXX ; switch in alternate set
+ PUSH HL ; preserve HL
+ SBC HL,HL ; set HL to zero also to take higher two bytes
+ ; of the result and clear carry.
+ EXX ; switch back.
+
+ LD B,$21 ; register B can now be used to count thirty
+ ; three shifts.
+ JR <A href="#L17F8">L17F8</a> ; forward to loop entry point STRT-MLT
+
+; ---
+
+; The multiplication loop is entered at STRT-LOOP.
+
+<a name="L17E7"></a>;; <b>MLT-LOOP</b>
+L17E7: JR NC,<A href="#L17EE">L17EE</a> ; forward if no carry to NO-ADD
+
+ ; else add in the multiplicand.
+
+ ADD HL,DE ; add the two low bytes to result
+ EXX ; switch to more significant bytes.
+ ADC HL,DE ; add high bytes of multiplicand and any carry.
+ EXX ; switch to main set.
+
+; in either case shift result right into B'C'C A
+
+<a name="L17EE"></a>;; <b>NO-ADD</b>
+L17EE: EXX ; switch to alternate set
+ RR H ; C > 76543210 > C
+ RR L ; C > 76543210 > C
+ EXX ;
+ RR H ; C > 76543210 > C
+ RR L ; C > 76543210 > C
+
+<a name="L17F8"></a>;; <b>STRT-MLT</b>
+L17F8: EXX ; switch in alternate set.
+ RR B ; C > 76543210 > C
+ RR C ; C > 76543210 > C
+ EXX ; now main set
+ RR C ; C > 76543210 > C
+ RRA ; C > 76543210 > C
+ DJNZ <A href="#L17E7">L17E7</a> ; loop back 33 times to MLT-LOOP
+
+;
+
+ EX DE,HL ;
+ EXX ;
+ EX DE,HL ;
+ EXX ;
+ POP BC ;
+ POP HL ;
+ LD A,B ;
+ ADD A,C ;
+ JR NZ,<A href="#L180E">L180E</a> ; forward to MAKE-EXPT
+
+ AND A ;
+
+<a name="L180E"></a>;; <b>MAKE-EXPT</b>
+L180E: DEC A ;
+ CCF ; Complement Carry Flag
+
+<a name="L1810"></a>;; <b>DIVN-EXPT</b>
+L1810: RLA ;
+ CCF ; Complement Carry Flag
+ RRA ;
+ JP P,<A href="#L1819">L1819</a> ; forward to OFLW1-CLR
+
+ JR NC,<A href="#L1880">L1880</a> ; forward to REPORT-6
+
+ AND A ;
+
+<a name="L1819"></a>;; <b>OFLW1-CLR</b>
+L1819: INC A ;
+ JR NZ,<A href="#L1824">L1824</a> ; forward to OFLW2-CLR
+
+ JR C,<A href="#L1824">L1824</a> ; forward to OFLW2-CLR
+
+ EXX ;
+ BIT 7,D ;
+ EXX ;
+ JR NZ,<A href="#L1880">L1880</a> ; forward to REPORT-6
+
+<a name="L1824"></a>;; <b>OFLW2-CLR</b>
+L1824: LD (HL),A ;
+ EXX ;
+ LD A,B ;
+ EXX ;
+
+; addition joins here with carry flag clear.
+
+<a name="L1828"></a>;; <b>TEST-NORM</b>
+L1828: JR NC,<A href="#L183F">L183F</a> ; forward to NORMALIZE
+
+ LD A,(HL) ;
+ AND A ;
+
+<a name="L182C"></a>;; <b>NEAR-ZERO</b>
+L182C: LD A,$80 ; prepare to rescue the most significant bit
+ ; of the mantissa if it is set.
+ JR Z,<A href="#L1831">L1831</a> ; skip forward to SKIP-ZERO
+
+<a name="L1830"></a>;; <b>ZERO-RSLT</b>
+L1830: XOR A ; make mask byte zero signaling set five
+ ; bytes to zero.
+
+<a name="L1831"></a>;; <b>SKIP-ZERO</b>
+L1831: EXX ; switch in alternate set
+ AND D ; isolate most significant bit (if A is $80).
+
+ CALL <A href="#L1738">L1738</a> ; routine ZEROS-4/5 sets mantissa without
+ ; affecting any flags.
+
+ RLCA ; test if MSB set. bit 7 goes to bit 0.
+ ; either $00 -> $00 or $80 -> $01
+ LD (HL),A ; make exponent $01 (lowest) or $00 zero
+ JR C,<A href="#L1868">L1868</a> ; forward if first case to OFLOW-CLR
+
+ INC HL ; address first mantissa byte on the
+ ; calculator stack.
+ LD (HL),A ; insert a zero for the sign bit.
+ DEC HL ; point to zero exponent
+ JR <A href="#L1868">L1868</a> ; forward to OFLOW-CLR
+
+; ---
+
+; this branch is common to addition and multiplication with the mantissa
+; result still in registers D'E'D E .
+
+<a name="L183F"></a>;; <b>NORMALIZE</b>
+L183F: LD B,$20 ; a maximum of thirty-two left shifts will be
+ ; needed.
+
+<a name="L1841"></a>;; <b>SHIFT-ONE</b>
+L1841: EXX ; address higher 16 bits.
+ BIT 7,D ; test the leftmost bit
+ EXX ; address lower 16 bits.
+
+ JR NZ,<A href="#L1859">L1859</a> ; forward if leftmost bit was set to NORML-NOW
+
+ RLCA ; this holds zero from addition, 33rd bit
+ ; from multiplication.
+
+ RL E ; C < 76543210 < C
+ RL D ; C < 76543210 < C
+
+ EXX ; address higher 16 bits.
+
+ RL E ; C < 76543210 < C
+ RL D ; C < 76543210 < C
+
+ EXX ; switch to main set.
+
+ DEC (HL) ; decrement the exponent byte on the calculator
+ ; stack.
+
+ JR Z,<A href="#L182C">L182C</a> ; back if exponent becomes zero to NEAR-ZERO
+ ; it's just possible that the last rotation
+ ; set bit 7 of D. We shall see.
+
+ DJNZ <A href="#L1841">L1841</a> ; loop back to SHIFT-ONE
+
+; if thirty-two left shifts were performed without setting the most significant
+; bit then the result is zero.
+
+ JR <A href="#L1830">L1830</a> ; back to ZERO-RSLT
+
+; ---
+
+<a name="L1859"></a>;; <b>NORML-NOW</b>
+L1859: RLA ; for the addition path, A is always zero.
+ ; for the mult path, ...
+
+ JR NC,<A href="#L1868">L1868</a> ; forward to OFLOW-CLR
+
+; this branch is taken only with multiplication.
+
+ CALL <A href="#L1741">L1741</a> ; routine ADD-BACK
+
+ JR NZ,<A href="#L1868">L1868</a> ; forward to OFLOW-CLR
+
+ EXX ;
+ LD D,$80 ;
+ EXX ;
+ INC (HL) ;
+ JR Z,<A href="#L1880">L1880</a> ; forward to REPORT-6
+
+; now transfer the mantissa from the register sets to the calculator stack
+; incorporating the sign bit already there.
+
+<a name="L1868"></a>;; <b>OFLOW-CLR</b>
+L1868: PUSH HL ; save pointer to exponent on stack.
+ INC HL ; address first byte of mantissa which was
+ ; previously loaded with sign bit $00 or $80.
+
+ EXX ; - - -
+ PUSH DE ; push the most significant two bytes.
+ EXX ; - - -
+
+ POP BC ; pop - true mantissa is now BCDE.
+
+; now pick up the sign bit.
+
+ LD A,B ; first mantissa byte to A
+ RLA ; rotate out bit 7 which is set
+ RL (HL) ; rotate sign bit on stack into carry.
+ RRA ; rotate sign bit into bit 7 of mantissa.
+
+; and transfer mantissa from main registers to calculator stack.
+
+ LD (HL),A ;
+ INC HL ;
+ LD (HL),C ;
+ INC HL ;
+ LD (HL),D ;
+ INC HL ;
+ LD (HL),E ;
+
+ POP HL ; restore pointer to num1 now result.
+ POP DE ; restore pointer to num2 now STKEND.
+
+ EXX ; - - -
+ POP HL ; restore pointer to next calculator literal.
+ EXX ; - - -
+
+ RET ; return.
+
+; ---
+
+<a name="L1880"></a>;; <b>REPORT-6</b>
+L1880: RST 08H ; ERROR-1
+ DEFB $05 ; Error Report: Arithmetic overflow.
+
+; ------------------------
+; THE <b><font color=#333388>'DIVISION'</font></b> OPERATION
+; ------------------------
+; "Of all the arithmetic subroutines, division is the most complicated and
+; the least understood. It is particularly interesting to note that the
+; Sinclair programmer himself has made a mistake in his programming ( or has
+; copied over someone else's mistake!) for
+; PRINT PEEK 6352 [ $18D0 ] ('unimproved' ROM, 6351 [ $18CF ] )
+; should give 218 not 225."
+; - Dr. Ian Logan, Syntax magazine Jul/Aug 1982.
+; [ i.e. the jump should be made to div-34th ]
+
+; First check for division by zero.
+
+<a name="L1882"></a>;; <b>division</b>
+L1882: EX DE,HL ; consider the second number first.
+ XOR A ; set the running sign flag.
+ CALL <A href="#L17BC">L17BC</a> ; routine PREP-M/D
+ JR C,<A href="#L1880">L1880</a> ; back if zero to REPORT-6
+ ; 'Arithmetic overflow'
+
+ EX DE,HL ; now prepare first number and check for zero.
+ CALL <A href="#L17BC">L17BC</a> ; routine PREP-M/D
+ RET C ; return if zero, 0/anything is zero.
+
+ EXX ; - - -
+ PUSH HL ; save pointer to the next calculator literal.
+ EXX ; - - -
+
+ PUSH DE ; save pointer to divisor - will be STKEND.
+ PUSH HL ; save pointer to dividend - will be result.
+
+ CALL <A href="#L16F7">L16F7</a> ; routine FETCH-TWO fetches the two numbers
+ ; into the registers H'B'C'C B
+ ; L'D'E'D E
+ EXX ; - - -
+ PUSH HL ; save the two exponents.
+
+ LD H,B ; transfer the dividend to H'L'H L
+ LD L,C ;
+ EXX ;
+ LD H,C ;
+ LD L,B ;
+
+ XOR A ; clear carry bit and accumulator.
+ LD B,$DF ; count upwards from -33 decimal
+ JR <A href="#L18B2">L18B2</a> ; forward to mid-loop entry point DIV-START
+
+; ---
+
+<a name="L18A2"></a>;; <b>DIV-LOOP</b>
+L18A2: RLA ; multiply partial quotient by two
+ RL C ; setting result bit from carry.
+ EXX ;
+ RL C ;
+ RL B ;
+ EXX ;
+
+<a name="L18AB"></a>;; <b>div-34th</b>
+L18AB: ADD HL,HL ;
+ EXX ;
+ ADC HL,HL ;
+ EXX ;
+ JR C,<A href="#L18C2">L18C2</a> ; forward to SUBN-ONLY
+
+<a name="L18B2"></a>;; <b>DIV-START</b>
+L18B2: SBC HL,DE ; subtract divisor part.
+ EXX ;
+ SBC HL,DE ;
+ EXX ;
+ JR NC,<A href="#L18C9">L18C9</a> ; forward if subtraction goes to NO-RSTORE
+
+ ADD HL,DE ; else restore
+ EXX ;
+ ADC HL,DE ;
+ EXX ;
+ AND A ; clear carry
+ JR <A href="#L18CA">L18CA</a> ; forward to COUNT-ONE
+
+; ---
+
+<a name="L18C2"></a>;; <b>SUBN-ONLY</b>
+L18C2: AND A ;
+ SBC HL,DE ;
+ EXX ;
+ SBC HL,DE ;
+ EXX ;
+
+<a name="L18C9"></a>;; <b>NO-RSTORE</b>
+L18C9: SCF ; set carry flag
+
+<a name="L18CA"></a>;; <b>COUNT-ONE</b>
+L18CA: INC B ; increment the counter
+ JP M,<A href="#L18A2">L18A2</a> ; back while still minus to DIV-LOOP
+
+ PUSH AF ;
+ JR Z,<A href="#L18B2">L18B2</a> ; back to DIV-START
+
+; "This jump is made to the wrong place. No 34th bit will ever be obtained
+; without first shifting the dividend. Hence important results like 1/10 and
+; 1/1000 are not rounded up as they should be. Rounding up never occurs when
+; it depends on the 34th bit. The jump should be made to div-34th above."
+; - Dr. Frank O'Hara, "The Complete Spectrum ROM Disassembly", 1983,
+; published by Melbourne House.
+; (<font color=#9900FF>Note.</font> on the ZX81 this would be JR Z,L18AB)
+;
+; However if you make this change, then while (1/2=.5) will now evaluate as
+; true, (.25=1/4), which did evaluate as true, no longer does.
+
+ LD E,A ;
+ LD D,C ;
+ EXX ;
+ LD E,C ;
+ LD D,B ;
+
+ POP AF ;
+ RR B ;
+ POP AF ;
+ RR B ;
+
+ EXX ;
+ POP BC ;
+ POP HL ;
+ LD A,B ;
+ SUB C ;
+ JP <A href="#L1810">L1810</a> ; jump back to DIVN-EXPT
+
+; ------------------------------------------------
+; THE <b><font color=#333388>'INTEGER TRUNCATION TOWARDS ZERO'</font></b> SUBROUTINE
+; ------------------------------------------------
+;
+
+<a name="L18E4"></a>;; <b>truncate</b>
+L18E4: LD A,(HL) ; fetch exponent
+ CP $81 ; compare to +1
+ JR NC,<A href="#L18EF">L18EF</a> ; forward, if 1 or more, to T-GR-ZERO
+
+; else the number is smaller than plus or minus 1 and can be made zero.
+
+ LD (HL),$00 ; make exponent zero.
+ LD A,$20 ; prepare to set 32 bits of mantissa to zero.
+ JR <A href="#L18F4">L18F4</a> ; forward to NIL-BYTES
+
+; ---
+
+<a name="L18EF"></a>;; <b>T-GR-ZERO</b>
+L18EF: SUB $A0 ; subtract +32 from exponent
+ RET P ; return if result is positive as all 32 bits
+ ; of the mantissa relate to the integer part.
+ ; The floating point is somewhere to the right
+ ; of the mantissa
+
+ NEG ; else negate to form number of rightmost bits
+ ; to be blanked.
+
+; for instance, disregarding the sign bit, the number 3.5 is held as
+; exponent $82 mantissa .11100000 00000000 00000000 00000000
+; we need to set $82 - $A0 = $E2 NEG = $1E (thirty) bits to zero to form the
+; integer.
+; The sign of the number is never considered as the first bit of the mantissa
+; must be part of the integer.
+
+<a name="L18F4"></a>;; <b>NIL-BYTES</b>
+L18F4: PUSH DE ; save pointer to STKEND
+ EX DE,HL ; HL points at STKEND
+ DEC HL ; now at last byte of mantissa.
+ LD B,A ; Transfer bit count to B register.
+ SRL B ; divide by
+ SRL B ; eight
+ SRL B ;
+ JR Z,<A href="#L1905">L1905</a> ; forward if zero to BITS-ZERO
+
+; else the original count was eight or more and whole bytes can be blanked.
+
+<a name="L1900"></a>;; <b>BYTE-ZERO</b>
+L1900: LD (HL),$00 ; set eight bits to zero.
+ DEC HL ; point to more significant byte of mantissa.
+ DJNZ <A href="#L1900">L1900</a> ; loop back to BYTE-ZERO
+
+; now consider any residual bits.
+
+<a name="L1905"></a>;; <b>BITS-ZERO</b>
+L1905: AND $07 ; isolate the remaining bits
+ JR Z,<A href="#L1912">L1912</a> ; forward if none to IX-END
+
+ LD B,A ; transfer bit count to B counter.
+ LD A,$FF ; form a mask 11111111
+
+<a name="L190C"></a>;; <b>LESS-MASK</b>
+L190C: SLA A ; 1 <- 76543210 <- o slide mask leftwards.
+ DJNZ <A href="#L190C">L190C</a> ; loop back for bit count to LESS-MASK
+
+ AND (HL) ; lose the unwanted rightmost bits
+ LD (HL),A ; and place in mantissa byte.
+
+<a name="L1912"></a>;; <b>IX-END</b>
+L1912: EX DE,HL ; restore result pointer from DE.
+ POP DE ; restore STKEND from stack.
+ RET ; return.
+
+
+;********************************
+;** FLOATING-POINT CALCULATOR **
+;********************************
+
+; As a general rule the calculator avoids using the IY register.
+; Exceptions are val and str$.
+; So an assembly language programmer who has disabled interrupts to use IY
+; for other purposes can still use the calculator for mathematical
+; purposes.
+
+
+; ------------------------
+; THE <b><font color=#333388>'TABLE OF CONSTANTS'</font></b>
+; ------------------------
+; The ZX81 has only floating-point number representation.
+; Both the ZX80 and the ZX Spectrum have integer numbers in some form.
+
+<a name="L1915"></a>;; <b>stk-zero</b> 00 00 00 00 00
+L1915: DEFB $00 ;;Bytes: 1
+ DEFB $B0 ;;Exponent $00
+ DEFB $00 ;;(+00,+00,+00)
+
+<a name="L1918"></a>;; <b>stk-one</b> 81 00 00 00 00
+L1918: DEFB $31 ;;Exponent $81, Bytes: 1
+ DEFB $00 ;;(+00,+00,+00)
+
+
+<a name="L191A"></a>;; <b>stk-half</b> 80 00 00 00 00
+L191A: DEFB $30 ;;Exponent: $80, Bytes: 1
+ DEFB $00 ;;(+00,+00,+00)
+
+
+<a name="L191C"></a>;; <b>stk-pi/2</b> 81 49 0F DA A2
+L191C: DEFB $F1 ;;Exponent: $81, Bytes: 4
+ DEFB $49,$0F,$DA,$A2 ;;
+
+<a name="L1921"></a>;; <b>stk-ten</b> 84 20 00 00 00
+L1921: DEFB $34 ;;Exponent: $84, Bytes: 1
+ DEFB $20 ;;(+00,+00,+00)
+
+
+; ------------------------
+; THE <b><font color=#333388>'TABLE OF ADDRESSES'</font></b>
+; ------------------------
+;
+; starts with binary operations which have two operands and one result.
+; three pseudo binary operations first.
+
+<a name="L1923"></a>;; <b>tbl-addrs</b>
+L1923: DEFW <A href="#L1C2F">L1C2F</a> ; $00 Address: $1C2F - jump-true
+ DEFW <A href="#L1A72">L1A72</a> ; $01 Address: $1A72 - exchange
+ DEFW <A href="#L19E3">L19E3</a> ; $02 Address: $19E3 - delete
+
+; true binary operations.
+
+ DEFW <A href="#L174C">L174C</a> ; $03 Address: $174C - subtract
+ DEFW <A href="#L17C6">L17C6</a> ; $04 Address: $176C - multiply
+ DEFW <A href="#L1882">L1882</a> ; $05 Address: $1882 - division
+ DEFW <A href="#L1DE2">L1DE2</a> ; $06 Address: $1DE2 - to-power
+ DEFW <A href="#L1AED">L1AED</a> ; $07 Address: $1AED - or
+
+ DEFW <A href="#L1AF3">L1AF3</a> ; $08 Address: $1B03 - no-&-no
+ DEFW <A href="#L1B03">L1B03</a> ; $09 Address: $1B03 - no-l-eql
+ DEFW <A href="#L1B03">L1B03</a> ; $0A Address: $1B03 - no-gr-eql
+ DEFW <A href="#L1B03">L1B03</a> ; $0B Address: $1B03 - nos-neql
+ DEFW <A href="#L1B03">L1B03</a> ; $0C Address: $1B03 - no-grtr
+ DEFW <A href="#L1B03">L1B03</a> ; $0D Address: $1B03 - no-less
+ DEFW <A href="#L1B03">L1B03</a> ; $0E Address: $1B03 - nos-eql
+ DEFW <A href="#L1755">L1755</a> ; $0F Address: $1755 - addition
+
+ DEFW <A href="#L1AF8">L1AF8</a> ; $10 Address: $1AF8 - str-&-no
+ DEFW <A href="#L1B03">L1B03</a> ; $11 Address: $1B03 - str-l-eql
+ DEFW <A href="#L1B03">L1B03</a> ; $12 Address: $1B03 - str-gr-eql
+ DEFW <A href="#L1B03">L1B03</a> ; $13 Address: $1B03 - strs-neql
+ DEFW <A href="#L1B03">L1B03</a> ; $14 Address: $1B03 - str-grtr
+ DEFW <A href="#L1B03">L1B03</a> ; $15 Address: $1B03 - str-less
+ DEFW <A href="#L1B03">L1B03</a> ; $16 Address: $1B03 - strs-eql
+ DEFW <A href="#L1B62">L1B62</a> ; $17 Address: $1B62 - strs-add
+
+; unary follow
+
+ DEFW <A href="#L1AA0">L1AA0</a> ; $18 Address: $1AA0 - neg
+
+ DEFW <A href="#L1C06">L1C06</a> ; $19 Address: $1C06 - code
+ DEFW <A href="#L1BA4">L1BA4</a> ; $1A Address: $1BA4 - val
+ DEFW <A href="#L1C11">L1C11</a> ; $1B Address: $1C11 - len
+ DEFW <A href="#L1D49">L1D49</a> ; $1C Address: $1D49 - sin
+ DEFW <A href="#L1D3E">L1D3E</a> ; $1D Address: $1D3E - cos
+ DEFW <A href="#L1D6E">L1D6E</a> ; $1E Address: $1D6E - tan
+ DEFW <A href="#L1DC4">L1DC4</a> ; $1F Address: $1DC4 - asn
+ DEFW <A href="#L1DD4">L1DD4</a> ; $20 Address: $1DD4 - acs
+ DEFW <A href="#L1D76">L1D76</a> ; $21 Address: $1D76 - atn
+ DEFW <A href="#L1CA9">L1CA9</a> ; $22 Address: $1CA9 - ln
+ DEFW <A href="#L1C5B">L1C5B</a> ; $23 Address: $1C5B - exp
+ DEFW <A href="#L1C46">L1C46</a> ; $24 Address: $1C46 - int
+ DEFW <A href="#L1DDB">L1DDB</a> ; $25 Address: $1DDB - sqr
+ DEFW <A href="#L1AAF">L1AAF</a> ; $26 Address: $1AAF - sgn
+ DEFW <A href="#L1AAA">L1AAA</a> ; $27 Address: $1AAA - abs
+ DEFW <A href="#L1ABE">L1ABE</a> ; $28 Address: $1A1B - peek
+ DEFW <A href="#L1AC5">L1AC5</a> ; $29 Address: $1AC5 - usr-no
+ DEFW <A href="#L1BD5">L1BD5</a> ; $2A Address: $1BD5 - str$
+ DEFW <A href="#L1B8F">L1B8F</a> ; $2B Address: $1B8F - chrs
+ DEFW <A href="#L1AD5">L1AD5</a> ; $2C Address: $1AD5 - not
+
+; end of true unary
+
+ DEFW <A href="#L19F6">L19F6</a> ; $2D Address: $19F6 - duplicate
+ DEFW <A href="#L1C37">L1C37</a> ; $2E Address: $1C37 - n-mod-m
+
+ DEFW <A href="#L1C23">L1C23</a> ; $2F Address: $1C23 - jump
+ DEFW <A href="#L19FC">L19FC</a> ; $30 Address: $19FC - stk-data
+
+ DEFW <A href="#L1C17">L1C17</a> ; $31 Address: $1C17 - dec-jr-nz
+ DEFW <A href="#L1ADB">L1ADB</a> ; $32 Address: $1ADB - less-0
+ DEFW <A href="#L1ACE">L1ACE</a> ; $33 Address: $1ACE - greater-0
+ DEFW <A href="#L002B">L002B</a> ; $34 Address: $002B - end-calc
+ DEFW <A href="#L1D18">L1D18</a> ; $35 Address: $1D18 - get-argt
+ DEFW <A href="#L18E4">L18E4</a> ; $36 Address: $18E4 - truncate
+ DEFW <A href="#L19E4">L19E4</a> ; $37 Address: $19E4 - fp-calc-2
+ DEFW <A href="#L155A">L155A</a> ; $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 <A href="#L1A7F">L1A7F</a> ; $39 Address: $1A7F - series-xx $80 - $9F.
+ DEFW <A href="#L1A51">L1A51</a> ; $3A Address: $1A51 - stk-const-xx $A0 - $BF.
+ DEFW <A href="#L1A63">L1A63</a> ; $3B Address: $1A63 - st-mem-xx $C0 - $DF.
+ DEFW <A href="#L1A45">L1A45</a> ; $3C Address: $1A45 - get-mem-xx $E0 - $FF.
+
+; Aside: 3D - 7F are therefore unused calculator literals.
+; 39 - 7B would be available for expansion.
+
+; -------------------------------
+; THE <b><font color=#333388>'FLOATING POINT CALCULATOR'</font></b>
+; -------------------------------
+;
+;
+
+<a name="L199D"></a>;; <b>CALCULATE</b>
+L199D: CALL <A href="#L1B85">L1B85</a> ; 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...
+
+<a name="L19A0"></a>;; <b>GEN-ENT-1</b>
+L19A0: 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
+
+<a name="L19A4"></a>;; <b>GEN-ENT-2</b>
+L19A4: 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.
+
+<a name="L19A7"></a>;; <b>RE-ENTRY</b>
+L19A7: 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
+
+<a name="L19AE"></a>;; <b>SCAN-ENT</b>
+L19AE: PUSH HL ; save pointer on stack *
+ AND A ; now test the literal
+ JP P,<A href="#L19C2">L19C2</a> ; 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 <A href="#L19D0">L19D0</a> ; forward to ENT-TABLE
+
+; ---
+
+; the branch was here with simple literals.
+
+<a name="L19C2"></a>;; <b>FIRST-3D</b>
+L19C2: CP $18 ; compare with first unary operations.
+ JR NC,<A href="#L19CE">L19CE</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 ;
+
+<a name="L19CE"></a>;; <b>DOUBLE-A</b>
+L19CE: RLCA ; double the literal
+ LD L,A ; and store in L for indexing
+
+<a name="L19D0"></a>;; <b>ENT-TABLE</b>
+L19D0: LD DE,<A href="#L1923">L1923</a> ; 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,<A href="#L19A7">L19A7</a> ; 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 <b><font color=#333388>'DELETE'</font></b> 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
+
+<a name="L19E3"></a>;; <b>delete</b>
+L19E3: RET ; return - indirect jump if from above.
+
+; ---------------------------------
+; THE <b><font color=#333388>'SINGLE OPERATION'</font></b> 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.
+
+<a name="L19E4"></a>;; <b>fp-calc-2</b>
+L19E4: 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 <A href="#L19AE">L19AE</a> ; back to SCAN-ENT
+ ; next literal will be end-calc in scanning
+
+; ------------------------------
+; THE <b><font color=#333388>'TEST 5 SPACES'</font></b> 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.
+
+<a name="L19EB"></a>;; <b>TEST-5-SP</b>
+L19EB: PUSH DE ; save
+ PUSH HL ; registers
+ LD BC,$0005 ; an overhead of five bytes
+ CALL <A href="#L0EC5">L0EC5</a> ; 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 <b><font color=#333388>'MOVE A FLOATING POINT NUMBER'</font></b> 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
+
+<a name="L19F6"></a>;; <b>duplicate</b>
+<a name="L19F6"></a>;; <b>MOVE-FP</b>
+L19F6: CALL <A href="#L19EB">L19EB</a> ; 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 <b><font color=#333388>'STACK LITERALS'</font></b> 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.
+
+<a name="L19FC"></a>;; <b>stk-data</b>
+L19FC: LD H,D ; transfer STKEND
+ LD L,E ; to HL for result.
+
+<a name="L19FE"></a>;; <b>STK-CONST</b>
+L19FE: CALL <A href="#L19EB">L19EB</a> ; 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 ??.
+
+ 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,<A href="#L1A14">L1A14</a> ; 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).
+
+<a name="L1A14"></a>;; <b>FORM-EXP</b>
+L1A14: 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
+ 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
+
+<a name="L1A27"></a>;; <b>STK-ZEROS</b>
+L1A27: 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 <A href="#L1A27">L1A27</a> ; loop back to STK-ZEROS until done.
+
+; -------------------------------
+; THE <b><font color=#333388>'SKIP CONSTANTS'</font></b> SUBROUTINE
+; -------------------------------
+; This routine traverses 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.
+
+<a name="L1A2D"></a>;; <b>SKIP-CONS</b>
+L1A2D: AND A ; test if initially zero.
+
+<a name="L1A2E"></a>;; <b>SKIP-NEXT</b>
+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
+ ; <font color=#9900FF>Note.</font> not a fault but this has to be
+ ; moved elsewhere when running in RAM.
+ ;
+ CALL <A href="#L19FE">L19FE</a> ; routine STK-CONST works through variable
+ ; length records.
+
+ POP DE ; restore real STKEND
+ POP AF ; restore count
+ DEC A ; decrease
+ JR <A href="#L1A2E">L1A2E</a> ; loop back to SKIP-NEXT
+
+; --------------------------------
+; THE <b><font color=#333388>'MEMORY LOCATION'</font></b> 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.
+
+<a name="L1A3C"></a>;; <b>LOC-MEM</b>
+L1A3C: 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 <b><font color=#333388>'GET FROM MEMORY AREA'</font></b> SUBROUTINE
+; -------------------------------------
+; offsets $E0 to $FF: 'get-mem-0', 'get-mem-1' etc.
+; A holds $00-$1F offset.
+; The calculator stack increases by 5 bytes.
+
+<a name="L1A45"></a>;; <b>get-mem-xx</b>
+L1A45: PUSH DE ; save STKEND
+ LD HL,($401F) ; MEM is base address of the memory cells.
+ CALL <A href="#L1A3C">L1A3C</a> ; routine LOC-MEM so that HL = first byte
+ CALL <A href="#L19F6">L19F6</a> ; 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 <b><font color=#333388>'STACK A CONSTANT'</font></b> 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 isn't very efficient and it would have been better to hold the
+; numbers in full, five byte form and stack them in a similar manner
+; to that which would be used later for semi-tone table values.
+
+<a name="L1A51"></a>;; <b>stk-const-xx</b>
+L1A51: LD H,D ; save STKEND - required for result
+ LD L,E ;
+ EXX ; swap
+ PUSH HL ; save pointer to next literal
+ LD HL,<A href="#L1915">L1915</a> ; Address: stk-zero - start of table of
+ ; constants
+ EXX ;
+ CALL <A href="#L1A2D">L1A2D</a> ; routine SKIP-CONS
+ CALL <A href="#L19FE">L19FE</a> ; routine STK-CONST
+ EXX ;
+ POP HL ; restore pointer to next literal.
+ EXX ;
+ RET ; return.
+
+; ---------------------------------------
+; THE <b><font color=#333388>'STORE IN A MEMORY AREA'</font></b> 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.
+
+<a name="L1A63"></a>;; <b>st-mem-xx</b>
+L1A63: PUSH HL ; save the result pointer.
+ EX DE,HL ; transfer to DE.
+ LD HL,($401F) ; fetch MEM the base of memory area.
+ CALL <A href="#L1A3C">L1A3C</a> ; routine LOC-MEM sets HL to the destination.
+ EX DE,HL ; swap - HL is start, DE is destination.
+ CALL <A href="#L19F6">L19F6</a> ; routine MOVE-FP.
+ ; note. a short ld bc,5; ldir
+ ; the embedded memory check is not required
+ ; so these instructions would be faster!
+ EX DE,HL ; DE = STKEND
+ POP HL ; restore original result pointer
+ RET ; return.
+
+; -------------------------
+; THE <b><font color=#333388>'EXCHANGE'</font></b> 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.
+
+<a name="L1A72"></a>;; <b>exchange</b>
+L1A72: LD B,$05 ; there are five bytes to be swapped
+
+; start of loop.
+
+<a name="L1A74"></a>;; <b>SWAP-BYTE</b>
+L1A74: LD A,(DE) ; each byte of second
+ LD C,(HL) ; each byte of first
+ EX DE,HL ; swap pointers
+ LD (DE),A ; store each byte of first
+ LD (HL),C ; store each byte of second
+ INC HL ; advance both
+ INC DE ; pointers.
+ DJNZ <A href="#L1A74">L1A74</a> ; loop back to SWAP-BYTE until all 5 done.
+
+ EX DE,HL ; even up the exchanges
+ ; so that DE addresses STKEND.
+ RET ; return.
+
+; ---------------------------------
+; THE <b><font color=#333388>'SERIES GENERATOR'</font></b> 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.
+
+<a name="L1A7F"></a>;; <b>series-xx</b>
+L1A7F: LD B,A ; parameter $00 - $1F to B counter
+ CALL <A href="#L19A0">L19A0</a> ; 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
+
+<a name="L1A89"></a>;; <b>G-LOOP</b>
+L1A89: 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 <A href="#L19FC">L19FC</a> ; routine STK-DATA is called directly to
+ ; push a value and advance H'L'.
+ CALL <A href="#L19A4">L19A4</a> ; 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 $EE ;;back to <A href="#L1A89">L1A89</a>, 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.
+
+<a name="L1AA0"></a>;; <b>NEGATE</b>
+<a name="L1AA0"></a>;; <b>negate</b>
+L1AA0: 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.
+
+<a name="L1AAA"></a>;; <b>abs</b>
+L1AAA: 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 negative. If it is zero then it is left as such.
+
+<a name="L1AAF"></a>;; <b>sgn</b>
+L1AAF: 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,<A href="#L1AE0">L1AE0</a> ; 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.
+
+<a name="L1ABE"></a>;; <b>peek</b>
+L1ABE: CALL <A href="#L0EA7">L0EA7</a> ; routine FIND-INT puts address in BC.
+ LD A,(BC) ; load contents into A register.
+
+<a name="L1AC2"></a>;; <b>IN-PK-STK</b>
+L1AC2: JP <A href="#L151D">L151D</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.
+; <font color=#9900FF>Note.</font> that STACK-BC re-initializes the IY register to $4000 if a user-written
+; program has altered it.
+
+<a name="L1AC5"></a>;; <b>usr-no</b>
+L1AC5: CALL <A href="#L0EA7">L0EA7</a> ; routine FIND-INT to fetch the
+ ; supplied address into BC.
+
+ LD HL,<A href="#L1520">L1520</a> ; 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 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.
+
+<a name="L1ACE"></a>;; <b>GREATER-0</b>
+<a name="L1ACE"></a>;; <b>greater-0</b>
+L1ACE: 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 <A href="#L1ADC">L1ADC</a> ; 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.
+
+<a name="L1AD5"></a>;; <b>NOT</b>
+<a name="L1AD5"></a>;; <b>not</b>
+L1AD5: LD A,(HL) ; get exponent byte.
+ NEG ; negate - sets carry if non-zero.
+ CCF ; complement so carry set if zero, else reset.
+ JR <A href="#L1AE0">L1AE0</a> ; 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.
+
+<a name="L1ADB"></a>;; <b>less-0</b>
+L1ADB: XOR A ; set xor mask to zero
+ ; (carry will become set if sign is negative).
+
+; transfer sign of mantissa to Carry Flag.
+
+<a name="L1ADC"></a>;; <b>SIGN-TO-C</b>
+L1ADC: 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.
+
+<a name="L1AE0"></a>;; <b>FP-0/1</b>
+L1AE0: PUSH HL ; save pointer to the first byte
+ LD B,$05 ; five bytes to do.
+
+<a name="L1AE3"></a>;; <b>FP-loop</b>
+L1AE3: LD (HL),$00 ; insert a zero.
+ INC HL ;
+ DJNZ <A href="#L1AE3">L1AE3</a> ; 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).
+
+<a name="L1AED"></a>;; <b>or</b>
+L1AED: LD A,(DE) ; fetch exponent of second number
+ AND A ; test it.
+ RET Z ; return if zero.
+
+ SCF ; set carry flag
+ JR <A href="#L1AE0">L1AE0</a> ; 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.
+
+<a name=""></a>;; <b>no-&-no</b>
+L1AF3: LD A,(DE) ; fetch exponent of second number.
+ AND A ; test it.
+ RET NZ ; return if not zero.
+
+ JR <A href="#L1AE0">L1AE0</a> ; 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.
+
+<a name=""></a>;; <b>str-&-no</b>
+L1AF8: 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 <b><font color=#333388>'x>y'</font></b> 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.
+
+<a name="L1B03"></a>;; <b>no-l-eql,etc.</b>
+L1B03: LD A,B ; transfer literal to accumulator.
+ SUB $08 ; subtract eight - which is not useful.
+
+ BIT 2,A ; isolate '>', '<', '='.
+
+ JR NZ,<A href="#L1B0B">L1B0B</a> ; skip to EX-OR-NOT with these.
+
+ DEC A ; else make $00-$02, $08-$0A to match bits 0-2.
+
+<a name="L1B0B"></a>;; <b>EX-OR-NOT</b>
+L1B0B: RRCA ; the first RRCA sets carry for a swap.
+ JR NC,<A href="#L1B16">L1B16</a> ; 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 <A href="#L1A72">L1A72</a> ; 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.
+
+; <font color=#9900FF>Note.</font> 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.
+
+<a name="L1B16"></a>;; <b>NU-OR-STR</b>
+L1B16: BIT 2,A ; test if a string comparison.
+ JR NZ,<A href="#L1B21">L1B21</a> ; 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 <A href="#L174C">L174C</a> ; routine subtract leaves result on stack.
+ JR <A href="#L1B54">L1B54</a> ; forward to END-TESTS
+
+; ---
+
+<a name="L1B21"></a>;; <b>STRINGS</b>
+L1B21: RRCA ; 2nd RRCA causes eql/neql to set carry.
+ PUSH AF ; save A and carry.
+
+ CALL <A href="#L13F8">L13F8</a> ; routine STK-FETCH gets 2nd string params
+ PUSH DE ; save start2 *.
+ PUSH BC ; and the length.
+
+ CALL <A href="#L13F8">L13F8</a> ; 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.
+
+<a name="L1B2C"></a>;; <b>BYTE-COMP</b>
+L1B2C: 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,<A href="#L1B3D">L1B3D</a> ; forward to SEC-PLUS if second not null.
+
+ OR C ; test length of first string.
+
+<a name="L1B33"></a>;; <b>SECND-LOW</b>
+L1B33: POP BC ; pop the second length off stack.
+ JR Z,<A href="#L1B3A">L1B3A</a> ; 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.
+ ; <font color=#9900FF>Note.</font> 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 <A href="#L1B50">L1B50</a> ; forward to leave via STR-TEST
+
+; ---
+; the branch was here with a match
+
+<a name="L1B3A"></a>;; <b>BOTH-NULL</b>
+L1B3A: POP AF ; restore carry - set for eql/neql
+ JR <A href="#L1B50">L1B50</a> ; forward to STR-TEST
+
+; ---
+; the branch was here when 2nd string not null and low byte of first is yet
+; to be tested.
+
+
+<a name="L1B3D"></a>;; <b>SEC-PLUS</b>
+L1B3D: OR C ; test the length of first string.
+ JR Z,<A href="#L1B4D">L1B4D</a> ; 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,<A href="#L1B4D">L1B4D</a> ; forward to FRST-LESS if carry set
+
+ JR NZ,<A href="#L1B33">L1B33</a> ; 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 <A href="#L1B2C">L1B2C</a> ; back to BYTE-COMP
+
+; ---
+; the false condition.
+
+<a name="L1B4D"></a>;; <b>FRST-LESS</b>
+L1B4D: POP BC ; discard length
+ POP AF ; pop A
+ AND A ; clear the carry for false result.
+
+; ---
+; exact match and x$>y$ rejoin here
+
+<a name="L1B50"></a>;; <b>STR-TEST</b>
+L1B50: 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.
+
+<a name="L1B54"></a>;; <b>END-TESTS</b>
+L1B54: POP AF ; pop carry - will be set if eql/neql
+ PUSH AF ; save it again.
+
+ CALL C,<A href="#L1AD5">L1AD5</a> ; routine NOT sets true(1) if equal(0)
+ ; or, for strings, applies true result.
+ CALL <A href="#L1ACE">L1ACE</a> ; greater-0 ??????????
+
+
+ POP AF ; pop A
+ RRCA ; the third RRCA - test for '<=', '>=' or '<>'.
+ CALL NC,<A href="#L1AD5">L1AD5</a> ; apply a terminal NOT if so.
+ RET ; return.
+
+; -------------------------
+; String concatenation ($17)
+; -------------------------
+; 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.
+
+<a name="L1B62"></a>;; <b>strs-add</b>
+L1B62: CALL <A href="#L13F8">L13F8</a> ; routine STK-FETCH fetches string parameters
+ ; and deletes calculator stack entry.
+ PUSH DE ; save start address.
+ PUSH BC ; and length.
+
+ CALL <A href="#L13F8">L13F8</a> ; 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 <A href="#L12C3">L12C3</a> ; 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,<A href="#L1B7D">L1B7D</a> ; to OTHER-STR if null string
+
+ LDIR ; copy string to workspace.
+
+<a name="L1B7D"></a>;; <b>OTHER-STR</b>
+L1B7D: POP BC ; now second length
+ POP HL ; and start of string
+ LD A,B ; test this one
+ OR C ; for zero length
+ JR Z,<A href="#L1B85">L1B85</a> ; skip forward to STK-PNTRS if so as complete.
+
+ LDIR ; else copy the bytes.
+ ; and continue into next routine which
+ ; sets the calculator stack pointers.
+
+; --------------------
+; Check stack pointers
+; --------------------
+; Register DE is set to STKEND and HL, the result pointer, is set to five
+; locations below this.
+; 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.
+
+<a name="L1B85"></a>;; <b>STK-PNTRS</b>
+L1B85: 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.
+
+; ----------------
+; Handle CHR$ (2B)
+; ----------------
+; 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".
+; <font color=#9900FF>Note.</font> the ZX81 does not have an ASCII character set.
+
+<a name="L1B8F"></a>;; <b>chrs</b>
+L1B8F: CALL <A href="#L15CD">L15CD</a> ; routine FP-TO-A puts the number in A.
+
+ JR C,<A href="#L1BA2">L1BA2</a> ; forward to REPORT-Bd if overflow
+ JR NZ,<A href="#L1BA2">L1BA2</a> ; 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
+
+ CALL <A href="#L12C3">L12C3</a> ; routine STK-STO-$ stacks descriptor.
+
+ EX DE,HL ; make HL point to result and DE to STKEND.
+ RET ; return.
+
+; ---
+
+<a name="L1BA2"></a>;; <b>REPORT-Bd</b>
+L1BA2: RST 08H ; ERROR-1
+ DEFB $0A ; Error Report: Integer out of range
+
+; ----------------------------
+; Handle VAL ($1A)
+; ----------------------------
+; 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.
+
+<a name="L1BA4"></a>;; <b>val</b>
+L1BA4: LD HL,($4016) ; fetch value of system variable CH_ADD
+ PUSH HL ; and save on the machine stack.
+
+ CALL <A href="#L13F8">L13F8</a> ; 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 <A href="#L0D92">L0D92</a> ; routine CLASS-06 - SCANNING evaluates string
+ ; expression and checks for integer result.
+
+ CALL <A href="#L0D22">L0D22</a> ; 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 <A href="#L0F55">L0F55</a> ; 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 <A href="#L1B85">L1B85</a> ; 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.
+
+; ----------------
+; Handle STR$ (2A)
+; ----------------
+; 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.
+
+<a name="L1BD5"></a>;; <b>str$</b>
+L1BD5: 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 <A href="#L15DB">L15DB</a> ; 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.
+
+ CALL <A href="#L12C3">L12C3</a> ; routine STK-STO-$ stores the string
+ ; descriptor on the calculator stack.
+
+ EX DE,HL ; HL = last value, DE = STKEND.
+ RET ; return.
+
+
+; -------------------
+; THE <b><font color=#333388>'CODE'</font></b> FUNCTION
+; -------------------
+; <font color=#339933>(offset $19: 'code')</font>
+; 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).
+
+
+<a name="L1C06"></a>;; <b>code</b>
+L1C06: CALL <A href="#L13F8">L13F8</a> ; 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,<A href="#L1C0E">L1C0E</a> ; skip to STK-CODE with zero if the null string.
+
+ LD A,(DE) ; else fetch the first character.
+
+<a name="L1C0E"></a>;; <b>STK-CODE</b>
+L1C0E: JP <A href="#L151D">L151D</a> ; jump back to STACK-A (with memory check)
+
+; --------------------
+; THE <b><font color=#333388>'LEN'</font></b> SUBROUTINE
+; --------------------
+; <font color=#339933>(offset $1b: 'len')</font>
+; 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
+
+<a name="L1C11"></a>;; <b>len</b>
+L1C11: CALL <A href="#L13F8">L13F8</a> ; routine STK-FETCH to fetch and delete the
+ ; string parameters from the calculator stack.
+ ; register BC now holds the length of string.
+
+ JP <A href="#L1520">L1520</a> ; jump back to STACK-BC to save result on the
+ ; calculator stack (with memory check).
+
+; -------------------------------------
+; THE <b><font color=#333388>'DECREASE THE COUNTER'</font></b> SUBROUTINE
+; -------------------------------------
+; <font color=#339933>(offset $31: 'dec-jr-nz')</font>
+; The calculator has an instruction that decrements a single-byte
+; pseudo-register and makes consequential relative jumps just like
+; the Z80's DJNZ instruction.
+
+<a name="L1C17"></a>;; <b>dec-jr-nz</b>
+L1C17: 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,<A href="#L1C24">L1C24</a> ; to JUMP-2 if not zero
+
+ INC HL ; step past the jump length.
+ EXX ; switch in the main set.
+ RET ; return.
+
+; <font color=#9900FF>Note.</font> 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 <b><font color=#333388>'JUMP'</font></b> SUBROUTINE
+; ---------------------
+; <font color=#339933>(Offset $2F; 'jump')</font>
+; This enables the calculator to perform relative jumps just like
+; the Z80 chip's JR instruction.
+; This is one of the few routines to be polished for the ZX Spectrum.
+; See, without looking at the ZX Spectrum ROM, if you can get rid of the
+; relative jump.
+
+<a name="L1C23"></a>;; <b>jump</b>
+<a name="L1C23"></a>;; <b>JUMP</b>
+L1C23: EXX ;switch in pointer set
+
+<a name="L1C24"></a>;; <b>JUMP-2</b>
+L1C24: LD E,(HL) ; the jump byte 0-127 forward, 128-255 back.
+ XOR A ; clear accumulator.
+ BIT 7,E ; test if negative jump
+ JR Z,<A href="#L1C2B">L1C2B</a> ; skip, if positive, to JUMP-3.
+
+ CPL ; else change to $FF.
+
+<a name="L1C2B"></a>;; <b>JUMP-3</b>
+L1C2B: LD D,A ; transfer to high byte.
+ ADD HL,DE ; advance calculator pointer forward or back.
+
+ EXX ; switch out pointer set.
+ RET ; return.
+
+; -----------------------------
+; THE <b><font color=#333388>'JUMP ON TRUE'</font></b> SUBROUTINE
+; -----------------------------
+; <font color=#339933>(Offset $00; 'jump-true')</font>
+; 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.
+
+<a name="L1C2F"></a>;; <b>jump-true</b>
+L1C2F: LD A,(DE) ; collect exponent byte
+
+ AND A ; is result 0 or 1 ?
+ JR NZ,<A href="#L1C23">L1C23</a> ; 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 <b><font color=#333388>'MODULUS'</font></b> SUBROUTINE
+; ------------------------
+; ( Offset $2E: 'n-mod-m' )
+; <font color=#CC00FF>( i1, i2 -- i3, i4 )</font>
+; 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.
+
+<a name="L1C37"></a>;; <b>n-mod-m</b>
+L1C37: 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 <b><font color=#333388>'INTEGER'</font></b> FUNCTION
+; ----------------------
+; <font color=#339933>(offset $24: 'int')</font>
+; 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 or minus 3.4 as examples.
+
+<a name="L1C46"></a>;; <b>int</b>
+L1C46: 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 <A href="#L1C46">L1C46</a>, X-NEG
+
+ DEFB $36 ;;truncate trunc 3.4 = 3.
+ DEFB $34 ;;end-calc 3.
+
+ RET ; return with + int x on stack.
+
+
+<a name="L1C4E"></a>;; <b>X-NEG</b>
+L1C4E: 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 <A href="#L1C59">L1C59</a>, EXIT -3.
+
+ DEFB $A1 ;;stk-one -3, 1.
+ DEFB $03 ;;subtract -4.
+
+<a name="L1C59"></a>;; <b>EXIT</b>
+L1C59: DEFB $34 ;;end-calc -4.
+
+ RET ; return.
+
+
+; ----------------
+; Exponential (23)
+; ----------------
+;
+;
+
+<a name="L1C5B"></a>;; <b>EXP</b>
+<a name="L1C5B"></a>;; <b>exp</b>
+L1C5B: RST 28H ;; FP-CALC
+ DEFB $30 ;;stk-data
+ 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 <A href="#L15CD">L15CD</a> ; routine FP-TO-A
+ JR NZ,<A href="#L1C9B">L1C9B</a> ; to N-NEGTV
+
+ JR C,<A href="#L1C99">L1C99</a> ; to REPORT-6b
+
+ ADD A,(HL) ;
+ JR NC,<A href="#L1CA2">L1CA2</a> ; to RESULT-OK
+
+
+<a name="L1C99"></a>;; <b>REPORT-6b</b>
+L1C99: RST 08H ; ERROR-1
+ DEFB $05 ; Error Report: Number too big
+
+<a name="L1C9B"></a>;; <b>N-NEGTV</b>
+L1C9B: JR C,<A href="#L1CA4">L1CA4</a> ; to RSLT-ZERO
+
+ SUB (HL) ;
+ JR NC,<A href="#L1CA4">L1CA4</a> ; to RSLT-ZERO
+
+ NEG ; Negate
+
+<a name="L1CA2"></a>;; <b>RESULT-OK</b>
+L1CA2: LD (HL),A ;
+ RET ; return.
+
+
+<a name="L1CA4"></a>;; <b>RSLT-ZERO</b>
+L1CA4: RST 28H ;; FP-CALC
+ DEFB $02 ;;delete
+ DEFB $A0 ;;stk-zero
+ DEFB $34 ;;end-calc
+
+ RET ; return.
+
+
+; --------------------------------
+; THE <b><font color=#333388>'NATURAL LOGARITHM'</font></b> FUNCTION
+; --------------------------------
+; <font color=#339933>(offset $22: 'ln')</font>
+; 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.
+
+<a name="L1CA9"></a>;; <b>ln</b>
+L1CA9: RST 28H ;; FP-CALC
+ DEFB $2D ;;duplicate
+ DEFB $33 ;;greater-0
+ DEFB $00 ;;jump-true
+ DEFB $04 ;;to <A href="#L1CB1">L1CB1</a>, VALID
+
+ DEFB $34 ;;end-calc
+
+
+<a name="L1CAF"></a>;; <b>REPORT-Ab</b>
+L1CAF: RST 08H ; ERROR-1
+ DEFB $09 ; Error Report: Invalid argument
+
+<a name="L1CB1"></a>;; <b>VALID</b>
+L1CB1: DEFB $A0 ;;stk-zero <font color=#9900FF>Note.</font> not
+ DEFB $02 ;;delete necessary.
+ DEFB $34 ;;end-calc
+ LD A,(HL) ;
+
+ LD (HL),$80 ;
+ CALL <A href="#L151D">L151D</a> ; routine STACK-A
+
+ 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 <A href="#L1CD2">L1CD2</a>, 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
+
+<a name="L1CD2"></a>;; <b>GRE.8</b>
+L1CD2: DEFB $01 ;;exchange
+ DEFB $30 ;;stk-data
+ 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 <b><font color=#333388>'TRIGONOMETRIC'</font></b> 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 <b><font color=#333388>'REDUCE ARGUMENT'</font></b> SUBROUTINE
+; --------------------------------
+; <font color=#339933>(offset $35: 'get-argt')</font>
+;
+; 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.
+
+
+<a name="L1D18"></a>;; <b>get-argt</b>
+L1D18: 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 <A href="#L1D35">L1D35</a>, 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.
+
+<a name="L1D35"></a>;; <b>ZPLUS</b>
+L1D35: 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 <A href="#L1D3C">L1D3C</a>, YNEG
+ ;;if angle in quadrant III
+
+; else angle is within quadrant II (-1 to 0)
+
+ DEFB $18 ;;negate range +1 to 0
+
+
+<a name="L1D3C"></a>;; <b>YNEG</b>
+L1D3C: DEFB $34 ;;end-calc quadrants II and III correct.
+
+ RET ; return.
+
+
+; ---------------------
+; THE <b><font color=#333388>'COSINE'</font></b> FUNCTION
+; ---------------------
+; <font color=#339933>(offset $1D: 'cos')</font>
+; 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.
+
+<a name="L1D3E"></a>;; <b>cos</b>
+L1D3E: 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 <A href="#L1D4B">L1D4B</a>, C-ENT
+ ;;forward to common code if in QII or QIII
+
+
+ DEFB $18 ;;negate else make positive.
+ DEFB $2F ;;jump
+ DEFB $03 ;;fwd to <A href="#L1D4B">L1D4B</a>, C-ENT
+ ;;with quadrants QI and QIV
+
+; -------------------
+; THE <b><font color=#333388>'SINE'</font></b> FUNCTION
+; -------------------
+; <font color=#339933>(offset $1C: 'sin')</font>
+; 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.
+
+<a name="L1D49"></a>;; <b>sin</b>
+L1D49: RST 28H ;; FP-CALC angle in radians
+ DEFB $35 ;;get-argt reduce - sign now correct.
+
+<a name="L1D4B"></a>;; <b>C-ENT</b>
+L1D4B: 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 <b><font color=#333388>'TANGENT'</font></b> FUNCTION
+; ----------------------
+; <font color=#339933>(offset $1E: 'tan')</font>
+;
+; 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.
+
+<a name="L1D6E"></a>;; <b>tan</b>
+L1D6E: 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 <b><font color=#333388>'ARCTAN'</font></b> FUNCTION
+; ---------------------
+; <font color=#339933>(Offset $21: 'atn')</font>
+; 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.
+
+<a name="L1D76"></a>;; <b>atn</b>
+L1D76: LD A,(HL) ; fetch exponent
+ CP $81 ; compare to that for 'one'
+ JR C,<A href="#L1D89">L1D89</a> ; 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 <A href="#L1D8B">L1D8B</a>, CASES
+
+ DEFB $18 ;;negate
+ DEFB $2F ;;jump
+ DEFB $03 ;;to <A href="#L1D8B">L1D8B</a>, CASES
+
+; ---
+
+<a name="L1D89"></a>;; <b>SMALL</b>
+L1D89: RST 28H ;; FP-CALC
+ DEFB $A0 ;;stk-zero
+
+<a name="L1D8B"></a>;; <b>CASES</b>
+L1D8B: 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 <b><font color=#333388>'ARCSIN'</font></b> FUNCTION
+; ---------------------
+; <font color=#339933>(Offset $1F: 'asn')</font>
+; 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+d = 180 degrees and b+a = 180 degrees then c=a/2.
+;
+; A value higher than 1 gives the required error as attempting to find the
+; square root of a negative number generates an error in Sinclair BASIC.
+
+<a name="L1DC4"></a>;; <b>asn</b>
+L1DC4: 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 <b><font color=#333388>'ARCCOS'</font></b> FUNCTION
+; ------------------------
+; <font color=#339933>(Offset $20: 'acs')</font>
+; 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
+
+<a name="L1DD4"></a>;; <b>acs</b>
+L1DD4: 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 <b><font color=#333388>'SQUARE ROOT'</font></b> FUNCTION
+; --------------------------
+; <font color=#339933>(Offset $25: 'sqr')</font>
+; Error A if argument is negative.
+; This routine is remarkable for its brevity - 7 bytes.
+; The ZX81 code was originally 9K and various techniques had to be
+; used to shoe-horn it into an 8K Rom chip.
+
+
+<a name="L1DDB"></a>;; <b>sqr</b>
+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 <A href="#L1DFD">L1DFD</a>, 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 <b><font color=#333388>'EXPONENTIATION'</font></b> OPERATION
+; ------------------------------
+; <font color=#339933>(Offset $06: 'to-power')</font>
+; This raises the first number X to the power of the second number Y.
+; As with the ZX80,
+; 0 ** 0 = 1
+; 0 ** +n = 0
+; 0 ** -n = arithmetic overflow.
+
+<a name="L1DE2"></a>;; <b>to-power</b>
+L1DE2: 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 <A href="#L1DEE">L1DEE</a>, 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.
+ DEFB $04 ;;multiply Y * LN X
+ DEFB $34 ;;end-calc
+
+ JP <A href="#L1C5B">L1C5B</a> ; jump back to EXP routine. ->
+
+; ---
+
+; These routines form the three simple results when the number is zero.
+; begin by deleting the known zero to leave Y the power factor.
+
+<a name="L1DEE"></a>;; <b>XISO</b>
+L1DEE: DEFB $02 ;;delete Y.
+ DEFB $2D ;;duplicate Y, Y.
+ DEFB $2C ;;not Y, (1/0).
+ DEFB $00 ;;jump-true
+ DEFB $09 ;;forward to <A href="#L1DFB">L1DFB</a>, 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 <A href="#L1DFD">L1DFD</a>, LAST if Y was any positive
+ ;; number.
+
+; else force division by zero thereby raising an Arithmetic overflow error.
+; There are some one and two-byte alternatives but perhaps the most formal
+; might have been to use end-calc; rst 08; defb 05.
+
+ DEFB $A1 ;;stk-one 0, 1.
+ DEFB $01 ;;exchange 1, 0.
+ DEFB $05 ;;division 1/0 >> error
+
+; ---
+
+<a name="L1DFB"></a>;; <b>ONE</b>
+L1DFB: DEFB $02 ;;delete .
+ DEFB $A1 ;;stk-one 1.
+
+<a name="L1DFD"></a>;; <b>LAST</b>
+L1DFD: DEFB $34 ;;end-calc last value 1 or 0.
+
+ RET ; return.
+
+; ---------------------
+; THE <b><font color=#333388>'SPARE LOCATIONS'</font></b>
+; ---------------------
+
+<a name="L1DFF"></a>;; <b>SPARE</b>
+L1DFF: DEFB $FF ; That's all folks.
+
+
+
+; ------------------------
+; THE <b><font color=#333388>'ZX81 CHARACTER SET'</font></b>
+; ------------------------
+
+<a name="L1E00"></a>;; <b>char-set</b> - begins with space character.
+
+; $00 - <b>Character: ' ' </b>CHR$(0)
+
+L<B>1</B>E00: DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+
+; $01 - <b>Character: mosaic </b>CHR$(1)
+
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+
+
+; $02 - <b>Character: mosaic </b>CHR$(2)
+
+ DEFB %0000<B>1</B><B>1</B><B>1</B><B>1</B>
+ DEFB %0000<B>1</B><B>1</B><B>1</B><B>1</B>
+ DEFB %0000<B>1</B><B>1</B><B>1</B><B>1</B>
+ DEFB %0000<B>1</B><B>1</B><B>1</B><B>1</B>
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+
+
+; $03 - <b>Character: mosaic </b>CHR$(3)
+
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+
+; $04 - <b>Character: mosaic </b>CHR$(4)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+
+; $05 - <b>Character: mosaic </b>CHR$(1)
+
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+
+; $06 - <b>Character: mosaic </b>CHR$(1)
+
+ DEFB %0000<B>1</B><B>1</B><B>1</B><B>1</B>
+ DEFB %0000<B>1</B><B>1</B><B>1</B><B>1</B>
+ DEFB %0000<B>1</B><B>1</B><B>1</B><B>1</B>
+ DEFB %0000<B>1</B><B>1</B><B>1</B><B>1</B>
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+
+; $07 - <b>Character: mosaic </b>CHR$(1)
+
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B>0000
+
+; $08 - <b>Character: mosaic </b>CHR$(1)
+
+ DEFB %<B>1</B>0<B>1</B>0<B>1</B>0<B>1</B>0
+ DEFB %0<B>1</B>0<B>1</B>0<B>1</B>0<B>1</B>
+ DEFB %<B>1</B>0<B>1</B>0<B>1</B>0<B>1</B>0
+ DEFB %0<B>1</B>0<B>1</B>0<B>1</B>0<B>1</B>
+ DEFB %<B>1</B>0<B>1</B>0<B>1</B>0<B>1</B>0
+ DEFB %0<B>1</B>0<B>1</B>0<B>1</B>0<B>1</B>
+ DEFB %<B>1</B>0<B>1</B>0<B>1</B>0<B>1</B>0
+ DEFB %0<B>1</B>0<B>1</B>0<B>1</B>0<B>1</B>
+
+; $09 - <b>Character: mosaic </b>CHR$(1)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %<B>1</B>0<B>1</B>0<B>1</B>0<B>1</B>0
+ DEFB %0<B>1</B>0<B>1</B>0<B>1</B>0<B>1</B>
+ DEFB %<B>1</B>0<B>1</B>0<B>1</B>0<B>1</B>0
+ DEFB %0<B>1</B>0<B>1</B>0<B>1</B>0<B>1</B>
+
+; $0A - <b>Character: mosaic </b>CHR$(10)
+
+ DEFB %<B>1</B>0<B>1</B>0<B>1</B>0<B>1</B>0
+ DEFB %0<B>1</B>0<B>1</B>0<B>1</B>0<B>1</B>
+ DEFB %<B>1</B>0<B>1</B>0<B>1</B>0<B>1</B>0
+ DEFB %0<B>1</B>0<B>1</B>0<B>1</B>0<B>1</B>
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+
+; $0B - <b>Character: '"' </b>CHR$(11)
+
+ DEFB %00000000
+ DEFB %00<B>1</B>00<B>1</B>00
+ DEFB %00<B>1</B>00<B>1</B>00
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+
+; $0B - <b>Character: £ </b>CHR$(12)
+
+ DEFB %00000000
+ DEFB %000<B>1</B><B>1</B><B>1</B>00
+ DEFB %00<B>1</B>000<B>1</B>0
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B>000
+ DEFB %00<B>1</B>00000
+ DEFB %00<B>1</B>00000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %00000000
+
+; $0B - <b>Character: '$' </b>CHR$(13)
+
+ DEFB %00000000
+ DEFB %0000<B>1</B>000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %00<B>1</B>0<B>1</B>000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %0000<B>1</B>0<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %0000<B>1</B>000
+
+; $0B - <b>Character: ':' </b>CHR$(14)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %000<B>1</B>0000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %000<B>1</B>0000
+ DEFB %00000000
+
+; $0B - <b>Character: '?' </b>CHR$(15)
+
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00000<B>1</B>00
+ DEFB %0000<B>1</B>000
+ DEFB %00000000
+ DEFB %0000<B>1</B>000
+ DEFB %00000000
+
+; $10 - <b>Character: '(' </b>CHR$(16)
+
+ DEFB %00000000
+ DEFB %00000<B>1</B>00
+ DEFB %0000<B>1</B>000
+ DEFB %0000<B>1</B>000
+ DEFB %0000<B>1</B>000
+ DEFB %0000<B>1</B>000
+ DEFB %00000<B>1</B>00
+ DEFB %00000000
+
+; $11 - <b>Character: ')' </b>CHR$(17)
+
+ DEFB %00000000
+ DEFB %00<B>1</B>00000
+ DEFB %000<B>1</B>0000
+ DEFB %000<B>1</B>0000
+ DEFB %000<B>1</B>0000
+ DEFB %000<B>1</B>0000
+ DEFB %00<B>1</B>00000
+ DEFB %00000000
+
+; $12 - <b>Character: '>' </b>CHR$(18)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %000<B>1</B>0000
+ DEFB %0000<B>1</B>000
+ DEFB %00000<B>1</B>00
+ DEFB %0000<B>1</B>000
+ DEFB %000<B>1</B>0000
+ DEFB %00000000
+
+; $13 - <b>Character: '<' </b>CHR$(19)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000<B>1</B>00
+ DEFB %0000<B>1</B>000
+ DEFB %000<B>1</B>0000
+ DEFB %0000<B>1</B>000
+ DEFB %00000<B>1</B>00
+ DEFB %00000000
+
+; $14 - <b>Character: '=' </b>CHR$(20)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %00000000
+ DEFB %00000000
+
+; $15 - <b>Character: '+' </b>CHR$(21)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %0000<B>1</B>000
+ DEFB %0000<B>1</B>000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %0000<B>1</B>000
+ DEFB %0000<B>1</B>000
+ DEFB %00000000
+
+; $16 - <b>Character: '-' </b>CHR$(22)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+
+; $17 - <b>Character: '*' </b>CHR$(23)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %000<B>1</B>0<B>1</B>00
+ DEFB %0000<B>1</B>000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %0000<B>1</B>000
+ DEFB %000<B>1</B>0<B>1</B>00
+ DEFB %00000000
+
+; $18 - <b>Character: '/' </b>CHR$(24)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %000000<B>1</B>0
+ DEFB %00000<B>1</B>00
+ DEFB %0000<B>1</B>000
+ DEFB %000<B>1</B>0000
+ DEFB %00<B>1</B>00000
+ DEFB %00000000
+
+; $19 - <b>Character: ';' </b>CHR$(25)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %000<B>1</B>0000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %000<B>1</B>0000
+ DEFB %000<B>1</B>0000
+ DEFB %00<B>1</B>00000
+
+; $1A - <b>Character: ',' </b>CHR$(26)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %0000<B>1</B>000
+ DEFB %0000<B>1</B>000
+ DEFB %000<B>1</B>0000
+
+; $1B - <b>Character: '"' </b>CHR$(27)
+
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %00000000
+ DEFB %000<B>1</B><B>1</B>000
+ DEFB %000<B>1</B><B>1</B>000
+ DEFB %00000000
+
+; $1C - <b>Character: '0' </b>CHR$(28)
+
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>000<B>1</B><B>1</B>0
+ DEFB %0<B>1</B>00<B>1</B>0<B>1</B>0
+ DEFB %0<B>1</B>0<B>1</B>00<B>1</B>0
+ DEFB %0<B>1</B><B>1</B>000<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %00000000
+
+; $1D - <b>Character: '1' </b>CHR$(29)
+
+ DEFB %00000000
+ DEFB %000<B>1</B><B>1</B>000
+ DEFB %00<B>1</B>0<B>1</B>000
+ DEFB %0000<B>1</B>000
+ DEFB %0000<B>1</B>000
+ DEFB %0000<B>1</B>000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %00000000
+
+; $1E - <b>Character: '2' </b>CHR$(30)
+
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %000000<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %00000000
+
+; $1F - <b>Character: '3' </b>CHR$(31)
+
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0000<B>1</B><B>1</B>00
+ DEFB %000000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %00000000
+
+; $20 - <b>Character: '4' </b>CHR$(32)
+
+ DEFB %00000000
+ DEFB %0000<B>1</B>000
+ DEFB %000<B>1</B><B>1</B>000
+ DEFB %00<B>1</B>0<B>1</B>000
+ DEFB %0<B>1</B>00<B>1</B>000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %0000<B>1</B>000
+ DEFB %00000000
+
+; $21 - <b>Character: '5' </b>CHR$(33)
+
+ DEFB %00000000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %000000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %00000000
+
+; $22 - <b>Character: '6' </b>CHR$(34)
+
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %00000000
+
+; $23 - <b>Character: '7' </b>CHR$(35)
+
+ DEFB %00000000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %000000<B>1</B>0
+ DEFB %00000<B>1</B>00
+ DEFB %0000<B>1</B>000
+ DEFB %000<B>1</B>0000
+ DEFB %000<B>1</B>0000
+ DEFB %00000000
+
+; $24 - <b>Character: '8' </b>CHR$(36)
+
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %00000000
+
+; $25 - <b>Character: '9' </b>CHR$(37)
+
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %000000<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %00000000
+
+; $26 - <b>Character: 'A' </b>CHR$(38)
+
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00000000
+
+; $27 - <b>Character: 'B' </b>CHR$(39)
+
+ DEFB %00000000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %00000000
+
+; $28 - <b>Character: 'C' </b>CHR$(40)
+
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %00000000
+
+; $29 - <b>Character: 'D' </b>CHR$(41)
+
+ DEFB %00000000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B>000
+ DEFB %0<B>1</B>000<B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>000<B>1</B>00
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B>000
+ DEFB %00000000
+
+; $2A - <b>Character: 'E' </b>CHR$(42)
+
+ DEFB %00000000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %00000000
+
+; $2B - <b>Character: 'F' </b>CHR$(43)
+
+ DEFB %00000000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B>000000
+ DEFB %00000000
+
+; $2C - <b>Character: 'G' </b>CHR$(44)
+
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B>00<B>1</B><B>1</B><B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %00000000
+
+; $2D - <b>Character: 'H' </b>CHR$(45)
+
+ DEFB %00000000
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00000000
+
+; $2E - <b>Character: 'I' </b>CHR$(46)
+
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %0000<B>1</B>000
+ DEFB %0000<B>1</B>000
+ DEFB %0000<B>1</B>000
+ DEFB %0000<B>1</B>000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %00000000
+
+; $2F - <b>Character: 'J' </b>CHR$(47)
+
+ DEFB %00000000
+ DEFB %000000<B>1</B>0
+ DEFB %000000<B>1</B>0
+ DEFB %000000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %00000000
+
+; $30 - <b>Character: 'K' </b>CHR$(48)
+
+ DEFB %00000000
+ DEFB %0<B>1</B>000<B>1</B>00
+ DEFB %0<B>1</B>00<B>1</B>000
+ DEFB %0<B>1</B><B>1</B><B>1</B>0000
+ DEFB %0<B>1</B>00<B>1</B>000
+ DEFB %0<B>1</B>000<B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00000000
+
+; $31 - <b>Character: 'L' </b>CHR$(49)
+
+ DEFB %00000000
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %00000000
+
+; $32 - <b>Character: 'M' </b>CHR$(50)
+
+ DEFB %00000000
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B><B>1</B>00<B>1</B><B>1</B>0
+ DEFB %0<B>1</B>0<B>1</B><B>1</B>0<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00000000
+
+; $33 - <b>Character: 'N' </b>CHR$(51)
+
+ DEFB %00000000
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B><B>1</B>000<B>1</B>0
+ DEFB %0<B>1</B>0<B>1</B>00<B>1</B>0
+ DEFB %0<B>1</B>00<B>1</B>0<B>1</B>0
+ DEFB %0<B>1</B>000<B>1</B><B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00000000
+
+; $34 - <b>Character: 'O' </b>CHR$(52)
+
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %00000000
+
+; $35 - <b>Character: 'P' </b>CHR$(53)
+
+ DEFB %00000000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>000000
+ DEFB %0<B>1</B>000000
+ DEFB %00000000
+
+; $36 - <b>Character: 'Q' </b>CHR$(54)
+
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0<B>1</B>00<B>1</B>0
+ DEFB %0<B>1</B>00<B>1</B>0<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %00000000
+
+; $37 - <b>Character: 'R' </b>CHR$(55)
+
+ DEFB %00000000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>000<B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00000000
+
+; $38 - <b>Character: 'S' </b>CHR$(56)
+
+ DEFB %00000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %0<B>1</B>000000
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %000000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %00000000
+
+; $39 - <b>Character: 'T' </b>CHR$(57)
+
+ DEFB %00000000
+ DEFB %<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %000<B>1</B>0000
+ DEFB %000<B>1</B>0000
+ DEFB %000<B>1</B>0000
+ DEFB %000<B>1</B>0000
+ DEFB %000<B>1</B>0000
+ DEFB %00000000
+
+; $3A - <b>Character: 'U' </b>CHR$(58)
+
+ DEFB %00000000
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00<B>1</B><B>1</B><B>1</B><B>1</B>00
+ DEFB %00000000
+
+; $3B - <b>Character: 'V' </b>CHR$(59)
+
+ DEFB %00000000
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00<B>1</B>00<B>1</B>00
+ DEFB %000<B>1</B><B>1</B>000
+ DEFB %00000000
+
+; $3C - <b>Character: 'W' </b>CHR$(60)
+
+ DEFB %00000000
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %0<B>1</B>0<B>1</B><B>1</B>0<B>1</B>0
+ DEFB %00<B>1</B>00<B>1</B>00
+ DEFB %00000000
+
+; $3D - <b>Character: 'X' </b>CHR$(61)
+
+ DEFB %00000000
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00<B>1</B>00<B>1</B>00
+ DEFB %000<B>1</B><B>1</B>000
+ DEFB %000<B>1</B><B>1</B>000
+ DEFB %00<B>1</B>00<B>1</B>00
+ DEFB %0<B>1</B>0000<B>1</B>0
+ DEFB %00000000
+
+; $3E - <b>Character: 'Y' </b>CHR$(62)
+
+ DEFB %00000000
+ DEFB %<B>1</B>00000<B>1</B>0
+ DEFB %0<B>1</B>000<B>1</B>00
+ DEFB %00<B>1</B>0<B>1</B>000
+ DEFB %000<B>1</B>0000
+ DEFB %000<B>1</B>0000
+ DEFB %000<B>1</B>0000
+ DEFB %00000000
+
+; $3F - <b>Character: 'Z' </b>CHR$(63)
+
+ DEFB %00000000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %00000<B>1</B>00
+ DEFB %0000<B>1</B>000
+ DEFB %000<B>1</B>0000
+ DEFB %00<B>1</B>00000
+ DEFB %0<B>1</B><B>1</B><B>1</B><B>1</B><B>1</B><B>1</B>0
+ DEFB %00000000
+
+.END ;TASM assembler instruction.
+
+
+</PRE>
+</BODY>
+</HTML>
diff --git a/mirror/tablix.org/zx81.txt b/mirror/tablix.org/zx81.txt
@@ -0,0 +1,10556 @@
+; ===========================================================
+; An Assembly Listing of the Operating System of the ZX81 ROM
+; ===========================================================
+; -------------------------
+; Last updated: 13-DEC-2004
+; -------------------------
+;
+; Work in progress.
+; This file will cross-assemble an original version of the "Improved"
+; ZX81 ROM. The file can be modified to change the behaviour of the ROM
+; when used in emulators although there is no spare space available.
+;
+; The documentation is incomplete and if you can find a copy
+; of "The Complete Spectrum ROM Disassembly" then many routines
+; such as POINTERS and most of the mathematical routines are
+; similar and often identical.
+;
+; I've used the labels from the above book in this file and also
+; some from the more elusive Complete ZX81 ROM Disassembly
+; by the same publishers, Melbourne House.
+
+
+#define DEFB .BYTE ; TASM cross-assembler definitions
+#define DEFW .WORD
+#define EQU .EQU
+
+
+;*****************************************
+;** Part 1. RESTART ROUTINES AND TABLES **
+;*****************************************
+
+; -----------
+; THE 'START'
+; -----------
+; All Z80 chips start at location zero.
+; At start-up the Interrupt Mode is 0, ZX computers use Interrupt Mode 1.
+; Interrupts are disabled .
+
+;; START
+L0000: OUT ($FD),A ; Turn off the NMI generator if this ROM is
+ ; running in ZX81 hardware. This does nothing
+ ; if this ROM is running within an upgraded
+ ; ZX80.
+ LD BC,$7FFF ; Set BC to the top of possible RAM.
+ ; The higher unpopulated addresses are used for
+ ; video generation.
+ JP L03CB ; Jump forward to RAM-CHECK.
+
+; -------------------
+; THE 'ERROR' RESTART
+; -------------------
+; The error restart deals immediately with an error. ZX computers execute the
+; same code in runtime as when checking syntax. If the error occurred while
+; running a program then a brief report is produced. If the error occurred
+; while entering a BASIC line or in input etc., then the error marker indicates
+; the exact point at which the error lies.
+
+;; ERROR-1
+L0008: LD HL,($4016) ; fetch character address from CH_ADD.
+ LD ($4018),HL ; and set the error pointer X_PTR.
+ JR L0056 ; forward to continue at ERROR-2.
+
+; -------------------------------
+; 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.
+
+;; PRINT-A
+L0010: AND A ; test for zero - space.
+ JP NZ,L07F1 ; jump forward if not to PRINT-CH.
+
+ JP L07F5 ; jump forward to PRINT-SP.
+
+; ---
+
+ DEFB $FF ; unused location.
+
+; ---------------------------------
+; THE 'COLLECT A CHARACTER' RESTART
+; ---------------------------------
+; The character addressed by the system variable CH_ADD is fetched and if it
+; is a non-space, non-cursor character it is returned else CH_ADD is
+; incremented and the new addressed character tested until it is not a space.
+
+;; GET-CHAR
+L0018: LD HL,($4016) ; set HL to character address CH_ADD.
+ LD A,(HL) ; fetch addressed character to A.
+
+;; TEST-SP
+L001C: AND A ; test for space.
+ RET NZ ; return if not a space
+
+ NOP ; else trickle through
+ NOP ; to the next routine.
+
+; ------------------------------------
+; THE 'COLLECT NEXT CHARACTER' RESTART
+; ------------------------------------
+; The character address in incremented and the new addressed character is
+; returned if not a space, or cursor, else the process is repeated.
+
+;; NEXT-CHAR
+L0020: CALL L0049 ; routine CH-ADD+1 gets next immediate
+ ; character.
+ JR L001C ; back to TEST-SP.
+
+; ---
+
+ DEFB $FF, $FF, $FF ; unused locations.
+
+; ---------------------------------------
+; THE 'FLOATING POINT CALCULATOR' RESTART
+; ---------------------------------------
+; this restart jumps to the recursive floating-point calculator.
+; the ZX81's internal, FORTH-like, stack-based language.
+;
+; In the five remaining bytes there is, appropriately, enough room for the
+; end-calc literal - the instruction which exits the calculator.
+
+;; FP-CALC
+L0028: JP L199D ; jump immediately to the CALCULATE routine.
+
+; ---
+
+;; end-calc
+L002B: 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.
+
+
+; -----------------------------
+; THE 'MAKE BC SPACES' RESTART
+; -----------------------------
+; This restart is used eight times to create, in workspace, the number of
+; spaces passed in the BC register.
+
+;; BC-SPACES
+L0030: PUSH BC ; push number of spaces on stack.
+ LD HL,($4014) ; fetch edit line location from E_LINE.
+ PUSH HL ; save this value on stack.
+ JP L1488 ; jump forward to continue at RESERVE.
+
+; -----------------------
+; THE 'INTERRUPT' RESTART
+; -----------------------
+; The Mode 1 Interrupt routine is concerned solely with generating the central
+; television picture.
+; On the ZX81 interrupts are enabled only during the interrupt routine,
+; although the interrupt
+; This Interrupt Service Routine automatically disables interrupts at the
+; outset and the last interrupt in a cascade exits before the interrupts are
+; enabled.
+; There is no DI instruction in the ZX81 ROM.
+; An maskable interrupt is triggered when bit 6 of the Z80's Refresh register
+; changes from set to reset.
+; The Z80 will always be executing a HALT (NEWLINE) when the interrupt occurs.
+; A HALT instruction repeatedly executes NOPS but the seven lower bits
+; of the Refresh register are incremented each time as they are when any
+; simple instruction is executed. (The lower 7 bits are incremented twice for
+; a prefixed instruction)
+; This is controlled by the Sinclair Computer Logic Chip - manufactured from
+; a Ferranti Uncommitted Logic Array.
+;
+; When a Mode 1 Interrupt occurs the Program Counter, which is the address in
+; the upper echo display following the NEWLINE/HALT instruction, goes on the
+; machine stack. 193 interrupts are required to generate the last part of
+; the 56th border line and then the 192 lines of the central TV picture and,
+; although each interrupt interrupts the previous one, there are no stack
+; problems as the 'return address' is discarded each time.
+;
+; The scan line counter in C counts down from 8 to 1 within the generation of
+; each text line. For the first interrupt in a cascade the initial value of
+; C is set to 1 for the last border line.
+; Timing is of the utmost importance as the RH border, horizontal retrace
+; and LH border are mostly generated in the 58 clock cycles this routine
+; takes .
+
+;; INTERRUPT
+L0038: DEC C ; (4) decrement C - the scan line counter.
+ JP NZ,L0045 ; (10/10) JUMP forward if not zero to SCAN-LINE
+
+ POP HL ; (10) point to start of next row in display
+ ; file.
+
+ DEC B ; (4) decrement the row counter. (4)
+ RET Z ; (11/5) return when picture complete to L028B
+ ; with interrupts disabled.
+
+ SET 3,C ; (8) Load the scan line counter with eight.
+ ; Note. LD C,$08 is 7 clock cycles which
+ ; is way too fast.
+
+; ->
+
+;; WAIT-INT
+L0041: LD R,A ; (9) Load R with initial rising value $DD.
+
+ EI ; (4) Enable Interrupts. [ R is now $DE ].
+
+ JP (HL) ; (4) jump to the echo display file in upper
+ ; memory and execute characters $00 - $3F
+ ; as NOP instructions. The video hardware
+ ; is able to read these characters and,
+ ; with the I register is able to convert
+ ; the character bitmaps in this ROM into a
+ ; line of bytes. Eventually the NEWLINE/HALT
+ ; will be encountered before R reaches $FF.
+ ; It is however the transition from $FF to
+ ; $80 that triggers the next interrupt.
+ ; [ The Refresh register is now $DF ]
+
+; ---
+
+;; SCAN-LINE
+L0045: POP DE ; (10) discard the address after NEWLINE as the
+ ; same text line has to be done again
+ ; eight times.
+
+ RET Z ; (5) Harmless Nonsensical Timing.
+ ; (condition never met)
+
+ JR L0041 ; (12) back to WAIT-INT
+
+; Note. that a computer with less than 4K or RAM will have a collapsed
+; display file and the above mechanism deals with both types of display.
+;
+; With a full display, the 32 characters in the line are treated as NOPS
+; and the Refresh register rises from $E0 to $FF and, at the next instruction
+; - HALT, the interrupt occurs.
+; With a collapsed display and an initial NEWLINE/HALT, it is the NOPs
+; generated by the HALT that cause the Refresh value to rise from $E0 to $FF,
+; triggering an Interrupt on the next transition.
+; This works happily for all display lines between these extremes and the
+; generation of the 32 character, 1 pixel high, line will always take 128
+; clock cycles.
+
+; ---------------------------------
+; THE 'INCREMENT CH-ADD' SUBROUTINE
+; ---------------------------------
+; This is the subroutine that increments the character address system variable
+; and returns if it is not the cursor character. The ZX81 has an actual
+; character at the cursor position rather than a pointer system variable
+; as is the case with prior and subsequent ZX computers.
+
+;; CH-ADD+1
+L0049: LD HL,($4016) ; fetch character address to CH_ADD.
+
+;; TEMP-PTR1
+L004C: INC HL ; address next immediate location.
+
+;; TEMP-PTR2
+L004D: LD ($4016),HL ; update system variable CH_ADD.
+
+ LD A,(HL) ; fetch the character.
+ CP $7F ; compare to cursor character.
+ RET NZ ; return if not the cursor.
+
+ JR L004C ; back for next character to TEMP-PTR1.
+
+; --------------------
+; THE 'ERROR-2' BRANCH
+; --------------------
+; This is a continuation of the error restart.
+; If the error occurred in runtime then the error stack pointer will probably
+; lead to an error report being printed unless it occurred during input.
+; If the error occurred when checking syntax then the error stack pointer
+; will be an editing routine and the position of the error will be shown
+; when the lower screen is reprinted.
+
+;; ERROR-2
+L0056: POP HL ; pop the return address which points to the
+ ; DEFB, error code, after the RST 08.
+ LD L,(HL) ; load L with the error code. HL is not needed
+ ; anymore.
+
+;; ERROR-3
+L0058: LD (IY+$00),L ; place error code in system variable ERR_NR
+ LD SP,($4002) ; set the stack pointer from ERR_SP
+ CALL L0207 ; routine SLOW/FAST selects slow mode.
+ JP L14BC ; exit to address on stack via routine SET-MIN.
+
+; ---
+
+ DEFB $FF ; unused.
+
+; ------------------------------------
+; THE 'NON MASKABLE INTERRUPT' ROUTINE
+; ------------------------------------
+; Jim Westwood's technical dodge using Non-Maskable Interrupts solved the
+; flicker problem of the ZX80 and gave the ZX81 a multi-tasking SLOW mode
+; with a steady display. Note that the AF' register is reserved for this
+; function and its interaction with the display routines. When counting
+; TV lines, the NMI makes no use of the main registers.
+; The circuitry for the NMI generator is contained within the SCL (Sinclair
+; Computer Logic) chip.
+; ( It takes 32 clock cycles while incrementing towards zero ).
+
+;; NMI
+L0066: EX AF,AF' ; (4) switch in the NMI's copy of the
+ ; accumulator.
+ INC A ; (4) increment.
+ JP M,L006D ; (10/10) jump, if minus, to NMI-RET as this is
+ ; part of a test to see if the NMI
+ ; generation is working or an intermediate
+ ; value for the ascending negated blank
+ ; line counter.
+
+ JR Z,L006F ; (12) forward to NMI-CONT
+ ; when line count has incremented to zero.
+
+; Note. the synchronizing NMI when A increments from zero to one takes this
+; 7 clock cycle route making 39 clock cycles in all.
+
+;; NMI-RET
+L006D: EX AF,AF' ; (4) switch out the incremented line counter
+ ; or test result $80
+ RET ; (10) return to User application for a while.
+
+; ---
+
+; This branch is taken when the 55 (or 31) lines have been drawn.
+
+;; NMI-CONT
+L006F: EX AF,AF' ; (4) restore the main accumulator.
+
+ PUSH AF ; (11) * Save Main Registers
+ PUSH BC ; (11) **
+ PUSH DE ; (11) ***
+ PUSH HL ; (11) ****
+
+; the next set-up procedure is only really applicable when the top set of
+; blank lines have been generated.
+
+ LD HL,($400C) ; (16) fetch start of Display File from D_FILE
+ ; points to the HALT at beginning.
+ SET 7,H ; (8) point to upper 32K 'echo display file'
+
+ HALT ; (1) HALT synchronizes with NMI.
+ ; Used with special hardware connected to the
+ ; Z80 HALT and WAIT lines to take 1 clock cycle.
+
+; ----------------------------------------------------------------------------
+; the NMI has been generated - start counting. The cathode ray is at the RH
+; side of the TV.
+; First the NMI servicing, similar to CALL = 17 clock cycles.
+; Then the time taken by the NMI for zero-to-one path = 39 cycles
+; The HALT above = 01 cycles.
+; The two instructions below = 19 cycles.
+; The code at L0281 up to and including the CALL = 43 cycles.
+; The Called routine at L02B5 = 24 cycles.
+; -------------------------------------- ---
+; Total Z80 instructions = 143 cycles.
+;
+; Meanwhile in TV world,
+; Horizontal retrace = 15 cycles.
+; Left blanking border 8 character positions = 32 cycles
+; Generation of 75% scanline from the first NEWLINE = 96 cycles
+; --------------------------------------- ---
+; 143 cycles
+;
+; Since at the time the first JP (HL) is encountered to execute the echo
+; display another 8 character positions have to be put out, then the
+; Refresh register need to hold $F8. Working back and counteracting
+; the fact that every instruction increments the Refresh register then
+; the value that is loaded into R needs to be $F5. :-)
+;
+;
+ OUT ($FD),A ; (11) Stop the NMI generator.
+
+ JP (IX) ; (8) forward to L0281 (after top) or L028F
+
+; ****************
+; ** KEY TABLES **
+; ****************
+
+; -------------------------------
+; THE 'UNSHIFTED' CHARACTER CODES
+; -------------------------------
+
+;; K-UNSHIFT
+L007E: DEFB $3F ; Z
+ DEFB $3D ; X
+ DEFB $28 ; C
+ DEFB $3B ; V
+ DEFB $26 ; A
+ DEFB $38 ; S
+ DEFB $29 ; D
+ DEFB $2B ; F
+ DEFB $2C ; G
+ DEFB $36 ; Q
+ DEFB $3C ; W
+ DEFB $2A ; E
+ DEFB $37 ; R
+ DEFB $39 ; T
+ DEFB $1D ; 1
+ DEFB $1E ; 2
+ DEFB $1F ; 3
+ DEFB $20 ; 4
+ DEFB $21 ; 5
+ DEFB $1C ; 0
+ DEFB $25 ; 9
+ DEFB $24 ; 8
+ DEFB $23 ; 7
+ DEFB $22 ; 6
+ DEFB $35 ; P
+ DEFB $34 ; O
+ DEFB $2E ; I
+ DEFB $3A ; U
+ DEFB $3E ; Y
+ DEFB $76 ; NEWLINE
+ DEFB $31 ; L
+ DEFB $30 ; K
+ DEFB $2F ; J
+ DEFB $2D ; H
+ DEFB $00 ; SPACE
+ DEFB $1B ; .
+ DEFB $32 ; M
+ DEFB $33 ; N
+ DEFB $27 ; B
+
+; -----------------------------
+; THE 'SHIFTED' CHARACTER CODES
+; -----------------------------
+
+
+;; K-SHIFT
+L00A5: DEFB $0E ; :
+ DEFB $19 ; ;
+ DEFB $0F ; ?
+ DEFB $18 ; /
+ DEFB $E3 ; STOP
+ DEFB $E1 ; LPRINT
+ DEFB $E4 ; SLOW
+ DEFB $E5 ; FAST
+ DEFB $E2 ; LLIST
+ DEFB $C0 ; ""
+ DEFB $D9 ; OR
+ DEFB $E0 ; STEP
+ DEFB $DB ; <=
+ DEFB $DD ; <>
+ DEFB $75 ; EDIT
+ DEFB $DA ; AND
+ DEFB $DE ; THEN
+ DEFB $DF ; TO
+ DEFB $72 ; cursor-left
+ DEFB $77 ; RUBOUT
+ DEFB $74 ; GRAPHICS
+ DEFB $73 ; cursor-right
+ DEFB $70 ; cursor-up
+ DEFB $71 ; cursor-down
+ DEFB $0B ; "
+ DEFB $11 ; )
+ DEFB $10 ; (
+ DEFB $0D ; $
+ DEFB $DC ; >=
+ DEFB $79 ; FUNCTION
+ DEFB $14 ; =
+ DEFB $15 ; +
+ DEFB $16 ; -
+ DEFB $D8 ; **
+ DEFB $0C ; ukp
+ DEFB $1A ; ,
+ DEFB $12 ; >
+ DEFB $13 ; <
+ DEFB $17 ; *
+
+; ------------------------------
+; THE 'FUNCTION' CHARACTER CODES
+; ------------------------------
+
+
+;; K-FUNCT
+L00CC: DEFB $CD ; LN
+ DEFB $CE ; EXP
+ DEFB $C1 ; AT
+ DEFB $78 ; KL
+ DEFB $CA ; ASN
+ DEFB $CB ; ACS
+ DEFB $CC ; ATN
+ DEFB $D1 ; SGN
+ DEFB $D2 ; ABS
+ DEFB $C7 ; SIN
+ DEFB $C8 ; COS
+ DEFB $C9 ; TAN
+ DEFB $CF ; INT
+ DEFB $40 ; RND
+ DEFB $78 ; KL
+ DEFB $78 ; KL
+ DEFB $78 ; KL
+ DEFB $78 ; KL
+ DEFB $78 ; KL
+ DEFB $78 ; KL
+ DEFB $78 ; KL
+ DEFB $78 ; KL
+ DEFB $78 ; KL
+ DEFB $78 ; KL
+ DEFB $C2 ; TAB
+ DEFB $D3 ; PEEK
+ DEFB $C4 ; CODE
+ DEFB $D6 ; CHR$
+ DEFB $D5 ; STR$
+ DEFB $78 ; KL
+ DEFB $D4 ; USR
+ DEFB $C6 ; LEN
+ DEFB $C5 ; VAL
+ DEFB $D0 ; SQR
+ DEFB $78 ; KL
+ DEFB $78 ; KL
+ DEFB $42 ; PI
+ DEFB $D7 ; NOT
+ DEFB $41 ; INKEY$
+
+; -----------------------------
+; THE 'GRAPHIC' CHARACTER CODES
+; -----------------------------
+
+
+;; K-GRAPH
+L00F3: DEFB $08 ; graphic
+ DEFB $0A ; graphic
+ DEFB $09 ; graphic
+ DEFB $8A ; graphic
+ DEFB $89 ; graphic
+ DEFB $81 ; graphic
+ DEFB $82 ; graphic
+ DEFB $07 ; graphic
+ DEFB $84 ; graphic
+ DEFB $06 ; graphic
+ DEFB $01 ; graphic
+ DEFB $02 ; graphic
+ DEFB $87 ; graphic
+ DEFB $04 ; graphic
+ DEFB $05 ; graphic
+ DEFB $77 ; RUBOUT
+ DEFB $78 ; KL
+ DEFB $85 ; graphic
+ DEFB $03 ; graphic
+ DEFB $83 ; graphic
+ DEFB $8B ; graphic
+ DEFB $91 ; inverse )
+ DEFB $90 ; inverse (
+ DEFB $8D ; inverse $
+ DEFB $86 ; graphic
+ DEFB $78 ; KL
+ DEFB $92 ; inverse >
+ DEFB $95 ; inverse +
+ DEFB $96 ; inverse -
+ DEFB $88 ; graphic
+
+; ------------------
+; THE 'TOKEN' TABLES
+; ------------------
+
+
+;; TOKENS
+L0111: DEFB $0F+$80 ; '?'+$80
+ DEFB $0B,$0B+$80 ; ""
+ DEFB $26,$39+$80 ; AT
+ DEFB $39,$26,$27+$80 ; TAB
+ DEFB $0F+$80 ; '?'+$80
+ DEFB $28,$34,$29,$2A+$80 ; CODE
+ DEFB $3B,$26,$31+$80 ; VAL
+ DEFB $31,$2A,$33+$80 ; LEN
+ DEFB $38,$2E,$33+$80 ; SIN
+ DEFB $28,$34,$38+$80 ; COS
+ DEFB $39,$26,$33+$80 ; TAN
+ DEFB $26,$38,$33+$80 ; ASN
+ DEFB $26,$28,$38+$80 ; ACS
+ DEFB $26,$39,$33+$80 ; ATN
+ DEFB $31,$33+$80 ; LN
+ DEFB $2A,$3D,$35+$80 ; EXP
+ DEFB $2E,$33,$39+$80 ; INT
+ DEFB $38,$36,$37+$80 ; SQR
+ DEFB $38,$2C,$33+$80 ; SGN
+ DEFB $26,$27,$38+$80 ; ABS
+ DEFB $35,$2A,$2A,$30+$80 ; PEEK
+ DEFB $3A,$38,$37+$80 ; USR
+ DEFB $38,$39,$37,$0D+$80 ; STR$
+ DEFB $28,$2D,$37,$0D+$80 ; CHR$
+ DEFB $33,$34,$39+$80 ; NOT
+ DEFB $17,$17+$80 ; **
+ DEFB $34,$37+$80 ; OR
+ DEFB $26,$33,$29+$80 ; AND
+ DEFB $13,$14+$80 ; <=
+ DEFB $12,$14+$80 ; >=
+ DEFB $13,$12+$80 ; <>
+ DEFB $39,$2D,$2A,$33+$80 ; THEN
+ DEFB $39,$34+$80 ; TO
+ DEFB $38,$39,$2A,$35+$80 ; STEP
+ DEFB $31,$35,$37,$2E,$33,$39+$80 ; LPRINT
+ DEFB $31,$31,$2E,$38,$39+$80 ; LLIST
+ DEFB $38,$39,$34,$35+$80 ; STOP
+ DEFB $38,$31,$34,$3C+$80 ; SLOW
+ DEFB $2B,$26,$38,$39+$80 ; FAST
+ DEFB $33,$2A,$3C+$80 ; NEW
+ DEFB $38,$28,$37,$34,$31,$31+$80 ; SCROLL
+ DEFB $28,$34,$33,$39+$80 ; CONT
+ DEFB $29,$2E,$32+$80 ; DIM
+ DEFB $37,$2A,$32+$80 ; REM
+ DEFB $2B,$34,$37+$80 ; FOR
+ DEFB $2C,$34,$39,$34+$80 ; GOTO
+ DEFB $2C,$34,$38,$3A,$27+$80 ; GOSUB
+ DEFB $2E,$33,$35,$3A,$39+$80 ; INPUT
+ DEFB $31,$34,$26,$29+$80 ; LOAD
+ DEFB $31,$2E,$38,$39+$80 ; LIST
+ DEFB $31,$2A,$39+$80 ; LET
+ DEFB $35,$26,$3A,$38,$2A+$80 ; PAUSE
+ DEFB $33,$2A,$3D,$39+$80 ; NEXT
+ DEFB $35,$34,$30,$2A+$80 ; POKE
+ DEFB $35,$37,$2E,$33,$39+$80 ; PRINT
+ DEFB $35,$31,$34,$39+$80 ; PLOT
+ DEFB $37,$3A,$33+$80 ; RUN
+ DEFB $38,$26,$3B,$2A+$80 ; SAVE
+ DEFB $37,$26,$33,$29+$80 ; RAND
+ DEFB $2E,$2B+$80 ; IF
+ DEFB $28,$31,$38+$80 ; CLS
+ DEFB $3A,$33,$35,$31,$34,$39+$80 ; UNPLOT
+ DEFB $28,$31,$2A,$26,$37+$80 ; CLEAR
+ DEFB $37,$2A,$39,$3A,$37,$33+$80 ; RETURN
+ DEFB $28,$34,$35,$3E+$80 ; COPY
+ DEFB $37,$33,$29+$80 ; RND
+ DEFB $2E,$33,$30,$2A,$3E,$0D+$80 ; INKEY$
+ DEFB $35,$2E+$80 ; PI
+
+
+; ------------------------------
+; THE 'LOAD-SAVE UPDATE' ROUTINE
+; ------------------------------
+;
+;
+
+;; LOAD/SAVE
+L01FC: INC HL ;
+ EX DE,HL ;
+ LD HL,($4014) ; system variable edit line E_LINE.
+ SCF ; set carry flag
+ SBC HL,DE ;
+ EX DE,HL ;
+ RET NC ; return if more bytes to load/save.
+
+ POP HL ; else drop return address
+
+; ----------------------
+; THE 'DISPLAY' ROUTINES
+; ----------------------
+;
+;
+
+;; SLOW/FAST
+L0207: LD HL,$403B ; Address the system variable CDFLAG.
+ LD A,(HL) ; Load value to the accumulator.
+ RLA ; rotate bit 6 to position 7.
+ XOR (HL) ; exclusive or with original bit 7.
+ RLA ; rotate result out to carry.
+ RET NC ; return if both bits were the same.
+
+; Now test if this really is a ZX81 or a ZX80 running the upgraded ROM.
+; The standard ZX80 did not have an NMI generator.
+
+ LD A,$7F ; Load accumulator with %011111111
+ EX AF,AF' ; save in AF'
+
+ LD B,$11 ; A counter within which an NMI should occur
+ ; if this is a ZX81.
+ OUT ($FE),A ; start the NMI generator.
+
+; Note that if this is a ZX81 then the NMI will increment AF'.
+
+;; LOOP-11
+L0216: DJNZ L0216 ; self loop to give the NMI a chance to kick in.
+ ; = 16*13 clock cycles + 8 = 216 clock cycles.
+
+ OUT ($FD),A ; Turn off the NMI generator.
+ EX AF,AF' ; bring back the AF' value.
+ RLA ; test bit 7.
+ JR NC,L0226 ; forward, if bit 7 is still reset, to NO-SLOW.
+
+; If the AF' was incremented then the NMI generator works and SLOW mode can
+; be set.
+
+ SET 7,(HL) ; Indicate SLOW mode - Compute and Display.
+
+ PUSH AF ; * Save Main Registers
+ PUSH BC ; **
+ PUSH DE ; ***
+ PUSH HL ; ****
+
+ JR L0229 ; skip forward - to DISPLAY-1.
+
+; ---
+
+;; NO-SLOW
+L0226: RES 6,(HL) ; reset bit 6 of CDFLAG.
+ RET ; return.
+
+; -----------------------
+; THE 'MAIN DISPLAY' LOOP
+; -----------------------
+; This routine is executed once for every frame displayed.
+
+;; DISPLAY-1
+L0229: LD HL,($4034) ; fetch two-byte system variable FRAMES.
+ DEC HL ; decrement frames counter.
+
+;; DISPLAY-P
+L022D: LD A,$7F ; prepare a mask
+ AND H ; pick up bits 6-0 of H.
+ OR L ; and any bits of L.
+ LD A,H ; reload A with all bits of H for PAUSE test.
+
+; Note both branches must take the same time.
+
+ JR NZ,L0237 ; (12/7) forward if bits 14-0 are not zero
+ ; to ANOTHER
+
+ RLA ; (4) test bit 15 of FRAMES.
+ JR L0239 ; (12) forward with result to OVER-NC
+
+; ---
+
+;; ANOTHER
+L0237: LD B,(HL) ; (7) Note. Harmless Nonsensical Timing weight.
+ SCF ; (4) Set Carry Flag.
+
+; Note. the branch to here takes either (12)(7)(4) cyles or (7)(4)(12) cycles.
+
+;; OVER-NC
+L0239: LD H,A ; (4) set H to zero
+ LD ($4034),HL ; (16) update system variable FRAMES
+ RET NC ; (11/5) return if FRAMES is in use by PAUSE
+ ; command.
+
+;; DISPLAY-2
+L023E: CALL L02BB ; routine KEYBOARD gets the key row in H and
+ ; the column in L. Reading the ports also starts
+ ; the TV frame synchronization pulse. (VSYNC)
+
+ LD BC,($4025) ; fetch the last key values read from LAST_K
+ LD ($4025),HL ; update LAST_K with new values.
+
+ LD A,B ; load A with previous column - will be $FF if
+ ; there was no key.
+ ADD A,$02 ; adding two will set carry if no previous key.
+
+ SBC HL,BC ; subtract with the carry the two key values.
+
+; If the same key value has been returned twice then HL will be zero.
+
+ LD A,($4027) ; fetch system variable DEBOUNCE
+ OR H ; and OR with both bytes of the difference
+ OR L ; setting the zero flag for the upcoming branch.
+
+ LD E,B ; transfer the column value to E
+ LD B,$0B ; and load B with eleven
+
+ LD HL,$403B ; address system variable CDFLAG
+ RES 0,(HL) ; reset the rightmost bit of CDFLAG
+ JR NZ,L0264 ; skip forward if debounce/diff >0 to NO-KEY
+
+ BIT 7,(HL) ; test compute and display bit of CDFLAG
+ SET 0,(HL) ; set the rightmost bit of CDFLAG.
+ RET Z ; return if bit 7 indicated fast mode.
+
+ DEC B ; (4) decrement the counter.
+ NOP ; (4) Timing - 4 clock cycles. ??
+ SCF ; (4) Set Carry Flag
+
+;; NO-KEY
+L0264: LD HL,$4027 ; sv DEBOUNCE
+ CCF ; Complement Carry Flag
+ RL B ; rotate left B picking up carry
+ ; C<-76543210<-C
+
+;; LOOP-B
+L026A: DJNZ L026A ; self-loop while B>0 to LOOP-B
+
+ LD B,(HL) ; fetch value of DEBOUNCE to B
+ LD A,E ; transfer column value
+ CP $FE ;
+ SBC A,A ;
+ LD B,$1F ;
+ OR (HL) ;
+ AND B ;
+ RRA ;
+ LD (HL),A ;
+
+ OUT ($FF),A ; end the TV frame synchronization pulse.
+
+ LD HL,($400C) ; (12) set HL to the Display File from D_FILE
+ SET 7,H ; (8) set bit 15 to address the echo display.
+
+ CALL L0292 ; (17) routine DISPLAY-3 displays the top set
+ ; of blank lines.
+
+; ---------------------
+; THE 'VIDEO-1' ROUTINE
+; ---------------------
+
+;; R-IX-1
+L0281: LD A,R ; (9) Harmless Nonsensical Timing or something
+ ; very clever?
+ LD BC,$1901 ; (10) 25 lines, 1 scanline in first.
+ LD A,$F5 ; (7) This value will be loaded into R and
+ ; ensures that the cycle starts at the right
+ ; part of the display - after 32nd character
+ ; position.
+
+ CALL L02B5 ; (17) routine DISPLAY-5 completes the current
+ ; blank line and then generates the display of
+ ; the live picture using INT interrupts
+ ; The final interrupt returns to the next
+ ; address.
+
+L028B: DEC HL ; point HL to the last NEWLINE/HALT.
+
+ CALL L0292 ; routine DISPLAY-3 displays the bottom set of
+ ; blank lines.
+
+; ---
+
+;; R-IX-2
+L028F: JP L0229 ; JUMP back to DISPLAY-1
+
+; ---------------------------------
+; THE 'DISPLAY BLANK LINES' ROUTINE
+; ---------------------------------
+; This subroutine is called twice (see above) to generate first the blank
+; lines at the top of the television display and then the blank lines at the
+; bottom of the display.
+
+;; DISPLAY-3
+L0292: POP IX ; pop the return address to IX register.
+ ; will be either L0281 or L028F - see above.
+
+ LD C,(IY+$28) ; load C with value of system constant MARGIN.
+ BIT 7,(IY+$3B) ; test CDFLAG for compute and display.
+ JR Z,L02A9 ; forward, with FAST mode, to DISPLAY-4
+
+ LD A,C ; move MARGIN to A - 31d or 55d.
+ NEG ; Negate
+ INC A ;
+ EX AF,AF' ; place negative count of blank lines in A'
+
+ OUT ($FE),A ; enable the NMI generator.
+
+ POP HL ; ****
+ POP DE ; ***
+ POP BC ; **
+ POP AF ; * Restore Main Registers
+
+ RET ; return - end of interrupt. Return is to
+ ; user's program - BASIC or machine code.
+ ; which will be interrupted by every NMI.
+
+; ------------------------
+; THE 'FAST MODE' ROUTINES
+; ------------------------
+
+;; DISPLAY-4
+L02A9: LD A,$FC ; (7) load A with first R delay value
+ LD B,$01 ; (7) one row only.
+
+ CALL L02B5 ; (17) routine DISPLAY-5
+
+ DEC HL ; (6) point back to the HALT.
+ EX (SP),HL ; (19) Harmless Nonsensical Timing if paired.
+ EX (SP),HL ; (19) Harmless Nonsensical Timing.
+ JP (IX) ; (8) to L0281 or L028F
+
+; --------------------------
+; THE 'DISPLAY-5' SUBROUTINE
+; --------------------------
+; This subroutine is called from SLOW mode and FAST mode to generate the
+; central TV picture. With SLOW mode the R register is incremented, with
+; each instruction, to $F7 by the time it completes. With fast mode, the
+; final R value will be $FF and an interrupt will occur as soon as the
+; Program Counter reaches the HALT. (24 clock cycles)
+
+;; DISPLAY-5
+L02B5: LD R,A ; (9) Load R from A. R = slow: $F5 fast: $FC
+ LD A,$DD ; (7) load future R value. $F6 $FD
+
+ EI ; (4) Enable Interrupts $F7 $FE
+
+ JP (HL) ; (4) jump to the echo display. $F8 $FF
+
+; ----------------------------------
+; THE 'KEYBOARD SCANNING' SUBROUTINE
+; ----------------------------------
+; The keyboard is read during the vertical sync interval while no video is
+; being displayed. Reading a port with address bit 0 low i.e. $FE starts the
+; vertical sync pulse.
+
+;; KEYBOARD
+L02BB: LD HL,$FFFF ; (16) prepare a buffer to take key.
+ LD BC,$FEFE ; (20) set BC to port $FEFE. The B register,
+ ; with its single reset bit also acts as
+ ; an 8-counter.
+ IN A,(C) ; (11) read the port - all 16 bits are put on
+ ; the address bus. Start VSYNC pulse.
+ OR $01 ; (7) set the rightmost bit so as to ignore
+ ; the SHIFT key.
+
+;; EACH-LINE
+L02C5: OR $E0 ; [7] OR %11100000
+ LD D,A ; [4] transfer to D.
+ CPL ; [4] complement - only bits 4-0 meaningful now.
+ CP $01 ; [7] sets carry if A is zero.
+ SBC A,A ; [4] $FF if $00 else zero.
+ OR B ; [7] $FF or port FE,FD,FB....
+ AND L ; [4] unless more than one key, L will still be
+ ; $FF. if more than one key is pressed then A is
+ ; now invalid.
+ LD L,A ; [4] transfer to L.
+
+; now consider the column identifier.
+
+ LD A,H ; [4] will be $FF if no previous keys.
+ AND D ; [4] 111xxxxx
+ LD H,A ; [4] transfer A to H
+
+; since only one key may be pressed, H will, if valid, be one of
+; 11111110, 11111101, 11111011, 11110111, 11101111
+; reading from the outer column, say Q, to the inner column, say T.
+
+ RLC B ; [8] rotate the 8-counter/port address.
+ ; sets carry if more to do.
+ IN A,(C) ; [10] read another half-row.
+ ; all five bits this time.
+
+ JR C,L02C5 ; [12](7) loop back, until done, to EACH-LINE
+
+; The last row read is SHIFT,Z,X,C,V for the second time.
+
+ RRA ; (4) test the shift key - carry will be reset
+ ; if the key is pressed.
+ RL H ; (8) rotate left H picking up the carry giving
+ ; column values -
+ ; $FD, $FB, $F7, $EF, $DF.
+ ; or $FC, $FA, $F6, $EE, $DE if shifted.
+
+; We now have H identifying the column and L identifying the row in the
+; keyboard matrix.
+
+; This is a good time to test if this is an American or British machine.
+; The US machine has an extra diode that causes bit 6 of a byte read from
+; a port to be reset.
+
+ RLA ; (4) compensate for the shift test.
+ RLA ; (4) rotate bit 7 out.
+ RLA ; (4) test bit 6.
+
+ SBC A,A ; (4) $FF or $00 {USA}
+ AND $18 ; (7) $18 or $00
+ ADD A,$1F ; (7) $37 or $1F
+
+; result is either 31 (USA) or 55 (UK) blank lines above and below the TV
+; picture.
+
+ LD ($4028),A ; (13) update system variable MARGIN
+
+ RET ; (10) return
+
+; ------------------------------
+; THE 'SET FAST MODE' SUBROUTINE
+; ------------------------------
+;
+;
+
+;; SET-FAST
+L02E7: BIT 7,(IY+$3B) ; sv CDFLAG
+ RET Z ;
+
+ HALT ; Wait for Interrupt
+ OUT ($FD),A ;
+ RES 7,(IY+$3B) ; sv CDFLAG
+ RET ; return.
+
+
+; --------------
+; THE 'REPORT-F'
+; --------------
+
+;; REPORT-F
+L02F4: RST 08H ; ERROR-1
+ DEFB $0E ; Error Report: No Program Name supplied.
+
+; --------------------------
+; THE 'SAVE COMMAND' ROUTINE
+; --------------------------
+;
+;
+
+;; SAVE
+L02F6: CALL L03A8 ; routine NAME
+ JR C,L02F4 ; back with null name to REPORT-F above.
+
+ EX DE,HL ;
+ LD DE,$12CB ; five seconds timing value
+
+;; HEADER
+L02FF: CALL L0F46 ; routine BREAK-1
+ JR NC,L0332 ; to BREAK-2
+
+;; DELAY-1
+L0304: DJNZ L0304 ; to DELAY-1
+
+ DEC DE ;
+ LD A,D ;
+ OR E ;
+ JR NZ,L02FF ; back for delay to HEADER
+
+;; OUT-NAME
+L030B: CALL L031E ; routine OUT-BYTE
+ BIT 7,(HL) ; test for inverted bit.
+ INC HL ; address next character of name.
+ JR Z,L030B ; back if not inverted to OUT-NAME
+
+; now start saving the system variables onwards.
+
+ LD HL,$4009 ; set start of area to VERSN thereby
+ ; preserving RAMTOP etc.
+
+;; OUT-PROG
+L0316: CALL L031E ; routine OUT-BYTE
+
+ CALL L01FC ; routine LOAD/SAVE >>
+ JR L0316 ; loop back to OUT-PROG
+
+; -------------------------
+; THE 'OUT-BYTE' SUBROUTINE
+; -------------------------
+; This subroutine outputs a byte a bit at a time to a domestic tape recorder.
+
+;; OUT-BYTE
+L031E: LD E,(HL) ; fetch byte to be saved.
+ SCF ; set carry flag - as a marker.
+
+;; EACH-BIT
+L0320: RL E ; C < 76543210 < C
+ RET Z ; return when the marker bit has passed
+ ; right through. >>
+
+ SBC A,A ; $FF if set bit or $00 with no carry.
+ AND $05 ; $05 $00
+ ADD A,$04 ; $09 $04
+ LD C,A ; transfer timer to C. a set bit has a longer
+ ; pulse than a reset bit.
+
+;; PULSES
+L0329: OUT ($FF),A ; pulse to cassette.
+ LD B,$23 ; set timing constant
+
+;; DELAY-2
+L032D: DJNZ L032D ; self-loop to DELAY-2
+
+ CALL L0F46 ; routine BREAK-1 test for BREAK key.
+
+;; BREAK-2
+L0332: JR NC,L03A6 ; forward with break to REPORT-D
+
+ LD B,$1E ; set timing value.
+
+;; DELAY-3
+L0336: DJNZ L0336 ; self-loop to DELAY-3
+
+ DEC C ; decrement counter
+ JR NZ,L0329 ; loop back to PULSES
+
+;; DELAY-4
+L033B: AND A ; clear carry for next bit test.
+ DJNZ L033B ; self loop to DELAY-4 (B is zero - 256)
+
+ JR L0320 ; loop back to EACH-BIT
+
+; --------------------------
+; THE 'LOAD COMMAND' ROUTINE
+; --------------------------
+;
+;
+
+;; LOAD
+L0340: CALL L03A8 ; routine NAME
+
+; DE points to start of name in RAM.
+
+ RL D ; pick up carry
+ RRC D ; carry now in bit 7.
+
+;; NEXT-PROG
+L0347: CALL L034C ; routine IN-BYTE
+ JR L0347 ; loop to NEXT-PROG
+
+; ------------------------
+; THE 'IN-BYTE' SUBROUTINE
+; ------------------------
+
+;; IN-BYTE
+L034C: LD C,$01 ; prepare an eight counter 00000001.
+
+;; NEXT-BIT
+L034E: LD B,$00 ; set counter to 256
+
+;; BREAK-3
+L0350: LD A,$7F ; read the keyboard row
+ IN A,($FE) ; with the SPACE key.
+
+ OUT ($FF),A ; output signal to screen.
+
+ RRA ; test for SPACE pressed.
+ JR NC,L03A2 ; forward if so to BREAK-4
+
+ RLA ; reverse above rotation
+ RLA ; test tape bit.
+ JR C,L0385 ; forward if set to GET-BIT
+
+ DJNZ L0350 ; loop back to BREAK-3
+
+ POP AF ; drop the return address.
+ CP D ; ugh.
+
+;; RESTART
+L0361: JP NC,L03E5 ; jump forward to INITIAL if D is zero
+ ; to reset the system
+ ; if the tape signal has timed out for example
+ ; if the tape is stopped. Not just a simple
+ ; report as some system variables will have
+ ; been overwritten.
+
+ LD H,D ; else transfer the start of name
+ LD L,E ; to the HL register
+
+;; IN-NAME
+L0366: CALL L034C ; routine IN-BYTE is sort of recursion for name
+ ; part. received byte in C.
+ BIT 7,D ; is name the null string ?
+ LD A,C ; transfer byte to A.
+ JR NZ,L0371 ; forward with null string to MATCHING
+
+ CP (HL) ; else compare with string in memory.
+ JR NZ,L0347 ; back with mis-match to NEXT-PROG
+ ; (seemingly out of subroutine but return
+ ; address has been dropped).
+
+
+;; MATCHING
+L0371: INC HL ; address next character of name
+ RLA ; test for inverted bit.
+ JR NC,L0366 ; back if not to IN-NAME
+
+; the name has been matched in full.
+; proceed to load the data but first increment the high byte of E_LINE, which
+; is one of the system variables to be loaded in. Since the low byte is loaded
+; before the high byte, it is possible that, at the in-between stage, a false
+; value could cause the load to end prematurely - see LOAD/SAVE check.
+
+ INC (IY+$15) ; increment system variable E_LINE_hi.
+ LD HL,$4009 ; start loading at system variable VERSN.
+
+;; IN-PROG
+L037B: LD D,B ; set D to zero as indicator.
+ CALL L034C ; routine IN-BYTE loads a byte
+ LD (HL),C ; insert assembled byte in memory.
+ CALL L01FC ; routine LOAD/SAVE >>
+ JR L037B ; loop back to IN-PROG
+
+; ---
+
+; this branch assembles a full byte before exiting normally
+; from the IN-BYTE subroutine.
+
+;; GET-BIT
+L0385: PUSH DE ; save the
+ LD E,$94 ; timing value.
+
+;; TRAILER
+L0388: LD B,$1A ; counter to twenty six.
+
+;; COUNTER
+L038A: DEC E ; decrement the measuring timer.
+ IN A,($FE) ; read the
+ RLA ;
+ BIT 7,E ;
+ LD A,E ;
+ JR C,L0388 ; loop back with carry to TRAILER
+
+ DJNZ L038A ; to COUNTER
+
+ POP DE ;
+ JR NZ,L039C ; to BIT-DONE
+
+ CP $56 ;
+ JR NC,L034E ; to NEXT-BIT
+
+;; BIT-DONE
+L039C: CCF ; complement carry flag
+ RL C ;
+ JR NC,L034E ; to NEXT-BIT
+
+ RET ; return with full byte.
+
+; ---
+
+; if break is pressed while loading data then perform a reset.
+; if break pressed while waiting for program on tape then OK to break.
+
+;; BREAK-4
+L03A2: LD A,D ; transfer indicator to A.
+ AND A ; test for zero.
+ JR Z,L0361 ; back if so to RESTART
+
+
+;; REPORT-D
+L03A6: RST 08H ; ERROR-1
+ DEFB $0C ; Error Report: BREAK - CONT repeats
+
+; -----------------------------
+; THE 'PROGRAM NAME' SUBROUTINE
+; -----------------------------
+;
+;
+
+;; NAME
+L03A8: CALL L0F55 ; routine SCANNING
+ LD A,($4001) ; sv FLAGS
+ ADD A,A ;
+ JP M,L0D9A ; to REPORT-C
+
+ POP HL ;
+ RET NC ;
+
+ PUSH HL ;
+ CALL L02E7 ; routine SET-FAST
+ CALL L13F8 ; routine STK-FETCH
+ LD H,D ;
+ LD L,E ;
+ DEC C ;
+ RET M ;
+
+ ADD HL,BC ;
+ SET 7,(HL) ;
+ RET ;
+
+; -------------------------
+; THE 'NEW' COMMAND ROUTINE
+; -------------------------
+;
+;
+
+;; NEW
+L03C3: CALL L02E7 ; routine SET-FAST
+ LD BC,($4004) ; fetch value of system variable RAMTOP
+ DEC BC ; point to last system byte.
+
+; -----------------------
+; THE 'RAM CHECK' ROUTINE
+; -----------------------
+;
+;
+
+;; RAM-CHECK
+L03CB: LD H,B ;
+ LD L,C ;
+ LD A,$3F ;
+
+;; RAM-FILL
+L03CF: LD (HL),$02 ;
+ DEC HL ;
+ CP H ;
+ JR NZ,L03CF ; to RAM-FILL
+
+;; RAM-READ
+L03D5: AND A ;
+ SBC HL,BC ;
+ ADD HL,BC ;
+ INC HL ;
+ JR NC,L03E2 ; to SET-TOP
+
+ DEC (HL) ;
+ JR Z,L03E2 ; to SET-TOP
+
+ DEC (HL) ;
+ JR Z,L03D5 ; to RAM-READ
+
+;; SET-TOP
+L03E2: LD ($4004),HL ; set system variable RAMTOP to first byte
+ ; above the BASIC system area.
+
+; ----------------------------
+; THE 'INITIALIZATION' ROUTINE
+; ----------------------------
+;
+;
+
+;; INITIAL
+L03E5: LD HL,($4004) ; fetch system variable RAMTOP.
+ DEC HL ; point to last system byte.
+ LD (HL),$3E ; make GO SUB end-marker $3E - too high for
+ ; high order byte of line number.
+ ; (was $3F on ZX80)
+ DEC HL ; point to unimportant low-order byte.
+ LD SP,HL ; and initialize the stack-pointer to this
+ ; location.
+ DEC HL ; point to first location on the machine stack
+ DEC HL ; which will be filled by next CALL/PUSH.
+ LD ($4002),HL ; set the error stack pointer ERR_SP to
+ ; the base of the now empty machine stack.
+
+; Now set the I register so that the video hardware knows where to find the
+; character set. This ROM only uses the character set when printing to
+; the ZX Printer. The TV picture is formed by the external video hardware.
+; Consider also, that this 8K ROM can be retro-fitted to the ZX80 instead of
+; its original 4K ROM so the video hardware could be on the ZX80.
+
+ LD A,$1E ; address for this ROM is $1E00.
+ LD I,A ; set I register from A.
+ IM 1 ; select Z80 Interrupt Mode 1.
+
+ LD IY,$4000 ; set IY to the start of RAM so that the
+ ; system variables can be indexed.
+ LD (IY+$3B),$40 ; set CDFLAG 0100 0000. Bit 6 indicates
+ ; Compute nad Display required.
+
+ LD HL,$407D ; The first location after System Variables -
+ ; 16509 decimal.
+ LD ($400C),HL ; set system variable D_FILE to this value.
+ LD B,$19 ; prepare minimal screen of 24 NEWLINEs
+ ; following an initial NEWLINE.
+
+;; LINE
+L0408: LD (HL),$76 ; insert NEWLINE (HALT instruction)
+ INC HL ; point to next location.
+ DJNZ L0408 ; loop back for all twenty five to LINE
+
+ LD ($4010),HL ; set system variable VARS to next location
+
+ CALL L149A ; routine CLEAR sets $80 end-marker and the
+ ; dynamic memory pointers E_LINE, STKBOT and
+ ; STKEND.
+
+;; N/L-ONLY
+L0413: CALL L14AD ; routine CURSOR-IN inserts the cursor and
+ ; end-marker in the Edit Line also setting
+ ; size of lower display to two lines.
+
+ CALL L0207 ; routine SLOW/FAST selects COMPUTE and DISPLAY
+
+; ---------------------------
+; THE 'BASIC LISTING' SECTION
+; ---------------------------
+;
+;
+
+;; UPPER
+L0419: CALL L0A2A ; routine CLS
+ LD HL,($400A) ; sv E_PPC_lo
+ LD DE,($4023) ; sv S_TOP_lo
+ AND A ;
+ SBC HL,DE ;
+ EX DE,HL ;
+ JR NC,L042D ; to ADDR-TOP
+
+ ADD HL,DE ;
+ LD ($4023),HL ; sv S_TOP_lo
+
+;; ADDR-TOP
+L042D: CALL L09D8 ; routine LINE-ADDR
+ JR Z,L0433 ; to LIST-TOP
+
+ EX DE,HL ;
+
+;; LIST-TOP
+L0433: CALL L073E ; routine LIST-PROG
+ DEC (IY+$1E) ; sv BERG
+ JR NZ,L0472 ; to LOWER
+
+ LD HL,($400A) ; sv E_PPC_lo
+ CALL L09D8 ; routine LINE-ADDR
+ LD HL,($4016) ; sv CH_ADD_lo
+ SCF ; Set Carry Flag
+ SBC HL,DE ;
+ LD HL,$4023 ; sv S_TOP_lo
+ JR NC,L0457 ; to INC-LINE
+
+ EX DE,HL ;
+ LD A,(HL) ;
+ INC HL ;
+ LDI ;
+ LD (DE),A ;
+ JR L0419 ; to UPPER
+
+; ---
+
+;; DOWN-KEY
+L0454: LD HL,$400A ; sv E_PPC_lo
+
+;; INC-LINE
+L0457: LD E,(HL) ;
+ INC HL ;
+ LD D,(HL) ;
+ PUSH HL ;
+ EX DE,HL ;
+ INC HL ;
+ CALL L09D8 ; routine LINE-ADDR
+ CALL L05BB ; routine LINE-NO
+ POP HL ;
+
+;; KEY-INPUT
+L0464: BIT 5,(IY+$2D) ; sv FLAGX
+ JR NZ,L0472 ; forward to LOWER
+
+ LD (HL),D ;
+ DEC HL ;
+ LD (HL),E ;
+ JR L0419 ; to UPPER
+
+; ----------------------------
+; THE 'EDIT LINE COPY' SECTION
+; ----------------------------
+; This routine sets the edit line to just the cursor when
+; 1) There is not enough memory to edit a BASIC line.
+; 2) The edit key is used during input.
+; The entry point LOWER
+
+
+;; EDIT-INP
+L046F: CALL L14AD ; routine CURSOR-IN sets cursor only edit line.
+
+; ->
+
+;; LOWER
+L0472: LD HL,($4014) ; fetch edit line start from E_LINE.
+
+;; EACH-CHAR
+L0475: LD A,(HL) ; fetch a character from edit line.
+ CP $7E ; compare to the number marker.
+ JR NZ,L0482 ; forward if not to END-LINE
+
+ LD BC,$0006 ; else six invisible bytes to be removed.
+ CALL L0A60 ; routine RECLAIM-2
+ JR L0475 ; back to EACH-CHAR
+
+; ---
+
+;; END-LINE
+L0482: CP $76 ;
+ INC HL ;
+ JR NZ,L0475 ; to EACH-CHAR
+
+;; EDIT-LINE
+L0487: CALL L0537 ; routine CURSOR sets cursor K or L.
+
+;; EDIT-ROOM
+L048A: CALL L0A1F ; routine LINE-ENDS
+ LD HL,($4014) ; sv E_LINE_lo
+ LD (IY+$00),$FF ; sv ERR_NR
+ CALL L0766 ; routine COPY-LINE
+ BIT 7,(IY+$00) ; sv ERR_NR
+ JR NZ,L04C1 ; to DISPLAY-6
+
+ LD A,($4022) ; sv DF_SZ
+ CP $18 ;
+ JR NC,L04C1 ; to DISPLAY-6
+
+ INC A ;
+ LD ($4022),A ; sv DF_SZ
+ LD B,A ;
+ LD C,$01 ;
+ CALL L0918 ; routine LOC-ADDR
+ LD D,H ;
+ LD E,L ;
+ LD A,(HL) ;
+
+;; FREE-LINE
+L04B1: DEC HL ;
+ CP (HL) ;
+ JR NZ,L04B1 ; to FREE-LINE
+
+ INC HL ;
+ EX DE,HL ;
+ LD A,($4005) ; sv RAMTOP_hi
+ CP $4D ;
+ CALL C,L0A5D ; routine RECLAIM-1
+ JR L048A ; to EDIT-ROOM
+
+; --------------------------
+; THE 'WAIT FOR KEY' SECTION
+; --------------------------
+;
+;
+
+;; DISPLAY-6
+L04C1: LD HL,$0000 ;
+ LD ($4018),HL ; sv X_PTR_lo
+
+ LD HL,$403B ; system variable CDFLAG
+ BIT 7,(HL) ;
+
+ CALL Z,L0229 ; routine DISPLAY-1
+
+;; SLOW-DISP
+L04CF: BIT 0,(HL) ;
+ JR Z,L04CF ; to SLOW-DISP
+
+ LD BC,($4025) ; sv LAST_K
+ CALL L0F4B ; routine DEBOUNCE
+ CALL L07BD ; routine DECODE
+
+ JR NC,L0472 ; back to LOWER
+
+; -------------------------------
+; THE 'KEYBOARD DECODING' SECTION
+; -------------------------------
+; The decoded key value is in E and HL points to the position in the
+; key table. D contains zero.
+
+;; K-DECODE
+L04DF: LD A,($4006) ; Fetch value of system variable MODE
+ DEC A ; test the three values together
+
+ JP M,L0508 ; forward, if was zero, to FETCH-2
+
+ JR NZ,L04F7 ; forward, if was 2, to FETCH-1
+
+; The original value was one and is now zero.
+
+ LD ($4006),A ; update the system variable MODE
+
+ DEC E ; reduce E to range $00 - $7F
+ LD A,E ; place in A
+ SUB $27 ; subtract 39 setting carry if range 00 - 38
+ JR C,L04F2 ; forward, if so, to FUNC-BASE
+
+ LD E,A ; else set E to reduced value
+
+;; FUNC-BASE
+L04F2: LD HL,L00CC ; address of K-FUNCT table for function keys.
+ JR L0505 ; forward to TABLE-ADD
+
+; ---
+
+;; FETCH-1
+L04F7: LD A,(HL) ;
+ CP $76 ;
+ JR Z,L052B ; to K/L-KEY
+
+ CP $40 ;
+ SET 7,A ;
+ JR C,L051B ; to ENTER
+
+ LD HL,$00C7 ; (expr reqd)
+
+;; TABLE-ADD
+L0505: ADD HL,DE ;
+ JR L0515 ; to FETCH-3
+
+; ---
+
+;; FETCH-2
+L0508: LD A,(HL) ;
+ BIT 2,(IY+$01) ; sv FLAGS - K or L mode ?
+ JR NZ,L0516 ; to TEST-CURS
+
+ ADD A,$C0 ;
+ CP $E6 ;
+ JR NC,L0516 ; to TEST-CURS
+
+;; FETCH-3
+L0515: LD A,(HL) ;
+
+;; TEST-CURS
+L0516: CP $F0 ;
+ JP PE,L052D ; to KEY-SORT
+
+;; ENTER
+L051B: LD E,A ;
+ CALL L0537 ; routine CURSOR
+
+ LD A,E ;
+ CALL L0526 ; routine ADD-CHAR
+
+;; BACK-NEXT
+L0523: JP L0472 ; back to LOWER
+
+; ------------------------------
+; THE 'ADD CHARACTER' SUBROUTINE
+; ------------------------------
+;
+;
+
+;; ADD-CHAR
+L0526: CALL L099B ; routine ONE-SPACE
+ LD (DE),A ;
+ RET ;
+
+; -------------------------
+; THE 'CURSOR KEYS' ROUTINE
+; -------------------------
+;
+;
+
+;; K/L-KEY
+L052B: LD A,$78 ;
+
+;; KEY-SORT
+L052D: LD E,A ;
+ LD HL,$0482 ; base address of ED-KEYS (exp reqd)
+ ADD HL,DE ;
+ ADD HL,DE ;
+ LD C,(HL) ;
+ INC HL ;
+ LD B,(HL) ;
+ PUSH BC ;
+
+;; CURSOR
+L0537: LD HL,($4014) ; sv E_LINE_lo
+ BIT 5,(IY+$2D) ; sv FLAGX
+ JR NZ,L0556 ; to L-MODE
+
+;; K-MODE
+L0540: RES 2,(IY+$01) ; sv FLAGS - Signal use K mode
+
+;; TEST-CHAR
+L0544: LD A,(HL) ;
+ CP $7F ;
+ RET Z ; return
+
+ INC HL ;
+ CALL L07B4 ; routine NUMBER
+ JR Z,L0544 ; to TEST-CHAR
+
+ CP $26 ;
+ JR C,L0544 ; to TEST-CHAR
+
+ CP $DE ;
+ JR Z,L0540 ; to K-MODE
+
+;; L-MODE
+L0556: SET 2,(IY+$01) ; sv FLAGS - Signal use L mode
+ JR L0544 ; to TEST-CHAR
+
+; --------------------------
+; THE 'CLEAR-ONE' SUBROUTINE
+; --------------------------
+;
+;
+
+;; CLEAR-ONE
+L055C: LD BC,$0001 ;
+ JP L0A60 ; to RECLAIM-2
+
+
+
+; ------------------------
+; THE 'EDITING KEYS' TABLE
+; ------------------------
+;
+;
+
+;; ED-KEYS
+L0562: DEFW L059F ; Address: $059F; Address: UP-KEY
+ DEFW L0454 ; Address: $0454; Address: DOWN-KEY
+ DEFW L0576 ; Address: $0576; Address: LEFT-KEY
+ DEFW L057F ; Address: $057F; Address: RIGHT-KEY
+ DEFW L05AF ; Address: $05AF; Address: FUNCTION
+ DEFW L05C4 ; Address: $05C4; Address: EDIT-KEY
+ DEFW L060C ; Address: $060C; Address: N/L-KEY
+ DEFW L058B ; Address: $058B; Address: RUBOUT
+ DEFW L05AF ; Address: $05AF; Address: FUNCTION
+ DEFW L05AF ; Address: $05AF; Address: FUNCTION
+
+
+; -------------------------
+; THE 'CURSOR LEFT' ROUTINE
+; -------------------------
+;
+;
+
+;; LEFT-KEY
+L0576: CALL L0593 ; routine LEFT-EDGE
+ LD A,(HL) ;
+ LD (HL),$7F ;
+ INC HL ;
+ JR L0588 ; to GET-CODE
+
+; --------------------------
+; THE 'CURSOR RIGHT' ROUTINE
+; --------------------------
+;
+;
+
+;; RIGHT-KEY
+L057F: INC HL ;
+ LD A,(HL) ;
+ CP $76 ;
+ JR Z,L059D ; to ENDED-2
+
+ LD (HL),$7F ;
+ DEC HL ;
+
+;; GET-CODE
+L0588: LD (HL),A ;
+
+;; ENDED-1
+L0589: JR L0523 ; to BACK-NEXT
+
+; --------------------
+; THE 'RUBOUT' ROUTINE
+; --------------------
+;
+;
+
+;; RUBOUT
+L058B: CALL L0593 ; routine LEFT-EDGE
+ CALL L055C ; routine CLEAR-ONE
+ JR L0589 ; to ENDED-1
+
+; ------------------------
+; THE 'ED-EDGE' SUBROUTINE
+; ------------------------
+;
+;
+
+;; LEFT-EDGE
+L0593: DEC HL ;
+ LD DE,($4014) ; sv E_LINE_lo
+ LD A,(DE) ;
+ CP $7F ;
+ RET NZ ;
+
+ POP DE ;
+
+;; ENDED-2
+L059D: JR L0589 ; to ENDED-1
+
+; -----------------------
+; THE 'CURSOR UP' ROUTINE
+; -----------------------
+;
+;
+
+;; UP-KEY
+L059F: LD HL,($400A) ; sv E_PPC_lo
+ CALL L09D8 ; routine LINE-ADDR
+ EX DE,HL ;
+ CALL L05BB ; routine LINE-NO
+ LD HL,$400B ; point to system variable E_PPC_hi
+ JP L0464 ; jump back to KEY-INPUT
+
+; --------------------------
+; THE 'FUNCTION KEY' ROUTINE
+; --------------------------
+;
+;
+
+;; FUNCTION
+L05AF: LD A,E ;
+ AND $07 ;
+ LD ($4006),A ; sv MODE
+ JR L059D ; back to ENDED-2
+
+; ------------------------------------
+; THE 'COLLECT LINE NUMBER' SUBROUTINE
+; ------------------------------------
+;
+;
+
+;; ZERO-DE
+L05B7: EX DE,HL ;
+ LD DE,L04C1 + 1 ; $04C2 - a location addressing two zeros.
+
+; ->
+
+;; LINE-NO
+L05BB: LD A,(HL) ;
+ AND $C0 ;
+ JR NZ,L05B7 ; to ZERO-DE
+
+ LD D,(HL) ;
+ INC HL ;
+ LD E,(HL) ;
+ RET ;
+
+; ----------------------
+; THE 'EDIT KEY' ROUTINE
+; ----------------------
+;
+;
+
+;; EDIT-KEY
+L05C4: CALL L0A1F ; routine LINE-ENDS clears lower display.
+
+ LD HL,L046F ; Address: EDIT-INP
+ PUSH HL ; ** is pushed as an error looping address.
+
+ BIT 5,(IY+$2D) ; test FLAGX
+ RET NZ ; indirect jump if in input mode
+ ; to L046F, EDIT-INP (begin again).
+
+;
+
+ LD HL,($4014) ; fetch E_LINE
+ LD ($400E),HL ; and use to update the screen cursor DF_CC
+
+; so now RST $10 will print the line numbers to the edit line instead of screen.
+; first make sure that no newline/out of screen can occur while sprinting the
+; line numbers to the edit line.
+
+ LD HL,$1821 ; prepare line 0, column 0.
+ LD ($4039),HL ; update S_POSN with these dummy values.
+
+ LD HL,($400A) ; fetch current line from E_PPC may be a
+ ; non-existent line e.g. last line deleted.
+ CALL L09D8 ; routine LINE-ADDR gets address or that of
+ ; the following line.
+ CALL L05BB ; routine LINE-NO gets line number if any in DE
+ ; leaving HL pointing at second low byte.
+
+ LD A,D ; test the line number for zero.
+ OR E ;
+ RET Z ; return if no line number - no program to edit.
+
+ DEC HL ; point to high byte.
+ CALL L0AA5 ; routine OUT-NO writes number to edit line.
+
+ INC HL ; point to length bytes.
+ LD C,(HL) ; low byte to C.
+ INC HL ;
+ LD B,(HL) ; high byte to B.
+
+ INC HL ; point to first character in line.
+ LD DE,($400E) ; fetch display file cursor DF_CC
+
+ LD A,$7F ; prepare the cursor character.
+ LD (DE),A ; and insert in edit line.
+ INC DE ; increment intended destination.
+
+ PUSH HL ; * save start of BASIC.
+
+ LD HL,$001D ; set an overhead of 29 bytes.
+ ADD HL,DE ; add in the address of cursor.
+ ADD HL,BC ; add the length of the line.
+ SBC HL,SP ; subtract the stack pointer.
+
+ POP HL ; * restore pointer to start of BASIC.
+
+ RET NC ; return if not enough room to L046F EDIT-INP.
+ ; the edit key appears not to work.
+
+ LDIR ; else copy bytes from program to edit line.
+ ; Note. hidden floating point forms are also
+ ; copied to edit line.
+
+ EX DE,HL ; transfer free location pointer to HL
+
+ POP DE ; ** remove address EDIT-INP from stack.
+
+ CALL L14A6 ; routine SET-STK-B sets STKEND from HL.
+
+ JR L059D ; back to ENDED-2 and after 3 more jumps
+ ; to L0472, LOWER.
+ ; Note. The LOWER routine removes the hidden
+ ; floating-point numbers from the edit line.
+
+; -------------------------
+; THE 'NEWLINE KEY' ROUTINE
+; -------------------------
+;
+;
+
+;; N/L-KEY
+L060C: CALL L0A1F ; routine LINE-ENDS
+
+ LD HL,L0472 ; prepare address: LOWER
+
+ BIT 5,(IY+$2D) ; sv FLAGX
+ JR NZ,L0629 ; to NOW-SCAN
+
+ LD HL,($4014) ; sv E_LINE_lo
+ LD A,(HL) ;
+ CP $FF ;
+ JR Z,L0626 ; to STK-UPPER
+
+ CALL L08E2 ; routine CLEAR-PRB
+ CALL L0A2A ; routine CLS
+
+;; STK-UPPER
+L0626: LD HL,L0419 ; Address: UPPER
+
+;; NOW-SCAN
+L0629: PUSH HL ; push routine address (LOWER or UPPER).
+ CALL L0CBA ; routine LINE-SCAN
+ POP HL ;
+ CALL L0537 ; routine CURSOR
+ CALL L055C ; routine CLEAR-ONE
+ CALL L0A73 ; routine E-LINE-NO
+ JR NZ,L064E ; to N/L-INP
+
+ LD A,B ;
+ OR C ;
+ JP NZ,L06E0 ; to N/L-LINE
+
+ DEC BC ;
+ DEC BC ;
+ LD ($4007),BC ; sv PPC_lo
+ LD (IY+$22),$02 ; sv DF_SZ
+ LD DE,($400C) ; sv D_FILE_lo
+
+ JR L0661 ; forward to TEST-NULL
+
+; ---
+
+;; N/L-INP
+L064E: CP $76 ;
+ JR Z,L0664 ; to N/L-NULL
+
+ LD BC,($4030) ; sv T_ADDR_lo
+ CALL L0918 ; routine LOC-ADDR
+ LD DE,($4029) ; sv NXTLIN_lo
+ LD (IY+$22),$02 ; sv DF_SZ
+
+;; TEST-NULL
+L0661: RST 18H ; GET-CHAR
+ CP $76 ;
+
+;; N/L-NULL
+L0664: JP Z,L0413 ; to N/L-ONLY
+
+ LD (IY+$01),$80 ; sv FLAGS
+ EX DE,HL ;
+
+;; NEXT-LINE
+L066C: LD ($4029),HL ; sv NXTLIN_lo
+ EX DE,HL ;
+ CALL L004D ; routine TEMP-PTR-2
+ CALL L0CC1 ; routine LINE-RUN
+ RES 1,(IY+$01) ; sv FLAGS - Signal printer not in use
+ LD A,$C0 ;
+ LD (IY+$19),A ; sv X_PTR_lo
+ CALL L14A3 ; routine X-TEMP
+ RES 5,(IY+$2D) ; sv FLAGX
+ BIT 7,(IY+$00) ; sv ERR_NR
+ JR Z,L06AE ; to STOP-LINE
+
+ LD HL,($4029) ; sv NXTLIN_lo
+ AND (HL) ;
+ JR NZ,L06AE ; to STOP-LINE
+
+ LD D,(HL) ;
+ INC HL ;
+ LD E,(HL) ;
+ LD ($4007),DE ; sv PPC_lo
+ INC HL ;
+ LD E,(HL) ;
+ INC HL ;
+ LD D,(HL) ;
+ INC HL ;
+ EX DE,HL ;
+ ADD HL,DE ;
+ CALL L0F46 ; routine BREAK-1
+ JR C,L066C ; to NEXT-LINE
+
+ LD HL,$4000 ; sv ERR_NR
+ BIT 7,(HL) ;
+ JR Z,L06AE ; to STOP-LINE
+
+ LD (HL),$0C ;
+
+;; STOP-LINE
+L06AE: BIT 7,(IY+$38) ; sv PR_CC
+ CALL Z,L0871 ; routine COPY-BUFF
+ LD BC,$0121 ;
+ CALL L0918 ; routine LOC-ADDR
+ LD A,($4000) ; sv ERR_NR
+ LD BC,($4007) ; sv PPC_lo
+ INC A ;
+ JR Z,L06D1 ; to REPORT
+
+ CP $09 ;
+ JR NZ,L06CA ; to CONTINUE
+
+ INC BC ;
+
+;; CONTINUE
+L06CA: LD ($402B),BC ; sv OLDPPC_lo
+ JR NZ,L06D1 ; to REPORT
+
+ DEC BC ;
+
+;; REPORT
+L06D1: CALL L07EB ; routine OUT-CODE
+ LD A,$18 ;
+
+ RST 10H ; PRINT-A
+ CALL L0A98 ; routine OUT-NUM
+ CALL L14AD ; routine CURSOR-IN
+ JP L04C1 ; to DISPLAY-6
+
+; ---
+
+;; N/L-LINE
+L06E0: LD ($400A),BC ; sv E_PPC_lo
+ LD HL,($4016) ; sv CH_ADD_lo
+ EX DE,HL ;
+ LD HL,L0413 ; Address: N/L-ONLY
+ PUSH HL ;
+ LD HL,($401A) ; sv STKBOT_lo
+ SBC HL,DE ;
+ PUSH HL ;
+ PUSH BC ;
+ CALL L02E7 ; routine SET-FAST
+ CALL L0A2A ; routine CLS
+ POP HL ;
+ CALL L09D8 ; routine LINE-ADDR
+ JR NZ,L0705 ; to COPY-OVER
+
+ CALL L09F2 ; routine NEXT-ONE
+ CALL L0A60 ; routine RECLAIM-2
+
+;; COPY-OVER
+L0705: POP BC ;
+ LD A,C ;
+ DEC A ;
+ OR B ;
+ RET Z ;
+
+ PUSH BC ;
+ INC BC ;
+ INC BC ;
+ INC BC ;
+ INC BC ;
+ DEC HL ;
+ CALL L099E ; routine MAKE-ROOM
+ CALL L0207 ; routine SLOW/FAST
+ POP BC ;
+ PUSH BC ;
+ INC DE ;
+ LD HL,($401A) ; sv STKBOT_lo
+ DEC HL ;
+ LDDR ; copy bytes
+ LD HL,($400A) ; sv E_PPC_lo
+ EX DE,HL ;
+ POP BC ;
+ LD (HL),B ;
+ DEC HL ;
+ LD (HL),C ;
+ DEC HL ;
+ LD (HL),E ;
+ DEC HL ;
+ LD (HL),D ;
+
+ RET ; return.
+
+; ---------------------------------------
+; THE 'LIST' AND 'LLIST' COMMAND ROUTINES
+; ---------------------------------------
+;
+;
+
+;; LLIST
+L072C: SET 1,(IY+$01) ; sv FLAGS - signal printer in use
+
+;; LIST
+L0730: CALL L0EA7 ; routine FIND-INT
+
+ LD A,B ; fetch high byte of user-supplied line number.
+ AND $3F ; and crudely limit to range 1-16383.
+
+ LD H,A ;
+ LD L,C ;
+ LD ($400A),HL ; sv E_PPC_lo
+ CALL L09D8 ; routine LINE-ADDR
+
+;; LIST-PROG
+L073E: LD E,$00 ;
+
+;; UNTIL-END
+L0740: CALL L0745 ; routine OUT-LINE lists one line of BASIC
+ ; making an early return when the screen is
+ ; full or the end of program is reached. >>
+ JR L0740 ; loop back to UNTIL-END
+
+; -----------------------------------
+; THE 'PRINT A BASIC LINE' SUBROUTINE
+; -----------------------------------
+;
+;
+
+;; OUT-LINE
+L0745: LD BC,($400A) ; sv E_PPC_lo
+ CALL L09EA ; routine CP-LINES
+ LD D,$92 ;
+ JR Z,L0755 ; to TEST-END
+
+ LD DE,$0000 ;
+ RL E ;
+
+;; TEST-END
+L0755: LD (IY+$1E),E ; sv BERG
+ LD A,(HL) ;
+ CP $40 ;
+ POP BC ;
+ RET NC ;
+
+ PUSH BC ;
+ CALL L0AA5 ; routine OUT-NO
+ INC HL ;
+ LD A,D ;
+
+ RST 10H ; PRINT-A
+ INC HL ;
+ INC HL ;
+
+;; COPY-LINE
+L0766: LD ($4016),HL ; sv CH_ADD_lo
+ SET 0,(IY+$01) ; sv FLAGS - Suppress leading space
+
+;; MORE-LINE
+L076D: LD BC,($4018) ; sv X_PTR_lo
+ LD HL,($4016) ; sv CH_ADD_lo
+ AND A ;
+ SBC HL,BC ;
+ JR NZ,L077C ; to TEST-NUM
+
+ LD A,$B8 ;
+
+ RST 10H ; PRINT-A
+
+;; TEST-NUM
+L077C: LD HL,($4016) ; sv CH_ADD_lo
+ LD A,(HL) ;
+ INC HL ;
+ CALL L07B4 ; routine NUMBER
+ LD ($4016),HL ; sv CH_ADD_lo
+ JR Z,L076D ; to MORE-LINE
+
+ CP $7F ;
+ JR Z,L079D ; to OUT-CURS
+
+ CP $76 ;
+ JR Z,L07EE ; to OUT-CH
+
+ BIT 6,A ;
+ JR Z,L079A ; to NOT-TOKEN
+
+ CALL L094B ; routine TOKENS
+ JR L076D ; to MORE-LINE
+
+; ---
+
+
+;; NOT-TOKEN
+L079A: RST 10H ; PRINT-A
+ JR L076D ; to MORE-LINE
+
+; ---
+
+;; OUT-CURS
+L079D: LD A,($4006) ; Fetch value of system variable MODE
+ LD B,$AB ; Prepare an inverse [F] for function cursor.
+
+ AND A ; Test for zero -
+ JR NZ,L07AA ; forward if not to FLAGS-2
+
+ LD A,($4001) ; Fetch system variable FLAGS.
+ LD B,$B0 ; Prepare an inverse [K] for keyword cursor.
+
+;; FLAGS-2
+L07AA: RRA ; 00000?00 -> 000000?0
+ RRA ; 000000?0 -> 0000000?
+ AND $01 ; 0000000? 0000000x
+
+ ADD A,B ; Possibly [F] -> [G] or [K] -> [L]
+
+ CALL L07F5 ; routine PRINT-SP prints character
+ JR L076D ; back to MORE-LINE
+
+; -----------------------
+; THE 'NUMBER' SUBROUTINE
+; -----------------------
+;
+;
+
+;; NUMBER
+L07B4: CP $7E ;
+ RET NZ ;
+
+ INC HL ;
+ INC HL ;
+ INC HL ;
+ INC HL ;
+ INC HL ;
+ RET ;
+
+; --------------------------------
+; THE 'KEYBOARD DECODE' SUBROUTINE
+; --------------------------------
+;
+;
+
+;; DECODE
+L07BD: LD D,$00 ;
+ SRA B ;
+ SBC A,A ;
+ OR $26 ;
+ LD L,$05 ;
+ SUB L ;
+
+;; KEY-LINE
+L07C7: ADD A,L ;
+ SCF ; Set Carry Flag
+ RR C ;
+ JR C,L07C7 ; to KEY-LINE
+
+ INC C ;
+ RET NZ ;
+
+ LD C,B ;
+ DEC L ;
+ LD L,$01 ;
+ JR NZ,L07C7 ; to KEY-LINE
+
+ LD HL,$007D ; (expr reqd)
+ LD E,A ;
+ ADD HL,DE ;
+ SCF ; Set Carry Flag
+ RET ;
+
+; -------------------------
+; THE 'PRINTING' SUBROUTINE
+; -------------------------
+;
+;
+
+;; LEAD-SP
+L07DC: LD A,E ;
+ AND A ;
+ RET M ;
+
+ JR L07F1 ; to PRINT-CH
+
+; ---
+
+;; OUT-DIGIT
+L07E1: XOR A ;
+
+;; DIGIT-INC
+L07E2: ADD HL,BC ;
+ INC A ;
+ JR C,L07E2 ; to DIGIT-INC
+
+ SBC HL,BC ;
+ DEC A ;
+ JR Z,L07DC ; to LEAD-SP
+
+;; OUT-CODE
+L07EB: LD E,$1C ;
+ ADD A,E ;
+
+;; OUT-CH
+L07EE: AND A ;
+ JR Z,L07F5 ; to PRINT-SP
+
+;; PRINT-CH
+L07F1: RES 0,(IY+$01) ; update FLAGS - signal leading space permitted
+
+;; PRINT-SP
+L07F5: EXX ;
+ PUSH HL ;
+ BIT 1,(IY+$01) ; test FLAGS - is printer in use ?
+ JR NZ,L0802 ; to LPRINT-A
+
+ CALL L0808 ; routine ENTER-CH
+ JR L0805 ; to PRINT-EXX
+
+; ---
+
+;; LPRINT-A
+L0802: CALL L0851 ; routine LPRINT-CH
+
+;; PRINT-EXX
+L0805: POP HL ;
+ EXX ;
+ RET ;
+
+; ---
+
+;; ENTER-CH
+L0808: LD D,A ;
+ LD BC,($4039) ; sv S_POSN_x
+ LD A,C ;
+ CP $21 ;
+ JR Z,L082C ; to TEST-LOW
+
+;; TEST-N/L
+L0812: LD A,$76 ;
+ CP D ;
+ JR Z,L0847 ; to WRITE-N/L
+
+ LD HL,($400E) ; sv DF_CC_lo
+ CP (HL) ;
+ LD A,D ;
+ JR NZ,L083E ; to WRITE-CH
+
+ DEC C ;
+ JR NZ,L083A ; to EXPAND-1
+
+ INC HL ;
+ LD ($400E),HL ; sv DF_CC_lo
+ LD C,$21 ;
+ DEC B ;
+ LD ($4039),BC ; sv S_POSN_x
+
+;; TEST-LOW
+L082C: LD A,B ;
+ CP (IY+$22) ; sv DF_SZ
+ JR Z,L0835 ; to REPORT-5
+
+ AND A ;
+ JR NZ,L0812 ; to TEST-N/L
+
+;; REPORT-5
+L0835: LD L,$04 ; 'No more room on screen'
+ JP L0058 ; to ERROR-3
+
+; ---
+
+;; EXPAND-1
+L083A: CALL L099B ; routine ONE-SPACE
+ EX DE,HL ;
+
+;; WRITE-CH
+L083E: LD (HL),A ;
+ INC HL ;
+ LD ($400E),HL ; sv DF_CC_lo
+ DEC (IY+$39) ; sv S_POSN_x
+ RET ;
+
+; ---
+
+;; WRITE-N/L
+L0847: LD C,$21 ;
+ DEC B ;
+ SET 0,(IY+$01) ; sv FLAGS - Suppress leading space
+ JP L0918 ; to LOC-ADDR
+
+; --------------------------
+; THE 'LPRINT-CH' SUBROUTINE
+; --------------------------
+; This routine sends a character to the ZX-Printer placing the code for the
+; character in the Printer Buffer.
+; Note. PR-CC contains the low byte of the buffer address. The high order byte
+; is always constant.
+
+
+;; LPRINT-CH
+L0851: CP $76 ; compare to NEWLINE.
+ JR Z,L0871 ; forward if so to COPY-BUFF
+
+ LD C,A ; take a copy of the character in C.
+ LD A,($4038) ; fetch print location from PR_CC
+ AND $7F ; ignore bit 7 to form true position.
+ CP $5C ; compare to 33rd location
+
+ LD L,A ; form low-order byte.
+ LD H,$40 ; the high-order byte is fixed.
+
+ CALL Z,L0871 ; routine COPY-BUFF to send full buffer to
+ ; the printer if first 32 bytes full.
+ ; (this will reset HL to start.)
+
+ LD (HL),C ; place character at location.
+ INC L ; increment - will not cross a 256 boundary.
+ LD (IY+$38),L ; update system variable PR_CC
+ ; automatically resetting bit 7 to show that
+ ; the buffer is not empty.
+ RET ; return.
+
+; --------------------------
+; THE 'COPY' COMMAND ROUTINE
+; --------------------------
+; The full character-mapped screen is copied to the ZX-Printer.
+; All twenty-four text/graphic lines are printed.
+
+;; COPY
+L0869: LD D,$16 ; prepare to copy twenty four text lines.
+ LD HL,($400C) ; set HL to start of display file from D_FILE.
+ INC HL ;
+ JR L0876 ; forward to COPY*D
+
+; ---
+
+; A single character-mapped printer buffer is copied to the ZX-Printer.
+
+;; COPY-BUFF
+L0871: LD D,$01 ; prepare to copy a single text line.
+ LD HL,$403C ; set HL to start of printer buffer PRBUFF.
+
+; both paths converge here.
+
+;; COPY*D
+L0876: CALL L02E7 ; routine SET-FAST
+
+ PUSH BC ; *** preserve BC throughout.
+ ; a pending character may be present
+ ; in C from LPRINT-CH
+
+;; COPY-LOOP
+L087A: PUSH HL ; save first character of line pointer. (*)
+ XOR A ; clear accumulator.
+ LD E,A ; set pixel line count, range 0-7, to zero.
+
+; this inner loop deals with each horizontal pixel line.
+
+;; COPY-TIME
+L087D: OUT ($FB),A ; bit 2 reset starts the printer motor
+ ; with an inactive stylus - bit 7 reset.
+ POP HL ; pick up first character of line pointer (*)
+ ; on inner loop.
+
+;; COPY-BRK
+L0880: CALL L0F46 ; routine BREAK-1
+ JR C,L088A ; forward with no keypress to COPY-CONT
+
+; else A will hold 11111111 0
+
+ RRA ; 0111 1111
+ OUT ($FB),A ; stop ZX printer motor, de-activate stylus.
+
+;; REPORT-D2
+L0888: RST 08H ; ERROR-1
+ DEFB $0C ; Error Report: BREAK - CONT repeats
+
+; ---
+
+;; COPY-CONT
+L088A: IN A,($FB) ; read from printer port.
+ ADD A,A ; test bit 6 and 7
+ JP M,L08DE ; jump forward with no printer to COPY-END
+
+ JR NC,L0880 ; back if stylus not in position to COPY-BRK
+
+ PUSH HL ; save first character of line pointer (*)
+ PUSH DE ; ** preserve character line and pixel line.
+
+ LD A,D ; text line count to A?
+ CP $02 ; sets carry if last line.
+ SBC A,A ; now $FF if last line else zero.
+
+; now cleverly prepare a printer control mask setting bit 2 (later moved to 1)
+; of D to slow printer for the last two pixel lines ( E = 6 and 7)
+
+ AND E ; and with pixel line offset 0-7
+ RLCA ; shift to left.
+ AND E ; and again.
+ LD D,A ; store control mask in D.
+
+;; COPY-NEXT
+L089C: LD C,(HL) ; load character from screen or buffer.
+ LD A,C ; save a copy in C for later inverse test.
+ INC HL ; update pointer for next time.
+ CP $76 ; is character a NEWLINE ?
+ JR Z,L08C7 ; forward, if so, to COPY-N/L
+
+ PUSH HL ; * else preserve the character pointer.
+
+ SLA A ; (?) multiply by two
+ ADD A,A ; multiply by four
+ ADD A,A ; multiply by eight
+
+ LD H,$0F ; load H with half the address of character set.
+ RL H ; now $1E or $1F (with carry)
+ ADD A,E ; add byte offset 0-7
+ LD L,A ; now HL addresses character source byte
+
+ RL C ; test character, setting carry if inverse.
+ SBC A,A ; accumulator now $00 if normal, $FF if inverse.
+
+ XOR (HL) ; combine with bit pattern at end or ROM.
+ LD C,A ; transfer the byte to C.
+ LD B,$08 ; count eight bits to output.
+
+;; COPY-BITS
+L08B5: LD A,D ; fetch speed control mask from D.
+ RLC C ; rotate a bit from output byte to carry.
+ RRA ; pick up in bit 7, speed bit to bit 1
+ LD H,A ; store aligned mask in H register.
+
+;; COPY-WAIT
+L08BA: IN A,($FB) ; read the printer port
+ RRA ; test for alignment signal from encoder.
+ JR NC,L08BA ; loop if not present to COPY-WAIT
+
+ LD A,H ; control byte to A.
+ OUT ($FB),A ; and output to printer port.
+ DJNZ L08B5 ; loop for all eight bits to COPY-BITS
+
+ POP HL ; * restore character pointer.
+ JR L089C ; back for adjacent character line to COPY-NEXT
+
+; ---
+
+; A NEWLINE has been encountered either following a text line or as the
+; first character of the screen or printer line.
+
+;; COPY-N/L
+L08C7: IN A,($FB) ; read printer port.
+ RRA ; wait for encoder signal.
+ JR NC,L08C7 ; loop back if not to COPY-N/L
+
+ LD A,D ; transfer speed mask to A.
+ RRCA ; rotate speed bit to bit 1.
+ ; bit 7, stylus control is reset.
+ OUT ($FB),A ; set the printer speed.
+
+ POP DE ; ** restore character line and pixel line.
+ INC E ; increment pixel line 0-7.
+ BIT 3,E ; test if value eight reached.
+ JR Z,L087D ; back if not to COPY-TIME
+
+; eight pixel lines, a text line have been completed.
+
+ POP BC ; lose the now redundant first character
+ ; pointer
+ DEC D ; decrease text line count.
+ JR NZ,L087A ; back if not zero to COPY-LOOP
+
+ LD A,$04 ; stop the already slowed printer motor.
+ OUT ($FB),A ; output to printer port.
+
+;; COPY-END
+L08DE: CALL L0207 ; routine SLOW/FAST
+ POP BC ; *** restore preserved BC.
+
+; -------------------------------------
+; THE 'CLEAR PRINTER BUFFER' SUBROUTINE
+; -------------------------------------
+; This subroutine sets 32 bytes of the printer buffer to zero (space) and
+; the 33rd character is set to a NEWLINE.
+; This occurs after the printer buffer is sent to the printer but in addition
+; after the 24 lines of the screen are sent to the printer.
+; Note. This is a logic error as the last operation does not involve the
+; buffer at all. Logically one should be able to use
+; 10 LPRINT "HELLO ";
+; 20 COPY
+; 30 LPRINT ; "WORLD"
+; and expect to see the entire greeting emerge from the printer.
+; Surprisingly this logic error was never discovered and although one can argue
+; if the above is a bug, the repetition of this error on the Spectrum was most
+; definitely a bug.
+; Since the printer buffer is fixed at the end of the system variables, and
+; the print position is in the range $3C - $5C, then bit 7 of the system
+; variable is set to show the buffer is empty and automatically reset when
+; the variable is updated with any print position - neat.
+
+;; CLEAR-PRB
+L08E2: LD HL,$405C ; address fixed end of PRBUFF
+ LD (HL),$76 ; place a newline at last position.
+ LD B,$20 ; prepare to blank 32 preceding characters.
+
+;; PRB-BYTES
+L08E9: DEC HL ; decrement address - could be DEC L.
+ LD (HL),$00 ; place a zero byte.
+ DJNZ L08E9 ; loop for all thirty-two to PRB-BYTES
+
+ LD A,L ; fetch character print position.
+ SET 7,A ; signal the printer buffer is clear.
+ LD ($4038),A ; update one-byte system variable PR_CC
+ RET ; return.
+
+; -------------------------
+; THE 'PRINT AT' SUBROUTINE
+; -------------------------
+;
+;
+
+;; PRINT-AT
+L08F5: LD A,$17 ;
+ SUB B ;
+ JR C,L0905 ; to WRONG-VAL
+
+;; TEST-VAL
+L08FA: CP (IY+$22) ; sv DF_SZ
+ JP C,L0835 ; to REPORT-5
+
+ INC A ;
+ LD B,A ;
+ LD A,$1F ;
+ SUB C ;
+
+;; WRONG-VAL
+L0905: JP C,L0EAD ; to REPORT-B
+
+ ADD A,$02 ;
+ LD C,A ;
+
+;; SET-FIELD
+L090B: BIT 1,(IY+$01) ; sv FLAGS - Is printer in use
+ JR Z,L0918 ; to LOC-ADDR
+
+ LD A,$5D ;
+ SUB C ;
+ LD ($4038),A ; sv PR_CC
+ RET ;
+
+; ----------------------------
+; THE 'LOCATE ADDRESS' ROUTINE
+; ----------------------------
+;
+;
+
+;; LOC-ADDR
+L0918: LD ($4039),BC ; sv S_POSN_x
+ LD HL,($4010) ; sv VARS_lo
+ LD D,C ;
+ LD A,$22 ;
+ SUB C ;
+ LD C,A ;
+ LD A,$76 ;
+ INC B ;
+
+;; LOOK-BACK
+L0927: DEC HL ;
+ CP (HL) ;
+ JR NZ,L0927 ; to LOOK-BACK
+
+ DJNZ L0927 ; to LOOK-BACK
+
+ INC HL ;
+ CPIR ;
+ DEC HL ;
+ LD ($400E),HL ; sv DF_CC_lo
+ SCF ; Set Carry Flag
+ RET PO ;
+
+ DEC D ;
+ RET Z ;
+
+ PUSH BC ;
+ CALL L099E ; routine MAKE-ROOM
+ POP BC ;
+ LD B,C ;
+ LD H,D ;
+ LD L,E ;
+
+;; EXPAND-2
+L0940: LD (HL),$00 ;
+ DEC HL ;
+ DJNZ L0940 ; to EXPAND-2
+
+ EX DE,HL ;
+ INC HL ;
+ LD ($400E),HL ; sv DF_CC_lo
+ RET ;
+
+; ------------------------------
+; THE 'EXPAND TOKENS' SUBROUTINE
+; ------------------------------
+;
+;
+
+;; TOKENS
+L094B: PUSH AF ;
+ CALL L0975 ; routine TOKEN-ADD
+ JR NC,L0959 ; to ALL-CHARS
+
+ BIT 0,(IY+$01) ; sv FLAGS - Leading space if set
+ JR NZ,L0959 ; to ALL-CHARS
+
+ XOR A ;
+
+ RST 10H ; PRINT-A
+
+;; ALL-CHARS
+L0959: LD A,(BC) ;
+ AND $3F ;
+
+ RST 10H ; PRINT-A
+ LD A,(BC) ;
+ INC BC ;
+ ADD A,A ;
+ JR NC,L0959 ; to ALL-CHARS
+
+ POP BC ;
+ BIT 7,B ;
+ RET Z ;
+
+ CP $1A ;
+ JR Z,L096D ; to TRAIL-SP
+
+ CP $38 ;
+ RET C ;
+
+;; TRAIL-SP
+L096D: XOR A ;
+ SET 0,(IY+$01) ; sv FLAGS - Suppress leading space
+ JP L07F5 ; to PRINT-SP
+
+; ---
+
+;; TOKEN-ADD
+L0975: PUSH HL ;
+ LD HL,L0111 ; Address of TOKENS
+ BIT 7,A ;
+ JR Z,L097F ; to TEST-HIGH
+
+ AND $3F ;
+
+;; TEST-HIGH
+L097F: CP $43 ;
+ JR NC,L0993 ; to FOUND
+
+ LD B,A ;
+ INC B ;
+
+;; WORDS
+L0985: BIT 7,(HL) ;
+ INC HL ;
+ JR Z,L0985 ; to WORDS
+
+ DJNZ L0985 ; to WORDS
+
+ BIT 6,A ;
+ JR NZ,L0992 ; to COMP-FLAG
+
+ CP $18 ;
+
+;; COMP-FLAG
+L0992: CCF ; Complement Carry Flag
+
+;; FOUND
+L0993: LD B,H ;
+ LD C,L ;
+ POP HL ;
+ RET NC ;
+
+ LD A,(BC) ;
+ ADD A,$E4 ;
+ RET ;
+
+; --------------------------
+; THE 'ONE SPACE' SUBROUTINE
+; --------------------------
+;
+;
+
+;; ONE-SPACE
+L099B: LD BC,$0001 ;
+
+; --------------------------
+; THE 'MAKE ROOM' SUBROUTINE
+; --------------------------
+;
+;
+
+;; MAKE-ROOM
+L099E: PUSH HL ;
+ CALL L0EC5 ; routine TEST-ROOM
+ POP HL ;
+ CALL L09AD ; routine POINTERS
+ LD HL,($401C) ; sv STKEND_lo
+ EX DE,HL ;
+ LDDR ; Copy Bytes
+ RET ;
+
+; -------------------------
+; THE 'POINTERS' SUBROUTINE
+; -------------------------
+;
+;
+
+;; POINTERS
+L09AD: PUSH AF ;
+ PUSH HL ;
+ LD HL,$400C ; sv D_FILE_lo
+ LD A,$09 ;
+
+;; NEXT-PTR
+L09B4: LD E,(HL) ;
+ INC HL ;
+ LD D,(HL) ;
+ EX (SP),HL ;
+ AND A ;
+ SBC HL,DE ;
+ ADD HL,DE ;
+ EX (SP),HL ;
+ JR NC,L09C8 ; to PTR-DONE
+
+ PUSH DE ;
+ EX DE,HL ;
+ ADD HL,BC ;
+ EX DE,HL ;
+ LD (HL),D ;
+ DEC HL ;
+ LD (HL),E ;
+ INC HL ;
+ POP DE ;
+
+;; PTR-DONE
+L09C8: INC HL ;
+ DEC A ;
+ JR NZ,L09B4 ; to NEXT-PTR
+
+ EX DE,HL ;
+ POP DE ;
+ POP AF ;
+ AND A ;
+ SBC HL,DE ;
+ LD B,H ;
+ LD C,L ;
+ INC BC ;
+ ADD HL,DE ;
+ EX DE,HL ;
+ RET ;
+
+; -----------------------------
+; THE 'LINE ADDRESS' SUBROUTINE
+; -----------------------------
+;
+;
+
+;; LINE-ADDR
+L09D8: PUSH HL ;
+ LD HL,$407D ;
+ LD D,H ;
+ LD E,L ;
+
+;; NEXT-TEST
+L09DE: POP BC ;
+ CALL L09EA ; routine CP-LINES
+ RET NC ;
+
+ PUSH BC ;
+ CALL L09F2 ; routine NEXT-ONE
+ EX DE,HL ;
+ JR L09DE ; to NEXT-TEST
+
+; -------------------------------------
+; THE 'COMPARE LINE NUMBERS' SUBROUTINE
+; -------------------------------------
+;
+;
+
+;; CP-LINES
+L09EA: LD A,(HL) ;
+ CP B ;
+ RET NZ ;
+
+ INC HL ;
+ LD A,(HL) ;
+ DEC HL ;
+ CP C ;
+ RET ;
+
+; --------------------------------------
+; THE 'NEXT LINE OR VARIABLE' SUBROUTINE
+; --------------------------------------
+;
+;
+
+;; NEXT-ONE
+L09F2: PUSH HL ;
+ LD A,(HL) ;
+ CP $40 ;
+ JR C,L0A0F ; to LINES
+
+ BIT 5,A ;
+ JR Z,L0A10 ; forward to NEXT-O-4
+
+ ADD A,A ;
+ JP M,L0A01 ; to NEXT+FIVE
+
+ CCF ; Complement Carry Flag
+
+;; NEXT+FIVE
+L0A01: LD BC,$0005 ;
+ JR NC,L0A08 ; to NEXT-LETT
+
+ LD C,$11 ;
+
+;; NEXT-LETT
+L0A08: RLA ;
+ INC HL ;
+ LD A,(HL) ;
+ JR NC,L0A08 ; to NEXT-LETT
+
+ JR L0A15 ; to NEXT-ADD
+
+; ---
+
+;; LINES
+L0A0F: INC HL ;
+
+;; NEXT-O-4
+L0A10: INC HL ;
+ LD C,(HL) ;
+ INC HL ;
+ LD B,(HL) ;
+ INC HL ;
+
+;; NEXT-ADD
+L0A15: ADD HL,BC ;
+ POP DE ;
+
+; ---------------------------
+; THE 'DIFFERENCE' SUBROUTINE
+; ---------------------------
+;
+;
+
+;; DIFFER
+L0A17: AND A ;
+ SBC HL,DE ;
+ LD B,H ;
+ LD C,L ;
+ ADD HL,DE ;
+ EX DE,HL ;
+ RET ;
+
+; --------------------------
+; THE 'LINE-ENDS' SUBROUTINE
+; --------------------------
+;
+;
+
+;; LINE-ENDS
+L0A1F: LD B,(IY+$22) ; sv DF_SZ
+ PUSH BC ;
+ CALL L0A2C ; routine B-LINES
+ POP BC ;
+ DEC B ;
+ JR L0A2C ; to B-LINES
+
+; -------------------------
+; THE 'CLS' COMMAND ROUTINE
+; -------------------------
+;
+;
+
+;; CLS
+L0A2A: LD B,$18 ;
+
+;; B-LINES
+L0A2C: RES 1,(IY+$01) ; sv FLAGS - Signal printer not in use
+ LD C,$21 ;
+ PUSH BC ;
+ CALL L0918 ; routine LOC-ADDR
+ POP BC ;
+ LD A,($4005) ; sv RAMTOP_hi
+ CP $4D ;
+ JR C,L0A52 ; to COLLAPSED
+
+ SET 7,(IY+$3A) ; sv S_POSN_y
+
+;; CLEAR-LOC
+L0A42: XOR A ; prepare a space
+ CALL L07F5 ; routine PRINT-SP prints a space
+ LD HL,($4039) ; sv S_POSN_x
+ LD A,L ;
+ OR H ;
+ AND $7E ;
+ JR NZ,L0A42 ; to CLEAR-LOC
+
+ JP L0918 ; to LOC-ADDR
+
+; ---
+
+;; COLLAPSED
+L0A52: LD D,H ;
+ LD E,L ;
+ DEC HL ;
+ LD C,B ;
+ LD B,$00 ;
+ LDIR ; Copy Bytes
+ LD HL,($4010) ; sv VARS_lo
+
+; ----------------------------
+; THE 'RECLAIMING' SUBROUTINES
+; ----------------------------
+;
+;
+
+;; RECLAIM-1
+L0A5D: CALL L0A17 ; routine DIFFER
+
+;; RECLAIM-2
+L0A60: PUSH BC ;
+ LD A,B ;
+ CPL ;
+ LD B,A ;
+ LD A,C ;
+ CPL ;
+ LD C,A ;
+ INC BC ;
+ CALL L09AD ; routine POINTERS
+ EX DE,HL ;
+ POP HL ;
+ ADD HL,DE ;
+ PUSH DE ;
+ LDIR ; Copy Bytes
+ POP HL ;
+ RET ;
+
+; ------------------------------
+; THE 'E-LINE NUMBER' SUBROUTINE
+; ------------------------------
+;
+;
+
+;; E-LINE-NO
+L0A73: LD HL,($4014) ; sv E_LINE_lo
+ CALL L004D ; routine TEMP-PTR-2
+
+ RST 18H ; GET-CHAR
+ BIT 5,(IY+$2D) ; sv FLAGX
+ RET NZ ;
+
+ LD HL,$405D ; sv MEM-0-1st
+ LD ($401C),HL ; sv STKEND_lo
+ CALL L1548 ; routine INT-TO-FP
+ CALL L158A ; routine FP-TO-BC
+ JR C,L0A91 ; to NO-NUMBER
+
+ LD HL,$D8F0 ; value '-10000'
+ ADD HL,BC ;
+
+;; NO-NUMBER
+L0A91: JP C,L0D9A ; to REPORT-C
+
+ CP A ;
+ JP L14BC ; routine SET-MIN
+
+; -------------------------------------------------
+; THE 'REPORT AND LINE NUMBER' PRINTING SUBROUTINES
+; -------------------------------------------------
+;
+;
+
+;; OUT-NUM
+L0A98: PUSH DE ;
+ PUSH HL ;
+ XOR A ;
+ BIT 7,B ;
+ JR NZ,L0ABF ; to UNITS
+
+ LD H,B ;
+ LD L,C ;
+ LD E,$FF ;
+ JR L0AAD ; to THOUSAND
+
+; ---
+
+;; OUT-NO
+L0AA5: PUSH DE ;
+ LD D,(HL) ;
+ INC HL ;
+ LD E,(HL) ;
+ PUSH HL ;
+ EX DE,HL ;
+ LD E,$00 ; set E to leading space.
+
+;; THOUSAND
+L0AAD: LD BC,$FC18 ;
+ CALL L07E1 ; routine OUT-DIGIT
+ LD BC,$FF9C ;
+ CALL L07E1 ; routine OUT-DIGIT
+ LD C,$F6 ;
+ CALL L07E1 ; routine OUT-DIGIT
+ LD A,L ;
+
+;; UNITS
+L0ABF: CALL L07EB ; routine OUT-CODE
+ POP HL ;
+ POP DE ;
+ RET ;
+
+; --------------------------
+; THE 'UNSTACK-Z' SUBROUTINE
+; --------------------------
+; This subroutine is used to return early from a routine when checking syntax.
+; On the ZX81 the same routines that execute commands also check the syntax
+; on line entry. This enables precise placement of the error marker in a line
+; that fails syntax.
+; The sequence CALL SYNTAX-Z ; RET Z can be replaced by a call to this routine
+; although it has not replaced every occurrence of the above two instructions.
+; Even on the ZX-80 this routine was not fully utilized.
+
+;; UNSTACK-Z
+L0AC5: CALL L0DA6 ; routine SYNTAX-Z resets the ZERO flag if
+ ; checking syntax.
+ POP HL ; drop the return address.
+ RET Z ; return to previous calling routine if
+ ; checking syntax.
+
+ JP (HL) ; else jump to the continuation address in
+ ; the calling routine as RET would have done.
+
+; ----------------------------
+; THE 'LPRINT' COMMAND ROUTINE
+; ----------------------------
+;
+;
+
+;; LPRINT
+L0ACB: SET 1,(IY+$01) ; sv FLAGS - Signal printer in use
+
+; ---------------------------
+; THE 'PRINT' COMMAND ROUTINE
+; ---------------------------
+;
+;
+
+;; PRINT
+L0ACF: LD A,(HL) ;
+ CP $76 ;
+ JP Z,L0B84 ; to PRINT-END
+
+;; PRINT-1
+L0AD5: SUB $1A ;
+ ADC A,$00 ;
+ JR Z,L0B44 ; to SPACING
+
+ CP $A7 ;
+ JR NZ,L0AFA ; to NOT-AT
+
+
+ RST 20H ; NEXT-CHAR
+ CALL L0D92 ; routine CLASS-6
+ CP $1A ;
+ JP NZ,L0D9A ; to REPORT-C
+
+
+ RST 20H ; NEXT-CHAR
+ CALL L0D92 ; routine CLASS-6
+ CALL L0B4E ; routine SYNTAX-ON
+
+ RST 28H ;; FP-CALC
+ DEFB $01 ;;exchange
+ DEFB $34 ;;end-calc
+
+ CALL L0BF5 ; routine STK-TO-BC
+ CALL L08F5 ; routine PRINT-AT
+ JR L0B37 ; to PRINT-ON
+
+; ---
+
+;; NOT-AT
+L0AFA: CP $A8 ;
+ JR NZ,L0B31 ; to NOT-TAB
+
+
+ RST 20H ; NEXT-CHAR
+ CALL L0D92 ; routine CLASS-6
+ CALL L0B4E ; routine SYNTAX-ON
+ CALL L0C02 ; routine STK-TO-A
+ JP NZ,L0EAD ; to REPORT-B
+
+ AND $1F ;
+ LD C,A ;
+ BIT 1,(IY+$01) ; sv FLAGS - Is printer in use
+ JR Z,L0B1E ; to TAB-TEST
+
+ SUB (IY+$38) ; sv PR_CC
+ SET 7,A ;
+ ADD A,$3C ;
+ CALL NC,L0871 ; routine COPY-BUFF
+
+;; TAB-TEST
+L0B1E: ADD A,(IY+$39) ; sv S_POSN_x
+ CP $21 ;
+ LD A,($403A) ; sv S_POSN_y
+ SBC A,$01 ;
+ CALL L08FA ; routine TEST-VAL
+ SET 0,(IY+$01) ; sv FLAGS - Suppress leading space
+ JR L0B37 ; to PRINT-ON
+
+; ---
+
+;; NOT-TAB
+L0B31: CALL L0F55 ; routine SCANNING
+ CALL L0B55 ; routine PRINT-STK
+
+;; PRINT-ON
+L0B37: RST 18H ; GET-CHAR
+ SUB $1A ;
+ ADC A,$00 ;
+ JR Z,L0B44 ; to SPACING
+
+ CALL L0D1D ; routine CHECK-END
+ JP L0B84 ;;; to PRINT-END
+
+; ---
+
+;; SPACING
+L0B44: CALL NC,L0B8B ; routine FIELD
+
+ RST 20H ; NEXT-CHAR
+ CP $76 ;
+ RET Z ;
+
+ JP L0AD5 ;;; to PRINT-1
+
+; ---
+
+;; SYNTAX-ON
+L0B4E: CALL L0DA6 ; routine SYNTAX-Z
+ RET NZ ;
+
+ POP HL ;
+ JR L0B37 ; to PRINT-ON
+
+; ---
+
+;; PRINT-STK
+L0B55: CALL L0AC5 ; routine UNSTACK-Z
+ BIT 6,(IY+$01) ; sv FLAGS - Numeric or string result?
+ CALL Z,L13F8 ; routine STK-FETCH
+ JR Z,L0B6B ; to PR-STR-4
+
+ JP L15DB ; jump forward to PRINT-FP
+
+; ---
+
+;; PR-STR-1
+L0B64: LD A,$0B ;
+
+;; PR-STR-2
+L0B66: RST 10H ; PRINT-A
+
+;; PR-STR-3
+L0B67: LD DE,($4018) ; sv X_PTR_lo
+
+;; PR-STR-4
+L0B6B: LD A,B ;
+ OR C ;
+ DEC BC ;
+ RET Z ;
+
+ LD A,(DE) ;
+ INC DE ;
+ LD ($4018),DE ; sv X_PTR_lo
+ BIT 6,A ;
+ JR Z,L0B66 ; to PR-STR-2
+
+ CP $C0 ;
+ JR Z,L0B64 ; to PR-STR-1
+
+ PUSH BC ;
+ CALL L094B ; routine TOKENS
+ POP BC ;
+ JR L0B67 ; to PR-STR-3
+
+; ---
+
+;; PRINT-END
+L0B84: CALL L0AC5 ; routine UNSTACK-Z
+ LD A,$76 ;
+
+ RST 10H ; PRINT-A
+ RET ;
+
+; ---
+
+;; FIELD
+L0B8B: CALL L0AC5 ; routine UNSTACK-Z
+ SET 0,(IY+$01) ; sv FLAGS - Suppress leading space
+ XOR A ;
+
+ RST 10H ; PRINT-A
+ LD BC,($4039) ; sv S_POSN_x
+ LD A,C ;
+ BIT 1,(IY+$01) ; sv FLAGS - Is printer in use
+ JR Z,L0BA4 ; to CENTRE
+
+ LD A,$5D ;
+ SUB (IY+$38) ; sv PR_CC
+
+;; CENTRE
+L0BA4: LD C,$11 ;
+ CP C ;
+ JR NC,L0BAB ; to RIGHT
+
+ LD C,$01 ;
+
+;; RIGHT
+L0BAB: CALL L090B ; routine SET-FIELD
+ RET ;
+
+; --------------------------------------
+; THE 'PLOT AND UNPLOT' COMMAND ROUTINES
+; --------------------------------------
+;
+;
+
+;; PLOT/UNP
+L0BAF: CALL L0BF5 ; routine STK-TO-BC
+ LD ($4036),BC ; sv COORDS_x
+ LD A,$2B ;
+ SUB B ;
+ JP C,L0EAD ; to REPORT-B
+
+ LD B,A ;
+ LD A,$01 ;
+ SRA B ;
+ JR NC,L0BC5 ; to COLUMNS
+
+ LD A,$04 ;
+
+;; COLUMNS
+L0BC5: SRA C ;
+ JR NC,L0BCA ; to FIND-ADDR
+
+ RLCA ;
+
+;; FIND-ADDR
+L0BCA: PUSH AF ;
+ CALL L08F5 ; routine PRINT-AT
+ LD A,(HL) ;
+ RLCA ;
+ CP $10 ;
+ JR NC,L0BDA ; to TABLE-PTR
+
+ RRCA ;
+ JR NC,L0BD9 ; to SQ-SAVED
+
+ XOR $8F ;
+
+;; SQ-SAVED
+L0BD9: LD B,A ;
+
+;; TABLE-PTR
+L0BDA: LD DE,L0C9E ; Address: P-UNPLOT
+ LD A,($4030) ; sv T_ADDR_lo
+ SUB E ;
+ JP M,L0BE9 ; to PLOT
+
+ POP AF ;
+ CPL ;
+ AND B ;
+ JR L0BEB ; to UNPLOT
+
+; ---
+
+;; PLOT
+L0BE9: POP AF ;
+ OR B ;
+
+;; UNPLOT
+L0BEB: CP $08 ;
+ JR C,L0BF1 ; to PLOT-END
+
+ XOR $8F ;
+
+;; PLOT-END
+L0BF1: EXX ;
+
+ RST 10H ; PRINT-A
+ EXX ;
+ RET ;
+
+; ----------------------------
+; THE 'STACK-TO-BC' SUBROUTINE
+; ----------------------------
+;
+;
+
+;; STK-TO-BC
+L0BF5: CALL L0C02 ; routine STK-TO-A
+ LD B,A ;
+ PUSH BC ;
+ CALL L0C02 ; routine STK-TO-A
+ LD E,C ;
+ POP BC ;
+ LD D,C ;
+ LD C,A ;
+ RET ;
+
+; ---------------------------
+; THE 'STACK-TO-A' SUBROUTINE
+; ---------------------------
+;
+;
+
+;; STK-TO-A
+L0C02: CALL L15CD ; routine FP-TO-A
+ JP C,L0EAD ; to REPORT-B
+
+ LD C,$01 ;
+ RET Z ;
+
+ LD C,$FF ;
+ RET ;
+
+; -----------------------
+; THE 'SCROLL' SUBROUTINE
+; -----------------------
+;
+;
+
+;; SCROLL
+L0C0E: LD B,(IY+$22) ; sv DF_SZ
+ LD C,$21 ;
+ CALL L0918 ; routine LOC-ADDR
+ CALL L099B ; routine ONE-SPACE
+ LD A,(HL) ;
+ LD (DE),A ;
+ INC (IY+$3A) ; sv S_POSN_y
+ LD HL,($400C) ; sv D_FILE_lo
+ INC HL ;
+ LD D,H ;
+ LD E,L ;
+ CPIR ;
+ JP L0A5D ; to RECLAIM-1
+
+; -------------------
+; THE 'SYNTAX' TABLES
+; -------------------
+
+; i) The Offset table
+
+;; offset-t
+L0C29: DEFB L0CB4 - $ ; 8B offset to; Address: P-LPRINT
+ DEFB L0CB7 - $ ; 8D offset to; Address: P-LLIST
+ DEFB L0C58 - $ ; 2D offset to; Address: P-STOP
+ DEFB L0CAB - $ ; 7F offset to; Address: P-SLOW
+ DEFB L0CAE - $ ; 81 offset to; Address: P-FAST
+ DEFB L0C77 - $ ; 49 offset to; Address: P-NEW
+ DEFB L0CA4 - $ ; 75 offset to; Address: P-SCROLL
+ DEFB L0C8F - $ ; 5F offset to; Address: P-CONT
+ DEFB L0C71 - $ ; 40 offset to; Address: P-DIM
+ DEFB L0C74 - $ ; 42 offset to; Address: P-REM
+ DEFB L0C5E - $ ; 2B offset to; Address: P-FOR
+ DEFB L0C4B - $ ; 17 offset to; Address: P-GOTO
+ DEFB L0C54 - $ ; 1F offset to; Address: P-GOSUB
+ DEFB L0C6D - $ ; 37 offset to; Address: P-INPUT
+ DEFB L0C89 - $ ; 52 offset to; Address: P-LOAD
+ DEFB L0C7D - $ ; 45 offset to; Address: P-LIST
+ DEFB L0C48 - $ ; 0F offset to; Address: P-LET
+ DEFB L0CA7 - $ ; 6D offset to; Address: P-PAUSE
+ DEFB L0C66 - $ ; 2B offset to; Address: P-NEXT
+ DEFB L0C80 - $ ; 44 offset to; Address: P-POKE
+ DEFB L0C6A - $ ; 2D offset to; Address: P-PRINT
+ DEFB L0C98 - $ ; 5A offset to; Address: P-PLOT
+ DEFB L0C7A - $ ; 3B offset to; Address: P-RUN
+ DEFB L0C8C - $ ; 4C offset to; Address: P-SAVE
+ DEFB L0C86 - $ ; 45 offset to; Address: P-RAND
+ DEFB L0C4F - $ ; 0D offset to; Address: P-IF
+ DEFB L0C95 - $ ; 52 offset to; Address: P-CLS
+ DEFB L0C9E - $ ; 5A offset to; Address: P-UNPLOT
+ DEFB L0C92 - $ ; 4D offset to; Address: P-CLEAR
+ DEFB L0C5B - $ ; 15 offset to; Address: P-RETURN
+ DEFB L0CB1 - $ ; 6A offset to; Address: P-COPY
+
+; ii) The parameter table.
+
+
+;; P-LET
+L0C48: DEFB $01 ; Class-01 - A variable is required.
+ DEFB $14 ; Separator: '='
+ DEFB $02 ; Class-02 - An expression, numeric or string,
+ ; must follow.
+
+;; P-GOTO
+L0C4B: DEFB $06 ; Class-06 - A numeric expression must follow.
+ DEFB $00 ; Class-00 - No further operands.
+ DEFW L0E81 ; Address: $0E81; Address: GOTO
+
+;; P-IF
+L0C4F: DEFB $06 ; Class-06 - A numeric expression must follow.
+ DEFB $DE ; Separator: 'THEN'
+ DEFB $05 ; Class-05 - Variable syntax checked entirely
+ ; by routine.
+ DEFW L0DAB ; Address: $0DAB; Address: IF
+
+;; P-GOSUB
+L0C54: DEFB $06 ; Class-06 - A numeric expression must follow.
+ DEFB $00 ; Class-00 - No further operands.
+ DEFW L0EB5 ; Address: $0EB5; Address: GOSUB
+
+;; P-STOP
+L0C58: DEFB $00 ; Class-00 - No further operands.
+ DEFW L0CDC ; Address: $0CDC; Address: STOP
+
+;; P-RETURN
+L0C5B: DEFB $00 ; Class-00 - No further operands.
+ DEFW L0ED8 ; Address: $0ED8; Address: RETURN
+
+;; P-FOR
+L0C5E: DEFB $04 ; Class-04 - A single character variable must
+ ; follow.
+ DEFB $14 ; Separator: '='
+ DEFB $06 ; Class-06 - A numeric expression must follow.
+ DEFB $DF ; Separator: 'TO'
+ DEFB $06 ; Class-06 - A numeric expression must follow.
+ DEFB $05 ; Class-05 - Variable syntax checked entirely
+ ; by routine.
+ DEFW L0DB9 ; Address: $0DB9; Address: FOR
+
+;; P-NEXT
+L0C66: DEFB $04 ; Class-04 - A single character variable must
+ ; follow.
+ DEFB $00 ; Class-00 - No further operands.
+ DEFW L0E2E ; Address: $0E2E; Address: NEXT
+
+;; P-PRINT
+L0C6A: DEFB $05 ; Class-05 - Variable syntax checked entirely
+ ; by routine.
+ DEFW L0ACF ; Address: $0ACF; Address: PRINT
+
+;; P-INPUT
+L0C6D: DEFB $01 ; Class-01 - A variable is required.
+ DEFB $00 ; Class-00 - No further operands.
+ DEFW L0EE9 ; Address: $0EE9; Address: INPUT
+
+;; P-DIM
+L0C71: DEFB $05 ; Class-05 - Variable syntax checked entirely
+ ; by routine.
+ DEFW L1409 ; Address: $1409; Address: DIM
+
+;; P-REM
+L0C74: DEFB $05 ; Class-05 - Variable syntax checked entirely
+ ; by routine.
+ DEFW L0D6A ; Address: $0D6A; Address: REM
+
+;; P-NEW
+L0C77: DEFB $00 ; Class-00 - No further operands.
+ DEFW L03C3 ; Address: $03C3; Address: NEW
+
+;; P-RUN
+L0C7A: DEFB $03 ; Class-03 - A numeric expression may follow
+ ; else default to zero.
+ DEFW L0EAF ; Address: $0EAF; Address: RUN
+
+;; P-LIST
+L0C7D: DEFB $03 ; Class-03 - A numeric expression may follow
+ ; else default to zero.
+ DEFW L0730 ; Address: $0730; Address: LIST
+
+;; P-POKE
+L0C80: DEFB $06 ; Class-06 - A numeric expression must follow.
+ DEFB $1A ; Separator: ','
+ DEFB $06 ; Class-06 - A numeric expression must follow.
+ DEFB $00 ; Class-00 - No further operands.
+ DEFW L0E92 ; Address: $0E92; Address: POKE
+
+;; P-RAND
+L0C86: DEFB $03 ; Class-03 - A numeric expression may follow
+ ; else default to zero.
+ DEFW L0E6C ; Address: $0E6C; Address: RAND
+
+;; P-LOAD
+L0C89: DEFB $05 ; Class-05 - Variable syntax checked entirely
+ ; by routine.
+ DEFW L0340 ; Address: $0340; Address: LOAD
+
+;; P-SAVE
+L0C8C: DEFB $05 ; Class-05 - Variable syntax checked entirely
+ ; by routine.
+ DEFW L02F6 ; Address: $02F6; Address: SAVE
+
+;; P-CONT
+L0C8F: DEFB $00 ; Class-00 - No further operands.
+ DEFW L0E7C ; Address: $0E7C; Address: CONT
+
+;; P-CLEAR
+L0C92: DEFB $00 ; Class-00 - No further operands.
+ DEFW L149A ; Address: $149A; Address: CLEAR
+
+;; P-CLS
+L0C95: DEFB $00 ; Class-00 - No further operands.
+ DEFW L0A2A ; Address: $0A2A; Address: CLS
+
+;; P-PLOT
+L0C98: DEFB $06 ; Class-06 - A numeric expression must follow.
+ DEFB $1A ; Separator: ','
+ DEFB $06 ; Class-06 - A numeric expression must follow.
+ DEFB $00 ; Class-00 - No further operands.
+ DEFW L0BAF ; Address: $0BAF; Address: PLOT/UNP
+
+;; P-UNPLOT
+L0C9E: DEFB $06 ; Class-06 - A numeric expression must follow.
+ DEFB $1A ; Separator: ','
+ DEFB $06 ; Class-06 - A numeric expression must follow.
+ DEFB $00 ; Class-00 - No further operands.
+ DEFW L0BAF ; Address: $0BAF; Address: PLOT/UNP
+
+;; P-SCROLL
+L0CA4: DEFB $00 ; Class-00 - No further operands.
+ DEFW L0C0E ; Address: $0C0E; Address: SCROLL
+
+;; P-PAUSE
+L0CA7: DEFB $06 ; Class-06 - A numeric expression must follow.
+ DEFB $00 ; Class-00 - No further operands.
+ DEFW L0F32 ; Address: $0F32; Address: PAUSE
+
+;; P-SLOW
+L0CAB: DEFB $00 ; Class-00 - No further operands.
+ DEFW L0F2B ; Address: $0F2B; Address: SLOW
+
+;; P-FAST
+L0CAE: DEFB $00 ; Class-00 - No further operands.
+ DEFW L0F23 ; Address: $0F23; Address: FAST
+
+;; P-COPY
+L0CB1: DEFB $00 ; Class-00 - No further operands.
+ DEFW L0869 ; Address: $0869; Address: COPY
+
+;; P-LPRINT
+L0CB4: DEFB $05 ; Class-05 - Variable syntax checked entirely
+ ; by routine.
+ DEFW L0ACB ; Address: $0ACB; Address: LPRINT
+
+;; P-LLIST
+L0CB7: DEFB $03 ; Class-03 - A numeric expression may follow
+ ; else default to zero.
+ DEFW L072C ; Address: $072C; Address: LLIST
+
+
+; ---------------------------
+; THE 'LINE SCANNING' ROUTINE
+; ---------------------------
+;
+;
+
+;; LINE-SCAN
+L0CBA: LD (IY+$01),$01 ; sv FLAGS
+ CALL L0A73 ; routine E-LINE-NO
+
+;; LINE-RUN
+L0CC1: CALL L14BC ; routine SET-MIN
+ LD HL,$4000 ; sv ERR_NR
+ LD (HL),$FF ;
+ LD HL,$402D ; sv FLAGX
+ BIT 5,(HL) ;
+ JR Z,L0CDE ; to LINE-NULL
+
+ CP $E3 ; 'STOP' ?
+ LD A,(HL) ;
+ JP NZ,L0D6F ; to INPUT-REP
+
+ CALL L0DA6 ; routine SYNTAX-Z
+ RET Z ;
+
+
+ RST 08H ; ERROR-1
+ DEFB $0C ; Error Report: BREAK - CONT repeats
+
+
+; --------------------------
+; THE 'STOP' COMMAND ROUTINE
+; --------------------------
+;
+;
+
+;; STOP
+L0CDC: RST 08H ; ERROR-1
+ DEFB $08 ; Error Report: STOP statement
+
+; ---
+
+; the interpretation of a line continues with a check for just spaces
+; followed by a carriage return.
+; The IF command also branches here with a true value to execute the
+; statement after the THEN but the statement can be null so
+; 10 IF 1 = 1 THEN
+; passes syntax (on all ZX computers).
+
+;; LINE-NULL
+L0CDE: RST 18H ; GET-CHAR
+ LD B,$00 ; prepare to index - early.
+ CP $76 ; compare to NEWLINE.
+ RET Z ; return if so.
+
+ LD C,A ; transfer character to C.
+
+ RST 20H ; NEXT-CHAR advances.
+ LD A,C ; character to A
+ SUB $E1 ; subtract 'LPRINT' - lowest command.
+ JR C,L0D26 ; forward if less to REPORT-C2
+
+ LD C,A ; reduced token to C
+ LD HL,L0C29 ; set HL to address of offset table.
+ ADD HL,BC ; index into offset table.
+ LD C,(HL) ; fetch offset
+ ADD HL,BC ; index into parameter table.
+ JR L0CF7 ; to GET-PARAM
+
+; ---
+
+;; SCAN-LOOP
+L0CF4: LD HL,($4030) ; sv T_ADDR_lo
+
+; -> Entry Point to Scanning Loop
+
+;; GET-PARAM
+L0CF7: LD A,(HL) ;
+ INC HL ;
+ LD ($4030),HL ; sv T_ADDR_lo
+
+ LD BC,L0CF4 ; Address: SCAN-LOOP
+ PUSH BC ; is pushed on machine stack.
+
+ LD C,A ;
+ CP $0B ;
+ JR NC,L0D10 ; to SEPARATOR
+
+ LD HL,L0D16 ; class-tbl - the address of the class table.
+ LD B,$00 ;
+ ADD HL,BC ;
+ LD C,(HL) ;
+ ADD HL,BC ;
+ PUSH HL ;
+
+ RST 18H ; GET-CHAR
+ RET ; indirect jump to class routine and
+ ; by subsequent RET to SCAN-LOOP.
+
+; -----------------------
+; THE 'SEPARATOR' ROUTINE
+; -----------------------
+
+;; SEPARATOR
+L0D10: RST 18H ; GET-CHAR
+ CP C ;
+ JR NZ,L0D26 ; to REPORT-C2
+ ; 'Nonsense in BASIC'
+
+ RST 20H ; NEXT-CHAR
+ RET ; return
+
+
+; -------------------------
+; THE 'COMMAND CLASS' TABLE
+; -------------------------
+;
+
+;; class-tbl
+L0D16: DEFB L0D2D - $ ; 17 offset to; Address: CLASS-0
+ DEFB L0D3C - $ ; 25 offset to; Address: CLASS-1
+ DEFB L0D6B - $ ; 53 offset to; Address: CLASS-2
+ DEFB L0D28 - $ ; 0F offset to; Address: CLASS-3
+ DEFB L0D85 - $ ; 6B offset to; Address: CLASS-4
+ DEFB L0D2E - $ ; 13 offset to; Address: CLASS-5
+ DEFB L0D92 - $ ; 76 offset to; Address: CLASS-6
+
+
+; --------------------------
+; THE 'CHECK END' SUBROUTINE
+; --------------------------
+; Check for end of statement and that no spurious characters occur after
+; a correctly parsed statement. Since only one statement is allowed on each
+; line, the only character that may follow a statement is a NEWLINE.
+;
+
+;; CHECK-END
+L0D1D: CALL L0DA6 ; routine SYNTAX-Z
+ RET NZ ; return in runtime.
+
+ POP BC ; else drop return address.
+
+;; CHECK-2
+L0D22: LD A,(HL) ; fetch character.
+ CP $76 ; compare to NEWLINE.
+ RET Z ; return if so.
+
+;; REPORT-C2
+L0D26: JR L0D9A ; to REPORT-C
+ ; 'Nonsense in BASIC'
+
+; --------------------------
+; COMMAND CLASSES 03, 00, 05
+; --------------------------
+;
+;
+
+;; CLASS-3
+L0D28: CP $76 ;
+ CALL L0D9C ; routine NO-TO-STK
+
+;; CLASS-0
+L0D2D: CP A ;
+
+;; CLASS-5
+L0D2E: POP BC ;
+ CALL Z,L0D1D ; routine CHECK-END
+ EX DE,HL ;
+ LD HL,($4030) ; sv T_ADDR_lo
+ LD C,(HL) ;
+ INC HL ;
+ LD B,(HL) ;
+ EX DE,HL ;
+
+;; CLASS-END
+L0D3A: PUSH BC ;
+ RET ;
+
+; ------------------------------
+; COMMAND CLASSES 01, 02, 04, 06
+; ------------------------------
+;
+;
+
+;; CLASS-1
+L0D3C: CALL L111C ; routine LOOK-VARS
+
+;; CLASS-4-2
+L0D3F: LD (IY+$2D),$00 ; sv FLAGX
+ JR NC,L0D4D ; to SET-STK
+
+ SET 1,(IY+$2D) ; sv FLAGX
+ JR NZ,L0D63 ; to SET-STRLN
+
+
+;; REPORT-2
+L0D4B: RST 08H ; ERROR-1
+ DEFB $01 ; Error Report: Variable not found
+
+; ---
+
+;; SET-STK
+L0D4D: CALL Z,L11A7 ; routine STK-VAR
+ BIT 6,(IY+$01) ; sv FLAGS - Numeric or string result?
+ JR NZ,L0D63 ; to SET-STRLN
+
+ XOR A ;
+ CALL L0DA6 ; routine SYNTAX-Z
+ CALL NZ,L13F8 ; routine STK-FETCH
+ LD HL,$402D ; sv FLAGX
+ OR (HL) ;
+ LD (HL),A ;
+ EX DE,HL ;
+
+;; SET-STRLN
+L0D63: LD ($402E),BC ; sv STRLEN_lo
+ LD ($4012),HL ; sv DEST-lo
+
+; THE 'REM' COMMAND ROUTINE
+
+;; REM
+L0D6A: RET ;
+
+; ---
+
+;; CLASS-2
+L0D6B: POP BC ;
+ LD A,($4001) ; sv FLAGS
+
+;; INPUT-REP
+L0D6F: PUSH AF ;
+ CALL L0F55 ; routine SCANNING
+ POP AF ;
+ LD BC,L1321 ; Address: LET
+ LD D,(IY+$01) ; sv FLAGS
+ XOR D ;
+ AND $40 ;
+ JR NZ,L0D9A ; to REPORT-C
+
+ BIT 7,D ;
+ JR NZ,L0D3A ; to CLASS-END
+
+ JR L0D22 ; to CHECK-2
+
+; ---
+
+;; CLASS-4
+L0D85: CALL L111C ; routine LOOK-VARS
+ PUSH AF ;
+ LD A,C ;
+ OR $9F ;
+ INC A ;
+ JR NZ,L0D9A ; to REPORT-C
+
+ POP AF ;
+ JR L0D3F ; to CLASS-4-2
+
+; ---
+
+;; CLASS-6
+L0D92: CALL L0F55 ; routine SCANNING
+ BIT 6,(IY+$01) ; sv FLAGS - Numeric or string result?
+ RET NZ ;
+
+
+;; REPORT-C
+L0D9A: RST 08H ; ERROR-1
+ DEFB $0B ; Error Report: Nonsense in BASIC
+
+; --------------------------------
+; THE 'NUMBER TO STACK' SUBROUTINE
+; --------------------------------
+;
+;
+
+;; NO-TO-STK
+L0D9C: JR NZ,L0D92 ; back to CLASS-6 with a non-zero number.
+
+ CALL L0DA6 ; routine SYNTAX-Z
+ RET Z ; return if checking syntax.
+
+; in runtime a zero default is placed on the calculator stack.
+
+ RST 28H ;; FP-CALC
+ DEFB $A0 ;;stk-zero
+ DEFB $34 ;;end-calc
+
+ RET ; return.
+
+; -------------------------
+; THE 'SYNTAX-Z' SUBROUTINE
+; -------------------------
+; This routine returns with zero flag set if checking syntax.
+; Calling this routine uses three instruction bytes compared to four if the
+; bit test is implemented inline.
+
+;; SYNTAX-Z
+L0DA6: BIT 7,(IY+$01) ; test FLAGS - checking syntax only?
+ RET ; return.
+
+; ------------------------
+; THE 'IF' COMMAND ROUTINE
+; ------------------------
+; In runtime, the class routines have evaluated the test expression and
+; the result, true or false, is on the stack.
+
+;; IF
+L0DAB: CALL L0DA6 ; routine SYNTAX-Z
+ JR Z,L0DB6 ; forward if checking syntax to IF-END
+
+; else delete the Boolean value on the calculator stack.
+
+ RST 28H ;; FP-CALC
+ DEFB $02 ;;delete
+ DEFB $34 ;;end-calc
+
+; register DE points to exponent of floating point value.
+
+ LD A,(DE) ; fetch exponent.
+ AND A ; test for zero - FALSE.
+ RET Z ; return if so.
+
+;; IF-END
+L0DB6: JP L0CDE ; jump back to LINE-NULL
+
+; -------------------------
+; THE 'FOR' COMMAND ROUTINE
+; -------------------------
+;
+;
+
+;; FOR
+L0DB9: CP $E0 ; is current character 'STEP' ?
+ JR NZ,L0DC6 ; forward if not to F-USE-ONE
+
+
+ RST 20H ; NEXT-CHAR
+ CALL L0D92 ; routine CLASS-6 stacks the number
+ CALL L0D1D ; routine CHECK-END
+ JR L0DCC ; forward to F-REORDER
+
+; ---
+
+;; F-USE-ONE
+L0DC6: CALL L0D1D ; routine CHECK-END
+
+ RST 28H ;; FP-CALC
+ DEFB $A1 ;;stk-one
+ DEFB $34 ;;end-calc
+
+
+
+;; F-REORDER
+L0DCC: RST 28H ;; FP-CALC v, l, s.
+ DEFB $C0 ;;st-mem-0 v, l, s.
+ DEFB $02 ;;delete v, l.
+ DEFB $01 ;;exchange l, v.
+ DEFB $E0 ;;get-mem-0 l, v, s.
+ DEFB $01 ;;exchange l, s, v.
+ DEFB $34 ;;end-calc l, s, v.
+
+ CALL L1321 ; routine LET
+
+ LD ($401F),HL ; set MEM to address variable.
+ DEC HL ; point to letter.
+ LD A,(HL) ;
+ SET 7,(HL) ;
+ LD BC,$0006 ;
+ ADD HL,BC ;
+ RLCA ;
+ JR C,L0DEA ; to F-LMT-STP
+
+ SLA C ;
+ CALL L099E ; routine MAKE-ROOM
+ INC HL ;
+
+;; F-LMT-STP
+L0DEA: PUSH HL ;
+
+ RST 28H ;; FP-CALC
+ DEFB $02 ;;delete
+ DEFB $02 ;;delete
+ DEFB $34 ;;end-calc
+
+ POP HL ;
+ EX DE,HL ;
+
+ LD C,$0A ; ten bytes to be moved.
+ LDIR ; copy bytes
+
+ LD HL,($4007) ; set HL to system variable PPC current line.
+ EX DE,HL ; transfer to DE, variable pointer to HL.
+ INC DE ; loop start will be this line + 1 at least.
+ LD (HL),E ;
+ INC HL ;
+ LD (HL),D ;
+ CALL L0E5A ; routine NEXT-LOOP considers an initial pass.
+ RET NC ; return if possible.
+
+; else program continues from point following matching NEXT.
+
+ BIT 7,(IY+$08) ; test PPC_hi
+ RET NZ ; return if over 32767 ???
+
+ LD B,(IY+$2E) ; fetch variable name from STRLEN_lo
+ RES 6,B ; make a true letter.
+ LD HL,($4029) ; set HL from NXTLIN
+
+; now enter a loop to look for matching next.
+
+;; NXTLIN-NO
+L0E0E: LD A,(HL) ; fetch high byte of line number.
+ AND $C0 ; mask off low bits $3F
+ JR NZ,L0E2A ; forward at end of program to FOR-END
+
+ PUSH BC ; save letter
+ CALL L09F2 ; routine NEXT-ONE finds next line.
+ POP BC ; restore letter
+
+ INC HL ; step past low byte
+ INC HL ; past the
+ INC HL ; line length.
+ CALL L004C ; routine TEMP-PTR1 sets CH_ADD
+
+ RST 18H ; GET-CHAR
+ CP $F3 ; compare to 'NEXT'.
+ EX DE,HL ; next line to HL.
+ JR NZ,L0E0E ; back with no match to NXTLIN-NO
+
+;
+
+ EX DE,HL ; restore pointer.
+
+ RST 20H ; NEXT-CHAR advances and gets letter in A.
+ EX DE,HL ; save pointer
+ CP B ; compare to variable name.
+ JR NZ,L0E0E ; back with mismatch to NXTLIN-NO
+
+;; FOR-END
+L0E2A: LD ($4029),HL ; update system variable NXTLIN
+ RET ; return.
+
+; --------------------------
+; THE 'NEXT' COMMAND ROUTINE
+; --------------------------
+;
+;
+
+;; NEXT
+L0E2E: BIT 1,(IY+$2D) ; sv FLAGX
+ JP NZ,L0D4B ; to REPORT-2
+
+ LD HL,($4012) ; DEST
+ BIT 7,(HL) ;
+ JR Z,L0E58 ; to REPORT-1
+
+ INC HL ;
+ LD ($401F),HL ; sv MEM_lo
+
+ RST 28H ;; FP-CALC
+ DEFB $E0 ;;get-mem-0
+ DEFB $E2 ;;get-mem-2
+ DEFB $0F ;;addition
+ DEFB $C0 ;;st-mem-0
+ DEFB $02 ;;delete
+ DEFB $34 ;;end-calc
+
+ CALL L0E5A ; routine NEXT-LOOP
+ RET C ;
+
+ LD HL,($401F) ; sv MEM_lo
+ LD DE,$000F ;
+ ADD HL,DE ;
+ LD E,(HL) ;
+ INC HL ;
+ LD D,(HL) ;
+ EX DE,HL ;
+ JR L0E86 ; to GOTO-2
+
+; ---
+
+
+;; REPORT-1
+L0E58: RST 08H ; ERROR-1
+ DEFB $00 ; Error Report: NEXT without FOR
+
+
+; --------------------------
+; THE 'NEXT-LOOP' SUBROUTINE
+; --------------------------
+;
+;
+
+;; NEXT-LOOP
+L0E5A: RST 28H ;; FP-CALC
+ DEFB $E1 ;;get-mem-1
+ DEFB $E0 ;;get-mem-0
+ DEFB $E2 ;;get-mem-2
+ DEFB $32 ;;less-0
+ DEFB $00 ;;jump-true
+ DEFB $02 ;;to L0E62, LMT-V-VAL
+
+ DEFB $01 ;;exchange
+
+;; LMT-V-VAL
+L0E62: DEFB $03 ;;subtract
+ DEFB $33 ;;greater-0
+ DEFB $00 ;;jump-true
+ DEFB $04 ;;to L0E69, IMPOSS
+
+ DEFB $34 ;;end-calc
+
+ AND A ; clear carry flag
+ RET ; return.
+
+; ---
+
+
+;; IMPOSS
+L0E69: DEFB $34 ;;end-calc
+
+ SCF ; set carry flag
+ RET ; return.
+
+; --------------------------
+; THE 'RAND' COMMAND ROUTINE
+; --------------------------
+; The keyword was 'RANDOMISE' on the ZX80, is 'RAND' here on the ZX81 and
+; becomes 'RANDOMIZE' on the ZX Spectrum.
+; In all invocations the procedure is the same - to set the SEED system variable
+; with a supplied integer value or to use a time-based value if no number, or
+; zero, is supplied.
+
+;; RAND
+L0E6C: CALL L0EA7 ; routine FIND-INT
+ LD A,B ; test value
+ OR C ; for zero
+ JR NZ,L0E77 ; forward if not zero to SET-SEED
+
+ LD BC,($4034) ; fetch value of FRAMES system variable.
+
+;; SET-SEED
+L0E77: LD ($4032),BC ; update the SEED system variable.
+ RET ; return.
+
+; --------------------------
+; THE 'CONT' COMMAND ROUTINE
+; --------------------------
+; Another abbreviated command. ROM space was really tight.
+; CONTINUE at the line number that was set when break was pressed.
+; Sometimes the current line, sometimes the next line.
+
+;; CONT
+L0E7C: LD HL,($402B) ; set HL from system variable OLDPPC
+ JR L0E86 ; forward to GOTO-2
+
+; --------------------------
+; THE 'GOTO' COMMAND ROUTINE
+; --------------------------
+; This token also suffered from the shortage of room and there is no space
+; getween GO and TO as there is on the ZX80 and ZX Spectrum. The same also
+; applies to the GOSUB keyword.
+
+;; GOTO
+L0E81: CALL L0EA7 ; routine FIND-INT
+ LD H,B ;
+ LD L,C ;
+
+;; GOTO-2
+L0E86: LD A,H ;
+ CP $F0 ;
+ JR NC,L0EAD ; to REPORT-B
+
+ CALL L09D8 ; routine LINE-ADDR
+ LD ($4029),HL ; sv NXTLIN_lo
+ RET ;
+
+; --------------------------
+; THE 'POKE' COMMAND ROUTINE
+; --------------------------
+;
+;
+
+;; POKE
+L0E92: CALL L15CD ; routine FP-TO-A
+ JR C,L0EAD ; forward, with overflow, to REPORT-B
+
+ JR Z,L0E9B ; forward, if positive, to POKE-SAVE
+
+ NEG ; negate
+
+;; POKE-SAVE
+L0E9B: PUSH AF ; preserve value.
+ CALL L0EA7 ; routine FIND-INT gets address in BC
+ ; invoking the error routine with overflow
+ ; or a negative number.
+ POP AF ; restore value.
+
+; Note. the next two instructions are legacy code from the ZX80 and
+; inappropriate here.
+
+ BIT 7,(IY+$00) ; test ERR_NR - is it still $FF ?
+ RET Z ; return with error.
+
+ LD (BC),A ; update the address contents.
+ RET ; return.
+
+; -----------------------------
+; THE 'FIND INTEGER' SUBROUTINE
+; -----------------------------
+;
+;
+
+;; FIND-INT
+L0EA7: CALL L158A ; routine FP-TO-BC
+ JR C,L0EAD ; forward with overflow to REPORT-B
+
+ RET Z ; return if positive (0-65535).
+
+
+;; REPORT-B
+L0EAD: RST 08H ; ERROR-1
+ DEFB $0A ; Error Report: Integer out of range
+
+; -------------------------
+; THE 'RUN' COMMAND ROUTINE
+; -------------------------
+;
+;
+
+;; RUN
+L0EAF: CALL L0E81 ; routine GOTO
+ JP L149A ; to CLEAR
+
+; ---------------------------
+; THE 'GOSUB' COMMAND ROUTINE
+; ---------------------------
+;
+;
+
+;; GOSUB
+L0EB5: LD HL,($4007) ; sv PPC_lo
+ INC HL ;
+ EX (SP),HL ;
+ PUSH HL ;
+ LD ($4002),SP ; set the error stack pointer - ERR_SP
+ CALL L0E81 ; routine GOTO
+ LD BC,$0006 ;
+
+; --------------------------
+; THE 'TEST ROOM' SUBROUTINE
+; --------------------------
+;
+;
+
+;; TEST-ROOM
+L0EC5: LD HL,($401C) ; sv STKEND_lo
+ ADD HL,BC ;
+ JR C,L0ED3 ; to REPORT-4
+
+ EX DE,HL ;
+ LD HL,$0024 ;
+ ADD HL,DE ;
+ SBC HL,SP ;
+ RET C ;
+
+;; REPORT-4
+L0ED3: LD L,$03 ;
+ JP L0058 ; to ERROR-3
+
+; ----------------------------
+; THE 'RETURN' COMMAND ROUTINE
+; ----------------------------
+;
+;
+
+;; RETURN
+L0ED8: POP HL ;
+ EX (SP),HL ;
+ LD A,H ;
+ CP $3E ;
+ JR Z,L0EE5 ; to REPORT-7
+
+ LD ($4002),SP ; sv ERR_SP_lo
+ JR L0E86 ; back to GOTO-2
+
+; ---
+
+;; REPORT-7
+L0EE5: EX (SP),HL ;
+ PUSH HL ;
+
+ RST 08H ; ERROR-1
+ DEFB $06 ; Error Report: RETURN without GOSUB
+
+; ---------------------------
+; THE 'INPUT' COMMAND ROUTINE
+; ---------------------------
+;
+;
+
+;; INPUT
+L0EE9: BIT 7,(IY+$08) ; sv PPC_hi
+ JR NZ,L0F21 ; to REPORT-8
+
+ CALL L14A3 ; routine X-TEMP
+ LD HL,$402D ; sv FLAGX
+ SET 5,(HL) ;
+ RES 6,(HL) ;
+ LD A,($4001) ; sv FLAGS
+ AND $40 ;
+ LD BC,$0002 ;
+ JR NZ,L0F05 ; to PROMPT
+
+ LD C,$04 ;
+
+;; PROMPT
+L0F05: OR (HL) ;
+ LD (HL),A ;
+
+ RST 30H ; BC-SPACES
+ LD (HL),$76 ;
+ LD A,C ;
+ RRCA ;
+ RRCA ;
+ JR C,L0F14 ; to ENTER-CUR
+
+ LD A,$0B ;
+ LD (DE),A ;
+ DEC HL ;
+ LD (HL),A ;
+
+;; ENTER-CUR
+L0F14: DEC HL ;
+ LD (HL),$7F ;
+ LD HL,($4039) ; sv S_POSN_x
+ LD ($4030),HL ; sv T_ADDR_lo
+ POP HL ;
+ JP L0472 ; to LOWER
+
+; ---
+
+;; REPORT-8
+L0F21: RST 08H ; ERROR-1
+ DEFB $07 ; Error Report: End of file
+
+; ---------------------------
+; THE 'PAUSE' COMMAND ROUTINE
+; ---------------------------
+;
+;
+
+;; FAST
+L0F23: CALL L02E7 ; routine SET-FAST
+ RES 6,(IY+$3B) ; sv CDFLAG
+ RET ; return.
+
+; --------------------------
+; THE 'SLOW' COMMAND ROUTINE
+; --------------------------
+;
+;
+
+;; SLOW
+L0F2B: SET 6,(IY+$3B) ; sv CDFLAG
+ JP L0207 ; to SLOW/FAST
+
+; ---------------------------
+; THE 'PAUSE' COMMAND ROUTINE
+; ---------------------------
+
+;; PAUSE
+L0F32: CALL L0EA7 ; routine FIND-INT
+ CALL L02E7 ; routine SET-FAST
+ LD H,B ;
+ LD L,C ;
+ CALL L022D ; routine DISPLAY-P
+
+ LD (IY+$35),$FF ; sv FRAMES_hi
+
+ CALL L0207 ; routine SLOW/FAST
+ JR L0F4B ; routine DEBOUNCE
+
+; ----------------------
+; THE 'BREAK' SUBROUTINE
+; ----------------------
+;
+;
+
+;; BREAK-1
+L0F46: LD A,$7F ; read port $7FFE - keys B,N,M,.,SPACE.
+ IN A,($FE) ;
+ RRA ; carry will be set if space not pressed.
+
+; -------------------------
+; THE 'DEBOUNCE' SUBROUTINE
+; -------------------------
+;
+;
+
+;; DEBOUNCE
+L0F4B: RES 0,(IY+$3B) ; update system variable CDFLAG
+ LD A,$FF ;
+ LD ($4027),A ; update system variable DEBOUNCE
+ RET ; return.
+
+
+; -------------------------
+; THE 'SCANNING' SUBROUTINE
+; -------------------------
+; This recursive routine is where the ZX81 gets its power. Provided there is
+; enough memory it can evaluate an expression of unlimited complexity.
+; Note. there is no unary plus so, as on the ZX80, PRINT +1 gives a syntax error.
+; PRINT +1 works on the Spectrum but so too does PRINT + "STRING".
+
+;; SCANNING
+L0F55: RST 18H ; GET-CHAR
+ LD B,$00 ; set B register to zero.
+ PUSH BC ; stack zero as a priority end-marker.
+
+;; S-LOOP-1
+L0F59: CP $40 ; compare to the 'RND' character
+ JR NZ,L0F8C ; forward, if not, to S-TEST-PI
+
+; ------------------
+; THE 'RND' FUNCTION
+; ------------------
+
+ CALL L0DA6 ; routine SYNTAX-Z
+ JR Z,L0F8A ; forward if checking syntax to S-JPI-END
+
+ LD BC,($4032) ; sv SEED_lo
+ CALL L1520 ; routine STACK-BC
+
+ RST 28H ;; FP-CALC
+ DEFB $A1 ;;stk-one
+ DEFB $0F ;;addition
+ DEFB $30 ;;stk-data
+ DEFB $37 ;;Exponent: $87, Bytes: 1
+ DEFB $16 ;;(+00,+00,+00)
+ DEFB $04 ;;multiply
+ DEFB $30 ;;stk-data
+ DEFB $80 ;;Bytes: 3
+ DEFB $41 ;;Exponent $91
+ DEFB $00,$00,$80 ;;(+00)
+ DEFB $2E ;;n-mod-m
+ DEFB $02 ;;delete
+ DEFB $A1 ;;stk-one
+ DEFB $03 ;;subtract
+ DEFB $2D ;;duplicate
+ DEFB $34 ;;end-calc
+
+ CALL L158A ; routine FP-TO-BC
+ LD ($4032),BC ; update the SEED system variable.
+ LD A,(HL) ; HL addresses the exponent of the last value.
+ AND A ; test for zero
+ JR Z,L0F8A ; forward, if so, to S-JPI-END
+
+ SUB $10 ; else reduce exponent by sixteen
+ LD (HL),A ; thus dividing by 65536 for last value.
+
+;; S-JPI-END
+L0F8A: JR L0F99 ; forward to S-PI-END
+
+; ---
+
+;; S-TEST-PI
+L0F8C: CP $42 ; the 'PI' character
+ JR NZ,L0F9D ; forward, if not, to S-TST-INK
+
+; -------------------
+; THE 'PI' EVALUATION
+; -------------------
+
+ CALL L0DA6 ; routine SYNTAX-Z
+ JR Z,L0F99 ; forward if checking syntax to S-PI-END
+
+
+ RST 28H ;; FP-CALC
+ DEFB $A3 ;;stk-pi/2
+ DEFB $34 ;;end-calc
+
+ INC (HL) ; double the exponent giving PI on the stack.
+
+;; S-PI-END
+L0F99: RST 20H ; NEXT-CHAR advances character pointer.
+
+ JP L1083 ; jump forward to S-NUMERIC to set the flag
+ ; to signal numeric result before advancing.
+
+; ---
+
+;; S-TST-INK
+L0F9D: CP $41 ; compare to character 'INKEY$'
+ JR NZ,L0FB2 ; forward, if not, to S-ALPHANUM
+
+; -----------------------
+; THE 'INKEY$' EVALUATION
+; -----------------------
+
+ CALL L02BB ; routine KEYBOARD
+ LD B,H ;
+ LD C,L ;
+ LD D,C ;
+ INC D ;
+ CALL NZ,L07BD ; routine DECODE
+ LD A,D ;
+ ADC A,D ;
+ LD B,D ;
+ LD C,A ;
+ EX DE,HL ;
+ JR L0FED ; forward to S-STRING
+
+; ---
+
+;; S-ALPHANUM
+L0FB2: CALL L14D2 ; routine ALPHANUM
+ JR C,L1025 ; forward, if alphanumeric to S-LTR-DGT
+
+ CP $1B ; is character a '.' ?
+ JP Z,L1047 ; jump forward if so to S-DECIMAL
+
+ LD BC,$09D8 ; prepare priority 09, operation 'subtract'
+ CP $16 ; is character unary minus '-' ?
+ JR Z,L1020 ; forward, if so, to S-PUSH-PO
+
+ CP $10 ; is character a '(' ?
+ JR NZ,L0FD6 ; forward if not to S-QUOTE
+
+ CALL L0049 ; routine CH-ADD+1 advances character pointer.
+
+ CALL L0F55 ; recursively call routine SCANNING to
+ ; evaluate the sub-expression.
+
+ CP $11 ; is subsequent character a ')' ?
+ JR NZ,L0FFF ; forward if not to S-RPT-C
+
+
+ CALL L0049 ; routine CH-ADD+1 advances.
+ JR L0FF8 ; relative jump to S-JP-CONT3 and then S-CONT3
+
+; ---
+
+; consider a quoted string e.g. PRINT "Hooray!"
+; Note. quotes are not allowed within a string.
+
+;; S-QUOTE
+L0FD6: CP $0B ; is character a quote (") ?
+ JR NZ,L1002 ; forward, if not, to S-FUNCTION
+
+ CALL L0049 ; routine CH-ADD+1 advances
+ PUSH HL ; * save start of string.
+ JR L0FE3 ; forward to S-QUOTE-S
+
+; ---
+
+
+;; S-Q-AGAIN
+L0FE0: CALL L0049 ; routine CH-ADD+1
+
+;; S-QUOTE-S
+L0FE3: CP $0B ; is character a '"' ?
+ JR NZ,L0FFB ; forward if not to S-Q-NL
+
+ POP DE ; * retrieve start of string
+ AND A ; prepare to subtract.
+ SBC HL,DE ; subtract start from current position.
+ LD B,H ; transfer this length
+ LD C,L ; to the BC register pair.
+
+;; S-STRING
+L0FED: LD HL,$4001 ; address system variable FLAGS
+ RES 6,(HL) ; signal string result
+ BIT 7,(HL) ; test if checking syntax.
+
+ CALL NZ,L12C3 ; in run-time routine STK-STO-$ stacks the
+ ; string descriptor - start DE, length BC.
+
+ RST 20H ; NEXT-CHAR advances pointer.
+
+;; S-J-CONT-3
+L0FF8: JP L1088 ; jump to S-CONT-3
+
+; ---
+
+; A string with no terminating quote has to be considered.
+
+;; S-Q-NL
+L0FFB: CP $76 ; compare to NEWLINE
+ JR NZ,L0FE0 ; loop back if not to S-Q-AGAIN
+
+;; S-RPT-C
+L0FFF: JP L0D9A ; to REPORT-C
+
+; ---
+
+;; S-FUNCTION
+L1002: SUB $C4 ; subtract 'CODE' reducing codes
+ ; CODE thru '<>' to range $00 - $XX
+ JR C,L0FFF ; back, if less, to S-RPT-C
+
+; test for NOT the last function in character set.
+
+ LD BC,$04EC ; prepare priority $04, operation 'not'
+ CP $13 ; compare to 'NOT' ( - CODE)
+ JR Z,L1020 ; forward, if so, to S-PUSH-PO
+
+ JR NC,L0FFF ; back with anything higher to S-RPT-C
+
+; else is a function 'CODE' thru 'CHR$'
+
+ LD B,$10 ; priority sixteen binds all functions to
+ ; arguments removing the need for brackets.
+
+ ADD A,$D9 ; add $D9 to give range $D9 thru $EB
+ ; bit 6 is set to show numeric argument.
+ ; bit 7 is set to show numeric result.
+
+; now adjust these default argument/result indicators.
+
+ LD C,A ; save code in C
+
+ CP $DC ; separate 'CODE', 'VAL', 'LEN'
+ JR NC,L101A ; skip forward if string operand to S-NO-TO-$
+
+ RES 6,C ; signal string operand.
+
+;; S-NO-TO-$
+L101A: CP $EA ; isolate top of range 'STR$' and 'CHR$'
+ JR C,L1020 ; skip forward with others to S-PUSH-PO
+
+ RES 7,C ; signal string result.
+
+;; S-PUSH-PO
+L1020: PUSH BC ; push the priority/operation
+
+ RST 20H ; NEXT-CHAR
+ JP L0F59 ; jump back to S-LOOP-1
+
+; ---
+
+;; S-LTR-DGT
+L1025: CP $26 ; compare to 'A'.
+ JR C,L1047 ; forward if less to S-DECIMAL
+
+ CALL L111C ; routine LOOK-VARS
+ JP C,L0D4B ; back if not found to REPORT-2
+ ; a variable is always 'found' when checking
+ ; syntax.
+
+ CALL Z,L11A7 ; routine STK-VAR stacks string parameters or
+ ; returns cell location if numeric.
+
+ LD A,($4001) ; fetch FLAGS
+ CP $C0 ; compare to numeric result/numeric operand
+ JR C,L1087 ; forward if not numeric to S-CONT-2
+
+ INC HL ; address numeric contents of variable.
+ LD DE,($401C) ; set destination to STKEND
+ CALL L19F6 ; routine MOVE-FP stacks the five bytes
+ EX DE,HL ; transfer new free location from DE to HL.
+ LD ($401C),HL ; update STKEND system variable.
+ JR L1087 ; forward to S-CONT-2
+
+; ---
+
+; The Scanning Decimal routine is invoked when a decimal point or digit is
+; found in the expression.
+; When checking syntax, then the 'hidden floating point' form is placed
+; after the number in the BASIC line.
+; In run-time, the digits are skipped and the floating point number is picked
+; up.
+
+;; S-DECIMAL
+L1047: CALL L0DA6 ; routine SYNTAX-Z
+ JR NZ,L106F ; forward in run-time to S-STK-DEC
+
+ CALL L14D9 ; routine DEC-TO-FP
+
+ RST 18H ; GET-CHAR advances HL past digits
+ LD BC,$0006 ; six locations are required.
+ CALL L099E ; routine MAKE-ROOM
+ INC HL ; point to first new location
+ LD (HL),$7E ; insert the number marker 126 decimal.
+ INC HL ; increment
+ EX DE,HL ; transfer destination to DE.
+ LD HL,($401C) ; set HL from STKEND which points to the
+ ; first location after the 'last value'
+ LD C,$05 ; five bytes to move.
+ AND A ; clear carry.
+ SBC HL,BC ; subtract five pointing to 'last value'.
+ LD ($401C),HL ; update STKEND thereby 'deleting the value.
+
+ LDIR ; copy the five value bytes.
+
+ EX DE,HL ; basic pointer to HL which may be white-space
+ ; following the number.
+ DEC HL ; now points to last of five bytes.
+ CALL L004C ; routine TEMP-PTR1 advances the character
+ ; address skipping any white-space.
+ JR L1083 ; forward to S-NUMERIC
+ ; to signal a numeric result.
+
+; ---
+
+; In run-time the branch is here when a digit or point is encountered.
+
+;; S-STK-DEC
+L106F: RST 20H ; NEXT-CHAR
+ CP $7E ; compare to 'number marker'
+ JR NZ,L106F ; loop back until found to S-STK-DEC
+ ; skipping all the digits.
+
+ INC HL ; point to first of five hidden bytes.
+ LD DE,($401C) ; set destination from STKEND system variable
+ CALL L19F6 ; routine MOVE-FP stacks the number.
+ LD ($401C),DE ; update system variable STKEND.
+ LD ($4016),HL ; update system variable CH_ADD.
+
+;; S-NUMERIC
+L1083: SET 6,(IY+$01) ; update FLAGS - Signal numeric result
+
+;; S-CONT-2
+L1087: RST 18H ; GET-CHAR
+
+;; S-CONT-3
+L1088: CP $10 ; compare to opening bracket '('
+ JR NZ,L1098 ; forward if not to S-OPERTR
+
+ BIT 6,(IY+$01) ; test FLAGS - Numeric or string result?
+ JR NZ,L10BC ; forward if numeric to S-LOOP
+
+; else is a string
+
+ CALL L1263 ; routine SLICING
+
+ RST 20H ; NEXT-CHAR
+ JR L1088 ; back to S-CONT-3
+
+; ---
+
+; the character is now manipulated to form an equivalent in the table of
+; calculator literals. This is quite cumbersome and in the ZX Spectrum a
+; simple look-up table was introduced at this point.
+
+;; S-OPERTR
+L1098: LD BC,$00C3 ; prepare operator 'subtract' as default.
+ ; also set B to zero for later indexing.
+
+ CP $12 ; is character '>' ?
+ JR C,L10BC ; forward if less to S-LOOP as
+ ; we have reached end of meaningful expression
+
+ SUB $16 ; is character '-' ?
+ JR NC,L10A7 ; forward with - * / and '**' '<>' to SUBMLTDIV
+
+ ADD A,$0D ; increase others by thirteen
+ ; $09 '>' thru $0C '+'
+ JR L10B5 ; forward to GET-PRIO
+
+; ---
+
+;; SUBMLTDIV
+L10A7: CP $03 ; isolate $00 '-', $01 '*', $02 '/'
+ JR C,L10B5 ; forward if so to GET-PRIO
+
+; else possibly originally $D8 '**' thru $DD '<>' already reduced by $16
+
+ SUB $C2 ; giving range $00 to $05
+ JR C,L10BC ; forward if less to S-LOOP
+
+ CP $06 ; test the upper limit for nonsense also
+ JR NC,L10BC ; forward if so to S-LOOP
+
+ ADD A,$03 ; increase by 3 to give combined operators of
+
+ ; $00 '-'
+ ; $01 '*'
+ ; $02 '/'
+
+ ; $03 '**'
+ ; $04 'OR'
+ ; $05 'AND'
+ ; $06 '<='
+ ; $07 '>='
+ ; $08 '<>'
+
+ ; $09 '>'
+ ; $0A '<'
+ ; $0B '='
+ ; $0C '+'
+
+;; GET-PRIO
+L10B5: ADD A,C ; add to default operation 'sub' ($C3)
+ LD C,A ; and place in operator byte - C.
+
+ LD HL,L110F - $C3 ; theoretical base of the priorities table.
+ ADD HL,BC ; add C ( B is zero)
+ LD B,(HL) ; pick up the priority in B
+
+;; S-LOOP
+L10BC: POP DE ; restore previous
+ LD A,D ; load A with priority.
+ CP B ; is present priority higher
+ JR C,L10ED ; forward if so to S-TIGHTER
+
+ AND A ; are both priorities zero
+ JP Z,L0018 ; exit if zero via GET-CHAR
+
+ PUSH BC ; stack present values
+ PUSH DE ; stack last values
+ CALL L0DA6 ; routine SYNTAX-Z
+ JR Z,L10D5 ; forward is checking syntax to S-SYNTEST
+
+ LD A,E ; fetch last operation
+ AND $3F ; mask off the indicator bits to give true
+ ; calculator literal.
+ LD B,A ; place in the B register for BREG
+
+; perform the single operation
+
+ RST 28H ;; FP-CALC
+ DEFB $37 ;;fp-calc-2
+ DEFB $34 ;;end-calc
+
+ JR L10DE ; forward to S-RUNTEST
+
+; ---
+
+;; S-SYNTEST
+L10D5: LD A,E ; transfer masked operator to A
+ XOR (IY+$01) ; XOR with FLAGS like results will reset bit 6
+ AND $40 ; test bit 6
+
+;; S-RPORT-C
+L10DB: JP NZ,L0D9A ; back to REPORT-C if results do not agree.
+
+; ---
+
+; in run-time impose bit 7 of the operator onto bit 6 of the FLAGS
+
+;; S-RUNTEST
+L10DE: POP DE ; restore last operation.
+ LD HL,$4001 ; address system variable FLAGS
+ SET 6,(HL) ; presume a numeric result
+ BIT 7,E ; test expected result in operation
+ JR NZ,L10EA ; forward if numeric to S-LOOPEND
+
+ RES 6,(HL) ; reset to signal string result
+
+;; S-LOOPEND
+L10EA: POP BC ; restore present values
+ JR L10BC ; back to S-LOOP
+
+; ---
+
+;; S-TIGHTER
+L10ED: PUSH DE ; push last values and consider these
+
+ LD A,C ; get the present operator.
+ BIT 6,(IY+$01) ; test FLAGS - Numeric or string result?
+ JR NZ,L110A ; forward if numeric to S-NEXT
+
+ AND $3F ; strip indicator bits to give clear literal.
+ ADD A,$08 ; add eight - augmenting numeric to equivalent
+ ; string literals.
+ LD C,A ; place plain literal back in C.
+ CP $10 ; compare to 'AND'
+ JR NZ,L1102 ; forward if not to S-NOT-AND
+
+ SET 6,C ; set the numeric operand required for 'AND'
+ JR L110A ; forward to S-NEXT
+
+; ---
+
+;; S-NOT-AND
+L1102: JR C,L10DB ; back if less than 'AND' to S-RPORT-C
+ ; Nonsense if '-', '*' etc.
+
+ CP $17 ; compare to 'strs-add' literal
+ JR Z,L110A ; forward if so signaling string result
+
+ SET 7,C ; set bit to numeric (Boolean) for others.
+
+;; S-NEXT
+L110A: PUSH BC ; stack 'present' values
+
+ RST 20H ; NEXT-CHAR
+ JP L0F59 ; jump back to S-LOOP-1
+
+
+
+; -------------------------
+; THE 'TABLE OF PRIORITIES'
+; -------------------------
+;
+;
+
+;; tbl-pri
+L110F: DEFB $06 ; '-'
+ DEFB $08 ; '*'
+ DEFB $08 ; '/'
+ DEFB $0A ; '**'
+ DEFB $02 ; 'OR'
+ DEFB $03 ; 'AND'
+ DEFB $05 ; '<='
+ DEFB $05 ; '>='
+ DEFB $05 ; '<>'
+ DEFB $05 ; '>'
+ DEFB $05 ; '<'
+ DEFB $05 ; '='
+ DEFB $06 ; '+'
+
+
+; --------------------------
+; THE 'LOOK-VARS' SUBROUTINE
+; --------------------------
+;
+;
+
+;; LOOK-VARS
+L111C: SET 6,(IY+$01) ; sv FLAGS - Signal numeric result
+
+ RST 18H ; GET-CHAR
+ CALL L14CE ; routine ALPHA
+ JP NC,L0D9A ; to REPORT-C
+
+ PUSH HL ;
+ LD C,A ;
+
+ RST 20H ; NEXT-CHAR
+ PUSH HL ;
+ RES 5,C ;
+ CP $10 ;
+ JR Z,L1148 ; to V-SYN/RUN
+
+ SET 6,C ;
+ CP $0D ;
+ JR Z,L1143 ; forward to V-STR-VAR
+
+ SET 5,C ;
+
+;; V-CHAR
+L1139: CALL L14D2 ; routine ALPHANUM
+ JR NC,L1148 ; forward when not to V-RUN/SYN
+
+ RES 6,C ;
+
+ RST 20H ; NEXT-CHAR
+ JR L1139 ; loop back to V-CHAR
+
+; ---
+
+;; V-STR-VAR
+L1143: RST 20H ; NEXT-CHAR
+ RES 6,(IY+$01) ; sv FLAGS - Signal string result
+
+;; V-RUN/SYN
+L1148: LD B,C ;
+ CALL L0DA6 ; routine SYNTAX-Z
+ JR NZ,L1156 ; forward to V-RUN
+
+ LD A,C ;
+ AND $E0 ;
+ SET 7,A ;
+ LD C,A ;
+ JR L118A ; forward to V-SYNTAX
+
+; ---
+
+;; V-RUN
+L1156: LD HL,($4010) ; sv VARS
+
+;; V-EACH
+L1159: LD A,(HL) ;
+ AND $7F ;
+ JR Z,L1188 ; to V-80-BYTE
+
+ CP C ;
+ JR NZ,L1180 ; to V-NEXT
+
+ RLA ;
+ ADD A,A ;
+ JP P,L1195 ; to V-FOUND-2
+
+ JR C,L1195 ; to V-FOUND-2
+
+ POP DE ;
+ PUSH DE ;
+ PUSH HL ;
+
+;; V-MATCHES
+L116B: INC HL ;
+
+;; V-SPACES
+L116C: LD A,(DE) ;
+ INC DE ;
+ AND A ;
+ JR Z,L116C ; back to V-SPACES
+
+ CP (HL) ;
+ JR Z,L116B ; back to V-MATCHES
+
+ OR $80 ;
+ CP (HL) ;
+ JR NZ,L117F ; forward to V-GET-PTR
+
+ LD A,(DE) ;
+ CALL L14D2 ; routine ALPHANUM
+ JR NC,L1194 ; forward to V-FOUND-1
+
+;; V-GET-PTR
+L117F: POP HL ;
+
+;; V-NEXT
+L1180: PUSH BC ;
+ CALL L09F2 ; routine NEXT-ONE
+ EX DE,HL ;
+ POP BC ;
+ JR L1159 ; back to V-EACH
+
+; ---
+
+;; V-80-BYTE
+L1188: SET 7,B ;
+
+;; V-SYNTAX
+L118A: POP DE ;
+
+ RST 18H ; GET-CHAR
+ CP $10 ;
+ JR Z,L1199 ; forward to V-PASS
+
+ SET 5,B ;
+ JR L11A1 ; forward to V-END
+
+; ---
+
+;; V-FOUND-1
+L1194: POP DE ;
+
+;; V-FOUND-2
+L1195: POP DE ;
+ POP DE ;
+ PUSH HL ;
+
+ RST 18H ; GET-CHAR
+
+;; V-PASS
+L1199: CALL L14D2 ; routine ALPHANUM
+ JR NC,L11A1 ; forward if not alphanumeric to V-END
+
+
+ RST 20H ; NEXT-CHAR
+ JR L1199 ; back to V-PASS
+
+; ---
+
+;; V-END
+L11A1: POP HL ;
+ RL B ;
+ BIT 6,B ;
+ RET ;
+
+; ------------------------
+; THE 'STK-VAR' SUBROUTINE
+; ------------------------
+;
+;
+
+;; STK-VAR
+L11A7: XOR A ;
+ LD B,A ;
+ BIT 7,C ;
+ JR NZ,L11F8 ; forward to SV-COUNT
+
+ BIT 7,(HL) ;
+ JR NZ,L11BF ; forward to SV-ARRAYS
+
+ INC A ;
+
+;; SV-SIMPLE$
+L11B2: INC HL ;
+ LD C,(HL) ;
+ INC HL ;
+ LD B,(HL) ;
+ INC HL ;
+ EX DE,HL ;
+ CALL L12C3 ; routine STK-STO-$
+
+ RST 18H ; GET-CHAR
+ JP L125A ; jump forward to SV-SLICE?
+
+; ---
+
+;; SV-ARRAYS
+L11BF: INC HL ;
+ INC HL ;
+ INC HL ;
+ LD B,(HL) ;
+ BIT 6,C ;
+ JR Z,L11D1 ; forward to SV-PTR
+
+ DEC B ;
+ JR Z,L11B2 ; forward to SV-SIMPLE$
+
+ EX DE,HL ;
+
+ RST 18H ; GET-CHAR
+ CP $10 ;
+ JR NZ,L1231 ; forward to REPORT-3
+
+ EX DE,HL ;
+
+;; SV-PTR
+L11D1: EX DE,HL ;
+ JR L11F8 ; forward to SV-COUNT
+
+; ---
+
+;; SV-COMMA
+L11D4: PUSH HL ;
+
+ RST 18H ; GET-CHAR
+ POP HL ;
+ CP $1A ;
+ JR Z,L11FB ; forward to SV-LOOP
+
+ BIT 7,C ;
+ JR Z,L1231 ; forward to REPORT-3
+
+ BIT 6,C ;
+ JR NZ,L11E9 ; forward to SV-CLOSE
+
+ CP $11 ;
+ JR NZ,L1223 ; forward to SV-RPT-C
+
+
+ RST 20H ; NEXT-CHAR
+ RET ;
+
+; ---
+
+;; SV-CLOSE
+L11E9: CP $11 ;
+ JR Z,L1259 ; forward to SV-DIM
+
+ CP $DF ;
+ JR NZ,L1223 ; forward to SV-RPT-C
+
+
+;; SV-CH-ADD
+L11F1: RST 18H ; GET-CHAR
+ DEC HL ;
+ LD ($4016),HL ; sv CH_ADD
+ JR L1256 ; forward to SV-SLICE
+
+; ---
+
+;; SV-COUNT
+L11F8: LD HL,$0000 ;
+
+;; SV-LOOP
+L11FB: PUSH HL ;
+
+ RST 20H ; NEXT-CHAR
+ POP HL ;
+ LD A,C ;
+ CP $C0 ;
+ JR NZ,L120C ; forward to SV-MULT
+
+
+ RST 18H ; GET-CHAR
+ CP $11 ;
+ JR Z,L1259 ; forward to SV-DIM
+
+ CP $DF ;
+ JR Z,L11F1 ; back to SV-CH-ADD
+
+;; SV-MULT
+L120C: PUSH BC ;
+ PUSH HL ;
+ CALL L12FF ; routine DE,(DE+1)
+ EX (SP),HL ;
+ EX DE,HL ;
+ CALL L12DD ; routine INT-EXP1
+ JR C,L1231 ; forward to REPORT-3
+
+ DEC BC ;
+ CALL L1305 ; routine GET-HL*DE
+ ADD HL,BC ;
+ POP DE ;
+ POP BC ;
+ DJNZ L11D4 ; loop back to SV-COMMA
+
+ BIT 7,C ;
+
+;; SV-RPT-C
+L1223: JR NZ,L128B ; relative jump to SL-RPT-C
+
+ PUSH HL ;
+ BIT 6,C ;
+ JR NZ,L123D ; forward to SV-ELEM$
+
+ LD B,D ;
+ LD C,E ;
+
+ RST 18H ; GET-CHAR
+ CP $11 ; is character a ')' ?
+ JR Z,L1233 ; skip forward to SV-NUMBER
+
+
+;; REPORT-3
+L1231: RST 08H ; ERROR-1
+ DEFB $02 ; Error Report: Subscript wrong
+
+
+;; SV-NUMBER
+L1233: RST 20H ; NEXT-CHAR
+ POP HL ;
+ LD DE,$0005 ;
+ CALL L1305 ; routine GET-HL*DE
+ ADD HL,BC ;
+ RET ; return >>
+
+; ---
+
+;; SV-ELEM$
+L123D: CALL L12FF ; routine DE,(DE+1)
+ EX (SP),HL ;
+ CALL L1305 ; routine GET-HL*DE
+ POP BC ;
+ ADD HL,BC ;
+ INC HL ;
+ LD B,D ;
+ LD C,E ;
+ EX DE,HL ;
+ CALL L12C2 ; routine STK-ST-0
+
+ RST 18H ; GET-CHAR
+ CP $11 ; is it ')' ?
+ JR Z,L1259 ; forward if so to SV-DIM
+
+ CP $1A ; is it ',' ?
+ JR NZ,L1231 ; back if not to REPORT-3
+
+;; SV-SLICE
+L1256: CALL L1263 ; routine SLICING
+
+;; SV-DIM
+L1259: RST 20H ; NEXT-CHAR
+
+;; SV-SLICE?
+L125A: CP $10 ;
+ JR Z,L1256 ; back to SV-SLICE
+
+ RES 6,(IY+$01) ; sv FLAGS - Signal string result
+ RET ; return.
+
+; ------------------------
+; THE 'SLICING' SUBROUTINE
+; ------------------------
+;
+;
+
+;; SLICING
+L1263: CALL L0DA6 ; routine SYNTAX-Z
+ CALL NZ,L13F8 ; routine STK-FETCH
+
+ RST 20H ; NEXT-CHAR
+ CP $11 ; is it ')' ?
+ JR Z,L12BE ; forward if so to SL-STORE
+
+ PUSH DE ;
+ XOR A ;
+ PUSH AF ;
+ PUSH BC ;
+ LD DE,$0001 ;
+
+ RST 18H ; GET-CHAR
+ POP HL ;
+ CP $DF ; is it 'TO' ?
+ JR Z,L1292 ; forward if so to SL-SECOND
+
+ POP AF ;
+ CALL L12DE ; routine INT-EXP2
+ PUSH AF ;
+ LD D,B ;
+ LD E,C ;
+ PUSH HL ;
+
+ RST 18H ; GET-CHAR
+ POP HL ;
+ CP $DF ; is it 'TO' ?
+ JR Z,L1292 ; forward if so to SL-SECOND
+
+ CP $11 ;
+
+;; SL-RPT-C
+L128B: JP NZ,L0D9A ; to REPORT-C
+
+ LD H,D ;
+ LD L,E ;
+ JR L12A5 ; forward to SL-DEFINE
+
+; ---
+
+;; SL-SECOND
+L1292: PUSH HL ;
+
+ RST 20H ; NEXT-CHAR
+ POP HL ;
+ CP $11 ; is it ')' ?
+ JR Z,L12A5 ; forward if so to SL-DEFINE
+
+ POP AF ;
+ CALL L12DE ; routine INT-EXP2
+ PUSH AF ;
+
+ RST 18H ; GET-CHAR
+ LD H,B ;
+ LD L,C ;
+ CP $11 ; is it ')' ?
+ JR NZ,L128B ; back if not to SL-RPT-C
+
+;; SL-DEFINE
+L12A5: POP AF ;
+ EX (SP),HL ;
+ ADD HL,DE ;
+ DEC HL ;
+ EX (SP),HL ;
+ AND A ;
+ SBC HL,DE ;
+ LD BC,$0000 ;
+ JR C,L12B9 ; forward to SL-OVER
+
+ INC HL ;
+ AND A ;
+ JP M,L1231 ; jump back to REPORT-3
+
+ LD B,H ;
+ LD C,L ;
+
+;; SL-OVER
+L12B9: POP DE ;
+ RES 6,(IY+$01) ; sv FLAGS - Signal string result
+
+;; SL-STORE
+L12BE: CALL L0DA6 ; routine SYNTAX-Z
+ RET Z ; return if checking syntax.
+
+; --------------------------
+; THE 'STK-STORE' SUBROUTINE
+; --------------------------
+;
+;
+
+;; STK-ST-0
+L12C2: XOR A ;
+
+;; STK-STO-$
+L12C3: PUSH BC ;
+ CALL L19EB ; routine TEST-5-SP
+ POP BC ;
+ LD HL,($401C) ; sv STKEND
+ LD (HL),A ;
+ INC HL ;
+ LD (HL),E ;
+ INC HL ;
+ LD (HL),D ;
+ INC HL ;
+ LD (HL),C ;
+ INC HL ;
+ LD (HL),B ;
+ INC HL ;
+ LD ($401C),HL ; sv STKEND
+ RES 6,(IY+$01) ; update FLAGS - signal string result
+ RET ; return.
+
+; -------------------------
+; THE 'INT EXP' SUBROUTINES
+; -------------------------
+;
+;
+
+;; INT-EXP1
+L12DD: XOR A ;
+
+;; INT-EXP2
+L12DE: PUSH DE ;
+ PUSH HL ;
+ PUSH AF ;
+ CALL L0D92 ; routine CLASS-6
+ POP AF ;
+ CALL L0DA6 ; routine SYNTAX-Z
+ JR Z,L12FC ; forward if checking syntax to I-RESTORE
+
+ PUSH AF ;
+ CALL L0EA7 ; routine FIND-INT
+ POP DE ;
+ LD A,B ;
+ OR C ;
+ SCF ; Set Carry Flag
+ JR Z,L12F9 ; forward to I-CARRY
+
+ POP HL ;
+ PUSH HL ;
+ AND A ;
+ SBC HL,BC ;
+
+;; I-CARRY
+L12F9: LD A,D ;
+ SBC A,$00 ;
+
+;; I-RESTORE
+L12FC: POP HL ;
+ POP DE ;
+ RET ;
+
+; --------------------------
+; THE 'DE,(DE+1)' SUBROUTINE
+; --------------------------
+; INDEX and LOAD Z80 subroutine.
+; This emulates the 6800 processor instruction LDX 1,X which loads a two-byte
+; value from memory into the register indexing it. Often these are hardly worth
+; the bother of writing as subroutines and this one doesn't save any time or
+; memory. The timing and space overheads have to be offset against the ease of
+; writing and the greater program readability from using such toolkit routines.
+
+;; DE,(DE+1)
+L12FF: EX DE,HL ; move index address into HL.
+ INC HL ; increment to address word.
+ LD E,(HL) ; pick up word low-order byte.
+ INC HL ; index high-order byte and
+ LD D,(HL) ; pick it up.
+ RET ; return with DE = word.
+
+; --------------------------
+; THE 'GET-HL*DE' SUBROUTINE
+; --------------------------
+;
+
+;; GET-HL*DE
+L1305: CALL L0DA6 ; routine SYNTAX-Z
+ RET Z ;
+
+ PUSH BC ;
+ LD B,$10 ;
+ LD A,H ;
+ LD C,L ;
+ LD HL,$0000 ;
+
+;; HL-LOOP
+L1311: ADD HL,HL ;
+ JR C,L131A ; forward with carry to HL-END
+
+ RL C ;
+ RLA ;
+ JR NC,L131D ; forward with no carry to HL-AGAIN
+
+ ADD HL,DE ;
+
+;; HL-END
+L131A: JP C,L0ED3 ; to REPORT-4
+
+;; HL-AGAIN
+L131D: DJNZ L1311 ; loop back to HL-LOOP
+
+ POP BC ;
+ RET ; return.
+
+; --------------------
+; THE 'LET' SUBROUTINE
+; --------------------
+;
+;
+
+;; LET
+L1321: LD HL,($4012) ; sv DEST-lo
+ BIT 1,(IY+$2D) ; sv FLAGX
+ JR Z,L136E ; forward to L-EXISTS
+
+ LD BC,$0005 ;
+
+;; L-EACH-CH
+L132D: INC BC ;
+
+; check
+
+;; L-NO-SP
+L132E: INC HL ;
+ LD A,(HL) ;
+ AND A ;
+ JR Z,L132E ; back to L-NO-SP
+
+ CALL L14D2 ; routine ALPHANUM
+ JR C,L132D ; back to L-EACH-CH
+
+ CP $0D ; is it '$' ?
+ JP Z,L13C8 ; forward if so to L-NEW$
+
+
+ RST 30H ; BC-SPACES
+ PUSH DE ;
+ LD HL,($4012) ; sv DEST
+ DEC DE ;
+ LD A,C ;
+ SUB $06 ;
+ LD B,A ;
+ LD A,$40 ;
+ JR Z,L1359 ; forward to L-SINGLE
+
+;; L-CHAR
+L134B: INC HL ;
+ LD A,(HL) ;
+ AND A ; is it a space ?
+ JR Z,L134B ; back to L-CHAR
+
+ INC DE ;
+ LD (DE),A ;
+ DJNZ L134B ; loop back to L-CHAR
+
+ OR $80 ;
+ LD (DE),A ;
+ LD A,$80 ;
+
+;; L-SINGLE
+L1359: LD HL,($4012) ; sv DEST-lo
+ XOR (HL) ;
+ POP HL ;
+ CALL L13E7 ; routine L-FIRST
+
+;; L-NUMERIC
+L1361: PUSH HL ;
+
+ RST 28H ;; FP-CALC
+ DEFB $02 ;;delete
+ DEFB $34 ;;end-calc
+
+ POP HL ;
+ LD BC,$0005 ;
+ AND A ;
+ SBC HL,BC ;
+ JR L13AE ; forward to L-ENTER
+
+; ---
+
+;; L-EXISTS
+L136E: BIT 6,(IY+$01) ; sv FLAGS - Numeric or string result?
+ JR Z,L137A ; forward to L-DELETE$
+
+ LD DE,$0006 ;
+ ADD HL,DE ;
+ JR L1361 ; back to L-NUMERIC
+
+; ---
+
+;; L-DELETE$
+L137A: LD HL,($4012) ; sv DEST-lo
+ LD BC,($402E) ; sv STRLEN_lo
+ BIT 0,(IY+$2D) ; sv FLAGX
+ JR NZ,L13B7 ; forward to L-ADD$
+
+ LD A,B ;
+ OR C ;
+ RET Z ;
+
+ PUSH HL ;
+
+ RST 30H ; BC-SPACES
+ PUSH DE ;
+ PUSH BC ;
+ LD D,H ;
+ LD E,L ;
+ INC HL ;
+ LD (HL),$00 ;
+ LDDR ; Copy Bytes
+ PUSH HL ;
+ CALL L13F8 ; routine STK-FETCH
+ POP HL ;
+ EX (SP),HL ;
+ AND A ;
+ SBC HL,BC ;
+ ADD HL,BC ;
+ JR NC,L13A3 ; forward to L-LENGTH
+
+ LD B,H ;
+ LD C,L ;
+
+;; L-LENGTH
+L13A3: EX (SP),HL ;
+ EX DE,HL ;
+ LD A,B ;
+ OR C ;
+ JR Z,L13AB ; forward if zero to L-IN-W/S
+
+ LDIR ; Copy Bytes
+
+;; L-IN-W/S
+L13AB: POP BC ;
+ POP DE ;
+ POP HL ;
+
+; ------------------------
+; THE 'L-ENTER' SUBROUTINE
+; ------------------------
+;
+
+;; L-ENTER
+L13AE: EX DE,HL ;
+ LD A,B ;
+ OR C ;
+ RET Z ;
+
+ PUSH DE ;
+ LDIR ; Copy Bytes
+ POP HL ;
+ RET ; return.
+
+; ---
+
+;; L-ADD$
+L13B7: DEC HL ;
+ DEC HL ;
+ DEC HL ;
+ LD A,(HL) ;
+ PUSH HL ;
+ PUSH BC ;
+
+ CALL L13CE ; routine L-STRING
+
+ POP BC ;
+ POP HL ;
+ INC BC ;
+ INC BC ;
+ INC BC ;
+ JP L0A60 ; jump back to exit via RECLAIM-2
+
+; ---
+
+;; L-NEW$
+L13C8: LD A,$60 ; prepare mask %01100000
+ LD HL,($4012) ; sv DEST-lo
+ XOR (HL) ;
+
+; -------------------------
+; THE 'L-STRING' SUBROUTINE
+; -------------------------
+;
+
+;; L-STRING
+L13CE: PUSH AF ;
+ CALL L13F8 ; routine STK-FETCH
+ EX DE,HL ;
+ ADD HL,BC ;
+ PUSH HL ;
+ INC BC ;
+ INC BC ;
+ INC BC ;
+
+ RST 30H ; BC-SPACES
+ EX DE,HL ;
+ POP HL ;
+ DEC BC ;
+ DEC BC ;
+ PUSH BC ;
+ LDDR ; Copy Bytes
+ EX DE,HL ;
+ POP BC ;
+ DEC BC ;
+ LD (HL),B ;
+ DEC HL ;
+ LD (HL),C ;
+ POP AF ;
+
+;; L-FIRST
+L13E7: PUSH AF ;
+ CALL L14C7 ; routine REC-V80
+ POP AF ;
+ DEC HL ;
+ LD (HL),A ;
+ LD HL,($401A) ; sv STKBOT_lo
+ LD ($4014),HL ; sv E_LINE_lo
+ DEC HL ;
+ LD (HL),$80 ;
+ RET ;
+
+; --------------------------
+; THE 'STK-FETCH' SUBROUTINE
+; --------------------------
+; This routine fetches a five-byte value from the calculator stack
+; reducing the pointer to the end of the stack by five.
+; For a floating-point number the exponent is in A and the mantissa
+; is the thirty-two bits EDCB.
+; For strings, the start of the string is in DE and the length in BC.
+; A is unused.
+
+;; STK-FETCH
+L13F8: LD HL,($401C) ; load HL from system variable STKEND
+
+ DEC HL ;
+ LD B,(HL) ;
+ DEC HL ;
+ LD C,(HL) ;
+ DEC HL ;
+ LD D,(HL) ;
+ DEC HL ;
+ LD E,(HL) ;
+ DEC HL ;
+ LD A,(HL) ;
+
+ LD ($401C),HL ; set system variable STKEND to lower value.
+ RET ; return.
+
+; -------------------------
+; THE 'DIM' COMMAND ROUTINE
+; -------------------------
+; An array is created and initialized to zeros which is also the space
+; character on the ZX81.
+
+;; DIM
+L1409: CALL L111C ; routine LOOK-VARS
+
+;; D-RPORT-C
+L140C: JP NZ,L0D9A ; to REPORT-C
+
+ CALL L0DA6 ; routine SYNTAX-Z
+ JR NZ,L141C ; forward to D-RUN
+
+ RES 6,C ;
+ CALL L11A7 ; routine STK-VAR
+ CALL L0D1D ; routine CHECK-END
+
+;; D-RUN
+L141C: JR C,L1426 ; forward to D-LETTER
+
+ PUSH BC ;
+ CALL L09F2 ; routine NEXT-ONE
+ CALL L0A60 ; routine RECLAIM-2
+ POP BC ;
+
+;; D-LETTER
+L1426: SET 7,C ;
+ LD B,$00 ;
+ PUSH BC ;
+ LD HL,$0001 ;
+ BIT 6,C ;
+ JR NZ,L1434 ; forward to D-SIZE
+
+ LD L,$05 ;
+
+;; D-SIZE
+L1434: EX DE,HL ;
+
+;; D-NO-LOOP
+L1435: RST 20H ; NEXT-CHAR
+ LD H,$40 ;
+ CALL L12DD ; routine INT-EXP1
+ JP C,L1231 ; jump back to REPORT-3
+
+ POP HL ;
+ PUSH BC ;
+ INC H ;
+ PUSH HL ;
+ LD H,B ;
+ LD L,C ;
+ CALL L1305 ; routine GET-HL*DE
+ EX DE,HL ;
+
+ RST 18H ; GET-CHAR
+ CP $1A ;
+ JR Z,L1435 ; back to D-NO-LOOP
+
+ CP $11 ; is it ')' ?
+ JR NZ,L140C ; back if not to D-RPORT-C
+
+
+ RST 20H ; NEXT-CHAR
+ POP BC ;
+ LD A,C ;
+ LD L,B ;
+ LD H,$00 ;
+ INC HL ;
+ INC HL ;
+ ADD HL,HL ;
+ ADD HL,DE ;
+ JP C,L0ED3 ; jump to REPORT-4
+
+ PUSH DE ;
+ PUSH BC ;
+ PUSH HL ;
+ LD B,H ;
+ LD C,L ;
+ LD HL,($4014) ; sv E_LINE_lo
+ DEC HL ;
+ CALL L099E ; routine MAKE-ROOM
+ INC HL ;
+ LD (HL),A ;
+ POP BC ;
+ DEC BC ;
+ DEC BC ;
+ DEC BC ;
+ INC HL ;
+ LD (HL),C ;
+ INC HL ;
+ LD (HL),B ;
+ POP AF ;
+ INC HL ;
+ LD (HL),A ;
+ LD H,D ;
+ LD L,E ;
+ DEC DE ;
+ LD (HL),$00 ;
+ POP BC ;
+ LDDR ; Copy Bytes
+
+;; DIM-SIZES
+L147F: POP BC ;
+ LD (HL),B ;
+ DEC HL ;
+ LD (HL),C ;
+ DEC HL ;
+ DEC A ;
+ JR NZ,L147F ; back to DIM-SIZES
+
+ RET ; return.
+
+; ---------------------
+; THE 'RESERVE' ROUTINE
+; ---------------------
+;
+;
+
+;; RESERVE
+L1488: LD HL,($401A) ; address STKBOT
+ DEC HL ; now last byte of workspace
+ CALL L099E ; routine MAKE-ROOM
+ INC HL ;
+ INC HL ;
+ POP BC ;
+ LD ($4014),BC ; sv E_LINE_lo
+ POP BC ;
+ EX DE,HL ;
+ INC HL ;
+ RET ;
+
+; ---------------------------
+; THE 'CLEAR' COMMAND ROUTINE
+; ---------------------------
+;
+;
+
+;; CLEAR
+L149A: LD HL,($4010) ; sv VARS_lo
+ LD (HL),$80 ;
+ INC HL ;
+ LD ($4014),HL ; sv E_LINE_lo
+
+; -----------------------
+; THE 'X-TEMP' SUBROUTINE
+; -----------------------
+;
+;
+
+;; X-TEMP
+L14A3: LD HL,($4014) ; sv E_LINE_lo
+
+; ----------------------
+; THE 'SET-STK' ROUTINES
+; ----------------------
+;
+;
+
+;; SET-STK-B
+L14A6: LD ($401A),HL ; sv STKBOT
+
+;
+
+;; SET-STK-E
+L14A9: LD ($401C),HL ; sv STKEND
+ RET ;
+
+; -----------------------
+; THE 'CURSOR-IN' ROUTINE
+; -----------------------
+; This routine is called to set the edit line to the minimum cursor/newline
+; and to set STKEND, the start of free space, at the next position.
+
+;; CURSOR-IN
+L14AD: LD HL,($4014) ; fetch start of edit line from E_LINE
+ LD (HL),$7F ; insert cursor character
+
+ INC HL ; point to next location.
+ LD (HL),$76 ; insert NEWLINE character
+ INC HL ; point to next free location.
+
+ LD (IY+$22),$02 ; set lower screen display file size DF_SZ
+
+ JR L14A6 ; exit via SET-STK-B above
+
+; ------------------------
+; THE 'SET-MIN' SUBROUTINE
+; ------------------------
+;
+;
+
+;; SET-MIN
+L14BC: LD HL,$405D ; normal location of calculator's memory area
+ LD ($401F),HL ; update system variable MEM
+ LD HL,($401A) ; fetch STKBOT
+ JR L14A9 ; back to SET-STK-E
+
+
+; ------------------------------------
+; THE 'RECLAIM THE END-MARKER' ROUTINE
+; ------------------------------------
+
+;; REC-V80
+L14C7: LD DE,($4014) ; sv E_LINE_lo
+ JP L0A5D ; to RECLAIM-1
+
+; ----------------------
+; THE 'ALPHA' SUBROUTINE
+; ----------------------
+
+;; ALPHA
+L14CE: CP $26 ;
+ JR L14D4 ; skip forward to ALPHA-2
+
+
+; -------------------------
+; THE 'ALPHANUM' SUBROUTINE
+; -------------------------
+
+;; ALPHANUM
+L14D2: CP $1C ;
+
+
+;; ALPHA-2
+L14D4: CCF ; Complement Carry Flag
+ RET NC ;
+
+ CP $40 ;
+ RET ;
+
+
+; ------------------------------------------
+; THE 'DECIMAL TO FLOATING POINT' SUBROUTINE
+; ------------------------------------------
+;
+
+;; DEC-TO-FP
+L14D9: CALL L1548 ; routine INT-TO-FP gets first part
+ CP $1B ; is character a '.' ?
+ JR NZ,L14F5 ; forward if not to E-FORMAT
+
+
+ RST 28H ;; FP-CALC
+ DEFB $A1 ;;stk-one
+ DEFB $C0 ;;st-mem-0
+ DEFB $02 ;;delete
+ DEFB $34 ;;end-calc
+
+
+;; NXT-DGT-1
+L14E5: RST 20H ; NEXT-CHAR
+ CALL L1514 ; routine STK-DIGIT
+ JR C,L14F5 ; forward to E-FORMAT
+
+
+ RST 28H ;; FP-CALC
+ DEFB $E0 ;;get-mem-0
+ DEFB $A4 ;;stk-ten
+ DEFB $05 ;;division
+ DEFB $C0 ;;st-mem-0
+ DEFB $04 ;;multiply
+ DEFB $0F ;;addition
+ DEFB $34 ;;end-calc
+
+ JR L14E5 ; loop back till exhausted to NXT-DGT-1
+
+; ---
+
+;; E-FORMAT
+L14F5: CP $2A ; is character 'E' ?
+ RET NZ ; return if not
+
+ LD (IY+$5D),$FF ; initialize sv MEM-0-1st to $FF TRUE
+
+ RST 20H ; NEXT-CHAR
+ CP $15 ; is character a '+' ?
+ JR Z,L1508 ; forward if so to SIGN-DONE
+
+ CP $16 ; is it a '-' ?
+ JR NZ,L1509 ; forward if not to ST-E-PART
+
+ INC (IY+$5D) ; sv MEM-0-1st change to FALSE
+
+;; SIGN-DONE
+L1508: RST 20H ; NEXT-CHAR
+
+;; ST-E-PART
+L1509: CALL L1548 ; routine INT-TO-FP
+
+ RST 28H ;; FP-CALC m, e.
+ DEFB $E0 ;;get-mem-0 m, e, (1/0) TRUE/FALSE
+ DEFB $00 ;;jump-true
+ DEFB $02 ;;to L1511, E-POSTVE
+ DEFB $18 ;;neg m, -e
+
+;; E-POSTVE
+L1511: DEFB $38 ;;e-to-fp x.
+ DEFB $34 ;;end-calc x.
+
+ RET ; return.
+
+
+; --------------------------
+; THE 'STK-DIGIT' SUBROUTINE
+; --------------------------
+;
+
+;; STK-DIGIT
+L1514: CP $1C ;
+ RET C ;
+
+ CP $26 ;
+ CCF ; Complement Carry Flag
+ RET C ;
+
+ SUB $1C ;
+
+; ------------------------
+; THE 'STACK-A' SUBROUTINE
+; ------------------------
+;
+
+
+;; STACK-A
+L151D: LD C,A ;
+ LD B,$00 ;
+
+; -------------------------
+; THE 'STACK-BC' SUBROUTINE
+; -------------------------
+; The ZX81 does not have an integer number format so the BC register contents
+; must be converted to their full floating-point form.
+
+;; STACK-BC
+L1520: LD IY,$4000 ; re-initialize the system variables pointer.
+ PUSH BC ; save the integer value.
+
+; now stack zero, five zero bytes as a starting point.
+
+ RST 28H ;; FP-CALC
+ DEFB $A0 ;;stk-zero 0.
+ DEFB $34 ;;end-calc
+
+ POP BC ; restore integer value.
+
+ LD (HL),$91 ; place $91 in exponent 65536.
+ ; this is the maximum possible value
+
+ LD A,B ; fetch hi-byte.
+ AND A ; test for zero.
+ JR NZ,L1536 ; forward if not zero to STK-BC-2
+
+ LD (HL),A ; else make exponent zero again
+ OR C ; test lo-byte
+ RET Z ; return if BC was zero - done.
+
+; else there has to be a set bit if only the value one.
+
+ LD B,C ; save C in B.
+ LD C,(HL) ; fetch zero to C
+ LD (HL),$89 ; make exponent $89 256.
+
+;; STK-BC-2
+L1536: DEC (HL) ; decrement exponent - halving number
+ SLA C ; C<-76543210<-0
+ RL B ; C<-76543210<-C
+ JR NC,L1536 ; loop back if no carry to STK-BC-2
+
+ SRL B ; 0->76543210->C
+ RR C ; C->76543210->C
+
+ INC HL ; address first byte of mantissa
+ LD (HL),B ; insert B
+ INC HL ; address second byte of mantissa
+ LD (HL),C ; insert C
+
+ DEC HL ; point to the
+ DEC HL ; exponent again
+ RET ; return.
+
+; ------------------------------------------
+; THE 'INTEGER TO FLOATING POINT' SUBROUTINE
+; ------------------------------------------
+;
+;
+
+;; INT-TO-FP
+L1548: PUSH AF ;
+
+ RST 28H ;; FP-CALC
+ DEFB $A0 ;;stk-zero
+ DEFB $34 ;;end-calc
+
+ POP AF ;
+
+;; NXT-DGT-2
+L154D: CALL L1514 ; routine STK-DIGIT
+ RET C ;
+
+
+ RST 28H ;; FP-CALC
+ DEFB $01 ;;exchange
+ DEFB $A4 ;;stk-ten
+ DEFB $04 ;;multiply
+ DEFB $0F ;;addition
+ DEFB $34 ;;end-calc
+
+
+ RST 20H ; NEXT-CHAR
+ JR L154D ; to NXT-DGT-2
+
+
+; -------------------------------------------
+; THE 'E-FORMAT TO FLOATING POINT' SUBROUTINE
+; -------------------------------------------
+; (Offset $38: 'e-to-fp')
+; invoked from DEC-TO-FP and PRINT-FP.
+; e.g. 2.3E4 is 23000.
+; This subroutine evaluates xEm where m is a positive or negative integer.
+; At a simple level x is multiplied by ten for every unit of m.
+; If the decimal exponent m is negative then x is divided by ten for each unit.
+; A short-cut is taken if the exponent is greater than seven and in this
+; case the exponent is reduced by seven and the value is multiplied or divided
+; by ten million.
+; Note. for the ZX Spectrum an even cleverer method was adopted which involved
+; shifting the bits out of the exponent so the result was achieved with six
+; shifts at most. The routine below had to be completely re-written mostly
+; in Z80 machine code.
+; Although no longer operable, the calculator literal was retained for old
+; times sake, the routine being invoked directly from a machine code CALL.
+;
+; On entry in the ZX81, m, the exponent, is the 'last value', and the
+; floating-point decimal mantissa is beneath it.
+
+
+;; e-to-fp
+L155A: RST 28H ;; FP-CALC x, m.
+ DEFB $2D ;;duplicate x, m, m.
+ DEFB $32 ;;less-0 x, m, (1/0).
+ DEFB $C0 ;;st-mem-0 x, m, (1/0).
+ DEFB $02 ;;delete x, m.
+ DEFB $27 ;;abs x, +m.
+
+;; E-LOOP
+L1560: DEFB $A1 ;;stk-one x, m,1.
+ DEFB $03 ;;subtract x, m-1.
+ DEFB $2D ;;duplicate x, m-1,m-1.
+ DEFB $32 ;;less-0 x, m-1, (1/0).
+ DEFB $00 ;;jump-true x, m-1.
+ DEFB $22 ;;to L1587, E-END x, m-1.
+
+ DEFB $2D ;;duplicate x, m-1, m-1.
+ DEFB $30 ;;stk-data
+ DEFB $33 ;;Exponent: $83, Bytes: 1
+
+ DEFB $40 ;;(+00,+00,+00) x, m-1, m-1, 6.
+ DEFB $03 ;;subtract x, m-1, m-7.
+ DEFB $2D ;;duplicate x, m-1, m-7, m-7.
+ DEFB $32 ;;less-0 x, m-1, m-7, (1/0).
+ DEFB $00 ;;jump-true x, m-1, m-7.
+ DEFB $0C ;;to L157A, E-LOW
+
+; but if exponent m is higher than 7 do a bigger chunk.
+; multiplying (or dividing if negative) by 10 million - 1e7.
+
+ DEFB $01 ;;exchange x, m-7, m-1.
+ DEFB $02 ;;delete x, m-7.
+ DEFB $01 ;;exchange m-7, x.
+ DEFB $30 ;;stk-data
+ DEFB $80 ;;Bytes: 3
+ DEFB $48 ;;Exponent $98
+ DEFB $18,$96,$80 ;;(+00) m-7, x, 10,000,000 (=f)
+ DEFB $2F ;;jump
+ DEFB $04 ;;to L157D, E-CHUNK
+
+; ---
+
+;; E-LOW
+L157A: DEFB $02 ;;delete x, m-1.
+ DEFB $01 ;;exchange m-1, x.
+ DEFB $A4 ;;stk-ten m-1, x, 10 (=f).
+
+;; E-CHUNK
+L157D: DEFB $E0 ;;get-mem-0 m-1, x, f, (1/0)
+ DEFB $00 ;;jump-true m-1, x, f
+ DEFB $04 ;;to L1583, E-DIVSN
+
+ DEFB $04 ;;multiply m-1, x*f.
+ DEFB $2F ;;jump
+ DEFB $02 ;;to L1584, E-SWAP
+
+; ---
+
+;; E-DIVSN
+L1583: DEFB $05 ;;division m-1, x/f (= new x).
+
+;; E-SWAP
+L1584: DEFB $01 ;;exchange x, m-1 (= new m).
+ DEFB $2F ;;jump x, m.
+ DEFB $DA ;;to L1560, E-LOOP
+
+; ---
+
+;; E-END
+L1587: DEFB $02 ;;delete x. (-1)
+ DEFB $34 ;;end-calc x.
+
+ RET ; return.
+
+; -------------------------------------
+; THE 'FLOATING-POINT TO BC' SUBROUTINE
+; -------------------------------------
+; The floating-point form on the calculator stack is compressed directly into
+; the BC register rounding up if necessary.
+; Valid range is 0 to 65535.4999
+
+;; FP-TO-BC
+L158A: CALL L13F8 ; routine STK-FETCH - exponent to A
+ ; mantissa to EDCB.
+ AND A ; test for value zero.
+ JR NZ,L1595 ; forward if not to FPBC-NZRO
+
+; else value is zero
+
+ LD B,A ; zero to B
+ LD C,A ; also to C
+ PUSH AF ; save the flags on machine stack
+ JR L15C6 ; forward to FPBC-END
+
+; ---
+
+; EDCB => BCE
+
+;; FPBC-NZRO
+L1595: LD B,E ; transfer the mantissa from EDCB
+ LD E,C ; to BCE. Bit 7 of E is the 17th bit which
+ LD C,D ; will be significant for rounding if the
+ ; number is already normalized.
+
+ SUB $91 ; subtract 65536
+ CCF ; complement carry flag
+ BIT 7,B ; test sign bit
+ PUSH AF ; push the result
+
+ SET 7,B ; set the implied bit
+ JR C,L15C6 ; forward with carry from SUB/CCF to FPBC-END
+ ; number is too big.
+
+ INC A ; increment the exponent and
+ NEG ; negate to make range $00 - $0F
+
+ CP $08 ; test if one or two bytes
+ JR C,L15AF ; forward with two to BIG-INT
+
+ LD E,C ; shift mantissa
+ LD C,B ; 8 places right
+ LD B,$00 ; insert a zero in B
+ SUB $08 ; reduce exponent by eight
+
+;; BIG-INT
+L15AF: AND A ; test the exponent
+ LD D,A ; save exponent in D.
+
+ LD A,E ; fractional bits to A
+ RLCA ; rotate most significant bit to carry for
+ ; rounding of an already normal number.
+
+ JR Z,L15BC ; forward if exponent zero to EXP-ZERO
+ ; the number is normalized
+
+;; FPBC-NORM
+L15B5: SRL B ; 0->76543210->C
+ RR C ; C->76543210->C
+
+ DEC D ; decrement exponent
+
+ JR NZ,L15B5 ; loop back till zero to FPBC-NORM
+
+;; EXP-ZERO
+L15BC: JR NC,L15C6 ; forward without carry to NO-ROUND
+
+ INC BC ; round up.
+ LD A,B ; test result
+ OR C ; for zero
+ JR NZ,L15C6 ; forward if not to GRE-ZERO
+
+ POP AF ; restore sign flag
+ SCF ; set carry flag to indicate overflow
+ PUSH AF ; save combined flags again
+
+;; FPBC-END
+L15C6: PUSH BC ; save BC value
+
+; set HL and DE to calculator stack pointers.
+
+ RST 28H ;; FP-CALC
+ DEFB $34 ;;end-calc
+
+
+ POP BC ; restore BC value
+ POP AF ; restore flags
+ LD A,C ; copy low byte to A also.
+ RET ; return
+
+; ------------------------------------
+; THE 'FLOATING-POINT TO A' SUBROUTINE
+; ------------------------------------
+;
+;
+
+;; FP-TO-A
+L15CD: CALL L158A ; routine FP-TO-BC
+ RET C ;
+
+ PUSH AF ;
+ DEC B ;
+ INC B ;
+ JR Z,L15D9 ; forward if in range to FP-A-END
+
+ POP AF ; fetch result
+ SCF ; set carry flag signaling overflow
+ RET ; return
+
+;; FP-A-END
+L15D9: POP AF ;
+ RET ;
+
+
+; ----------------------------------------------
+; THE 'PRINT A FLOATING-POINT NUMBER' SUBROUTINE
+; ----------------------------------------------
+; prints 'last value' x on calculator stack.
+; There are a wide variety of formats see Chapter 4.
+; e.g.
+; PI prints as 3.1415927
+; .123 prints as 0.123
+; .0123 prints as .0123
+; 999999999999 prints as 1000000000000
+; 9876543210123 prints as 9876543200000
+
+; Begin by isolating zero and just printing the '0' character
+; for that case. For negative numbers print a leading '-' and
+; then form the absolute value of x.
+
+;; PRINT-FP
+L15DB: RST 28H ;; FP-CALC x.
+ DEFB $2D ;;duplicate x, x.
+ DEFB $32 ;;less-0 x, (1/0).
+ DEFB $00 ;;jump-true
+ DEFB $0B ;;to L15EA, PF-NGTVE x.
+
+ DEFB $2D ;;duplicate x, x
+ DEFB $33 ;;greater-0 x, (1/0).
+ DEFB $00 ;;jump-true
+ DEFB $0D ;;to L15F0, PF-POSTVE x.
+
+ DEFB $02 ;;delete .
+ DEFB $34 ;;end-calc .
+
+ LD A,$1C ; load accumulator with character '0'
+
+ RST 10H ; PRINT-A
+ RET ; return. >>
+
+; ---
+
+;; PF-NEGTVE
+L15EA: DEFB $27 ; abs +x.
+ DEFB $34 ;;end-calc x.
+
+ LD A,$16 ; load accumulator with '-'
+
+ RST 10H ; PRINT-A
+
+ RST 28H ;; FP-CALC x.
+
+;; PF-POSTVE
+L15F0: DEFB $34 ;;end-calc x.
+
+; register HL addresses the exponent of the floating-point value.
+; if positive, and point floats to left, then bit 7 is set.
+
+ LD A,(HL) ; pick up the exponent byte
+ CALL L151D ; routine STACK-A places on calculator stack.
+
+; now calculate roughly the number of digits, n, before the decimal point by
+; subtracting a half from true exponent and multiplying by log to
+; the base 10 of 2.
+; The true number could be one higher than n, the integer result.
+
+ RST 28H ;; FP-CALC x, e.
+ DEFB $30 ;;stk-data
+ DEFB $78 ;;Exponent: $88, Bytes: 2
+ DEFB $00,$80 ;;(+00,+00) x, e, 128.5.
+ DEFB $03 ;;subtract x, e -.5.
+ DEFB $30 ;;stk-data
+ DEFB $EF ;;Exponent: $7F, Bytes: 4
+ DEFB $1A,$20,$9A,$85 ;; .30103 (log10 2)
+ DEFB $04 ;;multiply x,
+ DEFB $24 ;;int
+ DEFB $C1 ;;st-mem-1 x, n.
+
+
+ DEFB $30 ;;stk-data
+ DEFB $34 ;;Exponent: $84, Bytes: 1
+ DEFB $00 ;;(+00,+00,+00) x, n, 8.
+
+ DEFB $03 ;;subtract x, n-8.
+ DEFB $18 ;;neg x, 8-n.
+ DEFB $38 ;;e-to-fp x * (10^n)
+
+; finally the 8 or 9 digit decimal is rounded.
+; a ten-digit integer can arise in the case of, say, 999999999.5
+; which gives 1000000000.
+
+ DEFB $A2 ;;stk-half
+ DEFB $0F ;;addition
+ DEFB $24 ;;int i.
+ DEFB $34 ;;end-calc
+
+; If there were 8 digits then final rounding will take place on the calculator
+; stack above and the next two instructions insert a masked zero so that
+; no further rounding occurs. If the result is a 9 digit integer then
+; rounding takes place within the buffer.
+
+ LD HL,$406B ; address system variable MEM-2-5th
+ ; which could be the 'ninth' digit.
+ LD (HL),$90 ; insert the value $90 10010000
+
+; now starting from lowest digit lay down the 8, 9 or 10 digit integer
+; which represents the significant portion of the number
+; e.g. PI will be the nine-digit integer 314159265
+
+ LD B,$0A ; count is ten digits.
+
+;; PF-LOOP
+L1615: INC HL ; increase pointer
+
+ PUSH HL ; preserve buffer address.
+ PUSH BC ; preserve counter.
+
+ RST 28H ;; FP-CALC i.
+ DEFB $A4 ;;stk-ten i, 10.
+ DEFB $2E ;;n-mod-m i mod 10, i/10
+ DEFB $01 ;;exchange i/10, remainder.
+ DEFB $34 ;;end-calc
+
+ CALL L15CD ; routine FP-TO-A $00-$09
+
+ OR $90 ; make left hand nibble 9
+
+ POP BC ; restore counter
+ POP HL ; restore buffer address.
+
+ LD (HL),A ; insert masked digit in buffer.
+ DJNZ L1615 ; loop back for all ten to PF-LOOP
+
+; the most significant digit will be last but if the number is exhausted then
+; the last one or two positions will contain zero ($90).
+
+; e.g. for 'one' we have zero as estimate of leading digits.
+; 1*10^8 100000000 as integer value
+; 90 90 90 90 90 90 90 90 91 90 as buffer mem3/mem4 contents.
+
+
+ INC HL ; advance pointer to one past buffer
+ LD BC,$0008 ; set C to 8 ( B is already zero )
+ PUSH HL ; save pointer.
+
+;; PF-NULL
+L162C: DEC HL ; decrease pointer
+ LD A,(HL) ; fetch masked digit
+ CP $90 ; is it a leading zero ?
+ JR Z,L162C ; loop back if so to PF-NULL
+
+; at this point a significant digit has been found. carry is reset.
+
+ SBC HL,BC ; subtract eight from the address.
+ PUSH HL ; ** save this pointer too
+ LD A,(HL) ; fetch addressed byte
+ ADD A,$6B ; add $6B - forcing a round up ripple
+ ; if $95 or over.
+ PUSH AF ; save the carry result.
+
+; now enter a loop to round the number. After rounding has been considered
+; a zero that has arisen from rounding or that was present at that position
+; originally is changed from $90 to $80.
+
+;; PF-RND-LP
+L1639: POP AF ; retrieve carry from machine stack.
+ INC HL ; increment address
+ LD A,(HL) ; fetch new byte
+ ADC A,$00 ; add in any carry
+
+ DAA ; decimal adjust accumulator
+ ; carry will ripple through the '9'
+
+ PUSH AF ; save carry on machine stack.
+ AND $0F ; isolate character 0 - 9 AND set zero flag
+ ; if zero.
+ LD (HL),A ; place back in location.
+ SET 7,(HL) ; set bit 7 to show printable.
+ ; but not if trailing zero after decimal point.
+ JR Z,L1639 ; back if a zero to PF-RND-LP
+ ; to consider further rounding and/or trailing
+ ; zero identification.
+
+ POP AF ; balance stack
+ POP HL ; ** retrieve lower pointer
+
+; now insert 6 trailing zeros which are printed if before the decimal point
+; but mark the end of printing if after decimal point.
+; e.g. 9876543210123 is printed as 9876543200000
+; 123.456001 is printed as 123.456
+
+ LD B,$06 ; the count is six.
+
+;; PF-ZERO-6
+L164B: LD (HL),$80 ; insert a masked zero
+ DEC HL ; decrease pointer.
+ DJNZ L164B ; loop back for all six to PF-ZERO-6
+
+; n-mod-m reduced the number to zero and this is now deleted from the calculator
+; stack before fetching the original estimate of leading digits.
+
+
+ RST 28H ;; FP-CALC 0.
+ DEFB $02 ;;delete .
+ DEFB $E1 ;;get-mem-1 n.
+ DEFB $34 ;;end-calc n.
+
+ CALL L15CD ; routine FP-TO-A
+ JR Z,L165B ; skip forward if positive to PF-POS
+
+ NEG ; negate makes positive
+
+;; PF-POS
+L165B: LD E,A ; transfer count of digits to E
+ INC E ; increment twice
+ INC E ;
+ POP HL ; * retrieve pointer to one past buffer.
+
+;; GET-FIRST
+L165F: DEC HL ; decrement address.
+ DEC E ; decrement digit counter.
+ LD A,(HL) ; fetch masked byte.
+ AND $0F ; isolate right-hand nibble.
+ JR Z,L165F ; back with leading zero to GET-FIRST
+
+; now determine if E-format printing is needed
+
+ LD A,E ; transfer now accurate number count to A.
+ SUB $05 ; subtract five
+ CP $08 ; compare with 8 as maximum digits is 13.
+ JP P,L1682 ; forward if positive to PF-E-FMT
+
+ CP $F6 ; test for more than four zeros after point.
+ JP M,L1682 ; forward if so to PF-E-FMT
+
+ ADD A,$06 ; test for zero leading digits, e.g. 0.5
+ JR Z,L16BF ; forward if so to PF-ZERO-1
+
+ JP M,L16B2 ; forward if more than one zero to PF-ZEROS
+
+; else digits before the decimal point are to be printed
+
+ LD B,A ; count of leading characters to B.
+
+;; PF-NIB-LP
+L167B: CALL L16D0 ; routine PF-NIBBLE
+ DJNZ L167B ; loop back for counted numbers to PF-NIB-LP
+
+ JR L16C2 ; forward to consider decimal part to PF-DC-OUT
+
+; ---
+
+;; PF-E-FMT
+L1682: LD B,E ; count to B
+ CALL L16D0 ; routine PF-NIBBLE prints one digit.
+ CALL L16C2 ; routine PF-DC-OUT considers fractional part.
+
+ LD A,$2A ; prepare character 'E'
+ RST 10H ; PRINT-A
+
+ LD A,B ; transfer exponent to A
+ AND A ; test the sign.
+ JP P,L1698 ; forward if positive to PF-E-POS
+
+ NEG ; negate the negative exponent.
+ LD B,A ; save positive exponent in B.
+
+ LD A,$16 ; prepare character '-'
+ JR L169A ; skip forward to PF-E-SIGN
+
+; ---
+
+;; PF-E-POS
+L1698: LD A,$15 ; prepare character '+'
+
+;; PF-E-SIGN
+L169A: RST 10H ; PRINT-A
+
+; now convert the integer exponent in B to two characters.
+; it will be less than 99.
+
+ LD A,B ; fetch positive exponent.
+ LD B,$FF ; initialize left hand digit to minus one.
+
+;; PF-E-TENS
+L169E: INC B ; increment ten count
+ SUB $0A ; subtract ten from exponent
+ JR NC,L169E ; loop back if greater than ten to PF-E-TENS
+
+ ADD A,$0A ; reverse last subtraction
+ LD C,A ; transfer remainder to C
+
+ LD A,B ; transfer ten value to A.
+ AND A ; test for zero.
+ JR Z,L16AD ; skip forward if so to PF-E-LOW
+
+ CALL L07EB ; routine OUT-CODE prints as digit '1' - '9'
+
+;; PF-E-LOW
+L16AD: LD A,C ; low byte to A
+ CALL L07EB ; routine OUT-CODE prints final digit of the
+ ; exponent.
+ RET ; return. >>
+
+; ---
+
+; this branch deals with zeros after decimal point.
+; e.g. .01 or .0000999
+
+;; PF-ZEROS
+L16B2: NEG ; negate makes number positive 1 to 4.
+ LD B,A ; zero count to B.
+
+ LD A,$1B ; prepare character '.'
+ RST 10H ; PRINT-A
+
+ LD A,$1C ; prepare a '0'
+
+;; PF-ZRO-LP
+L16BA: RST 10H ; PRINT-A
+ DJNZ L16BA ; loop back to PF-ZRO-LP
+
+ JR L16C8 ; forward to PF-FRAC-LP
+
+; ---
+
+; there is a need to print a leading zero e.g. 0.1 but not with .01
+
+;; PF-ZERO-1
+L16BF: LD A,$1C ; prepare character '0'.
+ RST 10H ; PRINT-A
+
+; this subroutine considers the decimal point and any trailing digits.
+; if the next character is a marked zero, $80, then nothing more to print.
+
+;; PF-DC-OUT
+L16C2: DEC (HL) ; decrement addressed character
+ INC (HL) ; increment it again
+ RET PE ; return with overflow (was 128) >>
+ ; as no fractional part
+
+; else there is a fractional part so print the decimal point.
+
+ LD A,$1B ; prepare character '.'
+ RST 10H ; PRINT-A
+
+; now enter a loop to print trailing digits
+
+;; PF-FRAC-LP
+L16C8: DEC (HL) ; test for a marked zero.
+ INC (HL) ;
+ RET PE ; return when digits exhausted >>
+
+ CALL L16D0 ; routine PF-NIBBLE
+ JR L16C8 ; back for all fractional digits to PF-FRAC-LP.
+
+; ---
+
+; subroutine to print right-hand nibble
+
+;; PF-NIBBLE
+L16D0: LD A,(HL) ; fetch addressed byte
+ AND $0F ; mask off lower 4 bits
+ CALL L07EB ; routine OUT-CODE
+ DEC HL ; decrement pointer.
+ RET ; return.
+
+
+; -------------------------------
+; THE 'PREPARE TO ADD' SUBROUTINE
+; -------------------------------
+; This routine is called twice to prepare each floating point number for
+; addition, in situ, on the calculator stack.
+; The exponent is picked up from the first byte which is then cleared to act
+; as a sign byte and accept any overflow.
+; If the exponent is zero then the number is zero and an early return is made.
+; The now redundant sign bit of the mantissa is set and if the number is
+; negative then all five bytes of the number are twos-complemented to prepare
+; the number for addition.
+; On the second invocation the exponent of the first number is in B.
+
+
+;; PREP-ADD
+L16D8: LD A,(HL) ; fetch exponent.
+ LD (HL),$00 ; make this byte zero to take any overflow and
+ ; default to positive.
+ AND A ; test stored exponent for zero.
+ RET Z ; return with zero flag set if number is zero.
+
+ INC HL ; point to first byte of mantissa.
+ BIT 7,(HL) ; test the sign bit.
+ SET 7,(HL) ; set it to its implied state.
+ DEC HL ; set pointer to first byte again.
+ RET Z ; return if bit indicated number is positive.>>
+
+; if negative then all five bytes are twos complemented starting at LSB.
+
+ PUSH BC ; save B register contents.
+ LD BC,$0005 ; set BC to five.
+ ADD HL,BC ; point to location after 5th byte.
+ LD B,C ; set the B counter to five.
+ LD C,A ; store original exponent in C.
+ SCF ; set carry flag so that one is added.
+
+; now enter a loop to twos-complement the number.
+; The first of the five bytes becomes $FF to denote a negative number.
+
+;; NEG-BYTE
+L16EC: DEC HL ; point to first or more significant byte.
+ LD A,(HL) ; fetch to accumulator.
+ CPL ; complement.
+ ADC A,$00 ; add in initial carry or any subsequent carry.
+ LD (HL),A ; place number back.
+ DJNZ L16EC ; loop back five times to NEG-BYTE
+
+ LD A,C ; restore the exponent to accumulator.
+ POP BC ; restore B register contents.
+
+ RET ; return.
+
+; ----------------------------------
+; THE 'FETCH TWO NUMBERS' SUBROUTINE
+; ----------------------------------
+; This routine is used by addition, multiplication and division to fetch
+; the two five-byte numbers addressed by HL and DE from the calculator stack
+; into the Z80 registers.
+; The HL register may no longer point to the first of the two numbers.
+; Since the 32-bit addition operation is accomplished using two Z80 16-bit
+; instructions, it is important that the lower two bytes of each mantissa are
+; in one set of registers and the other bytes all in the alternate set.
+;
+; In: HL = highest number, DE= lowest number
+;
+; : alt': :
+; Out: :H,B-C:C,B: num1
+; :L,D-E:D-E: num2
+
+;; FETCH-TWO
+L16F7: PUSH HL ; save HL
+ PUSH AF ; save A - result sign when used from division.
+
+ LD C,(HL) ;
+ INC HL ;
+ LD B,(HL) ;
+ LD (HL),A ; insert sign when used from multiplication.
+ INC HL ;
+ LD A,C ; m1
+ LD C,(HL) ;
+ PUSH BC ; PUSH m2 m3
+
+ INC HL ;
+ LD C,(HL) ; m4
+ INC HL ;
+ LD B,(HL) ; m5 BC holds m5 m4
+
+ EX DE,HL ; make HL point to start of second number.
+
+ LD D,A ; m1
+ LD E,(HL) ;
+ PUSH DE ; PUSH m1 n1
+
+ INC HL ;
+ LD D,(HL) ;
+ INC HL ;
+ LD E,(HL) ;
+ PUSH DE ; PUSH n2 n3
+
+ EXX ; - - - - - - -
+
+ POP DE ; POP n2 n3
+ POP HL ; POP m1 n1
+ POP BC ; POP m2 m3
+
+ EXX ; - - - - - - -
+
+ INC HL ;
+ LD D,(HL) ;
+ INC HL ;
+ LD E,(HL) ; DE holds n4 n5
+
+ POP AF ; restore saved
+ POP HL ; registers.
+ RET ; return.
+
+; -----------------------------
+; THE 'SHIFT ADDEND' SUBROUTINE
+; -----------------------------
+; The accumulator A contains the difference between the two exponents.
+; This is the lowest of the two numbers to be added
+
+;; SHIFT-FP
+L171A: AND A ; test difference between exponents.
+ RET Z ; return if zero. both normal.
+
+ CP $21 ; compare with 33 bits.
+ JR NC,L1736 ; forward if greater than 32 to ADDEND-0
+
+ PUSH BC ; preserve BC - part
+ LD B,A ; shift counter to B.
+
+; Now perform B right shifts on the addend L'D'E'D E
+; to bring it into line with the augend H'B'C'C B
+
+;; ONE-SHIFT
+L1722: EXX ; - - -
+ SRA L ; 76543210->C bit 7 unchanged.
+ RR D ; C->76543210->C
+ RR E ; C->76543210->C
+ EXX ; - - -
+ RR D ; C->76543210->C
+ RR E ; C->76543210->C
+ DJNZ L1722 ; loop back B times to ONE-SHIFT
+
+ POP BC ; restore BC
+ RET NC ; return if last shift produced no carry. >>
+
+; if carry flag was set then accuracy is being lost so round up the addend.
+
+ CALL L1741 ; routine ADD-BACK
+ RET NZ ; return if not FF 00 00 00 00
+
+; this branch makes all five bytes of the addend zero and is made during
+; addition when the exponents are too far apart for the addend bits to
+; affect the result.
+
+;; ADDEND-0
+L1736: EXX ; select alternate set for more significant
+ ; bytes.
+ XOR A ; clear accumulator.
+
+
+; this entry point (from multiplication) sets four of the bytes to zero or if
+; continuing from above, during addition, then all five bytes are set to zero.
+
+;; ZEROS-4/5
+L1738: LD L,$00 ; set byte 1 to zero.
+ LD D,A ; set byte 2 to A.
+ LD E,L ; set byte 3 to zero.
+ EXX ; select main set
+ LD DE,$0000 ; set lower bytes 4 and 5 to zero.
+ RET ; return.
+
+; -------------------------
+; THE 'ADD-BACK' SUBROUTINE
+; -------------------------
+; Called from SHIFT-FP above during addition and after normalization from
+; multiplication.
+; This is really a 32-bit increment routine which sets the zero flag according
+; to the 32-bit result.
+; During addition, only negative numbers like FF FF FF FF FF,
+; the twos-complement version of xx 80 00 00 01 say
+; will result in a full ripple FF 00 00 00 00.
+; FF FF FF FF FF when shifted right is unchanged by SHIFT-FP but sets the
+; carry invoking this routine.
+
+;; ADD-BACK
+L1741: INC E ;
+ RET NZ ;
+
+ INC D ;
+ RET NZ ;
+
+ EXX ;
+ INC E ;
+ JR NZ,L174A ; forward if no overflow to ALL-ADDED
+
+ INC D ;
+
+;; ALL-ADDED
+L174A: EXX ;
+ RET ; return with zero flag set for zero mantissa.
+
+
+; ---------------------------
+; THE 'SUBTRACTION' OPERATION
+; ---------------------------
+; just switch the sign of subtrahend and do an add.
+
+;; subtract
+L174C: LD A,(DE) ; fetch exponent byte of second number the
+ ; subtrahend.
+ AND A ; test for zero
+ RET Z ; return if zero - first number is result.
+
+ INC DE ; address the first mantissa byte.
+ LD A,(DE) ; fetch to accumulator.
+ XOR $80 ; toggle the sign bit.
+ LD (DE),A ; place back on calculator stack.
+ DEC DE ; point to exponent byte.
+ ; continue into addition routine.
+
+; ------------------------
+; THE 'ADDITION' OPERATION
+; ------------------------
+; The addition operation pulls out all the stops and uses most of the Z80's
+; registers to add two floating-point numbers.
+; This is a binary operation and on entry, HL points to the first number
+; and DE to the second.
+
+;; addition
+L1755: EXX ; - - -
+ PUSH HL ; save the pointer to the next literal.
+ EXX ; - - -
+
+ PUSH DE ; save pointer to second number
+ PUSH HL ; save pointer to first number - will be the
+ ; result pointer on calculator stack.
+
+ CALL L16D8 ; routine PREP-ADD
+ LD B,A ; save first exponent byte in B.
+ EX DE,HL ; switch number pointers.
+ CALL L16D8 ; routine PREP-ADD
+ LD C,A ; save second exponent byte in C.
+ CP B ; compare the exponent bytes.
+ JR NC,L1769 ; forward if second higher to SHIFT-LEN
+
+ LD A,B ; else higher exponent to A
+ LD B,C ; lower exponent to B
+ EX DE,HL ; switch the number pointers.
+
+;; SHIFT-LEN
+L1769: PUSH AF ; save higher exponent
+ SUB B ; subtract lower exponent
+
+ CALL L16F7 ; routine FETCH-TWO
+ CALL L171A ; routine SHIFT-FP
+
+ POP AF ; restore higher exponent.
+ POP HL ; restore result pointer.
+ LD (HL),A ; insert exponent byte.
+ PUSH HL ; save result pointer again.
+
+; now perform the 32-bit addition using two 16-bit Z80 add instructions.
+
+ LD L,B ; transfer low bytes of mantissa individually
+ LD H,C ; to HL register
+
+ ADD HL,DE ; the actual binary addition of lower bytes
+
+; now the two higher byte pairs that are in the alternate register sets.
+
+ EXX ; switch in set
+ EX DE,HL ; transfer high mantissa bytes to HL register.
+
+ ADC HL,BC ; the actual addition of higher bytes with
+ ; any carry from first stage.
+
+ EX DE,HL ; result in DE, sign bytes ($FF or $00) to HL
+
+; now consider the two sign bytes
+
+ LD A,H ; fetch sign byte of num1
+
+ ADC A,L ; add including any carry from mantissa
+ ; addition. 00 or 01 or FE or FF
+
+ LD L,A ; result in L.
+
+; possible outcomes of signs and overflow from mantissa are
+;
+; H + L + carry = L RRA XOR L RRA
+; ------------------------------------------------------------
+; 00 + 00 = 00 00 00
+; 00 + 00 + carry = 01 00 01 carry
+; FF + FF = FE C FF 01 carry
+; FF + FF + carry = FF C FF 00
+; FF + 00 = FF FF 00
+; FF + 00 + carry = 00 C 80 80
+
+ RRA ; C->76543210->C
+ XOR L ; set bit 0 if shifting required.
+
+ EXX ; switch back to main set
+ EX DE,HL ; full mantissa result now in D'E'D E registers.
+ POP HL ; restore pointer to result exponent on
+ ; the calculator stack.
+
+ RRA ; has overflow occurred ?
+ JR NC,L1790 ; skip forward if not to TEST-NEG
+
+; if the addition of two positive mantissas produced overflow or if the
+; addition of two negative mantissas did not then the result exponent has to
+; be incremented and the mantissa shifted one place to the right.
+
+ LD A,$01 ; one shift required.
+ CALL L171A ; routine SHIFT-FP performs a single shift
+ ; rounding any lost bit
+ INC (HL) ; increment the exponent.
+ JR Z,L17B3 ; forward to ADD-REP-6 if the exponent
+ ; wraps round from FF to zero as number is too
+ ; big for the system.
+
+; at this stage the exponent on the calculator stack is correct.
+
+;; TEST-NEG
+L1790: EXX ; switch in the alternate set.
+ LD A,L ; load result sign to accumulator.
+ AND $80 ; isolate bit 7 from sign byte setting zero
+ ; flag if positive.
+ EXX ; back to main set.
+
+ INC HL ; point to first byte of mantissa
+ LD (HL),A ; insert $00 positive or $80 negative at
+ ; position on calculator stack.
+
+ DEC HL ; point to exponent again.
+ JR Z,L17B9 ; forward if positive to GO-NC-MLT
+
+; a negative number has to be twos-complemented before being placed on stack.
+
+ LD A,E ; fetch lowest (rightmost) mantissa byte.
+ NEG ; Negate
+ CCF ; Complement Carry Flag
+ LD E,A ; place back in register
+
+ LD A,D ; ditto
+ CPL ;
+ ADC A,$00 ;
+ LD D,A ;
+
+ EXX ; switch to higher (leftmost) 16 bits.
+
+ LD A,E ; ditto
+ CPL ;
+ ADC A,$00 ;
+ LD E,A ;
+
+ LD A,D ; ditto
+ CPL ;
+ ADC A,$00 ;
+ JR NC,L17B7 ; forward without overflow to END-COMPL
+
+; else entire mantissa is now zero. 00 00 00 00
+
+ RRA ; set mantissa to 80 00 00 00
+ EXX ; switch.
+ INC (HL) ; increment the exponent.
+
+;; ADD-REP-6
+L17B3: JP Z,L1880 ; jump forward if exponent now zero to REPORT-6
+ ; 'Number too big'
+
+ EXX ; switch back to alternate set.
+
+;; END-COMPL
+L17B7: LD D,A ; put first byte of mantissa back in DE.
+ EXX ; switch to main set.
+
+;; GO-NC-MLT
+L17B9: XOR A ; clear carry flag and
+ ; clear accumulator so no extra bits carried
+ ; forward as occurs in multiplication.
+
+ JR L1828 ; forward to common code at TEST-NORM
+ ; but should go straight to NORMALIZE.
+
+
+; ----------------------------------------------
+; THE 'PREPARE TO MULTIPLY OR DIVIDE' SUBROUTINE
+; ----------------------------------------------
+; this routine is called twice from multiplication and twice from division
+; to prepare each of the two numbers for the operation.
+; Initially the accumulator holds zero and after the second invocation bit 7
+; of the accumulator will be the sign bit of the result.
+
+;; PREP-M/D
+L17BC: SCF ; set carry flag to signal number is zero.
+ DEC (HL) ; test exponent
+ INC (HL) ; for zero.
+ RET Z ; return if zero with carry flag set.
+
+ INC HL ; address first mantissa byte.
+ XOR (HL) ; exclusive or the running sign bit.
+ SET 7,(HL) ; set the implied bit.
+ DEC HL ; point to exponent byte.
+ RET ; return.
+
+; ------------------------------
+; THE 'MULTIPLICATION' OPERATION
+; ------------------------------
+;
+;
+
+;; multiply
+L17C6: XOR A ; reset bit 7 of running sign flag.
+ CALL L17BC ; routine PREP-M/D
+ RET C ; return if number is zero.
+ ; zero * anything = zero.
+
+ EXX ; - - -
+ PUSH HL ; save pointer to 'next literal'
+ EXX ; - - -
+
+ PUSH DE ; save pointer to second number
+
+ EX DE,HL ; make HL address second number.
+
+ CALL L17BC ; routine PREP-M/D
+
+ EX DE,HL ; HL first number, DE - second number
+ JR C,L1830 ; forward with carry to ZERO-RSLT
+ ; anything * zero = zero.
+
+ PUSH HL ; save pointer to first number.
+
+ CALL L16F7 ; routine FETCH-TWO fetches two mantissas from
+ ; calc stack to B'C'C,B D'E'D E
+ ; (HL will be overwritten but the result sign
+ ; in A is inserted on the calculator stack)
+
+ LD A,B ; transfer low mantissa byte of first number
+ AND A ; clear carry.
+ SBC HL,HL ; a short form of LD HL,$0000 to take lower
+ ; two bytes of result. (2 program bytes)
+ EXX ; switch in alternate set
+ PUSH HL ; preserve HL
+ SBC HL,HL ; set HL to zero also to take higher two bytes
+ ; of the result and clear carry.
+ EXX ; switch back.
+
+ LD B,$21 ; register B can now be used to count thirty
+ ; three shifts.
+ JR L17F8 ; forward to loop entry point STRT-MLT
+
+; ---
+
+; The multiplication loop is entered at STRT-LOOP.
+
+;; MLT-LOOP
+L17E7: JR NC,L17EE ; forward if no carry to NO-ADD
+
+ ; else add in the multiplicand.
+
+ ADD HL,DE ; add the two low bytes to result
+ EXX ; switch to more significant bytes.
+ ADC HL,DE ; add high bytes of multiplicand and any carry.
+ EXX ; switch to main set.
+
+; in either case shift result right into B'C'C A
+
+;; NO-ADD
+L17EE: EXX ; switch to alternate set
+ RR H ; C > 76543210 > C
+ RR L ; C > 76543210 > C
+ EXX ;
+ RR H ; C > 76543210 > C
+ RR L ; C > 76543210 > C
+
+;; STRT-MLT
+L17F8: EXX ; switch in alternate set.
+ RR B ; C > 76543210 > C
+ RR C ; C > 76543210 > C
+ EXX ; now main set
+ RR C ; C > 76543210 > C
+ RRA ; C > 76543210 > C
+ DJNZ L17E7 ; loop back 33 times to MLT-LOOP
+
+;
+
+ EX DE,HL ;
+ EXX ;
+ EX DE,HL ;
+ EXX ;
+ POP BC ;
+ POP HL ;
+ LD A,B ;
+ ADD A,C ;
+ JR NZ,L180E ; forward to MAKE-EXPT
+
+ AND A ;
+
+;; MAKE-EXPT
+L180E: DEC A ;
+ CCF ; Complement Carry Flag
+
+;; DIVN-EXPT
+L1810: RLA ;
+ CCF ; Complement Carry Flag
+ RRA ;
+ JP P,L1819 ; forward to OFLW1-CLR
+
+ JR NC,L1880 ; forward to REPORT-6
+
+ AND A ;
+
+;; OFLW1-CLR
+L1819: INC A ;
+ JR NZ,L1824 ; forward to OFLW2-CLR
+
+ JR C,L1824 ; forward to OFLW2-CLR
+
+ EXX ;
+ BIT 7,D ;
+ EXX ;
+ JR NZ,L1880 ; forward to REPORT-6
+
+;; OFLW2-CLR
+L1824: LD (HL),A ;
+ EXX ;
+ LD A,B ;
+ EXX ;
+
+; addition joins here with carry flag clear.
+
+;; TEST-NORM
+L1828: JR NC,L183F ; forward to NORMALIZE
+
+ LD A,(HL) ;
+ AND A ;
+
+;; NEAR-ZERO
+L182C: LD A,$80 ; prepare to rescue the most significant bit
+ ; of the mantissa if it is set.
+ JR Z,L1831 ; skip forward to SKIP-ZERO
+
+;; ZERO-RSLT
+L1830: XOR A ; make mask byte zero signaling set five
+ ; bytes to zero.
+
+;; SKIP-ZERO
+L1831: EXX ; switch in alternate set
+ AND D ; isolate most significant bit (if A is $80).
+
+ CALL L1738 ; routine ZEROS-4/5 sets mantissa without
+ ; affecting any flags.
+
+ RLCA ; test if MSB set. bit 7 goes to bit 0.
+ ; either $00 -> $00 or $80 -> $01
+ LD (HL),A ; make exponent $01 (lowest) or $00 zero
+ JR C,L1868 ; forward if first case to OFLOW-CLR
+
+ INC HL ; address first mantissa byte on the
+ ; calculator stack.
+ LD (HL),A ; insert a zero for the sign bit.
+ DEC HL ; point to zero exponent
+ JR L1868 ; forward to OFLOW-CLR
+
+; ---
+
+; this branch is common to addition and multiplication with the mantissa
+; result still in registers D'E'D E .
+
+;; NORMALIZE
+L183F: LD B,$20 ; a maximum of thirty-two left shifts will be
+ ; needed.
+
+;; SHIFT-ONE
+L1841: EXX ; address higher 16 bits.
+ BIT 7,D ; test the leftmost bit
+ EXX ; address lower 16 bits.
+
+ JR NZ,L1859 ; forward if leftmost bit was set to NORML-NOW
+
+ RLCA ; this holds zero from addition, 33rd bit
+ ; from multiplication.
+
+ RL E ; C < 76543210 < C
+ RL D ; C < 76543210 < C
+
+ EXX ; address higher 16 bits.
+
+ RL E ; C < 76543210 < C
+ RL D ; C < 76543210 < C
+
+ EXX ; switch to main set.
+
+ DEC (HL) ; decrement the exponent byte on the calculator
+ ; stack.
+
+ JR Z,L182C ; back if exponent becomes zero to NEAR-ZERO
+ ; it's just possible that the last rotation
+ ; set bit 7 of D. We shall see.
+
+ DJNZ L1841 ; loop back to SHIFT-ONE
+
+; if thirty-two left shifts were performed without setting the most significant
+; bit then the result is zero.
+
+ JR L1830 ; back to ZERO-RSLT
+
+; ---
+
+;; NORML-NOW
+L1859: RLA ; for the addition path, A is always zero.
+ ; for the mult path, ...
+
+ JR NC,L1868 ; forward to OFLOW-CLR
+
+; this branch is taken only with multiplication.
+
+ CALL L1741 ; routine ADD-BACK
+
+ JR NZ,L1868 ; forward to OFLOW-CLR
+
+ EXX ;
+ LD D,$80 ;
+ EXX ;
+ INC (HL) ;
+ JR Z,L1880 ; forward to REPORT-6
+
+; now transfer the mantissa from the register sets to the calculator stack
+; incorporating the sign bit already there.
+
+;; OFLOW-CLR
+L1868: PUSH HL ; save pointer to exponent on stack.
+ INC HL ; address first byte of mantissa which was
+ ; previously loaded with sign bit $00 or $80.
+
+ EXX ; - - -
+ PUSH DE ; push the most significant two bytes.
+ EXX ; - - -
+
+ POP BC ; pop - true mantissa is now BCDE.
+
+; now pick up the sign bit.
+
+ LD A,B ; first mantissa byte to A
+ RLA ; rotate out bit 7 which is set
+ RL (HL) ; rotate sign bit on stack into carry.
+ RRA ; rotate sign bit into bit 7 of mantissa.
+
+; and transfer mantissa from main registers to calculator stack.
+
+ LD (HL),A ;
+ INC HL ;
+ LD (HL),C ;
+ INC HL ;
+ LD (HL),D ;
+ INC HL ;
+ LD (HL),E ;
+
+ POP HL ; restore pointer to num1 now result.
+ POP DE ; restore pointer to num2 now STKEND.
+
+ EXX ; - - -
+ POP HL ; restore pointer to next calculator literal.
+ EXX ; - - -
+
+ RET ; return.
+
+; ---
+
+;; REPORT-6
+L1880: RST 08H ; ERROR-1
+ DEFB $05 ; Error Report: Arithmetic overflow.
+
+; ------------------------
+; THE 'DIVISION' OPERATION
+; ------------------------
+; "Of all the arithmetic subroutines, division is the most complicated and
+; the least understood. It is particularly interesting to note that the
+; Sinclair programmer himself has made a mistake in his programming ( or has
+; copied over someone else's mistake!) for
+; PRINT PEEK 6352 [ $18D0 ] ('unimproved' ROM, 6351 [ $18CF ] )
+; should give 218 not 225."
+; - Dr. Ian Logan, Syntax magazine Jul/Aug 1982.
+; [ i.e. the jump should be made to div-34th ]
+
+; First check for division by zero.
+
+;; division
+L1882: EX DE,HL ; consider the second number first.
+ XOR A ; set the running sign flag.
+ CALL L17BC ; routine PREP-M/D
+ JR C,L1880 ; back if zero to REPORT-6
+ ; 'Arithmetic overflow'
+
+ EX DE,HL ; now prepare first number and check for zero.
+ CALL L17BC ; routine PREP-M/D
+ RET C ; return if zero, 0/anything is zero.
+
+ EXX ; - - -
+ PUSH HL ; save pointer to the next calculator literal.
+ EXX ; - - -
+
+ PUSH DE ; save pointer to divisor - will be STKEND.
+ PUSH HL ; save pointer to dividend - will be result.
+
+ CALL L16F7 ; routine FETCH-TWO fetches the two numbers
+ ; into the registers H'B'C'C B
+ ; L'D'E'D E
+ EXX ; - - -
+ PUSH HL ; save the two exponents.
+
+ LD H,B ; transfer the dividend to H'L'H L
+ LD L,C ;
+ EXX ;
+ LD H,C ;
+ LD L,B ;
+
+ XOR A ; clear carry bit and accumulator.
+ LD B,$DF ; count upwards from -33 decimal
+ JR L18B2 ; forward to mid-loop entry point DIV-START
+
+; ---
+
+;; DIV-LOOP
+L18A2: RLA ; multiply partial quotient by two
+ RL C ; setting result bit from carry.
+ EXX ;
+ RL C ;
+ RL B ;
+ EXX ;
+
+;; div-34th
+L18AB: ADD HL,HL ;
+ EXX ;
+ ADC HL,HL ;
+ EXX ;
+ JR C,L18C2 ; forward to SUBN-ONLY
+
+;; DIV-START
+L18B2: SBC HL,DE ; subtract divisor part.
+ EXX ;
+ SBC HL,DE ;
+ EXX ;
+ JR NC,L18C9 ; forward if subtraction goes to NO-RSTORE
+
+ ADD HL,DE ; else restore
+ EXX ;
+ ADC HL,DE ;
+ EXX ;
+ AND A ; clear carry
+ JR L18CA ; forward to COUNT-ONE
+
+; ---
+
+;; SUBN-ONLY
+L18C2: AND A ;
+ SBC HL,DE ;
+ EXX ;
+ SBC HL,DE ;
+ EXX ;
+
+;; NO-RSTORE
+L18C9: SCF ; set carry flag
+
+;; COUNT-ONE
+L18CA: INC B ; increment the counter
+ JP M,L18A2 ; back while still minus to DIV-LOOP
+
+ PUSH AF ;
+ JR Z,L18B2 ; back to DIV-START
+
+; "This jump is made to the wrong place. No 34th bit will ever be obtained
+; without first shifting the dividend. Hence important results like 1/10 and
+; 1/1000 are not rounded up as they should be. Rounding up never occurs when
+; it depends on the 34th bit. The jump should be made to div-34th above."
+; - Dr. Frank O'Hara, "The Complete Spectrum ROM Disassembly", 1983,
+; published by Melbourne House.
+; (Note. on the ZX81 this would be JR Z,L18AB)
+;
+; However if you make this change, then while (1/2=.5) will now evaluate as
+; true, (.25=1/4), which did evaluate as true, no longer does.
+
+ LD E,A ;
+ LD D,C ;
+ EXX ;
+ LD E,C ;
+ LD D,B ;
+
+ POP AF ;
+ RR B ;
+ POP AF ;
+ RR B ;
+
+ EXX ;
+ POP BC ;
+ POP HL ;
+ LD A,B ;
+ SUB C ;
+ JP L1810 ; jump back to DIVN-EXPT
+
+; ------------------------------------------------
+; THE 'INTEGER TRUNCATION TOWARDS ZERO' SUBROUTINE
+; ------------------------------------------------
+;
+
+;; truncate
+L18E4: LD A,(HL) ; fetch exponent
+ CP $81 ; compare to +1
+ JR NC,L18EF ; forward, if 1 or more, to T-GR-ZERO
+
+; else the number is smaller than plus or minus 1 and can be made zero.
+
+ LD (HL),$00 ; make exponent zero.
+ LD A,$20 ; prepare to set 32 bits of mantissa to zero.
+ JR L18F4 ; forward to NIL-BYTES
+
+; ---
+
+;; T-GR-ZERO
+L18EF: SUB $A0 ; subtract +32 from exponent
+ RET P ; return if result is positive as all 32 bits
+ ; of the mantissa relate to the integer part.
+ ; The floating point is somewhere to the right
+ ; of the mantissa
+
+ NEG ; else negate to form number of rightmost bits
+ ; to be blanked.
+
+; for instance, disregarding the sign bit, the number 3.5 is held as
+; exponent $82 mantissa .11100000 00000000 00000000 00000000
+; we need to set $82 - $A0 = $E2 NEG = $1E (thirty) bits to zero to form the
+; integer.
+; The sign of the number is never considered as the first bit of the mantissa
+; must be part of the integer.
+
+;; NIL-BYTES
+L18F4: PUSH DE ; save pointer to STKEND
+ EX DE,HL ; HL points at STKEND
+ DEC HL ; now at last byte of mantissa.
+ LD B,A ; Transfer bit count to B register.
+ SRL B ; divide by
+ SRL B ; eight
+ SRL B ;
+ JR Z,L1905 ; forward if zero to BITS-ZERO
+
+; else the original count was eight or more and whole bytes can be blanked.
+
+;; BYTE-ZERO
+L1900: LD (HL),$00 ; set eight bits to zero.
+ DEC HL ; point to more significant byte of mantissa.
+ DJNZ L1900 ; loop back to BYTE-ZERO
+
+; now consider any residual bits.
+
+;; BITS-ZERO
+L1905: AND $07 ; isolate the remaining bits
+ JR Z,L1912 ; forward if none to IX-END
+
+ LD B,A ; transfer bit count to B counter.
+ LD A,$FF ; form a mask 11111111
+
+;; LESS-MASK
+L190C: SLA A ; 1 <- 76543210 <- o slide mask leftwards.
+ DJNZ L190C ; loop back for bit count to LESS-MASK
+
+ AND (HL) ; lose the unwanted rightmost bits
+ LD (HL),A ; and place in mantissa byte.
+
+;; IX-END
+L1912: EX DE,HL ; restore result pointer from DE.
+ POP DE ; restore STKEND from stack.
+ RET ; return.
+
+
+;********************************
+;** FLOATING-POINT CALCULATOR **
+;********************************
+
+; As a general rule the calculator avoids using the IY register.
+; Exceptions are val and str$.
+; 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.
+
+;; stk-zero 00 00 00 00 00
+L1915: DEFB $00 ;;Bytes: 1
+ DEFB $B0 ;;Exponent $00
+ DEFB $00 ;;(+00,+00,+00)
+
+;; stk-one 81 00 00 00 00
+L1918: DEFB $31 ;;Exponent $81, Bytes: 1
+ DEFB $00 ;;(+00,+00,+00)
+
+
+;; stk-half 80 00 00 00 00
+L191A: DEFB $30 ;;Exponent: $80, Bytes: 1
+ DEFB $00 ;;(+00,+00,+00)
+
+
+;; stk-pi/2 81 49 0F DA A2
+L191C: DEFB $F1 ;;Exponent: $81, Bytes: 4
+ DEFB $49,$0F,$DA,$A2 ;;
+
+;; stk-ten 84 20 00 00 00
+L1921: DEFB $34 ;;Exponent: $84, Bytes: 1
+ DEFB $20 ;;(+00,+00,+00)
+
+
+; ------------------------
+; THE 'TABLE OF ADDRESSES'
+; ------------------------
+;
+; starts with binary operations which have two operands and one result.
+; three pseudo binary operations first.
+
+;; tbl-addrs
+L1923: DEFW L1C2F ; $00 Address: $1C2F - jump-true
+ DEFW L1A72 ; $01 Address: $1A72 - exchange
+ DEFW L19E3 ; $02 Address: $19E3 - delete
+
+; true binary operations.
+
+ DEFW L174C ; $03 Address: $174C - subtract
+ DEFW L17C6 ; $04 Address: $176C - multiply
+ DEFW L1882 ; $05 Address: $1882 - division
+ DEFW L1DE2 ; $06 Address: $1DE2 - to-power
+ DEFW L1AED ; $07 Address: $1AED - or
+
+ DEFW L1AF3 ; $08 Address: $1B03 - no-&-no
+ DEFW L1B03 ; $09 Address: $1B03 - no-l-eql
+ DEFW L1B03 ; $0A Address: $1B03 - no-gr-eql
+ DEFW L1B03 ; $0B Address: $1B03 - nos-neql
+ DEFW L1B03 ; $0C Address: $1B03 - no-grtr
+ DEFW L1B03 ; $0D Address: $1B03 - no-less
+ DEFW L1B03 ; $0E Address: $1B03 - nos-eql
+ DEFW L1755 ; $0F Address: $1755 - addition
+
+ DEFW L1AF8 ; $10 Address: $1AF8 - str-&-no
+ DEFW L1B03 ; $11 Address: $1B03 - str-l-eql
+ DEFW L1B03 ; $12 Address: $1B03 - str-gr-eql
+ DEFW L1B03 ; $13 Address: $1B03 - strs-neql
+ DEFW L1B03 ; $14 Address: $1B03 - str-grtr
+ DEFW L1B03 ; $15 Address: $1B03 - str-less
+ DEFW L1B03 ; $16 Address: $1B03 - strs-eql
+ DEFW L1B62 ; $17 Address: $1B62 - strs-add
+
+; unary follow
+
+ DEFW L1AA0 ; $18 Address: $1AA0 - neg
+
+ DEFW L1C06 ; $19 Address: $1C06 - code
+ DEFW L1BA4 ; $1A Address: $1BA4 - val
+ DEFW L1C11 ; $1B Address: $1C11 - len
+ DEFW L1D49 ; $1C Address: $1D49 - sin
+ DEFW L1D3E ; $1D Address: $1D3E - cos
+ DEFW L1D6E ; $1E Address: $1D6E - tan
+ DEFW L1DC4 ; $1F Address: $1DC4 - asn
+ DEFW L1DD4 ; $20 Address: $1DD4 - acs
+ DEFW L1D76 ; $21 Address: $1D76 - atn
+ DEFW L1CA9 ; $22 Address: $1CA9 - ln
+ DEFW L1C5B ; $23 Address: $1C5B - exp
+ DEFW L1C46 ; $24 Address: $1C46 - int
+ DEFW L1DDB ; $25 Address: $1DDB - sqr
+ DEFW L1AAF ; $26 Address: $1AAF - sgn
+ DEFW L1AAA ; $27 Address: $1AAA - abs
+ DEFW L1ABE ; $28 Address: $1A1B - peek
+ DEFW L1AC5 ; $29 Address: $1AC5 - usr-no
+ DEFW L1BD5 ; $2A Address: $1BD5 - str$
+ DEFW L1B8F ; $2B Address: $1B8F - chrs
+ DEFW L1AD5 ; $2C Address: $1AD5 - not
+
+; end of true unary
+
+ DEFW L19F6 ; $2D Address: $19F6 - duplicate
+ DEFW L1C37 ; $2E Address: $1C37 - n-mod-m
+
+ DEFW L1C23 ; $2F Address: $1C23 - jump
+ DEFW L19FC ; $30 Address: $19FC - stk-data
+
+ DEFW L1C17 ; $31 Address: $1C17 - dec-jr-nz
+ DEFW L1ADB ; $32 Address: $1ADB - less-0
+ DEFW L1ACE ; $33 Address: $1ACE - greater-0
+ DEFW L002B ; $34 Address: $002B - end-calc
+ DEFW L1D18 ; $35 Address: $1D18 - get-argt
+ DEFW L18E4 ; $36 Address: $18E4 - truncate
+ DEFW L19E4 ; $37 Address: $19E4 - fp-calc-2
+ DEFW L155A ; $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 L1A7F ; $39 Address: $1A7F - series-xx $80 - $9F.
+ DEFW L1A51 ; $3A Address: $1A51 - stk-const-xx $A0 - $BF.
+ DEFW L1A63 ; $3B Address: $1A63 - st-mem-xx $C0 - $DF.
+ DEFW L1A45 ; $3C Address: $1A45 - get-mem-xx $E0 - $FF.
+
+; Aside: 3D - 7F are therefore unused calculator literals.
+; 39 - 7B would be available for expansion.
+
+; -------------------------------
+; THE 'FLOATING POINT CALCULATOR'
+; -------------------------------
+;
+;
+
+;; CALCULATE
+L199D: CALL L1B85 ; 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
+L19A0: 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
+L19A4: 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
+L19A7: 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
+L19AE: PUSH HL ; save pointer on stack *
+ AND A ; now test the literal
+ JP P,L19C2 ; 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 L19D0 ; forward to ENT-TABLE
+
+; ---
+
+; the branch was here with simple literals.
+
+;; FIRST-3D
+L19C2: CP $18 ; compare with first unary operations.
+ JR NC,L19CE ; 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
+L19CE: RLCA ; double the literal
+ LD L,A ; and store in L for indexing
+
+;; ENT-TABLE
+L19D0: LD DE,L1923 ; 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,L19A7 ; 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
+L19E3: 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
+L19E4: 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 L19AE ; 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
+L19EB: PUSH DE ; save
+ PUSH HL ; registers
+ LD BC,$0005 ; an overhead of five bytes
+ CALL L0EC5 ; 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
+
+;; duplicate
+;; MOVE-FP
+L19F6: CALL L19EB ; 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
+L19FC: LD H,D ; transfer STKEND
+ LD L,E ; to HL for result.
+
+;; STK-CONST
+L19FE: CALL L19EB ; 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 ??.
+
+ 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,L1A14 ; 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
+L1A14: 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
+ 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
+L1A27: 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 L1A27 ; loop back to STK-ZEROS until done.
+
+; -------------------------------
+; THE 'SKIP CONSTANTS' SUBROUTINE
+; -------------------------------
+; This routine traverses 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.
+
+;; SKIP-CONS
+L1A2D: AND A ; test if initially zero.
+
+;; SKIP-NEXT
+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
+ ; Note. not a fault but this has to be
+ ; moved elsewhere when running in RAM.
+ ;
+ CALL L19FE ; 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
+L1A3C: 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.
+
+;; get-mem-xx
+L1A45: PUSH DE ; save STKEND
+ LD HL,($401F) ; MEM is base address of the memory cells.
+ CALL L1A3C ; routine LOC-MEM so that HL = first byte
+ CALL L19F6 ; 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 isn't very efficient and it would have been better to hold the
+; numbers in full, five byte form and stack them in a similar manner
+; to that which would be used later for semi-tone table values.
+
+;; stk-const-xx
+L1A51: LD H,D ; save STKEND - required for result
+ LD L,E ;
+ EXX ; swap
+ PUSH HL ; save pointer to next literal
+ LD HL,L1915 ; Address: stk-zero - start of table of
+ ; constants
+ EXX ;
+ CALL L1A2D ; routine SKIP-CONS
+ CALL L19FE ; 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.
+
+;; st-mem-xx
+L1A63: PUSH HL ; save the result pointer.
+ EX DE,HL ; transfer to DE.
+ LD HL,($401F) ; fetch MEM the base of memory area.
+ CALL L1A3C ; routine LOC-MEM sets HL to the destination.
+ EX DE,HL ; swap - HL is start, DE is destination.
+ CALL L19F6 ; routine MOVE-FP.
+ ; note. a short ld bc,5; ldir
+ ; the embedded memory check is not required
+ ; so these instructions would be faster!
+ 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
+L1A72: LD B,$05 ; there are five bytes to be swapped
+
+; start of loop.
+
+;; SWAP-BYTE
+L1A74: LD A,(DE) ; each byte of second
+ LD C,(HL) ; each byte of first
+ EX DE,HL ; swap pointers
+ LD (DE),A ; store each byte of first
+ LD (HL),C ; store each byte of second
+ INC HL ; advance both
+ INC DE ; pointers.
+ DJNZ L1A74 ; loop back to SWAP-BYTE until all 5 done.
+
+ EX DE,HL ; even up the exchanges
+ ; so that DE addresses STKEND.
+ 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.
+
+;; series-xx
+L1A7F: LD B,A ; parameter $00 - $1F to B counter
+ CALL L19A0 ; 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
+L1A89: 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 L19FC ; routine STK-DATA is called directly to
+ ; push a value and advance H'L'.
+ CALL L19A4 ; 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 $EE ;;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
+;; negate
+L1AA0: 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
+L1AAA: 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 negative. If it is zero then it is left as such.
+
+;; sgn
+L1AAF: 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,L1AE0 ; 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
+L1ABE: CALL L0EA7 ; routine FIND-INT puts address in BC.
+ LD A,(BC) ; load contents into A register.
+
+;; IN-PK-STK
+L1AC2: JP L151D ; 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
+L1AC5: CALL L0EA7 ; routine FIND-INT to fetch the
+ ; supplied address into BC.
+
+ LD HL,L1520 ; 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 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
+;; greater-0
+L1ACE: 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 L1ADC ; 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
+;; not
+L1AD5: LD A,(HL) ; get exponent byte.
+ NEG ; negate - sets carry if non-zero.
+ CCF ; complement so carry set if zero, else reset.
+ JR L1AE0 ; 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
+L1ADB: 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
+L1ADC: 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
+L1AE0: PUSH HL ; save pointer to the first byte
+ LD B,$05 ; five bytes to do.
+
+;; FP-loop
+L1AE3: LD (HL),$00 ; insert a zero.
+ INC HL ;
+ DJNZ L1AE3 ; 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
+L1AED: LD A,(DE) ; fetch exponent of second number
+ AND A ; test it.
+ RET Z ; return if zero.
+
+ SCF ; set carry flag
+ JR L1AE0 ; 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-&-no
+L1AF3: LD A,(DE) ; fetch exponent of second number.
+ AND A ; test it.
+ RET NZ ; return if not zero.
+
+ JR L1AE0 ; 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-&-no
+L1AF8: 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,etc.
+L1B03: LD A,B ; transfer literal to accumulator.
+ SUB $08 ; subtract eight - which is not useful.
+
+ BIT 2,A ; isolate '>', '<', '='.
+
+ JR NZ,L1B0B ; skip to EX-OR-NOT with these.
+
+ DEC A ; else make $00-$02, $08-$0A to match bits 0-2.
+
+;; EX-OR-NOT
+L1B0B: RRCA ; the first RRCA sets carry for a swap.
+ JR NC,L1B16 ; 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 L1A72 ; 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
+L1B16: BIT 2,A ; test if a string comparison.
+ JR NZ,L1B21 ; 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 L174C ; routine subtract leaves result on stack.
+ JR L1B54 ; forward to END-TESTS
+
+; ---
+
+;; STRINGS
+L1B21: RRCA ; 2nd RRCA causes eql/neql to set carry.
+ PUSH AF ; save A and carry.
+
+ CALL L13F8 ; routine STK-FETCH gets 2nd string params
+ PUSH DE ; save start2 *.
+ PUSH BC ; and the length.
+
+ CALL L13F8 ; 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
+L1B2C: 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,L1B3D ; forward to SEC-PLUS if second not null.
+
+ OR C ; test length of first string.
+
+;; SECND-LOW
+L1B33: POP BC ; pop the second length off stack.
+ JR Z,L1B3A ; 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 L1B50 ; forward to leave via STR-TEST
+
+; ---
+; the branch was here with a match
+
+;; BOTH-NULL
+L1B3A: POP AF ; restore carry - set for eql/neql
+ JR L1B50 ; 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
+L1B3D: OR C ; test the length of first string.
+ JR Z,L1B4D ; 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,L1B4D ; forward to FRST-LESS if carry set
+
+ JR NZ,L1B33 ; 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 L1B2C ; back to BYTE-COMP
+
+; ---
+; the false condition.
+
+;; FRST-LESS
+L1B4D: 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
+L1B50: 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
+L1B54: POP AF ; pop carry - will be set if eql/neql
+ PUSH AF ; save it again.
+
+ CALL C,L1AD5 ; routine NOT sets true(1) if equal(0)
+ ; or, for strings, applies true result.
+ CALL L1ACE ; greater-0 ??????????
+
+
+ POP AF ; pop A
+ RRCA ; the third RRCA - test for '<=', '>=' or '<>'.
+ CALL NC,L1AD5 ; apply a terminal NOT if so.
+ RET ; return.
+
+; -------------------------
+; String concatenation ($17)
+; -------------------------
+; 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
+L1B62: CALL L13F8 ; routine STK-FETCH fetches string parameters
+ ; and deletes calculator stack entry.
+ PUSH DE ; save start address.
+ PUSH BC ; and length.
+
+ CALL L13F8 ; 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 L12C3 ; 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,L1B7D ; to OTHER-STR if null string
+
+ LDIR ; copy string to workspace.
+
+;; OTHER-STR
+L1B7D: POP BC ; now second length
+ POP HL ; and start of string
+ LD A,B ; test this one
+ OR C ; for zero length
+ JR Z,L1B85 ; skip forward to STK-PNTRS if so as complete.
+
+ LDIR ; else copy the bytes.
+ ; and continue into next routine which
+ ; sets the calculator stack pointers.
+
+; --------------------
+; Check stack pointers
+; --------------------
+; Register DE is set to STKEND and HL, the result pointer, is set to five
+; locations below this.
+; 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
+L1B85: 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.
+
+; ----------------
+; Handle CHR$ (2B)
+; ----------------
+; 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
+L1B8F: CALL L15CD ; routine FP-TO-A puts the number in A.
+
+ JR C,L1BA2 ; forward to REPORT-Bd if overflow
+ JR NZ,L1BA2 ; 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
+
+ CALL L12C3 ; routine STK-STO-$ stacks descriptor.
+
+ EX DE,HL ; make HL point to result and DE to STKEND.
+ RET ; return.
+
+; ---
+
+;; REPORT-Bd
+L1BA2: RST 08H ; ERROR-1
+ DEFB $0A ; Error Report: Integer out of range
+
+; ----------------------------
+; Handle VAL ($1A)
+; ----------------------------
+; 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
+L1BA4: LD HL,($4016) ; fetch value of system variable CH_ADD
+ PUSH HL ; and save on the machine stack.
+
+ CALL L13F8 ; 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 L0D92 ; routine CLASS-06 - SCANNING evaluates string
+ ; expression and checks for integer result.
+
+ CALL L0D22 ; 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 L0F55 ; 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 L1B85 ; 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.
+
+; ----------------
+; Handle STR$ (2A)
+; ----------------
+; 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.
+
+;; str$
+L1BD5: 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 L15DB ; 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.
+
+ CALL L12C3 ; 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
+L1C06: CALL L13F8 ; 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,L1C0E ; skip to STK-CODE with zero if the null string.
+
+ LD A,(DE) ; else fetch the first character.
+
+;; STK-CODE
+L1C0E: JP L151D ; 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
+L1C11: CALL L13F8 ; routine STK-FETCH to fetch and delete the
+ ; string parameters from the calculator stack.
+ ; register BC now holds the length of string.
+
+ JP L1520 ; 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
+L1C17: 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,L1C24 ; 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 to be polished for the ZX Spectrum.
+; See, without looking at the ZX Spectrum ROM, if you can get rid of the
+; relative jump.
+
+;; jump
+;; JUMP
+L1C23: EXX ;switch in pointer set
+
+;; JUMP-2
+L1C24: LD E,(HL) ; the jump byte 0-127 forward, 128-255 back.
+ XOR A ; clear accumulator.
+ BIT 7,E ; test if negative jump
+ JR Z,L1C2B ; skip, if positive, to JUMP-3.
+
+ CPL ; else change to $FF.
+
+;; JUMP-3
+L1C2B: 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
+L1C2F: LD A,(DE) ; collect exponent byte
+
+ AND A ; is result 0 or 1 ?
+ JR NZ,L1C23 ; 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
+L1C37: 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 or minus 3.4 as examples.
+
+;; int
+L1C46: 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
+L1C4E: 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
+L1C59: DEFB $34 ;;end-calc -4.
+
+ RET ; return.
+
+
+; ----------------
+; Exponential (23)
+; ----------------
+;
+;
+
+;; EXP
+;; exp
+L1C5B: RST 28H ;; FP-CALC
+ DEFB $30 ;;stk-data
+ 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 L15CD ; routine FP-TO-A
+ JR NZ,L1C9B ; to N-NEGTV
+
+ JR C,L1C99 ; to REPORT-6b
+
+ ADD A,(HL) ;
+ JR NC,L1CA2 ; to RESULT-OK
+
+
+;; REPORT-6b
+L1C99: RST 08H ; ERROR-1
+ DEFB $05 ; Error Report: Number too big
+
+;; N-NEGTV
+L1C9B: JR C,L1CA4 ; to RSLT-ZERO
+
+ SUB (HL) ;
+ JR NC,L1CA4 ; to RSLT-ZERO
+
+ NEG ; Negate
+
+;; RESULT-OK
+L1CA2: LD (HL),A ;
+ RET ; return.
+
+
+;; RSLT-ZERO
+L1CA4: 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
+L1CA9: RST 28H ;; FP-CALC
+ DEFB $2D ;;duplicate
+ DEFB $33 ;;greater-0
+ DEFB $00 ;;jump-true
+ DEFB $04 ;;to L1CB1, VALID
+
+ DEFB $34 ;;end-calc
+
+
+;; REPORT-Ab
+L1CAF: RST 08H ; ERROR-1
+ DEFB $09 ; Error Report: Invalid argument
+
+;; VALID
+L1CB1: DEFB $A0 ;;stk-zero Note. not
+ DEFB $02 ;;delete necessary.
+ DEFB $34 ;;end-calc
+ LD A,(HL) ;
+
+ LD (HL),$80 ;
+ CALL L151D ; routine STACK-A
+
+ 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
+L1CD2: DEFB $01 ;;exchange
+ DEFB $30 ;;stk-data
+ 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 '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
+L1D18: 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
+L1D35: 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
+L1D3C: 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
+L1D3E: 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
+L1D49: RST 28H ;; FP-CALC angle in radians
+ DEFB $35 ;;get-argt reduce - sign now correct.
+
+;; C-ENT
+L1D4B: 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
+L1D6E: 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
+L1D76: LD A,(HL) ; fetch exponent
+ CP $81 ; compare to that for 'one'
+ JR C,L1D89 ; 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
+L1D89: RST 28H ;; FP-CALC
+ DEFB $A0 ;;stk-zero
+
+;; CASES
+L1D8B: 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+d = 180 degrees and b+a = 180 degrees then c=a/2.
+;
+; A value higher than 1 gives the required error as attempting to find the
+; square root of a negative number generates an error in Sinclair BASIC.
+
+;; asn
+L1DC4: 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
+L1DD4: 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 'SQUARE ROOT' FUNCTION
+; --------------------------
+; (Offset $25: 'sqr')
+; Error A if argument is negative.
+; This routine is remarkable for its brevity - 7 bytes.
+; The ZX81 code was originally 9K and various techniques had to be
+; used to shoe-horn it into an 8K Rom chip.
+
+
+;; sqr
+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 'EXPONENTIATION' OPERATION
+; ------------------------------
+; (Offset $06: 'to-power')
+; This raises the first number X to the power of the second number Y.
+; As with the ZX80,
+; 0 ** 0 = 1
+; 0 ** +n = 0
+; 0 ** -n = arithmetic overflow.
+
+;; to-power
+L1DE2: 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.
+ DEFB $04 ;;multiply Y * LN X
+ DEFB $34 ;;end-calc
+
+ JP L1C5B ; jump back to EXP routine. ->
+
+; ---
+
+; 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
+L1DEE: 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.
+; There are some one and two-byte alternatives but perhaps the most formal
+; might have been to use end-calc; rst 08; defb 05.
+
+ DEFB $A1 ;;stk-one 0, 1.
+ DEFB $01 ;;exchange 1, 0.
+ DEFB $05 ;;division 1/0 >> error
+
+; ---
+
+;; ONE
+L1DFB: DEFB $02 ;;delete .
+ DEFB $A1 ;;stk-one 1.
+
+;; LAST
+L1DFD: DEFB $34 ;;end-calc last value 1 or 0.
+
+ RET ; return.
+
+; ---------------------
+; THE 'SPARE LOCATIONS'
+; ---------------------
+
+;; SPARE
+L1DFF: DEFB $FF ; That's all folks.
+
+
+
+; ------------------------
+; THE 'ZX81 CHARACTER SET'
+; ------------------------
+
+;; char-set - begins with space character.
+
+; $00 - Character: ' ' CHR$(0)
+
+L1E00: 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: ukp 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.
+
+