Part A copyright (c) 1982 by Dr. I. Logan
Part B copyright (c) 1982 by Dr. I. Logan & Dr. Frank O'Hara
Melbourne House (Publishers) Ltd. ISBN 0 86161 113 6
National Library of Australia Card Number and ISBN 0 86759 124 2
The book for the programmer that needs those answers about the Timex TS1000/Sinclair ZX81 ROM. Dr. Logan and Dr. Frank O'Hara have examined all routines in the ROM and comment on each one. This book is a must for the experienced programmer.
Part A covers all functions that can be used except for the floating point calculator. Part B covers all the routines involved in the 'evaluation of an expression' and a detailed explanation of the 'floating-point calculator.
The 'flow diagram' for Part A
The 'Listing'
The 'forward references'
The RST0028 literals'
Notes on the SYSTEM VARIABLES
Index
The NMI generator is turned off and BC set to the 'top of possible RAM'
0000 START OUT (+FD),A LD BC,+7FFF JP 03CB,RAM-CHECK
0008 ERROR-1 LD HL,(CH-ADD) LD (X-PTR),HL JR 0056,ERROR-2
The code of the character to be printed is in the A register.
0010 PRINT-A AND A JP NZ,07F1,PRINT-CH JP 07F5,PRINT-SP. DEFB +FF
The A register is set with the character addressed by CH-ADD. Spaces are ignored.
0018 GET-CH. LD HL,(CH-ADD) LD A,(HL) 001C TEST-SP. AND A RET NZ NOP NOP
CH-ADD is incremented before the character is fetched.
0020 NEXT-CH CALL 0049,CH-ADD+1 JR 001C,TEST-SP. DEFB +FF DEFB +FF DEFB +FF
A direct jump is made to the calculator routine.
0028 FP-CALC. JP 199D,CALCULATE
The byte 34 ends a RST 0028 operation.
002B END-CALC. POP AF EXX EX (SP),HL EXX RET
BC spaces are made available for different purposes.
0030 BC-SPACES PUSH BC LD HL,(E-LINE) PUSH HL JP 1488,RESERVE
B holds the line number and C the number of the scan line.
0038 INTERRUPT DEC C JP NZ,0045,SCAN-LINE POP HL DEC B RET Z SET 3,C 0041 WAIT-INT. LD R,A EI JP (HL) 0045 SCAN-LINE POP DE RET Z JR 0041,WAIT-INT.
The pointer CH-ADD is incremented and the cursor ignored.
0049 CH-ADD+1 LD HL,(CH-ADD) 004C CURSOR-SO INC HL 004D TEMP-PTR. LD (CH-ADD),HL LD A,(HL) CP +7F RET NZ JR 004C,CURSOR-SO
L is loaded with the 'data byte'.
0056 ERROR-2 POP HL LD L,(HL) 0058 ERROR-3 LD (ERR-NR),L LD SP,(ERR-SP) CALL 0207,SLOW/FAST JP 14BC,SET-MEM DEFB +FF
This routine is entered whenever a 'SLOW' NM interrupt occurs.
0066 NMI EX AF,A'F' INC A JP M,006D NMI-RET JR Z,006F NMI-CONT. 006D NMI-RET EX AF,A'F' RET
The main registers are preserved on the stack, the NMI generator is switched off and a jump is made into the display routine. (IX holding 0281 or 028F)
006F NMI-CONT EX AF,A'F' PUSH AF PUSH BC PUSH DE PUSH HL LD HL,(D-FILE) SET 7,H HALT OUT (+FD),A JP (IX)
The 'unshifted' character codes 007E - 00A4 The 'shifted' character codes 00A5 - 00CB The 'function' character codes 00CC - 00F2 The 'graphic' character codes 00F3 - 0110 The 'token' tables 0111 - 01FB
HL is incremented until it matches the current value in 'E-LINE'.
0lFC LOAD/SAVE INC HL EX DE,HL LD HL,(E-LINE) SCF SBC HL,DE EX DE,HL RET NC POP HL
i) Test for SLOW or FAST Mode.
The SLOW flag, Bit 6 of CDFLAG is tested, and a return is made if the program is in FAST mode or 'SLOW' display is not available.
0207 SLOW/FAST LD HL,+CDFLAG LD A,(HL) RLA XOR (HL) RLA RET NC LD A,+7F EX AF,A'F' LD B,+ll OUT (+FE),A 0216 LOOP-11 DJNZ 0216,LOOP-11 OUT (+FD),A EX AF,A'F' RLA JR NC,0226,NO-SLOW SET 7,(HL) PUSH AF PUSH BC PUSH DE PUSH HL JR 0229,DISPLAY-1 0226 NO-SLOW RES 6,(HL) RET
ii) The main display routine.
The frame counter is first collected and decremented. A return is made if the frame counter reaches zero.
0229 DISPLAY-1 LD HL,(FRAMES) DEC HL 022D DISPLAY-P LD A,+7F AND H OR L LD A,H JR NZ,0237,ANOTHER RLA JR 0239,OVER-NC 0237 ANOTHER LD B,(HL) SCF 0239 OVER-NC LD H,A LD (FRAMES),HL RET NC
The keyboard is now read, and a return is made if a new key has been pressed. Otherwise a display is produced.
023E DISPLAY-2 CALL 02BB,KEYBOARD LD BC,(LAST-K) LD (LAST-K),HL LD A,B ADD A,+02 SBC HL,BC LD A,(DEBOUNCE) OR H OR L LD E,B LD B,+0B LD HL,+CDFLAG RES 0,(HL) JR NZ,0264,NO-KEY BIT 7,(HL) SET 0,(HL) RET Z DEC B NOP SCF 0264 NO-KEY LD HL,+DEBOUNCE CCF RL B 026A LOOP-B DJNZ 026A,LOOP-B LD B,(HL) LD A,E CP +FE SBC A,A LD B,+lF OR (HL) AND B RRA LD (HL),A OUT (+FF),A LD HL,(D-FILE) SET 7,H CALL 0292,DISPLAY-3 LD A,R LD BC,+1901 LD A,+F5 CALL 02B5,DISPLAY-5 DEC HL CALL 0292,DISPLAY-3 JP 0229,DISPLAY-1
The IX register is loaded with the 'return' address, and the main registers are restored after a 'slow' display.
0292 DISPLAY-3 POP IX LD C,(MARGIN) BIT 7,(CDFLAG) JR Z,02A9,DISPLAY-4 LD A,C NEG INC A EX AF,A'F' OUT (+FE),A POP HL POP DE POP BC POP AF RET
Sets up the A and B registers for the display.
02A9 DISPLAY-4 LD A,+FC LD B,+01 CALL 02B5,DISPLAY-5 DEC HL EX (SP),HL EX (SP),HL JP (IX)
Sets up the refresh register and waits for an interrupt.
02B5 DISPLAY-5 LD R,A LD A,+DD EI JP (HL)
The keyboard is scanned eight times and the result built up in the HL register pair. MARGIN is also determined.
02BB - KEYBOARD LD HL,+FFFF LD BC +FEFE IN A,(C) OR +01 02C5 EACH-LINE OR +E0 LD D,A CPL CP +01 SBC A,A OR B AND L LD L,A LD A,H AND D LD H,A RLC B IN A,(C) JR C,02C5,EACH-LINE RRA RL H RLA RLA RLA SBC A,A AND +18 ADD A,+1F LD (MARGIN),A RET
The NMI generator is turned off and bit 7 of CDFLAG is RESET. Bit 6 will remain SET if the overall mode is SLOW, i.e. in PAUSE.
02E7 SET-FAST BIT 7,(CDFLAG) RET Z HALT OUT (+FD),A RES 7,(CDFLAG) RET REPORT-F - No name supplied. 02F4 REPORT-F RST 0008,ERROR-1 DEFB +0E
HL is set to point to the start of the program name. There is a 6 second header and then the bytes of the name and the program are passed out to the cassette recorder.
02F6 SAVE CALL 03A8,NAME JR C,02F4,REPORT-F EX DE,HL LD DE,+12CB 02FF HEADER CALL 0F46,BREAK-1 JR NC,0332,BREAK-2 0304 DELAY-1 DJNZ 0304,DELAY-1 DEC DE LD A,D OR E JR NZ,02FF,HEADER 030B OUT-NAME CALL 031E,OUT-BYTE BIT 7,(HL) INC HL JR Z,030B OUT-NAME LD HL,+VERSN 0316 OUT-PROG. CALL 031E,OUT-BYTE CALL 01FC,LOAD/SAVE JR 0316,OUT-PROG. 031E OUT-BYTE LD E,(HL) SCF 0320 EACH-BIT RL E RET Z SBC A,A AND +05 ADD +04 LD C,A 0329 PULSES OUT (+FF),A LD B,+23 032D DELAY-2 DJNZ 032D,DELAY-2 CALL 0F46,BREAK-1 0332 BREAK-2 JR NC,03A6,REPORT-D LD B,+1E 0336 DELAY-3 DJNZ 0336,DELAY-3 DEC C JR NZ,0329,PULSES 033B DELAY-4 AND A DJNZ 033B,DELAY-4 JR 0320,EACH-BIT
The bytes collected from the tape are matched against the program name and then the program is loaded into RAM.
0340 LOAD CALL 03A8,NAME RL D RRC D 0347 NEXT-PROG CALL 034C,IN-BYTE JR 0347,NEXT-PROG 034C IN-BYTE LD C,+01 034E NEXT-BIT LD B,+00 0350 BREAK-3 LD A,+7F IN A,(+FE) OUT (+FF),A RRA JR NC,03A2,BREAK-4 RLA RLA JR C,0385,GET-BIT DJNZ 0350,BREAK-3 POP AF CP D 0361 RESTART JP NC,03E5,INITIAL. LD H,D LD L,E 0366 IN-NAME CALL 034C,IN-BYTE BIT 7,D LD A,C JR NZ,0371,MATCHING CP (HL) JR NZ,0347 NEXT-PROG 0371 MATCHING INC HL RLA JR NC,0366,IN-NAME INC (E-LINE-hi.) LD HL, + VERSN 037B IN-PROG. LD D,B CALL 034C,IN-BYTE LD (HL),C CALL 01FC,LOAD/SAVE JR 037B,IN-PROG 0385 GET-BIT PUSH DE LD E,+94 0388 TRAILER LD B,+1A 038A COUNTER DEC E IN A,(+FE) RLA BIT 7,E LD A,E JR C,0388,TRAILER DJNZ 038A,COUNTER POP DE JR NZ,039C,BIT-DONE CP +56 JR NC,034E,NEXT-BIT 039C BIT-DONE CCF RL C JR NC,034E,N EXT-BIT RET 03A2 BREAK-4 LD A,D AND A JR Z,0361,RESTART REPORT-D - Break pressed 03A6 REPORT-D RST 0008,ERROR-1 DEFB +0C
The name is checked for 'report C', 'FAST' mode is selected and the final letter of the name is inverted.
03A8 NAME CALL 0F55,SCANNING LD A,(FLAGS) ADD A,A JP M,0D9A,REPORT-C POP HL RET NC PUSH HL CALL 02E7,SET-FAST CALL 13F8 STK-FETCH LD H,D LD L,E DEC C RET M ADD HL,BC SET 7,(HL) RET
'FAST' mode is selected and BC is loaded with the present value of RAMTOP.
03C3 NEW CALL 02E7 SET-FAST LD BC,(RAMTOP) DEC BC
Starting with location RAMTOP-1 an attempt is made to fill each location with 02. The addresses are decremented until 3FFF is reached. Each location is then read-back until the first address that does not fetch 02 is found. This address is RAMTOP.
03CB RAM-CHECK LD H,B LD L,C LD A,+3F 03CF RAM-FILL LD (HL), +02 DEC HL CP H JR NZ,03CF,RAM-FILL 03D5 RAM-READ AND A SBC HL,BC ADD HL,BC INC HL JR NC,03E2,SET-TOP DEC (HL) JR Z,03E2,SET-TOP DEC (HL) JR Z,03D5,RAM-READ 03E2 SET-TOP LD (RAMTOP),HL
The different tasks of the initialisation routine are:
i. Set the top location in RAM to hold 3E.
ii. Set the stack pointer to point to the next location below
iii. Set ERR-SP to hold the address two locations below the stack
pointer.
iv. Set the register to hold 1E.
v. Select interrupt mode 1.
vi. Set the IY register to hold ERR-NR as its base address.
vii. Select 'SLOW' mode.
viii. Set D-FILE to hold PROGRAM, i.e. No program present.
ix. Make a collapsed D-FILE.
x. Set VARS.
xi. CALL CLEAR command routine.
xii. Put the cursor in the edit line.
xiii. Produce a 'SLOW' display.
03E5 INITIAL LD HL,(RAMTOP) DEC HL i. LD (HL),+3E DEC HL ii. LD SP,HL DEC HL iii. DEC HL LD (ERR-SP},HL iv. LD A,+1E LD I,A v. IMl LD IY,+ERR-NR vi. LD (CDFLAG),+40 vii. LD HL,+PROGRAM viii. LD (D-FILE),HL ix. LD B,+l9 0408 LINE LD (HL),+76 INC HL DJNZ 0408,LINE x. LD (VARS),HL xi. CALL 149A,CLEAR 0413 N/L-ONLY CALL 14AD,CURSOR-IN xiii. CALL 0207,SLOW/FAST
The 'upper' part of the display is produced by first calling the CLS command routine, then the BASIC program is listed from S-TOP. The use of the 'cursor down' key also causes the 'upper' part of the display to be rebuilt.
0419 UPPER CALL 0A2A,CLS LD HL,(E-PPC} LD DE,(S-TOP) AND A SBC HL,DE EX DE,HL JR NC,042D,ADDR.TOP ADD HL,DE LD (S-TOP),HL 042D ADDR-TOP CALL 09D8,LINE-ADDR JR Z,0433, LIST-TOP EX DE,HL 0433 LIST-TOP CALL 073E,LIST-PROG DEC (BERG) JR NZ,0472,LOWER LD HL,(E-PPC) CALL 09D8, LINE-ADDR LD HL,(CH-ADD) SCF SBC HL,DE LD HL,+S-TOP JR NC,0457,INC-LINE EX DE,HL LD A,(HL) INC HL LDI LD (DE),A JR 0419,UPPER 'cursor down' entry point. 0454 DOWN-KEY LD HL,+E-PPC 0457 INC-LINE LD E,(HL) INC HL LD D,(HL) PUSH HL EX DE,HL INC HL CALL 09D8,LINE-ADDR CALL 05BB,LINE-NO. POP HL 0464 KEY-INPUT BIT 5,(FLAGX) JR NZ,0472,LOWER LD (HL),D DEC HL LD (HL),E JR 0419,UPPER
The 'lower' part of the display is formed by copying the edit.line from the workspace to the bottom of the screen.
First floating point numbers are removed, then the blank part of the screen is defined and finally the edit.line is copied over with the 'lower' part of the screen being expanded if necessary.
The EDIT-INP. entry point comes into use when EDIT is used in reply to a request for INPUT.
046F EDIT-INP. CALL 14AD,CURSOR-IN 0472 LOWER LD HL,(E-LINE) 0475 EACH-CHAR LD A,(HL) CP +7E JR NZ,0482,END-LINE LD BC,+0006 CALL 0A60,RECLAIM-2 JR 0475,EACH-CHAR 0482 END-LINE CP +76 INC HL JR NZ,0475,EACH-CHAR 0487 EDIT-LINE CALL 0537 CURSOR 048A EDIT-ROOM CALL 0A1F,LINE-ENDS LD HL,(E-LINE) LD (ERR-NR),+FF CALL 0766,COPY-LINE BIT 7,(ERR-NR) JR NZ,04C1,DISPLAY-6 LD A,(DF-SZ) CP +18 JR NC,04C1,DISPLAY-6 INC A LD (DF-SZ),A LD B,A LD C,+01 CALL 0918,LOC.-ADDR LD D,H LD E,L LD A,(HL) 04Bl FREE-LINE DEC HL CP (HL) JR NZ,04Bl,FREE-LINE INC HL EX DE,HL LD A,(RAMTOP-hi.) CP +4D CALL C,0A5D,RECLAIM-1 JR 048A,EDIT-ROOM
The syntax error pointer is set to zero and a display is produced. Once a key has been pressed the display is terminated. The pressing of 'multiple keys' causes a jump back to LOWER.
04C1 DISPLAY-6 LD HL,+0000 LD (X-PTR),HL LD HL, + CDFLAG BIT 7,(HL) CALL Z,0229,DISPLAY-1 04CF SLOW-DISP BIT 0,(HL) JR Z,04CF,SLOW-DISP LD BC,(LAST-K) CALL 0F4B,D-BOUNCE CALL 07BD,DECODE JR NC,0472,LOWER
The differing modes give differing values for the keys of the keyboard. These are obtained from the key tables.
04DF MODE-SORT LD A,(MODE) DEC A JP M,0508,FETCH-2 JR NZ,04F7,FETCH-1 LD (MODE),A DEC E LD A,E SUB +27 JR C,04F2,FUNC-BASE LO E,A 04F2 FUNC-BASE LD HL,+00CC JR 0505,TABLE-ADD 04F7 FETCH-l LD A,(HL) CP +76 JR Z,052B,K/L-KEY CP +40 SET 7,A JR C,051B,ENTER LD HL,+00C7 0505 TABLE-ADD ADD HL,DE JR 0515,FETCH-3 0508 FETCH-2 LD A,(HL) BIT 2,FLAGS JR NZ,0516,TEST-CURS ADD A,+C0 CP +E6 JR NC,0516,TEST-CURS 0515 FETCH-3 LD A,(HL) 0516 TEST-CURS CP +F0 JP PE,052D,KEY-SORT 051B ENTER LD E,A CALL 0537,CURSOR LD A,E CALL 0526,ADD-CHAR 0523 BACK-NEXT JP 0472,LOWER
All of the RAM from (HL) to STKEND is moved up by one byte and the character code in the A register is entered into the extra location.
0526 ADD-CHAR CALL 099B,ONE-SPACE LD (DE),A RET
The addresses of the different routines for the cursor keys are obtained by adding the character code twice to the base address 0482. The address is then stacked.
052B K/L-KEY LD A,+78 052D KEY-SORT LD E,A LD HL,+0482 ADD HL,DE ADD HL,DE LD C,(HL) INC HL LD B,(HL) PUSH BC
The characters in the edit line are read in turn.
Initially K-mode is selected but it will be changed to L-mode unless the line holds only the cursor or the last token is THEN. The RET Z instruction takes the program to the cursor key routines.
0537 CURSOR LD HL, (E-LINE) BIT 5,(FLAGX) JR NZ,0556,L-MODE 0540 K-MODE RES 2,(FLAGS) 0544 TEST-CHAR LD A,(HL) CP +7F RET Z INC HL CALL 07B4,NUMBER JR Z,0544,TEST-CHAR CP +26 TEST-CHAR JR C,0544, CP +DE JR Z,0540,K-MODE 0556 L-MODE SET 2,(FLAGS) JR 0544,TEST-CHAR
The single character (HL) is overwritten by moving all of the RAM from (HL+1)-STKEND down by one byte.
055C CLEAR-ONE LD BC,+0001 JP 0A60,RECLAIM-2
0562 | 9F 05 | UP-KEY 059F 0564 | 54 04 | DOWN-KEY 0454 0566 | 76 05 | LEFT-KEY 0576 0568 | 7F 05 | RIGHT-KEY 057F 056A | AF 05 | GRAPHICS 05AF 056C | C4 05 | EDIT-KEY 05C4 056E | 0C 06 | N/L-KEY 060C 0570 | 8B 05 | RUBOUT 058B 0572 | AF 05 | FUNCTION 05AF 0574 | AF 05 | FUNCTION 05AF
0576 LEFT-KEY CALL 0593,LEFT-EDGE LD A,(HL) LD (HL),+7F INC HL JR 0588,GET-CODE
057F RIGHT-KEY INC HL LD A,(HL) CP +76 JR Z,059D,ENDED-2 LD (HL),+7F DEC HL 0588 GET-CODE LD (HL),A 0589 ENDED-1 JR 0523,BACK-NEXT
058B RUBOUT CALL 0593,LEFT-EDGE CALL 055C,CLEAR-ONE JR 0589,ENDED-1
The first character in the edit-line is tested against +7F, the cursor.
0593 LEFT-EDGE DEC HL LD DE,(E-LINE) LD A,(DE) CP +7F RET NZ POP DE 059D ENDED-2 JR 0589,ENDED-1
059F UP-KEY LD HL,(E-PPC) CALL 09D8,LINE-ADDR EX DE,HL CALL 05BB,LINE-NO. LD HL,+E-PPC-hi. JP 0464,KEY-INPUT
05AF FUNCTION LD A,E AND +07 LD (MODE),A JR 059D,ENDED-2
The subroutine is entered at LINE-NO, with an address in HL, If a line number is to be found at that position then it is returned in DE, otherwise DE is returned with +0000.
05B7 ZERO-DE EX DE,HL LD DE,+04C2 05BB LINE-NO. LD A,(HL) AND +C0 JR NZ,05B7,ZERO-DE LD D,(HL) INC HL LD E,(HL) RET
First the 'lower' part of the screen is cleared, then the flag that shows whether the INPUT Command is being followed, is tested and a return made if the flag is set. Next the line to be edited is located. Its number is printed, followed by the cursor, but before the line itself is copied into the workspace a test for sufficient room is made. A return is made if there is not enough available RAM.
05C4 EDIT-KEY CALL 0A1F,LINE-ENDS LD HL, + EDIT-INP. PUSH HL BIT 5,(FLAGX) RET NZ LD HL,(E-LINE) LD (DF-CC),HL LD HL,+1821 LD (S-POSN),HL LD HL,(E-PPC) CALL 09D8,LINE-ADDR CALL 05BB,LINE-NO. LD A,D OR E RET Z DEC HL CALL 0AA5,OUT-NO. INC HL LD C,(HL) INC HL LD B,(HL) INC HL LD DE,(DF-CC) LD A,+7F LD (DE),A INC DE PUSH HL LD HL, + 001D ADD HL,DE ADD HL,BC SBC HL,SP POP HL RET NC LDIR EX DE,HL POP DE CALL 14A6,SET-STK-B JR 059D,ENDED-2
The NEWLINE key can be used in three separate situations and these have to be dealt with in different ways. The first part of the routine is common to all situations.
The 'lower' part of the screen is cleared. The PRBUFF is also cleared unless the INPUT command is being used, or the direct command COPY. The line is then scanned to check for syntax errors. The cursor is removed and the line number found, if present.
060C N/L-KEY CALL 0A1F,LINE-ENDS LD HL,+LOWER BIT 5,(FLAGX) JR NZ,0629,NOW-SCAN LD HL,(E-LINE) LD A,(H L) CP +FF JR Z,0626,STK-UPPER CALL 08E2,CLEAR-PRB CALL 0A2A,CLS 0626 STK-UPPER LD HL, + UPPER 0629 NOW-SCAN PUSH HL CALL 0CBA, LINE-SCAN POP HL CALL 0537,CURSOR CALL 055C,CLEAR-ONE CALL 0A73,E-LINE-NO JR NZ,064E,N/L-INP. LD A,B OR C JP NZ,06E0,N/L-LINE
The second part sets up the required parameters for the execution of the line, either as a BASIC line or as an INPUT line.
An empty line is detected and the program jumps back to the initialization routine.
DEC BC DEC BC LD (RPC),BC LD (DF-SIZE),+02 LD DE,(DFILE) JR 0661,TEST-NULL 064E N/L-INP CP +76 JR Z,0664,N/L-NULL LD BC,(T-ADDR) CALL 0918,LOC-ADDR LD DE,(NXT-LIN) LD (DF-SIZE),+02 0661 TEST-NULL RST 0018,GET-CH CP +76 0664 N/L-NULL JP Z,+0413,N/L-ONLY LD (FLAGS),+80 EX DE,HL
The third part of the routine is the 'line execution loop'. When a BASIC program is being RUN, it is this 'loop' that leads to the execution of the BASIC lines in their correct order.
In the case of the INPUT command, the 'line' is detected and input in the LINE-RUN subroutine.
066C NEXT-LINE LD (NXTLIN),HL EX DE,HL CALL 004D,TEMP-PTR CALL 0CC1,LINE-RUN RES 1,(FLAGS) LD A,+C0 LD (X-PTR-hi),A CALL 14A3,X-TEMP RES 5,(FLAGX) BIT 7,(ERR-NR) JR Z,06AE,STOP-LINE LD HL,(NXTLIN) AND (HL) JR NZ,06AE STOP-LINE LD D,(HL) INC HL LD E,(HL) LD (PPC),DE INC HL LD E,(HL) INC HL LD D,(HL) INC HL EX DE,HL ADD HL,DE CALL 0F46,BREAK-1 JR C,066C,NEXT-LINE
The third part of the routine is used to produce the report at the end of a RUN, after other direct commands and following the use of the BREAK key.
LD HL,+ERR-NR BIT 7,(HL) JR Z,06AE,STOP-LINE LD (HL),+0C 06AE STOP-LINE BIT 7,(PR-CC) CALL Z,0871,COPY-BUFF LD BC,+0121 CALL 0918,LOC-ADDR LD A,(ERR-NR) LD BC,(PPC) INC A JR Z,06D1,REPORT CP +09 JR NZ,06CA,CONTINUE INC BC 06CA CONTINUE LD (OLDPPC),BC JR NZ,06D1,REPORT DEC BC 06D1 REPORT CALL 07EB,OUT-CODE LD A,+18 RST 0010,PRINT-A CALL 0A98,OUT-NUM CALL 14AD,CURSOR-IN JP 04C1,DISPLAY-6
The fourth part of the routine is involved in the entering of the BASIC line into its correct position in the BASIC program. Initially a search is made to see if there is already a line with the same name number. If a line is found then it is 'reclaimed'. The new line is then copied from the workspace to its correct place in the BASIC program.
06E0 N/L-LINE LD (E-PPC),BC LD HL,(CH-ADD) EX DE,HL LD HL,+N/L-ONLY PUSH HL LD HL,(STKBOT) SBC HL,DE PUSH HL PUSH BC CALL 02E7,SET-FAST CALL 0A2A,CLS POP HL CALL 09DB,LINE-ADD JR NZ,0705,COPY-OVER CALL 09F2,NEXT-ONE CALL 0A6O,RECLAIM-2 0705 COPY-OVER POP BC LD A,C DEC A OR B RET Z PUSH BC INC BC INC BC INC BC INC BC DEC HL CALL 099E,MAKE-ROOM CALL 0207,SL0W/FAST POP BC PUSH BC INC DE LD HL,(STKBOT) DEC HL LDDR LD HL,(E-PPC) EX DE,HL POP BC LD (HL),B DEC HL LD (HL),C DEC HL LD (HL),E DEC HL LD (HL),D RET
The 'LIST' command will list the BASIC program from a given line, or line zero if no number is supplied. The first part of the routine finds the 'parameter' and saves the line number in E-PPC. The second part of the routine repeatedly calls the OUT-LINE subroutine until either the screen is full or the last line has been printed.
072C LLIST SET 1,(FLAGS) 0730 LIST CALL 0EA7,FIND-INT. LD A,B AND +3F LD H,A LD L,C LD (E-PPC),HL CALL 09D8,LINE-ADDR 073E LIST-PROG LD E,+00 0740 UNTIL-END CALL 0745,OUT-LINE JR 0740,UNTIL-END
The first part of the routine fetches the line number of the 'current cursor line' and tests against the line number that it is to print The line number is then printed followed by the 'current line cursor' if required, or a space if not.
0745 OUT-LINE LD BC,(E-PPC) CALL 09EA,CP-LINES LD D, +92 JR Z,0755,TEST-END LD DE, +0000 RL E 0755 TEST-END LD (BERG),E LD A,(HL) CP + 40 POP BC RET NC PUSH BC CALL 0AA5,OUT-NO. INC HL LD A,D RST 0010,PRINT-A INC HL INC HL
The second part of the routine prints the actual line. By comparing CH-ADD & X-PTR the routine tests to see if the syntax error marker should be printed. The routine also tests for floating point numbers and jumps over them. When a 'token' is found a call is made to the 'token printing' subroutine. When the cursor marker is found the appropriate cursor is printed.
0766 COPY-LINE LD (CH-ADD),HL SET 0,(FLAGS) 076D MORE-LINE LD BC,(X-PTR) LD HL,(CH-ADD) AND A SBC HL,BC JR NZ,077C,TEST-NUM LD A,+B8 RST 0010,PRINT-A 077C TEST-NUM. LD HL,(CH-ADD) LD A,(HL) INC HL CALL 07B4,NUMBER LD (CH-ADD),HL JR Z,076D,MORE-LINE CP +7F JR Z,079D,OUT-CURS CP +76 JR Z,07EE,OUT-CH BIT 6,A JR Z,079A,NOT-TOKEN CALL 094B,TOKENS JR 076D,MORE-LINE 079A NOT-TOKEN RST 0010,PRINT-A JR 076D,MORE-LINE 079D OUT-CURS. LD A,(MODE) LD B,+AB AND A JR NZ,07AA,FLAGS-2 LD A,(FLAGS) LD B,+B0 07AA FLAGS-2 RRA RRA AND +01 ADD A,B CALL 07F5,PRINT-SP JR 076D,MORE-LINE
This subroutine tests the character in the register against the 'number marker'. If a match occurs then the value in the HL register pair is incremented five times, so to either skip over the floating point number or to reserve 5 bytes for such a number.
07B4 NUMBER CP +7E RET NZ INC HL INC HL INC HL INC HL INC HL RET
The dIfferent 'key values', held in the BC register pair, are 'decoded' into the usual ZXB1 character codes by looking-up the key table at 007E. (007D + 1) The character code specified as (HL).
07BD DECODE LD D,+00 SRA B SBC A,A OR +26 LD L,+05 SUB L 07C7 KEY-LINE ADD A,L SCF RR C JR C,07C7,KEY-LINE INC C RET NZ LD C,B DEC L LD L,+01 JR NZ,07C7,KEY-LINE LD HL,+007D LD E,A ADD HL,DE SCF RET
The two little routines WRITE-CH & WRITE are the essential parts of the printing subroutine. However before a character can be actually printed it is necessary for S-POSN to be collected and tested and the display eaxpanded if required. The various entry points to the subroutine are involved with the conversion of Hex. codes to ZX81 character codes.
i) Printing digits: 07DC LEAD-SP. LD A,E AND A RET M JR 07Fl,PRINT-CH 07E1 OUT-DIGIT XOR A 07E2 DIGIT-INC ADD HL,BC INC A JR C,07E2,DIGIT-INC SBC HL,BC DEC A JR Z,07DC,LEAD-SP 07EB OUT-CODE LD E,+1C ADD A,E 07EE OUT-CH AND A JR Z,07F5,PRINT-SP ii) Printing characters: 07F1 PRINT-CH. RES 0,(FLAGS) 07F5 PRINT-SP. EXX PUSH HL BIT 1,(FLAGS) JR NZ,0802,LPRINT-A CALL 0808,ENTER-CH JR 0805,PRINT-EXX 0802 LPRINT-A CALL 0851,LPRINT-CH 0805 PRINT-EXX POP HL EXX RET iii) Testing S-POSN: 0808 ENTER-CH LD D,A LD BC,(S-POSN) LD A,C CP +21 JR Z,082C,TEST-LOW 0812 TEST-N/L LD A,+76 CP D JR Z,0847,WRITE-N/L LD HL,(DF-CC) CP (HL) LD A,D JR NZ,083E,WRITE-CH DEC C JR NZ,083A,EXPAND-1 INC HL LD (DF-CC),HL LD C,+21 DEC B LD (S-POSN),BC 082C TEST-LOW LD A,B CP (DF-SZ) JR Z,0835,REPORT AND A JR NZ,08l2,TEST-N/L iv) REPORT-5 - insufficient room: 0835 REPORT-5 LD L,+04 JP 0058,ERROR-3 v) Expand the display: 083A EXPAND-1 CALL 099B,ONE-SPACE EX DE,HL vi) Writing an actual code: 083E WRITE-CH LD (HL),A INC HL LD (DF-CC),HL DEC (S-POSN-lo.) RET vii) Writing a N/L:
This is performed by decrementing the 'line counter' and using LOC.ADDR to give the correct values for DF-CC & S-POSN.
0847 WRITE-N/L LD C,+21 DEC B SET 0,(FLAGS) JP 0918,LOC.-ADDR
Characters are added one by one to the printer buffer. Once the buffer is full, or a N/L character is entered the buffer is emptied.
0851 LPRINT-CH CP +76 JR Z,0871,COPY-BUFF LD C,A LD A,(PR-CC) AND +7F CP +5C LD L,A LD H,+40 CALL Z,0871,COPY-BUFF LD (HL),C INC L LD (PR-CC),L RET
The COPY command routine starts with the D register being loaded with Hex.l6, being the number ot Iines in a full display. The Copy*D routine is then used to output these lines to the printer.
0869 COPY LD D,+16 LD HL,+D-FILE INC HL JR 0876,COPY*D
In COPY-BUFF the D register is only required to be given the value Hex.01.
0871 COPY-BUFF LD D,+01 LD HL,+PRBUFF
In COPY*D a loop is set up with D being the counter.
0876 COPY*D CALL 02E7,SET-FAST PUSH BC 087A COPY-LOOP PUSH HL XOR A LD E,A 087D COPY-TIME OUT (+FB),A POP HL 0880 COPY-BRK CALL 0F46,BREAK-1 JR C,088A,COPY-CONT RRA OUT (+FB),A 0888 REPORT-D2 RST 0008,ERROR-1 DEFB +0C 088A COPY-CONT IN A,(+FB) ADD A,A JP M,08DE,COPY-END JR NC,0880,COPY-BRK PUSH HL PUSH DE LD A,D CP +02 SBC A,A AND E RLCA AND E LD D,A 089C COPY-NEXT LD C,(HL) LD A,C INC HL CP +76 JR Z,08C7,COPY-N/L PUSH HL SLA A ADD A,A ADD A,A LD H,+0F RL H ADD A,E LD L,A RL C SBC A,A XOR (HL) LD C,A LD B,+08 08B5 COPY-BITS LD A,D RLC C RRA LD H,A 08BA COPY-WAIT IN A,(+FB) RRA JR NC,08BA,COPY-WAIT LD A,H OUT (+FB),A DJNZ 08B5,COPY-BITS POP HL JR 089C,COPY-NEXT 08C7 COPY-N/L IN A,(+FB) RRA JR NC,08C7,COPY-N/L LD A,D RRCA OUT (+FB),A POP DE INC E BIT 3,E JR Z,087D,COPY-TIME POP BC DEC D JR NZ,087A,COPY-LOOP LD A,+04 OUT (+FB),A 08DE COPY-END CALL 0207,SlOW-FAST POP BC
The printer buffer is cleared by overwriting it with Hex.00 characters and setting the final location to Hex.76.
08E2 CLEAR-PRB LD HL,+405C LD (HL),+76 LD B,+20 08E9 PRB-BYTES DEC HL LD (HL),+00 DJNZ 08E9,PRB-BYTES LD A,L SET 7,A LD (PR-CC),A RET
This routine checks the validity of the parameters given with the PRINT AT command. If the parameters are invalid an error is signalled otherwise the correct S-POSN & DF-CC is obtained by using the LOC.-ADDR routine.
08F5 PRINT-AT LD A,+17 SUB B JR C,0905,WRONG-VAL 08FA TEST-VAL. CP (DF-SZ) JP C,0835,REPORT-5 INC A LD B,A LD A,+1F SUB C 0905 WRONG-VAL JP C,0EAD,REPORT-B ADD A,+02 LD C,A 090B SET-FIELD BIT 1,(FLAGS) JR Z,0918,LOC.-ADDR The LPRINT AT command sets the value of PR-CC. LD A,+5D SUB C LD (PR-CC),A RET
This important subroutine sets the value of DF-CC for given values of a display location. If the display is collapsed and thereby does not truly hold the position then the required line is expanded.
0918 LOC.-ADDR LD (S-POSN),BC LD HL,(VARS) LD D,C LD A,+22 SUB C LD C,A LD A,+76 INC B 0927 LOOK-BACK DEC HL CP (HL) JR NZ,0927,LOOK-BACK DJNZ 0927,LOOK-BACK INC HL CPIR DEC HL LD (DF-CC),HL SCF RET PO DEC D RET Z PUSH BC CALL 099E,MAKE-ROOM POP BC LD B,C LD H,D LD L,E 0940 EXPAND-2 LD (HL),+00 DEC HL DJNZ 0940,EXPAND-2 EX DE,HL INC HL LD (DF-CC),HL RET
The character codes that are considered to be tokens are expanded using this subroutine. The address of each 'expanded token' in the 'token table' is found using TOKEN-ADD. The leading space is printed if specified by bit 0 of FLAGS, the letters of the token-word are then printed and a trailing space is added if needed.
094B TOKENS PUSH AF CALL 0975,TOKEN-ADD JR NC,0959,ALL-CHARS BIT 0,(FLAGS) JR NZ,0959,ALL-CHARS XOR A RST 0010,PRINT-A 0959 ALL-CHARS LD A,(BC) AND +3F RST 0010,PRINT-A LD A,(BC) INC BC ADD A,A JR NC,0959,ALL-CHARS POP BC BIT 7,B RET Z CP +1A JR Z,096D,TRAIL-SP. CP +38 RET C 096D TRAIL-SP. XOR A SET 0,(FLAGS) JP 07F5,PRINT-SP.
In TOKEN-ADD the base address of the TOKEN TABLE is hex.0111. The words in this table are found in turn and when the required word has been located a return is made with BC pointing to the start of the word.
0975 TOKEN-ADD PUSH HL LD HL,+0111 BIT 7,A JR Z,097F,TEST-HIGH AND +3F 097F TEST-HIGH CP +43 JR NC,0993,FOUND LD B,A INC B 0985 WORDS BIT 7,(HL) INC HL JR Z,0985,WORDS DJNZ 0985,WORDS BIT 6,A JR NZ,0992,COMP-FLAG CP +18 0992 COMP-FLAG CCF 0993 FOUND LD B,H LD C,L POP HL RET NC LD A,(BC) ADD A,+E4 RET
Whenever a single space is required in the program area or the display file then this subroutine is called.
099B ONE-SPACE LD BC,+0001
This routine creates BC spaces from the location (HL).
099E MAKE-ROOM PUSH HL CALL 0EC5,TEST-ROOM POP HL CALL 09AD,POINTERS LD HL,(STKEND) EX DE,HL LDDR RET
Whenever some of the pointers require to be changed this subroutine is called with the amount of change in BC, and HL determining which pointers are to be changed. All pointers that point lower than HL will not be altered.
09AD POINTERS PUSH AF PUSH HL LD HL,+D-FILE LD A,+09 09B4 NEXT-PTR LD E,(HL) INC HL LD D,(HL) EX (SP),HL AND A SBC HL,DE ADD HL,DE EX (SP),HL JR NC,09C8,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 09C8 PTR-DONE INC HL DEC A JR NZ,09B4,NEXT-PTR EX DE,HL POP DE POP AF AND A SBC HL,DE LD B,H LD C,L INC BC ADD HL DE EX DE,HL RET
For a given BASIC line number this subroutin=B7 will return the starting address of the actual line (ar~d the Z flag set) or the starting addres of the following line if it does not exist (C reset).
09D8 LINE-ADDR PUSH HL LD HL,+PROGRAM LD D,H LD E,L 09DE NEXT-TEST POP BC CALL 09EA,CP-LINES RET NC PUSH BC CALL 09F2,NEXT-ONE EX DE,HL JR 09DE,NEXT-TEST
The line number in (HL) is compared to the the number in BC.
09EA CP-LINES LD A,(HL) CP B RET NZ INC HL LD A,(HL) DEC HL CP C RET
This subroutine very cleverly finds the start of the next BASIC line or the start of the next variable in the variable area. Line numbers are identified by the high byte being less than Hex.40, and the different types of variables are identified by their differing bits 6 & 7.
09F2 NEXT-ONE PUSH HL LD A,(HL) CP +40 JR C,0A0F,LINES BIT 5,A JR Z,0A10,BIT-5-NIL ADD A,A JP M,0A01,NEXT+FIVE CCF 0A01 NEXT+FIVE LD BC,+0005 JR NC,0A08,NEXT-LETT LD C,+11 0A08 NEXT-LETT RLA INC HL LD A,(HL) JR NC,0A08,NEXT-LETT JR 0A15,NEXT-ADD 0A0F LINES INC HL 0A70 BIT-5-NIL INC HL LD C,(HL) INC HL LD B,(HL) INC HL 0A15 NEXT-ADD ADD HL,BC POP DE
This subroutine finds the difference in value between the contents of the HL and DE register pairs. The result is returned in the BC reglster pair.
0A17 DIFFER AND A SBC HL,DE LD B,H LD C,L ADD HL,DE EX DE,HL RET
The lines of the 'lower' screen are cleared by this subroutine.
0A1F LINE-ENDS LD B,(DF-SZ) PUSH BC CALL 0A2C,B-LINES POP BC DEC B JR 0A2C,B-LINES
i) The B register is loaded with Hex.lB, the number of lines in Ihe display file.
0A2A CLS LD B,+18
ii) The address of the start of that part of the display file that is to be cleared is found and a test is made to see if more, or less, than 3 1/4 K. of RAM is fitted.
0A2C B-LINES RES 1,(FLAGS) LD C,+21 PUSH BC CALL 0918,LOC.-ADDR POP BC LD A,(RAMTOP-hi.) CP +4D JR C,0A52,COLLAPSED
iii) As an expanded display file is required, a suitable number of spaces is printed so as to clear the specified number of lines.
SET 7,(S-POSN-hi.) 0A42 CLEAR-LOC XOR A CALL 07F5,PRINT-SP. LD HL,(S-POSN) LD A,L OR H AND +7E JR NZ,0A42,CLEAR-LOC JP 0918,LOC.-ADDR
iv) As a collapsed display file is required a LDIR instruction is used to copy a N/L character the number of times specified in the C register (formerly B). The system variable VARS is then found and excess memory reclaimed.
0A52 COLLAPSED LD D,H LD E,L DEC HL LD C,B LD B,+00 LDIR LD HL,(VARS)
The pointers are first changed and then the specified area of RAM is reclaimed by using a LDIR instruction to overwrite the unwanted part of the RAM contents.
0A5D RECLAIM-1 CALL 0A17,DIFFER 0A60 RECLAIM-2 PUSH BC LD A,B CPL LD B,A LD A,C CPL LD C,A INC BC CALL 09AD,POINTERS EX DE,HL POP HL ADD HL,DE PUSH DE LDIR POP HL RET
This routine is used to find out whether the current E-Line starts with a valid line number. i.e.1-9999. The pointer CH-ADD is used temporarily to point along the E-LINE. A return is made if the INPUT command is being executed. The INT-TO-FP routine is called to collect the possible number and the FP-TO-BC routine called to form an integer value. The value is then tested against dec.0-10,000. The subroutine returns via the SET-MEM subroutine that resets STKEND
0A73 E-LINE-NO LD HL,(E-LINE) CALL 004D,TEMP-PTR RST 0018,GET-CH. BIT 5,(FLAGX) RET NZ LD HL,+MEMBOT LD (STKEND),HL CALL 1548,INT-TO-FP CALL 158A,FP-TO-BC JR C,0A91,NO-NUMBER LD HL,+D8F0 ADD HL,BC 0A91 NO-NUMBER JP C,0D9A,REPORT-C CP A JP 14BC,SET-MEM
The OUT-NUM. entry point is used to print the error report line numbers and the OUT-NO. entry point is used for printing line numbers at the start of BASIC lines.
0A98 OUT-NUM. PUSH DE PUSH HL XOR A BIT 7,B JR NZ,0ABF,UNITS LD H,B LD L,C LD E,+FF JR 0AAD,THOUSAND 0AA5 OUT-NO. PUSH DE LD D,(HL) INC HL LD E,(HL) PUSH HL EX DE,HL LD E,+00 0AAD THOUSAND LD BC,+FC18 CALL 07E1,OUT-DIGIT LO BC,FF9C CALL 07E1,OUT-DIGIT LD C,+F6 CALL 07E1,OUT-DIGIT LD A,L 0ABF UNITS CALL 07EB,OUT-CODE POP H L POP DE RET
Bit 7 of FLAGS is set during the execution of a BASIC line but reset during syntax checking. This subroutine calls SYNTAX-Z and then either simply returns using a JP (HL) instruction during the execution of a BASIC line, or uses a RET Z instruction to 'return' to the address above on the stack during syntax checking.
0AC5 UNSTACK-Z CALL 0DA6,SYNTAX-Z POP HL RET Z JP (HL)
Bit 1 of FLAGS is set whenever a LPRINT command is executed.
0ACB LPRINT SET 1,(FLAGS)
This routine is fairly complex but fortunately it can be broken into simple parts.
i) Test for PRINT alone.
0ACF PRINT LD A,(HL) CP +76 JP Z,0B84,PRINT-END
ii) A loop is now set up to deal with each constituent part of a PRINT line. First, the next character is tested to see if it is a 'comma' or a 'semi-colon'.
0AD5 PRINT-1 SUB +1A ADC A,+00 JR Z,0B44,SPACING
iii) If the next character is an 'AT' it is dealt with as follows:
Test for 'AT'
CP +A7 JR NZ,0AFA,NOT-AT
The next character is collected.
RST 0020,NEXT-CH.
The next expression is identified.
CALL 0D92,CLASS-6
A test is made for the correct separator - a comma.
CP +1A JP NZ,0D9A,REPORT-C
The next character is collected.
RST 0020,NEXT-CH
The next expression is identitied.
CALL 0D92,CLASS-6
A test is made to see if a line is being executed or syntax is being checked. An indirect jump is made to PRINT-ON if syntax is being checked.
CALL 0B4E,SYNTAX-ON
The particulars of the two expressions are both on the calculator stack but they need to be switched over. This is done using a RST 0028 instruction and the literal 01.
RST 0028,FP-CALC. DEFB +01 (exchange, 1A72) DEFB +34 (end-calc.,002B)
The two expressions on the stack are then 'loaded' into the BC register pair by calling STK-TO-BC.
CALL 0BF5,STK-TO-BC
With the PRINT AT parameters now in BC the usual routine can be called to set DF-CC & S-POSN and a jump is then made to PRINT-ON
CALL 08F5,PRINT-AT JR 0B37,PRINT-ON
iv) If the next character is a 'TAB' it is dealt wit as follows: Test for 'TAB'
0AFA NOT-AT CP +A8 JR NZ,0B31,NOT-TAB
The single 'following expression' is collected. The syntax flag is checked and the value of the expression 'loaded' into the A register.
RST 0020,NEXT-CH. CALL 0D92,CLASS-6 CALL 0B4E,SYNTAX-QN CALL 0C02,STK-TO-A
The 'parameter' is then tested and the new values of DF-CC & S-POSN are found by calling TEST-VAL.
JP NZ,0EAD,REPORT-8 AND +1F LD C,A BIT 1,(FLAGS) JR Z,0B1E,TAB-TEST SUB (PR-CC) SET 7,A ADD A,+3C CALL NC,0871,COPY-BUFF 0B1E TAB-TEST ADD A,(S-POSN-lo.) CP +21 LD A,(S-POSN-hi.) SBC A,+01 CALL 08FA,TEST-VAL SET 0,(FLAGS) JR 0B37,PRINT-ON
v) The expression that comes next is collected and printed by using the PRINT-STK subroutine.
0B31 NOT-TAB CALL 0F55,SCANNING CALL 0B55,PRINT-STK
vi) The routine now proceeds to check for another expression.
0B37 PRINT-ON RST 0018,GET-CH. SUB +1A ADC A,+00 JR Z,0B44,SPACING CALL 0D1D,CHECK-END JP 0B84,PRINT-END
vii) The two characters 'comma & semi-colon' are now separated.
0B44 SPACING CALL NC,0B8B,FIELD RST 0020,NEXT-CH. CP +76 RET Z JP 0AD5,PRINT-1
viii) The SYNTAX-ON subroutine causes a jump to PRINT-ON if syntax is being checked.
0B4E SYNTAX-ON CALL 0DA6,SYNTAX-Z RET NZ POP HL JR 0B37,PRINT-ON
ix) The PRINT-STK routine collects the details of a string from the calculator stack. A number is dealt with by jumping to PRINT-FP, whereas a string is dealt with in the 'print string' section. First the syntax flag is read.
0B55 PRINT-STK CALL 0AC5,UNSTACK-2 BIT 6,(FLAGS) CALL Z,13F8,STK-FETCH JR Z,0B6B,PR-STR-4 JP 15DB,PRINT-FP
x) The string printing routine.
The length of the string is held in the BC register pair and the starting address of the string is held in the DE register pair.
0B64 PR-STR-1 LD A,+0B 0B66 PR-STR-2 RST 0010,PRINT-A 0B67 PR-STR-3 LD DE,(X-PTR) 0B6B PR-STR-4 LD A,B OR C DEC BC RET Z LD A,(DE) INC DE LD (X-PTR),DE BIT 6,A JR Z,0B66,PR-STR-2 CP +C0 JR Z,0B64,PR-STR-1 PUSH BC CALL 094B,TOKENS POP BC JR 0B67,PR-STR-3
xi) The PRINT-END routine.
The syntax flag is read and a N/L character is printed during line execution.
0B84 PRINT-END CALL 0AC5,UNSTACK-Z LD A,+76 RST 0010,PRINT-A RET
xii) The FIELD subroutine.
The appropriate value of S-POSN (and PR-CC if required) is found.
0B8B FIELD CALL 0AC5,UNSTACK-Z SET 0,(FLAGS) XOR A RST 001O,PRINT-A LD BC,(S-POSN) LD A,C BIT 1,(FLAGS) JR Z,0BA4,CENTRE LD A,+5D SUB (PR-CC) 0BA4 CENTRE LD C,+11 CP C JR NC,0BAB,RIGHT LD C,+01 0BAB RIGHT CALL 090B,SET-FIELD RET
Initially the x & y co-ordinates are fetched and tested. Then they are converted to row & column numbers. The value formed in the A register distinguishes which pixel is being identified.
0BAF PLOT/UNP. CALL 0BF5,STK-TO-BC LD (COORDS),BC LD A,+2B SUB B JP C,0EAD,REPORT-B LD B,A LD A,+01 SRA B JR NC,0BC5,COLUMNS LD A, + 04 0BC5 COLUMNS SRA C JR NC,0BCA,FIND-ADDR RLCA 0BCA FIND-ADDR PUSH AF CALL 08F5,PRINT-AT LD A,(HL) RLCA CP +10 JR NC,0BDA,TABLE-PTR RRCA JR NC,0BD9,SQ-SAVED XOR +8F 0BD9 SQ-SAVED LD B,A 0BDA TABLE-PTR LD DE,+0C9E
The two operations of PLOTting and UNPLOTting are distinguished by referring to T-ADDR and comparing the value against the constant 0C9E that is the value of the address of the UNPLOT command in the syntax table.
LD A,(T-ADDR) SUB E JP M,0BE9,PLOT POP AF CPL AND B JR 0BEB,UNPLOT 0BE9 PLOT POP AF OR B 0BEB UNPLOT CP +08 JR C,0BF1,PLOT-END XOR +8F 0BF1 PLOT-END EXX RST 0010,PRINT-A EXX RET
This subroutine 'loads' two floating point numbers into the BC register pair. The subroutine is therefore used to pick up parameters in the range 00-FF.
0BF5 STK-TO-BC CALL 0C02,STK-TO-A LD B,A PUSH BC CALL 0C02,STK-TO-A LD E,C POP BC LD D,C LD C,A RET
This subroutine 'loads' the A register with the floating point number held at the top of the calculator stack. The number must be in the range 00-FF.
STK-TO-A CALL 15CD,FP-TO-A JP C,0EAD,REPORT-B LD C,+01 RET Z LD C,+FF RET
The first part of the routine sets the correct values of DF-CC and S-POSN to allow for the next printing to occur at the start of the bottom line + 1.
Next the end address of the first line in the display file is identified and the whole of the display file moved to overwrite this line.
0C0E SCROLL LD B,(DF-SZ) LD C,+21 CALL 0918,LOC.-ADDR CALL 099B,ONE-SPACE LD A,(HL) LD (DE),A INC (S-POSN-hi.) LD HL,(D-FILE) INC HL LD D,H LD E,L CPIR JP 0A5D,RECLAIM-1
i) The offset table.
There is an offset value for each of the BASIC commands and by adding this offset to the value of the address where it is found, the correct address for the command in the parameter table is obtained.
0C29 8B LPRINT 0CB4 0C2A 8D LLIST 0CBl 0C2B 2D STOP 0C58 0C2C 7F SLOW 0CAB 0C2D 81 FAST 0CAE 0C2E 49 NEW 0C77 0C2F 75 SCROLl 0CA1 0C30 5F CONT 0CBF 0C31 40 DIM 0C71 0C32 42 REM 0C74 0C33 2B FOR 0C5E 0C34 17 GOTO 0C4B 0C35 1F GOSUB 0C54 0C36 37 INPUT 0C6D 0C37 52 LOAD 0C89 0C38 45 LIST 0C7D 0C39 0F LET 0C48 0C3A 6D PAUSE 0CA7 0C3B 2B NEXT 0C66 0C3C 44 POKE 0C80 0C3D 2D PRINT 0C6A 0C3E 5A PLOT 0C98 0C3F 3B RUN 0C7A 0C40 4C SAVE 0C8C 0C41 45 RAND 0C86 0C42 0D IF 0C4F 0C43 52 CLS 0C95 0C44 5A UNPLOT 0C9E 0Cd5 4D CLEAR 0C92 0C46 15 RETURN 0C5B 0C47 6A COPY 0CB4
ii) The parameter table.
For each of the BASIC commands there are between 3 & 8 entries in the parameter table. The command classes for each of the commands are given, together with the required separators and these are followed by the address of the appropriate routine.
0C48 P-LET 01 CLASS-1 14 '=' 02 CLASS-2 0C4B P-GOTO 06 CLASS-6 00 CLASS-0 81 0E GOTO,0EB1 0C4F P-IF 06 CLASS-6 DE 'THEN' 05 CLASS-5 AB 0D IF,0DAB 0C54 P-GOSUB 06 CLASS-6 00 CLASS-0 B5 0E GOSUB,0EB5 0C58 P-STOP 00 CLASS-0 DC 0C STOP,0CDC 0C5B P-RETURN 00 CLASS-0 D8 0E RETURN,0EDB 0C5E P-FOR 04 CLASS-4 1A '=' 06 CLASS-6 DF 'TO' 06 CLASS-6 05 CLASS-5 B9 0D FOR,0DB9 0C66 P-NEXT 04 CLASS-4 00 CLASS-0 2E 0E NEXT,0E2E 0C6A P-PRINT 05 CLASS-5 CF 0A PRINT,0ACF 0C6D P-INPUT 01 CLASS-1 00 CLASS-0 E9 0E INPUT,0EE9 0C71 P-DIM 05 CLASS-5 09 14 DIM,1409 0C74P-REM 05 CLASS-5 6A 0D REM,0D6A 0C77 P-NEW 00 CLASS-0 C3 03 NEW-03C3 0C7A P-RUN 03 CLASS-3 AF 0E RUN,0EAF 0C7D P-LIST 03 CLASS-3 30 07 LIST,0730 0C80 P-POKE 06 CLASS-6 1A ',' 06 CLASS-6 92 0E POKE,0E92 0C86 P-RAND 03 CLASS-3 6C 0E RAND,0E6C 0C89 P-LOAD 05 CLASS-5 40 03 LOAD,0340 0CBC P-SAVE 05 CLASS-5 F6 02 SAVE,02F6 0CBF P-CONT 00 CLASS-0 7C 0E CONT,0E7C 0C92 P-CLEAR 00 CLASS-0 9A 14 CLEAR,l49A 0C95 P-CLS 00 CLASS-0 2A 0A CLS,0A2A 0C98 P-PLOT 06 CLASS-6 1A ',' 06 CLASS-6 00 CLASS-0 AF 0B PLOT/UNP.,0BAF 0C9E P-UNPLOT 06 CLASS-6 1A ',' 06 CLASS-6 00 CLASS-0 AF 0B PLOT/UNP.,0BAF 0CA4 P-SCROLL 00 CLASS-0 0E UC SCROLL,0C0E 0CA7 P-PAUSE 06 CLASS-6 00 CLASS-0 32 0F PAUSE,0F32 0CAB P-SLOW 00 CLASS-0 2B 0F SLOW,0F2B 0CAE P-FAST 00 CLASS-0 23 0F FAST,0F23 0CB1 P-COPY 00 CLASS-0 69 08 COPY,0869 0CB4 P-LPRINT 05 CLASS-5 CB 0A LPRINT,0ACB 0CB7 P-LLIST 03 CLASS-3 2C 07 LLIST,072C
The BASIC interpreter scans each line for BASIC commands and as each one is found the appropriate command routine is followed. The different parts of the routine are: i) The LINE-SCAN entry point leads to the line number being checked for validity.
0CBA LINE-SCAN LD (FLAGS),+01 CALL 0A73,E-LINE-NO
ii) The LINE-RUN entry point is used when replying to an INPUT prompt and this fact has be identified.
0CC1 LINE-RUN CALL 14BC,SET-MEM LD HL,+ERR-NR LD (HL),+FF LD HL,+FLAGX BIT 5,(HL) JR Z,0CDE,LINE-NULL
iii) The INPUT reply is tested to see if STOP was entered.
CP +E3 LD A,(HL) JP NZ,0D6F,INPUT-RE CALL 0DA6,SYNTAX-2 RET Z
iv) If appropriate, report D is given.
RST 0008,ERROR-1 DEFB +0C
The only action is to give report 9.
0CDC STOP RST 0008,ERROR-1 DEFB +08
v) A return is made if the line is 'null'. 0CDE LINE-NULL RST 0018,GET-CH. LD B,+00 CP +76 RET Z
vi) The first character is tested so as to check that it is a command.
LD C,A RST 0020,NEXT-CH. LD A,C SUB +E1 JR C,0D26,REPORT-C
vii) The offset for the command is found from the offset table.
LD C,A LD HL,+0C29 ADD HL,BC LD C,(HL) ADD HL,BC JR 0CF7,GET-PARAM
viii) The parameters are fetched in turn by a loc that returns to 0CF4. The separators are identitied by the test against +0B.
0CF4 SCAN-LOOP LD HL,(T-ADDR) 0CF7 GET-PARAM LD A,(HL) INC HL LD (T-ADDR),HL LD BC,+0CF4 PUSH BC LD C,A CP +0B JR NC,0D10,SEPARATOR
ix) The address of the command class routine obtained by reference to the command class table at 0D16. A jump is made to the appropriate routine.
LD HL,+0D16 LD B,+00 ADD HL,BC LD C,(HL) ADD HL,BC PUSH HL RST 0018,GET-CH . RET
x) The correctness of the separator is simply tested by the following routine.
0D10 SEPARATOR RST 0018,GET-CH. CP C JR NZ,0D26,REPORT-C RST 0020,NEXT-CH. RET
The addresses for the seven different command classes are found from this table.
0D16 17 CLASS-0,0D2D 0D17 25 CLASS-1,0D3C 0D18 53 CLASS-2,0D6B 0D19 0F CLASS-3,0D28 0D1A 6B CLASS-4.0D85 0D1B 13 CLASS-5,0D2E 0D1C 7B CLASS-6,0D92
Line scanning is finished when the N/L character is reached.
0D1D CHECK-END CALL 0DA6,SYNTAX-Z RET NZ POP BC 0D22 CHECK-2 LD A,(HL) CP +76 RET Z 0D26 REPORT-C2 JR 0D9A,REPORT-C
The commands RUN, LIST, RAND and LLIST can be followed by a N/L or a number.
0D28 CLASS-3 CP +76 CALL 0D9C,NO-TO-STK
An entry here will cause the zero flag to be set prior to a call to CHECK-END.
0D2D CLASS-0 CP A
The commands IF, FOR, PRINT, DIM, REM, LOAD, SAVE and LPRINT all have class 5 as their last command class. A jump is made to the command routine directly.
0D2E CLASS-5 POP BC CALL Z,0D1D,CHECK-END EX DE,HL LD HL,(T-ADDR) LD C,(HL) INC HL LD B,(HL) EX DE,HL 0D3A CLASS-END PUSH BC RET
The commands LET and INPUT both require that a variable be specified. The command class 1 routine collects the details of the variable and stores them in the required places.
0D3C CLASS-1 CALL 111C,LOOK-VARS 0D3F CLASS-4-2 LD (FLAGX),+00 JR NC,0D4D,SET-STK SET 7,(FLAGX) JR NZ 0D63,SET-STRLN 0D4B REPORT-2 RST 0008,ERROR-1 DEFB +01 0D4D SET-STK CALL Z,11A7,STK-VAR BIT 6,(FLAGS) JR NZ,0D63,SET-STRLN XOR A CALL 0DA6,SYNTAX-Z CALL NZ,13F8,STK-FETCH LD HL,+FLAGX OR (HL) LD (HL),A EX DE,HL 0D63 SET-STRLN LD (STRLEN),BC LD (DEST),HL 0D6A REM RET
The value assigned to a variable in a LET command or in reply to an INPUT prompt is evaluated by calling SCANNING. If the value is appropriate then an indirect jump is made to the LET routine at 1321.
0D6B CLASS-2 POP BC LD A,(FLAGS) 0D6F INPUT-REP PUSH AF CALL 0F55,SCANNING POP AF LD BC,+1321 LD D,(FLAGS) XOR D AND +40 JR NZ,0D9A,REPORT-C BIT 7,D JR NZ,0D3A,CLASS-END JR 0D22,CHECK-2
The specified variable for the FOR and the NEXT commands are dealt with by this routine. Only single character variables are allowed and these are identified by their having both bits 5 & 6 set.
0D85 CLASS-4 CALL 111C,LOOK-VARS PUSH AF LD A,C OR +9F INC A JR NZ,0D9A,REPORT-C POP AF JR 0D3F,CLASS-4-2
CLASS-6 denotes that the following expression must yield an integer value. The SCANNlNG routine evaluates the expressic and a numeric value will give bit 6 of FLAGS set.
0D92 CLASS-6 CALL 0F55,SCANNING BIT 6,(FLAGS) RET NZ REPORT-C - no numeric value. 0D9A REPORT-C RST 0008,ERROR-1 DEFB +0B
During execution of a line this routine leads to number being placed on the calculator stack. If the zero flag is reset on entry the number put on the stack will be the result of evaluating the 'next' expression, but if the zero flag is set then zero will be placed on the stack by using a RST 0028 instruction.
0D9C NO-TO-STK JR NZ,0D92,CLASS-6 CALL 0DA6,SYNTAX-Z RET Z RST 0028,FP-CALC. DEFB +A0 (stk-zero, 1A51) DEFB +34 (end-calc.,002B) RET
A simple test of bit 7 of FLAGS will give the zero flag reset during execution and set during syntax checking. i.e. SYNTAX gives Z set.
0DA6 SYNTAX-Z BIT 7,(FLAGS) RET
At this point the value of the expression between the 'IF' and the 'THEN' is known, and is on the top of the calculator stack. During execution the result is deleted from the stack but the pointer DE is still available. The logical value of (DE) is tested and a return made if zero otherwise the routine jumps to LINE-NULL to execute the rest of the line.
0DAB IF CALL 0DA6,SYNTAX-Z JR Z,0DB6,IF-END RST 0028,FP-CALC. DEFB +02 (delete,19E3) DEFB +34 (end-calc.,002B) LD A,(DE) AND A RET Z 0DB6 IF-END JP 0CDE,LINE-NULL
This routine is made up of the following parts:
i) If a STEP variable is given then this is found and put on the stack, otherwise the value one is used.
0DB9 FOR CP +EO JR NZ,0DC6,USE-ONE RST 0020,NEXT-CH. CALL 0D92,CLASS-6 CALL 0D1D,CHECK-END JR 0DCC,REORDER 0DC6 USE-ONE CALL 0D1D,CHECK-END RST 0028,FP-CALC. DEFB +A1 (stk-one,1A51) DEFB +34 (end-calc.,002B)
ii) The top three values on the stack, the 'value', the 'limit' & the 'step' are re-ordered to give 'limit-step-value'.
0DCC REORDER RST 0028,FP-CALC. DEFB +C0 (st-mem-0,1A63) DEFB +02 (delete,19E3) DEFB +01 (exchange,1A72) DEFB +E0 (get-mem-O,1A451 DEFB +01 (exchange,1A72) DEFB +34 (end-calc,002B)
iii) The LET routine is used to locate an address in the VARS area for the FOR variable. If the variable already exists then it is overwritten, if not then the variable is added to the end of the VARS. The 'limit' & the 'step' are then transferred.
CALL 1321,LET LD (MEM),HL DEC HL LD A,(HL) SET 7,(H L) LD BC,+0006 ADD HL,BC RLCA JR C,0DEA,LMT+STEP SLA C CALL 099E,MAKE-ROOM INC HL 0DEA LMT+STEP PUSH HL RST 0028,FP-CALC. DEFB +02 (delete,l9E3) DEFB +02 (delete,19E3) DEFB +34 (end-calc.,0028) POP HL EX DE,HL LD C,+0A LDIR
iv) The current line number is fetched, incremented and added to the variable.
LD HL,(PPC) EX DE,HL INC DE LD (HL),E INC HL LD (HL),D
v) The NEXT-LOOP subroutine is called to check that a 'looping' is possible. If it is not possible then NXTLIN is set to the appropriate line number for jumping over the whole of the FOR-NEXT loop.
CALL 0E5A,NEXT-LOOP RET NC BIT 7,(PPC-hi.) RET NZ LD B,(STRLEN) RES 6,B LD HL (NXTLIN) 0E0E NXTLIN-NO LD A,(HL) AND +C0 JR NZ,0E2A,FOR-END PUSH BC CALL 09F2,NEXT-ONE POP BC INC HL INC HL INC HL CALL 004C,CURSOR-SO RST 0018,GET-CH. CP +F3 EX DE,HL JR NZ,0E0E,NXTLIN-NO EX DE,HL RST 0020,NEXT-CH EX DE,HL CP B JR NZ,0E0E,NXTLIN-NO 0E2A FOR-END LD (NXTLINE,HL RET
In this routine the address of the variable is collected from DEST. Next MEM is loaded with this address so that a RST 0028 instruction can be used to manipulate the different parts of the variable when the 'step' is added to the 'value'.
0E2E NEXT BIT 1,(FLAGX) JP NZ,0D4B,REPORT-2 LD HL,(DEST) BIT 7,(HL) JR Z,0E58,REPORT-1 INC HL LD (MEM),HL RST 0028,FP-CALC. DEFB +E0 (get-mem-0,1A45) DEFB +E2 (get-mem-2,1A45) DEFB +0F (addition,1755) DEFB +C0 (st-mem-0,1A63) DEFB +02 (delete,19E3) DEFB +34 (end-calc.,002B) CALL 0E5A,NEXT-LOOP RET C
An indirect jump is now made to the line number given in the last two bytes of the variable.
LD HL,(MEM) LD DE,+000F ADD HL,DE LD E,(HL) INC HL LD D,(HL) EX DE,HL JR 0E86,GOTO-2
0E58 REPORT-1 RST 0008,ERROR-1 DEFB +00
This subroutine is called by both the 'FOR' and the 'NEXT' command routines.
When called by the 'FOR' routine it determines whether or not a jump past the whole of the FOR-NEXT loop is to be made.
When called by the 'NEXT' command routine it determines whether another loop is, or is not, possible.
The routine tests the 'step' and then compares the 'limit' and the 'value'. The carry flag is set, or reset, as required.
0E5A NEXT-LOOP RST 0028,FP-CALC. DEFB +E1 (get-mem-1,1A45) DEFB +E0 (get-mem-0,1A45) DEFB +E2 (get-mem-2,1A45) DEFB +32 (less-0,1ACE) DEFB +00 (jump-true 1C2F) DEFB +02, to 0E62 DEFB +01 (exchange,1A72) 0E62 LMT-V-VAL DEFB +03 (subtract,174C) DEFB +33 (greater-0,1ADB) DEFB +00 (jump-true,1C2F) DEFB +04, to 0E69 DEFB +34 (end-calc.,002B) AND A RET 0E69 IMPOSS. DEFB +34 (end-calc,002B) SCF RET
The FIND-INT. subroutine is called to show whether a number was given with the RAND command. If not then FRAMES is used.
0E6C RAND CALL 0EA7,FIND-INT LD A,B OR C JR NZ,0E77,SET-SEED LD BC,(FRAMES) 0E77 SET-SEED LD (SEED),BC RET
The value of OLDPPC is fetched and used.
0E7C CONT LD HL,(OLDPPC) JR 0E86,GOTO-2
The line number is collected, tested and then passed to LINE-ADDR. The address returned is loaded into NXTLIN.
0E81 GOTO CALL 0EA7,FIND-INT. LD H,B LD L,C 0E86 GOTO-2 LD A,H CP +F0 JR NC,0EAD,REPORT-B CALL 09DB,LINE-ADDR. LD (NXTLIN),HL RET
The value to be entered is collected from the stack using FP-TO-A and the address of the location to be filled is collected using FIND-INT.
0E92 POKE CALL 15CD,FP-TO-A JR C,0EAD,REPORT-B JR Z,0E9B,POKE-SAVE NEG 0E9B POKE-SAVE PUSH AF CALL 0EA7,FIND-INT. POP AF BIT 7,(ERR-NR) RET Z LD (BC),A RET
The integer value of the floating point number on the top of the stack is found. Report B is given if the value exceeds 65535 decimal.
0EA7 FIND-INT. CALL 158A,FP-TO-BC JR C,0EAD,REPORT-B RET Z
0EAD REPORT-B RST 0008,ERROR-1 DEFB +0A
The line number is determined and a jump made to the CLEAR command routine.
0EAF RUN CALL 0E81,GOTO JP 149A,CLEAR
The current line number is fetched, incremented and stacked. The line number of the subroutine is determined and the registers set up for the TEST-ROOM subroutine.
0EB5 GOSUB LD HL,(PPC) INC HL EX (SP),HL PUSH HL LD (ERR-SP),SP CALL 0E81,GOTO LD BC,+0006
This subroutine tests the value of STKEND against the stack pointer allowing 36 bytes for other variables. Report 4 is given if there is insufficient room.
0EC5 TEST-ROOM LD HL,(STKEND) ADD HL,BC JR C,0ED3,REPORT-4 EX DE,HL LD HL,+0024 ADD HL,DE SBC HL,SP RET C
0ED3 REPORT-4 LD L,+03 JP 0058,ERROR-3
The 'return' line number is taken off the 'gosub stack' and tested to show that it is a real line number. Report 7 is given if there is a mistake.
0ED8 RETURN POP HL EX (SP),HL LD A,H CP +3E JR Z,0EE5,REPORT 7 LD (ERR-SP),SP JR 0E86,GOTO-2
The Stack is restored and report 7 given.
0EE5 REPORT-7 EX (SP),HL PUSH HL RST 0008,ERROR-1 DEFB +06
A test for report 8 is made and the workspace is cleared. Then the appropriate prompt characters are printed and the cursor marker added Finally a jump to LOWER is made so that the edit-line can be printed.
0EE9 INPUT BIT 7,(PPC-hi.) JR NZ,0F21,REPORT-8 CALL 14A3,X-TEMP LD HL,+FLAGX SET 5,(HL) RES 6,(HL) LD A,(FLAGS) AND +40 LD BC,+0002 JR NZ,0F05,PROMPT LD C,+04 0F05 PROMPT OR (HL) LD (HL),A RST 0030,BC-SPACES LD (HL),+76 LD A,C RRCA RRCA JR C,0F14,ENTER-CUR LD A,+0B LD (DE),A DEC HL LD (HL),A 0F14 ENTER-CUR DEC HL LD (HL),+7F LD HL,(S-POSN) LD (T-ADDR),HL POP HL JP 0472,LOWER
0F21 REPORT-8 RST 0008,ERROR-1 DEFB +07
The SET-FAST routine is called to reset bit 7 of CDFLAG, and then bit 6 is reset.
0F23 FAST CALL 02E7,SET-FAST RES 6,(CDFLAG) RET
The 'true' slow/fast flag - bit 6 of CDFLAG is set and a jump made to SLOW/FAST that copies this flag to bit 7 for compute and display.
0F2B SLOW SET 6,(CDFLAG) JP 0207,SLOW/FAST
The parameter of the PAUSE command is determined. Fast mode is selected for the period of the PAUSE and the DISPLAY-P routine called.
On returning the correct mode, SLOW or FAST is selected and the value of FRAMES-hi set to hex.FF. A jump to D-BOUNCE is then made.
Note: In the 'unimproved' ROM the value given to FRAMES-hi. was determined by a SET 7, (FRAMES-hi.) instruction and this failed to ensure that the 15th. bit of FRAMES would remain set as the first action of DISPLAY routine is to decrement FRAMES.
0F32 PAUSE CALL 0EA7,FIND-INT. CALL 02E7,SET-FAST LD H,B LD L,C CALL 022D,DISPLAY-P CALL 0207,SLOW/FAST LD (FRAMES-hi.),+FF JR 0F4B,D-BOUNCE
The 'break' key is tested.
0F46 BREAK-1 LD A,+7F IN A,(+FE) RRA
The system variable is set to its required value of Hex.FF.
0F55 SCANNING 111C LOOK-VARS 11A7 STK-VAR 1321 LET 13F8 STK-FETCH 1488 RESERVE 149A CLEAR 14A3 X-TEMP 14A6 SET-STK-B l4AD CURSOR-IN l4BC SET-MEM 1548 INT-TO-FP 158A FP-TO-BC 15CD FP-TO-A 15DB PRINT-FP 199D CALCULATE
NB: OCR'd and HTML'd quickly, not completely proof-read yet! Many corrections still needed. You can check against the original scans (3.7 MBytes)
CONTENTS PAGE The 'flow diagram for PART B' 1 The 'listing' SCANNING 2 LOOK-VARS 9 STK-VAR 11 LET 18 DIM 22 'utility subroutines' 24 PRINT-FP 31 CALCULATOR 49 The Appendix BASIC programs for the main series 76 Index 82
Note: Readers of this book who are using machines fitted with the 'unimproved' ROM will have to bear the following points in mind.
The production of this book has only been possible because of the immense help given by Dr. Frank O'Hara, to whom floating point arithmetic is almost second nature. I therefore wish to record my grateful thanks to Frank.
Ian Logan, Lincoln, January 1982
I am very pleased to have been able to help Ian Logan sort out the arithmetic of the ZX81. I remain amazed at the ease with which he works out what the machine is doing, without any help from the people who designed the hardware or those who wrote the programs.
Frank O'Hara, London, January 1982
THE 'FLOW DIAGRAM' FOR PART B of the 8K ROM Program
This subroutine is used to produce an evaluation result of the 'next expression'.
The result is returned as the 'last value' on the calculator stack. For a numerical result, the 'last value' will be the actual floating-point number. However for a string result the 'last value' will consist of a set of parameters. The first of the five bytes is unspecified, the second and third bytes hold the address of the 'start' of the string and the fourth and fifth bytes hold the 'length' of the string.
Bit 6 of FLAGS is set for a numeric result and reset for a string result.
When a 'next expression' consists of only a single operand, e.g.... A..., ... RND..., ...A$(4,3 TO 7)..., then the 'last value' is simply the value that is obtained from evaluating the operand.
However when the 'next expression' contains a function and an operand, e.g. ... CHRS A... , ... NOT A ... , ... SIN 1 ... , the operation code of the function is stored on the machine stack until the 'last value' of the operand has been calculated. This 'last value' is then subjected to the appropriate operation to give a new 'last value'.
In the case of there being an arithmetic or logical operation to be performed, e.g. ... A+B... , ... A**B ... , ... A=B ... , then both the 'last value' of the first argument and the operation code have to be kept until the 'last value' of the second argument has been found. Indeed the calculation of the 'last value' of the second argument may also involve the storing of 'last values' and operation codes whilst the calculation is being performed.
It can therefore be shown that as a complex expression is evaluated, e.g. ... CHR$ (T+A-26* INT ((T+A)/26)+38)... , a hierarchy of operations yet to be performed is built up until the point is reached from which it must be dismantled to produce the final 'last value'.
Each operation code has associated with it an appropriate priority code and operations of higher priority are always performed before those of lower priority.
The subroutine begins with the A register being set to hold the first character of the expression and a starting priority marker zero being put on the machine stack.
0F55 SCANNING RST 0018,GET-CH. The first character is fetched. LD B,+00 The starting priority marker. PUSH BC It is stacked.
The character is tested against the code for 'RND' and a jump made if it does not match.
0F59 SRND CP +40 Is it 'RND'? JR NZ,0F8C,S-PI Jump if it is not so.
Unless syntax is being checked the required random number is calculated and forms a 'last value' on the calculator stack.
CALL 0DA6,SYNTAX-Z Test for syntax checking. JR Z,0F8A,S-RND-END Jump if required. LD BC,(SEED) Fetch the current value of SEED. CALL 1520,STACK-BC Put it on the calculator stack. RST 0028,FP-CALC. Now use the calculator. DEFB +A1,stk-one,1A51 The 'last value' is now DEFB +0F,addition,1755 SEED+1. DEFB +30,stk-data,19FC Put the decimal number 75 DEFB +37,exponent 87 on the calculator stack. DEFB +16,(+00,+00,+00) DEFB +04,multiply,17C6 'Last value' = (SEED+1)*75. DEFB +30,stk-data,19FC See STACK LITERALS to see how DEFB +80,four bytes bytes are expanded so as to put the DEFB +41,exponent 91 decimal number 65537 on the DEFB +00,+00,+80,(+00) calculator stack. DEFB +2E,n-mod-m,lC37 Divide (SEED+1)*75 by 65537 to give a 'remainder' and an 'answer'. DEFB +02,delete,19E3 Discard the 'answer'. DEFB +A1,stk-one,1A51 The 'last value' is now DEFB +03,subtract,174C 'remainder'-1. DEFB +2D,duplicate,19F6 Make a copy of the 'last value'. DEFB +34,end-calc.,002B The calculation is finished. CALL 158A,FP-TO-BC Use the 'last value' to give the new LD (SEED),BC value for SEED. LD A,(HL) Fetch the exponent of 'last value'. AND A Jump forward if the exponent JR Z,0F8A,S-RND-END is zero. SUB +10 Reduce the exponent, i.e. divide 'last LD (HL),A value' by 65536 to give the required 'last value'. 0F8A S-RND-END JR 0F99,S-PI-END Jump past the 'Pl' routine.
The character is tested against the code for 'Pl' and a jump made if it does not match.
0F8C S-PI CP +42 Is it 'Pl'? JR NZ,0F9D,S-INKEYS Jump if it is not so.
Unless syntax is being checked the value of 'Pl' is calculated and forms the 'last value' on the calculator stack.
CALL 0DA6,SYNTAX-Z Test for syntax checking. JR Z,0F99,S-PI-END Jump if required. RST 0028,FP-CALC. Now use the calculator. DEFB +A3,stk-pi/2,1A51 The value of Pl/2 is put on the DEFB +34,end-calc.,002B calculator stack as the 'last value'. INC (HL) The exponent is incremented thereby doubling the 'last value' giving 'Pl'. 0F99 S-PI-END RST 0020,NEXT-CH Move on to the next character. JP 1083,S-NUMERIC Jump forward.
The character is tested 'against the code for 'INKEY$' and a jump made if it does not match.
0F9D S-INKEY$ CP +41 Is it 'INKEY$'? JR NZ,0FB2,S-ALPHNUM Jump if it is not so.
The keyboard is now scanned and the parameters for the INKEY$ string calculated. A null string will result in the BC register pair holding the value zero, whereas when a key has been pressed it holds the value one. The DE register pair points to the appropriate character in the key tables and that entry forms the actual string.
CALL 02BB,KEYBOARD Scan the keyboard & reset carry flag. LD B,H Copy the 'key value' into the LD C,L BC register pair. LD D,C Set the zero flag when dealing with INC D a null string. CALL NZ,07BD,DECODE Decode the 'key value'. The carry flag is set when only one key is pressed. LD A,D D register always holds zero. ADC A,D A now holds the value of the carry. LD B,D Clears the B register. LD C,A C holds zero or one. EX DE,HL The start pointer goes into DE. JR 0FED,S-STRING Jump forward.
The character is tested to see if it is alphanumeric.
0FB2 S-ALPHNUM CALL 14D2,ALPHANUM Test the character. JR C,1025,S-LET-NUM Jump if a letter or a digit.
The character is tested against the code for ':', hence identifying a decimal number without a leading zero.
CP +1B Is it a '.'? JP Z,1047,S-DECIMAL Jump forward if it is so.
The character is tested against the code for'-', hence identifying the 'unary minus' operation.
Before the actual test the B register is set to hold the priority 9 and the C register the operation code D8 that are required for this operation.
LD BC,+09D8 Priority 9, operation code D8. CP +16 Is it a "? JR Z,1020,S-PUSH-P0 Jump forward if it is 'unary minus'.
The character is tested against the code for 'I', hence identifying the presence of a parenthesised expression.
CP +10 !vital'? JR NZ,0FD6,SQUOTE Jump if it is not so.
A parenthesised expression is dealt with in a recursive manner. An error is reported if there is no closing bracket.
CALL 0049,CH-ADD+1 Points to the next character. CALL 0F55,SCANNING Call the present subroutine. CP +11 Is the present character a? JR NZ,0FFF,S-RPRT-C1 Report C if no closing bracket. CALL 0049,CH-ADD+1 Point to the next character. JR 0FFB,S-CONT-1 Jump forward.
The character is tested against the code for' "', hence identifying a string of characters.
0FD6 S-QUOTE CP -F0B Is it a '"'? JR NZ,1002,S-FUNCT Jump if it is not so.
The parameters for this string of characters are now calculated.
CALL 0049,CH-ADD+1 Point to the next character. PUSH HL Save the 'start' address. JR 0FE3,S-Q-END? Jump past the re-entry point. 0FE0 S-Q-NEXT CALL 0049,CH-ADD+1 Point to the next character. 0FE3 S-Q-END? CP -F0B Is it another ' '? JR NZ,0FFB,S-N/L-ERR Before re-entering the loop check that the line has not been finished. POP DE Get the 'start' into DE. AND A Clear the carry flag. SBC HL,DE Now find the 'length'. LD B,H Move the 'length' to the LD C,L BC register pair.
A string result has now been identified, either an INKEY$ or a string of characters, therefore bit 6 of FLAGS must be reset. Unless syntax is being checked the parameters of the string are put on the calculator stack to form a 'last value'.
0FED S-STRING LD HL,+FLAGS Make HL point to FLAGS. RES 6,(HL) Reset this bit string result. BIT 7,(HL) Test for line execution. CALL NZ,12C3,STK-STORE Stack the parameters if executing a line. RST 0020,NEXT-CH Move to the next character. 0FFB S-CONT-1 JP 1088,S-CONT-3 Jump forward.
A NEWLINE character will lead to an error being reported
0FFB S-N/L.ERR CP +76 Is it a'N/L'? JR NZ,0FE0,S-O'NEXT Reenter the loop if it is not the end of a line. 0FFF S-RPRT-C1 JP 0D9A,REPORT-C Jump back to give report C.
The present character must now represent a function.
1002 S-FUNCT SUB +C4 The range of the functions is changed from C4-D7 to 00-13 Hex. JR C,0FFF,S-RPRT-C1 Report an error if out of range.
The function 'NOT' is identified and dealt with separately from the others.
LD BC,+04EC Priority 4, operation code EC. CP +13 Is it the function 'NOT' ? JR Z,1020,S-PUSH-P0 Jump if it is so. JR NC,0FFF,S-RPRT-C1 Check the range again.
The remaining functions have priority 16 decimal. The operation codes for these functions are now calculated. Functions that operate on strings need bit 6 reset and functions that give string results need bit 7 reset in their operation codes.
LD B,+10 Priority 16 decimal.
ADD A,+D9 The function range is now D9-EB. LD C,A Transfer the operation code. CP +DC Separate C0DE, VAL & LEN which JR NC,101A,S-NO-TO-S operate on strings to give RES 8,C numerical results. 101A S-NO-TO-S CP +EA Separate STIRS & CHRS which operate JR C,1020,S.PUSH-P0 on numbers to give string results.
RES 7,C Mark the operation codes.
The other operation codes have bits 6 & 7 both set.
The priority code and the operation code for the function being considered are now pushed to the machine stack. A hierachy of operations is thereby built up.
1020 S-PUSH-P0 PUSH BC Stack the priority and operation codes RST 0020,NEXT-CH before moving on to consider the JP 0F59,S-RND next part of the expression.
The present character has been identified as being alphanumeric. If it is a letter then a variable name has been found; however if it is a digit then a decimal number has been found.
1025 S-LET-NUM CP +26 Jump if dealing with a digit. JR C,1047,S-DECIMAL
When a variable name has been identified a call is made to LOOK-VARS, which looks through those variables that already exist in the variable area. If an appropriate numeric value is found then it is copied to the calculator stack using MOVE-FP. However a string or string array entry has to have the appropriate parameters passed to the calculator stack by the STK-VAR subroutine.
CALL 111C,LOOK-VARS Look in the existing variables for the matching entry. JP C,0D4B,REPORT-2 An error is reported if there is no existing entry. CALL Z,11A7,STK-VAR Stack the parameters of the string entry/return numeric element base address. LD A,(FLAGS) Fetch FLAGS. CP +C0 Test bits 6 & 7 together. JR C,1087,S-CONT-2 One or both bits are reset. INC HL A numeric value is to be stacked. LD DE,(STKEND) Fetch the 'old' STKEND. CALL 19F6,MOVE-FP Move the actual number. EX DE,HL Move the pointer to HL. LD (STKEND),HL Enter the 'new' STKEND. JR 1087,S-CONT-2 Jump forward.
When a decimal number has been identified the action taken is very different for syntax checking and line execution.
If syntax is being checked then the floating-point form has to be calculated and copied into the actual BASIC line. However when a line is being executed the floating-point form will always be available to it is copied to the calculator stack to form a 'last value'.
1047 S-DECIMAL CALL 0DA6,SYNTAX-Z Jump forward if a line is being JR NZ,106F,S-STK-DEC executed.
During syntax checking:
CALL 14D9,DEC-TO-FP The floating-point form is found. RST 0018,GET-CH. Set HL to point one past the last digit. LD BC,+0006 Six locations are required. CALL 0B9E,MAKE-ROOM Make the room in the BASIC line. INC HL Point to the first 'new' location. LD (HL),+7E Enter the number marker character. INC HL Point to the second location, EX DE,HL This pointer is wanted in DE. LD HL,(STKEND) Fetch the 'old' STKEND. LD C,+05 There are 5 bytes to move. AND A Clear the carry flag. SBC HL,BC The 'new' STKEND= 'old' STKEND-5. LD (STKEND),HL Move the floating-point number from LDIR the calculator stack to the line. EX DE,HL Put the line pointer in HL. DEC HL Point to the last byte added. CALL 004C,CURSOR-S0 This sets CH-ADD. JR 1083,S-NUMERIC Jump forward.
During line execution:
106F S-STK-DEC RST 0020,NEXT-CH Move on to the next character in CP +7E turn until the number marker JR NZ,106F,S-STK-DEC character is found. INC HL Point to the first byte of the number. LD DE,(STKEND) Fetch the 'old' STKEND. CALL 19F6,MOVE-FP Move the floating point number. LD (STKEND),DE Save the'new' STKEND. LD (CH-ADD),HL This sets CH-ADD.
A numeric result has now been identified, coming from RND, PI or a decimal number, therefore bit 6 of FLAGS must be set.
1063 S-NUMERIC SET 6,(FLAGS) Set the numeric marker flag.
The scanning of the line now continues. The present argument may be followed by a 'I', a binary operator or, if the end of the expression has been reached, a NEW LINE character or a command.
1087 S-CONT-2 RST 0018,GET-CH. Fetch the present character. 1088 S-CONT-3 CP +10 Jump forward if it is not awhich JR NZ,1098,S-OPE RTR indicates a parenthesised expression.
If the 'last value' is numeric then the parenthesised expression is a true sub-expression and must be evaluated by itself. However if the 'last value' is a string then the parenthesised expression represents an element of an array or a slice of a string. A call to SLICING modifies the parameters of the string as required.
BIT 6,(FLAGS) Jump forward if dealing with a numeric JR NZ,108C,S-LOOP parenthesised expression. CALL 1263,SLICING Modify the parameters of the 'last value'. RST 0020,NEXT-CH Move on to consider the next character. JR 1088,S-CONT-3
If the present character is indeed a binary operator it will be given an operation code in the range C3-CF Hex., and the appropriate priority code.
1098 S-OPERTR LD BC,+00C3 Set default priority zero and the operation code offset to C3. CP +12 Compare the character against the lowest JR C,10BC,S.LOOP operator. Jump if out of range. SUB +16 The ranges of the operators are changed from 12-18 & D8-DD to FC-FF, 00-02 & C2-C7 Hex. JR NC,10A7,S-HIGH-OP Jump forward with 00-02 & C2-C7. ADD A,+0D The original range 12-15 is now 09-OC Hex. JR 1085,S-END-OP Jump forward. 10A7 S-HIGH-OP CP +03 Leave the original range 16-18 JR C,10B5,S-END-OP as 00-02 Hex. SUB +C2 The original range C2-C7 is now 00-05 Hex. JR C,108C,S-LOOP Again jump if out of range. CP +06 Test the upper limit. JR NC,10BC,S-LOOP Again jump if out of range. ADD A,+03 The original range C2-C7 is now 03-08 Hex. 1085 S-END-OP ADD A,C The offset C3 is added to give the range LD C,A of operation codes C3-CF Hex. LD HL,+104C The pointer to the priority table. i.e. 104C+C3=110F the first address. ADD HL,BC Index into the table. LD B,(HL) Fetch the appropriate priority.
The main loop of this subroutine is now entered. At this stage there are:
Initially the 'last' operation and priority is taken off the machine stack and is compared against the 'present' operation and priority.
If the 'present' priority is higher than the 'last' priority then an exit is made from the loop as the 'present' priority is considered to bind tighter than the 'last' priority.
However if the priorities are less binding then the operation specified as the 'last' operation is performed. The 'present' operation and priority go back on the machine stack to be carried round the loop again. In this manner the hierachy of functions and binary operations that have been queued are dealt with in the correct order.
10BC S-LOOP POP DE Get the 'last' operation and priority. LD A,D The priority goes to the A register. CP B Compare 'last' against 'present'. JR C,10ED,S-TIGHTER Exit to wait for the argument. AND A Are both priorities zero? JP 2,0018, GET-CH. Exit via GET-CH. thereby making 'last value' the required result. PUSH BC Stack the 'present' values. PUSH DE Stack the 'last' values briefly. CALL 0DA6,SVNTAX-Z Do not perform the actual operation JR Z,10D5,SSYNTEST if syntax is being checked. LD A,E The 'last' operation code. AND +3F Strip off bits 6 & 7 to convert the operation code to a calculator-offset. LD B,A It is required in the B register. RST 0028,FP-CA LC. Now use the calculator. DEFB +37,fp-calc-2,19E4 Perform the actual operation. DEFB +34,end-calc.,0028 It has been done. JR 1SDE,S-RUNTEST Jump forward.
An important part of syntax checking involves the testing of the operations to ensure that the nature of the 'last value' is of the correct type for the operation under consideration.
10D5 S-SYNTEST LD A,E Get the 'last' operation code. XOR (FLAGS) This tests the nature of the 'last value' AND +40 against the requirement of the operation. They are to be the same for correct syntax. 10D6 S-RPRT-C2 JP NZ,0D9A,REPORT-C Jump if syntax fails.
Before jumping back to go round the loop again the nature of the 'last value' must be recorded in FLAGS.
10DE S-RUNTEST POP DE Get the 'last' operation code. LD HL,+FLAGS Point to FLAGS. SET 6,(HL) Assume result to be numeric. BIT 7,E Jump forward if the nature of 'last value' JR NZ,10EA,S-ENDLOOP is numeric. RES 6,(HL) It is string. 10EA S-ENDLOOP POP BC Get the 'present' values into BC. JR 10BC,S-LOOP Jump back.
Whenever the operations bind tighter, the 'last' and the 'present' values go back on the machine stack. However if the 'present' operation requires a string as its operand then the operation code is modified to indicate this requirement.
10ED S-TIGHTER PUSH DE The 'last' values go on the stack. LD A,C Get the 'present' operation code. BIT 6,(FLAGS) Do not modify the operation code if JR NZ,110A,S-NEXT dealing with a numeric operand. AND +3F Clear bits 6 & 7. ADD A,+08 Increase the code by 08 Hex. LD C,A Return the code to the C register. CP +10 Is the operation 'AND ' ? JR NZ,1102,S-NOT-AND Jump if it is not so. SET 6,C 'AND ' requires a numeric operand. JR 110A,S-NEXT Jump forward. 1102 S-NOT-AND JR C,100B,S-RPRT-C2 The operations-,',/,"" & OR are not possible. CP +17 Is the operation '+'? JR Z,110A,S-NEXT Jump if it is so. SET 7,C The other operations yield a numeric result. 110A S-NEXT PUSH BC The 'present' values go on the stack.
RST 0020,NEXT-CH Move on to consider the next character in the expression. JP 0F59,S-RND Start by testing against 'FIND'.
address priority operation address priority operation
110F 06 - 1116 05 >= 1110 08 * 1117 05 <> 1111 08 / 1118 05 > 1112 0A ** 1119 05 < 1113 02 OR 111A 05 = 1114 03 AND 111B 06 + 1115 05 <
This subroutine is called whenever a search of the variable area is required. The subroutine is entered with CH-ADD pointing to the first letter of the variable name as it occurs in the BASIC line, either in the program area or the work space.
The subroutine initially builds up a discriminator byte, in the C register, that is based on the first letter of the variable name. Bits 5 & 6 of this byte indicate which type of variable is being handled.
The B register is used as a bit register to hold flags.
111C LOOK-VARS SET 6,(FLAGS) Presume a numeric variable. RST 0018,GET-CH. Get the first character into A. CALL 14CE,ALPHA Is it alphabetic? JP NC,0D9A,REPORT-C Give an error report if it is not so. PUSH HL Save the pointer to the first letter. LD C,A Transfer the letter to C. RST 0020,NEXT-CH Get the 2nd character into A. PUSH HL Save the pointer to the 2nd character. RES 5,C Start with bit 5 reset. CP +10 Is the 2nd character a'1' ? JR Z,1148,V-RUN/SYN Separate arrays of numbers. SET 6,C Now set bit 6. CP +0D Is the 2nd character a '5' ? JR Z,1143,V-STR-VAR Separate all the strings. SET 5,C Now set bit 5.
Now find the end character of a variable name which has more than one character.
1139 V-CHAR CALL 14D2,ALPHANUM Is the character alphanumeric? JR NC,1148,V-RUN/SYN Jump when the end is reached. RES 6,C Mark the discriminator byte. RST 0020,NEXT-CH Get the next character. JR 1139,V-CHAR Go back to test it.
Simple strings and arrays of strings require that bit 6 of FLAGS is reset.
1143 V-STR-VAR RST 0020,NEXT-CH Move CH-ADD on past the '8'. RES 6,(FLAGS) Reset the bit 6 to indicate a string.
Now test the syntax flag.
1148 V-RUN/SYN LD B,C Copy the discriminator to B. CALL 0DA6,SYNTAX-Z Test for syntax checking. JR NZ,1156,V-RUN Jump forward if executing a line. LD A,C Move it to A for manipulation. AND +ED Drop the character code part. SET 7,A Indicate syntax by setting bit 7. LD C,A Restore the discriminator to C. JR 118A,V-SYNTAX Jump forward.
A BASIC line is being executed so make a search of the variable area.
1156 V-RUN LD HL,(VARS) Pick up the VARS pointer. 1159 V-EACH LD A,(HL) The 1st letter of each variable. AND +7F Match on bits 0-6. JR 2,1188,V-80-BYTE Jump when the '80-byte' is reached. CP C The actual comparison. JR NZ,1180,V-NEXT Jump if the 1st letter does not match the discriminator byte. RLA Rotate A leftwards and then double ADD A,A it to test bits 5 & 6. JP P,1195,V-FOUND-2 Strings and array variables. JR C,1195,V-FOUND-2 Simple numeric and FOR -NEXT variables. POP DE Get the pointer to the 2nd character PUSH DE Put it back. PUSH HL Save the variable pointer. 116B V-MATCHES INC HL Go on to consider the next character 116C V-SPACES LD A,10EI Fetch each character in turn. INC DE Point to the next. AND A Is the character a 'space'? JR 2,116C,V-SPACES Ignore the spaces. CP (HL) Make the comparison. JR Z,116B,V-MATCHES Back for another if it does match. OR +80 Will it match with bit 7 set? CP (HL) Try it. JR NZ,117F,V-GET-PTR Jump if it does not match after all. LD A,(DE) Get the next character. CALL 1402,ALPHANUM Is it alphanumeric? JR NC,1194,V-FOUND-1 Jump if the correct entry has been located in the variable area. 117F V-GET-PTR POP HL Fetch the variable pointer. 1180 V-NEXT PUSH BC Save B & C briefly. CALL 09F2,NEXT-ONE DE will then point to the next variable in the variable area. EX DE,HL Transfer the pointer to HL. POP BC Get B & C back. JR 1159,V-EACH Round the loop again.
The variable name was not present in the variable area.
1188 V-80-BYTE SET 7,B Indicates no variable found.
The syntax path re-enters here.
1194 V-FOUND-1 POP DE Drop the saved variable pointer. 1195 V-FOUND-2 POP DE Drop the 2nd character pointer. POP DE Drop the first letter pointer. PUSH HL Save the 'last' letter pointer. RST 0018,GET-CH. Fetch the current character.
If the matching variable name has more than a single letter then the other characters must be passed-over.
1199 V-PASS CALL 14D2,ALPHANUM Is it alphanumeric? JR NC,11A1,V-END Jump when the end of the name is reached, otherwise test again. RST 0020,NEXT-CH Fetch the next character. JR 1199,V-PASS Go back to test it.
The exit-parameters require to be set.
11A1 V-END POP HL HL holds the 'first' or the 'last' letter pointer. RL B Rotate the whole register. BIT 6,B The zero flag is specified. RET Finished.
The exit-parameters for the subroutine are:
The system variable CH-ADD points to the first character after the variable name as it occurs in the BASIC line.
If no matching variable name was found in the variable area then: The carry flag is set. The zero flag is set when the search was for an array variable. ni The HL register pair points to the first letter of the variable name.
If the search yielded a matching entry in the variable area then: The carry flag is reset. ii The zero flag is set for both simple string variables and all array variables. The HL register pair points to the letter of a single lettered variable name, or the last character of a long variable name, as it occurs in the variable area.
Bit 6 of the C register is reset when dealing with an array of numbers and set when dealing with an array of strings.
Bit 7 of the C register is reset during line execution and set during syntax checking.
This subroutine is usually used either to find the parameters that define an existing string entry in the variable area, or to return in the HL register pair the base address of a particular element of an array of numbers. When called from DIM the subroutine only checks the syntax of the BASIC line.
Note that the parameters that define a string may be altered by calling SLICING if this should be specified.
Initially the A and B registeres are cleared and bit 7 of the C register is tested to determine whether syntax is being checked.
11A7 STK-VAR XOR A Clear the array flag. LD B,A Clear the B register for later. BIT 7,C Jump forward if syntax is being JR NZ,11F8,SV-COUNT checked.
Next, simple strings are separated from array variables.
BIT 7,(HL) Jump forward if dealing with an JR NZ,11BFSV-ARRAYS array variable.
The parameters for a simple string are readily found.
INC A Specify a simple string. 1182 SV-SMPLES INC HL Move along the variable entry. LD C,(HL) Pick up the low length counter. INC HL On one. LD B,(HL) Pick up the high length counter. INC HL On one. EX DE,HL Transfer the start pointer to DE. CALL 12C3,STK-STORE Pass these parameters to the stack. RST 0018,GET-CH. Fetch the present character. JP 125A,SV.SLICE? Jump to see if a 'slice' is required.
The base address of an element of an array is now found. Initially the 'number of dimensions' is collected.
11BF SV-ARRAYS INC HL Go past the total length counter. INC HL INC HL LD B,(HL) Collect the 'number of dimensions'. BIT 6,C Jump forward if dealing with an JR Z,11 Dl,SV-PTR array of numbers.
If an array of strings has its 'number of dimensions' equal to '1' then such an array can be handled as a simple string.
DEC B Decrease the 'number of dimensions'. JR Z,11B2,SV-SMPLE$ Jump if the number is now zero.
Next a check is made to ensure that in the BASIC line the variable is followed by a subscript.
EX DE,HL Save the variable pointer in DE. RST 0018,GET-CH. Get the present character. CP +10 Is it a 'l'? JR NZ,1231,REPORT-3 Report the error if it is not so. EX DE,HL Restore the variable pointer.
For both numeric arrays and arrays of strings the variable pointer is transferred to the DE register pair before the subscript is evaluated.
1101 SV-PTR EX DE,HL Variable pointer into DE. JR 11F8,SV-COUNT Jump forward.
The following loop is used to find the parameters of a specified element within an array.
The loop is entered at the mid-point SV-COUNT , where the element counter is set to zero.
The loop is accessed 'B' times, this being, for a numeric array, equal to the number of dimensions that are being used, but for an array of strings 'B' is one less than the number of dimensions in use as the last subscript is used to specify a 'slice' of the string.
1134 SV-COMMA PUSH HL Save the 'counter'. RST 0018,GET-CH. Get the present character. POP HL Restore the 'counter'. CP +1A Is the present character a',' ? JR Z,11 FB,SV-LOOP Jump to consider another subscript. BIT 7,C If a line is being executed then JR Z,1231,REPORT-3 there is an error. BIT 6,C Jump if dealing with an JR NZ,11E9,SV-CLOSE array of strings. CP +11 Is the present character a ? JR NZ,1223,SV-RPT-C Report an error if it is not to. RST 0020,NEXT-CH Move CH-ADD to point to the next character in the BASIC line. RET Return as the syntax is correct.
For an array of strings the present subscript may represent a'slice', or the subscript for a'slice' may yet be present in the BASIC line.
11E9 SV-CLOSE CP +11 Is the present character a '1' ? JR Z,1259,SV-DIM Jump forward and check whether there is another subscript. CP +DF Is the present character a 'TO' ? JR NZ,1223,SV-RPT-C It must not be otherwise. 11E1 SV-CH-ADD RST 0018,GET-CH. Get the present character. DEC HL Point to the preceding character. LD (CH-ADD),HL Make CH-ADD point to this location. JR 1256,SV-SLICE Evaluate this 'slice'.
Enter the loop here
11F8 SV-COUNT LD HL,+0000 Set the 'counter' to zero. 11FB SV-LOOP PUSH HL Save the 'counter' briefly. RST 0020,NEXT-CH Makes CH-ADD point to the next character. POP HL Restore the 'counter'. LD A,C Fetch the discriminator byte. CP +C0 Jump unless checking the syntax for JR NZ,120C,SV-MULT an array of strings. RST 0018,GET-CH. Get the present character. CP +11 Is it a '1'? JR Z,1259,SV-DIM Jump forward as finished counting elements. CP +DF Is it a 'TO'? JR Z,11F1,SV-CH-ADD Jump back if dealing with a 'slice'. 120C SV-MULT PUSH BC Save the dimension-number counter and the discriminator byte. PUSH HL Save the element 'counter'. CALL 12FF,DE,(DE+11 Get a 'dimension-size' into DE. EX (SP),HL The 'counter' moves to HL and the variable pointer is stacked. EX DE,HL The 'counter' moves to DE and the 'dimension-size' to HL. CALL 12DD,INT: EXP1 Evaluate the next subscript. JR C,1231,REPORT-3 Give the error if out of range. DEC BC The result of the evaluation is decremented as the 'counter' is to count the elements occurring before the specified element. CALL 1305,HL=HL*DE Multiply 'counter' by 'dimension-size'. ADD HL,BC Add the result of 'INT: EXP1'-1 to the 'present counter. POP DE Fetch the variable pointer. POP BC Fetch the dimension-number counter and the discriminator byte. DJNZ 11 D4,SV-COMMA Keep going round the loop until 'B' equals zero.
The syntax flag is checked before arrays of strings are separated from numeric arrays.
BIT 7,C Syntax or line execution?
1223 SV-RPT-C JR NZ,128B,SL-RPT-C Report the error if checking syntax. PUSH HL Save the 'counter'. BIT 6,C Jump forward if dealing with JR NZ,123D,SV-ELEMS an array of strings.
When dealing with a numeric array the present character must be a
LD B,D Transfer the variable pointer to LD C,E the BC register pair. RST 0018,GET-CH. Fetch the present character. CP +11 Is it a ')'? JR Z,1233,SV-NUMBER Report an error if it is not so.
Give report 3.
1231 REPORT-3 RST 0008,ERROR-1 Subscript out of range. DEFB -02
The address of the location before the actual floating-point number can now be calculated.
1233 SV-NUMBER RST 0020,NEXT-CH Move CH-ADD on one location. POP HL Fetch the 'counter'. LD DE,+0005 There are 5 bytes to each element. CALL 1305,HL=HL*DE Compute the total number of bytes. ADD HL,BC Add this number to the variable pointer, thereby HL will point to the location before the required element. RET Finished with numeric arrays.
When dealing with an array of strings the length of an element is given by the last dimension-size. The appropriate parameters are calculated before being put on the calculator stack.
1230 SV-ELEMS CALL 12FF,DE,(DE+1) Fetch the last 'dimension-size'. EX (SP),HL The variable pointer goes on the stack and the 'counter' to HL. CALL 1305,HL=HL*DE Multiply 'counter' by 'dimension-size'. POP BC Fetch the 'variable pointer'. ADD HL,BC This gives HL pointing to the location before the actual element. INC HL So point to the start of the string. LD B,D Transfer the last 'dimension-size' LD C,E to BC to form the length. EX DE,HL Transfer the start pointer to DE. CALL 12C2,STK-ST-0 Pass the parameters to the calculator stack.
There are three possible forms of the last subscript. The first is illustrated by A$(2,4 TO B), the second by AS(2) (4 TO 8) and the third by A$(2) which is the default value indicating that the whole string is required.
RST 0018,GET-CH. Get the present character. CP +11 Is it a ')'? JR Z,1259,SV-DIM Jump if it is so. CP +1A Is it a ','? JR NZ,1231,REPORT-3 Report an error if it is not so. 1256 SV-SLICE CALL 1263,SLICING Use SLICING to modify the parameters. 1259 SV-DIM RST 0020,NEXT-CH Get the next character. 125A SV-SLICE? CP +10 Is it a '('? JR Z,1256,SV-SLICE Jump back to evaluate the 'slice'. RES 6,(FLAGS) Indicate a string result. RET Finished with arrays of strings.
The present string can be sliced using this subroutine. The subroutine is entered with the parameters of the string being present on the top of the calculator stack.
Initially the syntax flag is checked and the parameters of the string are fetched only if a line is being executed.
1263 SLICING CALL 0DA6,SYNTAX-Z Check the syntax flag. CALL NZ,13FB,STK-FETCH Collect the parameters if a line is being executed.
The possibility of the 'slice' being '()' has to be considered.
RST 0020,NEXT-CH Get the next character. CP +11 Is it a ')'? JR Z,12BE,SL-STORE Jump forward if it is so.
Before proceeding the registers are set up as required.
PUSH DE The 'start' goes on the stack. XOR A The A register is cleared and also PUSH AF saved on the stack. PUSH BC Save the 'length' briefly. LD DE,+0001 Assume that the 'slice' is to begin with the first character. RST 001B,GET-CH. Get the first character into A. POP HL Put the 'length' into HL.
The first parameter of the 'slice' is now evaluated.
CP +DF Is the present character a 'TO' ? JR 2,1292,SL-SECOND The first parameter by default will be the current value of 0F, i.e. '1'. POP AF At this stage A will hold zero. CALL 12DE,INT:EXP2 BC will hold the first parameter and PUSH AF A will hold Hex.FF if there has been an 'out of range' error. LD D,B Transfer the first parameter to the LD E,C DE register pair. PUSH HL Save the 'length' briefly. RST 0018,GET-CH. Get the present character. POP HL Restore the 'length'. CP +DF Is the present character a 'TO'? JR Z,1292,S L-SECOND Jump forward to consider the second parameter. CP +11 Is the present character a ')' ? 1288 SL-RPT-C JP NZ,0D9A,REPORT-C There must be a closing bracket.
There is no second value to the 'slice' under consideration.
LD H,D The last character of the 'slice' is LD L,E also the first character. JR 12A5,SL-DEFINE Jump forward.
The second parameter of the 'slice' is now evaluated.
1292 SL-SECOND PUSH HL Save the 'length' briefly. RST 0020,NEXT-CH Get the next character. POP HL Restore the 'length'. CP +11 Is the present character a') ' ? JR Z,12A5,SL-DEFINE Jump if there is no second parameter. POP AF If the first parameter was in range A will hold zero, otherwise Hex. FF. CALL 12DE,INT:EXP2 BC will hold the second parameter. PUSH AF Save the error register again. RST 0018,GET-CH. Get the present character. LD H,B The value held in BC is the last LD L,C character of the 'slice'. CP +11 Is the present character a 'I' 7 JR NZ,128B,SL-RPT-C Report the error if it is not so.
The 'new' parameters are now defined.
12A5 SL-DEFINE POP AF Fetch the error register. EX (SP),HL Second parameter goes on the stack and the start goes to HL. ADD HL,DE Add the f irst parameter to the start of the string. DEC HL Go back a location to get it correct. EX (SP),HL The 'new start' goes on the stack and the second parameter to HL. AND A Prepare for subtraction. SBC HL,DE Finds the 'new length'. LD BC,+0000 By default the 'new length' is zero. JR C,12B9,SL-OVER A 'negative slice' is a null string. INC HL Add the inclusive byte. AND A Now test the error register. JP M,1231,REPORT3 Jump if there was an 'out of range' error whilst in INT: EXP2. LD B,H Transfer the 'new length' to the LD C,L BC register pair. 1289 SL-OVER POP DE Get the 'new start' from the stack. RES 6,(FLAGS) Ensure a string is indicated.
When a line is being executed this subroutine enters the STK-STOR E subroutine directly so as to stack the parameters of the string.
12BE SL-STORE CALL 0DA6,SYNTAX-Z Check the syntax flag and return if RET Z syntax is being checked.
This subroutine passes the values held in the A, B, C, D and E registers to the calculator stack. The stack thereby grows in size by 5 bytes.
Although this subroutine could be used to transfer floating-point numbers it is, however, only used to transfer the parameters of strings.
Note that the A register is used as a flag to show whether the string is a simple string or part of an array of strings. However this flag would appear to be redundant in the final program.
12C2 STK-STOX OR A Clear the array flag. 12C3 STK-STORE PUSH BC Save the BC register pair briefly. CALL 19E8, TEST-5-SP Is there room for the 5 bytes? POP BC Restore BC. LD HL,(ISTKEND) Fetch the current value of STKEND. LD (HL),A Pass the array flag. INC HL On one. LD (HL),E Pass the low address pointer. INC HL On one. LD (HL),D Pass the high address pointer. INC HL On one. LD (HL),C Pass the low length counter. INC HL On one. LD (HL),B Pass the high length counter. INC HL On one. LD (STKEND),HL Save the value in HL as STK END. RES 6,(FLAGS) Show that the 'last value' is a string. RET Finished.
This subroutine returns to the calling routine the evaluation result of the 'next expression' as an integer value held in the BC register pair. The subroutine also tests this result against a limit value supplied in the HL register pair. The carry flag becomes set if there is an 'out of range' error.
The A register is used as an error register and holds Hex.00 if there has not been a previous error and Hex.FF if there was an error when the subroutine was last called.
12DD INT.-EXP1 XOR A Clear the error register.
12DE INT.-EXP2 PUSH DE Save both DE and HL for the duration PUSH HL of the subroutine. PUSH AF Save the error register briefly. CALL 0D92,CLASS-6 The 'next expression' is evaluated to give a 'last value'. POP AF Restore the error register. CALL 0DA6,SYNTAX-Z Jump forward if syntax is being JR Z,12FC,I-RESTORE checked. PUSH AF Save the error register briefly. CALL 0EA7,FIND-INT. The 'last value' is compressed into the 16 bits of the BC register pair. POP DE Get the error register into D. LD A,B Test the evaluation result. OR C SCF Presume the error condition. JR Z,12F9,I-CARRY Jump if evaluation result is zero. POP HL Copy the 'limit value'. This will be PUSH HL either 'dimension-size', 'DIM-limit' or 'string length'. AND A Prepare for the subtraction. SBC HL,BC Make the test.
12F9 I-CARRY LD A,D Fetch the error register. SBC A,+00 If there is no error and no previous error then A holds zero and carry is reset. Otherwise A holds Hex.FF or FE, and carry is set.
12FC I-RESTORE POP HL Restore the HL and DE POP DE register pairs. RET Finished.
This subroutine performs the construction LD DE,(DE+1) and returns HL pointing to DE+2.
12FF DE,(DE+1) EX DE,HL Use HL for the construction. INC HL Points to'DE+1'. LD E,(HL) In effect LD E,(DE+1). INC HL Points to 'DE+2'. LD D,(HL) In effect LD D,(DE+2). RET Finished.
Unless syntax is being checked this subroutine performs the multiplication as stated.
Overflow of the 16 bits available gives 'REPORT 4'. This is not exactly the true situation but it implies that the machine is not large enough for the task envisaged by the programmer.
1305 HL=HL*DE CALL 0DA6,SYNTAX-Z Return if syntax is being RET Z checked. PUSH BC BC is saved. LD 8,+10 It is to be a 16 bit multiplication. LD A,H A holds the high byte. LD C,L C holds the low byte. LD HL,+0000 Initialise the result to zero. 1311 HL-LOOP ADD HL,HL Double the result. JR C,131A,HL-OVER Jump if overflow. RL C Rotate bit 7 of C into the carry. RLA Rotate the carry into bit 0 and bit 7 into the carry flag. JR NC,131D,HL-AGAIN Jump if the carry flag is reset. ADD HL,DE Otherwise add DE in once. 131A HL-OVER JP C,0ED3,REPORT-4 Report the overflow error. 131D HL-AGAIN DJNZ 1311,HL-LOOP Until 16 passes have been made. POP BC Restore BC. RET Finished.
This is the actual assignment routine for both the LET and the INPUT commands.
When the destination variable is a newly declared variable then DEST will point to the first letter of the variable name as it occurs in the current BASIC line. Bit 1 of FLAGX will be set.
However if the destination variable has been used previously they bit 1 of FLAGX will be reset and DEST will point for a numeric variable to the location before the five bytes of the existing number; and for a string variable to the first location used by the existing string. The use of DEST in this manner applies to simple variables and to the elements of arrays.
Bit 0 of FLAGX is reset if the variable name indicates an array variable.
Initially the currant value of DEST is collected and bit 1 of FLAGS tested.
1321 LET LD HL,(DEST) Fetch the present value of DEST. BIT 1,(FLAGX) Jump if dealing with an existing JR Z,136E,L-EXISTS variable.
A new variable is being used so the length of the name is found.
LD BC,+0005 Assume a numeric variable. 132D L-EACH-CH INC BC For each character of a name: 132E L-NO-SP INC HL Move along the name. LD A,(HL) Put the character in the A register. AND A Is the character a'space'? JR Z,132E,L-NO-SP Ignore any spaces in a name. CALL 14D2,ALPHANUM Is the character alphanumeric? JR C,132D,L-EACH-CH Jump back for another if it is so. CP +0D Is the present character a '8'7 JP Z,13C8,L-NEWS Jump as dealing with a new string variable a simple string.
The appropriate amount of room for the variable name and its value is made available in the work space. The characters of a long name, with the exception of the first letter, are transferred. The last letter is ORed with Hex.BO.
RST 0030,8C-SPACES Make the appropriate amount of free space available in the work space. PUSH DE DE points to the 2nd new space. LD HL,(DEST) Pointer to the start of the name. DEC DE DE points to the 1st new space. LD A,C Get the size of the variable. SUB +06 The minimum site is 6. LD B,A B equals the number of extra letters. LD A,+40 Prepare to mark the first letter. JR Z,1359,L-SINGLE Jump forward if name is short. 134B L-CHAR INC HL For each letter of a long name: LD A,(HL) Put the character in the A register. AND A Again ignore any spaces. JR Z,134B,L-CHAR Jump back if it is a 'space'. INC DE For each location in the work space. LD (DE),A Transfer the character of the name. DJNZ 134B,L-CHAR Until the whole name is done. OR +80 Prepare to mark the last letter. LD (DE),A Now mark it. LD A,+80 Prepare to mark the first letter. 1359 L-SINGLE LD HL,(DEST) Pointer to the start of the name. XOR (HL) Mark the first letter as is required. POP HL Fetch the pointer to the 2nd free location.
The work space is now cleared up to the current entry and this entry is included in the variable area
CALL 13E7,L-CLEAR Clear the work space from E-LINE to (HL) & include the new entry in the variable area by changing the pointers.
An RST 0028 instruction is used to 'delete' the 'last value' on the calculator stack. However this value is not overwritten.
1361 L-NUMERIC PUSH HL Save the pointer to the location after the 'value' of the variable. RST 002B,FP-CALC. Now use the calculator. DEFB +02,delete,19E3 This moves STKEND back five DEFB +34,end-calc.,002B locations. POP HL Restore the pointer.
The HL register pair is made to point to the first location of the 'value' of the variable.
LD BC,+0005 There are 5 locations. AND A Prepare for subtraction. SBC HL,BC HL now points to the first location. JR 13AE,L-ENTER Jump forward to enter the value.
Enter here if dealing with a variable name that has already been used. Bit 6 of FLAGS is tested to separate numeric variables from string, or array of string variables.
136E L-EXISTS BIT 6,(FLAGS) Jump forward if dealing with a JR Z,137A,L-DELETES string variable.
The new numeric value overwrites the old value, but first the HL register pair must be set to point 'one location past' the old value.
LD DE,+0006 Six bytes for a numeric variable. ADD HL,DE HL now points one past. JR 1361,L-NUMERIC Jump back to do the actual overwriting.
The parameters of the string variable are fetched and simple string variables separated from array of string variables.
137A L-DELETES LD HL,(DEST) Fetch the start pointer. LD BC,(STR LEN) Fetch the length counter. BIT 0,(FLAGX) Jump if dealing with a JR NZ,1387,L-ADDS simple string.
The new string must not be a null string.
LD A,B High length counter. OR C Low length counter. RET Z Return if the string is null.
The next stage involves making available an appropriate amount of room for the new string in the work space.
PUSH HL Save the start pointer. RST 0030,BC-SPACES Make room in the work space. PUSH DE Save the pointer to the 2nd space. PUSH BC Save the length. LD D,H HL holds the address of STKBOT 1. LD E,L INC HL HL now points to STKBOT. LD (HL),+00 A space is entered. LDDR All of the new locations are now set to zero (except the 1st space).
The pointer to this 'new' area in the workspace is saved whilst the parameters of the 'new' string are fetched from the calculator stack.
PUSH HL Save the 'new' area pointer. CALL 13FB,STK-FETCH Fetch the parameters. POP HL Restore the pointer.
The length of the string is now compared to the amount of room that has been made available for it.
EX (SP),HL 'Length' of new area to HL. 'Pointer' to new area to stack. AND A Prepare for subtraction. SBC HL,BC Find the difference in the lengths. ADD HL,BC Add it back. JR NC,13A3,L-LENGTH Jump if the 'new' string will fit. LD B,H The procrustean shortening of a LD C,L string that is too long. 13A3 L-LENGTH EX (SP),HL 'Length' of new area to stack. 'Pointer' to new area to HL.
As long as the new string is not a null string it is copied into the workspace. Procrustean lengthening is achieved by only moving the number of characters specified in the BC register pair.
EX DE,HL 'Start' of string to HL. 'Pointer' to new area to DE. LD A,B Test the 'length' of OR C the new string. JR Z,13AB,L-IN-W-S Jump forward if a null string. LDIR The string is copied to the area reserved for it in the work space.
13AB L-IN-W-S POP BC 'Length' of new area. POP DE Pointer to the 2nd space. POP HL The pointer to the start of the element in the array.
The string is now copied from the work space to its specified place in the variable area. Note: Also used to transfer numeric values.
13AE L-ENTER EX DE,HL Change pointers over. LD A,B There is no need to move a string OR C or number that has 'no length' RET Z attributed to it. PUSH DE Save the address of the element. LDI R Move the string or number. POP HL The address of the element is in HL. RET Finished with array variables.
When a new string is to replace an old string the new string is entered as if it were a totally new variable before the old copy of that variable is reclaimed.
13B7 L-ADDS DEC HL HL is made to point to the DEC HL variable name of the old copy of the DEC HL string in the variable area. LD A,(HL) The name goes into the A register. PUSH HL The pointer to the name is saved. PUSH BC The length of the old copy is saved.
The new string is copied into the work space and included in the variable area by calling L-STRING before the old copy is reclaimed.
CALL 13CE,L-STRING Add the new string to the variables. POP BC The length of the old copy. POP HL The starting address of the old copy. INC BC The total length of a string INC BC variable is given by adding three INC BC to the number of characters. JP OA60,RECLAIM-2 Exit by jumping to RECLAIM-2 which reclaims BC bytes starting at )HL).
A totally new string variable is added to the variable area as follows:
The variable's name is collected from the BASIC line and marked as representing a simple string.
1308 L-NEWS LD A,+60 Prepare for the marking of the name. LD HL,(DEST) Fetch the address of the name. XOR (HL) Mark the name.
The parameters of the string are fetched and the appropriate amount of room is made for the string in the work space.
13CE L-STRING PUSH AF Save the name. CALL 13F8,STK-FETCH Fetch the parameters of the string. EX DE,HL Switch over the pointers. ADD HL,BC Find the end of the new string +1. PUSH HL Save the pointer to the 'end + 1'. INC BC Add three to the number of INC BC characters to make the full length INC BC that is required. RST 0030,BC-SPACES Make the room in the work space. EX DE,HL End of the work space in DE. POP HL Restore 'end + 1' of string.
The new string can now be copied into the room prepared for it in the work space. The 'length' is calculated and added to the variable.
DEC BC Move the new string and DEC BC one extra byte. PUSH BC Save the count of the bytes. LDDR Copy the string to the work space. EX DE,HL The location before the string to HL. POP BC Restore the count. DEC BC The length of the new string. LD (HL),B Enter high-length. DEC HL Back one. LD (HL),C Enter low-length. POP AF Restore the variable name.
All of the work space before the location pointed to by the HL register pair is reclaimed and the variable name is entered into the '80 byte'.
13E7 L-CLEAR PUSH AF Save the variable name briefly. CALL 14C7,RECLAIM-3 Reclaim the work space up to (HL). POP AF Restore the variable name. DEC HL Now point to the '80 byte'. LD (HL),A Overwrite with the variable name.
The system variable E-LINE is set to equal STKBOT and hence clears the work space and an '80' is entered into the extra location at the end of the new string.
LD HL,(STKBOT) Get the pointer STKBOT. LD (E-LINE),HL Make E-LINE equal STKBOT. DEC HL The extra byte after the new string. LD (HL),+80 Make the new '80 byte'. RET Finished adding a new string.
This subroutine collects either a five byte floating-point number, or a set of parameters that define a string, from the calculator stack. These five bytes represent the current 'last value'.
13F8 STK-FETCH LD HL,(STKEND) Get STKEND. DEC HL Back one. LD B,(HL) The fifth value. DEC HL Back one. LD C,(HL) The fourth value. DEC HL Back one. LD D,(HL) The third value. DEC HL Back one. LD E,(HL) The second value. DEC HL Back one. LD A,(HL) The first value. LD (STKEND),HL The new value for STKEND. RET Finished.
The routine starts with a search of the variable area to ascertain if a variable with the same variable name already exists. If such a variable is found then it is deleted by reclaiming the bytes involved.
The size of the new array is calculated and the appropriate amount of room is made available in the variable area. The parameters of the variable are entered and all of the elements are set to zero.
1409 DIM CALL 111C,LOOK-VARS Look for an existing variable. 140C D-RPORT-C JP NZ,0D9A,REPORT-C Give report C as there is an error. CALL 0DA6,SYNTAX-Z Jump forward to D-RUN unless JR NZ,141C,D-RUN syntax is being checked. RES 6,C Presume a numeric array. CALL 11A7,STK-VAR Check the syntax further. CALL 0D1D,CHECK-END Exit via CHECK-END. 141C D-RUN JR C,1426,D-LETTER Jump if no existing variable. PUSH BC Save the variable name. CALL 09F2,NEXT-ONE Find the start of the next variable. CALL 0A60,RECLAIM-2 Reclaim the bytes of the existing variable. POP BC Restore the variable name.
The initial parameters of the variable are set.
1426 D-LETTER SET 7,C An array variable name has bit 7 net. LD B,+00 Make the dimension counter zero. PUSH BC Save the counter and the name. LD HL,+0001 Element length for an array of strings. BIT 6,C Jump if dealing with an JR NZ,1434,D-SIZE array of strings. LD L,+05 Element length for a numerical array. 1434 D-SIZE EX DE,HL Element length is to be in DE.
The following loop is passaged for each dimension that is specified in the BASIC line. The total number of bytes required for the elements of the array is built up in the DE register pair.
1435 D-NO-LOOP RST 0020,NEXT-CH Move CH-ADD on one byte. LD H,+40 Set a'limit-value'. CALL 12DD,INT.-EXP1 Evaluate the parameter. JP C,1231,REPORT-3 Give an error if out of range. POP HL Restore the counter and the name. PUSH BC Stack the result of INT: EXP1. INC H Increase the dimension count. PUSH HL Save the counter and the name. LD H,B Result of INT.-EXP1 is LD L,C required in HL. CALL 1305,HL=HL*DE Check that enough RAM is available EX DE,HL and transfer the byte total to DE. RST 001B,GET-CH. Get the present character. CP +1A Is it a ','? JR 2,1435,D-NO-LOOP Jump back if there is another dimension to be included.
The final values of the parameters are calculated.
CP +11 Is it a 'P? JR NZ,140C,D-RPORT-C Jump if there has been an error. RST 0020,NEXT-CH Move CH-ADD on one byte. POP BC Restore the counter and the name. LD A,C Move the variable name to A. LD L,B Move the dimension counter to L. LD H,+00 Clear the H register. INC HL Increase the dimension count by INC HL two and then double the result to ADD HL,HL obtain the number of bytes required for the parameters. ADD HL,DE Add to this the number of bytes required for the elements. JP C,0ED3,REPORT-4 Out of RAM' if result too great. PUSH DE Save the element-byte total. PUSH BC Save the counter and the name. PUSH HL Save the 'total'. LD B,H Move the 'total' to the LD C,L BC register pair.
The appropriate amount of room is now made in the variable area.
LD HL,(E-LINE) Fetch E-LINE. DEC HL Point to the '80 byte'. CALL 099E,MAKE-ROOM Make BC spaces before the' 80 byte'. INC HL Make HL point to the first space.
The parameters are now entered.
LD (HL),A Enter the variable name. POP BC Fetch the 'total' and DEC BC decrease it by three to give DEC BC the required number. DEC BC INC HL Now point to the second location. LD (HL),C Enter the low-total. INC HL Point to the third location. LD (HL),B Enter the high-total. POP AF Fetch the 'dimension counter'. INC HL Point to the fourth location. LD (HL),A Enter the counter.
The elements of the array are all set to zero.
LD H,D HL is made to point to the last LD L,E byte. DEC DE DE now points to the last but one. LD (HL),+00 Enter a 'zero'. POP BC Fetch the element-byte total. LDDR Enter a'zero' into all the other bytes and finish with HL pointing to the byte before the first element.
The 'dimension-sizes' are now entered.
147F DIM-SIZES POP BC Get the last dimension-size. LD (HL),B Enter the high byte. DEC HL Go back one location. LD (HL),C Enter the low byte. DEC HL Go back another location. DEC A Decrease the dimension counter. JR NZ,147F,DIM-SIZES Repeat the operation until the counter reaches zero. RET Finished.
This subroutine is a continuation of RST 0030,BC-SPACES, and is used to increase the size of the work space by the number of bytes specified.
1488 RESERVE LD HL,(STKBOT) Fetch the current value of STKBOT. DEC HL Make HL point to the last location of the current work space. CALL 099E,MAKE-ROOM Create BC spaces in the work space before the last location. INC HL HL points to the 1st new space. INC HL HL points to the 2nd new space. POP BC Fetch the old value of E-LINE LD (E-LINE),BC and restore it unaltered. POP BC Restore BC, the number of new spaces. EX DE,HL Now DE points to the 2nd new space. INC HL Make HL point to the last location of the work space once again. RET Finished.
This routine 'clears' the variable area.
149A CLEAR LD HL,(VARS) Fetch the current value of VARS. LD (HL),+80 Make this byte the '80 byte'. INC HL Point to the next location. LD (E-LINE),HL Make E-LINE point to this location.
This subroutine 'clears' the work space.
14A3 X-TEMP LD HL,(E-LINE) Fetch the current value of E-LINE.
This subroutine 'places' an 'empty' calculator stack at the position pointed to by the HL register pair.
14A6 SET-STK-B LD (STKBOT),HL Set the bottom of the stack. 14A9 SET-STK-E LD (STKEND),HL Set the top of the stack. RET Finished.
This subroutine sets the workspace to hold a line consisting of only the cursor marker and the NEWLINE characters. The lower screen is set to be two lines in size and the calculator stack is cleared
14AD CURSOR-IN LD HL,(E-LINE) Fetch the current value of E-LINE. LD (HL),+7F Enter the cursor marker. INC HL Move to the next location. LD (HL),+76 Enter the NEW LINE character. INC HL Make HL point to the next location. LD (DFSZ),+02 Lower screen is to be two lines. JR 14A6,SET-STK-B Jump back to clear the calculator stack.
This subroutine makes MEM point to MEMBOT and returns STKEND pointing to the top of the calculator stack.
14BC SET-MEM LD HL,+MEMBOT Make HL point to MEMBOT. LD (MEM),HL Make MEM point to MEMBOT. LD HL,(STKBOT) Make HL.point to the bottom of the calculator stack. JR 14A9,SET-STK-E Jump back to make STKEND once again refer to the calculator stack.
This subroutine 'clears' the work space from its start to the location before that pointed to by the HL register pair.
14C7 RECLAIM-3 LD DE,(E-LINE) Fetch the current value of E-LINE. JP 0A5D,RECLAIM-1 Jump back to perform the clearance.
This subroutine returns with the carry flag set if the present value of the A register denotes a valid letter of the alphabet.
14CE ALPHA CP +26 Test against Hex. 28. The code for 'A'. JR 14D4,ALPHA-2 Jump forward.
This subroutine returns with the carry flag set if the present value of the A register denotes a valid digit or letter.
14D2 ALPHANUM CP +1C Test against Hex. IC. The code for '0'. 14D4 ALPHA-2 CCF Complement the carry flag. RET NC Return if not a valid character code. CP +40 Test against the upper limit. RET Finished.
As part of syntax checking decimal numbers that occur in a BASIC line are converted to their floating-point forms. This subroutine reads the decimal number digit by digit and gives its result as a 'last value' on the calculator stack. Firstly any integer part is converted.
14D9 DEC-TO-FP CALL 1548,INT-TO-FP Forms a 'last value' of the integer.
If the next character is a '.', they consider the decimal fraction.
CP +1B Is the character a"? JR NZ,14F5,E-FORMAT Jump forward to see if it is an 'E'. RST 002B,FP-CALC. Now use the calculator. DEFB +A1,stk-one,1A51 Find the floating-point form of the DEFB +C0,st-mem-0,1A63 decimal number'1', and save it in DEFB +02,delete,19E3 the memory area. DEFB +34,end-calc.,002B 14E5 NXT-DGT-1 RST 0020,NEXT-CH Get the next character. CALL 1514,STK-DIGIT If it is a digit then stack it. JR C,14F5,E-FORMAT If not jump forward. RST 0028,FP-CALC. Now use the calculator. DEFB +E0,get-mem-0,1A45 For each passage of the loop, the DEFB +A4,stk-ten,1A51 number saved in the memory area is DEFB +05,division,1882 fetched, divided by 10 and restored. DEFB +C0,st-mem-0,1A63 i.e. going from.1 to .01 to .001 etc. DEFB +04,multiply,17C6 The present digit is multiplied by DEFB +0F,addition,1755 the 'saved number' and added to the DEFB +34,end-calc.,002B 'last value'. JR 14E5,NXT-DGT-1 Jump back to consider the next character.
Next consider any 'E-notation', i.e. the form xEm where m is a positive or negative integer.
14F5 E-FORMAT CP +2A Is the present character en'E'? RET NZ Finished unless it is so. LD (MEMBOT),+FF Use the first byte of 'mem-O' as a sign-flag. RST 0020,NEXT-CH Get the next character. CP +15 Is it a '+'? JR Z,1508,SIGN-DONE Jump forward. CP +16 Is it a "? JR NZ,1509,ST-E-PART Jump if neither '+' nor'-'. INC (MEMBOT) Change the sign of the flag. 1508 SIGN-DONE RST 0020,NEXT-CH Point to the first digit. 1509 ST-E-PART CALL 1548,INT-TO-FP Use this subroutine to stack the whole of the exponent, i.e. ABS m. RST 0028,FP-CALC. Now use the calculator. DEFB +E0,get.mem-0,1A45 Fetch the sign-flag. DEFB +00,jump-true,1C2F Jump if the sign-flag denotes '+'. DEFB +02, to 1511,E-FP DEFB +18,negate,1AA0 Negate the value of the exponent. 1511 E-FP DEFB +38,e-to-fp,155A The 'last value' is given the result of x`10**m. DEFB +34,end-calc.,002B RET Finished.
This subroutine simply returns if the current value held in the A register does not represent a digit but if it does then the floating-point form for the digit becomes the 'last value' on the calculator stack.
1514 STK-DIGIT CP +1C Is the value Hex.1C? RET C Return if not in range. CP +26 Is the value Hex.26? CCF Complement the carry flag. RET C Return if not in range. SUB +1C Replace code by the actual digit.
This subroutine gives the floating-point form for the absolute binary value currently held in the A register.
151D STACK-A LD C,A Transfer the value to the C register. LD B,+00 Clear the B register.
This subroutine gives the floating-point form for the absolute binary value currently held in the BC register pair.
1520 STACK-BC LD IY,+ERR-NR Re-initialise the IY register pair. PUSH BC Save BC briefly. RST 0028,FP-CALC. Use the calculator. DEFB +A0,stk-zero,l A51 Put zero on the stack so as to DEFB +34,end-calc.,002B reserve 5 bytes. (Last value = 01 POP BC Restore BC. LD (HL),+91 Set exponent to 17 decimal for a LD A,B 16-bit number, and then test AND A whether B is in fact zero. JR NZ,1536,NORML-FP Jump forward when B is non-zero. LD (HL),A Else, zero to exponent byte. OR C Return if C is also zero as the RET Z 'last value' is to be zero. LD B,C Transfer C to B. LD C,(HL) Clear the C register. LD (HL),+89 Set exponent to 9 decimal for an 8-bit number. 1536 NORML-FP DEC (HL) Normalize the floating-point form by SLA C shifting C & 8 left until a set RL B bit is found. The exponent is JR NC,1536,NORML-FP decremented on each loop. SRL B Now shift B & C right, resetting the RR C set bit for a positive number. INC HL Point to the 2nd byte. LD (HL),B Copy over the B register. INC HL Point to the 3rd byte. LD (HL),C Copy over the C register. DEC HL Return with the HL register DEC HL pair pointing to the exponent. RET Finished.
This subroutine returns a 'last value' on the calculator stack that is the result of converting an integer in a BASIC line, i.e. the integer part of a decimal number or the line number, to its floating-point form.
Repeated calls to NEXT-CH fetch each digit of the integer in turn. An exit is made when a character that is not a digit has been fetched.
1548 INT-TO-FP PUSH AF Save the first digit in A. RST 0028,FP-CALC. Use the calculator. DEFB +A0,stk-zero,1 A51 The 'last value' is now zero. DEFB +34,endcalc.,002B POP AF Restore the first digit.
Now a loop is setup. As long as the character is a digit then its floating-point form is found and stacked under the 'last value'. The 'last value' is then multiplied by decimal 10 and added to the 'digit' to form a new 'last value' which is carried back to the start of the loop.
154D NXT-DGT-2 CALL 1514,STK-DIGIT If the character is a digit then RET C stack its floating-point form. RST 0028,FP-CALC. Use the calculator. DEFB +01,exchange,1A72 'Digit' goes under 'last value'. DEFB +A4,stk-ten,1A51 Define decimal 10. DEFB +04,multiply,17C6 'Last value' = 'last value' 10. DEFB +0F,addition,l755 'Last value' ='last value' + 'digit'. DEFB +34,end-calc.,002B RST 0020,NEXT-CH Next character goes into A. JR 154D,NXT-DGT-2 Loop back with this character.
This subroutine gives a 'last value' on the top of the calculator stack that is the result of converting a number given in the form xEm, where m is a positive or negative integer. The subroutine is entered with mat the top of the calculator stack and x underneath m.
The method used is to find the absolute value of m, say p, and to multiply or divide x by 10'p according to whether m is positive or negative.
To achieve this, p is reduced by 7 for as long as possible and then by 1 until it is exhausted. Since p is usually less than decimal 38, no more than 8 loops are commonly taken.
i. Once again the first byte of mem-0 is used as a sign flag. It shows whether multiplication or division by 10""p is required.
Calculator Stack 155A e-to-fp RST 0028,FP-CALC. x, m DEFB +2D,duplicate,19F6 x, m, m DEFB +32,less-0,1AD8 m, 11/01 Logical value of m. DEFB +C0,st-mem-0,1A63 x, m, 11/01 Store sign flag in DEFB +02,delete,19E3 x, m first byte of mem-0. DEFB +27,abs,1AAA x,p p= ABS m.
ii. Now the main loop is entered. It starts by testing p to see whether it is exhausted.
1560 E-VET DEFB +A1,stk-one,1A51 x, p,1 DEFB +03,subtract,174C x, p-1 DEFB +2D,duplicate,19F6 x, p-1, p-1 DEFB +32,less-0,1ADB x, p-1, 11/0) DEFB +00,jump-true,1C2F x, p-1 DEFB +22, to 1587,END-E x, p-1
iii Next p is reduced by 7 if possible, by 1 otherwise; and 10"7 or 10""1 is put on the calculator stack preparatory to multiplying or dividing.
DEFB +2D,duplicate,19F6 x, p-1, p-1 DEFB +30,stkdata,19FC x, p-1, 6 DEFB +33,exponent 83 DEFB +40,(+00,+00,+00) DEFB +03,subtract,174C x, p-1, p-7 DEFB +2D,duplicate,19F6 x, p-1, p-7, p-7 DEFB +32,less-0,1ADB x, p-1, p-7, (1/0) DEFB +00,jump-true,lC2F x, p-1, p-7 DEFB +OC, to 157A,E-ONE x, p-1, p-7 DEFB +01,exchange,1A72 x, p-7, p-1 DEFB +02,delete,19E3 x, p-7 DEFB +01,exchange,1A72 p-7, x DEFB +30,stk-data,19FC p-7, x, 10**7 DEFB +80,four bytes DEFB +48,exponent 98 DEFB +16,+96,+80,(+00) DEFB +2F,jump,1 C23 p-7, x, 10**7 DEFB +04, to 157D,E-M/D p-7, x, 10**7 157A E-ONE DEFB +02,delete,19E3 x, p-1 DEFB +01,exchange,1A72 p-1, x DEFB +A4,stk-ten,1A51 p-1, x, 10
iv. The sign-flag is collected and tested thereby showing whether to multiply or divide by 10"i, where i=1 or 7.After the arithmetic operation a jump is made back to E-YET.
157D E-M/D DEFB +E0,get-mem-0,1A45 p-i, x, 10**i, (1/0) DEFB +00,jump-true,1 C2F p-i, x, DEFB +04, to 1583,E-DIV p-i, x, 10**i DEFB +04,multiply,17C6 p-i, x * 10**i DEFB +2F,jump,1C23 p-i, x * 10**i DEFB +02, to 1584,E-EXC p-i, x * 10**i 1583 E-DIV DEFB +05,division,1882 p-i, x * 10**-i 1584 E-EXC DEFB +01,exchange,1 A72 x*10 ** +/- i, p-i DEFB +2F,jump,lC23 x*10 ** +/- i, p-i DEFB +DA, to 1560,E-YET x*10 ** +/- i, p-i
v. An exit is made from the subroutine with the required 'last value'.
1587 END-E DEFB +02,delete,19E3 x`10. fin DEFB +34,endcalc.,002B RET
This subroutine is called from four different places for various purposes and is used to compress the floating-point 'last value' into the BC register pair.
If the result is too large, i.e. greater than 65535 decimal, then the subroutine returns with the carry f lag set. If the 'last value' is negative then the zero flag is reset.
The low-byte of the result is also copied to the A register.
158A FP-TO-BC CALL 13F8,STK-FETCH Get the 'last value'. AND A Is the exponent zero? JR NZ,1595,NOT-ZERO Jump if it is not so. LD B,A Set B to hold zero. LD C,A Set C to hold zero. PUSH AF Save the carry and the zero flag. JR 15C6,FBC-END Jump forward.
Once the special case of zero has been excluded, the upper limit is considered by comparing the value of the exponent against Hex.91.
1595 NOT-ZERO LD B,E 1st byte of mantissa to B. LD E,C 3rd byte of mantissa to E. LD C,D 2nd byte of mantissa to C. SUB +91 Reduce the exponent by 145 decimal. CCF Complement the carry flag. BIT 7,B The zero flag complements the sign bit, i.e. NZ for -ve numbers. PUSH AF Save the zero and the carry flags. SET 7,B Restore the true numeric bit. JR C,15C6,FBC-END Jump to the end if the exponent is too great.
Note that the exponent byte e holds 128 decimal plus the true exponent, e'.
So far the cases of the exponent byte being zero, or greater than 144 decimal, have been dealt with. The exponent byte is currently in the A register and now has the range -144 to -1 decimal which corresponds to the true exponent e' range of -127 to 16 decimal.
Numbers whose true exponent is in the range 1 to 8 decimal, will compress into a single register, whereas an exponent in the range 9 to 16 requires two registers. Numbers whose true exponents are negative will vanish.
INC A Range is now -143 to 0 decimal. NEG Range is now 143 to 0 decimal. CP +08 Define the true exponents 9 to 16 JR C,15AF,SHIFT-TST and jump forward with them. LD E,C Move 2nd byte of mantissa to E. LD C,B Move 1st byte of mantissa to C. LD B,+00 Clear the B register. SUB +08 Range is now 135 to 0 decimal here.
Note that if the A register now holds zero it means that no shift of BC is needed (e' is 8 or 16 dec.). Otherwise the A register gives the length of the shift right needed. If the shift is to be greater than 8 places then the number will vanish for true exponents -127 to -1).
15AF SHIFT-TST AND A If zero then no shift is needed. LD D,A Transfer shift counter to D. LD A,E Prepare 9th/17th bit for RLCA rounding up. JR Z,15BC,IN-PLACE Jump if A was zero; no shift. 15B5 SHIFT-BC SRL B Shift B & C right D times to RR C produce the correct number. DEC D Decrement the shift counter. JR NZ,1585,SHIFT-BC Loop until D becomes zero. 15BC IN-PLACE JR NC,15C6,FBC-END End if no rounding-up needed; INC BC else round up. LD A,B Test if number now equals 65536 dec. OR C i.e. BC now zero out of range. JR NZ,15C6.FBC-END Jump if in range. POP AF Fetch zero and carry flags. SCF Set carry flag as out of range. PUSH AF Save the zero and carry flags. 15C6 FBC-END PUSH BC Save the result briefly. RST 002B,FP-CALC. Use the calculator. DEFB +34,end-calc.,0026 This makes HL point to STKEND - 5. POP BC Restore the result. POP AF Restore the zero and the carry flags. LD A,C Copy over the low byte of the result. RET Finished.
This short but vital subroutine is called at least 5 times for various purposes. It uses the previous subroutine, FP-TO-BC, to get the' last value' into the A register where this is possible. It therefore tests whether the modulus of the number rounds to more than 255 and if it does the subroutine returns with the carry flag set. Otherwise it returns with the modulus of the number, rounded to the nearest integer, in the A register, and the zero flag set to imply that the number was positive, or reset to imply that it was negative.
15CD FP-TO-A CALL 158A,FP-TO-BC Compress the 'last value' into BC. RET C Return if out of range already. PUSH AF Save the result and the flags. DEC B Again it will be out of range INC B if the B register does not hold zero. JR Z,15D9,FP-A-END Jump if in range. POP AF Fetch the result and the flags. SCF Signal the result is out of range. RET Finished unsuccessful. 15D9 FP-A-END POP AF Fetch the result and the flags. RET Finished successful.
This subroutine is called by the PRINT command routine at OB61 and by STRSat 1 BD5, which converts to a string the number as it would be printed. The subroutine prints X, the 'last value' on the calculator stack. The print format never occupies more than 14 spaces. The subroutine first calculates:
n = INT ( (e' - .5 ) * log10 2 ), where e' is the true exponent.
The number of digits before the decimal point of X is always n, n+1 or n+2.
Next the subroutine calculates:
m = INT ABS X+.5), the decimal representation of which is stored in an ad hoc print buffer in mem-2 to mem-4.
The 8 most significant digits of X, correctly rounded, are printed out from m; 1 or 2 leading zeros in m as needed ensure that the correct number of digits are printed before the decimal point (without the leading zeros of course); trailing zeros are suppressed; and E-format is printed if needed.
So many cases are possible that it is best to try examples, referring to the ZX81 manual as needed.
First the sign of X is taken care of: If X is negative, the subroutine jumps to P-NEG, takes ABS X and prints the minus sign. If X is zero, X is deleted from the calculator stack, a '0' is printed and a return is made from the subroutine. If Xis positive, the routine just continues.
15DB PRINT-FP RST 0028,FP-CALC. Use the calculator. DEFB +2D,duplicate,19F6 X, X DEFB +32less-0,1ADB X, (1/0) Logical value of X. DEFB +00,jump-true,lC2F X DEFB +OB, to 15EA,P-NEG X DEFB +2D,duplicate,19F6 X, X DEFB +33,greater-0,1 ACE X, (1/0) Logical value of X. DEFB +00,jump-true,lC2F X DEFB +0D, to 15F0,P-POS X Hereafter X' = ABS X. DEFB +02,delete,19E3 DEFB +34,end-calc.,0028 LD A,+1C Enter the character code for'0'. RST 0010,PRINT-A Print the '0'. RET Finished as the 'last value' is equal to zero. 15EA P-NEG DEFB +27,ebs,1AAA X' X'=ABS X. DEFB +34,end-calc.,002B X' LD A,+16 Enter the character code for '-'. RST 0010,PRINT-A Print the '-'. RST 0028,FP-CALC. Use the calculator again. 15E0 P-POS DEFB +34,end-calc.,002B Exit with HL pointing to the exponent byte of X'.
The number n is calculated and stored in mem-1, to be recalled for use after the 'print buffer' has been set up. Note that e' is obtained by subtracting Hex.80 from the full exponent e presently addressed by the HL register pair. In fact 128.5 decimal is subtracted all at once. It and log of 2 to base 10 are both stacked as immediate data by calling 'stk-data' at 19FC.
LD A,(HL) Fetch the exponent e of X'. CALL 151D,STACK-A X', e RST 0028,FP-CALC. Use the calculator. DEFB +30,stk-data,19FC X', e, 128.5 (dec) DEFB +78,exponent88 DEFB +00,+80,(+00,+00) DEFB +03,subtract,174C DEFB +30,stk-data,19FC X', e'-.5, log 2 (base 10) DEFB +EF,exponent 7F DEFB +1A,+20,+9A,+85 DEFB +04,multiply,17C6 X', (e'-.5) *log 2 DEFB +24,int,1C46 X', n DEFB +C1,st-mem-1,1A63 X', n (n is copied to mem-1)
iii. Next m is calculated, providing enough digits to give a print buffer from which the 8 most significant digits of X, correctly rounded, can be printed out.
DEFB +30,stk-data,l9FC X',n,8 DEFB +34,exponent 84 DEFB +00,(+00,+00,+00) DEFB +03,subtracn,174C X', n-8 DEFB +18,negate,lAA0 X', 8-n
DEFB +38,e-to-fp,155A 10"(8-n)'X' DEFB +A2,stk-half,1A51 10"(8-n)'X', .5 DEFB +0F,addition,1755 10"(8-n)'X'+.5 DEFB +24,int,1C46 m DEFB +34,endcalc.,002B m
Ten digits from m are now stored in mem-3 and mem-4 in reverse order. This means that up to 2 leading zeros are stored (since m has 8 to 10 digits) and this will ensure that the correct number of digits are printed before the decimal in X.
LD HL,+406B Address of last byte of mem-2. LD (HL)+90 Marker byte Hex.90 see 1620 below. LD B,+0A B will count the 10 digits.
Perform the following loop 10 times.
1615 NXT-DGT-3 INC HL Each byte of mem-3 and mem-4. PUSH HL Save the pointer. PUSH BC Save the digit-counter. RST 0028,FP-CALC. Use the calculator. DEFB +A4,stk-ten,1A51 m, 10 DEFB +2E,n-mod-m,1C37 m mod 10, INT (m/10) DEFB +01,exchange,1A72 INT (m/10), m mod 10 DEFB +34,end-calc.,002B CALL 15CD,FP-TO-A A will hold m mod 10. 1620 OR +90 Add left nibble of Hex.9 to each digit; this ensures full carry on half carry after DAA. POP BC Restore the digit-counter. POP HL Restore the pointer. LD (HL),A Store this digit in the buffer. DJNZ 1615,NXT-DGT-3 Until 10 digits have been stored.
Pass over any leading zeros.
INC HL Point one-past the end of mem-4. LD BC,+0008 Looking for 8 digits. PUSH HL Save the pointer. 162C GET-FIRST DEC HL Pass over any leading zeros; the LD A,(HL) first non-zero digit will be the CP +90 first digit of X to be printed. JR Z,162C,GET-FIRST Jump back if digit is zero.
Round up the digits if necessary.
SBC HL,BC Point to the 9th digit; use it to PUSH HL round up 8th digit; first save the LD A,(HL) pointer here, then add Hex.6B. ADD A,+6B (8B + 95 = 0100 Hex. & carry set) PUSH AF Save the carry flag. 1639 ROUND-UP POP AF Restore the carry inside loop. INC HL Increment the pointer. LD A,(HL) Get the digit and round it up by ADC A,+00 adding in the carry. DAA Set the carry if the digit becomes 10 decimal. PUSH AF Save the new carry. AND +0F Remove the left nibble of the digit. LD (HL),A Store the digit. SET 7,(HL) This changes Hex.00 to Hex.80 and prevents any final 0's after the decimal from being printed. (see 164B,MARKERS) JR Z,1639,ROUND-UP Go for any carry ripple or further final zeros. POP AF Discard the carry. POP HL Restore the pointer to the 9th digit.
Enter six marker bytes.
LD B,+06 These six markers will end output 1648 MARKERS LD (HL),+80 by setting the overflow flag after DEC HL DEC and INC see 16C4 and 16CA DJNZ 164B,MARKERS below.
Note that the markers are in the 6 locations which precede the 8 significant digits of the number; no they will end the output even after 13 digits are printed; a marker will turn into a '0' when its left nibble is cleared.
v. The digits can now be printed.
RST 0028,FP-CALC. Use the calculator. DEFB +02,delete,19E3 Delete the '0' left on the stack. DEFB +E1,get-mem-1,1A45 Get the number n from mem-1. DEFB +34,endcalc.,002B CALL 15CD,FP-TO-A Put ABS n into the A register. JR Z,165B,SIGND-EXP If n positive (Z flag set), jump. NEG Else, negate A. 165B SIGND-EXP LD E,A A now holds true n; copy to E. INC E INC E E now holds n+2. POP HL Get the pointer to one-past the end of mem-4. 165F GET-FST-2 DEC HL Find first non-zero digit of X DEC E again, thus passing over the 1 or 2 LD A,(HL) leading zeros that may be present; AND +0F decrease E to ensure that the correct JR Z,165F,GET-FST-2 number of digits before the decimal are printed. LD A,E Put count back into A; at this point -5 and 12 are the critical values of the counter. SUB +05 Subtract 5; -10 and 7 are now the CP +08 critical values; i.e. the jump to JP P,1682,E-NEEDED E-NEEDED will now occur, unless A CP +F6 is less than 8, or greater than 245. JP M,1682,E-NEEDED 1245 dec. is -11 in 2's comp.) ADD A,+06 Add 6, giving the true critical values, i.e. -4 and 13.
Note that A now contains the correct number of digits before the decimal in X, and that these digits will be printed in full if they are not more than 13 decimal, while up to 4 initial zeros will be printed after the decimal if A is negative. Outside that range E-format will be needed.
JR Z,16BF,OUT-ZERO If A holds zero then go and print a '0' and continue into decimal part. JP M,16B2,EXP-MINUS If A is minus then to and print the 'decimal-point' and the digits. LD B,A A is positive, so transfer to B. 167B OUT-B-CHS CALL 16D0,OUT-NEXT Print B characters. DJNZ 167B4OUT-B-CHS Then jump forward to test whether JR 16C2,TEST-INT just an integer, or a 'decimal-point' is needed.
E-format is required.
1682 E-NEEDED LD B,E B now contains the correct integer to follow 'E' of E.format. CALL 16D0,OUT-NEXT Print the first digit. CALL 16C2,TEST-INT Test whether there are more non-zero digits, in which case a 'decimal-point' will be needed. LD A,+2A Enter the character code for 'E'. RST 0010,PRINT-A Print the 'E'. LD A,B Transfer the 'exponent' integer to A. AND A Set the flags. JP P,1698,PLUS-SIGN If positive, jump and print a'+', NEG Else, change its sign. LD B,A Transfer back to B, briefly. LD A,+16 Enter the character code for'-'. JR 169A,OUTSIGN Jump forward. 1698 PLUS-SIGN LD A,+15 Enter the character code for '+'. 169A OUT-SIGN RST 0010,PRINT-A Print the sign character. LD A,B Transfer the 'exponent' back to A. LD B,+FF Now reduce A mod 10 to give B equal 169E TEN-MORE INC B to INT (A/10); initialise B to -1 SUB +OA (2's comp.) and increment it each JR NC,169E,TEN-MOR E time A is decreased by 10. ADD A,-1-0A After the loop, restore the last 10 LD C,A to A; and store A in the C register. LD A,B Transfer the 'tens' to A. AND A Test to we if there are any 'tens'. JR Z,16AD,BYTE-TWO Jump forward if no 'tens'. CALL 07EB4,OUT-CODE Print the first digit. 16AD BYTE-TWO LD A,C Fetch the 'unit' digit. CALL 07EB4,OUT-CODE Print the digit. RET Finished with E-format.
Decimal format is required.
1682 EXP-MINUS NEG A was negative but in range for simple printing so the format is .000...dddd with up to 4 zeros. LD B,A B will count out the zeros. LD A,+1B Enter the character code for': . RST 0010,PRINT-A Now print the 'decimal-point'. LD A,+1C Enter the character code for '0'. 16BA OUT-ZEROS RST 0010,PRINT-A Print the'0'. DJNZ 16BA,OUT-ZEROS Until B reaches zero. JR 16C8,TEST-DONE Exit via TEST-DONE to print the digits until they also are finished.
The special case of the 'exponent' being zero.
16BF OUT-ZERO LD A,+1C Enter the character code for '0'. RST 0010,PRINT-A Print the '0' and continue with TEST-INT to print the decimal part.
If the next digit to be printed is a 'marker' byte then the subroutine returns, otherwise the decimal point is printed and the subroutine enters TEST-DONE.
16C2 TEST-INT DEC (HL) This gives PE (overflow/parity flag set) if (HL) was Hex.80. INC (HL) PE is kept, incrementing to Hex.80.
16C4 RET PE So a 'marker' byte forces a return. LD A,+1 B Enter the character code for ." . RST 0010,PRINT-A Now print the 'decimal-point'.
Note that the decimal point is not printed if the number is an integer, all printed, or if there is just one digit to go before the 'E' of the exponent part.
The digits in the ad hoc print buffer, mem-2 to mem-4, are printed in turn until a 'marker' byte is found.
16C8 TEST-DONE DEC (HL) Test the digit to see if it is a INC (HL) 'marker' (see TEST-INT). 16CA RET PE Return when a 'marker' is found. CALL 16D0,OUT-NEXT Print the digit. JR 16CB,TEST-DONE Jump back to consider the next digit.
This subroutine prepares the current digit for printing, passes it to OUT-C0DE and moves the pointer to the next digit.
16D0 OUT-NEXT LD A,(HL) Fetch the present digit. AND +0F Mask off any unwanted bits. CALL 07EB,OUT-CODE Pass the digit for actual printing. DEC HL Move the pointer back an address. RET Finished.
This subroutine is the first of four subroutines that are used by the main arithmetic operation routines SUBTRACTION, ADDITION, MULTIPLICATION and DIVISION.
This particular subroutine prepares a floating-point number for addition, mainly by replacing the sign bit with a true numerical bit, 1, and negating the number (2's complement) if it is negative. The exponent is returned in the A register and the first byte is net to Hex.00 for a positive number and Hex.FF fora negative number.
16DB PREP-ADD LD A,(HL) Transfer the exponent to A. LD (HL),+00 Presume a positive number. AND A If the number is zero then the RET Z preparation is already finished. INC HL Now point to the sign byte. BIT 7,(HL) Set the zero flag for positive number. SET 7,(HL) Restore the true numeric bit. DEC HL Point to the first byte again. RET Z Positive numbers have been prepared, but negative numbers need to be 2's complemented. PUSH BC Save any earlier exponent. LD BC,+0005 There are 5 bytes to be handled. ADD HL,BC Point one-past the last byte. LD B,C Transfer the '5' to B. LD C,A Save the exponent in C. SCF Set carry flag for negation. 16EC NEG-BYTE DEC HL Point to each byte in turn. LD A,(HL) Get each byte. CPL One's complement the byte. ADC A,+00 Add in carry for negation. LD (HL),A Restore the byte. DJNZ 16EC,NEG-BYTE Loop the '5' times. LD A,C Restore the exponent to A. POP BC Restore any earlier exponent. RET Finished.
This subroutine is called by ADDITION, MULTIPLICATION and DIVISION to get two numbers from the calculator stack and put them into the registers, including the exchange registers.
On entry to the subroutine the HL register pair points to the first byte of the first number and the DE register pair points to the first byte of the second number.
When the subroutine is called from MULTIPLICATION or DIVISION the sign of the result Is saved in the second byte of the first number.
16F7 FETCH-TWO PUSH HL HL is preserved. PUSH AF AF is preserved.
Call the five bytes of the first number M1, M2, M3, M4 & M5. and for the second number N1, N2, N3, N4 & N5.
LD C,(HL) M1 to C. INC HL Next. LD B,(HL) M2 to B. LD (HL),A Copy the sign of the result to (HL). INC HL Next. LD A,C M7 to A. LD C,(HL) M3 to C. PUSH BC Save M2 & M3 on the machine stack. INC HL Next. LD C,(HL) M4 to C. INC HL Next. LD B,(HL) M5 to B. EX DE,HL HL now points to N1. LD D,A M1 to D. LD E,(HL) N1 to E. PUSH DE Save M1 & N1 on the machine stack. INC HL Next. LD D,(HL) N2 to D. INC HL Next. LD E,(HL) N3 to E. PUSH DE Save N2 & N3 on the machine stack. EXX Get the exchange registers. POP DE N2toD'&N3 to E'. POP HL Ml to H' & N1 to L'. POP BC M2 to B' & M3 to C' . EXX Get the original not of registers. INC HL Next. LD D,(HL) N4 to D. INC HL Next. LD E,(HL) N5 to E. POP AF Restore the original AF. POP HL Restore the original HL. RET Finished.
Summary M1 M5 are in: H', B', C', C, B. N1 N5 are in: L', D', E', D, E. HL points to the first byte of the first number.
This subroutine shifts a floating-point number up to 32 decimal, Hex.20, places right to line it up properly for addition. The number with the smaller exponent has been put in the addend position before this subroutine is called. Any overflow to the right, into the carry, is added back into the number. If the exponent difference is greater than 32 decimal, or the carry ripples right back to the beginning of the number then the number is set to zero so that the addition will not alter the other number (the augend).
171A SHIFT-FP AND A If the exponent difference is zero, RET Z the subroutine returns at once. CP +21 If the difference is greater than JR NC,1736,ADDEND-0 Hex.20, jump forward. PUSH BC Save BC briefly. LD B,A Transfer the exponent difference to B to count the shifts right. 1722 ONE-SHIFT EXX Arithmetic shift right for L', SRA L preserving the sign marker bits. RR D Rotate right with carry D', E', RR E D&E. EXX Thereby shifting the whole five bytes RR D of the number to the right as RR E many times as B counts. DJNZ 1722,0NE-SHIFT Loop back until B reaches zero. POP BC Restore the original BC. RET NC Done if no carry to retrieve. CALL 1741,ADD-BACK Retrieve carry. RET NZ Return unless the carry rippled right back. In this case there is nothing to add) 1736 ADDEND-0 EXX Fetch L', D' & E'. XOR A Clear the A register. 1738 ZEROS-4/5 LD L,+00 Set the addend to zero in D', E', LD D,A D & E, together with its marker byte LD E,L (sign indicator) L', which was EXX Hex.00 for a positive number and LD DE,+0000 Hex.FF for a negative number. ZEROS-4/5 produces only 4 zero bytes when called for near underflow at 1833. RET Finished.
Note: The original 8K ROM had 3 further bytes in this subroutine, immediately after the EXX at the label ADDEND-0 laddress 1733 in the old ROM), namely LD A,H; SUB L;& LD H,A. These bytes would seem to have been a mistaken attempt to counteract the effect of bytes 177D 177F below. In fact they caused errors in subtraction and, through the LN function at byte 1D15, in exponentiation and SQR as well. These three bytes were simply omitted when the program was improved. It is interesting to note also that the hardware add-on, fitted to some 'unimproved' machines worked by changing the instruction LD H,A to a DAA instruction and thereby prevented any corruption of the H register.
This subroutine adds back into the number any carry which has overflowed to the right. In the extreme case, the carry ripples right back to the left of the number. When this subroutine is called during addition, this ripple means that a mantissa of 0.5 was shifted a full 32 places right, and the addend will now be set to zero; when called from MULTIPLICATION, it means that the exponent must be incremented, and this may result in overflow.
1741 ADD-BACK INC E Add carry to rightmost byte. RET NZ Return if no overflow to left. INC D Continue to the next byte. RET NZ Return if no overflow to left. EXX Get the next byte. INC E Increment it too. JR NZ,174A,ALL-ADDED Jump if no overflow. INC D Increment the last byte. 174A ALL-ADDED EXX Restore the original registers. RET Finished.
This subroutine simply changes the sign of the subtrahend and carries on into ADDITION. Note that HL points to the minuend and DE points to the subtrahend. (See ADDITION for more details.)
174C SUBTRACT LD A,(DE) Get the exponent byte of subtrahend. AND A Test whether zero. RET Z If no, return. INC DE Point to the sign byte. LD A,(DE) Transfer the sign byte to A. XOR +80 Change the sign bit. LD (DE),A Replace the byte. DEC DE Point to the exponent byte again. Continue on into ADDITION.
The first of three major arithmetical subroutines, this subroutine carries out floating-point addition of two numbers, each with a 4-byte mantissa and a 1-byte exponent. In these three subroutines, the two numbers at the top of the calculator stack are added/multiplied/divided to give one number at the top of the calculator stack, a 'last value'. HL points to the second number from the top, the augend/multiplier/dividend. DE points to the number at the top of the calculator stack, the addend/multiplicand/divisor. Afterwards HL points to the resultant 'last value' whose address can also be considered to be STKEND - 5.
ADDITION first calls PREP-ADD for each number, then gets the 2 numbers from the calculator stack and puts the one with the smaller exponent into the addend position. It then calls SHIFT-FP to shift the addend up to 32 decimal places right to line it up for addition. The actual addition is done in a few bytes, a single shift is made for carry (overflow to the left) if needed, the result is 2's complemented if negative, and any arithmetic overflow is reported; otherwise the subroutine jumps to TEST-NORM to normalize the result and return it to the stack with the correct sign bit inserted into the second byte.
1755 addition EXX Exchange the registers. PUSH HL Save the next literal address. EXX Exchange the registers. PUSH DE Save pointer to the addend. PUSH HL Save pointer to the augend. CALL 16D8,PREP-ADD Prepare the augend. LD B,A Save its exponent in B. EX DE,HL Exchange the pointers. CALL 16D8,PREP-ADD Prepare the addend. LD C,A Save its exponent in C. CP 8 If the first exponent is smaller, JR NC,1769,SHIFT-LEN keep the first number in the LD A,B addend position:otherwise LD B,C change the exponents and the EX DE,HL pointers back again. 1769 SHIFT-LEN PUSH AF Save the larger exponent in A. SUB B The difference between the exponents is the length of the shift right. CALL 16F7,FETCH-TWO Get the two numbers from the stack. CALL 171A,SHIFT-FP Shift the addend right. POP AF Restore the larger exponent. POP HL HL is to point to the result. LD (HL),A Store the exponent of the result. PUSH HL Save the pointer again. LD L,B M4 to L&MS to H, LD H,C (see FETCH-TWO). ADD HL,DE Add the two right bytes. EXX N2 to H'&N3 to L', EX DE,HL (see FETCH-TWO). ADC HL,BC Add left bytes with carry. EX DE,HL Result back in D'E'. LD A,H Add H', L' and the carry; the ADC A,L resulting mechanism will ensure LD L,A that a single shift right is called RRA if the sum of 2 positive numbers XOR L has overflowed left, or the sum of 2 EXX negative numbers has not overflowed left EX DE,HL The result is now in DAD' E'. POP HL Get the pointer to the exponent. RRA The test for shift IH', L' were JR NC,1790,TEST-NEG Hex.00 for positive numbers and Hex.FF for negative numbers). LD A,+01 A counts a single shift right. CALL 171A,SHIFT-FP The shift is called. INC (HL) Add 1 to the exponent; this may JR Z,17B3,ADD-REP-6 lead to arithmetic overflow. 1790 TEST-NEG EXX Test for negative result: get LD A,L sign bit of L' into A this now AND +80 correctly indicates the sign of EXX the result). INC HL Store it in the second byte LD IN LI,A position of the result on DEC HL the calculator stack. JR Z,17B9,GO-NC-MLT If it is zero, then do not 2's complement the result. LD A,E Get the first byte. NEG Negate it. CCF Complement the carry for continued LD E,A negation, and store byte. LD A,D Get the next byte. CPL One's complement it. ADC A,+00 Add in the carry for negation. LD D,A Store the byte. EXX Proceed to get next byte into the LD A,E A register. CPL One's complement it. ADC A,+00 Add in the carry for negation. LD E,A Store the byte. LD A,D Get the last byte. CPL One's complement it. ADC A,+00 Add in the carry for negation. JR NC,17B7,END-COMPL Done if no carry. RRA Else, get .5 into mantissa and add 1 EXX to the exponent; this will be needed INC (HL) when two negative numbers add to give an exact power of 2, and it may lead to arithmetic overflow. 1783 ADD-REP-6 JP Z,1880,REPORT-8 Give the error if required. EXX 17B7 END-COMPL LD D,A Store the last byte. EXX 17B9 GO-NC-MLT XOR A Clear the carry flag. JR 1828,TEST-NORM Exit via TEST-NORM.
This subroutine prepares a floating-point number for multiplication or division, returning with carry set if the number is zero, getting the sign of the result into the A register, and replacing the sign bit in the number by the true numeric bit, 1.
17BC PREP-M/D SCF Set the carry flag. DEC (HL) Test the exponent byte. INC (HL) RET Z If the number is zero, return with both the zero and the carry flags net. INC HL Point to the sign byte. XOR (HL) Get sign for result into A (like signs give plus, unlike give minus); also reset carry flag. SET 7,(HL) Set the true numeric bit. DEC HL Point to the exponent again. RET Return with carry flag reset.
This subroutine prepares the first number for multiplication by calling PREP-M/D, returning if it is zero; otherwise the second number is prepared by again calling PREP-M/D, and if it is zero the subroutine goes to set the result to zero. Next it fetches the two numbers from the calculator stack and multiplies their mantissas in the usual way, rotating the first number (treated as the multiplier) right and adding in the second number (the multiplicand) to the result whenever the multiplier bit is set. The exponents are then added together and checks are made for overflow and for underflow (giving the result zero). Finally, the result is normalized and returned to the calculator stack with the correct sign bit in the second byte.
17C6 multiply XOR A A is set to Hex.00 so that the sign of the first number will go into A. CALL 17BC,PREP-M/D Prepare the first number, and return RET C if zero. (Result already zero.) EXX Exchange the registers. PUSH HL Save the next literal address. EXX Exchange the registers. PUSH DE Save the pointer to the multiplicand. EX DE,HL Exchange the pointers. CALL 17BC,PREP-M/D Prepare the 2nd number. EX DE,HL Exchange the pointers again. JR C,1830,ZERO.RSLT Jump forward if 2nd number is zero. PUSH HL Save the pointer to the result. CALL 16F7,FETCH-TWO Get the two numbers from the stack. LD A,B M5 to A (see FETCH-TWO). AND A Prepare for a subtraction. SBC HL,HL Initialise HL to zero for the result. EXX Exchange the registers. PUSH HL Save Ml & NI (see FETCH-TWO). SBC HL,HL Also initialise H' L' for the result. EXX Exchange the registers. LD B,+21 B counts 33 decimal, Hex.21, shifts. JR 17F8,STRT-MLT Jump forward into the loop.
Now enter the multiplier loop.
17E7 MLT-LOOP JR NC,17EE,NO-ADD Jump forward to NO-ADD if no carry, i.e. the multiplier bit was reset; ADD HL,DE Else, add the multiplicand in EXX D'E' DE (see FETCH-TWO) into the ADC HL,DE result being built up in H'L' HL. EXX 17EE NO-ADD EXX Whether multiplicand was added RR H or not, shift result right in RR L H'L'HL, i.e. the shift is done by EXX rotating each byte with carry, to that RR H any bit that drops into the carry is RR L picked up by the next byte, and the shift continues into B'C'CA. 17F8 STRT-MLT EXX Shift right the multiplier in RR B B'C'CA (see FETCH-TWO & above). RR C A final bit dropping into the carry EXX will trigger another add of the RR C multiplicand to the result. RRA DJNZ 17E7,MLT-LOOP Loop 33 times to get all the bits. EX DE,HL Move the result from, EXX EX DE,HL H'L'HL to D'E'DE. EXX
Next add the exponents together.
POP BC Restore the exponents Ml & N1. POP HL Restore the pointer to the exponent byte. LD A,B Get the sum of the two exponent ADD A,C bytes in A, and the correct carry. JR NZ,180E,MAKE-EXPT If the sum equals zero then clear AND A the carry; else leave it unchanged. 180E MAKE-EXPT DEC A Prepare to increase the exponent by CCF Hex.80.
The rest of the subroutine is common to both MULTIPLICATION and DIVISION.
1810 DIVN-EXPT RLA These few bytes very cleverly make CCF the correct exponent byte. RRA Rotating left then right gets the exponent byte (true exponent plus Hex.BO( into A. JP P,1819,OFLW1-CLR If the sign flag is reset, no report of arithmetic overflow needed. JR NC,1880,REPORT-6 Report the overflow if carry reset. AND A Clear the carry now. 1819 OFLW1-CLR INC A The exponent byte is now complete; JR NZ,1824,OFLW2'CLR but if A is zero a further check for JR C,1824,OFLW2-CLR overflow is needed. EXX If there is no carry set and the BIT 7,D result is already in normal form EXX (bit 7 of D' set) then there is overflow t JR NZ,1880,REPORT-6 report; but if bit 7 of D' is reset, the result is just in range, i.e. just under 2"127. 1824 OFLW2-CLR LD (HL),A Store the exponent byte, at last. EXX Pass the fifth result byte to A for the LD A,B normalization sequence, i.e. EXX the overflow from L into B'.
The remainder of the subroutine that deals with normalization is common to all the arithmetic routines.
1828 TEST-NORM JR NC,183F,NORMALIZE If no carry then normalize now. LD A,(HL) Else, deal with underflow (zero result) AND A or near underflow 182C NEAR-ZERO LD A,+80 (result 2"" -128): JR Z,1831,SKIP-ZERO return exponent to A, test if A is 1830 ZERO-RSLT XOR A zero (case 2""-128) and if so 1831 SKIP-ZERO EXX produce 2"-128 if rntimber is normal; AND 0 otherwise produce zero. CALL 1738,ZEROS-415 The exponent must then be set to RLCA zero (for zero) or 1 (for 2"-1281. LD (HL),A Restore the exponent byte. JR C,1868,OFLOW-CLR Jump if case 2"" -128. INC HL Otherwise, put zero into second LD (HL),A byte of result on the calculator DEC HL stack. JR 1868,0FLOW-CLR Jump forward to transfer the result.
The actual normalization operation.
183F NORMALIZE LD B,+20 Normalize the result by up to 32 1841 SHIFT-ONE EXX decimal, Hex.20, shifts left of BIT 7,D D'E' DE (with A adjoined) until bit 7 EXX of D' is set. A holds zero after JR NZ,1859,NORML-NOW addition, so no precision is RLCA gained or lost; A holds the fifth RL E byte from B' after multiplication RL D or division; but as only about 32 EXX bits can be correct, no precision RL E is lost. Note that A is rotated RL D circularly, with branch at carry... EXX ..eventually a random process. DEC (HL) The exponent is decremented on each shift. JR Z,182C,NEAR-ZERO If the exponent becomes zero, then numbers from 2** -129 are rounded up to 2"" -128. DJNZ 1841,SHIFT-ONE Loop back, up to 32 times. JR 1830,ZERO-RSLT If bit 7 never became 1 then the whole result is to be zero.
Finish the normalization by considering the 'carry'.
1859 NORML-NOW RLA After normalization add back any JR NC,1868,OFLOW-CLR final carry that went into A. CALL 1741,ADD-BACK Jump forward if the carry does not JR NZ,1868,OFL0W-CLR ripple right back. EXX If it should ripple right back then LD D,-1-80 set mantissa to 0.5 and increment EXX the exponent. INC (HL) This action may lead to arithmetic JR Z,1880,REPORT-6 overflow (final case).
The final part of the subroutine involves passing the result to the bytes reserved for it on the calculator stack and resetting the pointers.
1868 OFLOW-CLR PUSH HL Save the result pointer. INC HL Point to the sign byte in the result. EXX The result is moved from its present PUSH DE registers, D'E' DE, to BCDE; and EXX then to ACDE. POP BC LD A,B The sign bit is retrieved from its RLA temporary store and transferred to RL (HL) its correct position of bit 7 of the RRA first byte of the mantissa. LD (HL),A The first byte is stored. INC HL Next. LD (HL),C The second byte is stored. INC HL Next. LD (HL),D The third byte is stored. INC HL Next. LD (HL),E The fourth byte is stored. POP HL Restore the pointer to the result. POP DE Restore the pointer to second number. EXX Exchange the registers. POP HL Restore the next literal address. EXX Exchange the registers. RET Finished.
REPORT-6 Arithmetic overflow
1880 REPORT-6 RST 0008,ERROR-1 DEFB +05
This subroutine first prepares the divisor by calling PREP-M/D, reporting arithmetic overflow if it is zero; then it prepares the dividend by again calling PREP-M/D, retuming if it is zero. Next it fetches the two numbers from the calculator stack and divides their mantissas by means of the usual restoring division, trial subtracting the divisor from the dividend and restoring if there is carry, otherwise adding 1 to the quotient. The maximum precision is obtained for a 4-byte division, and after subtracting the exponents the subroutine exits by joining the later part of MULTIPLICATION.
1882 division EX DE,HL Exchange the pointers. XOR A A is set to Hex.00, so that the sign of the first number will go into A. CALL 17BC,PREP-M/D Prepare the divisor and give the JR C,1880,REPORT-6 report for arithmetic overflow if it is zero. EX DE,HL Exchange the pointers. CALL 17BC,PREP-M/D Prepare the dividend and return if RET C it is zero (result already zerol. EXX Exchange the registers. PUSH HL Save the next literal address. EXX Exchange the registers. PUSH DE Save pointer to divisor. PUSH HL Save pointer to dividend. CALL 16F7,FETCH-TWO Get the two numbers from the stack. EXX Exchange the registers. PUSH HL Save M7 & N1 on the machine stack. LD H,B Copy the four bytes of the dividend LD L,C from registers B'C'CB (i.e. M2, M3, EXX M4 & M5; see FETCH-TWO) to the LD H,C registers H'L' HL. LD L,B XOR A Clear A and reset the carry flag. LD B,+DF B will count upwards from -33 to -1, 2's complement, Hex.DF to FF, looping on minus and will jump again on zero for extra precision. JR 18132,DIV-START Jump forward into the division loop for the first trial subtraction.
Now enter the division loop.
18A2 DIV-LOOP RLA Shift the rh`sult left into B'C'CA, RLC shifting out the bits already there, EXX picking up 1 from the carry RLC whenever it Is set, and rotating RLB left each byte with carry to EXX achieve the 32 bit shift. ADD HL,HL Move what remains of the dividend EXX left in H'L' HL before the next ADC HL,HL trial subtraction; if a bit drops into the EXX carry, force no restore and a bit for the quotient, thus retrieving the lost bit and JR C,18C2,SUBN-ONLY allowing a full 32-bit divisor. 1882 DIV-START SBC HL,DE Trial subtract divisor in D'E'DE EXX from rest of dividend in H' L'HL; SBC HL,DE there is no initial carry (see EXX previous step). JR NC,18C9,NO-RSTORE Jump forward if there is no carry. ADD HL,DE Otherwise restore, i.e. add back the EXX divisor. Then clear the carry so that ADC HL,DE there will be no bit for the EXX quotient the divisor 'did not go'). AND A JR 18CA,000NT-ONE Jump forward to the counter. 18C2 SUBN-ONLY AND A Just subtract with no restore and SBC HL,DE go on to set the carry flag because EXX the lost bit of the dividend is to SBC HL,DE be retrieved and used for the EXX quotient. 1809 NO-RSTORE SCF One for the quotient in B'C'CA. 18CA COUNT-ONE INC B Step the loop count up by one. JP M,18A2,DIV-LOOP Loop 32 times for all bits. PUSH AF Save any 33rd bit for extra precision the present carry). JR Z,18B2,DIV-START Trial subtract yet again for any 34th bit; the PUSH AF above saves this bit too. LD E,A Now move the four bytes that form LD D,C the mantissa bytes of the result EXX from B'C'CA to D'E'DE. LD E,C LD D,B POP AF Then put any 34th and 33rd bits into RR B B' to be picked upon normalization. POP AF RR B EXX POP BC Restore the exponent bytes, M1 & N1. POP HL Restore the pointer to the result. LD A,B Get the difference between the two SUB C exponent bytes into A and set the carry flag if required. JP 1810,DIVN-EXPT Exit via DIVN-EXPT.
This subroutine (say I IX) returns the result of integer truncation of X, the 'last value', towards zero. Thus, I (2.4) is 2 and I (-2.4) is -2. The subroutine returns zero if the exponent byte of X is less than Hex.81 (mod X less than 1). It returns X if the exponent byte is Hex.AO or greater (X has no significant non-integral part). Otherwise the correct number of bytes of X are set to zero and, if needed, one more byte is split with a mask.
18E4 truncate LD A,(HL) Get the exponent byte of X into A. CP +81 Compare e, the exponent, to Hex.81. JR NC,1BEF,X-LARGE Jump if e is greater than Hex.80. LD (HL),+00 Else, set the exponent to zero; LD A,+20 enter 32 decimal, Hex .20, into A JR 18F4,NIL-BYTES and jump forward to NIL-BYTES to make all the bits of X be zero. 18EF X-LARGE SUB +A0 Subtract 160 decimal, Hex.A0, from e. RET P Return on plus X has no significant non-integral part. (If the true exponent were reduced to zero, the 'binary point' would come at or after the end of the four bytes of the mantissa.) NEG Else, negate the remainder; this gives the number of bits to become zero (the number of bits after the 'binary point').
Now the bits of the mantissa can be cleared.
18F4 NIL-BYTES PUSH DE Save the current value of DE (STKEND). EX DE,HL Make HL point one-past the 5th byte. DEC HL HL now points to the 5th byte of X. LD B,A Get the number of bits to be set to SRL B zero into B and divide it by 8 to give SRL B the number of whole bytes SRL B implied. JR Z,1905,BITS-ZERO Jump forward if the result is zero. 1900 BYTE-ZERO LD (HL),+O0 Else, set the bytes to zero; B DEC HL counts them. DJNZ 1900,BYTE-ZERO 1905 BITS-ZERO AND +07 Get A (mod 8): this is the number of bits still to be set to zero. JR Z,1912,IX-END Jump to the end if nothing more to do. LD B,A B will count the bits now. LD A,+FF Prepare the mask. 190C LESS-MASK SLA A With each loop a zero enters the DJNZ 190C,LESS-MASK mask from the right and thereby a mask of the correct 'length' is produced. AND (HL) The unwanted bits of (HL) are lost LD (HL),A as the masking if performed. 1912 IX-END EX DE,HL Return the pointer to HL. POP DE Return the pointer to DE, (STKEND). RET Finished.
The table of constants:
This first table holds the five useful and frequently needed numbers zero, one, a half, a half of pi and ten. The numbers are held in a condensed form which is expanded by the STACK LITERALS subroutine, see below, to give the required floating-point form.
data: constant: when expanded gives: exp. mantissa: (Hex.)
1915 stk-zero DEFB +00 zero 00 00 00 00 00 DEFB +B0 DEFB +00
1918 stk-one DEFB +31 one 81 00 00 00 00 DEFB +00
191A stk-half DEFB +30 a half 80 00 00 00 C0 DEFB +00
191C stk-pi/2 DEFB +F1 a half of pi 81 49 0F DA A2 DEFB +49 DEFB +0F DEFB +DA DEFB +A2
1921 stk-ten DEFB +34 ten 84 20 00 00 00 DEFB +20
The table of addresses:
This second table is a look-up table of the addresses of the 61 decimal, operational subroutines of the calculator. The offsets used to index into the table are derived either from the operation codes used in SCANNING, see 10BC etc., or from the literals that follow an RST 0028 instruction.
offset label address offset label address offset label address
1923 00 jump-true 2F 194D 15 str-less 03 1977 2A strs D5 1 1C 1B 1B 1925 01 exchange 72 194F 16 strs-eql 03 1979 2B chrs 8F 1A 1B 1B 1927 02 delete E3 1951 17 sirs-add 62 1978 2C not D5 19 1B 1A 1929 03 subtract 4C 1953 18 negate A0 197D 2D duplicate F6 17 1A 19 1928 04 multiply C6 1955 19 code 06 197F 2E n-mod-rn 37 17 1C 1C 192D 05 division 82 1957 1A val A4 1981 2F jump 23 18 1B 1C 192F 06 to-power E2 1959 1B len 11 1983 30 stk-data FC 1D 1C 19 1931 07 or ED 1958 1C sin 49 1985 31 dec-jr-nz 17 1A 1D 1C 1933 08 no. -&-no. F3 195D 1D cos 3E 1987 32 less-0 DB 1A 1D 1A 1935 09 no:l -eql 03 195F 1E tan 6E 1989 33 greater-0 CE 18 1D 1A 1937 OA no.gr-eq 03 1961 1F asn C4 1988 34 end-talc. 2B 1B 1D 00 1939 08 nos:negl 03 1963 20 acs D4 198D 35 getargt. 18 1B ID 1D 193B OC no:grtr 03 1965 21 atn 76 198F 36 truncate E4 1B 1D 18 193D 0D no.less 03 1967 22 In AS 1991 37 fp-calc-2 E4 1B IC 19 193F 0E nos.-eql 03 1969 23 exp 5B 1993 38 a-to-fp 5A 1B 1C 15 1941 0F addition 55 196B 24 int 46 1995 39 series-06 7F 17 1C etc. 1A 1943 10 str-&-no- F8 196D 25 sqr DB 1997 3A stk-zero 51 1A 1D etc. 1A 1945 11 str-l-eql 03 196F 26 sgn AF 1999 3B sc-mm-0 63 1B 1A etc. 1A 1947 12 str-gr-eq 03 1971 27 abs AA 199B 3C get-mem-0 45 1B 1A etc. 1A 1949 13 strs-negl 03 1973 28 peek BE 18 1A 194B 14 str-grtr 03 1975 29 usr C5 1B 1A
Note: The last four subroutines are multi-purpose subroutines and are entered with a parameter that is a copy of the righthand five bits of the original literal. The full set follows:
Offset 39 series-06, series-08 & series-0C. Offset 3A stk-zero, stk-one, stk-half, stk-pi/2 & stk-ten. Offset 38 st-mem-0, st-mem-1, st-mem-2, st-mem-3, st-mem-4 & st-mem-5. Offset 3C get-mem0, get-mem-1, get-mem-2, get-mem-3, get-mem-4 & get-mem-5.
Note: TABLE-CON EQU 1915 TABLE-ADD EQU 1923
This subroutine is used to perform floating-point calculations. These can be considered to be of three types:
The operations to be performed are specified as a series of data-bytes, the literals, that follow an RST 0028 instruction that calls this subroutine. The last literal in the list is always '34' which leads to an end to the whole operation.
In the case of a single operation needing to be performed, the operation offset can be passed to the CALCULATOR in the B register, and operation '37', the SINGLE CALCULATION operation, performed.
It is also possible to call this subroutine recursively, i.e. from within itself, and in such a case it is possible to use the system variable BERG as a counter that controls how many operations are performed before returning.
The first part of this subroutine is complicated but essentially it performs the two tasks of setting the registers to hold their required values, and to produce an offset, and possibly a parameter, from the literal that is currently being considered.
The offset is used to index into the calculator's table of addresses, see above, to find the required subroutine address.
The parameter is used when the multi-purpose subroutines are called.
Note: A floating-point number may in reality be a set of string parameters.
199D CALCULATE CALL 1B85,STK-PNTRS Presume a unary operation and therefore set HL to point to the start of the 'last value' on the calculator stack and DE one-past this floating-point number (STKEND). 19A0 GEN-ENT-1 LD A,B Either, transfer a single operation LD (BERG),A offset to BERG temporarily, or, when using the subroutine recursively pass the parameter to BERG to be used as a counter. 19A4 GEN-ENT-2 EXX The return address of the subroutine is EX (SP),HL stored in H' L'. This saves the pointer EXX to the first literal. Entering the CALCULATOR at GEN-ENT-2 is used whenever BERG is in use as a counter and is not to be disturbed. 19A7 RE-ENTRY LD (STKEND),DE A loop is now entered to handle each literal in the list that follows the calling instruction; so first, always set STKEND. EXX Go to the alternate register net, and LD A,(HL) fetch the literal for this loop. INC HL Make H' L' point to the next literal. 19AE SCAN-ENT. PUSH HL This pointer is saved briefly on the machine stack. SCAN-ENT. is used by the SINGLE CALCULATION subroutine to find the subroutine that is required. AND A Test the A register. JP P,19C2,FIRST-38 Separate the simple literals from the multi-purpose literals. Jump with literals 00 38. LD D,A Save the literal in D. AND +60 Continue only with bits 5 & 6. RRCA Four right shifts make them RRCA now bits 1 & 2. RRCA RRCA ADO A,+72 The offsets required are 39 3C, LD LA and L will now hold double the required offset. LD A,D Now produce the parameter by AND +1F taking bits 0,1,2,3 & 4 of the literal; keep the parameter in A. JR 19D0,ENT-TABLE Jump forward to find the address of the required subroutine. 19C2 FIRST-38 CP +18 Jump forward if performing a JR NC,19CE,DOUBLE-A unary operation. EXX All of the subroutines that perform LD BC,+FFFB binary operations require that LD D,H HL points to the first operand and LD E,L DE points to the second operand the ADD HL,BC 'last value') as they appear on EXX the calculator stack. 19CE DOUBLE-A RLCA As each entry in the table of addresses LD LA takes up two bytes the offset produced is doubled. 1900 ENT-TABLE LD DE +TABLE-ADD The base address of the table. LD H,+00 The address of the required table ADD HL,DE entry is formed in HL; and the LD E,(HL) required subroutine address is INC HL loaded into the DE register pair. LD D,(HL) LD HL,+RE-ENTRY The RE-ENTRY address of 19A7 is EX (SP),HL put on the machine stack underneath PUSH DE the subroutine address. EXX Return to the main set of registers. LD BC,(STKEND-hi.) The current value of BERG is transferred to the B register thereby returning the single operation offset. (See COMPARISON at 1803( 19E3 delete RET An indirect jump to the required subroutine.
This subroutine contains only the single RET instruction at 19E3, above. The literal '02' results in this subroutine being considered as a binary operation that is to be entered with a first number addressed by the HL register pair and a second number addressed by the DE register pair, and the result produced again addressed by the HL register pair. The single RET instruction thereby leads to the first number being considered as the resulting 'last value' and the second number considered as being deleted. Of course the number has not been deleted from the memory but remains inactive and will probably soon be overwritten.
This subroutine is only called from SCANNING, see page 2, and is used to perform a single arithmetic operation. The offset that specifies which operation is to be performed is supplied to the calculator in the B register and subsequently transferred to the system variable BERG.
The effect of calling this subroutine is essentially to make a jump to the appropriate subroutine for the single operation.
19E4 fp-talc-2 POP AF Discard the RE-ENTRY address. LD A,(BERG) Transfer the offset to A. EXX Enter the alternate register set. JR 19AE,SCAN-ENT. Jump back to find the required address; stack the RE-ENTRY address and jump to the subroutine for the operation.
This subroutine tests whether there is sufficient room in memory for another 5-byte floating-point number to be added to the calculator stack.
19EB TEST-5-SP PUSH DE Save-DE briefly. PUSH HL Save HL briefly. LD BC,+0005 Specify the test is for 5 bytes. CALL 0EC5,TEST-ROOM Make the test. POP HL Restore HL. POP DE Restore DE. RET Finished.
This subroutine moves a floating-point number to the top of the calculator stack (3 cases) or from the top of the stack to the calculator's memory area 11 case). It is also called through the calculator when it simply duplicates the number at the top of the calculator stack, the 'last value', thereby extending the stack by five bytes.
19F6 MOVE-FP CALL 19EB,TEST-5-SP A test is made for room. LDIR Move the five bytes involved. RET Finished.
This subroutine places on the calculator stack, as a 'last value', the floating-point number supplied to it as 2, 3, 4 or 5 literals.
When called by using offset '30' the literals follow the '30' in the list of literals; when called by the SERIES GENERATOR , see below, the literals are supplied by the subroutine that called fora series to be generated; and when called by SKIP CONSTANTS & STACK A CONSTANT the literals are obtained from the calculator's table of constants (1915-1922).
In each case, the first literal supplied is divided by Hex.40, and the integer quotient plus 1 determines whether 1, 2, 3 or 4 further literals will be taken from the source to form the mantissa of the number. Any unfilled bytes of the five bytes that go to form a 5-byte floating-point number are set to zero. The first literal is also used to determine the exponent, after reducing mod Hex.40, unless the remainder is zero, in which case the second literal is used, as it stands, without reducing mod Hex.40. I n either case, Hex.50 is added to the literal, giving the augmented exponent byte, a (the true exponent e' plus Hex.80). The rest of the 5 bytes are stacked, including any zeros needed, and the subroutine returns.
19FC STK-DATA LD H,D This subroutine performs the LD L,E manipulatory operation of adding a 'last value' to the calculator stack; hence.HL is set to point one-past the present 'last value' and hence point to the result.
19FE STK-CONST CALL 19EB,TEST-5-SP Now test that there is indeed room. EXX Go to the alternate register set and PUSH HL stack the pointer to the next EXX literal. EX ISP),HL Switch over the result pointer and the next literal pointer. PUSH BC Save BC briefly. LD A,(HL) The first literal is put into A AND +C0 and divided by Hex.40 to give the RLCA integer values 0, 1, 2 or 3. RLCA LD C,A The integer value is transferred to INC C C and incremented, thereby giving the range 1, 2, 3 or 4 for the number of literals that will be needed. LD A,(HL) The literal is fetched anew, reduced AND +3F mod Hex.40 and discarded as JR NZ,1A14,FORM-EXP inappropriate if the remainder is INC HL zero; in which case the next literal LD A,(HL) is fetched and used unreduced. 1A14 FOR M-EXP ADD A,+50 The exponent, e, is formed by the LD (DE),A addition of Hex.50 and passed to the calculator stack as the first of the five bytes of the result. LD A,+05 The number of literals specified SUB C in C are taken from the source INC HL and entered into the bytes of the INC DE result. LD B,+00 LDIR POP BC Restore BC. EX (SP),HL Return the result pointer to HL EXX and the next literal pointer to POP HL its usual position in H' & L'. EXX LD B,A The number of zero bytes required XOR A at this stage is given by 5-C-1; 1A27 STK-ZEROS DEC B and this number of zeros is added RET Z to the result to make up the LD (DE),A required five bytes. INC DE JR 1A27,STK-ZEROS
This subroutine is entered with the HL register pair holding the base address of the calculator's table of constants and the A register holding a parameter that shows which of the five constants is being requested.
The subroutine performs the null operations of loading the five bytes of each unwanted constant into the locations 0000, 0001, 0002, 0003 and 0004 at the beginning of the ROM until the requested constant is reached.
The subroutine returns with the HL register pair holding the base address of the requested constant within the table of constants.
1A2D SKIP-CONS AND A The subroutine returns if the 1A2E SKIP-NEXT RET Z parameter is zero, or when the requested constant has been reached. PUSH AF Save the parameter. PUSH DE Save the result pointer. LD DE,+0000 The dummy address. CALL 19FE,STK-CONST Perform imaginary stacking of an expanded constant. POP DE Restore the result pointer. POP AF Restore the parameter. DEC A Count the loops. JR 1A2E,SKIP-NEXT Jump back to consider the value of the counter.
This subroutine finds the base address for each five byte portion of the calculator's memory area to orfrom which a floating-point number is to be moved from or to the calculator stack. It does this operation by adding five times the parameter supplied to the bare address for the area which is held in the HL register pair.
Note that when a FOR-NEXT variable is being handled then the pointers are changed so that the variable is treated as if it were the calculator's memory area (part A, pp.23-25).
1A3C LOC-MEM LD C,A Copy the parameter to C. RLCA Double the parameter. RLCA Double that result. ADD A,C Add the value of the parameter to give five times the original value. LD C,A This result is wanted in the LD B,+00 BC register pair. ADD HL,BC Produce the new base address. RET Finished.
This subroutine is called using the literals E0 to E5 and the parameter derived from these literals is held in the A register. The subroutine calls MEMOR Y LOCATION to put the required source address into the HL register pair and MOVE A FLOATING-POINT NUMBER to copy the five bytes involved from the calculator's memory area to the top of the calculator stack to form a new 'last value'.
1A45 get-mem-0 PUSH DE Save the result pointer. etc. LD HL,(MEM) Fetch the pointer to the current memory area (see above). CALL 1A3C,LOC-MEM The hare address is found. CALL 19F6,MOVE-FP The five bytes are moved. POP HL Set the result pointer. RET Finished.
This subroutine uses SKIP CONSTANTS to find the base address of the requested constant from the calculator's table of constants and then calls STACK LITERALS, entering at STK-CONST, to make the expanded form of the constant the 'last value' on the calculator stack.
1A51 stk-zero LD H,D Set HL to hold the result pointer. etc. LD L,E EXX Go to the alternate register set and PUSH HL save the next literal pointer. LD HL,+TABLE-CON The base address of the calculator's table of constants.
EXX Back to the main set of registers. CALL 1A2D,SKIP-CONS Find the requested base address. CALL 19FE,STK-CONST Expand the constant. EXX POP HL Restore the next literal pointer. EXX RET Finished.
This subroutine is called using the literals C0 to C5 and the parameter derived from these literals is held in the A register. This subroutine is very similar to the GET FROM MEMORY subroutine but the source and destination pointers are exchanged.
1A63 st-mem-0 PUSH HL Save the result pointer. etc. EX DE,HL Source to DE briefly. LD HL,(MEM) Fetch the pointer to the current memory area. CALL 1A3C,LOC-MEM The base address is found. EX DE,HL Exchange source and destination pointers. CALL 19F6,MOV-FP The five bytes are moved. EX DE,HL 'Last value' +5, i.e. STKEND to DE. POP HL Result pointer to HL. RET Finished.
Note that the pointers HL and DE remain as they were, pointing to STKEND-5 and STKEND respectively, so that the 'last value' remains on the calculator stack. If required it can be removed by using 'delete'.
This binary operation 'exchanges' the first number with the second number, i.e. the topmost two numbers on the calculator stack are exchanged.
1A72 EXCHANGE LD B,+05 There are five bytes involved. 1A74 SWAP-BYTE LD A,(DE) Each byte of the second number. LD C,(HL) Each byte of the first number. EX DE,HL Switch source and destination. LD (DE),A Now to the first number. LD (HL),C Now to the second number. INC HL Move to consider the next pair INC DE of bytes. DJNZ 1A74,SWAP-BYTE Exchange the five bytes. EX DE,HL Get the pointers correct as the number 5 is an odd number. RET Finished.
This important subroutine generates the series of Chebyshev polynomials which are used to approximate to SIN, ATN, LN and EXP and hence to derive the other arithmetic functions which depend on these (COS, TAN, ASN, ACS, and SOR E
The polynomials are generated, for n=1, 2,..., by the recurrence relation:
T,,, (z) = 2zTn (z) - T._t (z), where Tn (z) is the nth Chebyshev polynomial in z.
The series in fact generates:
To, 2T,, 2'I, 2T,, t, where n is 6 for SIN, B for EXP and 12 decimal, for LN and ATN.
The coefficients of the powers of z in these polynomials may be found in the Handbook of Mathematical Functions by M. Abramowitz and I.A. Stegun (Dover 1965), page 795.
BASIC programs showing the generation of each of the four functions are given here in the Appendix.
In simple terms this subroutine is called with the 'last value' on the calculator stack, say Z, being a number that bears a simple relationship to the argument, say X, when the task is to evaluate, for instance, SIN X. The calling subroutine also supplies the list of constants that are to be required (six constants for SIN). The SERIES GENERATOR then manipulates its data and returns to the calling routine a 'last value' that bears a simple relationship to the requested function, for instance, SIN X.
This subroutine can be considered to have four major parts:
The setting of the loop counter: The calling subroutine passes its parameter in the A register for use as a counter. The calculator is entered at GEN-ENT-1 so that the counter can be set.
1A7F series-06 LD B,A Move the parameter to B. etc. CALL 19A0,GEN-ENT-1 In effect an RST 0028 instruction but sets the counter.
The handling of the 'last value', Z: The loop of the generator requires 2*Z to be placed in mem-0, zero to be placed in mem-2 and the 'last value' to be zero.
calculator stack DEFB +2D,duplicate,19F6 Z, Z DEFB +0F,addition,1755 2*Z DEFB +C0,st-mem-0,1A63 2*Z mem-0 holds 2*Z DEFB +02,delete,19E3 DEFB +A0,stk-zero,1A51 0 DEFB +C2,st-mem-2,1A63 0 mem-2 holds 0
The main loop: The series is generated by looping, using BERG as a counter; the constants in the calling subroutine are stacked in turn by calling STK-DATA; the calculator is re-entered at GEN-ENT-2 so as not to disturb the value of BERG; and the series is built up in the form:
BIR) =2Z*6(R-1)-B(R-2) + AIR(, for R= 1, 2,...,N, where A(1), A(2) AIN) are the constants supplied by the calling subroutine (SIN, ATN, LN and EXP) and B(S) = 0 = B(-1).
The (R+1)th loop starts with B(R) on the stack and with 2*Z, BIR-2) and B(R-1) in mem-0, mem-1 and mem-2 respectively.
1A89 G-LOOP DEFB +20,duplicate,19F6 B(R),B(R) DEFB +E0,get-mem-0,1A45 B(R), B(R), 2*Z DEFB +04,multiply,17C6 B(R), 2*B(R)*Z DEFB +E2,get-mem-2,1A45 B(R), 2*B(R)*Z, B(R-1) DEFB +C1,st-mem-1,1A63 mem-1 holds B(R-1) DEFB +03,subtract,174C B(R), 2*B(R)*Z-B(R-1) DEFB +34,end-calc.,0028
The next constant is placed on the calculator stack.
CALL 19FC,STK-DATA B(R), 2*B(R)*Z-B(R-1), A(R+1)
The Calculator is re-entered without disturbing BERG.
CALL 19A4,GEN-ENT-2 DEFB +0F,addition,1755 B(R), 2 B(R)'Z-B(R-1)+A(R+1) DEFB +01,exchange,1A72 2*B(R)*Z-BIR-1(+A(R+1), B(R) DEFB +C2,st-mem-2,1A63 mem-2 holds B(R) DEFB +02,delete,19E3 2*B(R)*Z-B(R-1)+A(R+1) =B(R+1) DEFB +31,dec-jr-nz,1C17 B(R+1) DEFB +6E, to 1A89,G-LOOP
iv. The subtraction of B(N-2):
The loop above leaves B(N) on the stack and the required result
is given by B(N) - B(N-2).
DEFB +E1,get-mem-1,1A45 B(N), B(N-2) DEFB +03,subtract,174C B(N)-B(N-2) DEFB +34,end-calc.,002B RET Finished
This subroutine performs its unary operation by changing the sign of the 'last value' on the calculator stack.
1AA0 negate LD A,(HL) Fetch the exponent, e. AND A Test it. RET Z Return if the 'last value' is zero. INC HL Point to the sign byte. LD A,(HL) Fetch the sign byte. XOR +80 Change the sign bit. LD (HL),A Return the sign byte. DEC HL Set the result pointer. RET Finished.
This subroutine performs its unary operation by ensuring that the sign bit of a floating-point number is reset.
1AAA abs INC HL Point to the sign bit of the 'last value'. RES 7,(HL) The bit must be reset always. DEC HL Set the result pointer. RET Finished.
This subroutine handles the function SGN X and therefore returns a 'last value' of 1 if X is positive, zero if X is zero and -1 if X is negative.
1AAF sgn INC HL Point to the sign byte of the present 'last value'. LD A,(HL) Fetch the sign byte. DEC HL Point to the exponent. DEC (HL) Test the exponent byte; the zero INC (HL) flag is set for zero. SCF Set the carry flag. CALL NZ,1AE0,FP-0/1 If the value is not zero then call FP-0/1 with carry set to give a 'last value' of 1. INC HL Point to the sign byte again. RLCA The sign bit of X is passed into the RR (HL) carry, and hence into the result. DEC HL Set the result pointer. RET Finished.
This subroutine handles the function PEEK X. The 'last value' is unstacked by calling FIND-INT. and replaced by the value of the contents of the required location.
1ABE peek CALL 0EA7,FIND-INT. Evaluate the 'last value', rounded to the nearest integer; test that it is in range and return it in BC. LD A,(BC) Fetch the required byte. JP 151 D,STACK-A Exit by jumping to STACK-A.
This subroutine handles the function USR X. The value of X is evaluated, a return address is stacked and the machine code executed from location X.
1AC5 usr CALL 0EA7,FIND-INT. Evaluate the 'last value', rounded to the nearest integer; test that it is in range and return it in BC. LD HL,+STACK-BC Make the return address be that of the PUSH HL subroutine STACK-BC. PUSH BC Make an indirect jump to the RET required location.
Nate: It is interesting that the IY register pair is re-initialised when the return to STACK-BC has been made, but the important H' L' that holds the next literal pointer is not restored should it have been disturbed.
This subroutine returns a 'last value' of 1 if the present 'last value' is greater than zero and zero otherwise. It is also used by other subroutines to jump on plus'.
1ACE GREATER-0 LD A,(HL) Fetch the exponent byte. AND A Test it. RET Z Return if the 'last value' is zero. LD A,+FF Jump forward to LESS THAN ZERO JR 1ADC,SIGN-TO-C but signal the opposite action is needed.
This subroutine returns a 'last value' of 1 if the present 'last value' is zero and zero otherwise. It is also used by other subroutines to 'jump on zero'.
1AD5 NOT LD A,(HL) Fetch the exponent byte. NEG Negating and complementing ensure CCF that the carry is set only if the 'last value' is zero; this gives the correct return. JR 1AE0,FP-0/1 Jump'forward.
This subroutine returns a 'last value' of 1 if the present 'last value' is less than zero and zero otherwise. It is also used by other subroutines to 'jump on minus'.
1ADB less-0 XOR A Clear the A register. 1ADC SIGN-TO-C INC HL Point to the sign byte. XOR (HL) The sign bit is collected and stored DEC HL in the carry; when entered from RLCA GREATER-0 the opposite sign goes to the carry.
This subroutine gives the 'last value' as zero if the carry flag is reset and the value 1 if it is set.
1AE0 FP-0/1 PUSH HL Save the result pointer. LD B,+05 There are five bytes. 1AE3 FP-ZERO LD (HL),+00 Enter zero on each loop. INC HL Move to next byte. DJNZ 1AE3,FP-ZERO Until the five bytes are done. POP HL Restore the result pointer. RET NC Return the zero if carry reset. LD (HL),+81 Return 1 if the carry flag is RET set.
This subroutine performs the binary operation 'X OR Y' and returns X if Y is zero and the value 1 otherwise.
1AED or LD A,(DE) Fetch the exponent of the second AND A number; test it and return with the RET Z first number as the 'last value' if it is zero. SCF Set the carry flag and jump back to JR 1AE0,FP-0/1 give the 'last value' as 1.
This subroutine performs the binary operation 'X AND Y' and returns X if Y is non-zero and the value zero otherwise.
1AF3 no.-&-no. LD A,(DE) Fetch the exponent of the second AND A number; test it and return with the RET NZ first number as the 'last value' if it is not zero. JR 1AE0,FP-0/1 With the carry flag reset, jump back to give the 'last value' as zero.
This subroutine performs the binary operation 'A$ AND Y' and returns A$ if V is non-zero and a null string otherwise.
1AF8 str-&-no. LD A,(DE) Fetch the exponent of the number; AND A test it and return with the string as the RET NZ 'last value' if it is not zero. PUSH DE Save the pointer to the number. DEC DE Point to the 5th byte of the string parameters i.e. length-high. XOR A Clear the A register. LD (DE),A Length-high is now set to zero. DEC DE Point to length-low. LD (DE),A Length-low is now set to zero. POP DE Restore the pointer. RET Return with the string parameters being the 'last value'.
This subroutine is used to perform the twelve possible comparison operations. The single operation offset is present in the B register at the start of the subroutine.
1B03 no:I-eql LD A,B The single operation offset goes to etc. the A register. SUB +08 The range is now 01-06 & 09-0E. BIT 2,A This range is changed to, JR NZ,1B0B,EX-OR-NOT 00-02, 04-06, 08-0A & DEC A OC-0E. 1B0B EX-OR-NOT RRCA Then reduced to 00-07 with carry set for 'greater than or equal to' & 'less than'; the operations with JR NC,1816,NU-OR-STR carry set are then treated as their PUSH AF complementary operations once the PUSH HL values have been exchanged. CALL 1A72,EXCHANGE POP DE EX DE,HL POP AF 1816 NU-OR-STR BIT 2,A The numerical comparisons are now JR NZ,1 B21,STRINGS separated from the string comparisons by testing bit 2. RRCA The numerical operations now have the range 00-01 with carry set for 'equal' and 'not equal'. PUSH AF Save the offset. CALL 174C,SUBTRACT The numbers are subtracted for the JR 1B54,END-TESTS final tests. 1B21 STRINGS RRCA The string comparisons now have the range 02-03 with carry set for 'equal' and 'not equal'. PUSH AF Save the offset. CALL 13F8,STK-FETCH The lengths and starting addresses PUSH DE of the strings are fetched from the PUSH BC calculator stack. CALL 13F8,STK-FETCH POP HL The length of the second string. 1B2C BYTE-COMP LD A,H OR L EX (SP),HL LD A,B JR NZ,1 B3D,SEC-PLUS Jump unless the second string is null. OR C 1833 SECND-LOW POP BC Here the second string is either null or less than the first. JR Z,1B3A,BOTH-NULL POP AF CCF The carry is complemented to give JR 1B50STR-TEST the correct test results. 1B3A BOTH-NULL POP AF Here the carry is used as it JR 1650,STR-TEST stands. 1B3D SEC-PLUS OR C JR Z,1B4D,FRST-LESS The first string is now null, the second not. LD A,(DE) Neither string is null, so their SUB (HL) next bytes are compared. JR C,1 B4D,FRST-LESS The first byte is less. JR NZ,1B33,SECND-LOW The second byte is less. DEC BC The bytes are equal; so the lengths, INC DE are decremented and a jump is INC HL made to BYTE-COMP to compare the EX (SP),HL next bytes of the reduced strings. DEC HL JR 182C,BYTE-COMP 184D FRST-LESS POP BC POP AF AND A The carry is cleared here for the correct test results. 1650 STR-TEST PUSH AF For the string tests, a zero is RST 0028,FP-CALC. put on to the calculator stack. DEFB +A0,stk-zero,1A51 DEFB +34,end-calc.,0028 1854 END-TESTS POP AF These three tests, called as needed, PUSH AF give the correct results for all CALL C,1AD5,NOT twelve comparisons. The initial CALL 1ACE,GREATER-0 carry is set for 'not equal' and POP AF 'equal', and the final carry is set RRCA for 'greater than', 'less than' and CALL NC,1AD5,NOT 'equal'. RET Finished.
This subroutine performs the binary operation 'A$+B$'.The parameters for these strings are fetched and the total length found. Sufficient room to hold both the strings is made available in the work space and the strings are copied over. The result of this subroutine is therefore to produce a temporary variable A$+B$ that resides in the work space.
1662 strs-add CALL 13F8STK-FETCH The parameters of the second string PUSH DE are fetched and saved. PUSH BC CALL 13FB,STK-FETCH The parameters of the first string are fetched. POP HL PUSH HL The lengths are now in HL and BC. PUSH DE The parameters of the first string PUSH BC are saved. ADD HL,BC The total length of the two strings is LD B,H calculated and passed to BC. LD C,L RST 0030,BC-SPACES Sufficient room is made available. CALL 12C3,STK-STORE The parameters of the new string are passed to the calculator stack. POP BC The parameters of the first string are POP HL retrieved and the string copied to LD A,B the work space as long as it is not OR C a null string. JR Z,1B7D,OTHER-STR LDIR 1B7D OTHER-STR POP BC Exactly the same procedure is followed POP HL for the second string thereby LD A,B giving 'A$+B$'. OR C JR Z,1B85,STK-PNTRS LDIR
This subroutine resets the HL register pair to point to the first byte of the 'last value', i.e. STKEND-5, and the DE register pair to point one-past the 'last value', i.e. STKEND.
1885 STK-PNTRS LD HL,(STKEND) Fetch the current value of STKEND. LD DE +FFFB Set DE to -5, 2's complement. PUSH HL Stack the value for STKEND. ADD HL,DE Calculate STKEND-5. POP DE DE now holds STKEND and HL holds RET STKEND-5.
This subroutine handles the function CHR$ X and creates a single character string in the work space.
1B8F chrs CALL 15CD,FP-TO-A The 'last value' is compressed into the A register. JR C,1BA2,REPORT-B2 Give the error report if X was greater than than 255 decimal, or JR NZ,1 BA2,REPORT-B2 X was a negative number. PUSH AF Save the compressed value of X. LD BC,+0001 Make one space available in the RST 0030,8CSPACES work space. POP AF Fetch the value. LD (DE),A Copy the value to the work space. CALL 12C3,STK-STORE Pass the parameters of the new string to the calculator stack. EX DE,HL Reset the pointers. RET Finished.
REPORT-B2 integer out of range
1BA2 REPORT-B2 RST 0008,ERROR-1 DEFB +0A
This subroutine handles the function VAL A$ and returns a 'last value' that is the result of evaluating the string as an arithmetical expression.
1BA4 val LD HL,(CH-ADD) The current value of CH-ADD is PUSH HL preserved on the machine stack. CALL 13F8,STK-FETCH The parameters of the string are fetched; PUSH DE the starting address is saved; one byte INC BC is added to the length and room made RST 0030,BC-SPACES available for the string (+1) in the work space. POP HL The starting address of the string goes to HL as a source address. LD (CH-ADD),DE The pointer to the 2nd new space goes PUSH DE to CH-ADD and the machine stack. LDIR The string is copied to the work space, together with an extra byte. EX DE,HL Switch the pointers. DEC HL The extra byte is replaced by a LD (HL),+76 NEWLINE character. RES 7,(FLAGS) The syntax flag is reset and the string CALL 0D82,CLASS-6 scanned for correct syntax. CALL 0D22,CHECK-2 A check is made that the end of a line has been reached. POP HL The starting address of the string is LD (CH-ADD),HL fetched and copied to CH-ADD. SET 7,(FLAGS) The flag is set for line execution. CALL 0F55,SCANNING The string is treated as a'next expression' and a 'last value' produced. POP HL The original value of CH-ADD is LD (CH-ADD),HL restored. JR 1B85,STK-PNTRS The subroutine exits via STK-PNTRS which resets the pointers.
This subroutine handles the function ST R$ X and returns a 'last value' which is a set of parameters that defines a string containing what would appear on the screen if X were displayed by a PRINT command.
1BD5 strs LD BC,+0007 One space is made in the work RST 0030,BCSPACES space and a NEWLINE character put LD (HL),+76 into the location after it. LD HL,(S-POSN) The current value of S-POSN is PUSH HL preserved on the machine stack. LD L,+FF The column number of the PRINT LD (S-POSN),HL position is set to a high value. LD HL,(DF-CC) The current value of DF-CC is PUSH HL preserved on the machine stack. LD (DF-CC),DE The pointer to the NEWLINE PUSH DE character becomes the destination painter of the PRINT operation. A copy is saved on the machine stack. CALL 15DB,PRINT-FP The 'last value', X, is now printed out in the work space and the work space is expanded with each character as DF-CC points to a NEWLINE character. POP DE In effect now the start address. LD HL,(DF-CC) Now the NEWLINE character is one-past AND A the end of the string and hence the SBC HL,DE difference is the length. LD B,H Transfer the length to BC. LD C,L POP HL Restore the original value of LD (DF-CC),HL DF-CC. POP HL Restore the original value of LD (S-POSN),HL S-POSN. CALL 12C3,STK-STORE Pass the parameters of the new string to the calculator stack. EX DE,HL Reset the pointers. RET Finished.
This subroutine handles the function C0DE ASand returns the ZX81 code of the first character in AS, or zero if AS should be null.
1C06 code CALL 13FB,STK-FETCH The parameters of the string are fetched. LD A,B The length is tested and the A OR C register holding zero is carried forward JR Z,1C0E,STK-CODE if A$ is a null string. LD A,(DE) The code of the first character is put into A otherwise. 1C0E STK-CODE JP 151D,STACK-A The subroutine exits via STACK-A which gives the correct 'last value'.
This subroutine handles the function LEN AS and returns a 'last value' that is equal to the length of the string.
1C11 len CALL 13FB,STK-FETCH The parameters of the string are fetched. JP 1520,STACK-BC The subroutine exits via STACK-BC which gives the correct 'last value'.
This subroutine is only called by the SERIES GENERATOR subroutine and in effect isa 'DJNZ' operation but the counter is the system variable, BERG, rather than the B register.
1C17 dec-jr-nz EXX Go to the alternate register set and PUSH HL save the next literal pointer on the machine stack. LD HL-BERG Make HL point to BERG. DEC (HL) Decrease BERG. POP HL Restore the next literal pointer. JR NZ,1 C24,JUMP-2 The jump is made on non-zero. INC HL The next literal is passed over. EXX Return to the main register set. RET Finished.
This subroutine executes an unconditional jump when called by the literal '2F'. It is also used by the subroutines DECREASE THE COUNTER and JUMP ON TRUE.
1C23 JUMP EXX Go to the alternate register set. 1C24 JUMP-2 LD E,(HL) The next literal (jump length) is put in the E' register. XOR A The A register is cleared. BIT 7,E If E' is negative, indicating a backwards JR Z,1C2B,NEW-ADDR. jump then Hex.FF is formed in the CPL A register instead of the Hex.00. 1C2B NEW-ADDR. LD D,A Hex.00 or Hex.FF goes to D. ADD HL,DE The registers H' & L' now hold the EXX new next literal pointer. RET Finished.
This subroutine executes a conditional jump if the 'last value' on the calculator stack, or more precisely the number addressed currently by the DE register pair, is true.
1C2F jump-true LD A,(DE) Fetch the exponent. AND A Test it. JR NZ,1C23,JUMP Make the jump on true, or more precisely, on not-false. EXX Go to the alternate register set. INC HL Pass over the jump length. EXX Back to the main set of registers. RET Finished.
This subroutine calculates N (mod M), where M is a positive integer held at the top of the calculator stack, the 'last value', and N is an integer held on the stack beneath M.
The subroutine returns the integer quotient INT (N/M) at the top of the calculator stack, the 'last value', and the remainder N-INT (N/M) in the second place on the stack.
This subroutine is called by PRINT-FP to reduce N mod 10 decimal, and during the calculation of a random number to reduce N mod 65537 decimal.
1C37 n-mod-m RST 0028,FP-CALC. N, M DEFB +C0,st-mem-0,1A63 N, M mem-0 holds M DEFB +02,delete,19E3 N DEFB +2D,duplicate,19F6 N, N DEFB +E0,get-mem-0,1 A45 N, N, M DEFB +05,division,1882 N, N/M DEFB +24,int,1C46 N, INT(N/M) DEFB +E0,get-mem-0,1A45 N, INT(N/M), M DEFB +01,exchange,1A72 N, M, INT(N/M) DEFB +C0,st-mem-0,1A63 N, M, INT(N/M) mem-0 holds INT (N/M) DEFB +04,multiply,17C6 N, M*INT(N/M) DEFB +03,subtract,174C N-M*INT(N/M) DEFB +E0,get-mem-0,1 A45 N-M*INT(N/M), INT (N/M) DEFB +34,end-calc.,002B RET Finished.
This subroutine handles the function INT X and returns a 'last value' that is the 'integer part' of the value supplied. Thus INT 2.4 gives 2 but as the subroutine always rounds the result down INT -2,4 gives -3.
The subroutine uses the INTEGER TRUNCATION TOWARDS ZERO subroutine at 18E4 to produce I (XI such that I (2.4) gives 2 and I (-2.41 gives-2. Thus, INT Xis given by I (X) for values of X that are greater than or equal to zero, and I (X)-1 for negative values of X that are not already integers, when the result is, of course, I (X).
1C46 int RST 0026,FP-CALC. X DEFB +2D,duplicate,19F6 X, X DEFB +32,less-0,1ADB X, (1/0) DEFB +00,jump-true,lC2F X DEFB +04, to 1C4E,X-NEG X
For values of X that have been shown to be greater than or equal to zero there is no jump and I (X) is readily found.
DEFB +36,truncate,18E4 I(X) DEFB +34,end-calc.,002B RET Finished. When Xis a negative integer I IX) is returned, otherwise I(X)-1 is returned. 1C4E X-NEG DEFB +2D,duplicate,19F6 X, X DEFB +36,truncate,18E4 X, I(X) DEFB +C0,st-mem-0,1A63 X, I(X) mem-O holds I (X) DEFB +03,subtract,174C X-I(X) DEFB +E0,get-mem-0,1A45 X-I(X), I(X) DEFB +01,exchange,1A72 I(X),X-I(X) DEFB +2C,not,1AD5 I(X), (1/0) DEFB +00,jump-true,1C2F I(X) DEFB +03, to 1C59,EXIT I(X)
The jump is made for values of X that are negative integers, otherwise there is no jump and I (X)-1 is calculated.
DEFB +A1,stkone,1A51 I(X), 1 DEFB +03,subtract,174C I(X)-1
In either case the subroutine finishes with;
1059 EXIT DEFB +34,end-calc.,002B I(X) or I(X)-1 RET
This subroutine handles the function EXP X and is the first of the four routines that use SERIES GENERATOR to produce Chebyshev polynomials. The approximation to EXP X is found as follows:
1C5B EXP RST 0028,FP-CA LC. X
Perform step i.
DEFB +30,stk-data,l9FC X, 1/LN 2 DEFB +F 1,exponent 81 DEFB +38,+AA,+3B,+29 DEFB +04,multiply,17C6 X/LN 2 = Y
Perform step ii.
DEFB +2D,duplicate,19F6 Y, Y DEFB +24,int,1C46 Y, INT Y = N DEFB +C3,st-mem-3,1A63 Y, N mem-3 holds N
Perform step iii.
DEFB +03,subtract,174C Y-N = W
Perform step iv.
DEFB +2D,duplicate,19F6 W, W DEFB +0F,addition,1755 2*W DEFB +A1,stk-one,1 A51 2*W, 1 DEFB +03,subtract,174C 2*W-1 = Z
Perform step v, passing to the SERIES GENERATOR the parameter '8' and the eight constants required.
DEFB +88,series-08,1A7F 1. DEFB +13,exponent 63 DEFB +36,(+00,+00,+00) 2. DEFB +58,exponent 68 DEFB +65,+66,(+00,+00) 3. DEFB +9D,exponent 6D DEFB +78,+65,+40,(+00) 4. DEFB +A2,exponent 72 DEFB +60,+32,+C9,(+00) 5. DEFB +E7,exponent 77 DEFB +21,+F7,+AF+24 6. DEFB +EB,exponent 7B DEFB +2F,+B0,+B0,+14 7. DEFB +EE exponent 7E DEFB +7E,+88,+94,+58 8. DEFB +F1,exponent 81 DEFB +3A,+7E,+F8,+CF
At the end of the last loop the 'last value' is 2"W.
Perform step vi.
DEFB +E3,get-mem-3,1A45 2'"W, N DEFB +34,end-calc.,002B CALL 15CD,FP-TO-A The absolute value of N mod 256 decimal, is put into the A register. JR NZ,CC9B,N-NEGTV Jump forward if N was negative. JR C,1C99,REPORT6-2 Error if ABS N greater than 255 dec. ADD A,(HL) Now add ABS N to the exponent. JR NC,1CA2,RESULT-OK Jump unless e greater than 255 dec. 1C99 REPORT6-2 RST 0006,ERROR-1 Otherwise report the overflow. DEFB +05 1C9B N-NEGTV JR C,1CA4,RSLT-ZERO The result is to be zero if N is less than -255 decimal. SUB (HL) Subtract ABS N from the exponent as N was negative. JR NC,CCA4,RSLT-ZERO Zero result if e less than zero. NEG Minus a is changed to e. 1CA2 RESULT-OK LD (HL),A The exponent, e, is entered. RET Finished: 'last value' is EXP X. 1CA4 RSLT-ZERO RST 0028,FP-CALC. Use the calculator to make the DEFB +02,delete,19E3 'last value' zero. DEFB +A0,stk-zero,1A51 DEFB +34,endcalc.,002B RET Finished, with EXP X = 0.
This subroutine handles the function LN X and is the second of the four routines that use SERIES GENERATOR to produce Chebyshev polynomials.
The approximation to LN X is found as follows:
i. X is tested and report A is given if X is not positive. ii. X is then split into its true exponent, e', and its mantissa X' = X/12'e'l, where X' is greater than, or equal to, 0.5 but still less than 1. iii. The required value V1 or Y2 is formed. If X' is greater than 0.8 then Y1 = e' LN 2 and if otherwise Y2 = le'-1) `LN 2. If X' is greater than 0.8 then the quantity X'-t is stacked; otherwise 2'X'-1 is stacked. iv. Now the argument Z is formed, being, if X' is greater than 0.8, Z = 2.5X'-3; otherwise Z = 5'X'-3. In each case, -1<= Z <=1, as required for the series to converge. vi. The SERIES GENERATOR is used to produce the required function. vii. Finally a simple multiplication and addition leads to LN X being returned as the 'last value'.
1CA9 In RST 0028,FP-CALC. X
Perform step i.
DEFB +2D,duplicate,19F6 X, X DEFB +33,greater-0,1ACE X, (110) DEFB +00,jump-true,1C2F X DEFB +04, to 1CB1,VALID X DEFB +34,end-calc.,002B X RST 0008,ERROR-1 DEFB +09 Give report A invalid argument.
Perform step ii.
1CB1 VALID DEFB +A0,stk-zero,1A51 X, 0 The deleted 1 is overwritten DEFB +02,delete,19E3 X with zero. DEFB +34,end-calc.,002B X LD A,(HL) The exponent, e, goes into A. LD (HL),+80 X is reduced to X'. CALL 151 D,STACK-A The stack holds: X', e. RST 0028,FP-CALC. X', e DEFB +30,stkdata,19FC X', e, 128 (decimal) DEFB +38,exponent 88 DEFB +00,(+00,+00,+00) DEFB +03,subtract,174C
Perform step iii.
DEFB +01,exchange,1 A72 e', X' DEFB +2D,duplicate, 19F6 e', X', X' DEFB +30,stk-data,19FC e', X', X',0.8 (decimal) DEFB +F0,exponent80 DEFB +4C,+CC,+CC,+CD DEFB +03,subtract,174C e', X', X'-0.8 DEFB +33,greater-0,1 ACE e', X', (1/0) DEFB +00,jump-true,1C2F e', X' DEFB +08, to 1CD2,GRE.8 e', X' DEFB +01,exchange,1 A72 DEFB +Al,stkone,1A51 DEFB +03,subtract,174C DEFB +01,exchange,1 A72 DEFB +34,end-calc.,002B INC (HL) Double X' to give 2'X'. RST 0028,FP-CALC. e'-1,2X' 1CD2 GRE.B DEFB +01,exchange, 1A72 X' large. - X' small.
DEFB +30,stk-data,19FC X', e', LN 2 2'X', e'-1, LN 2 DEFB +F0,exponent 80 DEFB +31,+72,+17,+F8 DEFB +04,multiply,17C6 (eN 2 = Y1 2'X', (e'-1)' LN 2 = Y2
Perform step iv.
DEFB +01,exchange,1A72 Y1, X' X' large. Y2, 2*X' X' small. DEFB +A2,stk-half,1A51 Y1, X', .5 (decimal) Y2, 2*X', .5 DEFB +03,subtract,174C Y1, X'-.5 Y2, 2*X'-.5 DEFB +A2,stk-half,1A51 Y1, X'-.5, .5 Y2, 2*X'-.5, .5 DEFB +03,subtract,174C Y1, X'-1 Y2, 2*X'-1
Perform step v.
DEFB +2D,duplicate,19F6 Y, X'-1, X'-1 Y2, 2*X'-1, 2*X'-1 DEFB +30,stk-data,19FC Y1, X'-1, X'-1, 2.5 (decimal) Y2, 2'X'-1, 2'X'-1, 2.5 DEFB +32,exponent 82 DEFB +20,(+00,+00,+00) DEFB +04,multiply,17C6 Y1, X'-1, 2.5'X'-2.5 Y2, 2`X'-1, 5'X'-2.5 DEFB +A2,stk-half,lA51 Y1, X'-1, 2.5'X'-2.5, .5 Y2, 2'X'-1, 5'X'-2.5, .5 DEFB +03,subtract,174C Y1, X'-1,2.5'X'-3=Z Y2, 2'X'-1, 5'X'-3 = Z
Perform step vi, passing to the SERIES GENERATOR the parameter '12' decimal, and the twelve constant required.
DEFB +8C,series-OC,1A7F Y7, X'-1, Z or Y2, 2*X'-1, Z 1. DEFB +11,exponent61 DEFB +AC,(+00,+00,+00) 2. DEFB +14,exponent64 DEFB +09,(+00,+00,+00) 3. DEFB +56,exponent66 DEFB +DA,+A5,(+00,+00) 4. DEFB +59,exponent69 DEFB +30,+C5,(+00,+00) 5. DEFB +5C,exponent 6C DEFB +90,+AA,(+00,+00) 6. DEFB +9E,exponent6E DEFB +70,+6F,+61,(+00) 7. DEFB +A1,exponent 71 DEFB +CB,+DA,+96,(+00) 8. DEFB +A4,ex ponent 74 DEFB +31,+9F,+B4,(+00) 9. DEFB +E7,exponent 77 DEFB +A0,+FE,+5C,+FC 10. DEFB +EA,exponent 7A DEFB +1B,+43,+CA,+36 11. DEFB +ED,exponent7D DEFB +A7,+9C+7E,+5E 12. DEFB +F0,exponent 80 DEFB +6E+23,+80,+93
At the end of the last loop the 'last value' is:
either LN X'/(X'-l) for the larger values of X' or LN (2*X')/(2*X'-1) for the smaller values of X'.
Perform step vii.
DEFB +04,multiply,17C6 Y1=LN (2**e'), LN X' Y2=LN (2**le'-1)1, LN 12*X') DEFB +0F,addition,1755 LN ((2**e'1*X') = LN X LN ( 2**l e'-11*2*X') = LN X DEFB +34,end-calc.,002B LN X RET Finished: 'last value' is LN X.
This subroutine transforms the argument X of SIN X or COS X into a value V.
The subroutine first finds a value Y such that:
Y = X/(2*PI I INT (X/(2*Pl ( + 0.5), where Y is greater than, or equal to, -.5 but less than +.5.
The subroutine returns with: V=4*Y if -1<=4*Y<=1 casei. or, V = 2-4*Y if 1 <4*Y<2 case ii. or, V -4*Y-2 if -2<=4 Y<-1. case iii.
In each case, -1<=V<=1 and SIN (PI*V/2) = SIN X.
1D16 get-argt. RST 0028,FP-CALC. X DEFB +30,stk-data,19FC X, 1/(2PI) DEFB +EE,exponent 7E DEFB +22,+F9,+83,+6E DEFB +04,multiply,17C8 X/(2*PI) DEFB +2D,duplicate,19F6 X/(2PI), X/(2Pll DEFB +A2,stk-half,1A51 X/(2PI), X/(2*Pl), 0.5 DEFB +0F,addition,1755 X/(2PI), X/(2*Pl)+0.5 DEFB +24,int,1C46 X/(2*PI), INT (X/(2*PI)+0.5) DEFB +03,subtract,174C X/(2*P11-INT (X/12*PN+0.5) = V
Note. Adding 0.5 and taking INT rounds the result to the nearest integer.
DEFB +2D,duplicate,19F6 Y, Y DEFB +0F,addition,1755 2*Y DEFB +2D,duplicate,19F6 2*Y 2*Y DEFB +0F,addition,1755 4*Y DEFB +2D,duplicate,19F6 4*Y, 4*Y DEFB +27,abs,1AAA 4*Y, ABS (4Y) DEFB +A1,stk-one,1A51 4V, ABS )4V), 1 DEFB +03,subtract,174C 4*Y, ABS (4 V)-1 = Z DEFB +2D,duplicate,19F6 4*V, Z, Z DEFB +33,greater-0,1ACE 4*Y, Z, (1/0) DEFB +C0,st-mem-0,1 A63 Mem-0 holds the result of the test. DEFB +00,jump-true,l C2F 4Y, Z DEFB +04, to 1 D35,ZPLUS 4Y, Z DEFB +02,delete,19E3 4Y DEFB +34,endcalc.,0028 4Y = V case i. RET Finished.
If the jump was made then continue.
1D35 ZPLUS DEFB +Al,stkone,1A51 4Y, Z, 1 DEFB +03,subtrect,174C 4Y, Z-1 DEFB +01,exchange,1A72 Z-1, 4Y DEFB +32,less-0,1ADB Z-1, (1/0) DEFB +00,jump-true,l C2F Z-1 DEFB +02, to 1D3C,YNEG Z-1 DEFB +18,negate,lAA0 1-Z 1D3C YNEG DEFB +34,end-calc.,002B 1-Z=V case ii. Z-1 = V case iii. RET Finished.
This subroutine handles the function COS X and returns a 'last value' that is an approximation to COS X.
The subroutine uses the expression:
COS X = SIN (PI W/2), where -1 <=W<=1.
In deriving W from X the subroutine uses the test result obtained in the previous subroutine and stored for this purpose in teem-0. It then jumps to the SINE subroutine, entering at C-ENT, to produce a 'last value' of COS X.
1D3E cos RST 0028,FP-CALC. X DEFB +35,get-argt.,lD18 V DEFB +27,abs,1AAA ABS V DEFB +Al,stkone,1 A51 ABS V, 1 DEFB +03,subtract,174C ABS V-1 DEFB +E0,get-mem-0,1A45 ABS V-1, (1/0) DEFB +O0,jump-true,lC2F ABS V-1 DEFB +06, to 1D4B, C-ENT ABS V-1 =IN
If the jump was not made then continue.
DEFB +18,negate,1AA0 1-ABS V DEFB +2F,jump,1C23 1-ABS V DEFB +03, to 1D4B,C-ENT 1-ABS V = W
This subroutine handles the function SIN X and is the third of the four routines that use SERIES GENERATOR Cu produce Chebyshev polynomials.
The approximation to SIN X is found as follows:
1D49 sin RST 0028,FP-CALC. X
Perform step i.
DEFB +35,get-argt.,1D18 W
Perform step ii. The subroutine from now on is common to both the SINE and COSINE functions.
1D4B C-ENT DEFB +2D,duplicate,19F6 W, W, DEFB +2D,duplicate,19F6 W, W, W DEFB +04,multiply,17C6 W,WW DEFB +2D,duplicate,19F6 W, WW, WW DEFB +0F,addition,1755 W, 2WW DEFB +A1,stk-one,1A51 W, 2WW 1 DEFB +03,subtract,174C W, 2WW-1 = Z
Perform step iii, passing to the SERIES GENERATOR the parameter '6' and the six constants required.
DEFB +86,series-06,1A7F W,Z 1. DEFB +14,exponent64 DEFB +E6,(+00,+00,+00) 2. DEFB +5C,exponent 6C DEFB +1F,+06,(+00,+00) 3. DEFB +A3,exponent 73 DEFB +8F,+38,+EE,(+00) 4. DEFB +E9,exponent 79 DEFB +15,+63,+68,+23 5. DEFB +EE,exponent 7E DEFB +92 +0D,+CD,+ED 6. DEFB +F1,exponent81 DEFB +23,+5D,+1B,+EA
At the end of the last loop the 'last value' is (SIN (PIW/21)/W.
Perform step v.
DEFB +04,multiply,17C6 SIN (PIW/2) = SIN X (or= COS X) DEFB +34,end-calc.,0028 RET Finished: 'last value' = SIN X. or ('last value' = COS X I.
This subroutine handles the function TAN X. The subroutine simply returns SIN X/COS X, with arithmetic overflow if COS X=0 .
1D6E tan RST 0028,FP-CALC. X DEFB +2D,duplicate,19F6 X, X DEFB +1C,sin,1D49 X, SIN X DEFB +01,exchange,1A72 SIN X, X DEFB +1D,cos,103E SIN X, COS X DEFB +05,division,1882 SIN X/COS X = TAN X Report arithmetic overflow if needed. DEFB +34,end-calc.,002B TAN X RET Finished: 'last value' = TAN X.
This subroutine handles the function ATN X and is the last of the four routines that use SERIES GENERATOR to produce Chebyshev polynomials. It returns a real number between -PI/2 and PI/2, which is equal to the value in radians of the angle whose tan is X.
The approximation to ATN X is found as follows:
i. The values W and Y are found for three cases of X, such that:
if-1<X<1 then W=0 & V=X -case i. if 1<=X then W=PI/2 & Y=-1/X - case ii. if X<=-1 then W=-PI/2 & Y=-1/X - case iv.
In each case, -1<=Y<-1, as required for the series to converge.
The argument Z is formed, such that: if-1<X<1 then Z = 2*Y`Y-1 = 2*X*X-1 - case i. if 1<X then Z = 2*YY-1 = 2/(X'X)-1 - case ii. if X<=-1 then Z = 2*Y'Y-1 = 2/(X'X)-1 - case iii.
iii. The SERIES GENERATOR is used to produce the required function.
iv. Finally a simple multiplication and addition give ATN X.
Perform stage i.
1D76 atn LD A,(RL) Fetch the exponent of X. CP +81 JR C,1D89,SMALL Jump forward for case is Y = X. RST 0028,FP-CALC. X DEFB +A1,stk-one,1A51 X, 1 DEFB +18,negate,1AA0 X,-1 DEFB +01,exchange,1A72 -1, X DEFB +05,division,1882 -1/X DEFB +2D,duplicate,19F6 -1/X, -1/X DEFB +32,less-0,1ADB -1/X, I1/0) DEFB +A3,stk-pi/2,1A51 -1/X, 11/01, PI/2 DEFB +01,exchange,1A72 -1/X, P1/2, (1/01 DEFB +00,jump-true,1C2F -1/X, PI/2 DEFB +06, to 1D8B,CASES Jump forward for case ii: Y = -1/X W=PI/2 DEFB +18,negate,lAA0 -1/X,-PI/2 DEFB +2F,jump,1C23 -1/X,-PI/2 DEFB +03, to 1 D88,CASES Jump forward for case F. Y1/X W = -PI/2 1D89 SMALL RST 0028,FP-CALL. Y DEFB +A0,stk-zero,l A51 Y,0 Continue for case is W = 0
Perform step ii.
1D88 CASES DEFB +01,exchange,1A72 W,V DEFB +2D,duplicate,19F6 W, Y, Y DEFB +2D,duplicate,19F6 W, Y, Y, Y DEFB +04,multiply,17C6 W, Y, Y'V DEFB +2D,duplicate,19F6 W, Y, Y"Y, Y'Y DEFB +0F,addition,1755 W, Y, 2'YY DEFB +A1,stkone,lA51 W, Y, 2*Y'Y, 1 DEFB +03,subtract,174C W, Y, 2Y*V-1 = Z
Perform step Hi, passing to the SERIES GENERATOR the parameter '12' decimal, and the twelve constants required.
DEFB +8C,series-0C,1A7F W, Y,Z 1. DEFB +10,exponent 60 DEFB +B2,(+00,+00,+00) 2. DEFB +1Cexponent 63 DEFB +0E,(+00,+00,+00) 3. DEFB +55,exponent65 DEFB +E4,+8D,(+00,+00) 4. DEFB +58,exponent68 DEFB +39,+BC,(+00,+00) 5. DEFB +SB,exponent 6B DEFB +98+FD,(+00,+00) 6. DEFB +9E,exponent 6E DEFB +00,+36,+75,(+OO) 7. DEFB +A0,exponent 70 DEFB +DB,+E8,+B4,(+00) 8. DEFB +63,exponent 73 DEFB +42,+C4,(+00,+00) 9. DEFB +E6,exponent 76 DEFB +85,+09,+36,+BE 10. DEFB +E9,exponent 79 DEFB +36,+73,+18+5D 11. DEFB +EC,exponent 7C DEFB +D8,+DE,+63,+BE 12. DEFB +F0,exponent BO DEFB +61 +A1,+B3,+OC
At the end of the last loop the 'last value' is:
ATN X/X case 1. ATN I-1/XI/(-1/X) case ii. ATN (-1/X)/l-1/XI case iii.
Perform step iv.
DEFB +04,multiply,17C6 W, ATN X case i. W, ATN (-1/X) case ii. W, ATN (-1/X) case iii. DEFB +0F,addition,1755 ATN X all cases now. DEFB +34,end-calc.,0028 RET Finished: 'last value' = ATN X.
This subroutine handles the function ASN X and returns a real number from -PI/2 to PI/2 inclusive which is equal to the value in radians of the angle whose sine is X. Thereby if Y = ASN X then X = SIN V.
This subroutine uses the trigonometric identity)
TAN (Y/2) = SIN Y/(1+COS Y)
to obtain TAN (Y/2) and hence (using ATN) Y/2 and finally Y.
1DC4 asn RST 0028,FP-CALC. X DEFB +2D,duplicate,19F6 X, X DEFB +2D,duplicate,19F6 X, X, X DEFB +04,multiply,17C6 X, XX DEFB +Al,stk-one,lA51 X, X*X, 1 DEFB +03,subtract,174C X, X*X-1 DEFB +18,negate,lAA0 X, 1-X*X DEFB +25,sgr,1DDB X, SQR (1-X*X) DEFB +A1,stk-one,1 A51 X, SQR (1-X*X), 1 DEFB +0F,addition,1755 X, 1+SQR DEFB +05,division,1882 X/11+SQR (1-X*X)1= TAN (Y/2)
DEFB +21,atn,1D76 Y/2 DEFB +2D,duplicate,19F6 Y/2, Y/2 DEFB +0F,addition,1755 Y = ASN X DEFB +34,end-calc.,002B RET Finished: 'last value' = ASN X.
This subroutine handles the function ACS X and returns a real number from zero to PI inclusive which is equal to the value in radians of the angle whose cosine is X.
This subroutine uses the relation:
ACS X = PI/2 - ASN X
1DD4 as RST 0028,FP-CALC. X DEFB +1F,asn,1DC4 ASN X DEFB +A3,stk-pi/2,1A51 ASN X, PI/2 DEFB +03,subtract,174C ASN X-PI/2 DEFB +18,negate,lAA0 PI/2-ASN X = ACS X DEFB +34,endcalc.,002B RET Finished: 'last value' = ACS X.
This subroutine handles the function SQR X and returns the positive square root of the real number X if X is positive, and zero if X is zero. A negative value of X gives rise to report A invalid argument (via In in the EXPONENTIATION subroutine).
This subroutine treats the square root operation as being X".5 and therefore stacks the value .5 and proceeds directly into the EXPONENTIATION subroutine.
1DDB sqr RST 0028,FP-CALC. X DEFB +2D,duplicate,19F6 X, X DEFB +2C,not,1AD5 X, (1/0) DEFB +00,jump-true,lC2F X DEFB +1E, to lDFD,LAST X
The jump is made if X = 0, otherwise continue with:
DEFB +A2,stk-helf,lA51 X, .5 DEFB +34,endcalc.,002B
and then find the result of X".5.
This subroutine performs the binary operation of raising the first number, X, to the power of the second number, Y.
The subroutine treats the result X"Y as being equivalent to EXP (Y' LN X). It returns this value unless X is zero, in which case it returns 1 if Y is also zero (0"0 =1), returns zero if Y is positive and reports arithmetic overflow if Y is negative.
1DE2 to-power RST 0028,FP-CALC. X,V DEFB +01,exchange,1 A72 Y, X DEFB +2D,duplicate,19F6 Y, X, X DEFB +2C,not,1AD5 Y, X, (1/0) DEFB +00,jump-true,lC2F Y, X DEFB +07, to 1DEE,XIS0 Y, X
The jump is made if X = 0, otherwise EXP (Y' LN X) is formed.
DEFB +22,ln,1CA9 Y, LN X Giving report A if X is negative. DEFB +04,multiply,17C6 Y' LNX DEFB +34,end-calc.,002B JP 1C5B,EXP Exit via EXP to form EXP (Y'LN X).
The value of Xis zero so consider the three possible cases involved.
1DEE XIS0 DEFB +02,delete,19E3 Y DEFB +2D,duplicate,19F6 Y, V DEFB +2C,not,1AD5 Y, (1/0) DEFB +00,jump-true,lC2F Y DEFB +09, to 1DFB4,ONE Y
The jump is made if X = 0 and Y = 0, otherwise proceed.
DEFB +A0,stk-zero,1A51 Y, 0 DEFB +01,exchange,1A72 0, Y DEFB +33,greater-0,1ACE 0, (1/0) DEFB +00,jump-true,lC2F 0 DEFB +06, to 1DFD,LAST 0
The jump is made if X = 0 and Y is positive, otherwise proceed.
DEFB +A1,stkone,1 A51 0, 1 DEFB +01,exchange,1A72 1, 0 DEFB +05,division,1882 Exit via 'division' as dividing by zero gives 'arithmetic overflow'.
The result is to be 1 for the operation.
1DFB ONE DEFB +02,delete,19E3 DEFB +A1,stk-one,1A51
Now return with the 'last value' on the stack being O'"Y.
1DFD LAST DEFB +34,end-calc.,002B (1/0) RET Finished: 'last value' is 0 or 1.
The following BASIC programs have been included as they give a good illustration of how Chebyshev polynomials are used to produce the approximations to the functions SIN, EXP, LN and ATN.
The series generator:
This subroutine is called by all the 'function' programs.
500 REM SERIES GENERATOR, ENTER 510 REM USING THE COUNTER BERG 520 REM AND ARRAY-A HOLDING THE 530 REM CONTANTS. 540 REM FIRST VALUE IN Z. 550 LET M0=2*Z 560 LET M2=0 570 LET T=0 580 FOR I=BERG TO 1 STEP-1 590 LET M1=M2 600 LET U=T*M0-M2-rA(BERG+1-I) 610 LET M2=T 620 LET T=U 630 NEXT I 640 LET T=T-M1 650 RETURN 660 REM LAST VALUE IN T.
In the above subroutine the variable are:
2 the entry value. T the exit value. M0 mem-0 M1 mem-1 M2 mem-2 I the counter for BERG. U a temporary variable for T. A(1) to A(BERG) the constants. BERG the number of constants to be used.
To see how the Chebyshev polynomials are generated, record on paper the values of U, M1, M2 and T through the lines 550 to 630, passing, say 6 times, through the loop, and keeping the algebraic expressions for A111 to A(6) without substituting numerical values. Then record T-M1. The multipliers of the constants All) to A)6) will then be the required Chebyshev polynomials. More precisely, the multiplier of A(1) will be 2*Ta(Z), for A(2) it will be 2*T0(Z) and so on to 2*Tr (21 for A(5) and finally To(Z) for A(61.
Note that To (Z)=1, Tr (Z)=Z and, for n>=2, T~(Z)=2'Z'T, (Z)-T~_~ (Z).
10 REM DEMONSTRATION FOR SIN X 20 SLOW 30 DIM A(6) 40 LET A(1)=-.000000003 50 LET A(21=0.000000592 60 LET A(3)=-.000068294 70 LET A(41=0.004559008 80 LET A)5)=-.142630785 90 LET A(6)=1.276278962 100 PRINT 110 PRINT "ENTER START VALUE IN DEGREES" 120 INPUT C 130 CLS 140 LET C=C-10 150 PRINT "BASIC PROGRAM", "ROM PROGRAM" 160 PRINT " " " 170 PRINT 180 FOR J=1 TO4 190 LET C=C+10 200 LET Y=C/360-INT IC1360.+.5) 210 LET W=4'Y 220 IF W>1 THEN LET W=2-W 230 IF W<-1 THEN LET W=-W-2 240 LET Z=2WW-1 250 LET BERG=6 260 REM USE "SERIES GENERATOR " 270 GOSUB 550 280 PRINT TAB6;"SIN ";C;" DEGREES" 290 PRINT 300 PRINT TW,SIN (PIC/180) 310 PRINT 320 NEXT J 330 GOTO 100
NOTES;
The constants A(l) to A(6) in lines 40 to 90 are given (apart from a factor of (A) in Abramowitzand Stegun Handbook of Mathematical Functions (Dover 1965) page 76. They can be checked by integrating (SIN (PI'X/2))/X over the interval U=0 to PI, after first multiplying by COS (N U) for each constant lie. N=1,2 6) and substituting COS U=2'X'X-1. Each result should then be divided by Pl. (This integration can be performed by approximate methods e.g. using Simpson's Rule if there is a reasonable computer or programmable calculator to hand.)
10 REM DEMONSTRATION FOR EXP X 20 SLOW 30 LET T=0 (This makes T the first variable.) 40 DIM A(8) 50 LET A(1)=0.000000001 60 LET A(2)=0.000000053 70 LET A(3)=0.000001851 8O LET A(4)=0.000053453 90 LET A(5)=0.001235714 100 LET A(6)=0.021446556 110 LET A(7)=0.248762434 120 LET A(8)=1.456999875 130 PRINT 140 PRINT "ENTER START VALUE" 150 INPUT C 160 CLS 170 LET C=C-10 180 PRINT "BASIC PROGRAM", "ROM PROGRAM" 190 PRINT " , 200 PRINT 210 FOR J=1 TO 4 220 LET C=C+10 230 LET D=C*1.442695041 (D=C*(1/LN 2);EXP C=2**D) 240 LET N=1NT D 250 LET Z=D-N (2(N+Z) is now required). 260 LET Z=2Z-1 270 LET BERG=8 280 REM USE "SERIES GENERATOR " 290 GOSUB 550 300 LET V=PEEK 16400+256*PEEK 16401+1 (V=(VAR S)+1) 310 LET N=N+PEEK V 320 IF N>255 THEN POKE 16384,5 (Gives report 6, arithmetic overf low; 330 IF N<O THEN GOTO 360 program stops). 340 POKE V,N 350 GOTO 370 360 LET T=0 370 PRINT TAB 11;"EXP ";C 380 PRINT 390 PRINT T,EXPC 400 PRINT 410 NEXT J 420 GOTO 130
NOTES:
10 REM DEMONSTRATION FOR LN X 20 SLOW 30 LET D=0 This makes D the first variable). 40 DIM A(12) 50 LET A(1)=-.0000000003 60 LET A(2)=0.0000000020 70 LET A(3)=-.0000000127 80 LET A(4)=00000000823 90 LET A(5)=-.0000005389 100 LET A(6)=90000035828 110 LET A(7)=-.0000243013 120 LET A(8)=0.0001693953 130 LET A(9)=-.0012282837 140 LET A(10)=0.0094766116 150 LET A(11)=-.0818414567 160 LET A(12)=0.9302292213 170 PRINT 180 PRINT "ENTER START VALUE" 190 INPUT C 200 CLS 210 PRINT "BASIC PROGRAM", "ROM PROGRAM" 220 PRINT ' PRINT 240 LET C=SQR C 250 FOR J=1 T04 260 LET C=CC 270 IF C=0 THEN POKE 16384,9 (Gives report A, invalid argument; 280 LET D=C program stops). 290 LET V=PEEK 16400+256"PEEK 16401+1 300 LET N=PEEK V-128 IN holds e'). 310 POKE, V,128 320 IF D<=0.8 THEN GOTO 360 (D holds X). 330 LET S=D-1 340 LET Z=2.5D-3 350 GOTO 390 360 LET N=N-1 370 LET S=2*D-1 380 LET Z=5^-3 390 LET R=N0.6931471806 (R holds N'LN 2). 400 LET BERG=12 410 REM USE "SERIES GENERATOR " 420 GOSUB 550 430 PRINT TABS;"LN";C 440 PRINT 450 PRINT S"T+R,LN C 460 PRINT 470 NEXTJ 480 GOTO 170
NOTES:
The above program requires more than 1K of RAM.
When C is entered this program calculates and prints LN C, LN (C"2(, LN IC' '41 and LN (C8(. It also prints the values obtained by using the ROM program. Fore specimen of results, try entering these values: 1.1; 0.9; 300; 0.004; 1 E5 (for overflow) and 1E-5 (for report A).
The constants All) to A(12) in lines 50 to 160 can be obtained by integrating 5 LN (4(X+11/5(/(4X-1) over the interval U=O to PI, after first multiplying by COS (NU) for each constant (i.e. for N =1,2...,12) and substituting COS U= 2X-1. Each result should then be divided by PI.
10 REM DEMONSTRATION FOR ATN X 20 SLOW 30 DIM A(12) 40 LET A(1)=-.0000000002 50 LET A(2)=0.0000000010 60 LET A(3)=-.0000000066 70 LET A(4)=00000000432 80 LET A(5)=-.0000002850 90 LET A(6)=0.0000019105 100 LET A(7)=-.0000131076 110 LET A(8)=0.0000928715 120 LET A(9)=-.0006905975 130 LET A(10)=0.0055679210 140 LET A(11)=-.0529464623 150 LET A(12)=0.8813735870 160 PRINT 170 PRINT "ENTER START VALUE" 180 INPUT C 190 CLS 200 PRINT "BASIC PROGRAM", "ROM PROGRAM" 210 PRINT " " , " 220 PRINT 230 FOR .1=1 TO 4 240 LET 8=J'C 250 LET D=B 260 IF ABS B>=1 THEN LET D=-118 270 LET Z=2D'D-1 280 LET BERG=12 290 REM USE "SERIES GENERATOR " 300 GOSUB 550 310 LET T=DT 320 IF B>= I THEN LET T=T+PI/2 330 IF B<=-1 THEN LET T=T-PI/2 340 PRINT TAB8;"ATN ";B 350 PRINT 360 PRINT T,ATN 6 (or PRINT T180/PI,ATN B180/PI 370 PRINT to obtain the answers in degrees) 380 NEXTJ 390 GOTO 160
NOTES:
The above program requires more than 1K of RAM.
When C is entered this program calculates and prints ATN C, ATN (C'2), ATN (C'3( and ATN IC'41. For a specimen of results, try entering these values: 0.2; -1; 10 and -100. The results may be found more interesting if converted to yield degrees by multiplying the answers in line 360 by 180/PI. For those readers who are using an unimproved ROM it is interesting to note the results given by entering 4.2E9 and then 4.3E9.
The constants A(l) to A1121 in lines 40 to 150 are given (apart from a factor of/:) in Abramowitz and Stegon Handbook of Mathematical Functions (Dover 19651 page 82. They can be checked by integrating ATN X/X over the interval U=0 to PI, after first multiplying by COS (N'U) for each parameter (i.e. for N=1,2 12) and substituting COS U=2X'X-1. Each result should then be divided by Pl.
An alternative subroutine for SIN X:
It is fairly straightforward to produce the full expansion of the Chebyshev polynomials and this can be written in BASIC as follows:
550 LET T=(32ZZ'Z`ZZ-40ZZ"Z+10Z)A(1) +(16`Z'Z'Z'Z-16`ZZ+2)A(2) +(8ZZZ-6 Z)A(3) +(4'Z`Z-2)A(4) + A(6) 560 RETURN
This subroutine is called instead of the SERIES GENERATOR and can be seen to be of a similar accuracy.
An alternative subroutine for EXP X:
The full expansion for EXP X is:
550 LET T=(128'ZZ'ZZZ'ZZ-224Z'Z'ZZ'Z+112ZZZ-14Z)A(1) +(64"ZZ'ZZZZ-061'ZZZ'Z+36'ZZ-2)'A(2) +132'Z'Z'ZZ"Z-40'ZZZ+10ZI'A(3) +I16'ZZZ"Z-16ZZ+2)'A(4) +18ZZZ-6ZI'A(5)
+(2Zl'A(7) +A(8) 560 RETURN
It is left as an exercise for the reader to produce the alternative subroutine for LN X and ATN X.
Page Page
ABSOLUTE MAGNITUDE 56 OR 5 ADD-BACK 38 OUT-NEXT 3 ADDITION 39 PEEK 5 ALPHA 26 PREPARE TO ADD 3 ALPHANUM 26 PREPARE TO MULTIPLY OR DIVIDE 4 ARCCOS 74 PRINT A FLOATING-POINT NUMBER 3 ARCSIN 73 PRIORITY TABLE 9 ARCTAN 71 RECLAIM-3 2 CALCULATOR 49 REDUCE ARGUMENT 8 CALCULATOR TABLES 47/48 REPORT-6 4 CHRIS 61 REPORT-B2 6 CLEAR 25 RESERVE 2 C0DE 63 SCANNING 2 COMPARISON 59 SERIES GENERATOR 5 COSINE 70 SET-MEM 2 CURSOR-IN 25 SET-STK-8 2 DE,(DE?1) 17 SHIFT ADDEND 3 DECIMAL TO FLOATING-POINT 26 SIGNUM 6 DECREASE THE COUNTER 63 SINE 7 DELETE 50 SINGLE OPERATION 5 DIM 22 SKIP CONSTANTS 5 DIVISION 44 SLICING 1 E-FORMAT TO FLOATING-POINT 28 SQUARE ROOT 7 EXCHANGE 54 STACK-A 2 EXPONENTIAL 65 STACK A CONSTANT 5 EXPONENTIATION 74 STACK-BC 2 FETCH TWO NUMBERS 37 STACK LITERALS 5 FLOATING-POINT TO A 31 STK-DIGIT 2 FLOATING-POINT TO 8C 30 STK-FETCH 2 GET FROM MEMORY AREA 53 STK-PNTRS 5 GREATER THAN ZERO 57 STKSTORE 1 HL=HL*DE 18 STK-VAR 1 INT 64 STORE IN MEMORY AREA 6 INT:EXP 17 STRING AND NUMBER 5 INTEGER TO FLOATING-POINT 28 STRING CONCATENATION 6 INTEGER TRUNCATION TOWARDS ZERO 46 STRS 6 JUMP 63 SUBTRACTION 3 JUMP ON TRUE 64 TABLE OF ADDRESSES 4 LEN 63 TABLE OF CONSTANTS 4 LESS THAN ZERO 58 TAN 7 LET 18 TEST-5-SPACES 6 LOOK-VARS 9 TEST-DONE 3 MEMORY LOCATION 53 TEST-INT 3 M0DULUS 64 UNARY MINUS 5 MOVE A FLOATING-POINT NUMBER 61 USR 5 MULTIPLICATION 41 VAL 6 NATURAL LOGARITHM 68 X-TEMP 2 NOT 67 ZERO OR ONE 6 NUMBER AND NUMBER 58
00 jump-true 1C2F 01 exchange 1A72 02 delete 19E3 03 subtract 174C 0F addition 1755 32 less-0 1ADB 33 greater-0 1ACE 34 end-calc. 002B A0 stk-zero 1A51 A1 stk-one 1A51 C0 st-mem-0 1A63 C1 st-mem-1 1A63 C2 st-mem-2 1A63 E0 get-mem-0 1A45 E1 get-mem-1 1A45 E2 get-mem-2 1A45
Part 1 converted to text by Einar Saukas with thanks to Wilf Rigter and David Gonzales
Part 2 roughly OCR'd to text
Formatting as HTML by Keith Howell.