zx81-rom

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit 3e5d2faac223164a0a12c25510ddc6fe4392e3d7
Author: Brian Swetland <swetland@frotz.net>
Date:   Thu, 18 Aug 2022 22:33:42 -0700

Initial commit of rom sources from
https://cdn.hackaday.io/files/289631239152992/ZX81_dual_2018-02-09.htm

Author unknown.

Diffstat:
Azx81plus.asm | 15647+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 15647 insertions(+), 0 deletions(-)

diff --git a/zx81plus.asm b/zx81plus.asm @@ -0,0 +1,15647 @@ + + +; =========================================================== +; 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 ; 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 ; 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 ; jump forward if not to PRINT_CH. + + JP 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 ; gets next immediate + ; character. + JR 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 ; jump immediately to the CALCULATE routine. +#else + + JP 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 ; 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 ; (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 ; (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 ; 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 ; selects slow mode. + JP 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 ; (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 ; (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 up to and including the CALL = 43 cycles. +; The Called routine at 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 ; 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 ; 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 ; 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 ; (12/7) forward if bits 14-0 are not zero + ; to ANOTHER + + RLA ; (4) test bit 15 of FRAMES. + JR 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 ; 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 ; 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 ; 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 ; (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 ; (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 ; displays the bottom set of blank lines. + +; ___ + +mark_028F: +R_IX_2: + JP 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 ; 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 ; (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 ; [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 + JR C,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 + +#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 + +mark_0304: +DELAY_1: + DJNZ DELAY_1 + + DEC DE ; + LD A,D ; + OR E ; + JR NZ,HEADER ; back for delay to HEADER + +mark_030B: +OUT_NAME: + CALL OUT_BYTE + BIT 7,(HL) ; test for inverted bit. + INC HL ; address next character of name. + JR Z,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 + + CALL LOAD_SAVE ; >> + JR 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 ; self-loop + + CALL BREAK_1 ; test for BREAK key. + +mark_0332: +BREAK_2: + JR NC,REPORT_D ; forward with break to REPORT_D + + LD B,$1E ; set timing value. + +mark_0336: +DELAY_3: + + DJNZ DELAY_3 ; self-loop + + DEC C ; decrement counter + JR NZ,PULSES ; loop back + +mark_033B: +DELAY_4: + AND A ; clear carry for next bit test. + DJNZ DELAY_4 ; self loop (B is zero - 256) + + JR EACH_BIT ; loop back + + +; THE 'LOAD COMMAND' ROUTINE + + +mark_0340: +LOAD: + CALL 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 + JR 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 ; forward if so to BREAK_4 + + RLA ; reverse above rotation + RLA ; test tape bit. + JR C,GET_BIT ; forward if set to GET_BIT + + DJNZ BREAK_3 ; loop back + + POP AF ; drop the return address. + CP D ; ugh. + +mark_0361: +RESTART: + JP NC,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 ; 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 ; forward with null string + + CP (HL) ; else compare with string in memory. + JR NZ,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 ; 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 ; loads a byte + LD (HL),C ; insert assembled byte in memory. + CALL LOAD_SAVE ; >> + JR 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 ; loop back with carry to TRAILER + + DJNZ COUNTER + + POP DE ; + JR NZ,BIT_DONE + + CP $56 ; + JR NC,NEXT_BIT + +mark_039C: +BIT_DONE: + CCF ; complement carry flag + RL C ; + JR NC,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 ; 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 + LD A,(FLAGS) ; sv + ADD A,A ; + JP M,REPORT_C + + POP HL ; + RET NC ; + + PUSH HL ; + CALL SET_FAST + CALL 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 + 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 + +mark_03D5: +RAM_READ: + AND A ; + SBC HL,BC ; + ADD HL,BC ; + INC HL ; + JR NC,SET_TOP + + DEC (HL) ; + JR Z,SET_TOP + + DEC (HL) ; + JR Z,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 ; loop back for all twenty five to LINE + + LD (VARS),HL ; set system variable VARS to next location + + CALL CLEAR ; sets $80 end-marker and the + ; dynamic memory pointers E_LINE, STKBOT and + ; STKEND. + +mark_0413: +N_L_ONLY: + CALL CURSOR_IN ; inserts the cursor and + ; end-marker in the Edit Line also setting + ; size of lower display to two lines. + + CALL SLOW_FAST ; selects COMPUTE and DISPLAY + + +; THE 'BASIC LISTING' SECTION + + +mark_0419: +UPPER: + CALL CLS + LD HL,(E_PPC) ; sv + LD DE,(S_TOP) ; sv + AND A ; + SBC HL,DE ; + EX DE,HL ; + JR NC,ADDR_TOP + + ADD HL,DE ; + LD (S_TOP),HL ; sv + +mark_042D: +ADDR_TOP: + CALL LINE_ADDR + JR Z,LIST_TOP + + EX DE,HL ; + +mark_0433: +LIST_TOP: + CALL LIST_PROG + DEC (IY+BERG-RAMBASE) + JR NZ,LOWER + + LD HL,(E_PPC) ; sv + CALL LINE_ADDR + LD HL,(CH_ADD) ; sv + SCF ; Set Carry Flag + SBC HL,DE ; + LD HL,S_TOP ; sv + JR NC,INC_LINE + + EX DE,HL ; + LD A,(HL) ; + INC HL ; + LDI ; + LD (DE),A ; + JR 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 + CALL LINE_NUM + POP HL ; + +mark_0464: +KEY_INPUT: + BIT 5,(IY+FLAGX-RAMBASE) + JR NZ,LOWER ; forward + + LD (HL),D ; + DEC HL ; + LD (HL),E ; + JR 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 ; 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 ; forward if not + + LD BC,6 ; else six invisible bytes to be removed. + CALL RECLAIM_2 + JR EACH_CHAR ; back +; ___ + +mark_0482: +END_LINE: + CP ZX_NEWLINE ; + INC HL ; + JR NZ,EACH_CHAR + +mark_0487: +EDIT_LINE: + CALL CURSOR ; sets cursor K or L. + +mark_048A: +EDIT_ROOM: + CALL LINE_ENDS + LD HL,(E_LINE) ; sv + LD (IY+ERR_NR-RAMBASE),$FF + CALL COPY_LINE + BIT 7,(IY+ERR_NR-RAMBASE) + JR NZ,DISPLAY_6 + + LD A,(DF_SZ) ; + CP CHARS_VERTICAL ; $18 = 24 + JR NC,DISPLAY_6 + + INC A ; + LD (DF_SZ),A ; + LD B,A ; + LD C,1 ; + CALL LOC_ADDR + LD D,H ; + LD E,L ; + LD A,(HL) ; + +mark_04B1: +FREE_LINE: + DEC HL ; + CP (HL) ; + JR NZ,FREE_LINE + + INC HL ; + EX DE,HL ; + LD A,(RAMTOP+1) ; sv RAMTOP_hi + CP $4D ; + CALL C,RECLAIM_1 + JR 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 + +mark_04CF: +SLOW_DISP: + BIT 0,(HL) ; + JR Z,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 + CALL DECODE + + JR NC,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 ; forward, if was zero + + JR NZ,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 ; forward, if so + + LD E,A ; else set E to reduced value + +mark_04F2: +FUNC_BASE: + LD HL,K_FUNCT ; address of K_FUNCT table for function keys. + JR TABLE_ADD ; forward +; ___ +mark_04F7: +FETCH_1: + LD A,(HL) ; + CP ZX_NEWLINE ; + JR Z,K_L_KEY + + CP ZX_RND ; $40 + SET 7,A ; + JR C,ENTER + + LD HL,$00C7 ; (expr reqd) + +mark_0505: +TABLE_ADD: + ADD HL,DE ; + JR FETCH_3 + +; ___ + +mark_0508: +FETCH_2: + LD A,(HL) ; + BIT 2,(IY+FLAGS-RAMBASE) ; K or L mode ? + JR NZ,TEST_CURS + + ADD A,$C0 ; + CP $E6 ; + JR NC,TEST_CURS + +mark_0515: +FETCH_3: + LD A,(HL) ; + +mark_0516: +TEST_CURS: + CP $F0 ; + JP PE,KEY_SORT + +mark_051B: +ENTER: + LD E,A ; + CALL CURSOR + + LD A,E ; + CALL ADD_CHAR + +mark_0523: +BACK_NEXT: + JP LOWER ; back + + +; THE 'ADD CHARACTER' SUBROUTINE + +mark_0526: +ADD_CHAR: + CALL 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 + +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 + JR Z,TEST_CHAR + + CP ZX_A ; $26 + JR C,TEST_CHAR + + CP $DE ; ZX_THEN ?? + JR Z,K_MODE + +mark_0556: +L_MODE: + SET 2,(IY+FLAGS-RAMBASE) ; Signal use L mode + JR TEST_CHAR + + +; THE 'CLEAR_ONE' SUBROUTINE + +mark_055C: +CLEAR_ONE: + LD BC,$0001 ; + JP RECLAIM_2 + + +; THE 'EDITING KEYS' TABLE + +mark_0562: +ED_KEYS: + DEFW UP_KEY + DEFW DOWN_KEY + DEFW LEFT_KEY + DEFW RIGHT_KEY + DEFW FUNCTION + DEFW EDIT_KEY + DEFW N_L_KEY + DEFW RUBOUT + DEFW FUNCTION + DEFW FUNCTION + + + +; THE 'CURSOR LEFT' ROUTINE + +; +; + +mark_LEFT_KEY: +LEFT_KEY: + CALL LEFT_EDGE + LD A,(HL) ; + LD (HL),ZX_CURSOR ; + INC HL ; + JR GET_CODE + + +; THE 'CURSOR RIGHT' ROUTINE + + +mark_RIGHT_KEY: +RIGHT_KEY: + INC HL ; + LD A,(HL) ; + CP ZX_NEWLINE ; + JR Z,ENDED_2 + + LD (HL),ZX_CURSOR ; + DEC HL ; + +mark_0588: +GET_CODE: + LD (HL),A ; + +mark_0589: +ENDED_1: + JR BACK_NEXT + + +; THE 'RUBOUT' ROUTINE + + +mark_058B: +RUBOUT: + CALL LEFT_EDGE + CALL CLEAR_ONE + JR 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 + + +; THE 'CURSOR UP' ROUTINE + +; +; + +mark_059F: +UP_KEY: + LD HL,(E_PPC) ; sv + CALL LINE_ADDR + EX DE,HL ; + CALL LINE_NUM + LD HL,E_PPC+1 ; point to system variable E_PPC_hi + JP KEY_INPUT ; jump back + + +; THE 'FUNCTION KEY' ROUTINE + +; +; + +mark_FUNCTION: +FUNCTION: + LD A,E ; + AND $07 ; + LD (MODE),A ; sv + JR ENDED_2 ; back + + +; THE 'COLLECT LINE NUMBER' SUBROUTINE + +mark_05B7: +ZERO_DE: + EX DE,HL ; + LD DE,DISPLAY_6 + 1 ; $04C2 - a location addressing two zeros. + +; -> + +mark_05BB: +LINE_NUM: + LD A,(HL) ; + AND $C0 ; + JR NZ,ZERO_DE + + LD D,(HL) ; + INC HL ; + LD E,(HL) ; + RET ; + + +; THE 'EDIT KEY' ROUTINE + + +mark_EDIT_KEY: +EDIT_KEY: + CALL LINE_ENDS ; clears lower display. + + LD HL,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 (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 ; gets address or that of + ; the following line. + CALL 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 ; 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 ; sets STKEND from HL. + + JR ENDED_2 ; back to ENDED_2 and after 3 more jumps + ; to 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 + + LD HL,LOWER ; prepare address: LOWER + + BIT 5,(IY+FLAGX-RAMBASE) + JR NZ,NOW_SCAN + + LD HL,(E_LINE) ; sv + LD A,(HL) ; + CP $FF ; + JR Z,STK_UPPER + + CALL CLEAR_PRB + CALL CLS + +mark_0626: +STK_UPPER: + LD HL,UPPER ; Address: UPPER + +mark_0629: +NOW_SCAN: + PUSH HL ; push routine address (LOWER or UPPER). + CALL LINE_SCAN + POP HL ; + CALL CURSOR + CALL CLEAR_ONE + CALL E_LINE_NUM + JR NZ,N_L_INP + + LD A,B ; + OR C ; + JP NZ,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 ; forward + +; ___ + +mark_064E: +N_L_INP: + CP ZX_NEWLINE ; + JR Z,N_L_NULL + + LD BC,(T_ADDR) ; + CALL 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 + + LD (IY+FLAGS-RAMBASE),$80 + EX DE,HL ; + +mark_066C: +NEXT_LINE: + LD (NXTLIN),HL ; + EX DE,HL ; + CALL TEMP_PTR2 + CALL 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 + RES 5,(IY+FLAGX-RAMBASE) + BIT 7,(IY+ERR_NR-RAMBASE) + JR Z,STOP_LINE + + LD HL,(NXTLIN) ; + AND (HL) ; + JR NZ,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 + JR C,NEXT_LINE + + LD HL,ERR_NR + BIT 7,(HL) + JR Z,STOP_LINE + + LD (HL),$0C + +mark_06AE: +STOP_LINE: + BIT 7,(IY+PR_CC-RAMBASE) + CALL Z,COPY_BUFF +; +#if 0 + LD BC,$0121 ; +#else + LD BC,256*1 + CHARS_HORIZONTAL + 1 +#endif +; +; + CALL LOC_ADDR + LD A,(ERR_NR) + LD BC,(PPC) + INC A + JR Z,REPORT + + CP $09 + JR NZ,CONTINUE + + INC BC + +mark_06CA: +CONTINUE: + LD (OLDPPC),BC ; + JR NZ,REPORT + + DEC BC ; + +mark_06D1: +REPORT: + CALL OUT_CODE + LD A,ZX_SLASH + + RST _PRINT_A + CALL OUT_NUM + CALL CURSOR_IN + JP DISPLAY_6 + +; ___ + +mark_06E0: +N_L_LINE: + LD (E_PPC),BC ; + LD HL,(CH_ADD) ; + EX DE,HL ; + LD HL,N_L_ONLY + PUSH HL ; + LD HL,(STKBOT) ; + SBC HL,DE ; + PUSH HL ; + PUSH BC ; + CALL SET_FAST + CALL CLS + POP HL ; + CALL LINE_ADDR + JR NZ,COPY_OVER + + CALL NEXT_ONE + CALL 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 + CALL 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 + + 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 + +mark_073E: +LIST_PROG: + LD E,$00 ; + +mark_0740: +UNTIL_END: + CALL 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 ; loop back to UNTIL_END + + +; THE 'PRINT A BASIC LINE' SUBROUTINE + + +mark_0745: +OUT_LINE: + LD BC,(E_PPC) ; sv + CALL CP_LINES + LD D,$92 ; + JR Z,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 + 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 + + LD A,ZX_INV_S ; $B8 ; + + RST _PRINT_A + +mark_077C: +TEST_NUM: + LD HL,(CH_ADD) ; + LD A,(HL) ; + INC HL ; + CALL NUMBER + LD (CH_ADD),HL ; + JR Z,MORE_LINE + + CP ZX_CURSOR ; + JR Z,OUT_CURS + + CP ZX_NEWLINE ; + JR Z,OUT_CH + + BIT 6,A ; + JR Z,NOT_TOKEN + + CALL TOKENS + JR MORE_LINE +; ___ + +mark_079A: +NOT_TOKEN: + RST _PRINT_A + JR 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 ; 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 + JR 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 + + INC C ; + RET NZ ; + + LD C,B ; + DEC L ; + LD L,1 ; + JR NZ,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 + +; ___ +; 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 ; loop + + SBC HL,BC ; undo last iteration + DEC A ; undo last iteration + JR Z,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 + +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 + + CALL ENTER_CH + JR PRINT_EXX + +; ___ + +mark_0802: +LPRINT_A: + CALL 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 + +mark_0812: +TEST_N_L: + LD A,ZX_NEWLINE ; + CP D ; + JR Z,WRITE_N_L + + LD HL,(DF_CC) ; + CP (HL) ; + LD A,D ; + JR NZ,WRITE_CH + + DEC C ; + JR NZ,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 + + AND A ; + JR NZ,TEST_N_L + +mark_0835: +REPORT_5: + LD L,4 ; 'No more room on screen' + JP ERROR_3 + +; ___ + +mark_083A: +EXPAND_1: + CALL 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 + + +; 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 ; 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 ; 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 ; 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 + + 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 + JR C,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 ; jump forward with no printer to COPY_END + + JR NC,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 ; 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 ; 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 ; loop for all eight bits to COPY_BITS + + POP HL ; * restore character pointer. + JR 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 ; 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 ; 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 ; 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 + 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 ; 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 + +mark_08FA: +TEST_VAL: + CP (IY+DF_SZ-RAMBASE) + JP C,REPORT_5 + + INC A ; + LD B,A ; + LD A,CHARS_HORIZONTAL-1 ; originally 31 + + SUB C ; + +mark_0905: +WRONG_VAL: + JP C,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 + + 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 + + DJNZ 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 + 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 + + EX DE,HL ; restore HL + INC HL ; + LD (DF_CC),HL ; + RET ; + + +; THE 'EXPAND TOKENS' SUBROUTINE + + +mark_094B: +TOKENS: + PUSH AF ; + CALL TOKEN_ADD + JR NC,ALL_CHARS + + BIT 0,(IY+FLAGS-RAMBASE) ; Leading space if set + JR NZ,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 + + POP BC ; + BIT 7,B ; + RET Z ; + + CP ZX_COMMA ; $1A == 26 + JR Z,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 + +; ___ + +mark_0975: +TOKEN_ADD: + PUSH HL ; + LD HL,TOKEN_TABLE + BIT 7,A ; + JR Z,TEST_HIGH + + AND $3F ; + +mark_097F: +TEST_HIGH: + CP $43 ; + JR NC,FOUND + + LD B,A ; + INC B ; + +mark_0985: +WORDS: + BIT 7,(HL) ; + INC HL ; + JR Z,WORDS + + DJNZ WORDS + + BIT 6,A ; + JR NZ,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 + POP HL ; + CALL 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 + + 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 + + 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 + RET NC ; + + PUSH BC ; + CALL NEXT_ONE + EX DE,HL ; + JR 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 + + BIT 5,A ; + JR Z,NEXT_0_4 ; skip forward + + ADD A,A ; + JP M,NEXT_PLUS_FIVE + + CCF ; Complement Carry Flag + +mark_0A01: +NEXT_PLUS_FIVE: + LD BC,$0005 ; + JR NC,NEXT_LETT + + LD C,$11 ; 17 + +mark_0A08: +NEXT_LETT: + RLA ; + INC HL ; + LD A,(HL) ; + JR NC,NEXT_LETT ; loop + + JR 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 + POP BC ; + DEC B ; + JR 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 + POP BC ; + LD A,(RAMTOP+1) ; is RAMTOP_hi + CP $4D ; + JR C,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 ; prints a space + LD HL,(S_POSN) ; + LD A,L ; + OR H ; + AND $7E ; + JR NZ,CLEAR_LOC + + JP 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 + +mark_0A60: +RECLAIM_2: + PUSH BC ; + LD A,B ; + CPL ; + LD B,A ; + LD A,C ; + CPL ; + LD C,A ; + INC BC ; + CALL 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 + + RST _GET_CHAR + BIT 5,(IY+FLAGX-RAMBASE) + RET NZ ; + + LD HL,MEM_0_1st ; + LD (STKEND),HL ; + CALL INT_TO_FP + CALL FP_TO_BC + JR C,NO_NUMBER ; to NO_NUMBER + + LD HL,-10000 ; $D8F0 ; value '-10000' + ADD HL,BC ; + +mark_0A91: +NO_NUMBER: + JP C,REPORT_C ; to REPORT_C + + CP A ; + JP 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 + + LD H,B ; HL := BC + LD L,C ; + LD E,$FF ; + JR 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 + LD BC,-100 ; $FF9C ; + CALL OUT_DIGIT + LD C,-10 ; $F6 ; B is already FF, so saves a byte. + CALL OUT_DIGIT + LD A,L ; + +mark_0ABF: +UNITS: + CALL 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 ; 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 ; to PRINT_END + +mark_0AD5: +PRINT_1: + SUB ZX_COMMA ; $1A == 26 + ADC A,$00 ; + JR Z,SPACING ; to SPACING + ; + ; Compare with AT, + ; less comma recently subtracted. + ; + CP ZX_AT-ZX_COMMA ; $A7 == 167 + JR NZ,NOT_AT ; + + + RST _NEXT_CHAR + CALL CLASS_6 + CP ZX_COMMA ; $1A = 26 + JP NZ,REPORT_C ; + + RST _NEXT_CHAR + CALL CLASS_6 + CALL SYNTAX_ON + + RST _FP_CALC ;; + DEFB __exchange ;; + DEFB __end_calc ;; + + CALL STK_TO_BC + CALL PRINT_AT + JR PRINT_ON +; ___ + +mark_0AFA: +NOT_AT: + CP ZX_TAB-ZX_COMMA ; $A8 == 168 + JR NZ,NOT_TAB + + + RST _NEXT_CHAR + CALL CLASS_6 + CALL SYNTAX_ON + CALL STK_TO_A + JP NZ,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 + + SUB (IY+PR_CC-RAMBASE) + SET 7,A ; + ADD A,$3C ; 60 + CALL NC,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 + SET 0,(IY+FLAGS-RAMBASE) ; sv FLAGS - Suppress leading space + JR PRINT_ON +; ___ + +mark_0B31: +NOT_TAB: + CALL SCANNING + CALL PRINT_STK + +mark_0B37: +PRINT_ON: + RST _GET_CHAR + SUB ZX_COMMA ; $1A + ADC A,0 ; + JR Z,SPACING + + CALL CHECK_END + JP PRINT_END +; ___ +mark_0B44: +SPACING: + CALL NC,FIELD + + RST _NEXT_CHAR + CP ZX_NEWLINE ; + RET Z ; + + JP PRINT_1 +; ___ +mark_0B4E: +SYNTAX_ON: + CALL SYNTAX_Z + RET NZ ; + + POP HL ; + JR PRINT_ON +; ___ +mark_0B55: +PRINT_STK: + CALL UNSTACK_Z + BIT 6,(IY+FLAGS-RAMBASE) ; Numeric or string result? + CALL Z,STK_FETCH + JR Z,PR_STR_4 + + JP 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 + + CP $C0 ; + JR Z,PR_STR_1 + + PUSH BC ; + CALL TOKENS + POP BC ; + JR PR_STR_3 + +; ___ + +mark_0B84: +PRINT_END: + CALL UNSTACK_Z + LD A,ZX_NEWLINE ; + + RST _PRINT_A + RET ; + +; ___ + +mark_0B8B: +FIELD: + CALL 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 + + LD A,$5D ; + SUB (IY+PR_CC-RAMBASE) + +mark_0BA4: +CENTRE: + LD C,$11 ; + CP C ; + JR NC,RIGHT + + LD C,$01 ; + +mark_0BAB: +RIGHT: + CALL 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 + 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 + + LD B,A ; + LD A,$01 ; + SRA B ; + JR NC,COLUMNS + + LD A,$04 ; + +mark_0BC5: +COLUMNS: + SRA C ; + JR NC,FIND_ADDR + + RLCA ; + +mark_0BCA: +FIND_ADDR: + PUSH AF ; + CALL PRINT_AT + LD A,(HL) ; + RLCA ; + CP ZX_BRACKET_LEFT ; $10 + JR NC,TABLE_PTR + + RRCA ; + JR NC,SQ_SAVED + + XOR $8F ; + +mark_0BD9: +SQ_SAVED: + LD B,A ; + +mark_0BDA: +TABLE_PTR: + LD DE,P_UNPLOT ; Address: P_UNPLOT + LD A,(T_ADDR) ; get T_ADDR_lo + SUB E ; + JP M,PLOT + + POP AF ; + CPL ; + AND B ; + JR 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 + + 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 + LD B,A ; + PUSH BC ; + CALL 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 + JP C,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 + CALL 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 + + +; THE 'SYNTAX' TABLES + + +; i) The Offset table + +mark_0C29: +offset_t: + DEFB P_LPRINT - $ ; 8B offset + DEFB P_LLIST - $ ; 8D offset + DEFB P_STOP - $ ; 2D offset + DEFB P_SLOW - $ ; 7F offset + DEFB P_FAST - $ ; 81 offset + DEFB P_NEW - $ ; 49 offset + DEFB P_SCROLL - $ ; 75 offset + DEFB P_CONT - $ ; 5F offset + DEFB P_DIM - $ ; 40 offset + DEFB P_REM - $ ; 42 offset + DEFB P_FOR - $ ; 2B offset + DEFB P_GOTO - $ ; 17 offset + DEFB P_GOSUB - $ ; 1F offset + DEFB P_INPUT - $ ; 37 offset + DEFB P_LOAD - $ ; 52 offset + DEFB P_LIST - $ ; 45 offset + DEFB P_LET - $ ; 0F offset + DEFB P_PAUSE - $ ; 6D offset + DEFB P_NEXT - $ ; 2B offset + DEFB P_POKE - $ ; 44 offset + DEFB P_PRINT - $ ; 2D offset + DEFB P_PLOT - $ ; 5A offset + DEFB P_RUN - $ ; 3B offset + DEFB P_SAVE - $ ; 4C offset + DEFB P_RAND - $ ; 45 offset + DEFB P_IF - $ ; 0D offset + DEFB P_CLS - $ ; 52 offset + DEFB P_UNPLOT - $ ; 5A offset + DEFB P_CLEAR - $ ; 4D offset + DEFB P_RETURN - $ ; 15 offset + DEFB 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 + +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 + +mark_0C54: +P_GOSUB: + DEFB _CLASS_06 ; A numeric expression must follow. + DEFB _CLASS_00 ; No further operands. + DEFW GOSUB + +mark_0C58: +P_STOP: + DEFB _CLASS_00 ; No further operands. + DEFW STOP + +mark_0C5B: +P_RETURN: + DEFB _CLASS_00 ; No further operands. + DEFW 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 + +mark_0C66: +P_NEXT: + DEFB _CLASS_04 ; A single character variable must + ; follow. + DEFB _CLASS_00 ; No further operands. + DEFW NEXT + +mark_0C6A: +P_PRINT: + DEFB _CLASS_05 ; Variable syntax checked entirely + ; by routine. + DEFW PRINT ; not LPRINT ??? + +mark_0C6D: +P_INPUT: + DEFB _CLASS_01 ; A variable is required. + DEFB _CLASS_00 ; No further operands. + DEFW INPUT + +mark_0C71: +P_DIM: + DEFB _CLASS_05 ; Variable syntax checked entirely + ; by routine. + DEFW DIM + +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 + +mark_0C7A: +P_RUN: + DEFB _CLASS_03 ; A numeric expression may follow + ; else default to zero. + DEFW RUN + +mark_0C7D: +P_LIST: + DEFB _CLASS_03 ; A numeric expression may follow + ; else default to zero. + DEFW 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 + +mark_0C86: +P_RAND: + DEFB _CLASS_03 ; A numeric expression may follow + ; else default to zero. + DEFW RAND + +mark_0C89: +P_LOAD: + DEFB _CLASS_05 ; Variable syntax checked entirely + ; by routine. + DEFW LOAD + +mark_0C8C: +P_SAVE: + DEFB _CLASS_05 ; Variable syntax checked entirely + ; by routine. + DEFW SAVE + +mark_0C8F: +P_CONT: + DEFB _CLASS_00 ; No further operands. + DEFW CONT + +mark_0C92: +P_CLEAR: + DEFB _CLASS_00 ; No further operands. + DEFW CLEAR + +mark_0C95: +P_CLS: + DEFB _CLASS_00 ; No further operands. + DEFW 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 + +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 + +mark_0CA4: +P_SCROLL: + DEFB _CLASS_00 ; No further operands. + DEFW SCROLL + +mark_0CA7: +P_PAUSE: + DEFB _CLASS_06 ; A numeric expression must follow. + DEFB _CLASS_00 ; No further operands. + DEFW PAUSE + +mark_0CAB: +P_SLOW: + DEFB _CLASS_00 ; No further operands. + DEFW SLOW + +mark_0CAE: +P_FAST: + DEFB _CLASS_00 ; No further operands. + DEFW FAST + +mark_0CB1: +P_COPY: + DEFB _CLASS_00 ; No further operands. + DEFW COPY + +mark_0CB4: +P_LPRINT: + DEFB _CLASS_05 ; Variable syntax checked entirely + ; by routine. + DEFW LPRINT + +mark_0CB7: +P_LLIST: + DEFB _CLASS_03 ; A numeric expression may follow + ; else default to zero. + DEFW LLIST + + + +; THE 'LINE SCANNING' ROUTINE + + +mark_0CBA: +LINE_SCAN: + LD (IY+FLAGS-RAMBASE),1 + CALL E_LINE_NUM + +mark_0CC1: +LINE_RUN: + CALL SET_MIN + LD HL,ERR_NR ; + LD (HL),$FF ; + LD HL,FLAGX ; + BIT 5,(HL) ; + JR Z,LINE_NULL + + CP $E3 ; 'STOP' ? + LD A,(HL) ; + JP NZ,INPUT_REP + + CALL 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 ; forward if less + + LD C,A ; reduced token to C + LD HL,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 +; ___ + +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 + PUSH BC ; is pushed on machine stack. + + LD C,A ; + CP ZX_QUOTE ; $0B + JR NC,SEPARATOR + + LD HL,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 + ; 'Nonsense in BASIC' + + RST _NEXT_CHAR + RET ; return + + + +; THE 'COMMAND CLASS' TABLE + +; +mark_0D16: +class_tbl: + DEFB CLASS_0 - $ ; 17 offset to; Address: CLASS_0 + DEFB CLASS_1 - $ ; 25 offset to; Address: CLASS_1 + DEFB CLASS_2 - $ ; 53 offset to; Address: CLASS_2 + DEFB CLASS_3 - $ ; 0F offset to; Address: CLASS_3 + DEFB CLASS_4 - $ ; 6B offset to; Address: CLASS_4 + DEFB CLASS_5 - $ ; 13 offset to; Address: CLASS_5 + DEFB 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 + 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 + ; 'Nonsense in BASIC' + + +; COMMAND CLASSES 03, 00, 05 + + +mark_0D28: +CLASS_3: + CP ZX_NEWLINE ; + CALL NUMBER_TO_STK + +mark_0D2D: +CLASS_0: + CP A ; + +mark_0D2E: +CLASS_5: + POP BC ; + CALL Z,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 + +mark_0D3F: +CLASS_4_2: + LD (IY+FLAGX-RAMBASE),$00 + JR NC,SET_STK + + SET 1,(IY+FLAGX-RAMBASE) + JR NZ,SET_STRLN + + +mark_0D4B: +REPORT_2: + RST _ERROR_1 + DEFB $01 ; Error Report: Variable not found +; ___ + +mark_0D4D: +SET_STK: + CALL Z,STK_VAR + BIT 6,(IY+FLAGS-RAMBASE) ; Numeric or string result? + JR NZ,SET_STRLN + + XOR A ; + CALL SYNTAX_Z + CALL NZ,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 + POP AF ; + LD BC,LET ; Address: LET + LD D,(IY+FLAGS-RAMBASE) + XOR D ; + AND $40 ; + JR NZ,REPORT_C ; to REPORT_C + + BIT 7,D ; + JR NZ,CLASS_END ; to CLASS_END + + JR CHECK_2 ; to CHECK_2 +; ___ + +mark_0D85: +CLASS_4: + CALL LOOK_VARS + PUSH AF ; + LD A,C ; + OR $9F ; + INC A ; + JR NZ,REPORT_C ; to REPORT_C + + POP AF ; + JR CLASS_4_2 ; to CLASS_4_2 + +; ___ + +mark_0D92: +CLASS_6: + CALL 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 ; back to CLASS_6 with a non-zero number. + + CALL 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 + JR Z,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 ; jump back + + +; THE 'FOR' COMMAND ROUTINE + +; +; + +mark_0DB9: +FOR: + CP ZX_STEP ; is current character 'STEP' ? + JR NZ,F_USE_ONE ; forward if not + + + RST _NEXT_CHAR + CALL CLASS_6 ; stacks the number + CALL CHECK_END + JR F_REORDER ; forward to F_REORDER +; ___ + +mark_0DC6: +F_USE_ONE: + CALL 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 + + 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 + + SLA C ; + CALL 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 ; 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 ; forward at end of program + + PUSH BC ; save letter + CALL 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 ; sets CH_ADD + + RST _GET_CHAR + CP ZX_NEXT ; + EX DE,HL ; next line to HL. + JR NZ,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 ; 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 + + LD HL,(DEST) + BIT 7,(HL) + JR Z,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 + 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 +; ___ + +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 - $ ;; + + DEFB __exchange ;; + +mark_0E62: +LMT_V_VAL: + DEFB __subtract ;; + DEFB __greater_0 ;; + DEFB __jump_true ;; + DEFB 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 + LD A,B ; test value + OR C ; for zero + JR NZ,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 ; 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 + LD H,B ; + LD L,C ; + +mark_0E86: +GOTO_2: + LD A,H ; + CP $F0 ; ZX_LIST ??? + JR NC,REPORT_B + + CALL LINE_ADDR + LD (NXTLIN),HL ; sv + RET ; + + +; THE 'POKE' COMMAND ROUTINE + + +mark_0E92: +POKE: + CALL FP_TO_A + JR C,REPORT_B ; forward, with overflow + + JR Z,POKE_SAVE ; forward, if positive + + NEG ; negate + +mark_0E9B: +POKE_SAVE: + PUSH AF ; preserve value. + CALL 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 + JR C,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 + JP 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 + 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 + + 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 + + +; THE 'RETURN' COMMAND ROUTINE + + +mark_0ED8: +RETURN: + POP HL ; + EX (SP),HL ; + LD A,H ; + CP $3E ; + JR Z,REPORT_7 + + LD (ERR_SP),SP ; + JR 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 ; to REPORT_8 + + CALL X_TEMP + LD HL,FLAGX ; + SET 5,(HL) ; + RES 6,(HL) ; + LD A,(FLAGS) ; + AND $40 ; 64 + LD BC,2 ; + JR NZ,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 + + 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 + +; ___ + +mark_0F21: +REPORT_8: + RST _ERROR_1 + DEFB 7 ; Error Report: End of file + + +; THE 'PAUSE' COMMAND ROUTINE + + +mark_0F23: +FAST: + CALL SET_FAST + RES 6,(IY+CDFLAG-RAMBASE) + RET ; return. + + +; THE 'SLOW' COMMAND ROUTINE + + +mark_0F2B: +SLOW: + SET 6,(IY+CDFLAG-RAMBASE) + JP SLOW_FAST + + +; THE 'PAUSE' COMMAND ROUTINE + + +mark_0F32: +PAUSE: + CALL FIND_INT + CALL SET_FAST + LD H,B ; + LD L,C ; + CALL DISPLAY_P + + LD (IY+FRAMES_hi-RAMBASE),$FF + + CALL SLOW_FAST + JR 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 ; forward, if not, to S_TEST_PI + + +; THE 'RND' FUNCTION + +RND: + + CALL SYNTAX_Z + JR Z,S_JPI_END ; forward if checking syntax to S_JPI_END + + LD BC,(SEED) ; sv + CALL 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 + 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 ; 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 ; forward + +; ___ + +mark_0F8C: +S_TEST_PI: + CP ZX_PI ; the 'PI' character + JR NZ,S_TST_INK ; forward, if not + + +; THE 'PI' EVALUATION + + + CALL SYNTAX_Z + JR Z,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 ; 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 ; forward, if not + + +; THE 'INKEY$' EVALUATION + + + CALL KEYBOARD + LD B,H ; + LD C,L ; + LD D,C ; + INC D ; + CALL NZ,DECODE + LD A,D ; + ADC A,D ; + LD B,D ; + LD C,A ; + EX DE,HL ; + JR S_STRING ; forward + +; ___ + +mark_0FB2: +S_ALPHANUM: + CALL ALPHANUM + JR C,S_LTR_DGT ; forward, if alphanumeric + + CP ZX_PERIOD ; is character a '.' ? + JP Z,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 ; forward, if so + + CP ZX_BRACKET_LEFT ; is character a '(' ? + JR NZ,S_QUOTE ; forward if not + + CALL CH_ADD_PLUS_1 ; advances character pointer. + + CALL SCANNING ; recursively call to evaluate the sub_expression. + + CP ZX_BRACKET_RIGHT; is subsequent character a ')' ? + JR NZ,S_RPT_C ; forward if not + + + CALL CH_ADD_PLUS_1 ; advances. + JR 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 ; forward, if not + + CALL CH_ADD_PLUS_1 ; advances + PUSH HL ; * save start of string. + JR S_QUOTE_S ; forward + +; ___ + + +mark_0FE0: +S_Q_AGAIN: + CALL CH_ADD_PLUS_1 + +mark_0FE3: +S_QUOTE_S: + CP ZX_QUOTE ; is character a '"' ? + JR NZ,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 ; 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 + +; ___ + +; A string with no terminating quote has to be considered. + +mark_0FFB: +S_Q_NL: + CP ZX_NEWLINE + JR NZ,S_Q_AGAIN ; loop back if not + +mark_0FFF: +S_RPT_C: + JP REPORT_C +; ___ + +mark_1002: +S_FUNCTION: + SUB $C4 ; subtract 'CODE' reducing codes + ; CODE thru '<>' to range $00 - $XX + JR 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 ; forward, if so + + JR NC,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 ; 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 ; 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 ; jump back +; ___ + +mark_1025: +S_LTR_DGT: + CP ZX_A ; compare to 'A'. + JR C,S_DECIMAL ; forward if less to S_DECIMAL + + CALL LOOK_VARS + JP C,REPORT_2 ; back if not found + ; a variable is always 'found' when checking + ; syntax. + + CALL Z,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 ; forward if not numeric + + INC HL ; address numeric contents of variable. + LD DE,(STKEND) ; set destination to STKEND + CALL 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 ; 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 + JR NZ,S_STK_DEC ; forward in run-time + + CALL DEC_TO_FP + + RST _GET_CHAR ; advances HL past digits + LD BC,$0006 ; six locations are required. + CALL 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 ; advances the character + ; address skipping any white-space. + JR 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 ; 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 ; 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 ; forward if not + + BIT 6,(IY+FLAGS-RAMBASE) ; Numeric or string result? + JR NZ,S_LOOP ; forward if numeric + +; else is a string + + CALL SLICING + + RST _NEXT_CHAR + JR 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 ; forward if less, as + ; we have reached end of meaningful expression + + SUB ZX_MINUS ; is character '-' ? + JR NC,SUBMLTDIV ; forward with - * / and '**' '<>' + + ADD A,13 ; increase others by thirteen + ; $09 '>' thru $0C '+' + JR GET_PRIO ; forward + +; ___ + +mark_10A7: +SUBMLTDIV: + CP $03 ; isolate $00 '-', $01 '*', $02 '/' + JR C,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 ; forward if less + + CP $06 ; test the upper limit for nonsense also + JR NC,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 - $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 ; forward if so to S_TIGHTER + + AND A ; are both priorities zero + JP Z,GET_CHAR ; exit if zero via GET_CHAR + + PUSH BC ; stack present values + PUSH DE ; stack last values + CALL SYNTAX_Z + JR Z,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 ; 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 ; 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 ; forward if numeric + + RES 6,(HL) ; reset to signal string result + +mark_10EA: +S_LOOPEND: + POP BC ; restore present values + JR 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 ; 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 ; forward if not + + SET 6,C ; set the numeric operand required for 'AND' + JR S_NEXT ; forward to S_NEXT + +; ___ + +mark_1102: +S_NOT_AND: + JR C,S_RPORT_C ; back if less than 'AND' + ; Nonsense if '-', '*' etc. + + CP __strs_add ; compare to 'strs_add' literal + JR Z,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 ; 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 + JP NC,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 + + SET 6,C ; + CP ZX_DOLLAR ; $0D + JR Z,V_STR_VAR ; forward + + SET 5,C ; + +mark_1139: +V_CHAR: + CALL ALPHANUM + JR NC,V_RUN_SYN ; forward when not + + RES 6,C ; + + RST _NEXT_CHAR + JR 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 + JR NZ,V_RUN ; forward + + LD A,C ; + AND $E0 ; + SET 7,A ; + LD C,A ; + JR 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 ; + + CP C ; + JR NZ,V_NEXT ; + + RLA ; + ADD A,A ; + JP P,V_FOUND_2 + + JR C,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 ; back + + CP (HL) ; + JR Z,V_MATCHES ; back + + OR $80 ; + CP (HL) ; + JR NZ,V_GET_PTR ; forward + + LD A,(DE) ; + CALL ALPHANUM + JR NC,V_FOUND_1 ; forward + +mark_117F: +V_GET_PTR: + POP HL ; + +mark_1180: +V_NEXT: + PUSH BC ; + CALL NEXT_ONE + EX DE,HL ; + POP BC ; + JR 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 ; forward + + SET 5,B ; + JR 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 + JR NC,V_END ; forward if not alphanumeric + + + RST _NEXT_CHAR + JR 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 ; forward + + BIT 7,(HL) ; + JR NZ,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 + + RST _GET_CHAR + JP 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 ; forward + + DEC B ; + JR Z,SV_SIMPLE_STR ; forward + + EX DE,HL ; + + RST _GET_CHAR + CP $10 ; + JR NZ,REPORT_3 ; forward + + EX DE,HL ; + +mark_11D1: +SV_PTR: + EX DE,HL ; + JR SV_COUNT ; forward +; ___ +mark_11D4: +SV_COMMA: + PUSH HL ; + + RST _GET_CHAR + POP HL ; + CP ZX_COMMA ; $1A == 26 + JR Z,SV_LOOP ; forward + + BIT 7,C ; + JR Z,REPORT_3 ; forward + + BIT 6,C ; + JR NZ,SV_CLOSE ; forward + + CP ZX_BRACKET_RIGHT ; $11 + JR NZ,SV_RPT_C ; forward + + + RST _NEXT_CHAR + RET ; +; ___ +mark_11E9: +SV_CLOSE: + CP ZX_BRACKET_RIGHT ; $11 + JR Z,SV_DIM ; forward + + CP $DF ; + JR NZ,SV_RPT_C ; forward + +mark_11F1: +SV_CH_ADD: + RST _GET_CHAR + DEC HL ; + LD (CH_ADD),HL ; sv + JR 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 ; forward + + + RST _GET_CHAR + CP ZX_BRACKET_RIGHT + JR Z,SV_DIM ; forward + + CP ZX_TO ; + JR Z,SV_CH_ADD ; back + +mark_120C: +SV_MULT: + PUSH BC ; + PUSH HL ; + CALL DE_DE_PLUS_ONE + EX (SP),HL ; + EX DE,HL ; + CALL INT_EXP1 + JR C,REPORT_3 + + DEC BC ; + CALL GET_HL_TIMES_DE + ADD HL,BC ; + POP DE ; + POP BC ; + DJNZ SV_COMMA ; loop back + + BIT 7,C ; + +mark_1223: +SV_RPT_C: + JR NZ,SL_RPT_C + + PUSH HL ; + BIT 6,C ; + JR NZ,SV_ELEM_STR + + LD B,D ; + LD C,E ; + + RST _GET_CHAR + CP ZX_BRACKET_RIGHT; is character a ')' ? + JR Z,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 + ADD HL,BC ; + RET ; return >> + +; ___ + +mark_123D: +SV_ELEM_STR: + CALL DE_DE_PLUS_ONE + EX (SP),HL ; + CALL GET_HL_TIMES_DE + POP BC ; + ADD HL,BC ; + INC HL ; + LD B,D ; + LD C,E ; + EX DE,HL ; + CALL STK_ST_0 + + RST _GET_CHAR + CP ZX_BRACKET_RIGHT ; is it ')' ? + JR Z,SV_DIM ; forward if so + + CP ZX_COMMA ; $1A == 26 ; is it ',' ? + JR NZ,REPORT_3 ; back if not + +mark_1256: +SV_SLICE: + CALL SLICING + +mark_1259: +SV_DIM: + RST _NEXT_CHAR + +mark_125A: +SV_SLICE_QUERY: + CP $10 ; + JR Z,SV_SLICE ; back + + RES 6,(IY+FLAGS-RAMBASE) ; Signal string result + RET ; return. + + +; THE 'SLICING' SUBROUTINE + +; +; + +mark_1263: +SLICING: + CALL SYNTAX_Z + CALL NZ,STK_FETCH + + RST _NEXT_CHAR + CP ZX_BRACKET_RIGHT; is it ')' ? + JR Z,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 ; forward if so + + POP AF ; + CALL 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 ; forward if so + + CP ZX_BRACKET_RIGHT; $11 + +mark_128B: +SL_RPT_C: + JP NZ,REPORT_C + + LD H,D ; + LD L,E ; + JR SL_DEFINE ; forward + +; ___ + +mark_1292: +SL_SECOND: + PUSH HL ; + + RST _NEXT_CHAR + POP HL ; + CP ZX_BRACKET_RIGHT; is it ')' ? + JR Z,SL_DEFINE ; forward if so + + POP AF ; + CALL INT_EXP2 + PUSH AF ; + + RST _GET_CHAR + LD H,B ; + LD L,C ; + CP ZX_BRACKET_RIGHT; is it ')' ? + JR NZ,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 ; forward + + INC HL ; + AND A ; + JP M,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 + 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 + 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 + POP AF ; + CALL SYNTAX_Z + JR Z,I_RESTORE ; forward if checking syntax + + PUSH AF ; + CALL FIND_INT + POP DE ; + LD A,B ; + OR C ; + SCF ; Set Carry Flag + JR Z,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 + 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 ; forward with carry + + RL C ; + RLA ; + JR NC,HL_AGAIN ; forward with no carry + + ADD HL,DE ; + +mark_131A: +HL_END: + JP C,REPORT_4 + +mark_131D: +HL_AGAIN: + DJNZ 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 ; 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 ; back + + CALL ALPHANUM + JR C,L_EACH_CH ; back + + CP ZX_DOLLAR ; is it '$' ? + JP Z,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 + +mark_134B: +L_CHAR: + INC HL ; + LD A,(HL) ; + AND A ; is it a space ? + JR Z,L_CHAR ; back + + INC DE ; + LD (DE),A ; + DJNZ 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 + +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 ; forward + +; ___ + +mark_136E: +L_EXISTS: + BIT 6,(IY+FLAGS-RAMBASE) ; Numeric or string result? + JR Z,L_DELETE_STR ; forward + + LD DE,$0006 ; + ADD HL,DE ; + JR L_NUMERIC ; back + +; ___ + +mark_137A: +L_DELETE_STR: + LD HL,(DEST) ; + LD BC,(STRLEN) ; + BIT 0,(IY+FLAGX-RAMBASE) + JR NZ,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 + POP HL ; + EX (SP),HL ; + AND A ; + SBC HL,BC ; + ADD HL,BC ; + JR NC,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 ; 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 + + POP BC ; + POP HL ; + INC BC ; + INC BC ; + INC BC ; + JP 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 + 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 + 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 + +mark_140C: +D_RPORT_C: + JP NZ,REPORT_C + + CALL SYNTAX_Z + JR NZ,D_RUN ; forward + + RES 6,C ; + CALL STK_VAR + CALL CHECK_END + +mark_141C: +D_RUN: + JR C,D_LETTER ; forward + + PUSH BC ; + CALL NEXT_ONE + CALL 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 ; 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 + JP C,REPORT_3 + + POP HL ; + PUSH BC ; + INC H ; + PUSH HL ; + LD H,B ; + LD L,C ; + CALL GET_HL_TIMES_DE + EX DE,HL ; + + RST _GET_CHAR + CP ZX_COMMA ; $1A == 26 + JR Z,D_NO_LOOP ; back + + CP ZX_BRACKET_RIGHT; is it ')' ? + JR NZ,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 + + PUSH DE ; + PUSH BC ; + PUSH HL ; + LD B,H ; + LD C,L ; + LD HL,(E_LINE) ; sv + DEC HL ; + CALL 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 ; back + + RET ; return. + + +; THE 'RESERVE' ROUTINE + +; +; + +mark_1488: +RESERVE: + LD HL,(STKBOT) ; address STKBOT + DEC HL ; now last byte of workspace + CALL 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 ; 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 ; back + + + +; THE 'RECLAIM THE END_MARKER' ROUTINE + + +mark_14C7: +REC_V80: + LD DE,(E_LINE) ; sv + JP RECLAIM_1 + + +; THE 'ALPHA' SUBROUTINE + + +mark_14CE: +ALPHA: + CP ZX_A ; $26 + JR 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 ; gets first part + CP ZX_PERIOD ; is character a '.' ? + JR NZ,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 + JR C,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 ; 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 ; forward if so + + CP ZX_MINUS ; is it a '-' ? + JR NZ,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 + + RST _FP_CALC ;; m, e. + DEFB __get_mem_0 ;; m, e, (1/0) TRUE/FALSE + DEFB __jump_true ;; + DEFB 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 ; 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 ; 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 + RET C ; + + RST _FP_CALC ;; + DEFB __exchange ;; + DEFB __stk_ten ;; + DEFB __multiply ;; + DEFB __addition ;; + DEFB __end_calc ;; + + RST _NEXT_CHAR + JR 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 - $ ;; 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 - $ ;; + +; 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 - $ ;; + +; ___ + +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 - $ ;; + + DEFB __multiply ;; m-1, x*f. + DEFB __jump ;; + DEFB 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 - $ ;; + +; ___ + +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 ; exponent to A + ; mantissa to EDCB. + AND A ; test for value zero. + JR NZ,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 ; 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 ; 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 ; 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 ; 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 ; loop back till zero + +mark_15BC: +EXP_ZERO: + JR NC,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 ; 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 + RET C ; + + PUSH AF ; + DEC B ; + INC B ; + JR Z,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 - $ ;; x. + + DEFB __duplicate ;; x, x + DEFB __greater_0 ;; x, (1/0). + DEFB __jump_true ;; + DEFB 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 ; 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 ; $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 ; 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 ; 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 ; 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 ; 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 + JR Z,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 ; 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 ; forward if positive to PF_E_FMT + + CP $F6 ; test for more than four zeros after point. + JP M,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 ; forward if so to PF_ZERO_1 + + JP M,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 + DJNZ PF_NIB_LP ; loop back for counted numbers + + JR 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 ; prints one digit. + CALL 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 ; 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 ; 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 ; 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 ; skip forward if so to PF_E_LOW + + CALL OUT_CODE ; prints as digit '1' - '9' + +mark_16AD: +PF_E_LOW: + LD A,C ; low byte to A + CALL 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 ; 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 ;+ New loop back to PF-ZRO-LP +#endif + + JR 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 + JR 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 + 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 ; 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 ; 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 ; 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 + 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 ; 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 + LD B,A ; save first exponent byte in B. + EX DE,HL ; switch number pointers. + CALL PREP_ADD + LD C,A ; save second exponent byte in C. + CP B ; compare the exponent bytes. + JR NC,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 + CALL 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 ; 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 ; performs a single shift + ; rounding any lost bit + INC (HL) ; increment the exponent. + JR Z,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 ; 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 ; 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 ; 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 ; 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 + 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 + + EX DE,HL ; HL first number, DE - second number + JR C,ZERO_RESULT ; forward with carry to ZERO_RESULT + ; anything * zero = zero. + + PUSH HL ; save pointer to first number. + + CALL 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 ; forward to loop entry point STRT_MLT + +; ___ + +; The multiplication loop is entered at STRT_LOOP. + +mark_17E7: +MLT_LOOP: + JR NC,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 ; 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 ; 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 + + JR NC,REPORT_6 + + AND A ; + +mark_1819: +OFLW1_CLR: + INC A ; + JR NZ,OFLW2_CLR + + JR C,OFLW2_CLR + + EXX ; + BIT 7,D ; + EXX ; + JR NZ,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 ; 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 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 ; 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 ; 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 ; 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 ; 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 ; back if exponent becomes zero + ; it's just possible that the last rotation + ; set bit 7 of D. We shall see. + + DJNZ 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 ; back + +; ___ + +mark_1859: +NORML_NOW: + RLA ; for the addition path, A is always zero. + ; for the mult path, ... + + JR NC,OFLOW_CLR ; forward + +; this branch is taken only with multiplication. + + CALL ADD_BACK + + JR NZ,OFLOW_CLR ; forward + + EXX ; + LD D,$80 ; + EXX ; + INC (HL) ; + JR Z,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 + JR C,REPORT_6 ; back if zero + ; 'Arithmetic overflow' + + EX DE,HL ; now prepare first number and check for zero. + CALL 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 ; 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 ; 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 ; forward + +mark_18B2: +DIVISION_START: + SBC HL,DE ; subtract divisor part. + EXX ; + SBC HL,DE ; + EXX ; + JR NC,NUM_RESTORE ; forward if subtraction goes + + ADD HL,DE ; else restore + EXX ; + ADC HL,DE ; + EXX ; + AND A ; clear carry + JR 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 ; back while still minus to DIV_LOOP + + PUSH AF ; + JR Z,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 ; 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 ; 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 ; 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 ; 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 ; loop back + +; now consider any residual bits. + +mark_1905: +BITS_ZERO: + AND $07 ; isolate the remaining bits + JR Z,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 ; 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 ; $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 boolean_num_and_num ; $08 Address: $1AF3 - boolean_num_and_num + DEFW num_l_eql ; $09 Address: $1B03 - num_l_eql + DEFW num_gr_eql ; $0A Address: $1B03 - num_gr_eql + DEFW nums_neql ; $0B Address: $1B03 - nums_neql + DEFW num_grtr ; $0C Address: $1B03 - num_grtr + DEFW num_less ; $0D Address: $1B03 - num_less + DEFW nums_eql ; $0E Address: $1B03 - nums_eql + DEFW ADDITION ; $0F Address: $1755 - addition + + DEFW strs_and_num ; $10 Address: $1AF8 - str_and_num + DEFW str_l_eql ; $11 Address: $1B03 - str_l_eql + DEFW str_gr_eql ; $12 Address: $1B03 - str_gr_eql + DEFW strs_neql ; $13 Address: $1B03 - strs_neql + DEFW str_grtr ; $14 Address: $1B03 - str_grtr + DEFW str_less ; $15 Address: $1B03 - str_less + DEFW strs_eql ; $16 Address: $1B03 - strs_eql + DEFW strs_add ; $17 Address: $1B62 - strs_add + +; unary follow + + DEFW neg ; $18 + DEFW code ; $19 + DEFW val ; $1A + DEFW len ; $1B + DEFW sin ; $1C + DEFW cos ; $1D + DEFW tan ; $1E + DEFW asn ; $1F + DEFW acs ; $20 + DEFW atn ; $21 + DEFW ln ; $22 + DEFW exp ; $23 + DEFW int ; $24 + DEFW sqr ; $25 + DEFW sgn ; $26 + DEFW abs ; $27 + DEFW PEEK ; $28 Address: $1A1B - peek !!!! + DEFW usr_num ; $29 + DEFW str_dollar ; $2A + DEFW chr_dollar ; $2B + DEFW not ; $2C + +; end of true unary + + DEFW duplicate ; $2D + DEFW n_mod_m ; $2E + + DEFW jump ; $2F + DEFW stk_data ; $30 + + DEFW dec_jr_nz ; $31 + DEFW less_0 ; $32 + DEFW greater_0 ; $33 + DEFW end_calc ; $34 + DEFW get_argt ; $35 + DEFW TRUNCATE ; $36 + DEFW FP_CALC_2 ; $37 + DEFW 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 ; $39 : $80 - $9F. + DEFW stk_const_xx ; $3A : $A0 - $BF. + DEFW st_mem_xx ; $3B : $C0 - $DF. + DEFW 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 ; 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 ; 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 ; forward + +; ___ + +; the branch was here with simple literals. + +mark_19C2: +FIRST_3D: + CP $18 ; compare with first unary operations. + JR NC,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 ; 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 + 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 ; 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 ; 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 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 ; 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 ; 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 ; 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 ; works through variable + ; length records. + + POP DE ; restore real STKEND + POP AF ; restore count + DEC A ; decrease + JR 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 ; so that HL = first byte + CALL 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 ; Address: stk_zero - start of table of + ; constants + EXX ; + CALL SKIP_CONS + CALL 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 ; 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 ; sets HL to the destination. + EX DE,HL ; swap - HL is start, DE is destination. + +#if ORIGINAL + CALL 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 ; 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 + ; 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 ; is called directly to + ; push a value and advance H'L'. + CALL 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 + +; 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 ; 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 ; puts address in BC. + LD A,(BC) ; load contents into A register. + +mark_1AC2: +IN_PK_STK: + JP STACK_A ; exit via STACK_A to put value on the + ; calculator stack. + + +; USR number (29) + +; The USR function followed by a number 0-65535 is the method by which +; the ZX81 invokes machine code programs. This function returns the +; contents of the BC register pair. +; Note. that STACK_BC re-initializes the IY register to ERR_NR if a user-written +; program has altered it. + +mark_1AC5: +usr_num: + CALL 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 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 ; 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 ; 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 ; 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 ; 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 ; 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 ; 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 ; 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 ; 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 ; 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 ; forward to STRINGS if so. + +#endif + + CALL SUBTRACT ; leaves result on stack. + JR 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 ; gets 2nd string params + PUSH DE ; save start2 *. + PUSH BC ; and the length. + + CALL 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 ; 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 ; 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 ; 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 ; 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 ; forward to FRST_LESS if length is zero. + +; both strings have at least one character left. + + LD A,(DE) ; fetch character of first string. + SUB (HL) ; subtract with that of 2nd string. + JR C,FRST_LESS ; forward to FRST_LESS if carry set + + JR NZ,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 ; 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 ; sets true(1) if equal(0) + ; or, for strings, applies true result. + CALL greater_0 ; ?????????? + + + POP AF ; pop A + RRCA ; the third RRCA - test for '<=', '>=' or '<>'. + CALL NC,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 ; fetches string parameters + ; and deletes calculator stack entry. + PUSH DE ; save start address. + PUSH BC ; and length. + + CALL 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 ; 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 ; to OTHER_STR if null string + LDIR ; copy string to workspace. +#else + CALL 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 ; 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 ;+ 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 ; puts the number in A. + + JR C,REPORT_Bd ; forward if overflow + JR NZ,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 ; 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 ; 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 ; evaluates string + ; expression and checks for integer result. + + CALL 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 ; 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 ; 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 + + 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 ; 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 ; fetch and delete the string parameters. + ; DE points to the start, BC holds the length. + LD A,B ; test length + OR C ; of the string. + JR Z,STK_CODE ; skip with zero if the null string. + + LD A,(DE) ; else fetch the first character. + +mark_1C0E: +STK_CODE: + JP 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 ; fetch and delete the + ; string parameters from the calculator stack. + ; register BC now holds the length of string. + + JP 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 ; 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 ; 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 ; 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 - $ ;; 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 - $ ;; -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 + JR NZ,N_NEGTV + + JR C,REPORT_6b + + ADD A,(HL) ; + JR NC,RESULT_OK + + +mark_1C99: +REPORT_6b: + RST _ERROR_1 + DEFB $05 ; Error Report: Number too big + +mark_1C9B: +N_NEGTV: + JR C,RESULT_ZERO + + SUB (HL) ; + JR NC,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 + + 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 + + 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 - $ ;; + + 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 ; back to REPORT_A + ; 'Invalid argument' + +; This guess is based on a Usenet discussion. +; Halve the exponent to achieve a good guess.(accurate with .25 16 64 etc.) + + LD HL,$4071 ; Address first byte of mem-3 + + LD A,(HL) ; fetch exponent of mem-3 + XOR $80 ; toggle sign of exponent of mem-3 + SRA A ; shift right, bit 7 unchanged. + INC A ; + JR Z,ASIS ; forward with say .25 -> .5 + JP P,ASIS ; leave increment if value > .5 + DEC A ; restore to shift only. +ASIS XOR $80 ; restore sign. + LD (HL),A ; and put back 'halved' exponent. + +; Now re-enter the calculator. + + RST 28H ;; FP-CALC x + +SLOOP DEFB __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 - $ ;; 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 - $ ;; 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 - $ ;; + ;;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 - $ ;;fwd to C_ENT + ;;forward to common code if in QII or QIII + + + DEFB __negate ;; else make positive. + DEFB __jump ;; + DEFB 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 ; 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 - $ ;; + + DEFB __negate ;; + DEFB __jump ;; + DEFB 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 - $ ;; 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 - $ ;;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 ; 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 - $ ;; 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 - $ ;; 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 %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$(5) + + DEFB %11110000 + DEFB %11110000 + DEFB %11110000 + DEFB %11110000 + DEFB %11110000 + DEFB %11110000 + DEFB %11110000 + DEFB %11110000 + +; $06 - Character: mosaic CHR$(6) + + DEFB %00001111 + DEFB %00001111 + DEFB %00001111 + DEFB %00001111 + DEFB %11110000 + DEFB %11110000 + DEFB %11110000 + DEFB %11110000 + +; $07 - Character: mosaic CHR$(7) + + DEFB %11111111 + DEFB %11111111 + DEFB %11111111 + DEFB %11111111 + DEFB %11110000 + DEFB %11110000 + DEFB %11110000 + DEFB %11110000 + +; $08 - Character: mosaic CHR$(8) + + DEFB %10101010 + DEFB %01010101 + DEFB %10101010 + DEFB %01010101 + DEFB %10101010 + DEFB %01010101 + DEFB %10101010 + DEFB %01010101 +; $09 - Character: mosaic CHR$(9) + + 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 + +; $0C - Character: £ CHR$(12) + + DEFB %00000000 + DEFB %00011100 + DEFB %00100010 + DEFB %01111000 + DEFB %00100000 + DEFB %00100000 + DEFB %01111110 + DEFB %00000000 + +; $0D - Character: '$' CHR$(13) + + DEFB %00000000 + DEFB %00001000 + DEFB %00111110 + DEFB %00101000 + DEFB %00111110 + DEFB %00001010 + DEFB %00111110 + DEFB %00001000 + +; $0E - Character: ':' CHR$(14) + + DEFB %00000000 + DEFB %00000000 + DEFB %00000000 + DEFB %00010000 + DEFB %00000000 + DEFB %00000000 + DEFB %00010000 + DEFB %00000000 + +; $0F - 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. + + + +; +; 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 + + +