; =================================================== ; ; An Assembly Listing of the ROM of the Sinclair ZX80 ; --------------------------------------------------- ; ; ------------------------- ; Last updated: 12-SEP-2002 ; ------------------------- ; ; Note. This is not the original text file, which was ; written by John Grant in 1979, but a file that ; performs a similar function in that it assembles ; a 4K ROM file that may be used in ZX80 emulators. ; ; The resultant ROM file is identical to the original ; and a comparison between the object code and the original ; is made as part of the process of uploading this file. ; ; It would be desirable that the original file be published but, until such ; time, this file may serve as a poor substitute. ; ; Actually I learn that the complete Assembly Listing was published, with ; "Designer's Annotations", in 1980. Also in that year, appeared "The ZX80 ; Monitor Listing" by Ian Logan, published by LINSAC. ; ; =================================================== #define DEFB .BYTE ; TASM cross-assembler definitions #define DEFW .WORD #define EQU .EQU ; =================================================== ; ; To do justice to the original program it is desirable ; that, while the instructions should not be over ; commented, what is appended should be of high quality. ; ; Send details of any improvements/corrections to ; geoff@wearmouth.demon.co.uk.spamnothanks ; All contributions will be credited. ; ; File incorporates contributions from ; Peter Liebert-Adelt ; ; and borrows from the writings of ; Wilf Rigter, ; Dr Ian Logan, ; Dr Frank O'Hara. ; ; =================================================== .ORG $0000 ; ----------- ; THE 'START' ; ----------- ;; START L0000: LD HL,$7FFF ; top of possible RAM. ; (highest integer is 32767). LD A,$3F ; page before RAM. JP L0261 ; forward to RAM-FILL. ; ------------------- ; THE 'ERROR' RESTART ; ------------------- ;; ERROR-1 L0008: POP HL ; drop the return address. LD L,(HL) ; fetch the error code after RST 8. BIT 7,(IY+$00) ; test ERR_NR for value $FF (OK) JR L0013 ; forward to continue at ERROR-2. ; ------------------------------- ; THE 'PRINT A CHARACTER' RESTART ; ------------------------------- ;; PRINT-A L0010: JP L0560 ; jump forward immediately to PRINT-A-2 ; --- ; A continuation of the previous Error restart. ;; ERROR-2 L0013: RET Z ; return if $FF - OK. LD (IY+$00),L ; else set system variable ERR_NR RET ; return. ; --------------------------------------------- ; THE 'COLLECT NEXT CHARACTER OR SPACE' RESTART ; --------------------------------------------- ; This will collect any next character including space (zero). ;; NXT-CH-SP L0018: JR L0052 ; forward to CH_ADD+1 ; --- ; This subroutine will collect the character at the current character address ; searching for the next non-space character should the fetched character be ; a space. ;; get-char L001A: LD HL,($4026) ; get pointer from CH_ADD LD A,(HL) ; fetch addressed character. ; This subroutine tests the current character in the accumulator retrieving ; the next non-space character should the accumulator contain a space ;; TEST-CHAR L001E: AND A ; test for space (zero). RET NZ ; return if not a space. ;------------------------------------------- ; THE 'COLLECT NEXT VALID CHARACTER' RESTART ;------------------------------------------- ;; NEXT-CHAR L0020: CALL L0052 ; routine CH_ADD+1 JR L001E ; loop back to TEST-CHAR until valid ; --- ; This subroutine advances the character pointer and evaluates the following ; expression. ; It is called twice with CH_ADD addressing the '(' character ;; EVAL-EXPR L0025: CALL L0055 ; routine CH_ADD_LP ; --------------------------------- ; THE 'SCANNING-CALCULATOR' RESTART ; --------------------------------- ;; SCAN-CALC L0028: CALL L001A ; routine get-char. LD B,$00 ; set B to zero as a starting ; priority marker. JP L09E1 ; jump forward to SCANNING ; ---------------------------- ; THE 'MAKE BC SPACES' RESTART ; ---------------------------- ;; BC-SPACES L0030: CALL L094F ; routine TEST-ROOM RET NC ; return if not enough room. PUSH BC ; save number of bytes required. JP L0CF3 ; jump forward to RESERVE ; -------------------------------- ; THE 'MASKABLE INTERRUPT' ROUTINE ; -------------------------------- ; Note. the maskable interrupt is concerned with generating the TV picture, ; one of the main tasks in the ZX80. This requires some understanding of ; how the video hardware interacts with the system and part of the process ; is to present to the Z80 chip a phantom display file in the upper ; unpopulated 32K of memory. This topsy-turvy display file ; executes characters like "HELLO WORLD" as NOP instructions but recognizes ; a newline ($76) as a true HALT instruction. ; The video hardware sniffs the databus and grabs the data as it flies by ; sending it on to the shifting circuits. The I register permanently holds ; $0E. The video circuitry uses this register and the lower six bits of the ; character to index into the character set bitmaps at the end of this ROM, ; at $0E00, and so cobble together a scan-line. ; If bit 7 of the character latch is set, then the serial video data is ; inverted so that any character in the range 127-191 appears as the inverse ; of normal characters 0 - 63. ; For a proper explanation of this system, I recommend Wilf Rigter's ; online documentation, available from several indexed sites. ; I have borrowed a few comments from that file to remind myself of what ; is happening. I have indicated where the Z80 instructions should be ; read in conjunction with Wilf's file by using a double semi-colon. ; On entry, B holds the line number and C the number of the scanline. ;; MASK-INT L0038: DEC C ;; decrement the scan line counter in register C. JP NZ,L0045 ;; JUMP to SCAN-LINE : repeats 8 times for each ;; row of characters in DFILE. POP HL ;; point to the start of next DFILE row DEC B ;; decrement ROW counter RET Z ;; return if zero to SET 3,C ;; load scan line counter with 08 was 00. ;; WAIT-INT L0041: LD R,A ;; load refresh register with value $DD. EI ;; enable interrupts. JP (HL) ;; jump to execute the NOPs in DFILE ;; terminated by a NEWLINE/HALT instruction. ; --- ;; SCAN-LINE L0045: POP DE ;; discard return address. RET Z ;; delay (Zero never set) JR L0041 ;; back to WAIT-INT above ; ---------------------------------------------- ; THE 'EVALUATE BRACKETED EXPRESSION' SUBROUTINE ; ---------------------------------------------- ; This subroutine is used when an opening bracket is encountered to evaluate ; the expression within. It is called from LOOK-VARS when an integral function ; or array is encountered and recursively from within SCANNING when any ; bracketed argument or sub-expression is encountered. ;; BRACKET L0049: CALL L0025 ; routine EVAL-EXPR LD A,(HL) ; fetch subsequent character CP $D9 ; is character a ')' ? JP NZ,L08AE ; jump to INS-ERR with other characters. ; else continue and get the character after the ')' ... ; --------------------------------- ; THE 'INCREMENT CH_ADD' SUBROUTINE ; --------------------------------- ;; CH_ADD+1 L0052: LD HL,($4026) ; fetch character address from CH_ADD ;; CH_ADD_LP L0055: INC HL ; increment the pointer. LD ($4026),HL ; set system variable CH_ADD LD A,(HL) ; fetch the addressed value. CP $B0 ; is character inverse 'K' RET NZ ; return if not. >> LD ($4004),HL ; set P_PTR system variable BIT 7,(IY+$19) ; test FLAGX - will be set if K-mode JR Z,L0055 ; back to CH_ADD_LP if not K-mode L0066: SET 2,(IY+$01) ; update FLAGS set K mode. JR L0055 ; back to CH_ADD_LP ; Note there is no NMI routine at L0066. ; --------------- ; THE 'KEY' TABLE ; --------------- ; The Key Table is indexed with a key value 1-78. ; ----------------------- ; THE 39 'UNSHIFTED' KEYS ; ----------------------- ;; MAIN-KEYS L006C: DEFB $3F ; Z DEFB $3D ; X DEFB $28 ; C DEFB $3B ; V DEFB $26 ; A DEFB $38 ; S DEFB $29 ; D DEFB $2B ; F DEFB $2C ; G DEFB $36 ; Q DEFB $3C ; W DEFB $2A ; E DEFB $37 ; R DEFB $39 ; T DEFB $1D ; 1 DEFB $1E ; 2 DEFB $1F ; 3 DEFB $20 ; 4 DEFB $21 ; 5 DEFB $1C ; 0 DEFB $25 ; 9 DEFB $24 ; 8 DEFB $23 ; 7 DEFB $22 ; 6 DEFB $35 ; P DEFB $34 ; O DEFB $2E ; I DEFB $3A ; U DEFB $3E ; Y DEFB $76 ; NEWLINE ED-ENTER DEFB $31 ; L DEFB $30 ; K DEFB $2F ; J DEFB $2D ; H DEFB $00 ; SPACE DEFB $1B ; . DEFB $32 ; M DEFB $33 ; N DEFB $27 ; B ; ---------------------- ; THE 39 'SHIFTED' CODES ; ---------------------- DEFB $0E ; ':' DEFB $D7 ; ';' DEFB $0F ; '?' DEFB $DF ; '/' DEFB $09 ; mosaic $09 DEFB $08 ; mosaic $08 DEFB $06 ; mosaic $06 DEFB $07 ; mosaic $07 DEFB $0B ; mosaic $0B DEFB $02 ; mosaic $02 DEFB $03 ; mosaic $03 DEFB $04 ; mosaic $0A DEFB $05 ; mosaic $04 DEFB $0A ; mosaic $05 DEFB $DB ; 'NOT' DEFB $E0 ; 'AND' DEFB $D5 ; 'THEN' DEFB $D6 ; 'TO' DEFB $72 ; cursor left DEFB $77 ; [ RUBOUT ] DEFB $74 ; [ HOME ] DEFB $73 ; cursor right DEFB $70 ; cursor up DEFB $71 ; cursor down DEFB $DE ; '*' DEFB $D9 ; ')' DEFB $DA ; '(' DEFB $0D ; '$' DEFB $01 ; '"' DEFB $75 ; [ EDIT ] DEFB $E3 ; '=' DEFB $DD ; '+' DEFB $DC ; '-' DEFB $E2 ; '**' DEFB $0C ; uk currency symbol DEFB $D8 ; ',' DEFB $E4 ; '>' DEFB $E5 ; '<' DEFB $E1 ; 'OR' ; ----------------- ; THE 'TOKEN' TABLE ; ----------------- ;; TKN-TABLE L00BA: DEFB $D4 ; chr$ 212 - the threshold character ; tokens below this are printed using ; the next character DEFB $8F ; '?' + $80 DEFB $81 ; '"' + $80 DEFB $39,$2D,$2A,$B3 ; THEN DEFB $39,$B4 ; TO DEFB $99 ; ; DEFB $9A ; , DEFB $91 ; ( DEFB $90 ; ) DEFB $33,$34,$B9 ; NOT DEFB $92 ; - DEFB $93 ; + DEFB $94 ; * DEFB $95 ; / DEFB $26,$33,$A9 ; AND DEFB $34,$B7 ; OR DEFB $14,$14+$80 ; ** DEFB $96 ; = DEFB $97 ; < DEFB $98 ; > DEFB $31,$2E,$38,$B9 ; LIST DEFB $37,$2A,$39,$3A,$37,$B3 ; RETURN DEFB $28,$31,$B8 ; CLS DEFB $29,$2E,$B2 ; DIM DEFB $38,$26,$3B,$AA ; SAVE DEFB $2B,$34,$B7 ; FOR DEFB $2C,$34,$00,$39,$B4 ; GO TO DEFB $35,$34,$30,$AA ; POKE DEFB $2E,$33,$35,$3A,$B9 ; INPUT DEFB $37,$26,$33,$29 ; ... DEFB $34,$32,$2E,$38,$AA ; RANDOMISE DEFB $31,$2A,$B9 ; LET DEFB $8F ; '?' + $80 DEFB $8F ; '?' + $80 DEFB $33,$2A,$3D,$B9 ; NEXT DEFB $35,$37,$2E,$33,$B9 ; PRINT DEFB $8F ; '?' + $80 DEFB $33,$2A,$BC ; NEW DEFB $37,$3A,$B3 ; RUN DEFB $38,$39,$34,$B5 ; STOP DEFB $28,$34,$33,$39,$2E ; ... DEFB $33,$3A,$AA ; CONTINUE DEFB $2E,$AB ; IF DEFB $2C,$34,$00,$38,$3A,$A7 ; GO SUB DEFB $31,$34,$26,$A9 ; LOAD DEFB $28,$31,$2A,$26,$B7 ; CLEAR DEFB $37,$2A,$B2 ; REM DEFB $8F ; '?' + $80 ; ---------------------- ; THE 'DISPLAY' ROUTINES ; ---------------------- ; -> ;; DISP-1 L013C: CALL L01AD ;; routine DISP-2 ; The initial entry point ;; KEYBOARD L013F: LD B,$08 ; (7) set counter to 8 ;; KB-1 L0141: DJNZ L0141 ; (13,8) and loop back 7 times. (7*13+8) ; "WASTE 99 T-STATES" ;; KB-2 L0143: LD HL,($401E) ; (16) fetch two-byte FRAMES value. INC HL ; ( 6) increment LD ($401E),HL ; (16) and store in FRAMES again. ; now read the keyboard LD HL,$FFFF ; (10) prepare a buffer LD B,$FE ; ( 7) set B to $FE LD C,B ; ( 4) now BC is $FEFE - slightly slower than ; the equally time-critical LD BC,$FEFE (10) ; that is used in the ZX81 ROM. IN A,(C) ; (12) now read port $FEFE the half-row with ; the shift key. ; "START FRAME SYNC" ; START COUNTING OR $01 ; (7) set the rightmost bit so as to ignore ; shift. ;; EACH-LINE L0154: OR $E0 ; [7] OR 11100000. LD D,A ; [4] transfer to D. CPL ; [4] complement - only bits 4-0 meaningful now. CP $01 ; [7] sets carry if A is zero. SBC A,A ; [4] $FF if $00 else zero. OR B ; [4] $FF or port FE,FD,FB.... AND L ; [4] unless more than one key, L will still ; be $FF if more than one key pressed 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) ; [12] read another half-row. ; all five bits this time. JR C,L0154 ; [12],(7) loop back, until done, to EACH-LINE ; (658 T-states). ; the last row read is SHIFT,Z,X,C,V for the second time. RRA ; (4) test the shift key - carry reset if ; pressed. ;; KB-3 L0168: RL H ; (8) rotate H to the left 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 columns and L identifying the row of the ; keyboard matrix. ; This is a good time to test if this is an American or British machine. ; The US machine has an extra diode that causes bit 6 of a byte read from a ; port to be reset. RLA ; (4) compensate for the shift test. RLA ; (4) rotate bit 7 out. RLA ; (4) test bit 6. SBC A,A ; (4) $FF or $00 (USA) AND $18 ; (7) and 24 ADD A,$20 ; (7) add 32 ; gives either 32 (USA) or 56 (UK) blank lines above the TV picture. ; This value will be decremented for the lower border. LD ($4023),A ; (13) place margin in RESULT_hi. ; The next snippet tests that the same raw key is read twice in succession. ; The first time through, the routine uses a character address value, ; which is inappropriate to match against a key value, but the next time ; through it matches the key value it placed there on the first pass. ; Seems to be 713 T-states. ; ; "717 T-STATES SINCE START OF FRAME SYNC, 545 BEFORE END" LD BC,($4026) ; (20) fetch possible previous key value from ; CH_ADD LD ($4026),HL ; (16) put the fresh key value in CH_ADD. LD A,B ; ( 4) fetch high byte. ADD A,$02 ; ( 7) test for $FF, no-key which will set ; carry. SBC HL,BC ; (15) subtract the two raw keys. EX DE,HL ; ( 4) result, possibly zero, to DE. LD HL,$4022 ; (10) now address system variable RESULT. LD A,(HL) ; ( 7) load A from RESULT_lo. OR D ; ( 4) check the OR E ; ( 4) subtraction result. RET Z ; ( 5,11) return if all three zero. >>> ; T-states = 96 so far ; proceed to debounce. The 'no-key' value $FF must be returned five times ; before a new key is accepted above. ; Holding down a key causes the shift counter to be maintained at five. ; The initial state of RESULT is unimportant. LD A,B ; ( 4) fetch hi byte of PREVIOUS key code. CP $FE ; ( 7) sets carry if valid - ; $FD, $FB, $F7, $EF, $DF SBC A,A ; ( 4) gives $FF if pressed or $00 if no-key. LD B,$1F ; ( 7) prepare the shift counter ; (and also the timed delay) OR (HL) ; ( 7) OR with RESULT_lo AND B ; ( 4) limit the count to five set bits. RRA ; ( 4) 'shift' to right LD (HL),A ; ( 7) place result in RESULT_lo DEC B ; ( 4) adjust the delay counter B to thirty. ; t states = 48 ( Total 96+48=144) ;; KB-4 L0194: DJNZ L0194 ;; (13,8) wait a while looping to KB-4 ;; equals 13*29+8 = 385 ; "FRAME SYNC ENDS AT NEXT M1" OUT ($FF),A ;; (11) stops the VSYNC pulse LD A,$EC ;; ( 7) the value for R register LD B,$19 ;; there are 25 HALTs including the initial ;; one. LD HL,($400C) ;; point HL to D-FILE the first HALT ;; instruction. SET 7,H ;; now point to the DFILE echo in the ;; top 32K of address space. CALL L01AD ;; routine DISP-2 LD A,$F3 ;; prepare to set the R refresh register to $F3. INC B ;; increment the line count DEC HL ;; decrement screen address. DEC (IY+$23) ;; decrement RESULT_hi the blank line counter. JR L013C ;; back to display and read ; --- ;; DISP-2 L01AD: LD C,(IY+$23) ;; load C the col count from RESULT_hi. LD R,A ;; R increments with each opcode until A6 ;; goes low which generates the INT signal. LD A,$DD ;; set the left margin of all other lines. ;; loaded later to R - the incremental refresh ;; register. EI ;; with R set up, enable interrupts. JP (HL) ;; jump to execute the echo DFILE starting with ;; HALT and waits for the first INT to ;; come to the rescue. ; -------------------------- ; THE 'SAVE' COMMAND ROUTINE ; -------------------------- ; There isn't a program name involved. ; The routine saves the System Variables, Program Area and BASIC Variables. ; One of the five System commands that cannot be used from within a program. ;; SAVE L01B6: POP DE ; discard return address. LD DE,$12CB ; timing value of 5 seconds for leader. ;; SAVE-1 L01BA: LD A,$7F ; read port $7FFE. IN A,($FE) ; all 16 bits are placed on address bus. RRA ; test for the space key. JR NC,L0203 ; forward, if pressed, indirectly to MAIN-EXEC. ;; SAVE-2 L01C1: DJNZ L01C1 ; delay self-looping to SAVE-2 DEC DE ; decrement LD A,D ; and test OR E ; for zero. JR NZ,L01BA ; back if not zero to outer delay loop SAVE-1. LD HL,$4000 ; commence saving at start of RAM. ;; SAVE-3 L01CB: LD DE,$F808 ; register E counts the 8 bits. ; $F8 is first delay. ;; EACH-BIT L01CE: RLC (HL) ; spin the actual program byte. SBC A,A ; $FF or $00. AND $05 ; $05 or $00. ADD A,$04 ; $09 or $04. LD C,A ; timer to C. ; a set bit has a pulse longer than ; an unset bit. ;; SAVE-4 L01D6: OUT ($FF),A ; pulses LD B,$24 ; delay counter. ;; SAVE-5 L01DA: DJNZ L01DA ; self loop for delay to SAVE-5 LD A,$7F ; read the space row and hold for later. IN A,($FE) ; also ... LD B,$23 ; another delay counter. ;; SAVE-6 L01E2: DJNZ L01E2 ; self loop for delay2 to SAVE-6 DEC C ; decrement pulse counter JR NZ,L01D6 ; back while more to SAVE-4. LD B,D ; a terminating delay - D is zero (256). ;; SAVE-7 L01E8: NOP ; 4 T-states. DJNZ L01E8 ; execute the NOP 256 times. LD D,$FE ; subsequent timing value DEC E ; decrement the 8 counter. JR NZ,L01CE ; back if more to EACH-BIT. RRA ; test for space key pressed at last test. JR NC,L0203 ; forward, if so, indirectly to MAIN-EXEC. CALL L01F8 ; routine TEST-END does not return if at ; the end. >> JR L01CB ; else back to do another byte. ; --- ; This subroutine is used by both the SAVE and LOAD command routines ; to check when the required area has been completed and to then make an exit ; from the called loop. ; Note. that for the LOAD command the value of E_LINE is not that at the outset ; of the LOAD command but at the start of the command that saved the section. ; The first bytes to be loaded are the System Variables and E_LINE will be the ; eleventh and twelfth bytes to be loaded. The low byte is read in before the ; high byte so after the low byte is read in, E_LINE is in an indeterminate ; state. Hence E_LINE_hi is incremented at the outset to avoid a premature ; end to loading. ;; TEST-END L01F8: INC HL ; increase pointer. EX DE,HL ; LD HL,($400A) ; load HL with E_LINE - the location following ; the variables end-marker. SCF ; force a carry when equal. SBC HL,DE ; trial subtraction. EX DE,HL ; restore pointer. RET NC ; return if more bytes to do. POP HL ; else drop the return address. ;; JUMP-EXEC L0203: JP L0283 ; JUMP forward to MAIN-EXEC. ; Note. the above jump could be replaced by a relative jump saving one ; instruction byte. A few other direct jumps to this destination could be ; replaced with a series of relative jumps as has been done elsewhere. ; -------------------------- ; THE 'LOAD' COMMAND ROUTINE ; -------------------------- ; A System Command to load a program from tape. ;; LOAD L0206: POP DE ; discard the return address. ;; LOAD-1 L0207: LD DE,$5712 ; set a timing constant. ;; LOAD-2 L020A: LD A,$7F ; read from port $7FFE. IN A,($FE) ; the keyboard row with space. RRA ; test the outer key. JR NC,L0203 ; back, if pressed, indirectly to MAIN-EXEC RLA ; cancel the above RRA. RLA ; now do an RLA to read tape signal - bit 7. JR C,L0207 ; back without signal to outer loop LOAD-1. DEC DE ; decrement timer LD A,D ; and test OR E ; for zero. JR NZ,L020A ; back if not to inner loop LOAD-2. INC (IY+$0B) ; increment E_LINE_hi to prevent premature ; end after loading E_LINE-lo. ; see TEST-END. LD HL,$4000 ; start of RAM - system variables to be ; overwritten. ;; LOAD-3 L0220: LD E,$08 ; the bit counter for each byte. ;; LOAD-4 L0222: LD A,$7F ; test the keyboard IN A,($FE) ; reading the RRA ; space key. JR NC,L024D ; forward, if space pressed, to LD-ABORT. RLA ; restore to original state. RLA ; now test the tape bit. JR NC,L0222 ; back if ???? to LOAD-4 ; start building up a byte. LD C,$94 ; set timing value. The exit value of this ; register determines if a bit was set or unset. ;; LOAD-5 L022F: LD B,$1A ; inner timer ;; LOAD-6 L0231: DEC C ; decrement counter. IN A,($FE) ; read the tape port. RLA ; test the tape bit. BIT 7,C ; test if counter above 127. A set bit. LD A,C ; save in A. JR C,L022F ; back while bit set to LOAD-5 DJNZ L0231 ; decrement B counter and loop while not ; zero to LOAD-6. ; Note. this instruction has no effect on any ; flags. JR NZ,L0242 ; forward if C was > $7F (with NC) to LOAD-7 CP $56 ; compare copy of counter to $56 JR NC,L0222 ; back if $56-$7F to LOAD-4 ;; LOAD-7 L0242: CCF ; else clear if from above but set carry if ; branching to here. RL (HL) ; rotate the bit into position. DEC E ; decrement the eight counter JR NZ,L0222 ; loop back for entire byte. CALL L01F8 ; routine TEST-END quits early at end. JR L0220 ; and back to load another byte. ; --------------------------- ; THE 'LOAD ABORT' EXIT ROUTE ; --------------------------- ; If the LOAD command has started to load data then a reset is performed. ; If it's still waiting for the leader then rejoin the main execution loop ; after restoring the location of the Edit Line to its correct value. ;; LD-ABORT L024D: DEC D ; ?? JP P,L0000 ; a reset DEC (IY+$0B) ; restore E_LINE_hi to a valid state. JR L0203 ; indirect jump to MAIN-EXEC. ; -------------------------- ; THE 'LIST' COMMAND ROUTINE ; -------------------------- ; Another System command that can't be used from within a program. ;; LIST L0256: RES 7,B ; start by making the high byte, ; of an invalid, user-supplied, RES 6,B ; line number within range $00-$3F. ; this invisible mending is inappropriate and it is preferable to tell the ; user of any typos. e.g. LIST 40000 is silently changed to LIST 7232 ; when the user probably meant to type LIST 4000. However space is tight. LD ($4006),BC ; set E-PPC from line number. POP BC ; discard return address. JR L0283 ; forward to MAIN-EXEC which produces an ; 'automatic listing'. ; ---------------------------- ; THE 'INITIALIZATION' ROUTINE ; ---------------------------- ; A holds $3F, HL holds $7FFF. ;; RAM-FILL L0261: LD (HL),$01 ; fill location with 1 (null). DEC HL ; decrement address. CP H ; compare address high byte to $3F. JR NZ,L0261 ; back, while higher, to RAM-FILL. ;; RAM-READ L0267: INC HL ; address the next higher location. DEC (HL) ; decrement to zero. JR Z,L0267 ; back, if successful to RAM-READ. ; else we have encountered first unpopulated RAM location. LD SP,HL ; initialize stack pointer at end. PUSH AF ; place gosub end-marker $3F?? LD A,$0E ; set the I register to $0E to tell LD I,A ; the video hardware where to find ; the character set ($0E00). IM 1 ; select Interrupt Mode 1. LD IY,$4000 ; set IY to the start of the forty system ; variables. ; ----------------------------------------------------------------------------- ; ; --------------------- ; THE 'ZX80 MEMORY MAP' ; --------------------- ; ; There are forty ($28) system variables followed by Program area ; These are located at the start of RAM. ; ; +---------+---------+-----------+---+-----------+-----------+-------+-------+ ; | | | | | | | | | ; | SYSVARS | Program | Variables |80h| WKG Space | Disp File | Spare | Stack | ; | | | | | | | | | ; +---------+---------+-----------+---+-----------+-----------+-------+-------+ ; ^ ^ ^ ^ ^ ^ ^ ; $4024 VARS E_LINE D_FILE DF_END SP ; DF_EA ; ; ----------------------------------------------------------------------------- LD HL,$4028 ; set to location after sysvars. LD ($4008),HL ; set the system variable VARS. LD (HL),$80 ; and insert variables end-marker. INC HL ; address the next location. LD ($400A),HL ; set the system variable E_LINE. ; and continue... ; ------------------------- ; THE 'MAIN EXECUTION' LOOP ; ------------------------- ; This is the MAIN EXECUTION LOOP that handles the creation and interpretation ; of user input. The various 'subroutines' from this main loop including those ; launched from the Editing Keys Table are really just branches which all ; ultimately jump back to here. Although service routines make use of the ; machine stack, the stack is generally empty and only has one return address ; on it during command execution. ;; MAIN-EXEC L0283: LD HL,($400A) ; fetch E-LINE LD (HL),$B0 ; insert the character inverse 'K'. INC HL ; address the next location. LD (HL),$76 ; insert a newline. INC HL ; address the next location. LD ($400C),HL ; set D-FILE to start of dynamic display file. LD (IY+$12),$02 ; set DF-SZ to 2 lines. ; -> ;; AUTO-LIST L0293: CALL L0747 ; routine CLS sets a minimal display and ; initializes screen values in registers. EX DE,HL ; LD A,B ; load line value, 23, to A. SUB (IY+$12) ; subtract DF-SZ of lower screen. JR C,L02F7 ; forward if the lower screen is 24 lines ; to ED-COPY. INC A ; allow for a blank line. LD B,A ; place in B line EXX ; switch to preserve line/column values. LD HL,($4006) ; fetch E_PPC the current line number. LD DE,($4013) ; fetch the top line on screen from S_TOP. SBC HL,DE ; subtract the two BASIC line numbers EX DE,HL ; and bring S_TOP to HL. JR NC,L02B0 ; forward if current line >= top line to LIST-1. ADD HL,DE ; else reform the E_PPC value LD ($4013),HL ; and make S_TOP the same. ;; LIST-1 L02B0: CALL L060A ; routine LINE-ADDR gets the address of the ; BASIC line in HL. LD E,$00 ; signal current line yet to be printed ;; LIST-ALL L02B5: CALL L04F7 ; routine OUT-LINE JR C,L02B5 ; loop until upper screen is full to LIST-ALL. DEC E ; test if current line has appeared. JR NZ,L02F0 ; forward to LIST-DONE if current line ; has appeared. ; else the current line has yet to appear. PUSH HL ; else save HL ( ) LD HL,($4006) ; fetch E_PPC - the current line. CALL L060A ; routine LINE-ADDR in DE POP HL ; restore HL AND A ; prepare to subtract. SBC HL,DE ; subtract setting carry. LD HL,$4013 ; address system variable S_TOP JR NC,L02D8 ; forward if E_PPC precedes to LN-FETCH EX DE,HL ; else swap pointers. LD A,(HL) ; pick up high byte. INC HL ; address low byte. LDI ; copy low byte to S_TOP_lo. LD (DE),A ; insert the high byte. ;; AUTO-L-J L02D3: JR L0293 ; back to AUTO-LIST. ; ------------------------------------ ; THE 'CURSOR DOWN EDITING' SUBROUTINE ; ------------------------------------ ;; ED-DOWN L02D5: LD HL,$4006 ; address system variable E_PPC ; and continue... ; ---------------------- ; THE 'LN-FETCH' SECTION ; ---------------------- ;; LN-FETCH L02D8: LD E,(HL) ; INC HL ; LD D,(HL) ; PUSH HL ; EX DE,HL ; INC HL ; increment as starting point CALL L060A ; routine LINE-ADDR CALL L03C2 ; LINE-NO POP HL ; restore hi pointer. ; ---------------------- ; THE 'LN-STORE' SECTION ; ---------------------- ; On entry, HL holds E_PPC_hi. ;; LN-STORE L02E5: BIT 5,(IY+$19) ; test FLAGX. JR NZ,L02F7 ; forward if INPUT to ED-COPY. LD (HL),D ; insert high byte DEC HL ; DECrement LD (HL),E ; insert low byte ; JR L0293 ; back to AUTO-LIST ; -------------------------- ; THE 'LIST-DONE' SUBROUTINE ; -------------------------- ; When the listing is complete then the rest of the upper display is blanked, ; to erase what may have been printed during the interim, the display file ; cursor is updated and the current line is printed in the lower screen. ;; LIST-DONE L02F0: CALL L05C2 ; CL-EOD clear to end of upper display. LD ($400E),DE ; set lower screen position DF_EA ; to end ; and continue... ; ------------------------------------- ; THE 'LOWER SCREEN COPYING' SUBROUTINE ; ------------------------------------- ; This is called. ; When the line in the editing area is to be printed in the lower screen. ; It is by repeatedly printing the line when any key is pressed that the ; cursor for instance appears to move. ; It is called in a similar fashion to animate the input line. ;; ED-COPY L02F7: LD (IY+$01),$01 ; set FLAGS leading space allowed LD HL,($400A) ; E_LINE CALL L07BE ; routine MAIN-G checks syntax of line. LD DE,($400E) ; fetch start of lower screen from DF_EA LD B,(IY+$12) ; fetch lines in lower screen from DF_SZ LD C,$01 ; set column to 1 ; to print an initial newline for gap? EXX ; LD HL,($400A) ; fetch start of edit line from E_LINE CALL L0512 ; routine OUT-LINE-2 prints characters starting ; with the individual digits of line number. JR C,L031D ; forward with success to LINE-DONE ; else there wasn't enough room in lower screen for line. LD HL,$4012 ; address DF_SZ the Display Size for ; the lower screen. INC (HL) ; increment it. LD A,$18 ; load A with 24 decimal. CP (HL) ; compare to DF-SZ JR NC,L02D3 ; indirect jump back to AUTO-LIST ; if no greater than 24 lines. LD (HL),A ; else limit to 24 lines. ;; LINE-DONE L031D: CALL L05C2 ; routine CL-EOD clears to the end of lower ; screen CALL L013F ; routine KEYBOARD gets key values in BC. ; now decode the value SRA B ; sets carry if unshifted (bit 7 remains set) SBC A,A ; $FF unshifted, else $00 OR $26 ; $FF unshifted, else $26 LD L,$05 ; there are five keys in each row. SUB L ; set the starting point ;; KEY-LINE L032B: ADD A,L ; add value 5 (or 1) SCF ; carry will go to bit 7 RR C ; test C (which has 1 unset bit identifying row) JR C,L032B ; back if carry to KEY-LINE ; if only one key pressed C should now be $FF. INC C ; test for $FF JR NZ,L02F7 ; back if multiple keys to ED-COPY ; the high byte of the key value identifies the column - again only one bit is ; now reset. LD C,B ; transfer to B DEC L ; test if this is first time through LD L,$01 ; reduce increment from five to one. JR NZ,L032B ; back if L was five to KEY-LINE ; The accumulator now holds a key value 1-78 decimal. LD HL,L006C - 1 ; location before the MAIN-KEYS table ($006B) ; the index value is 1 - 78. LD E,A ; code to E (D is zero from keyboard) ADD HL,DE ; index into the table. LD A,(HL) ; pick up the letter/number/. BIT 2,(IY+$01) ; test FLAGS K-MODE ? JR Z,L034D ; skip forward if not ADD A,$C0 ; add 192 decimal ; e.g. 'A' 38d + 192 = 230 (LIST) CP $E6 ; compare to 'LIST' JR NC,L034D ; skip forward if command tokens to EDC-2. LD A,(HL) ; else load A from HL again ; (numbers and symbols) ;; EDC-2 L034D: CP $C0 ; set the overflow flag for editing key $70-$77 JP PE,L035E ; forward with range $40 - $7F to ED-KEYS LD HL,($4004) ; else fetch keyboard cursor from P_PTR LD BC,$0001 ; one space required. CALL L05D5 ; routine MAKE-ROOM makes room at cursor. ; note HL - first, DE - LAST LD (DE),A ; and insert the keyboard character. ;; EDC-JR L035C: JR L02F7 ; loop back to ED-COPY ; ----------------------------- ; THE 'EDITING KEYS' SUBROUTINE ; ----------------------------- ;; ED-KEYS L035E: LD E,A ; transfer code to E. ; (D holds zero from 'keyboard') LD HL,L0372-$70-$70; theoretical base of ED-K-TAB $0292 ADD HL,DE ; index twice ADD HL,DE ; as a two-byte address is required. LD C,(HL) ; low byte of routine. INC HL LD B,(HL) ; high byte of routine. PUSH BC ; push routine address to stack. LD HL,($4004) ; set HL to cursor from P_PTR RET ; jump to routine. ; Note the stack is empty. ; --------------------------------------------- ; THE EDITING 'DELETE ONE CHARACTER' SUBROUTINE ; --------------------------------------------- ;; ED-DEL-1 L036C: LD BC,$0001 ; one character JP L0666 ; routine RECLAIM-2 ; ------------------------ ; THE 'EDITING KEYS' TABLE ; ------------------------ ;; ED-K-TAB L0372: DEFW L03A9 ; ED-UP $70 DEFW L02D5 ; ED-DOWN $71 DEFW L0382 ; ED-LEFT $72 DEFW L0387 ; ED-RIGHT $73 DEFW L03B9 ; ED-HOME $74 DEFW L03CB ; ED-EDIT $75 DEFW L0408 ; ED-ENTER $76 DEFW L0395 ; ED-DELETE $77 ; ------------------------------------ ; THE 'CURSOR LEFT EDITING' SUBROUTINE ; ------------------------------------ ;; ED-LEFT L0382: CALL L039E ; routine ED-EDGE checks that cursor ; not at start without disturbing HL. ; quits early if not possible. >> DEC HL ; move left. DEC HL ; and again for luck. ; ... ; ------------------------------------- ; THE 'CURSOR RIGHT EDITING' SUBROUTINE ; ------------------------------------- ;; ED-RIGHT L0387: INC HL ; move right LD A,(HL) ; pick up the character. CP $76 ; is it newline ? JR Z,L03A7 ; triple jump back to ED-COPY if so. LD (HL),$B0 ; else place inverse cursor there. LD HL,($4004) ; fetch P_PTR LD (HL),A ; and put character there JR L035C ; double jump back to ED-COPY ; ------------------------------- ; THE 'DELETE EDITING' SUBROUTINE ; ------------------------------- ;; ED-DELETE L0395: CALL L039E ; routine ED-EDGE will loop back to ; ED-COPY if no deletion possible >> DEC HL ; decrement position CALL L036C ; routine ED-DEL-1 JR L035C ; back to ED-COPY ; ------------------------ ; THE 'ED-EDGE' SUBROUTINE ; ------------------------ ;; ED-EDGE L039E: LD DE,($400A) ; fetch E_LINE - start of edit line. LD A,(DE) ; pick up first character. CP $B0 ; test for inverse 'K' RET NZ ; return if cursor not at start. POP DE ; else drop the return address. ;; EDC-JR2 L03A7: JR L035C ; and back to ED-COPY ; ---------------------------------- ; THE 'CURSOR UP EDITING' SUBROUTINE ; ---------------------------------- ;; ED-UP L03A9: LD HL,($4006) ; E_PPC CALL L060A ; routine LINE-ADDR EX DE,HL CALL L03C2 ; LINE-NO ;; ED-LINE L03B3: LD HL,$4007 ; E_PPC_hi JP L02E5 ; to LN-STORE to store new line ; and produce an automatic listing. ; ------------------------ ; THE 'ED-HOME' SUBROUTINE ; ------------------------ ; ED-HOME (SHIFT 9) starts the listing at the first line. ; dropped in later ZX computers. ;; ED-HOME L03B9: LD DE,$0000 ; start at 'line zero' JR L03B3 ; back to ED-LINE above. ; -------------------------------------- ; THE 'COLLECT A LINE NUMBER' SUBROUTINE ; -------------------------------------- ;; LINE-NO-A L03BE: EX DE,HL ; bring previous line to HL ; and set DE in case we loop back a second time. LD DE,L03B9 + 1 ; address of $00 $00 within the subroutine ; above. ; -> The Entry Point. ;; LINE-NO L03C2: LD A,(HL) ; fetch hi byte of line number AND $C0 ; test against $3F JR NZ,L03BE ; back to LINE-NO-A if at end. LD D,(HL) ; else high byte to D INC HL ; increase pointer LD E,(HL) ; low byte in E. RET ; return. ; with next line number in DE ; ------------------------- ; THE 'EDIT KEY' SUBROUTINE ; ------------------------- ; Pressing the EDIT key causes the current line to be copied to the ; edit line. The two-byte line number is converted into 4 characters ; using leading spaces if the line is less than 1000. Next the 'K' ; cursor is inserted and the rest of the characters are copied verbatim ; into the edit buffer, keywords remaining as single character tokens. ;; ED-EDIT L03CB: LD C,$00 ; set column to zero to inhibit a line feed ; while 'sprinting' to the edit line. ; see PRINT-A-2. LD DE,($400A) ; set DE (print destination) to E_LINE EXX ; switch. LD HL,($4006) ; E_PPC current line. CALL L060A ; routine LINE-ADDR CALL L03C2 ; routine LINE-NO LD A,D OR E JP Z,L0283 ; back if zero to MAIN-EXEC ; no program. DEC HL ; point to location before CALL L06BF ; routine OUT-NUM-2 prints line number ; to the edit line (unseen). DEC HL ; point to line number again CALL L0624 ; routine NEXT-ONE gets length in ; BC register. INC HL ; point to the INC HL ; first token. DEC BC ; decrease the length DEC BC ; by the same. EXX PUSH DE ; pick up the print position in the EXX ; edit line. POP DE ; and pop it to this set of registers LD A,$B0 ; the inverse 'K' cursor LD (DE),A ; is inserted after line number. INC DE ; address next 'print' location. PUSH HL ; push position within program. LD HL,$0022 ; an overhead of 34d bytes. ADD HL,DE ; add to edit line position ADD HL,BC ; add in length of line. SBC HL,SP ; subtract the stack pointer. JR NC,L03A7 ; back to ED-COPY if not enough ; room to fill edit line. POP HL ; restore program position. LDIR ; and copy it to edit line. LD ($400C),DE ; update D_FILE JP L0293 ; jump back to AUTO-LIST ; ------------------------------ ; THE 'ENTER EDITING' SUBROUTINE ; ------------------------------ ; This causes the line to be parsed. ; The subroutine then loops back to MAIN-EXEC. ;; ED-ENTER L0408: LD HL,($4015) ; fetch X_PTR the error pointer. LD A,H ; check that it is OR L ; zero - no error. JR NZ,L03A7 ; double jump back to ED-COPY ; if an error has occurred during ; syntax checking. LD HL,($4004) ; P_PTR CALL L036C ; ED-DEL-1 gets rid of cursor. LD HL,($400A) ; E_LINE LD ($4026),HL ; CH_ADD CALL L001A ; get-char BIT 5,(IY+$19) ; FLAGX input 1/edit 0 JR NZ,L043C ; forward to MAIN-1 if in input mode. ; else the edit line is to be run. CALL L0679 ; INT-TO-HL line number to HL' EXX ; switch in set with the line number. LD A,H ; and test OR L ; for zero. JP NZ,L04BA ; jump forward with a number to MAIN-ADD ; to add a new BASIC line or replacement. ; else must be a direct command. DEC HL ; make the line number DEC HL ; the value minus two. LD ($4002),HL ; and set PPC CALL L0747 ; routine CLS EXX ; LD A,(HL) ; fetch first character. CP $76 ; is it just a newline ? JP Z,L0283 ; jump back with newline to MAIN-EXEC ; to produce an automatic listing. ; else check syntax and enter ;; MAIN-1 L043C: LD (IY+$00),$FF ; set ERR_NR to no error LD (IY+$01),$88 ; update FLAGS ; set bit 7 - syntax checking off ; set bit 3 - 'K' mode ;; M-2 L0444: CALL L07BE ; routine MAIN-G parses and executes the line. ; Note. this causes the value L0447 to be placed ; on the machine stack as a return address. ;; M-3 L0447: CALL L0D0A ; REC-EDIT reclaims the edit line LD DE,($4002) ; fetch current line number from PPC LD HL,$4019 ; address FLAGX BIT 5,(HL) ; test FLAGX - input??? JR Z,L0458 ; skip if editing to -> RES 5,(HL) ; update FLAGX - signal editing. INC DE ; increase line number so cursor doesn't show. ;; M-4 L0458: BIT 7,(IY+$00) ; check ERR_NR. JR Z,L0488 ; forward if an error has occurred. LD HL,$4001 ; address FLAGS system variable BIT 3,(HL) ; test FLAGS - K mode ? RES 3,(HL) ; update FLAGS - set L mode for future anyway. LD HL,($4026) ; fetch character address CH_ADD INC HL ; JR Z,L0474 ; forward if not K mode. EX DE,HL ; current line to HL, next char to DE. LD A,H ; fetch high byte of line number. AND $C0 ; test for -2, -1 - direct command. JR NZ,L0488 ; forward to MAIN-ERR if so CALL L060A ; routine LINE-ADDR gets address of this line. ;; M-5 L0474: LD A,(HL) ; fetch AND $C0 ; JR NZ,L0488 ; at program end ; else pick up the next line number LD D,(HL) ; INC HL ; LD E,(HL) ; LD ($4002),DE ; place in PPC system variable INC HL ; point to first character ; (space or command) LD A,$7F ; test for IN A,($FE) ; space key pressed. RRA ; the space bit. JR C,L0444 ; back if BREAK ; else continue... ;; MAIN-ERR L0488: CALL L06E0 ; UNSTACK-Z quits if checking syntax >>> CALL L05C2 ; routine CL-EOD clears to the end of upper ; display area. LD BC,$0120 ; set line 1, column 32 for lower screen. EXX ; LD A,($4000) ; fetch the error number from ERR_NR LD BC,($4002) ; fetch the current line from PPC INC A ; test if error still $FF JR Z,L04A8 ; forward if so to MAIN-5. CP $09 ; is the error the STOP statement ? JR NZ,L04A1 ; forward if not STOP to SET-CONT to make the ; continuing line the same as current. INC BC ; else increment line number for STOP. ;; SET-CONT L04A1: LD ($4017),BC ; store line number in OLDPPC JR NZ,L04A8 ; forward if not STOP as line number is current DEC BC ; else decrement line number again. ; Now print the report line e.g. 100/0 (terminated OK at line 100) ;; MAIN-5 L04A8: CALL L0556 ; routine OUT-CODE prints line number LD A,$15 ; prepare character '/' RST 10H ; print the separator CALL L06A1 ; OUT-NUM-1 to print error-code in A. CALL L05C2 ; routine CL-EOD CALL L013F ; routine KEYBOARD JP L0283 ; jump back to MAIN-EXEC ; --------------------- ; THE 'MAIN-ADD' BRANCH ; --------------------- ; This section allows a new BASIC line to be added to the Program. ;; MAIN-ADD L04BA: LD ($4006),HL ; make E_PPC the new line number. EXX ; EX DE,HL ; CALL L0747 ; routine CLS SBC HL,DE ; EXX ; CALL L060A ; routine LINE-ADDR PUSH HL ; JR NZ,L04D1 ; forward if line doesn't exist to MAIN-ADD1. CALL L0624 ; routine NEXT-ONE gets length of old line CALL L0666 ; routine RECLAIM-2 ;; MAIN-ADD1 L04D1: EXX ; INC HL ; LD B,H ; LD C,L ; LD A,L ; SUB $03 ; OR H ; CALL NZ,L094F ; routine TEST-ROOM POP HL ; JR NC,L04F4 ; double jump back to MAIN-EXEC ; not possible. PUSH BC ; DEC HL ; CALL L05D5 ; routine MAKE-ROOM INC DE ; LD HL,($400C) ; set HL from D_FILE DEC HL ; now points to end of edit line. POP BC ; restore length DEC BC ; LDDR ; copy line from edit line to prog. LD HL,($4006) ; E_PPC - line number EX DE,HL ; swap LD (HL),D ; insert high byte INC HL ; LD (HL),E ; insert low byte ;; MAIN-JR L04F4: JP L0283 ; jump back to MAIN-EXEC ; ----------------------------------------- ; THE 'PRINT A WHOLE BASIC LINE' SUBROUTINE ; ----------------------------------------- ;; OUT-LINE L04F7: LD BC,($4006) ; fetch E_PPC CALL L061C ; routine CP-LINES LD D,$97 ; prepare character '>' JR Z,L0507 ; forward with line cursor if line is the ; current edit line to OUT-LINE-1 LD DE,$0000 ; else replace line cursor with a ; space in D, and zero to E. RL E ; pick up any carry from CP-LINES ; should the line precede the ; current edit line. ;; OUT-LINE-1 L0507: LD A,(HL) ; fetch the high byte of line number. CP $40 ; compare with end marker CALL C,L06BF ; routine OUT-NUM-2 if a valid line number. RET NC ; return if out of screen >>> INC HL ; address the first command character. LD A,D ; fetch the space/cursor RST 10H ; print it. RET NC ; return if out of screen. ;; OUT-LINE-2 L0512: SET 0,(IY+$01) ; update FLAGS - suppress a leading space ;; OUT-LINE-3 L0516: LD BC,($4015) ; fetch error pointer - X_PTR AND A ; prepare to subtract. SBC HL,BC ; subtract the current address. JR NZ,L0523 ; forward to OUT-LINE-4 if not an ; exact match. LD A,$B8 ; prepare inverse 'S' to show syntax error. RST 10H ; print it. RET Z ; return if at end ;; OUT-LINE-4 L0523: ADD HL,BC ; restore pointer. LD A,(HL) ; fetch character. INC HL ; address next character. CP $B0 ; is character inverse 'K' ? JR Z,L053C ; forward if so to OUT-CURS. ; then cleverly split the characters into 4 streams. CP $C0 ; compare character to 192 ? JP PE,L0559 ; jump forward with 64-127 to OUT-SP-CH ; thereby exiting the routine ; as it must be the 118, NEWLINE character. JR C,L0536 ; forward with 0-63, 128-191 to OUT-LINE-5 ; to print simple characters and their inverse ; forms. ; that leaves tokens $C0 - $FF CALL L0584 ; routine PO-TOKEN JR L0539 ; forward to OUT-LINE-6 ; --- ;; OUT-LINE-5 L0536: CALL L0559 ; routine OUT-SP-CH ;; OUT-LINE-6 L0539: RET NC ; return if out of screen. >> JR L0516 ; else back to OUT-LINE-3 for more. ; --------------------------------------------------------------------------- ; Z80 PARITY/OVERFLOW FLAG: ; ------------------------ ; The use of this flag is two-fold depending on the type of operation. ; It indicates the parity of the result of a LOGICAL operation such as an AND, ; OR, XOR by being set PE if there are an even number of set bits and reset ; PO if there are an odd number of set bits. ; so 10101010 is parity even, 00000001 is parity odd. ; JP PE, LABEL ; JP PO, LABEL are obvious. ; For MATHEMATICAL operations, (ADD, SUB, CP etc.) the P/V bit indicates a ; carry out of bit position 6 of the accumulator if signed values are being ; used. ; This indicates an overflow of a result greater than 127, which carries ; into bit 7, the sign bit. ; So as CP is just a SUB with the result thrown away. ; $C0 SUB $CO gives result $00 (PO - no overflow from 6 to 7) ; $8O SUB $C0 gives result $C0 (PO - no overflow from 6 to 7) ; $00 SUB $C0 gives result $40 (PO - no overflow from 6 to 7) ; $40 SUB $CO gives result $80 (PE - overflow from 6 to 7) ; The overflow flag is similarly set following 16-bit addition and subtraction ; routines. ; --------------------------------------------------------------------------- ; ----------------------------- ; THE 'PRINT THE CURSOR' BRANCH ; ----------------------------- ;; OUT-CURS L053C: BIT 2,(IY+$01) ; test FLAGS - K-mode ? JR NZ,L0543 ; skip to OUT-K if 'K' mode. INC A ; change from 'K' to 'L' cursor. ;; OUT-K L0543: RST 10H ; print the cursor. JR L0539 ; back to OUT-LINE-6 above. ; ----------------------------------------------------- ; THE 'PRINTING CHARACTERS IN A BASIC LINE' SUBROUTINES ; ----------------------------------------------------- ;; OUT-SP-2 L0546: LD A,E ; transfer E to A ; register E will be ; $FF - no leading space. ; $01 - the leading space itself. ; $1C - '0' from a previous non-space print. RLCA ; test for the RRCA ; value $FF. RET C ; return if no leading space JR L055C ; forward to OUT-LD-SP ; --- ; --> The Entry Point. ;; OUT-SP-NO L054C: XOR A ; set accumulator to zero. ;; OUT-SP-1 L054D: ADD HL,BC ; addition of negative number. INC A ; increment the digit. JR C,L054D ; back while overflow exists to OUT-SP-1 SBC HL,BC ; else reverse the last addition. DEC A ; and decrement the digit. JR Z,L0546 ; back to OUT-SP-2 if digit is zero again. ; else continue to print the final digit using OUT-CODE. ;; OUT-CODE L0556: LD E,$1C ; load E with '0' ; Note. that E will remain as such for all ; further calls. The leading space is no more. ADD A,E ; add the digit 1-9 to give '1' to '9' ;; OUT-SP-CH L0559: AND A ; test value for space. JR Z,L0560 ; skip if zero to PRINT-A-2 ;; OUT-LD-SP L055C: RES 0,(IY+$01) ; signal allow leading space to FLAGS ; and continue... ; ------------------------------ ; THE 'MAIN PRINTING' SUBROUTINE ; ------------------------------ ; This is a continuation of the PRINT restart. ; It is used primarily to print to the dynamic screen checking free memory ; before every character is printed. ; However it can also be used as an invisible process to 'sprint' the line ; number of a BASIC line to the Edit Line by ED-EDIT setting DE from E_LINE. ; ; As lines are unexpanded, then when the column count is reduced from 32 to 0 a ; newline is inserted before the character and the column count is reset. ;; PRINT-A-2 L0560: EXX ; switch sets. LD H,A ; preserve character in H. ; Note. this is restored by TEST-RM-2 RLA ; rotate character twice to RLA ; test bit 6 - sets carry for NEWLINE. DEC C ; decrease column count - affects zero / sign. JR NC,L0569 ; forward if 0-63 or inverse to NO-NL ; else the incoming character is a NEWLINE $76 LD C,$00 ; set column to zero without disturbing flags. ; if this is a received NEWLINE. ; this will be set to 32 if a subsequent ; character is printed ;; NO-NL L0569: JP M,L0574 ; jump to PR-SPR if column was originally 0 JR C,L057C ; forward to PRI-CHAR with a received NEWLINE. JR NZ,L057C ; forward if column not yet reduced to zero ; to PRI-CHAR ; else an automatic newline is required before the received character as ; we are at end of line. LD A,$76 ; prepare the newline LD (DE),A ; insert at screen position INC DE ; increase the address pointer. ;; PR-SPR L0574: JR C,L0578 ; skip if a received newline to PRI-SKIP LD C,$20 ; reset column to 32 decimal. ;; PRI-SKIP L0578: AND A ; clear carry now to signal failure should the ; next test fail. DEC B ; decrease line. JR Z,L0582 ; forward with out of screen to PR-END. ;; PRI-CH L057C: LD L,B ; transfer line number, B to L for next routine. CALL L0958 ; routine TEST-RM-2 tests room. ; (character is in H returned in A) ; carry set if there is room. LD (DE),A ; insert chr at screen (or edit line). INC DE ; increase destination address. ;; PR-END L0582: EXX ; switch to protect registers. RET ; return ; ------------------------------- ; THE 'TOKEN PRINTING' SUBROUTINE ; ------------------------------- ;; PO-TOKEN L0584: CALL L05A8 ; routine PO-SEARCH locates token JR NC,L0592 ; forward to PO-LOOP if first character is ; not alphanumeric. e.g. '**' ; else consider a leading space. BIT 0,(IY+$01) ; test FLAGS - leading space allowed ? JR NZ,L0592 ; forward to PO-LOOP if not. ; else print a leading space. XOR A ; prepare a space RST 10H ; print it RET NC ; return if out of screen. ; now enter a loop to print each character and then consider a trailing space. ;; PO-LOOP L0592: LD A,(BC) ; fetch character from token table. AND $3F ; mask to give range ' ' to 'Z' CALL L0559 ; routine OUT-SP-CH RET NC ; return if out of screen. LD A,(BC) ; reload the character INC BC ; point to next. ADD A,A ; test for the inverted bit. JR NC,L0592 ; loop back if not inverted to PO-LOOP ; CP $38 ; compare with what was '0' before doubling. RET C ; return if less. i.e. not a command. >> XOR A ; else prepare a space SET 0,(IY+$01) ; update FLAGS - use no leading space JR L0560 ; back to PRINT-A-2 for trailing space. >> ; ----------------------------- ; THE 'TABLE SEARCH' SUBROUTINE ; ----------------------------- ;; PO-SEARCH L05A8: PUSH HL ; * preserve character pointer LD HL,$00BA ; point to start of the table SUB (HL) ; test against the threshold character 212 INC HL ; address next in table. ('?' + $80 ) JR C,L05B9 ; forward to PO-FOUND if less than 212 ; to print a question mark. INC A ; make range start at 1 for chr 212. ; note - should the required token be 212 ; the printable quote character then the ; pointer currently addresses '"' + $80. LD B,A ; save reduced token in B as a counter. ;; PO-STEP L05B2: BIT 7,(HL) ; test for inverted bit INC HL ; increase address JR Z,L05B2 ; back to PO-STEP for inverted bit DJNZ L05B2 ; decrement counter and loop back to PO-STEP ; until at required token. ;; PO-FOUND L05B9: LD B,H ; transfer the address LD C,L ; to BC. POP HL ; * restore string address LD A,(BC) ; fetch first character from token. AND $3F ; mask off range 0-63d, SPACE to Z ADD A,$E4 ; add value 228 RET ; return with carry set if alphanumeric and a ; leading space is required. ; ------------------------------------- ; THE 'CLEAR TO END OF DISPLAY' ROUTINE ; ------------------------------------- ;; CL-EOD L05C2: EXX ; switch in the set with screen values. XOR A ; clear accumulator. CP B ; compare with line counter - 0 to 23. JR Z,L05D0 ; forward if clear to SET-EOD. CP C ; compare to column count - 0 to 32. LD A,$76 ; prepare a NEWLINE. JR Z,L05CE ; forward, if zero, to CL-EOL. ;; INS-CR L05CC: LD (DE),A ; insert a newline/carriage return. INC DE ; address next position. ;; CL-EOL L05CE: DJNZ L05CC ; reduce line counter and loop back to INS-CR. ;; SET-EOD L05D0: LD ($4010),DE ; update DF_END - display file end. RET ; return. ; -------------------------- ; THE 'MAKE-ROOM' SUBROUTINE ; -------------------------- ;; MAKE-ROOM L05D5: CALL L05DF ; routine POINTERS also sets BC LD HL,($4010) ; fetch new display file end DF_END EX DE,HL ; switch source/destination. LDDR ; now make the room. RET ; return. ; with HL pointing at first new location. ; ------------------------- ; THE 'POINTERS' SUBROUTINE ; ------------------------- ;; POINTERS L05DF: PUSH AF ; PUSH HL ; LD HL,$4008 ; VARS LD A,$05 ; ;; PTR-NEXT L05E6: LD E,(HL) ; INC HL ; LD D,(HL) ; EX (SP),HL ; AND A ; SBC HL,DE ; ADD HL,DE ; EX (SP),HL ; JR NC,L05FA ; forward to PTR-DONE PUSH DE ; EX DE,HL ; ADD HL,BC ; EX DE,HL ; LD (HL),D ; DEC HL ; LD (HL),E ; INC HL ; POP DE ; ;; PTR-DONE L05FA: INC HL ; DEC A ; JR NZ,L05E6 ; back to PTR-NEXT for all five ; dynamic variables. ; now find the size of the block to be moved. 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 ; return -> ; -------------------------- ; THE 'LINE-ADDR' SUBROUTINE ; -------------------------- ;; LINE-ADDR L060A: PUSH HL ; save the given line number. LD HL,$4028 ; start of PROG LD D,H ; transfer the address LD E,L ; to the DE register pair. ;; LINE-AD-1 L0610: POP BC ; the given line number. EX DE,HL ; CALL L061C ; routine CP-LINES RET NC ; return if carry set >> PUSH BC ; otherwise save given line number CALL L0624 ; routine NEXT-ONE JR L0610 ; back to LINE-AD-1 to consider the next ; line of the program. ; ------------------------------------- ; THE 'COMPARE LINE NUMBERS' SUBROUTINE ; ------------------------------------- ;; CP-LINES L061C: LD A,(HL) ; fetch the high byte of the addressed line CP B ; number and compare it. RET NZ ; return if they do not match. INC HL ; next compare the low bytes. LD A,(HL) ; DEC HL ; CP C ; RET ; return with carry flag set if the addressed ; line number has yet to reach the ; given line number. ;------------------------------------------------------------------------ ; Storage of variables. For full details - see Page 107 ; ZX80 BASIC Programming by Hugo Davenport 1980. ; It is bits 7-5 of the first character of a variable that allow ; the five types to be distinguished. Bits 4-0 are the reduced letter. ; So any variable name is higher that $3F and can be distinguished ; also from the variables area end-marker $80. ; ; 76543210 meaning brief outline of format after letter. ; -------- ------------------------ ----------------------- ; 011 simple integer variable. 2 bytes. (after letter) ; 010 long-named integer variable 2 bytes. (after inverted name) ; 100 string letter + contents + $01. ; 101 array of integers letter + max subs byte + subs * 2. ; 111 for-next loop variable. 7 bytes - letter, value, limit, line. ; 10000000 the variables end-marker. ; ; Note. any of the above six will serve as a program end-marker. ; ; ----------------------------------------------------------------------- ; ------------------------- ; THE 'NEXT-ONE' SUBROUTINE ; ------------------------- ;; NEXT-ONE L0624: PUSH HL ; save address of current line or variable. LD A,(HL) ; fetch the first byte. ADD A,A ; test bits 7 and 6 JP M,L0635 ; jump forward if simple, long-named or for-next ; control variable to NO-SLNFM JR C,L0643 ; forward if string or arrays to NO-STR-AR ; that leaves program line numbers. INC HL ; step past high byte LD A,$76 ; the search is for newline ;; NO-SEARCH L062F: INC HL ; skip to next address past low byte. LD B,A ; save search byte in B to create ; a large value in BC so that search is ; not curtailed. CPIR ; and locate the known character. JR L0652 ; forward to ??? with HL addressing ; the following character. ; --- ; the branch was here with simple, long-named and for-next variables ;; NO-SLNFN L0635: LD BC,$0002 ; presume a for-next variable (1+2 cells) JR C,L063B ; skip forward if for-next variable. LD C,B ; set C to zero - just one cell for simple ; and long-named. ;; NO-FNXT L063B: RLA ; original bit 5 is now bit 7. ;; NO-LNLP L063C: RLA ; test original bit 5 of letter. INC HL ; advance address. LD A,(HL) ; pick up next byte - possibly a letter JR NC,L063C ; back if originally long-named or if ; on subsequent loops character is not inverted ; whatever the route we are now pointing at the first cell with the number ; of cells less one in register C. JR L064F ; forward to NO-CELLS to calculate space to the ; end of variable. ; --- ; the branch was here with either single strings or numeric array variables ;; NO-STR_AR L0643: AND $40 ; test shifted bit 6 - will be set for arrays LD A,$01 ; set search for null terminator JR Z,L062F ; back if not an array to NO-SEARCH to ; search for the end of string. ; the object is a NUMERIC ARRAY INC HL ; point to maximum subscription LD A,(HL) ; and fetch INC HL ; point to first cell. LD B,$00 ; prepare to index LD C,A ; max subscription to C ; and continue to find following byte. ;; NXT-O-6 L064F: INC BC ; bump the range ADD HL,BC ; add to start ADD HL,BC ; add again as each cell is two bytes. ;; NXT-O-7 L0652: POP DE ; restore previous address to DE and ; continue into the difference routine... ; --------------------------- ; THE 'DIFFERENCE' SUBROUTINE ; --------------------------- ;; DIFFER L0653: AND A ; prepare to subtract. SBC HL,DE ; calculate the length of the line/var LD B,H ; transfer the length LD C,L ; to the BC register pair. ADD HL,DE ; reform the address of next one in HL. EX DE,HL ; swap pointers RET ; return. ; ------------------------------ ; THE 'CLEAR' COMMAND SUBROUTINE ; ------------------------------ ; The CLEAR command removes all BASIC variables. ;; CLEAR L065B: LD HL,($400A) ; set HL to E_LINE. DEC HL ; decrement to point to the $80 end-marker. LD DE,($4008) ; set start from VARS system variable. ; ---------------------------- ; THE 'RECLAIMING' SUBROUTINES ; ---------------------------- ;; RECLAIM-1 L0663: CALL L0653 ; routine DIFFER ;; RECLAIM-2 L0666: PUSH BC ; LD A,B ; CPL ; LD B,A ; LD A,C ; CPL ; LD C,A ; INC BC ; CALL L05DF ; routine POINTERS EX DE,HL ; POP HL ; ADD HL,DE ; PUSH DE ; LDIR ; POP HL ; RET ; return. ; ---------------------------------------- ; THE 'INTEGER TO ALTERNATE HL' SUBROUTINE ; ---------------------------------------- ;; INT-TO-HL L0679: LD A,(HL) ; fetch first digit EXX ; switch LD HL,$0000 ; initialize result register to zero. LD B,H ; make B zero also. ;; DEC-LP L067F: SUB $1C ; subtract chr '0' JR C,L069A ; forward to STOR-RSLT if less. >> CP $0A ; compare with 'ten' JR NC,L069A ; forward to STOR-RSLT if higher than '9'. >> LD C,A ; save unit in C. ; now test that the result is not about to enter the 32768 - 65535 region. LD A,$0D ; value 13 to A CP H ; compare to result_hi JR NC,L068E ; forward if less to NO-OVERFLW LD H,A ; else maintain the overflow condition. ;; NO-OVRFLW L068E: LD D,H ; copy HL. LD E,L ; to DE. ADD HL,HL ; double result ADD HL,HL ; and again. ADD HL,DE ; now * 5 ADD HL,HL ; now *10 ADD HL,BC ; add in new digit. EXX ; switch RST 18H ; NXT-CH-SP EXX ; switch JR L067F ; loop back to DEC-LP for more digits. ; ------------------------------------- ; THE 'STORE INTEGER RESULT' SUBROUTINE ; ------------------------------------- ;; STOR-RSLT L069A: LD A,H ; transfer high byte to A. LD ($4022),HL ; set value of expression RESULT EXX ; switch RLA ; sets carry if higher than 32767 RET ; return. ; ------------------------------------------------ ; THE 'REPORT AND LINE NUMBER PRINTING' SUBROUTINE ; ------------------------------------------------ ; Actually the first entry point prints any number in the ; range -32768 to 32767. ; --> This entry point prints a number in BC. ;; OUT-NUM-1 L06A1: PUSH DE ; preserve registers PUSH HL ; throughout LD H,B ; transfer number LD L,C ; to be printed to HL. BIT 7,B ; test the sign bit JR Z,L06B5 ; forward if positive to OUT-NUM-P LD A,$12 ; prepare character '-' CALL L0559 ; routine OUT-SP-CH JR NC,L06DD ; forward if out of screen to OUT-NUM-4 LD HL,$0001 ; else make the negative number SBC HL,BC ; positive. ; at this stage the number is positive ;; OUT-NUM-P L06B5: LD E,$FF ; signal no leading space. LD BC,$D8F0 ; prepare the value -10000 CALL L054C ; routine OUT-SP-NO will print the first digit ; of a 5-digit number but nothing if smaller. JR L06C8 ; forward to OUT-NUM-3 ; to consider other four digits in turn. ; (with carry set from a successful print) ; --- ; --> This entry point prints a BASIC line number addressed by HL. ;; OUT-NUM-2 L06BF: PUSH DE ; save DE throughout LD D,(HL) ; fetch high byte of number to D INC HL LD E,(HL) ; fetch low byte of number to E PUSH HL ; save HL now till the end. EX DE,HL ; number to HL. LD E,$00 ; prepare a leading space SCF ; set carry flag for subtractions. ; both paths converge here. ;; OUT-NUM-3 L06C8: LD BC,$FC18 ; the value -1000 CALL C,L054C ; routine OUT-SP-NO LD BC,$FF9C ; the value -100 CALL C,L054C ; routine OUT-SP-NO LD C,$F6 ; the value -10 CALL C,L054C ; routine OUT-SP-NO LD A,L ; the remainder. CALL C,L0556 ; routine OUT-CODE ;; OUT-NUM-4 L06DD: POP HL ; restore original POP DE ; registers. RET ; return. ; -------------------------- ; THE 'UNSTACK-Z' SUBROUTINE ; -------------------------- ;; UNSTACK-Z L06E0: BIT 7,(IY+$01) ; test FLAGS - Checking Syntax ? POP HL ; drop the return address RET Z ; return if so. ; else fetch screen coordinates alternate registers for the run-time situation. EXX LD DE,($400E) ; fetch display print position DF_EA LD BC,($4024) ; fetch line and column from SPOSN EXX ; exchange and continue... ; and jump back to the calling routine... ; ------------------ ; THE 'USR' FUNCTION ; ------------------ ;; USR L06F0: JP (HL) ; that appears to be it. ; --------------------------- ; THE 'PRINT ITEM' SUBROUTINE ; --------------------------- ;; PR-ITEM L06F1: BIT 7,(IY+$00) ; ERR_NR RET Z ; return if an error has already been ; encountered. CALL L06E0 ; UNSTACK-Z quits if checking syntax LD HL,($4022) ; fetch result of SCANNING from RESULT BIT 6,(IY+$01) ; test FLAGS for result type. JR Z,L070C ; forward to PR-STRING if type string. LD B,H ; transfer result LD C,L ; to BC register pair. CALL L06A1 ; routine OUT-NUM-1 JR L0723 ; forward to PO-CHECK to check for ; success and store position ; ----------------------------- ; THE 'PRINT STRING' SUBROUTINE ; ----------------------------- ;; PO-CHAR L0709: RST 10H ; PRINT-A ;; PO-LOOP L070A: JR NC,L0725 ; forward to ERROR-05 with carry ; Out of screen. ; --> Entry Point. ;; PR-STRING L070C: LD A,(HL) ; fetch a character. INC HL ; increment pointer. CP $01 ; is it null-terminator. JR Z,L073A ; forward to PO-STORE if so. BIT 6,A ; test if simple character or inverse JR Z,L0709 ; back to PO-CHAR if so CALL L0584 ; routine PO-TOKEN to print ; ranges $40 - $7f, $0C - $FF JR L070A ; loop back to PO-LOOP ; -------------------------------- ; THE 'CARRIAGE RETURN' SUBROUTINE ; -------------------------------- ;; PRINT-CR L071B: CALL L06E0 ; UNSTACK-Z quits if checking syntax LD A,$76 ; prepare a NEWLINE character CALL L0559 ; routine OUT-SP-CH prints it ; returning with carry reset if there ; was no room on the screen. ;; PO-CHECK L0723: JR C,L073A ; forward to PO-STORE if OK ;; ERROR-05 L0725: RST 08H ; ERROR restart DEFB $04 ; No more room on screen. ; ------------------------ ; THE 'PO-FILL' SUBROUTINE ; ------------------------ ;; PO-FILL L0727: CALL L06E0 ; UNSTACK-Z return if checking syntax. SET 0,(IY+$01) ; signal no leading space. ;; PO-SPACE L072E: XOR A ; prepare a space RST 10H ; PRINT-A outputs the character. JR NC,L0725 ; back to ERROR-05 if out of screen EXX ; LD A,C ; get updated column EXX ; DEC A ; decrement it. AND $07 ; isolate values 0 - 7 JR NZ,L072E ; back to PO-SPACE for more. ; ------------------------------- ; THE 'POSITION STORE' SUBROUTINE ; ------------------------------- ;; PO-STORE L073A: EXX ; switch in the set that maintains the print ; positions in the registers. EX DE,HL ; switch print position to HL for easier coding. ;; PO-STOR-2 L073C: LD ($4024),BC ; set SPOSN to line/column LD ($400E),HL ; set DF_EA to output address LD ($4010),HL ; set DF_END output address RET ; return. ; ---------------------------- ; THE 'CLS' COMMAND SUBROUTINE ; ---------------------------- ;; CLS L0747: LD HL,($400C) ; fetch start of display from D_FILE LD (HL),$76 ; insert a single newline. INC HL ; advance address. LD BC,$1721 ; set line to 23 and column to 33. JR L073C ; back to PO-STOR-2 above ; ------------------- ; THE 'SYNTAX TABLES' ; ------------------- ;; i. The offset table L0752: DEFB L07A1 - $ ; $4F offset to $07A1 P-LIST DEFB L077F - $ ; $2C offset to $077F P-RETURN DEFB L07B8 - $ ; $64 offset to $07B8 P-CLS DEFB L0794 - $ ; $3F offset to $0794 P-DIM DEFB L07AF - $ ; $59 offset to $07AF P-SAVE DEFB L0782 - $ ; $2B offset to $0782 P-FOR DEFB L076F - $ ; $17 offset to $076F P-GO-TO DEFB L07A4 - $ ; $4B offset to $07A4 P-POKE DEFB L0790 - $ ; $36 offset to $0790 P-INPUT DEFB L07A9 - $ ; $4E offset to $07A9 P-RANDOMISE DEFB L076C - $ ; $10 offset to $076C P-LET DEFB L07BB - $ ; $5E offset to $07BB P-CH-END DEFB L07BB - $ ; $5D offset to $07BB P-CH-END DEFB L0789 - $ ; $2A offset to $0789 P-NEXT DEFB L078D - $ ; $2D offset to $078D P-PRINT DEFB L07BB - $ ; $5A offset to $07BB P-CH-END DEFB L07C2 + 1 - $ ; $61 offset to $07C3 P-NEW DEFB L079E - $ ; $3B offset to $079E P-RUN DEFB L077C - $ ; $18 offset to $077C P-STOP DEFB L07B2 - $ ; $4D offset to $07B2 P-CONTINUE DEFB L0773 - $ ; $0D offset to $0773 P-IF DEFB L0778 - $ ; $11 offset to $0778 P-GOSUB DEFB L07AC - $ ; $44 offset to $07AC P-LOAD DEFB L07B5 - $ ; $4C offset to $07B5 P-CLEAR DEFB L079B - $ ; $31 offset to $079B P-REM DEFB L07BB - $ ; $50 offset to $07BB P-CH-END ;; ii. The parameter table. ;; P-LET L076C: DEFB $01 ; Class-01 - a variable is required. DEFB $E3 ; separator '=' DEFB $02 ; Class-02 - an expression, of type integer or ; string must follow. ;; P-GO-TO L076F: DEFB $06 ; Class-06 - a numeric expression must follow. DEFB $00 ; Class-00 - no further operands. DEFW L0934 ; address: $0934 ;; P-IF L0773: DEFB $06 ; Class-06 - a numeric expression must follow. DEFB $D5 ; separator 'THEN' DEFB $05 ; Class-05 - variable syntax checked entirely ; by routine. DEFW L08B9 ; address: $08B9 ;; P-GOSUB L0778: DEFB $06 ; Class-06 - a numeric expression must follow. DEFB $00 ; Class-00 - no further operands. DEFW L0943 ; address: $0943 ;; P-STOP L077C: DEFB $00 ; Class-00 - no further operands. DEFW L092E ; address: $092E ;; P-RETURN L077F: DEFB $00 ; Class-00 - no further operands. DEFW L0965 ; address: $0965 ;; P-FOR L0782: DEFB $04 ; Class-04 - a single-character variable must ; follow. DEFB $E3 ; separator '=' DEFB $06 ; Class-06 - a numeric expression must follow. DEFB $D6 ; separator 'TO' DEFB $05 ; Class-05 - variable syntax checked entirely ; by routine. DEFW L08C4 ; address: $08C4 ;; P-NEXT L0789: DEFB $04 ; Class-04 - a single-character variable must ; follow. DEFB $00 ; Class-00 - no further operands. DEFW L08F9 ; address: $08F9 ;; P-PRINT L078D: DEFB $05 ; Class-05 - variable syntax checked entirely ; by routine. DEFW L0972 ; address: $0972 ;; P-INPUT L0790: DEFB $01 ; Class-01 - a variable is required. DEFB $00 ; Class-00 - no further operands. DEFW L099A ; address: $099A ;; P-DIM L0794: DEFB $04 ; Class-04 - a single-character variable must ; follow. DEFB $DA ; separator '(' DEFB $06 ; Class-06 - a numeric expression must follow. DEFB $D9 ; separator ')' DEFB $00 ; Class-00 - no further operands. DEFW L0CD3 ; address: $0CD3 ;; P-REM L079B: DEFB $05 ; Class-05 - variable syntax checked entirely ; by routine. DEFW L084A ; address: $084A ;; P-RUN L079E: DEFB $03 ; Class-03 - a numeric expression may follow ; otherwise zero will be used. DEFW L093D ; address: $093D ;; P-LIST L07A1: DEFB $03 ; Class-03 - a numeric expression may follow ; else default to zero. DEFW L0256 ; Address: $0256 ;; P-POKE L07A4: DEFB $06 ; Class-06 - a numeric expression must follow. DEFB $D8 ; separator ',' DEFB $05 ; Class-05 - variable syntax checked entirely ; by routine. DEFW L09D1 ; address: $09D1 ;; P-RANDOMISE L07A9: DEFB $03 ; Class-03 - a numeric expression may follow ; otherwise zero will be used. DEFW L0923 ; address: $0923 ;; P-LOAD L07AC: DEFB $00 ; Class-00 - no further operands. DEFW L0206 ; address: $0206 ;; P-SAVE L07AF: DEFB $00 ; Class-00 - no further operands. DEFW L01B6 ; address: $01B6 ;; P-CONTINUE L07B2: DEFB $00 ; Class-00 - no further operands. DEFW L0930 ; address: $0930 ;; P-CLEAR L07B5: DEFB $00 ; Class-00 - no further operands. DEFW L065B ; address: $065B ;; P-CLS L07B8: DEFB $00 ; Class-00 - no further operands. DEFW L0747 ; Address: $0747 ;; P-CH-END L07BB: DEFB $05 ; Class-05 - variable syntax checked entirely ; by routine. DEFW L0844 ; address: $0844 ; Note. one would expect the entry for the P-NEW parameters to be here. ; It should consist of a class 0, followed by the address word zero as, ; without any protected RAM, the NEW command is no more sophisticated than ; a reset. ; However, there just isn't room. All 4096 bytes of the ROM have been ; put to good use so the required entry, three zero bytes, is embedded ; in the next routine, adding a harmless NOP to make up the three zero bytes. ; Aye, and you try telling young people of today that. And they won't ; believe you. ; ------------------------------ ;; MAIN-G L07BE: DEC HL LD ($4026),HL ; CH_ADD ;; P-NEW-1 L07C2: LD HL,$0000 ; prepare to clear error pointer. NOP ; Note. See comment above. LD ($4015),HL ; clear X_PTR LD HL,$4019 ; address FLAGX BIT 5,(HL) ; is INPUT mode set ? JR Z,L07D7 ; forward if not to E-LINE-NO ; else runtime input. RES 7,(HL) ; signal L mode. LD B,(HL) ; FLAGX to B for class routine. RST 18H ; NXT-CH-SP advances. JP L0889 ; jump forward to VAL-FETCH. ; ----------------------- ; THE 'E-LINE-NO' SECTION ; ----------------------- ;; E-LINE-NO L07D7: SET 7,(HL) ; update FLAGX - signal K mode RST 20H ; NEXT-CHAR CALL L0679 ; routine INT-TO-HL puts the BASIC Line Number ; into HL' JR C,L07E5 ; forward if a negative to insert error. ; else test against upper limit. EXX ; LD DE,$D8F0 ; value -9999 ADD HL,DE ; EXX ; ;; E-L-ERR L07E5: CALL C,L08AE ; routine INS-ERR if greater than 9999 ; ----------------------- ; THE 'LINE-SCAN' SECTION ; ----------------------- ;; LINE-SCAN L07E8: CALL L001A ; get the COMMAND CHARACTER. RES 7,(IY+$19) ; update FLAGX signal not K mode anymore. LD BC,$0000 ; this also sets B to zero for later. LD ($4022),BC ; default RESULT to ZERO ; for, say, RUN without an operand. CP $76 ; compare to just newline RET Z ; return if so. ; for example with a space for formatting. LD C,A ; transfer the character to C RST 20H ; NEXT_CHAR advances pointer LD A,C ; fetch back character to A. SUB $E6 ; subtract lowest command 'LIST' JR C,L07E5 ; back if not a command to E-L-ERR ; the loop will eventually find the newline ; and the original error point will not be ; altered. LD C,A ; place reduced character in C. LD HL,L0752 ; set HL to offset table ADD HL,BC ; add the one-byte offset LD C,(HL) ; fetch the offset from table ADD HL,BC ; add to form address of parameters. JR L080C ; forward to GET-PARAM ; ------------------------ ; THE 'MAIN SCANNING LOOP' ; ------------------------ ; entered at GET-PARAM after first instruction. ;; SCAN-LOOP L0809: LD HL,($401A) ; T_ADDR ; --> Entry Point. ;; GET-PARAM L080C: LD A,(HL) ; get parameter from syntax table. INC HL ; point to next one. LD ($401A),HL ; initialize or update T_ADDR LD BC,$0809 ; pre-load the machine stack with the PUSH BC ; return address SCAN-LOOP above. LD C,A ; copy parameter entry to C for later. RLA ; test bit 7 JR C,L0826 ; forward to SEPARATOR if inverted. LD HL,L0836 ; base address of command class table. LD B,$00 ; prepare to index. ADD HL,BC ; add the command class 0 - 6 LD C,(HL) ; fetch the addressed byte to C ADD HL,BC ; compute starting address of routine. PUSH HL ; push the address on the machine stack. CALL L001A ; routine GET-CHAR advances character position ; and resets the zero flag - see later. RET ; >> an indirect jump to the COMMAND CLASS ; routine. ; Note. HL addresses the next non-space ; character e.g. the variable in LET I = 1 ; the non-space character is in A ; ---------------------- ; THE 'SEPARATOR' BRANCH ; ---------------------- ; branch to here if the parameter has bit seven set. ;; SEPARATOR L0826: CALL L001A ; get character in A CP $D5 ; compare to the token 'THEN' JR NZ,L0831 ; forward if another character to SEP-1. SET 7,(IY+$19) ; else update FLAGX back to K mode ;; SEP-1 L0831: CP C ; compare with expected token/character JR NZ,L08AE ; forward if no match to set X-PTR ; using INS-ERR RST 20H ; else step past a correct character. RET ; return >> ; (to SCAN-LOOP) ; ------------------------- ; THE 'COMMAND CLASS' TABLE ; ------------------------- ;; TAB-CLASS L0836: DEFB L0855 - $ ; $1F offset to class-0 $0855 DEFB L086A - $ ; $33 offset to class-1 $086A DEFB L0885 - $ ; $4D offset to class-2 $0885 DEFB L0850 - $ ; $17 offset to class-3 $0850 DEFB L089E - $ ; $64 offset to class-4 $089E DEFB L0856 - $ ; $1B offset to class-5 $0856 DEFB L08A8 - $ ; $6C offset to class-6 $08A8 ; -------------------------- ; THE 'CHECK END' SUBROUTINE ; -------------------------- ;; CHECK-END L083D: BIT 7,(IY+$01) ; check FLAGS - checking syntax ? RET NZ ; return if running program. POP BC ; else drop the return address. ;; CH-END-2 L0843: LD A,(HL) ; fetch character from CH_ADD address ;; CH-END-3 L0844: CP $76 ; compare to carriage return. CALL NZ,L08AE ; routine INS-ERR if not disturbing the ; accumulator. ;; SEE-BELOW L0849: LD A,(HL) ; reload character again. ; and continue... ; ------------------------- ; THE 'REM' COMMAND ROUTINE ; ------------------------- ; The REM command compares each character until a newline is encountered. ; However this is a class 5 routine so the initial accumulator value will ; be zero (from the BC test) and not the character following REM. ; A line consisting of a single REM will have the newline skipped and if no ; $76 is encountered in the binary line number then the following line will ; be skipped also as in ; 10 REM ; 20 PRINT "THIS IS NOT HERE" ; The command address should be that of the previous instruction L0849 as the ; accumulator has been disturbed. ;; REM L084A: CP $76 ; compare with newline. RET Z ; return with newline. RST 20H ; NEXT-CHAR JR L084A ; loop back to REM until newline found. ; ----------------------------------- ; THE 'COMMAND CLASSES - 00, 03 & 05' ; ----------------------------------- ; these three commands always terminate a sequence of parameters and ; are followed by the address of a routine. ;; CLASS-03 L0850: CP $76 ; check for carriage return CALL NZ,L08A8 ; else look for optional number using CLASS-06 ; e.g. RUN & RUN 100 ; return and continue through other two classes. ;; CLASS-00 L0855: CP A ; set the zero flag to invoke CHECK-END later. ; this class has no operands e.g. CONTINUE. ;; CLASS-05 L0856: POP BC ; drop the looping address - last in sequence. CALL Z,L083D ; routine CHECK-END if zero flag set. ; (classes 03 and 00) EX DE,HL ; save HL in DE (original CH_ADD) LD HL,($401A) ; fetch table address from T_ADDR LD C,(HL) ; low byte to C INC HL ; LD B,(HL) ; high byte to B EX DE,HL ; bring back the original character address ;; JUMP-BC L0862: PUSH BC ; push routine address on machine stack LD BC,($4022) ; load value of last expression from RESULT LD A,B ; test the value OR C ; for zero. RET ; jump to the command routine. ; with HL pointing at original CH_ADD ; DE pointing to T_ADDR ; BC holding parameter ; --------------------------------------- ; THE 'COMMAND CLASSES - 01, 02, 04 & 06' ; --------------------------------------- ; the first routine is for LET or INPUT. ;; CLASS-01 L086A: CALL L0D14 ; routine ALPHA tests the character. JR NC,L08AE ; forward to INS-ERR if character not A-Z. BIT 7,(IY+$01) ; test FLAGS - the syntax bit. JP Z,L0AAD ; jump forward to LOOK-VARS if checking syntax. ; continue in runtime LD ($4020),HL ; save address of destination variable ; in BASIC line in DEST system variable. RES 7,(IY+$01) ; signal to FLAGS that syntax is being checked. CALL L0AAD ; routine LOOK-VARS. SET 7,(IY+$01) ; set FLAGS back to 'running program' status. RET ; return (to SCAN-LOOP). ; ------------------------------ ; used only for LET - an expression of the correct type must be present. ;; CLASS-02 L0885: POP BC ; drop the looping address as CLASS-02 is the ; last in a sequence of parameters. It is ; relevant only to the LET command. LD B,(IY+$01) ; load B with value of FLAGS. ; (runtime input joins here with FLAGX in B instead of FLAGS) ; --------------------------- ; THE 'FETCH A VALUE' SECTION ; --------------------------- ;; VAL-FETCH L0889: PUSH BC ; preserve value of FLAGS (or FLAGX if input) RST 28H ; SCAN-CALC evaluates the expression ; to be assigned setting the result type flag. POP DE ; restore the pre-evaluation copy of the ; flag register to D. LD BC,L0C3D ; the address of the LET routine is pushed on ; the machine stack. LD A,($4001) ; fetch the post-evaluation FLAGS to A BIT 7,A ; test the syntax bit. JR NZ,L0862 ; back in runtime to JUMP-BC and then LET ; if checking syntax. XOR D ; exclusive or the two flags AND $40 ; AND 01000000 to isolate the type bit. CALL NZ,L08AE ; routine INS-ERR inserts the error position ; when they are not the same type. JR L0843 ; back to CH-END-2 to consider lesser errors ; and advance to end of line. ; ------------------------------ ; FOR, NEXT, DIM - HL points to variable in BASIC line, A holds the character ;; CLASS-04 L089E: LD ($4020),HL ; set system variable DEST from HL. CALL L0D14 ; routine ALPHA checks the character. JR NC,L08AE ; forward to INS-ERR if not A-Z. RST 18H ; NXT-CH-SP advances character address. RET ; return to SCAN-LOOP >> ; ------------------------------ ; a mandatory INTEGER expression must follow. e.g. GO TO 100 ;; CLASS-06 L08A8: RST 28H ; SCAN-CALC evaluates expression. BIT 6,(IY+$01) ; test FLAGS - numeric result ? RET NZ ; return if numeric. ; ----------------------------- ; THE 'INSERT ERROR' SUBROUTINE ; ----------------------------- ;; INS-ERR L08AE: LD A,($4015) ; check that error pointer X_PTR OR (IY+$16) ; contains zero. RET NZ ; return if there is already an error LD ($4015),HL ; else place error address at X-PTR RET ; return. ; ------------------------ ; THE 'IF' COMMAND ROUTINE ; ------------------------ ;; IF L08B9: JR NZ,L08C1 ; if expression is TRUE forward to IF-1 BIT 7,(IY+$01) ; test FLAGS - checking syntax ? JR NZ,L084A ; back to REM to ignore rest of the line ; in runtime. ; - else continue and check the syntax of the rest of the line. ;; IF-1 L08C1: JP L07E8 ; jump back to LINE-SCAN to execute what ; follows the 'THEN' ; ------------------------- ; THE 'FOR' COMMAND ROUTINE ; ------------------------- ; for example, FOR X = 1 TO 10 ; There is no step or direction. ; The body of the loop is always entered at least once - even if the initial ; value exceeds the limit. ; The ZX81 and ZX Spectrum adhered more closely to the ANS X3.60 1978 BASIC ; standard. ;; FOR L08C4: PUSH BC ; save the start value. CALL L08A8 ; routine CLASS-06 evaluates LIMIT ; expression. POP BC ; start value back to BC CALL L083D ; routine CHECK-END quits if checking ; syntax >> LD HL,($4022) ; fetch limit from RESULT PUSH HL ; save limit CALL L0C3D ; routine LET POP BC ; restore limit to BC BIT 7,(IY+$00) ; examine ERR_NR RET Z ; return if not $FF >> PUSH BC ; push the limit value. DEC HL ; point to letter. BIT 7,(HL) ; test bit 7 - is it a FOR-NEXT variable. SET 7,(HL) ; set bit 7 as it is going to be. INC HL ; point to end of value INC HL JR NZ,L08EA ; skip forward if it is a proper ; for/next variable to FOR-2 LD BC,$0004 ; else an extra 4 bytes are needed. INC HL ; point to start of new space. CALL L05D5 ; routine MAKE-ROOM creates it. ; HL - first, DE- last ;; FOR-2 L08EA: INC HL ; address limit location POP DE ; retrieve limit value to DE. LD (HL),E ; insert low byte of limit. INC HL LD (HL),D ; and then the high byte INC HL ; point to the looping line cell. LD DE,($4002) ; load DE with the current line from PPC INC DE ; increment as iteration will start from the ; next line at least. LD (HL),E ; insert low byte of line number. INC HL LD (HL),D ; insert high byte of line number. RET ; return. ; -------------------------- ; THE 'NEXT' COMMAND ROUTINE ; -------------------------- ;; NEXT L08F9: LD HL,($4020) ; fetch address of variable in BASIC from DEST. CALL L0B3B ; routine LV-FIND finds the equivalent in the ; variables area and returns the value in HL. BIT 7,(IY+$00) ; test ERR_NR RET Z ; return with error. ; will be 02 - variable not found. ; continue if LV-FIND found the variable - HL contains the value, DE points ; to the high byte of value location. EX DE,HL ; value to DE, address to HL DEC HL ; point to low byte DEC HL ; point to the variable letter. BIT 7,(HL) ; - should have letter mask 111xxxxx JR Z,L0921 ; forward to ERROR-01 if not initialized by FOR. ; - NEXT without FOR. INC DE ; increment the integer value ; no step or direction possible. INC HL ; address first location LD (HL),E ; store low byte of value. INC HL ; next LD (HL),D ; store high byte of value. INC HL ; LD C,(HL) ; pick up limit low INC HL ; LD B,(HL) ; and limit high. PUSH BC ; save limit. EX (SP),HL ; limit to HL, pointer to stack. CALL L0DCD ; routine no-less compares HL DE ; setting carry if HL is less. POP HL ; retrieve the pointer from the stack. RET C ; return if no more iterations possible >> INC HL ; else address next location. LD C,(HL) ; pick up low byte of line number INC HL ; address next LD B,(HL) ; pick up high byte of looping line. JR L0934 ; jump to GOTO to perform another ; iteration ; --- ;; ERROR-01 L0921: RST 08H ; ERROR restart DEFB $00 ; NEXT without FOR ; ------------------------------- ; THE 'RANDOMISE' COMMAND ROUTINE ; ------------------------------- ; This command sets the seed to the supplied integer -32767 to 32767. ; In the absence of a parameter the FRAMES counter, related to the time ; the computer has been switched on, is used. ;; RANDOMISE L0923: JR NZ,L0929 ; forward to RAND-1 if parameter is ; not zero. LD BC,($401E) ; else use value of system variable FRAMES. ;; RAND-1 L0929: LD ($401C),BC ; insert value in system variable SEED. RET ; return. ; -------------------------- ; THE 'STOP' COMMAND ROUTINE ; -------------------------- ;; STOP ;; ERROR-9 L092E: RST 08H ; ERROR restart DEFB $08 ; - STOP statement executed. ; ------------------------------ ; THE 'CONTINUE' COMMAND ROUTINE ; ------------------------------ ;; CONTINUE L0930: LD BC,($4017) ; fetch continuing line number from OLDPPC ; and continue into GOTO routine. ; --------------------------- ; THE 'GO TO' COMMAND ROUTINE ; --------------------------- ;; GOTO L0934: LD ($4002),BC ; set PPC to supplied line number. SET 3,(IY+$01) ; update FLAGS - use K cursor. RET ; return. ; ------------------------- ; THE 'RUN' COMMAND ROUTINE ; ------------------------- ; The RUN command may have an optional line number that will be passed to ; the GOTO routine before erasing any variables and executing the line ; (or first line after zero). ;; RUN L093D: CALL L0934 ; routine GOTO sets up any supplied line number. JP L065B ; exit via CLEAR to erase variables. ; ---------------------------- ; THE 'GO SUB' COMMAND ROUTINE ; ---------------------------- ;; GOSUB L0943: LD HL,($4002) ; fetch current line from PPC INC HL ; increment the line number EX (SP),HL ; place on machine stack ; PUSH HL ; push what was on the stack back up there. CALL L0934 ; routine GOTO sets up a branch to the line ; number. LD BC,$0006 ; and exit by a six-byte memory check. ; -------------------------- ; THE 'TEST ROOM' SUBROUTINE ; -------------------------- ; The ZX80 dates from the days when RAM chips cost a fortune and it came with ; only 1K of RAM, 1024 bytes. ; The screen could show 768 characters and to economize it is dynamic and ; initialized to a single newline ($76) by CLS. The TEST-ROOM routine has to ; allow for enough newlines to expand down to the bottom line and a few extra ; for the report codes "0/9999". ; The second entry point is from PRINT-A and the character is similarly ; in H and the line number in L. ;; TEST-ROOM L094F: LD HL,($4010) ; fetch DF_END last location before ; spare memory. ADD HL,BC ; add the supplied overhead. EX DE,HL ; save the result in DE. LD HL,($4025) ; SPOSN-Y to L gives 24 - number ; of screen lines used so far. LD H,A ; preserve the accumulator in H ;; TEST-RM-2 L0958: LD A,$13 ; load A with 19 ADD A,L ; add to L to give the number of bytes ; required to fill rest of screen with ; newlines - plus a bit extra. LD L,A ; put result in L. LD A,H ; restore the accumulator. LD H,$00 ; set H to zero. ADD HL,DE ; add this extra screen allowance ; to the previous result. SBC HL,SP ; subtract the stack pointer. RET C ; return if the stack pointer is ; above the estimate. All is well. ; ;; ERROR-4 L0963: RST 08H ; ERROR restart DEFB $03 ; No room ; ---------------------------- ; THE 'RETURN' COMMAND ROUTINE ; ---------------------------- ; As with all commands, there is only one value on the machine stack during ; command execution. This is the return address. ; Above the machine stack is the gosub stack that contains a line number ; (only one statement per line). ;; RETURN L0965: POP HL ; drop the return address clearing the stack. POP BC ; drop a line number off the gosub stack. PUSH HL ; restore the machine stack. LD A,B ; test high byte of line number. CP $3F ; against the gosub stack end-marker. JR NZ,L0934 ; back to GOTO if a valid line number. POP HL ; else collapse the machine stack. PUSH BC ; push the end-marker. PUSH HL ; restore the machine stack. ;; ERROR-07 RST 08H ; ERROR restart DEFB $06 ; RETURN with no corresponding GO SUB. ; --------------------------- ; THE 'PRINT' COMMAND ROUTINE ; --------------------------- ;; PRINT L0972: LD A,(HL) ; fetch the character CP $76 ; compare to NEWLINE JP Z,L071B ; back to PRINT-CR if so. ;; PR-POSN-1 L0978: SUB $D8 ; subtract ',' ; (';' gives -1 and carry set) ADC A,$00 ; convert the two separators to zero. JR Z,L0991 ; forward to PR-POSN-2 with ';' and ',' RST 28H ; else SCAN-CALC evaluates expression. CALL L06F1 ; routine PRINT-ITEM prints it. CALL L001A ; routine GET-CHAR gets following character. SUB $D8 ; compare with ',' and test for ADC A,$00 ; terminating separators. JR Z,L0991 ; forward to PR-POSN-2 with ';' and ',' CALL L083D ; routine CHECK-END errors with anything else. JP L071B ; jump to PRINT-CR for carriage return. ; --- ;; PR-POSN-2 L0991: CALL NC,L0727 ; routine PO-FILL if comma control. RST 20H ; NEXT-CHAR CP $76 ; compare to NEWLINE RET Z ; return if so leaving print position ; unchanged. JR L0978 ; else loop back to PR-POSN-1 to consider ; more sequences of positional ; controls and print items. ; --------------------------- ; THE 'INPUT' COMMAND ROUTINE ; --------------------------- ; INPUT must be used from a running program. It is not available as a ; direct command. ;; INPUT L099A: BIT 7,(IY+$03) ; test PPC_hi - will be -2 if a direct command JR NZ,L09CF ; forward if so, to ERROR-08 POP HL ; discard return address - L0447 LD HL,$4019 ; point to FLAGX SET 5,(HL) ; signal input RES 6,(HL) ; reset so as not to affect combine LD A,($4001) ; fetch FLAGS to A AND $40 ; isolate bit 6 - the result type LD BC,$0002 ; allow two locations for numeric. JR NZ,L09B4 ; skip forward to IN-PR-1 if numeric. LD C,$04 ; allow two extra spaces for quotes. ;; IN-PR-1 L09B4: OR (HL) ; combine FLAG bit with FLAGX. LD (HL),A ; and place result in FLAGS. RST 30H ; BC-SPACES creates 2/4 locations. RET NC ; return with problems. LD (HL),$76 ; insert a newline at end. LD A,C ; now test C - 2 (num) 4 (str). RRCA ; 1 2 RRCA ; carry 1 JR C,L09C2 ; skip forward with numeric to IN-PR-3 LD (DE),A ; insert initial quote (chr$ 1) at DE DEC HL ; decrease HL pointer LD (HL),A ; insert closing quote. ;; IN-PR-3 L09C2: DEC HL ; decrease pointer LD (HL),$B0 ; insert cursor inverse 'K' LD A,($4025) ; SPOSN-Y INC A ; allow a blank line LD ($4012),A ; set DF-SZ JP L02F7 ; jump back to ED-COPY ; --- ;; ERROR-08 L09CF: RST 08H ; ERROR restart DEFB $07 ; INPUT can only be used in a program. ; -------------------------- ; THE 'POKE' COMMAND ROUTINE ; -------------------------- ;; POKE L09D1: PUSH BC ; save result of first expression. RST 28H ; use SCAN-CALC to evaluate expression ; after the comma. POP DE ; restore destination address. CALL L083D ; routine CHECK-END LD A,($4022) ; RESULT BIT 7,(IY+$00) ; ERR_NR RET Z ; return if error LD (DE),A ; load memory location with A RET ; return ; ---------------------- ; THE 'SCANNING' ROUTINE ; ---------------------- ; The scanning routine is a continuation of RST 28. ; The B register has been set to zero as a starting priority. ; The HL register contains the character address CH_ADD. ; The addressed character is in A. ;; SCANNING L09E1: LD C,B ; make BC zero - the starting priority ; marker. PUSH BC ; save on machine stack. ;; S-LOOP-1 L09E3: CALL L0D18 ; routine ALPHANUM JR C,L0A24 ; forward if a variable or digit. to S-VAR-NUM ; now consider negate (-) and perform '$0000 - value' if so. LD BC,$0900 ; prepare priority $09, operation 'subtract' LD D,C ; set DE to $0000 for value to be stacked. LD E,C ; SUB $DC ; subtract the character '-' JR Z,L0A17 ; forward with unary minus to S-PUSH-PO ; now consider 'not' and perform $FFFF - value if so. DEC DE ; set DE to $FFFF for value to be stacked. LD B,$04 ; prepare priority 4, operation still 'subtract' INC A ; test for 'NOT' ? JR Z,L0A17 ; forward with NOT to S-PUSH-PO ; now consider an opening bracket. INC A ; test the character. JR Z,L0A1C ; forward with '(' to S-BRACKET ; to evaluate the sub-expression recursively ; using SCANNING. CP $27 ; commencing quote ? JR NZ,L0A0E ; forward to S-ABORT if not, as all valid ; possibilities have been exhausted. ; continue to evaluate a string. RES 6,(IY+$01) ; signal string result to FLAGS. INC HL ; step past the opening quote. LD ($4022),HL ; store the string pointer in ; system variable RESULT. ;; S-Q-CHAR L0A06: RST 18H ; NXT-CH-SP DEC A ; test for the string terminator. JR Z,L0A21 ; forward to S-CONT if found. >> CP $75 ; [ EDIT ] SHIFT-ENTER JR NZ,L0A06 ; loop back to S-Q-CHAR till terminator found. ; --- ; the branch was here when something unexpected appeared in the expression ; or, if from above, in the string. ;; S-ABORT L0A0E: CALL L08AE ; routine INS-ERR marks the spot. EXX ; LD BC,$0000 ; this forces the zero priority marker down ; from the stack. ; Note. just setting B to zero should do. JR L0A4C ; forward to S-LOOP to balance and exit ; --- ; the ZX80 juggles with expression components using just the machine stack ; pushing first the value and then the priority/operator beneath. ; As with all ZX computers, provided there is enough memory, an expression of ; unlimited complexity can be evaluated. ;; S-PUSH-PO L0A17: PUSH DE ; push the value ($0000 if '-', $FFFF if 'NOT') PUSH BC ; then push the priority and operator. ;; SCAN-LOOP L0A19: RST 20H ; NEXT-CHAR advances the character address. JR L09E3 ; back to S-LOOP-1 ; --- ;; S-BRACKET L0A1C: CALL L0049 ; routine BRACKET evaluates expression ; inside the brackets checking for ; terminator using SCANNING ; recursively. JR L0A37 ; forward to S-OPERTR ; --- ; the branch was here when the end of a string had been found. ;; S-CONT L0A21: RST 18H ; NXT-CH-SP JR L0A37 ; forward to S-OPERTR to consider comparisons ; --- ;; S-VAR-NUM L0A24: CP $26 ; compare to 'A' JR C,L0A2D ; forward if numeric to S-DIGIT ; present character is alpha CALL L0AAD ; routine LOOK-VARS JR L0A37 ; forward to S-OPERTR ; --- ;; S-DIGIT L0A2D: CALL L0679 ; routine INT-TO-HL CALL C,L08AE ; routine INS-ERR with overflow. SET 6,(IY+$01) ; signal numeric result in FLAGS ;; S-OPERTR L0A37: CALL L001A ; routine get-char EXX LD BC,$0000 ; prepare zero priority in case not an operator ; in which case at end of expression SUB $DC ; reduce by '-' JR C,L0A4C ; forward if less than an operator to S-LOOP CP $0A ; compare to ten. JR NC,L0A4C ; forward if higher than nine to S-LOOP ; leaves ten operators -, +, *, /, AND, OR, **, =, >, <. LD C,A ; transfer operation to C, register B is zero. LD HL,L0AA3 ; address table of priorities. ADD HL,BC ; index into table. LD B,(HL) ; pick up the priority. ;; S-LOOP L0A4C: POP DE ; pop the previous priority/operation LD A,D ; priority to A CP B ; compare with current priority B JR C,L0A88 ; forward to S-TIGHTER if current priority is ; higher ; else this is the correct place in the expression to perform this operation. AND A ; first test for zero priority marker EXX ; RET Z ; return if so, HL is result. >>>>> EXX ; BIT 7,(IY+$01) ; FLAGS JR Z,L0A6F ; forward if checking syntax to S-SYNTEST ; but in runtime the operation is performed. LD D,$00 ; prepare to index. LD HL,L0D1F ; address the table of operators and addresses. ADD HL,DE ; index twice using the operation code. ADD HL,DE ; as there are two bytes per entry. LD E,(HL) ; pick up low byte of address. INC HL ; next location. LD D,(HL) ; get high byte of address. LD HL,L0A7F ; the return address S-INS-VAL EX (SP),HL ; goes to the stack and argument to HL PUSH DE ; now push the address of the routine. LD DE,($4022) ; pick up last value from RESULT RET ; and make an indirect jump to ; the routine. >>>>>>>> ; ------------------------------ ;; S-SYNTEST L0A6F: LD A,E ; get the last operation code CP $0A ; compare to ten - sets carry if numeric RRA ; carry to bit 7 RRA ; carry to bit 6 XOR (IY+$01) ; exclusive or with FLAGS AND $40 ; isolate bit 6 - the result type. EXX ; CALL NZ,L08AE ; routine INS-ERR if not of same type. EXX ; POP HL ; fetch the last value from machine stack ; >>>>>>>> ; Note. this is also the return address from mathematical and string ; comparisons, see above, in which case HL will contain the result and BC ; the priority/operation. ;; S-INS-VAL L0A7F: LD ($4022),HL ; place value in system variable RESULT SET 6,(IY+$01) ; signal numeric result to FLAGS JR L0A4C ; back to S-LOOP ; --- ;; S-TIGHTER L0A88: PUSH DE ; push lower priority LD A,C ; fetch operator BIT 6,(IY+$01) ; test FLAGS JR NZ,L0A9A ; forward if numeric to S-NEXT. ADD A,$03 ; augment nos-eql to strs-eql etc. LD C,A ; and put back in C CP $0A ; compare to ten - start of string comparisons EXX ; CALL C,L08AE ; routine INS-ERR if lower ; a$ * b$ is invalid but so too ; is a$ + b$ (no string concatenation) EXX ; ;; S-NEXT L0A9A: LD HL,($4022) ; fetch RESULT to HL PUSH HL ; push intermediate result PUSH BC ; and then priority/operator EXX ; JP L0A19 ; jump back to SCAN-LOOP ; ------------------------- ; THE 'TABLE OF PRIORITIES' ; ------------------------- ; Table of mathematical priorities that dictate, in the absence of brackets, ; the order in which operations are performed. ; unary minus (priority $09) and NOT (priority $04) are handled directly. ;; TAB-PRIO L0AA3: DEFB $06 ; $00 subtract DEFB $06 ; $01 addition DEFB $08 ; $02 multiply DEFB $07 ; $03 division DEFB $03 ; $04 and DEFB $02 ; $05 or DEFB $0A ; $06 to-power DEFB $05 ; $07 nos-eql DEFB $05 ; $08 no-grtr DEFB $05 ; $09 no-less ; -------------------------- ; THE 'LOOK-VARS' SUBROUTINE ; -------------------------- ;; LOOK-VARS L0AAD: PUSH HL ; * push pointer to first letter LD HL,$4001 ; address FLAGS RES 5,(HL) ; update FLAGS - signal not a function yet. ; but no use is made of this flag bit. SET 6,(HL) ; update FLAGS - presume a numeric result. RST 18H ; NXT-CH-SP CP $0D ; compare to '$' ? JP Z,L0B30 ;; JUMP forward with match to STRING CP $DA ; compare to '(' ? JP Z,L0B2B ;; JUMP forward with match to ARRAY ; that leaves three types of integer plus functions. ;; V-CHAR L0AC0: CALL L0D18 ; routine ALPHANUM JR NC,L0AC8 ; forward when not alphanumeric to FUNC-LOOP. RST 18H ; fetch NXT-CH-SP. JR L0AC0 ; loop back to V-CHAR for more. ; --- ;; FUNC-LOOP L0AC8: CP $DA ; compare to '(' ? JR Z,L0AD6 ; forward with a match to FUNC-SRCH CP $0D ; compare to '$' ? JP NZ,L0B35 ;; JUMP forward if not to V-SYN ; but if this is a string function such as CHR$ then the bracket must follow. RST 18H ; NXT-CH-SP CP $DA ; compare to '(' ? JR NZ,L0B27 ; forward if not to FUNC-ERR. ; This has the correct format for a function and an exact match must now be ; made to one of the entries in the functions table. ;; FUNC-SRCH L0AD6: LD DE,L0BC0 - 1 ; point to location before TAB-FUNC ;; FUNC-LOOP L0AD9: POP HL ; pop pointer to first character in command PUSH HL ; and push again. ;; FUNC-CHAR L0ADB: LD C,(HL) ; fetch command character to C. CALL L0055 ; routine CH-ADD-LP advances CH-ADD ; to next non-space position. INC DE ; increment position in table LD A,(DE) ; fetch table character to A. CP C ; compare with one in command. JR Z,L0ADB ; loop back with match to FUNC-CHAR ; e.g. PEEK AND $3F ; cancel any inversion. CP C ; and compare again JR NZ,L0AEE ; skip if no match to FUNC-NEXT. LD A,$DA ; load with '(' CP (HL) ; compare to next valid character JR Z,L0AF9 ; forward with success to FUNC-MTCH. ;; FUNC-NEXT L0AEE: LD A,(DE) ; fetch next character from table. AND A ; test for zero end-marker. JR Z,L0B27 ; forward if at end of table to FUNC-ERR. INC DE ; else increment pointer. RLA ; test for inverted bit. JR NC,L0AEE ; loop back to FUNC-NEXT ; until new token found. INC DE ; increment pointer. ; to skip address in table. JR L0AD9 ; loop back to FUNC-LOOP ; which begins by skipping the ; remaining address byte. ; --- ; A function such as PEEK has been found with the necessary opening bracket. ;; FUNC-MTCH L0AF9: PUSH DE ; save pointer to address within ; table. CALL L0049 ; routine BRACKET evaluates an ; expression within brackets in command. ; result in HL POP DE ; retrieve table address pointer. EX (SP),HL ; result to stack, discarding command ; character pointer. LD HL,$4001 ; load with address FLAGS LD A,(DE) ; fetch the last inverted character. XOR (HL) ; XOR with FLAGS AND $40 ; isolate bit 6 - result type. JR NZ,L0B27 ; to FUNC-ERR to insert an error with ; an argument type mismatch. SET 5,(HL) ; update FLAGS signal a function has been found ; but no use is made of this ????? SET 6,(HL) ; default the result type to be numeric. LD A,(DE) ; fetch last character AND $3F ; lose the indicator bits. CP $0D ; is character '$' ? ; i.e. CHR$, STR$ or TL$. JR NZ,L0B15 ; forward with numeric function results ; to FUNC-SYN. RES 6,(HL) ; else set FLAGS to indicate a string ; result is expected. ;; FUNC-SYN L0B15: BIT 7,(HL) ; test FLAGS checking syntax? POP HL ; restore RESULT of expression in brackets. RET Z ; return if checking syntax. >> LD HL,L0BBA ; else the routine INS-RSLT PUSH HL ; is pushed on the machine stack EX DE,HL ; HL now points to table entry. INC HL ; point to address low byte. LD E,(HL) ; pick up the low byte. INC HL LD D,(HL) ; pick up the high byte. PUSH DE ; push routine address on stack. LD HL,($4022) ; load HL with argument from RESULT ; either integer or string pointer. RET ; indirect jump to routine and then ; to INS-RSLT . ; --- ;; FUNC-ERR L0B27: POP HL ; balance stack. JP L08AE ; jump back to INS-ERR ; ------------------------------ ;; ARRAY L0B2B: CALL L0049 ; routine BRACKET evaluates expression JR L0B35 ; skip to V-SYN ; --- ;; STRING L0B30: RES 6,(IY+$01) ; FLAGS signal string result. RST 18H ; NXT-CH-SP ;; V-SYN L0B35: POP HL ; * restore pointer to first letter BIT 7,(IY+$01) ; check FLAGS RET Z ; return if checking syntax ; but continue in run-time ; also called from NEXT and LET ; HL points to first letter of variable in the command. ;; LV-FIND L0B3B: LD C,(HL) ; C first character INC HL LD A,(HL) ; A second character PUSH HL ; save pointer to character 2 CP $DA ; is second character '(' ? JR NZ,L0B5C ; forward if not to LV-ENCODE with strings and ; simple numeric variables. ; an array PUSH BC ; save BC on stack LD BC,($4026) ; fetch character address CH_ADD PUSH BC ; and save that on stack as well. CALL L0025 ; routine EVAL-EXPR evaluates the ; expression after the current '(' ; disturbing CH_ADD POP HL ; restore original value of CH_ADD LD ($4026),HL ; and backdate CH_ADD system variable. POP BC ; restore the letter in BC. LD HL,$4000 ; address system variable ERR_NR BIT 7,(HL) ; test if $FF has been disturbed by eval_expr. JR NZ,L0B6B ; forward if not to V-RUN. LD (HL),$02 ; else insert the code for subscript error POP HL ; balance the stack RET ; return with error set. >> ; --- ; encode the variable type into bits 5-7 of the letter. ;; LV-ENCODE L0B5C: RES 5,C ; presume type string CP $0D ; is second character '$' ? JR Z,L0B6B ; forward if so to V-RUN SET 6,C ; presume long-named numeric. CALL L0D18 ; routine ALPHANUM test second character. JR C,L0B6B ; forward if so to V-RUN SET 5,C ; else mark as simple numeric or for/next ;; V-RUN L0B6B: LD HL,($4008) ; point HL to the first variable from VARS. ;; V-EACH L0B6E: LD A,(HL) ; fetch letter/marker AND $7F ; reset bit 7 to allow simple numeric variables ; to match against FOR-NEXT variables. JP Z,L0CD0 ; if character was $80 then forward to ERROR-02 ; Variable not found. CP C ; else compare to first letter in command JR NZ,L0B93 ; forward if no match to V-NEXT RLA ; rotate A to left and then ADD A,A ; double to test bits 5 and 6. JP M,L0BA4 ; forward to STK-VAR with ; all single letter numeric variables ; including for/next and arrays. JR NC,L0BB8 ; forward to STR-RSLT with string. ; that leaves long-named variables (mask 010xxxxx) ; that have to be matched in full. POP DE ; take a copy of pointer. PUSH DE ; to 2nd character in BASIC area. PUSH HL ; save 1st letter pointer in vars area. ;; V-MATCHES L0B81: INC HL ; point to next vars character. LD A,(DE) ; fetch each BASIC char in turn. INC DE ; advance BASIC pointer CP (HL) ; compare to character in variable JR Z,L0B81 ; back if the same to V-MATCHES OR $80 ; try a match on inverted character. CP (HL) ; compare to variable JR NZ,L0B92 ; forward to V-GET-PTR without full ; match. LD A,(DE) ; check that the end of name in BASIC ; has been reached. CALL L0D18 ; routine ALPHANUM checks that no ; more letters follow. JR NC,L0B9B ; forward to V-FOUND-1 with a full ; match on an inverted long name. ; else continue the search ;; V-GET-PTR L0B92: POP HL ; fetch the pointer. ;; V-NEXT L0B93: PUSH BC ; save B and C CALL L0624 ; routine NEXT-ONE points DE at next ; variable EX DE,HL ; switch pointers. POP BC ; retrieve B and C. JR L0B6E ; back for another search to V-EACH. ; --- ;; V-FOUND-1 L0B9B: POP DE ; drop saved var pointer ;; V-FOUND-2 L0B9C: POP DE ; drop pointer to second character ;; V-FOUND-3 L0B9D: INC HL ; advance to value. LD E,(HL) ; fetch low byte to E INC HL ; LD D,(HL) ; fetch high byte to D. EX DE,HL ; value to HL JR L0BBA ; forward to INS-RSLT ; --- ; simple 011xxxxx, array 101xxxxx, for/next 111xxxxx ;; STK-VAR L0BA4: JR C,L0B9C ; back to V-FOUND-2 above with simple ; and FOR/NEXT variables. ;; SV-ARRAYS EX (SP),HL ; save pointer to letter on stack discarding ; the second letter pointer LD HL,($4022) ; fetch argument within brackets from RESULT RLC H ; test the high byte. POP DE ; retrieve pointer to letter JR NZ,L0BBE ; forward to ERROR-03 subscript error ; if subscript > 255 INC DE ; point to dimensions value - 1 byte LD A,(DE) ; fetch the max subscription CP L ; compare to low byte of argument. JR C,L0BBE ; forward if higher than max subscription ; to ERROR-03. ADD HL,HL ; double the subscript 0 - 510 ADD HL,DE ; add to variable pointer ; now point to location before required cell. ; if the first element is 0 then still pointing ; at the max subscription byte. JR L0B9D ; back to V-FOUND-3 above. ; --- ; string type mask 100xxxxx ;; STR-RSLT L0BB8: POP DE ; drop pointer to var. INC HL ; advance to first character of string. ;; INS-RSLT L0BBA: LD ($4022),HL ; insert value/pointer into RESULT RET ; return. ; --- ;; ERROR-03 L0BBE: RST 08H ; ERROR restart DEFB $02 ; subscript error ; ------------------------------ ; THE 'INTEGRAL FUNCTIONS TABLE' ; ------------------------------ ; Table of functions to be parsed and addresses. ; Parsed by LOOK-VARS. ; Inversion is with $80 (string argument) ; and with $CO (numeric argument). ; The TL$, "Truncate Left string", of "CABBAGE" is "ABBAGE". ;; TAB-FUNC L0BC0: DEFB $35,$2A,$2A,$F0 ; PEEK (+$C0) DEFW L0C24 ; $0C24 DEFB $28,$2D,$37,$CD ; CHR$ (+$C0) DEFW L0C28 ; $0C28 DEFB $28,$34,$29,$AA ; CODE (+$80) DEFW L0C24 ; $0C24 DEFB $37,$33,$E9 ; RND (+$C0) DEFW L0BED ; $OBED DEFB $39,$31,$8D ; TL$ (+$80) DEFW L0C38 ; $0C38 DEFB $3A,$38,$F7 ; USR (+$C0) DEFW L06F0 ; $06F0 DEFB $38,$39,$37,$CD ; STR$ (+$C0) DEFW L0C10 ; $0C10 DEFB $26,$27,$F8 ; ABS (+$C0) DEFW L0DF2 ; $0DF2 DEFB $00 ; zero end-marker ; ------------------ ; THE 'RND' FUNCTION ; ------------------ ; e.g. LET LOTTERYNUMBER = RND (49) produces a random number in the range ; 1 to 49. ; the routine has two stages - ; First the seed is fetched and manipulated in such a way that it cycles through ; every value between 0 and 65535 in a pseudo-random way before repeating the ; sequence. If the seed fetched is zero it is set to 65536-77. ; The multiplicand used is 77 and any overflow is subtracted from the ; register result. ;; RND L0BED: PUSH HL ; * save the integer parameter e.g. 49. LD HL,($401C) ; fetch the 'seed' from SEED. LD DE,$004D ; place 77 in DE LD A,H ; test the seed OR L ; for value zero JR Z,L0C03 ; forward if zero. CALL L0D55 ; routine MULT16 multiplies seed by 77 ; BC contains zero or overflow AND A ; clear carry flag. SBC HL,BC ; subtract any overflow from lower 16 bits JR NC,L0C05 ; forward if no carry to RND-3 INC HL ; increase seed value. JR L0C05 ; forward to RND-3 ; --- ;; RND-2 L0C03: SBC HL,DE ; form number $FFB3 if seed is zero. ;; RND-3 L0C05: LD ($401C),HL ; store new value of SEED ; now multiply the new seed by the argument to give result-1 in BC. POP DE ; * restore argument CALL L0D55 ; routine MULT16 multiplies HL by DE ; returning in BC, for the example, 0-48 LD H,B ; transfer BC LD L,C ; to HL - the result register. INC HL ; increment - make range start with 1. RET ; return ; ------------------- ; THE 'STR$' FUNCTION ; ------------------- ; the function produces a string comprising the characters that would appear ; if the numeric argument were printed. ; So seven characters e.g. "-10000" terminated by the null character ($01) ; is the maximum amount of characters required. ; Note. that for this reason the ZX80, unlike the ZX81 and ZX Spectrum, is able ; to have four tabstops across the 32 character screen. ;; str$ L0C10: EXX LD BC,$0007 ; 7 characters required at most. RST 30H ; routine BC-SPACES JR NC,L0C34 ; forward to NULL-STR if not enough ; memory. PUSH DE ; * save start of new space EXX ; switch in other set LD B,H ; transfer argument to BC LD C,L ; register. CALL L06A1 ; OUT-NUM-1 prints at this DE in WKG Space. EXX ; switch back LD A,$01 ; prepare the terminating '"' LD (DE),A ; and place at end of string. ;; POP-RET L0C22: POP HL ; * restore result pointer. RET ; return. ; ------------------------------- ; THE 'CODE' AND 'PEEK' FUNCTIONS ; ------------------------------- ; Two functions in one subroutine. ; CODE with HL pointing to start of string. ; and also, ; PEEK with HL pointing to a memory address. ; The return value is in HL. ;; CODE ;; PEEK L0C24: LD L,(HL) ; parameter is in HL. LD H,$00 ; RET ; return with result in HL. ; ------------------- ; THE 'CHR$' FUNCTION ; ------------------- ; this function returns the null-terminated single-character string that ; corresponds to the integer argument e.g. CHR$(38) returns "A". ;; chr$ L0C28: LD BC,$0002 ; two locations required. LD A,L ; character to A. RST 30H ; BC-SPACES creates two locations ; in WORKSPACE JR NC,L0C34 ; forward to NULL-STR if no room. ;; NULL-PTR L0C2F: LD (HL),$01 ; insert the '"' terminator at last new location DEC HL ; decrease the pointer. LD (HL),A ; insert the character. RET ; return with HL pointing to string. ; --- ;; NULL-STR L0C34: LD HL,L0C2F + 1 ; point to the null string at NULL-PTR + 1 ; in the above code. RET ; return. ; ------------------ ; THE 'TL$' FUNCTION ; ------------------ ; This limited string slicing function returns the tail of a string starting ; at the second character and the null string otherwise. ; It requires no string workspace. ;; tl$ L0C38: LD A,(HL) ; fetch first character of string DEC A ; decrement it. RET Z ; return if was CHR$ 1 - the null string. INC HL ; else increase the string pointer RET ; return with HL pointing at result. ; ----------------- ; THE 'LET' ROUTINE ; ----------------- ; This subroutine is called from the FOR command and the CLASS-02 routine ; to create the variable. ;; LET L0C3D: BIT 7,(IY+$00) ; test ERR_NR RET Z ; return if not $FF ; proceed if no errors so far. PUSH BC ; save start val LD HL,($4020) ; fetch location of letter in BASIC from DEST CALL L0B3B ; routine LV-FIND will set error LD HL,$4000 ; ERR_NR LD A,(HL) CP $02 ; compare to 2 - subscript out of range JR Z,L0C22 ; back to POP-RET if so >>> ; continue with variable not found or OK. RLA ; test for $FF?? BIT 6,(IY+$01) ; test bit 6 FLAGS - affects zero flag only. ; zero if string NZ if numeric JR C,L0C93 ; forward if error was $FF to L-EXISTS ; continue if variable does not exist. LD (HL),$FF ; cancel the error as variable will be created. JR Z,L0CA3 ; forward to L-STRING with string var. ; continue with numeric INTEGER variable LD HL,($4020) ; pick up destination from DEST LD BC,$0002 ; set default space for integer contents ; will be 3 including letter ;; L-EACH-CH L0C62: INC BC ; pre-increment character count. INC HL ; increment character pointer in BASIC or ; workspace. LD A,(HL) ; fetch the character. CALL L0D18 ; routine ALPHANUM check if "[0-Z]" JR C,L0C62 ; loop back if so to L-EACH-CH CP $DA ; is character '(' ? JR Z,L0CD0 ; forward if so to ERROR-02 - var not found. ; e.g. perhaps a function has been misspelled. RST 30H ; BC-SPACES creates room for new INTEGER ; variable at D-FILE - 1, the variables ; end-marker. JR NC,L0C22 ; back to POP-RET if not enough room PUSH DE ; save first new location *** LD HL,($4020) ; fetch DEST the pointer to letter in command DEC BC ; reduce count by DEC BC ; the three bytes DEC BC ; for simple integer. DEC DE ; point to destination LD A,B ; check if this is a one-character OR C ; variable name from reduced count. LD A,$40 ; prepare mask 010xxxxx JR Z,L0C87 ; forward to L-SINGLE if is simple numeric. LDIR ; else copy all but one characters of name. LD A,(HL) ; fetch last character OR $80 ; invert it LD (DE),A ; place at last destination LD A,$60 ; prepare mask 011xxxxx ;; L-SINGLE L0C87: POP HL ; restore first new location *** CALL L0CB9 ; routine L-MASK inserts masked letter. EX DE,HL ; DEC DE ; ; and continue to initialize variable contents. ; this branch is taken from below to overwrite contents. ;; L-NUMERIC L0C8D: POP HL ; restore variable value EX DE,HL ; HL points last location LD (HL),D ; insert high byte. DEC HL ; decrement the pointer. LD (HL),E ; and insert low-byte value RET ; return. with HL addressing the value. >>>> ; --- ;; L-EXISTS L0C93: JR NZ,L0C8D ; back to L-NUMERIC to overwrite variable ; if numeric type. POP HL ; restore string CALL L0CA4 ; routine L-LENGTH evaluates length of OLD ; string LD HL,($4022) ; fetch string pointer from RESULT DEC HL ; decrement to point to letter. CALL L0624 ; routine NEXT-ONE calculate space to delete JP L0666 ; routine RECLAIM-2 ; now continue into L-STRING to evaluate length of new string. ; --- ;; L-STRING L0CA3: POP HL ; restore pointer to contents. ;; L-LENGTH L0CA4: LD A,$01 ; the search will be for the quote character. LD BC,$0001 ; initialize length to one. ;; L-COUNT L0CA9: CP (HL) ; is addressed character null ? INC HL ; increase pointer. INC BC ; increase length. JR NZ,L0CA9 ; loop back to L-COUNT till terminating ; quote found. PUSH HL ; save pointer to end - null terminator. RST 30H ; routine BC-SPACES creates room at end. EX DE,HL ; transfer end to DE. POP HL ; retrieve pointer to null terminator in E-LINE. RET NC ; return if no room was available. LDDR ; else copy string to the variables area. EX DE,HL ; HL now points to letter -1 INC HL ; adjust LD A,$A0 ; prepare mask %10100000 ;; L-MASK L0CB9: EX DE,HL ; save variable pointer in DE. LD HL,($4020) ; fetch destination in prog/e-line area ; from system variable DEST XOR (HL) ; XOR mask with the letter. ; Note. All letters have bit 5 set. The ; preparation of masks must accommodate this. EX DE,HL ; variable pointer to HL, PUSH AF ; save masked letter CALL L0D0D ; routine REC-V80 reclaims ; the previous $80 variables end-marker. POP AF ; pop the letter. DEC HL ; point to the letter in the variables area. ; which is now one location lower than it was ; a moment ago. LD (HL),A ; insert masked letter. LD HL,($400C) ; use D_FILE value LD ($400A),HL ; to update new E_LINE DEC HL ; step back. LD (HL),$80 ; and insert the new variable $80 end-marker. RET ; return. ; --- ;; ERROR-02 L0CD0: POP HL ; RST 08H ; ERROR restart DEFB $01 ; variable name not found. ; ------------------------- ; THE 'DIM' COMMAND ROUTINE ; ------------------------- ; This routine creates a one-dimensional numeric array with up to ; 256 subscripts. Each is initialized to the integer zero. ; Note. array subscripts begin at zero. On later ZX computers subscripts began ; at 1 and there were no limits to the dimensions and subscripts other than ; memory. ;; DIM L0CD3: AND B ; check high byte of parameter. ; a maximum of 255 subscripts possible. JP NZ,L0BBE ; back to ERROR-03 - subscript error. PUSH BC ; save max subscript LD H,B ; transfer LD L,C ; to HL. INC HL ; increment to make range 1-256 from 0-255 INC HL ; increment for letter and subscript byte ADD HL,HL ; double - allocates two bytes per integer ; and two for the letter and subscript. LD B,H ; transfer count LD C,L ; to BC RST 30H ; BC-SPACES JP NC,L0C22 ; back to POP-RET if out of memory DEC HL ; point to last new location LD D,H ; transfer to DE LD E,L ; - the destination. DEC DE ; make DE one less than source. DEC BC ; reduce count DEC BC ; by two. LD (HL),$00 ; insert a zero at source. LDDR ; block fill locations with zero. POP BC ; restore number of subscripts LD (HL),C ; and place in location before data. LD A,$80 ; prepare mask %100 JR L0CB9 ; back to L-MASK ; --------------------- ; THE 'RESERVE' ROUTINE ; --------------------- ; A continuation of the BC-SPACES RESTART. ; the number of bytes required is on the machine stack. ;; RESERVE L0CF3: LD HL,($400A) ; fetch start of WKG Space from E_LINE PUSH HL ; preserve location. LD HL,($400C) ; fetch location after WKG Space from D_FILE DEC HL ; point to last byte of WKG space. CALL L05D5 ; routine MAKE-ROOM creates the space after ; last byte sliding D-FILE up and updating ; D_FILE, DF_EA and DF_END INC HL ; increase address INC HL ; by two bytes POP BC ; retrieve E_LINE which may have been updated ; by pointers LD ($400A),BC ; restore E_LINE POP BC ; restore the number of bytes required. EX DE,HL ; switch - DE points to first INC HL ; make HL point to last new byte SCF ; signal success RET ; return ; -------------------------------------- ; THE 'RECLAIM THE EDIT LINE' SUBROUTINE ; -------------------------------------- ; Interestingly, Hugo Davenport refers to this subroutine in the manual ; by its Nine Tiles source code label X_TEMP. ; The second entry point deletes the old variables end-marker when creating ; a new variable immediately after this position. ;; REC-EDIT L0D0A: LD HL,($400C) ; D_FILE ;; REC-V80 L0D0D: LD DE,($400A) ; E_LINE JP L0663 ; RECLAIM-1 ; ---------------------- ; THE 'ALPHA' SUBROUTINE ; ---------------------- ;; ALPHA L0D14: CP $26 ; compare to 'A' JR L0D1A ; forward to ALPHA-2 to compare ; against 'Z' ; ------------------------- ; THE 'ALPHANUM' SUBROUTINE ; ------------------------- ; The zx80 character set makes this routine as straightforward as the one above ; as there is no gap between numerals and alphabetic characters. ;; ALPHANUM L0D18: CP $1C ; compare to '0' - carry set if less ;; ALPHA-2 L0D1A: CCF ; change to carry reset if less. RET NC ; return if less than '0' CP $40 ; compare to character after 'Z' RET ; return with carry set if in the ; range '0' - 'Z' ; ------------------------------------------------ ; THE 'ARITHMETIC OPERATORS AND COMPARISONS' TABLE ; ------------------------------------------------ ; This table is indexed with the operator * 2 to access the address of the ; associated routine. ;; TAB-OPS L0D1F: DEFW L0D39 ; $00 subtract DEFW L0D3E ; $01 addition DEFW L0D44 ; $02 multiply DEFW L0D90 ; $03 division DEFW L0DB5 ; $04 and DEFW L0DBC ; $05 or DEFW L0D70 ; $06 to-power DEFW L0DC3 ; $07 nos-eql DEFW L0DCC ; $08 no-grtr DEFW L0DCD ; $09 no-less DEFW L0DD9 ; $0A strs-eql DEFW L0DDF ; $0B str-grtr DEFW L0DDE ; $0C str-less ; --------------------------- ; THE 'SUBTRACTION' OPERATION ; --------------------------- ; offset $00 : subtract ; This operation simply uses the Z80's 16-bit register subtract instruction ; which sets the overflow flag if the lower 15 bits overflow. ;; subtract L0D39: AND A ; clear carry flag. SBC HL,DE ; 16 bit subtraction. JR L0D41 ; forward to RSLT-TEST ; ------------------------ ; THE 'ADDITION' OPERATION ; ------------------------ ; offset $01 : add ; This operation simply uses the Z80's 16-bit register add instruction ; which sets the overflow flag in the manner above. ;; addition L0D3E: AND A ; clear carry flag. ADC HL,DE ; 16 bit addition. ;; RSLT-TEST L0D41: RET PO ; return if no twos-complement arithmetic ; overflow. ;; ERROR-06 L0D42: RST 08H ; ERROR restart DEFB $05 ; arithmetic overflow. ; ------------------------------ ; THE 'MULTIPLICATION' OPERATION ; ------------------------------ ; offset $02 : multiply ; the multiplication operation converts the two numbers HL and DE to positive ; integers, saving the result sign in the accumulator. If the positive result ; is above 32767 then an error code is produced else result is converted ; to the required sign, if necessary, as dictated by the accumulator. ;; multiply L0D44: CALL L0DED ; routine PREP-MD PUSH BC ; save priority/operation EX AF,AF' ; save result sign CALL L0D55 ; routine MULT16 JR NZ,L0D8D ; forward with overflow to POP6 ; clear the stack and produce ERROR-06 ;; MULT-2 L0D4E: POP BC ; restore priority/operation EX AF,AF' ; restore result sign. RRA ; test sign bit. RET NC ; return if result positive. JP L0DF6 ; exit via routine TWOS-COMP ; ---------------------------------------- ; THE 'SIXTEEN BIT MULTIPLICATION' ROUTINE ; ---------------------------------------- ; Binary long multiplication by shifting and addition at the appropriate place ; if the multiplier bit is set. ; This important subroutine is called from the multiply routine, the to-power ; routine and twice from the RND function routine. ; It multiplies the 16 bit multiplier, HL, by the 16-bit multiplicand DE. ; Since the highest number the ZX80 can hold is 32767, the routine detects ; any overflow above this, resetting the zero flag - NZ with overflow. ; However if overflow occurs the routine does not abort, as does say the ; Spectrum, but continues to calculate the 32-bit result in B, C, H, L. ; Use is made of this by the RND routine. ;; MULT16 L0D55: LD B,H ; transfer HL to BC LD C,L ; register. LD A,$10 ; count 16 bits. LD HL,$0000 ; initialize result register. ;; MULT-LP L0D5C: ADD HL,HL ; shift result left. RL C ; shift multiplier RL B ; to the left. ; and capture any overflow. JR NC,L0D67 ; skip addition if no carry to MULT-SKIP. ADD HL,DE ; else add in multiplicand for this bit JR NC,L0D67 ; forward if no overflow. INC BC ; capture overflow in BC ;; MULT-SKIP L0D67: DEC A ; decrement bit count. JR NZ,L0D5C ; loop back for all 16 bits to MULT-LP. LD A,H ; test for a AND $80 ; negative result. OR B ; test for any OR C ; intermediate overflow RET ; return with zero flag set ; for success. ; ------------------------ ; THE 'TO-POWER' OPERATION ; ------------------------ ; offset $06 : to-power ; This routine raises HL to the power DE, by performing a multiplication ; for each unit of the power. For the integer range supported this is quite ; adequate with 2**14 returning the result without any noticeable delay ; and 1**32767 blacking the screen out for no more than a second. ; Note also that ; 0 ** 0 = 1. ; 0 ** +n = 0. ; 0 ** -n = arithmetic overflow. ;; to-power L0D70: BIT 7,D ; test if second number negative. JR NZ,L0D42 ; back to ERROR-06 if so. XOR A ; initialize sign flag CALL L0DF2 ; routine ABS - makes HL positive. ; A holds 1 if HL was negative else 0. AND E ; EX AF,AF' ; save result PUSH BC ; save priority/operation LD B,D ; transfer power LD C,E ; to BC EX DE,HL ; transfer number to DE LD HL,$0001 ; initialize result. ;; POWER-LP L0D81: DEC BC ; decrement power counter. BIT 7,B ; check when zero passed. JR NZ,L0D4E ; back when finished to MULT-2 ; to test result. >> PUSH BC ; save counter. CALL L0D55 ; routine MULT16 POP BC ; restore counter. JR Z,L0D81 ; loop while no overflow exists from ; the multiplication to POWER-LP. ;; POP6 L0D8D: POP BC ; restore priority/operation JR L0D42 ; back to ERROR-06 - arithmetic overflow. ; ------------------------ ; THE 'DIVISION' OPERATION ; ------------------------ ; offset $03 : division ; Binary long division by shifting and subtraction at the appropriate place, ; setting correct quotient bit if the subtraction goes. ; dividend (HL) / divisor (DE) = quotient (HL) ;; division L0D90: LD A,D ; test divisor for zero OR E ; avoiding division by zero. JR Z,L0D42 ; to ERROR-06 - arithmetic overflow ; if so. CALL L0DED ; routine PREP-MD converts HL and DE to 15-bit ; integers and records the result sign in A. PUSH BC ; save the priority/operation. RRA ; sets carry if a negative result. ADC HL,HL ; pick up the carry in HL, (bit 15 was reset) LD A,H ; transfer modified dividend to LD C,L ; registers A and C. LD HL,L0000 ; initialize 'accumulator' to zero. LD B,$10 ; sixteen bits including sign bit. ;; DIV-1 L0DA2: ADC HL,HL ; SBC HL,DE ; subtract divisor. JR NC,L0DA9 ; skip forward if subtraction goes to DIV-2. ADD HL,DE ; add back divisor. ;; DIV-2 L0DA9: RL C ; as dividend bits are shifted out, the RLA ; result bits are shifted in. DJNZ L0DA2 ; back for all 16 bits. ; note after 16 bits the final RLA retrieves the sign LD H,A ; transfer result in A and C LD L,C ; to HL INC HL ; increment POP BC ; restore priority/operation. RET C ; return if . JR L0DF6 ; else forward to TWOS-COMP. ; --------------------------- ; THE 'BITWISE AND' OPERATION ; --------------------------- ; offset $04 : and ;; and L0DB5: LD A,H ; AND D ; LD H,A ; LD A,L ; AND E ; LD L,A ; RET ; ; -------------------------- ; THE 'BITWISE OR' OPERATION ; -------------------------- ; offset $05 : or ;; or L0DBC: LD A,H ; OR D ; LD H,A ; LD A,L ; OR E ; LD L,A ; RET ; ; ----------------------------------------- ; THE 'THREE NUMERIC COMPARISON' OPERATIONS ; ----------------------------------------- ; offsets $07 - nos-eql, $08 - no-grtr, $09 - no-less. ; ; for example, PRINT 2=2 gives result -1 (true) ;; nos-eql L0DC3: AND A ; prepare to subtract. SBC HL,DE ; subtract the two numbers. ;; SET-RSLT L0DC6: LD HL,$FFFF ; prepare true result. RET Z ; return true result, $FFFF, in HL ; if remainder was zero. INC HL ; else increment to $0000 RET ; return false result, zero in HL. ; --- ;; no-grtr L0DCC: EX DE,HL ; swap values and continue into ... ;; no-less L0DCD: AND A ; prepare for true subtraction SBC HL,DE ; subtract using registers LD A,H ; fetch MSB RLA ; test the sign bit without affecting P/V flag JP PO,L0DD6 ; skip to TEST-HL with no overflow CCF ; complement the carry flag ;; TEST-HL L0DD6: SBC HL,HL ; result HL will be $0000 false or $FFFF true ; with carry. RET ; return ; ---------------------------------------- ; THE 'THREE STRING COMPARISON' OPERATIONS ; ---------------------------------------- ; offsets $0A - strs-eql, $0B - str-grtr, $0C - str-less. ;; strs-eql L0DD9: CALL L0DE4 ; routine STR-CMP JR L0DC6 ; to SET-RSLT ; --- ;; str-grtr L0DDE: EX DE,HL ; swap the two string pointers ;; str-less L0DDF: CALL L0DE4 ; routine STR-CMP JR L0DD6 ; back to TEST-HL ; ---------------------------------- ; THE 'STRING COMPARISON' SUBROUTINE ; ---------------------------------- ;; STR-CMP L0DE4: LD A,(DE) ; fetch character of 2nd string. CP (HL) ; compare to first. RET NZ ; return with mismatch, carry flag ; shows the comparison. DEC A ; test for the null string chr$ 1. RET Z ; return as both strings have ; terminated - an exact match. INC DE ; else increase INC HL ; both the string pointers. JR L0DE4 ; and loop back to STR-CMP till one ; of the two conditions is met. ; ---------------------------------------------- ; THE 'PREPARE TO MULTIPLY OR DIVIDE' SUBROUTINE ; ---------------------------------------------- ;; PREP-MD L0DED: XOR A ; initialize a sign flag. CALL L0DF1 ; call PREP-1 to prepare one number ; and continue into routine to prepare ; the other number. ;; PREP-1 L0DF1: EX DE,HL ; switch numbers at each pass ; ------------------ ; THE 'ABS' FUNCTION ; ------------------ ; finds the absolute value of an signed integer. ; Negative numbers are twos complemented. ; e.g. minus 1 ($FFFF) is first 'ones complemented' to $0000 then incremented. ;; abs L0DF2: BIT 7,H ; test sign of HL. RET Z ; return if positive. INC A ; sets bit 0 if result is negative. ; two negatives will reset bit 0 when this ; routine is used to prepare for multiplication. ; 'a minus times a minus gives a plus'. ;; TWOS-COMP L0DF6: EX AF,AF' ; save running flag. LD A,H ; fetch high byte CPL ; complement it LD H,A ; put back LD A,L ; fetch low byte CPL ; complement LD L,A ; put back INC HL ; twos complement EX AF,AF' ; restore running flag. RET ; return. ; ------------------- ; THE 'SPARE' SECTION ; ------------------- ; Start of Spare bytes ; End of Spare bytes. ;-------------------- ; THE 'CHARACTER SET' ;-------------------- ;; char-set ; $00 - space character CHR$(0) L0E00: DEFB %00000000 DEFB %00000000 DEFB %00000000 DEFB %00000000 DEFB %00000000 DEFB %00000000 DEFB %00000000 DEFB %00000000 ; $01 - Character: '"' CHR$(1) DEFB %00000000 DEFB %00010100 DEFB %00010100 DEFB %00000000 DEFB %00000000 DEFB %00000000 DEFB %00000000 DEFB %00000000 ; $02 - Character: mosaic CHR$(2) DEFB %11110000 DEFB %11110000 DEFB %11110000 DEFB %11110000 DEFB %11110000 DEFB %11110000 DEFB %11110000 DEFB %11110000 ; $03 - Character: mosaic CHR$(3) DEFB %00000000 DEFB %00000000 DEFB %00000000 DEFB %00000000 DEFB %11111111 DEFB %11111111 DEFB %11111111 DEFB %11111111 ; $04 - Character: mosaic CHR$(4) DEFB %11110000 DEFB %11110000 DEFB %11110000 DEFB %11110000 DEFB %00000000 DEFB %00000000 DEFB %00000000 DEFB %00000000 ; $05 - Character: mosaic CHR$(5) DEFB %00001111 DEFB %00001111 DEFB %00001111 DEFB %00001111 DEFB %00000000 DEFB %00000000 DEFB %00000000 DEFB %00000000 ; $06 - Character: mosaic CHR$(6) DEFB %00000000 DEFB %00000000 DEFB %00000000 DEFB %00000000 DEFB %11110000 DEFB %11110000 DEFB %11110000 DEFB %11110000 ; $07 - Character: mosaic CHR$(7) DEFB %00000000 DEFB %00000000 DEFB %00000000 DEFB %00000000 DEFB %00001111 DEFB %00001111 DEFB %00001111 DEFB %00001111 ; $08 - Character: mosaic CHR$(8) DEFB %00001111 DEFB %00001111 DEFB %00001111 DEFB %00001111 DEFB %11110000 DEFB %11110000 DEFB %11110000 DEFB %11110000 ; $09 - Character: mosaic CHR$(9) DEFB %10101010 DEFB %01010101 DEFB %10101010 DEFB %01010101 DEFB %10101010 DEFB %01010101 DEFB %10101010 DEFB %01010101 ; $0A - Character: mosaic CHR$(10) DEFB %00000000 DEFB %00000000 DEFB %00000000 DEFB %00000000 DEFB %10101010 DEFB %01010101 DEFB %10101010 DEFB %01010101 ; $0B - Character: mosaic CHR$(11) DEFB %10101010 DEFB %01010101 DEFB %10101010 DEFB %01010101 DEFB %00000000 DEFB %00000000 DEFB %00000000 DEFB %00000000 ; $0C - Character: uk pound CHR$(12) DEFB %00000000 DEFB %00011110 DEFB %00100001 DEFB %01111000 DEFB %00100000 DEFB %00100000 DEFB %01111111 DEFB %00000000 ; $0D - Character: '$' CHR$(13) DEFB %00000000 DEFB %00001000 DEFB %00111110 DEFB %01001000 DEFB %00111110 DEFB %00001001 DEFB %00111110 DEFB %00001000 ; $0E - Character: ':' CHR$(14) DEFB %00000000 DEFB %00000000 DEFB %00000000 DEFB %00001000 DEFB %00000000 DEFB %00000000 DEFB %00001000 DEFB %00000000 ; $0F - Character: '?' CHR$(15) DEFB %00000000 DEFB %00111110 DEFB %01000001 DEFB %00000110 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 %00010000 DEFB %00001000 DEFB %00001000 DEFB %00001000 DEFB %00001000 DEFB %00010000 DEFB %00000000 ; $12 - Character: '-' CHR$(18) DEFB %00000000 DEFB %00000000 DEFB %00000000 DEFB %00000000 DEFB %00111110 DEFB %00000000 DEFB %00000000 DEFB %00000000 ; $13 - Character: '+' CHR$(19) DEFB %00000000 DEFB %00000000 DEFB %00001000 DEFB %00001000 DEFB %00111110 DEFB %00001000 DEFB %00001000 DEFB %00000000 ; $14 - Character: '*' CHR$(20) DEFB %00000000 DEFB %00000000 DEFB %00101010 DEFB %00011100 DEFB %00001000 DEFB %00011100 DEFB %00101010 DEFB %00000000 ; $15 - Character: '/' CHR$(21) DEFB %00000000 DEFB %00000000 DEFB %00000010 DEFB %00000100 DEFB %00001000 DEFB %00010000 DEFB %00100000 DEFB %00000000 ; $16 - Character: '=' CHR$(22) DEFB %00000000 DEFB %00000000 DEFB %00000000 DEFB %00111110 DEFB %00000000 DEFB %00111110 DEFB %00000000 DEFB %00000000 ; $17 - Character: '>' CHR$(23) DEFB %00000000 DEFB %00000000 DEFB %00010000 DEFB %00001000 DEFB %00000100 DEFB %00001000 DEFB %00010000 DEFB %00000000 ; $18 - Character: '<' CHR$(24) DEFB %00000000 DEFB %00000000 DEFB %00000100 DEFB %00001000 DEFB %00010000 DEFB %00001000 DEFB %00000100 DEFB %00000000 ; $19 - Character: ';' CHR$(25) DEFB %00000000 DEFB %00000000 DEFB %00001000 DEFB %00000000 DEFB %00000000 DEFB %00001000 DEFB %00001000 DEFB %00010000 ; $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 %00001100 DEFB %00001100 DEFB %00000000 ; $1C - Character: '0' CHR$(28) DEFB %00000000 DEFB %00011100 DEFB %00100010 DEFB %01000001 DEFB %01000001 DEFB %00100010 DEFB %00011100 DEFB %00000000 ; $1D - Character: '1' CHR$(29) DEFB %00000000 DEFB %00001100 DEFB %00010100 DEFB %00000100 DEFB %00000100 DEFB %00000100 DEFB %00011110 DEFB %00000000 ; $1E - Character: '2' CHR$(30) DEFB %00000000 DEFB %00111110 DEFB %01000001 DEFB %00000001 DEFB %00111110 DEFB %01000000 DEFB %01111111 DEFB %00000000 ; $1F - Character: '3' CHR$(31) DEFB %00000000 DEFB %00111110 DEFB %01000001 DEFB %00000110 DEFB %00000001 DEFB %01000001 DEFB %00111110 DEFB %00000000 ; $20 - Character: '4' CHR$(32) DEFB %00000000 DEFB %00001100 DEFB %00010100 DEFB %00100100 DEFB %01000100 DEFB %01111111 DEFB %00000100 DEFB %00000000 ; $21 - Character: '5' CHR$(33) DEFB %00000000 DEFB %01111111 DEFB %01000000 DEFB %01111110 DEFB %00000001 DEFB %01000001 DEFB %00111110 DEFB %00000000 ; $22 - Character: '6' CHR$(34) DEFB %00000000 DEFB %00111110 DEFB %01000000 DEFB %01111110 DEFB %01000001 DEFB %01000001 DEFB %00111110 DEFB %00000000 ; $23 - Character: '7' CHR$(35) DEFB %00000000 DEFB %01111111 DEFB %00000001 DEFB %00000010 DEFB %00000100 DEFB %00001000 DEFB %00001000 DEFB %00000000 ; $24 - Character: '8' CHR$(36) DEFB %00000000 DEFB %00111110 DEFB %01000001 DEFB %00111110 DEFB %01000001 DEFB %01000001 DEFB %00111110 DEFB %00000000 ; $25 - Character: '9' CHR$(37) DEFB %00000000 DEFB %00111110 DEFB %01000001 DEFB %01000001 DEFB %00111111 DEFB %00000001 DEFB %00111110 DEFB %00000000 ; $26 - Character: 'A' CHR$(38) DEFB %00000000 DEFB %00111110 DEFB %01000001 DEFB %01000001 DEFB %01111111 DEFB %01000001 DEFB %01000001 DEFB %00000000 ; $27 - Character: 'B' CHR$(39) DEFB %00000000 DEFB %01111110 DEFB %01000001 DEFB %01111110 DEFB %01000001 DEFB %01000001 DEFB %01111110 DEFB %00000000 ; $28 - Character: 'C' CHR$(40) DEFB %00000000 DEFB %00011110 DEFB %00100001 DEFB %01000000 DEFB %01000000 DEFB %00100001 DEFB %00011110 DEFB %00000000 ; $29 - Character: 'D' CHR$(41) DEFB %00000000 DEFB %01111100 DEFB %01000010 DEFB %01000001 DEFB %01000001 DEFB %01000010 DEFB %01111100 DEFB %00000000 ; $2A - Character: 'E' CHR$(42) DEFB %00000000 DEFB %01111111 DEFB %01000000 DEFB %01111100 DEFB %01000000 DEFB %01000000 DEFB %01111111 DEFB %00000000 ; $2B - Character: 'F' CHR$(43) DEFB %00000000 DEFB %01111111 DEFB %01000000 DEFB %01111100 DEFB %01000000 DEFB %01000000 DEFB %01000000 DEFB %00000000 ; $2C - Character: 'G' CHR$(44) DEFB %00000000 DEFB %00011110 DEFB %00100001 DEFB %01000000 DEFB %01000111 DEFB %00100001 DEFB %00011110 DEFB %00000000 ; $2D - Character: 'H' CHR$(45) DEFB %00000000 DEFB %01000001 DEFB %01000001 DEFB %01111111 DEFB %01000001 DEFB %01000001 DEFB %01000001 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 %00100010 DEFB %00011100 DEFB %00000000 ; $30 - Character: 'K' CHR$(48) DEFB %00000000 DEFB %01000010 DEFB %01000100 DEFB %01111000 DEFB %01000100 DEFB %01000010 DEFB %01000001 DEFB %00000000 ; $31 - Character: 'L' CHR$(49) DEFB %00000000 DEFB %01000000 DEFB %01000000 DEFB %01000000 DEFB %01000000 DEFB %01000000 DEFB %01111111 DEFB %00000000 ; $32 - Character: 'M' CHR$(50) DEFB %00000000 DEFB %01000001 DEFB %01100011 DEFB %01010101 DEFB %01001001 DEFB %01000001 DEFB %01000001 DEFB %00000000 ; $33 - Character: 'N' CHR$(51) DEFB %00000000 DEFB %01100001 DEFB %01010001 DEFB %01001001 DEFB %01000101 DEFB %01000011 DEFB %01000001 DEFB %00000000 ; $34 - Character: 'O' CHR$(52) DEFB %00000000 DEFB %00111110 DEFB %01000001 DEFB %01000001 DEFB %01000001 DEFB %01000001 DEFB %00111110 DEFB %00000000 ; $35 - Character: 'P' CHR$(53) DEFB %00000000 DEFB %01111110 DEFB %01000001 DEFB %01000001 DEFB %01111110 DEFB %01000000 DEFB %01000000 DEFB %00000000 ; $36 - Character: 'Q' CHR$(54) DEFB %00000000 DEFB %00111110 DEFB %01000001 DEFB %01000001 DEFB %01001001 DEFB %01000101 DEFB %00111110 DEFB %00000000 ; $37 - Character: 'R' CHR$(55) DEFB %00000000 DEFB %01111110 DEFB %01000001 DEFB %01000001 DEFB %01111110 DEFB %01000100 DEFB %01000010 DEFB %00000000 ; $38 - Character: 'S' CHR$(56) DEFB %00000000 DEFB %00111110 DEFB %01000000 DEFB %00111110 DEFB %00000001 DEFB %01000001 DEFB %00111110 DEFB %00000000 ; $39 - Character: 'T' CHR$(57) DEFB %00000000 DEFB %01111111 DEFB %00001000 DEFB %00001000 DEFB %00001000 DEFB %00001000 DEFB %00001000 DEFB %00000000 ; $3A - Character: 'U' CHR$(58) DEFB %00000000 DEFB %01000001 DEFB %01000001 DEFB %01000001 DEFB %01000001 DEFB %01000001 DEFB %00111110 DEFB %00000000 ; $3B - Character: 'V' CHR$(59) DEFB %00000000 DEFB %01000001 DEFB %01000001 DEFB %01000001 DEFB %00100010 DEFB %00010100 DEFB %00001000 DEFB %00000000 ; $3C - Character: 'W' CHR$(60) DEFB %00000000 DEFB %01000001 DEFB %01000001 DEFB %01000001 DEFB %01001001 DEFB %01010101 DEFB %00100010 DEFB %00000000 ; $3D - Character: 'X' CHR$(61) DEFB %00000000 DEFB %00100001 DEFB %00010010 DEFB %00001100 DEFB %00001100 DEFB %00010010 DEFB %00100001 DEFB %00000000 ; $3E - Character: 'Y' CHR$(62) DEFB %00000000 DEFB %01000001 DEFB %00100010 DEFB %00011100 DEFB %00001000 DEFB %00001000 DEFB %00001000 DEFB %00000000 ; $3F - Character: 'Z' CHR$(63) DEFB %00000000 DEFB %01111111 DEFB %00000010 DEFB %00000100 DEFB %00001000 DEFB %00010000 DEFB %01111111 L0FFF: DEFB %00000000 .END ;TASM assembler directive. ; ----------------------------------------------------------------------------- ; ; ------------------- ; The 'Character set' ; ------------------- ; ; $00 $01 $02 $03 $04 $05 $06 $07 $08 $09 $0A $0B $0C $0D $0E $0F ; nul gra gra gra gra gra gra gra gra gra gra ukp $ : ? ; ; $10 $11 $12 $13 $14 $15 $16 $17 $18 $19 $1A $1B $1C $1D $1E $1F ; ( ) - + * / = > < ; , . 0 1 2 3 ; ; $20 $21 $22 $23 $24 $25 $26 $27 $28 $29 $2A $2B $2C $2D $2E $2F ; 4 5 6 7 8 9 A B C D E F G H I J ; ; $30 $31 $32 $33 $34 $35 $36 $37 $38 $39 $3A $3B $3C $3D $3E $3F ; K L M N O P Q R S T U V W X Y Z ; ; ----------------------------------------------------------------------------- ; ; ------------------- ; THE 'ZX80 KEYBOARD' ; ------------------- ; [] mosaic graphic ukp currency symbol ; ; NOT AND THEN TO <= V ^ => HOME RUBOUT ;+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ ;| | | | | | | | | | | | | | | | | | | | ;| 1 | | 2 | | 3 | | 4 | | 5 | | 6 | | 7 | | 8 | | 9 | | 0 | ;+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ ; ; NEW LOAD SAVE RUN CONT REM IF INPUT PRINT ;+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ ;| [] | | [] | | [] | | [] | | [] | | " | | $ | | ( | | ) | | * | ;| Q | | W | | E | | R | | T | | Y | | U | | I | | O | | P | ;+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ ; ; LIST STOP DIM FOR GOTO POKE RAND LET EDIT ;+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ ;| [] | | [] | | [] | | [] | | [] | | ** | | - | | + | | = | | NEW | ;| A | | S | | D | | F | | G | | H | | J | | K | | L | | LINE| ;+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ ; ; CLEAR CLS GOSUB RET NEXT BREAK ;+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ ;| | | : | | ; | | ? | | / | | OR | | < | | > | | , | | ukp | ;|SHIFT| | Z | | X | | C | | V | | B | | N | | M | | . | |SPACE| ;+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ ; ; ; ; ----------------------------------------------------------------------------- ; ; ---------------------- ; THE 'SYSTEM VARIABLES' ; ---------------------- ; Note. the names of the System Variables are taken from the original ; Nine Tiles Assembly Listing. ; ; 1 16384 $4000 IY+$00 ERR_NR One less than report code. ; X1 16385 $4001 IY+$01 FLAGS Various Flags to control BASIC System. ; 7 1-Syntax off 0-Syntax on ; 6 1-Numeric result 0-String result ; 5 1-Evaluating function (not used) ; 3 1-K cursor 0-L cursor ; 2 1-K mode 0-L mode. ; 0 1-No leading space 0-Leading space. ; 2 16386 $4002 IY+$02 PPC Line number of current line. ; N2 16388 $4004 IY+$04 P_PTR. Position in RAM of [K] or [L] cursor. ; 2 16390 $4006 IY+$06 E_PPC Number of current line with [>] cursor. ; X2 16392 $4008 IY+$08 VARS Address of start of variables area. ; X2 16394 $400A IY+$0A E_LINE Address of start of Edit Line. ; X2 16396 $400C IY+$0C D_FILE Start of Display File. ; X2 16398 $400E IY+$0E DF_EA Address of the start of lower screen. ; X2 16400 $4010 IY+$10 DF_END Display File End. ; X1 16402 $4012 IY+$12 DF_SZ Number of lines in lower screen. ; 2 16403 $4013 IY+$13 S_TOP. The number of first line on screen. ; 2 16405 $4015 IY+$15 X_PTR Address of the character preceding ; the [S] marker. ; 2 16407 $4017 IY+$17 OLDPPC Line number to which continue jumps. ; N1 16409 $4019 IY+$19 FLAGX. More flags. ; 7 1-K mode 0-L mode. ; 6 1-Numeric result 0-String result ; 5 1-Inputting 0-Editing ; N2 16410 $401A IY+$1A T_ADDR Address of next item in syntax table. ; U2 16412 $401C IY+$1C SEED The seed for the random number. ; U2 16414 $401E IY+$1E FRAMES Count of frames shown since start-up. ; N2 16416 $4020 IY+$20 DEST Address of variable in statement. ; N2 16418 $4022 IY+$22 RESULT. Value of the last expression. ; X1 16420 $4024 IY+$24 S_POSN_X Column number for print position. ; X1 16421 $4025 IY+$25 S_POSN_Y Line number for print position. ; X2 16422 $4026 IY+$26 CH_ADD. Address of next character to be ; interpreted. ; ; -----------------------------------------------------------------------------