zx81plus.asm (246238B)
1 ; =========================================================== 2 ; An Assembly Listing of the Operating System of the ZX81 ROM 3 ; =========================================================== 4 5 ; Last updated: 13-DEC-2004 6 ; 2011 Updated to remove -, +, /, *, &, 7 ; characters from labels (which confuse assemblers) 8 ; 9 ; 2011 Updated for conditional assembly of ORIGINAL or "Shoulders of Giants" ROM 10 ; 11 ; 2014-08-01 Updated to add CHARS_PER_LINE_WINDOW which is normally 32. 12 ; 13 ; The ideal pixel rates for square pixels on a PAL system are 14 ; 14.75 MHz (interlaced) and 15 ; 7.375 MHz (non-interlaced, which the ZX80/ZX81 are). 16 ; These are not commonly available but fortunately one can buy 17 ; baud-rate generator frequencies such as 18 ; 14.7456 and 7.3728 MHz that are only 0.03% low 19 ; which is more than close enough. 20 ; 21 ; ZX video normally has 6.5 MHz pixel rate, 22 ; so 32 characters take 256 pixels in 39.4 microseconds. 23 ; A 7.3728 MHz clock and 40 characters produces 24 ; 320 pixels in 43.4 microseconds. 25 ; 26 ; ZX80 video generation is software defined so it is 27 ; easy to get square pixels simply by subtracting 8 from the bytes 28 ; at hex addresses 287, 2AA and 2B8. 29 ; The video will appear to the left of the screen but 30 ; the characters will be square and a diagonal graphic line 31 ; will be at 45 degrees. 32 ; 33 ; ZX81 video generation in fast mode exactly the same as the ZX80. 34 ; 35 ; ZX81 video generation in slow mode is problematic, in that 36 ; the NMI generator expects a 3.25 MHz CPU clock 37 ; (3.25MHz / 208 = 15.625 kHz = 64 microsecond line period) 38 ; It is inside the ULA where it cannot be modified. 39 ; 40 ; Simply fitting a 7.3728 MHz crystal would reduce the line period to 41 ; 57.3 microseconds. Slow mode would require the CPU clock to be 42 ; divided by 236. 43 ; 44 ; Square pixels on NTSC requires 11+3/11 = 11.272... MHz (interlaced) 45 ; or 5.63.. non-interlaced which is slower than the original 6.5 MHz. 46 ; The NTSC line period is still 64 microseconds, so 256 pixels 47 ; stretch over 45 microseconds, and 320 pixels over 56 microseconds. 48 ; Thus it is possible to get square pixels on an NTSC display, 49 ; it is not possible to get 40 column text as well. 50 ; That would require the PAL clock, but pixels would not be square. 51 ; 52 ; The ZX printer is fixed in hardware. 53 ; It will not work in 40-column mode. 54 ; 55 ; 56 ; 57 ; PIXEL_CLOCK: equ 7372500 58 ; 59 ; on-line assembler complains about the line above 60 ; 61 ; CHARS_PER_LINE_WINDOW always 32 for 6.5 MHz pixel rate 62 ; always 40 for 7.375 MHz PAL square pixel rate 63 ; 64 ;CHARS_PER_LINE_WINDOW: equ 40 ; 32 originally 65 CHARS_PER_LINE_WINDOW: equ 32 66 ; 67 ; CHARS_PER_LINE always 32 for 6.5 MHz pixel rate 68 ; but 32 or 40 if using PAL square pixel rate 69 ; 70 ;CHARS_HORIZONTAL: equ 40 ; 32 originally 71 CHARS_HORIZONTAL: equ 32 72 CHARS_VERTICAL: equ 24 73 ; 74 ; 2014-08-01 75 ; Largely working but some bugs remain. 76 ; Working: 77 ; You can type text and it takes 40 characters before new line. 78 ; 40 characters are nicely centred in the screen. 79 ; PLOT X,Y accepts X from 0 to 79. 80 ; Faulty: 81 ; System crashing in an authentic ZX81 fashion, 82 ; I don't know if this is due to software bugs 83 ; or socket joint disturbance from key presses. 84 ; 85 ; 86 ; 2018-01-09 add org 87 ; Assembles using on-line assembler "zasm" at: 88 ; 89 ; http://k1.spdns.de/cgi-bin/zasm.cgi 90 ; 91 org 0 92 93 FALSE: equ 0 94 95 ORIGINAL: equ 1 96 NOT_BODGED: equ 1 97 98 ; 2018-02-09 CHARS_HORIZONTAL placed in SCROLL routine. 99 ; Thanks to Adam Klotblixt for testing code and spotting this bug. 100 ; Also added to some G007 routines. 101 ; 102 103 104 ; 105 ; Work in progress. 106 ; This file will cross-assemble an original version of the "Improved" 107 ; ZX81 ROM. 108 ; The file can be modified to change the behaviour of the ROM 109 ; when used in emulators although there is no spare space available. 110 ; 111 ; The documentation is incomplete and if you can find a copy 112 ; of "The Complete Spectrum ROM Disassembly" then many routines 113 ; such as POINTERS and most of the mathematical routines are 114 ; similar and often identical. 115 ; 116 ; I've used the labels from the above book in this file and also 117 ; some from the more elusive Complete ZX81 ROM Disassembly 118 ; by the same publishers, Melbourne House. 119 120 ; define stuff sensibly: 121 ; 122 ; I/O locations: 123 ; 124 IO_PORT_TAPE: equ $FF ; write 125 IO_PORT_SCREEN: equ $FF ; write 126 127 IO_PORT_KEYBOARD_RD: equ $FE ; A0 low 128 IO_PORT_NMI_GEN_ON: equ $FE ; A0 low 129 IO_PORT_NMI_GEN_OFF: equ $FD ; A1 low 130 IO_PORT_PRINTER: equ $FB ; A2 low 131 132 ; 133 ; System variables: 134 ; 135 RAMBASE: equ $4000 136 ERR_NR: equ $4000 ; The report code. Incremented before printing. 137 FLAGS: equ $4001 ; Bit 0: Suppression of leading space. 138 ; Bit 1: Control Flag for the printer. 139 ; Bit 2: Selects K or F mode; or, F or G 140 ; Bit 6: FP no. or string parameters. 141 ; Bit 7: Reset during syntax checking." 142 ERR_SP: equ $4002 ; Pointer to the GOSUB stack. 143 RAMTOP: equ $4004 ; The top of available RAM, or as specified. 144 MODE: equ $4006 ; Holds the code for K or F 145 PPC: equ $4007 ; Line number of the current statement. 146 PPC_hi: equ PPC+1 147 VERSN: equ $4009 ; Marks the start of the RAM that is saved. 148 E_PPC: equ $400A ; The BASIC line with the cursor 149 D_FILE: equ $400C ; Pointer to Display file 150 DF_CC: equ $400E ; Address for PRINT AT position 151 VARS: equ $4010 ; Pointer to variable area 152 DEST: equ $4012 ; Address of current variable in program area 153 E_LINE: equ $4014 ; Pointer to workspace 154 E_LINE_hi: equ E_LINE+1 155 CH_ADD: equ $4016 ; Pointer for scanning a line, in program or workspace 156 X_PTR: equ $4018 ; Pointer to syntax error. 157 X_PTR_lo: equ X_PTR 158 X_PTR_hi: equ X_PTR+1 159 STKBOT: equ $401A ; Pointer to calculator stack bottom. 160 STKEND: equ $401C ; Pointer to calculator stack end. 161 BERG: equ $401E ; Used for many different counting purposes 162 MEM: equ $401F ; Pointer to base of table of fp. nos, either in calc. stack or variable area. 163 ; ; Unused by ZX BASIC. Or FLAG Y for G007 164 DF_SZ: equ $4022 ; Number of lines in the lower screen 165 S_TOP: equ $4023 ; Current line number of automatic listing 166 LAST_K: equ $4025 ; Last Key pressed 167 DEBOUNCE_VAR: equ $4027 ; The de-bounce status 168 MARGIN: equ $4028 ; Adjusts for differing TV standards 169 NXTLIN: equ $4029 ; Next BASIC line to be interpreted 170 OLDPPC: equ $402B ; Last line number, in case needed. 171 FLAGX: equ $402D ; Bit 0: Reset indicates an arrayed variable 172 ; Bit 1: Reset indicates a given variable exists 173 ; Bit 5: Set during INPUT mode 174 ; Bit 7: Set when INPUT is to be numeric 175 STRLEN: equ $402E ; Length of a string, or a BASIC line 176 STRLEN_lo: equ STRLEN ; 177 T_ADDR: equ $4030 ; Pointer to parameter table. & distinguishes between PLOT & UNPLOT 178 SEED: equ $4032 ; For RANDOM function 179 FRAMES: equ $4034 ; Frame counter 180 FRAMES_hi: equ FRAMES+1 ; 181 COORDS: equ $4036 ; X & Y for PLOT 182 COORDS_x: equ COORDS ; 183 PR_CC: equ $4038 ; Print buffer counter 184 S_POSN: equ $4039 ; Line & Column for PRINT AT 185 S_POSN_x: equ $4039 ; 186 S_POSN_y: equ $403A ; 187 CDFLAG: equ $403B ; Bit 6 = the true fast/slow flag 188 ; Bit 7 = copy of the fast/slow flag. RESET when FAST needed 189 PRBUFF: equ $403C ; Printer buffer 190 PRBUFF_END: equ $405C ; 191 MEM_0_1st: equ $405D ; room for 5 floating point numbers (meme_0 to mem_ 5???) 192 ; $407B ; unused. Or RESTART to G007 193 ; $407D ; The BASIC program starts here 194 ;: equ $40 195 ;: equ $40 196 ;: equ $40 197 ; First byte after system variables: 198 USER_RAM: equ $407D 199 MAX_RAM: equ $7FFF 200 201 ;=============================== 202 ; ZX81 constants: 203 ;=============================== 204 ; ZX characters (not the same as ASCII) 205 ;------------------------------- 206 ZX_SPACE: equ $00 207 ; ZX_graphic: equ $01 208 ; ZX_graphic: equ $02 209 ; ZX_graphic: equ $03 210 ; ZX_graphic: equ $04 211 ; ZX_graphic: equ $05 212 ; ZX_graphic: equ $06 213 ; ZX_graphic: equ $07 214 ; ZX_graphic: equ $08 215 ; ZX_graphic: equ $09 216 ; ZX_graphic: equ $0A 217 ZX_QUOTE: equ $0B 218 ZX_POUND: equ $0C 219 ZX_DOLLAR: equ $0D 220 ZX_COLON: equ $0E 221 ZX_QUERY: equ $0F 222 ZX_BRACKET_LEFT: equ $10 223 ZX_BRACKET_RIGHT: equ $11 224 ZX_GREATER_THAN: equ $12 225 ZX_LESS_THAN: equ $13 226 ZX_EQUAL: equ $14 227 ZX_PLUS: equ $15 228 ZX_MINUS: equ $16 229 ZX_STAR: equ $17 230 ZX_SLASH: equ $18 231 ZX_SEMICOLON: equ $19 232 ZX_COMMA: equ $1A 233 ZX_PERIOD: equ $1B 234 ZX_0: equ $1C 235 ZX_1: equ $1D 236 ZX_2: equ $1E 237 ZX_3: equ $1F 238 ZX_4: equ $20 239 ZX_5: equ $21 240 ZX_6: equ $22 241 ZX_7: equ $23 242 ZX_8: equ $24 243 ZX_9: equ $25 244 ZX_A: equ $26 245 ZX_B: equ $27 246 ZX_C: equ $28 247 ZX_D: equ $29 248 ZX_E: equ $2A 249 ZX_F: equ $2B 250 ZX_G: equ $2C 251 ZX_H: equ $2D 252 ZX_I: equ $2E 253 ZX_J: equ $2F 254 ZX_K: equ $30 255 ZX_L: equ $31 256 ZX_M: equ $32 257 ZX_N: equ $33 258 ZX_O: equ $34 259 ZX_P: equ $35 260 ZX_Q: equ $36 261 ZX_R: equ $37 262 ZX_S: equ $38 263 ZX_T: equ $39 264 ZX_U: equ $3A 265 ZX_V: equ $3B 266 ZX_W: equ $3C 267 ZX_X: equ $3D 268 ZX_Y: equ $3E 269 ZX_Z: equ $3F 270 ZX_RND: equ $40 271 ZX_INKEY_STR: equ $41 272 ZX_PI: equ $42 273 ; 274 ; $43 to $6F not used 275 ; 276 ZX_cursor_up: equ $70 277 ZX_cursor_down: equ $71 278 ZX_cursor_left: equ $72 279 ZX_cursor_right: equ $73 280 281 ZX_GRAPHICS: equ $74 282 ZX_EDIT: equ $75 283 ZX_NEWLINE: equ $76 284 ZX_RUBOUT: equ $77 285 ZX_KL: equ $78 286 ZX_FUNCTION: equ $79 287 ; 288 ; $7A to $7F not used 289 ; 290 ZX_CURSOR: equ $7F 291 ; 292 ; $80 to $BF are inverses of $00 to $3F 293 ; 294 ; ZX_graphic: equ $80 ; inverse space 295 ; ZX_graphic: equ $81 296 ; ZX_graphic: equ $82 297 ; ZX_graphic: equ $83 298 ; ZX_graphic: equ $84 299 ; ZX_graphic: equ $85 300 ; ZX_graphic: equ $86 301 ; ZX_graphic: equ $87 302 ; ZX_graphic: equ $88 303 ; ZX_graphic: equ $89 304 ; ZX_graphic: equ $8A 305 ZX_INV_QUOTE: equ $8B 306 ZX_INV_POUND: equ $8C 307 ZX_INV_DOLLAR: equ $8D 308 ZX_INV_COLON: equ $8E 309 ZX_INV_QUERY: equ $8F 310 ZX_INV_BRACKET_RIGHT: equ $90 311 ZX_INV_BRACKET_LEFT: equ $91 312 ZX_INV_GT: equ $92 313 314 ZX_INV_PLUS: equ $95 315 ZX_INV_MINUS: equ $96 316 317 ZX_INV_K: equ $B0 318 ZX_INV_S: equ $B8 319 320 ZX_DOUBLE_QUOTE: equ $C0 321 ZX_AT: equ $C1 322 ZX_TAB: equ $C2 323 ; not used: equ $C3 324 ZX_CODE: equ $C4 325 ZX_VAL: equ $C5 326 ZX_LEN: equ $C6 327 ZX_SIN: equ $C7 328 ZX_COS: equ $C8 329 ZX_TAN: equ $C9 330 ZX_ASN: equ $CA 331 ZX_ACS: equ $CB 332 ZX_ATN: equ $CC 333 ZX_LN: equ $CD 334 ZX_EXP: equ $CE 335 ZX_INT: equ $CF 336 337 ZX_SQR: equ $D0 338 ZX_SGN: equ $D1 339 ZX_ABS: equ $D2 340 ZX_PEEK: equ $D3 341 ZX_USR: equ $D4 342 ZX_STR_STR: equ $D5 ; STR$ 343 ZX_CHR_STR: equ $D6 ; CHR$ 344 ZX_NOT: equ $D7 345 ZX_POWER: equ $D8 346 ZX_OR: equ $D9 347 ZX_AND: equ $DA 348 ZX_LESS_OR_EQUAL: equ $DB 349 ZX_GREATER_OR_EQUAL: equ $DC 350 ZX_NOT_EQUAL: equ $DD 351 ZX_THEN: equ $DE 352 ZX_TO: equ $DF 353 354 ZX_STEP: equ $E0 355 ZX_LPRINT: equ $E1 356 ZX_LLIST: equ $E2 357 ZX_STOP: equ $E3 358 ZX_SLOW: equ $E4 359 ZX_FAST: equ $E5 360 ZX_NEW: equ $E6 361 ZX_SCROLL: equ $E7 362 ZX_CONT: equ $E8 363 ZX_DIM: equ $E9 364 ZX_REM: equ $EA 365 ZX_FOR: equ $EB 366 ZX_GOTO: equ $EC 367 ZX_GOSUB: equ $ED 368 ZX_INPUT: equ $EE 369 ZX_LOAD: equ $EF 370 371 ZX_LIST: equ $F0 372 ZX_LET: equ $F1 373 ZX_PAUSE: equ $F2 374 ZX_NEXT: equ $F3 375 ZX_POKE: equ $F4 376 ZX_PRINT: equ $F5 377 ZX_PLOT: equ $F6 378 ZX_RUN: equ $F7 379 ZX_SAVE: equ $F8 380 ZX_RAND: equ $F9 381 ZX_IF: equ $FA 382 ZX_CLS: equ $FB 383 ZX_UNPLOT: equ $FC 384 ZX_CLEAR: equ $FD 385 ZX_RETURN: equ $FE 386 ZX_COPY: equ $FF 387 388 389 ; 390 _CLASS_00: equ 0 391 _CLASS_01: equ 1 392 _CLASS_02: equ 2 393 _CLASS_03: equ 3 394 _CLASS_04: equ 4 395 _CLASS_05: equ 5 396 _CLASS_06: equ 6 397 398 399 400 ; These values taken from BASIC manual 401 ; 402 ; 403 ERROR_CODE_SUCCESS: equ 0 404 ERROR_CODE_CONTROL_VARIABLE: equ 1 405 ERROR_CODE_UNDEFINED_VARIABLE: equ 2 406 ERROR_CODE_SUBSCRIPT_OUT_OF_RANGE: equ 3 407 ERROR_CODE_NOT_ENOUGH_MEMORY: equ 4 408 ERROR_CODE_NO_ROOM_ON_SCREEN: equ 5 409 ERROR_CODE_ARITHMETIC_OVERFLOW: equ 6 410 ERROR_CODE_RETURN_WITHOUT_GOSUB: equ 7 411 ERROR_CODE_INPUT_AS_A_COMMAND: equ 8 412 ERROR_CODE_STOP: equ 9 413 ERROR_CODE_INVALID_ARGUMENT: equ 10 414 415 ERROR_CODE_INTEGER_OUT_OF_RANGE: equ 11 416 ERROR_CODE_VAL_STRING_INVALID: equ 12 417 ERROR_CODE_BREAK: equ 13 418 419 ERROR_CODE_EMPTY_PROGRAM_NAME: equ 15 420 421 ; 422 ; codes for Forth-like calculator 423 ; 424 __jump_true: equ $00 425 __exchange: equ $01 426 __delete: equ $02 427 __subtract: equ $03 428 __multiply: equ $04 429 __division: equ $05 430 __to_power: equ $06 431 __or: equ $07 432 __boolean_num_and_num: equ $08 433 __num_l_eql: equ $09 434 __num_gr_eql: equ $0A 435 __nums_neql: equ $0B 436 __num_grtr: equ $0C 437 __num_less: equ $0D 438 __nums_eql: equ $0E 439 __addition: equ $0F 440 __strs_and_num: equ $10 441 __str_l_eql: equ $11 442 __str_gr_eql: equ $12 443 __strs_neql: equ $13 444 __str_grtr: equ $14 445 __str_less: equ $15 446 __strs_eql: equ $16 447 __strs_add: equ $17 448 __negate: equ $18 449 __code: equ $19 450 __val: equ $1A 451 __len: equ $1B 452 __sin: equ $1C 453 __cos: equ $1D 454 __tan: equ $1E 455 __asn: equ $1F 456 __acs: equ $20 457 __atn: equ $21 458 __ln: equ $22 459 __exp: equ $23 460 __int: equ $24 461 __sqr: equ $25 462 __sgn: equ $26 463 __abs: equ $27 464 __peek: equ $28 465 __usr_num: equ $29 466 __str_dollar: equ $2A 467 __chr_dollar: equ $2B 468 __not: equ $2C 469 __duplicate: equ $2D 470 __n_mod_m: equ $2E 471 __jump: equ $2F 472 __stk_data: equ $30 473 __dec_jr_nz: equ $31 474 __less_0: equ $32 475 __greater_0: equ $33 476 __end_calc: equ $34 477 __get_argt: equ $35 478 __truncate: equ $36 479 __fp_calc_2: equ $37 480 __e_to_fp: equ $38 481 482 ; 483 ; __series_xx: equ $39 : $80__$9F. 484 ; tells the stack machine to push 485 ; 0 to 31 floating-point values on the stack. 486 ; 487 __series_06: equ $86 488 __series_08: equ $88 489 __series_0C: equ $8C 490 ; __stk_const_xx: equ $3A : $A0__$BF. 491 ; __st_mem_xx: equ $3B : $C0__$DF. 492 ; __get_mem_xx: equ $3C : $E0__$FF. 493 494 __st_mem_0: equ $C0 495 __st_mem_1: equ $C1 496 __st_mem_2: equ $C2 497 __st_mem_3: equ $C3 498 __st_mem_4: equ $C4 499 __st_mem_5: equ $C5 500 __st_mem_6: equ $C6 501 __st_mem_7: equ $C7 502 503 504 __get_mem_0: equ $E0 505 __get_mem_1: equ $E1 506 __get_mem_2: equ $E2 507 __get_mem_3: equ $E3 508 __get_mem_4: equ $E4 509 510 511 __stk_zero: equ $A0 512 __stk_one: equ $A1 513 __stk_half: equ $A2 514 __stk_half_pi: equ $A3 515 __stk_ten: equ $A4 516 517 ;***************************************** 518 ;** Part 1. RESTART ROUTINES AND TABLES ** 519 ;***************************************** 520 521 522 ; THE 'START' 523 524 ; All Z80 chips start at location zero. 525 ; At start-up the Interrupt Mode is 0, ZX computers use Interrupt Mode 1. 526 ; Interrupts are disabled . 527 528 mark_0000: 529 START: 530 OUT (IO_PORT_NMI_GEN_OFF),A ; Turn off the NMI generator if this ROM is 531 ; running in ZX81 hardware. This does nothing 532 ; if this ROM is running within an upgraded 533 ; ZX80. 534 LD BC,MAX_RAM ; Set BC to the top of possible RAM. 535 ; The higher unpopulated addresses are used for 536 ; video generation. 537 JP RAM_CHECK ; Jump forward to RAM_CHECK. 538 539 540 ; THE 'ERROR' RESTART 541 542 ; The error restart deals immediately with an error. 543 ; ZX computers execute the same code in runtime as when checking syntax. 544 ; If the error occurred while running a program 545 ; then a brief report is produced. 546 ; If the error occurred while entering a BASIC line or in input etc., 547 ; then the error marker indicates the exact point at which the error lies. 548 549 mark_0008: 550 ERROR_1: 551 LD HL,(CH_ADD) ; fetch character address from CH_ADD. 552 LD (X_PTR),HL ; and set the error pointer X_PTR. 553 JR ERROR_2 ; forward to continue at ERROR_2. 554 555 556 ; THE 'PRINT A CHARACTER' RESTART 557 558 ; This restart prints the character in the accumulator using the alternate 559 ; register set so there is no requirement to save the main registers. 560 ; There is sufficient room available to separate a space (zero) from other 561 ; characters as leading spaces need not be considered with a space. 562 563 mark_0010: 564 PRINT_A: 565 AND A ; test for zero - space. 566 JP NZ,PRINT_CH ; jump forward if not to PRINT_CH. 567 568 JP PRINT_SP ; jump forward to PRINT_SP. 569 570 ; ___ 571 if ORIGINAL 572 DEFB $FF ; unused location. 573 else 574 DEFB $01 ;+ unused location. Version. PRINT PEEK 23 575 endif 576 577 578 ; THE 'COLLECT A CHARACTER' RESTART 579 580 ; The character addressed by the system variable CH_ADD is fetched and if it 581 ; is a non-space, non-cursor character it is returned else CH_ADD is 582 ; incremented and the new addressed character tested until it is not a space. 583 584 mark_0018: 585 GET_CHAR: 586 LD HL,(CH_ADD) ; set HL to character address CH_ADD. 587 LD A,(HL) ; fetch addressed character to A. 588 589 TEST_SP: 590 AND A ; test for space. 591 RET NZ ; return if not a space 592 593 NOP ; else trickle through 594 NOP ; to the next routine. 595 596 597 ; THE 'COLLECT NEXT CHARACTER' RESTART 598 599 ; The character address is incremented and the new addressed character is 600 ; returned if not a space, or cursor, else the process is repeated. 601 602 mark_0020: 603 NEXT_CHAR: 604 CALL CH_ADD_PLUS_1 ; gets next immediate 605 ; character. 606 JR TEST_SP ; back 607 ; ___ 608 609 DEFB $FF, $FF, $FF ; unused locations. 610 611 612 ; THE 'FLOATING POINT CALCULATOR' RESTART 613 614 ; this restart jumps to the recursive floating-point calculator. 615 ; the ZX81's internal, FORTH-like, stack-based language. 616 ; 617 ; In the five remaining bytes there is, appropriately, enough room for the 618 ; end-calc literal - the instruction which exits the calculator. 619 620 mark_0028: 621 FP_CALC: 622 if ORIGINAL 623 JP CALCULATE ; jump immediately to the CALCULATE routine. 624 else 625 626 JP CALCULATE ;+ jump to the NEW calculate routine address. 627 endif 628 629 mark_002B: 630 end_calc: 631 POP AF ; drop the calculator return address RE_ENTRY 632 EXX ; switch to the other set. 633 634 EX (SP),HL ; transfer H'L' to machine stack for the 635 ; return address. 636 ; when exiting recursion then the previous 637 ; pointer is transferred to H'L'. 638 639 EXX ; back to main set. 640 RET ; return. 641 642 643 644 ; THE 'MAKE BC SPACES' RESTART 645 646 ; This restart is used eight times to create, in workspace, the number of 647 ; spaces passed in the BC register. 648 649 mark_0030: 650 BC_SPACES: 651 PUSH BC ; push number of spaces on stack. 652 LD HL,(E_LINE) ; fetch edit line location from E_LINE. 653 PUSH HL ; save this value on stack. 654 JP RESERVE ; jump forward to continue at RESERVE. 655 656 657 658 _START: equ $00 659 _ERROR_1: equ $08 660 _PRINT_A: equ $10 661 _GET_CHAR: equ $18 662 _NEXT_CHAR: equ $20 663 _FP_CALC: equ $28 664 _BC_SPACES: equ $30 665 666 ; THE 'INTERRUPT' RESTART 667 668 ; The Mode 1 Interrupt routine is concerned solely with generating the central 669 ; television picture. 670 ; On the ZX81 interrupts are enabled only during the interrupt routine, 671 ; although the interrupt 672 ; 673 ; This Interrupt Service Routine automatically disables interrupts at the 674 ; outset and the last interrupt in a cascade exits before the interrupts are 675 ; enabled. 676 ; 677 ; There is no DI instruction in the ZX81 ROM. 678 ; 679 ; A maskable interrupt is triggered when bit 6 of the Z80's Refresh register 680 ; changes from set to reset. 681 ; 682 ; The Z80 will always be executing a HALT (NEWLINE) when the interrupt occurs. 683 ; A HALT instruction repeatedly executes NOPS but the seven lower bits 684 ; of the Refresh register are incremented each time as they are when any 685 ; simple instruction is executed. (The lower 7 bits are incremented twice for 686 ; a prefixed instruction) 687 ; 688 ; This is controlled by the Sinclair Computer Logic Chip - manufactured from 689 ; a Ferranti Uncommitted Logic Array. 690 ; 691 ; When a Mode 1 Interrupt occurs the Program Counter, which is the address in 692 ; the upper echo display following the NEWLINE/HALT instruction, goes on the 693 ; machine stack. 193 interrupts are required to generate the last part of 694 ; the 56th border line and then the 192 lines of the central TV picture and, 695 ; although each interrupt interrupts the previous one, there are no stack 696 ; problems as the 'return address' is discarded each time. 697 ; 698 ; The scan line counter in C counts down from 8 to 1 within the generation of 699 ; each text line. For the first interrupt in a cascade the initial value of 700 ; C is set to 1 for the last border line. 701 ; Timing is of the utmost importance as the RH border, horizontal retrace 702 ; and LH border are mostly generated in the 58 clock cycles this routine 703 ; takes . 704 705 706 707 MARK_0038: 708 INTERRUPT: 709 DEC C ; (4) decrement C - the scan line counter. 710 JP NZ,SCAN_LINE ; (10/10) JUMP forward if not zero to SCAN_LINE 711 712 POP HL ; (10) point to start of next row in display 713 ; file. 714 715 DEC B ; (4) decrement the row counter. (4) 716 RET Z ; (11/5) return when picture complete to R_IX_1_LAST_NEWLINE 717 ; with interrupts disabled. 718 719 SET 3,C ; (8) Load the scan line counter with eight. 720 ; Note. LD C,$08 is 7 clock cycles which 721 ; is way too fast. 722 723 ; -> 724 725 mark_0041: 726 WAIT_INT: 727 ; 728 ; NB $DD is for 32-column display 729 ; 730 LD R,A ; (9) Load R with initial rising value $DD. 731 732 EI ; (4) Enable Interrupts. [ R is now $DE ]. 733 734 JP (HL) ; (4) jump to the echo display file in upper 735 ; memory and execute characters $00 - $3F 736 ; as NOP instructions. The video hardware 737 ; is able to read these characters and, 738 ; with the I register is able to convert 739 ; the character bitmaps in this ROM into a 740 ; line of bytes. Eventually the NEWLINE/HALT 741 ; will be encountered before R reaches $FF. 742 ; It is however the transition from $FF to 743 ; $80 that triggers the next interrupt. 744 ; [ The Refresh register is now $DF ] 745 746 ; ___ 747 748 mark_0045: 749 SCAN_LINE: 750 POP DE ; (10) discard the address after NEWLINE as the 751 ; same text line has to be done again 752 ; eight times. 753 754 RET Z ; (5) Harmless Nonsensical Timing. 755 ; (condition never met) 756 757 JR WAIT_INT ; (12) back to WAIT_INT 758 759 ; Note. that a computer with less than 4K or RAM will have a collapsed 760 ; display file and the above mechanism deals with both types of display. 761 ; 762 ; With a full display, the 32 characters in the line are treated as NOPS 763 ; and the Refresh register rises from $E0 to $FF and, at the next instruction 764 ; - HALT, the interrupt occurs. 765 ; With a collapsed display and an initial NEWLINE/HALT, it is the NOPs 766 ; generated by the HALT that cause the Refresh value to rise from $E0 to $FF, 767 ; triggering an Interrupt on the next transition. 768 ; This works happily for all display lines between these extremes and the 769 ; generation of the 32 character, 1 pixel high, line will always take 128 770 ; clock cycles. 771 772 773 ; THE 'INCREMENT CH_ADD' SUBROUTINE 774 775 ; This is the subroutine that increments the character address system variable 776 ; and returns if it is not the cursor character. The ZX81 has an actual 777 ; character at the cursor position rather than a pointer system variable 778 ; as is the case with prior and subsequent ZX computers. 779 780 mark_0049: 781 CH_ADD_PLUS_1: 782 LD HL,(CH_ADD) ; fetch character address to CH_ADD. 783 784 mark_004C: 785 TEMP_PTR1: 786 INC HL ; address next immediate location. 787 788 mark_004D: 789 TEMP_PTR2: 790 LD (CH_ADD),HL ; update system variable CH_ADD. 791 792 LD A,(HL) ; fetch the character. 793 CP ZX_CURSOR ; compare to cursor character. 794 RET NZ ; return if not the cursor. 795 796 JR TEMP_PTR1 ; back for next character to TEMP_PTR1. 797 798 799 ; THE 'ERROR_2' BRANCH 800 801 ; This is a continuation of the error restart. 802 ; If the error occurred in runtime then the error stack pointer will probably 803 ; lead to an error report being printed unless it occurred during input. 804 ; If the error occurred when checking syntax then the error stack pointer 805 ; will be an editing routine and the position of the error will be shown 806 ; when the lower screen is reprinted. 807 808 mark_0056: 809 ERROR_2: 810 POP HL ; pop the return address which points to the 811 ; DEFB, error code, after the RST 08. 812 LD L,(HL) ; load L with the error code. HL is not needed 813 ; anymore. 814 815 mark_0058: 816 ERROR_3: 817 LD (IY+ERR_NR-RAMBASE),L ; place error code in system variable ERR_NR 818 LD SP,(ERR_SP) ; set the stack pointer from ERR_SP 819 CALL SLOW_FAST ; selects slow mode. 820 JP SET_MIN ; exit to address on stack via routine SET_MIN. 821 822 ; ___ 823 824 DEFB $FF ; unused. 825 826 827 ; THE 'NON MASKABLE INTERRUPT' ROUTINE 828 829 ; Jim Westwood's technical dodge using Non-Maskable Interrupts solved the 830 ; flicker problem of the ZX80 and gave the ZX81 a multi-tasking SLOW mode 831 ; with a steady display. Note that the AF' register is reserved for this 832 ; function and its interaction with the display routines. When counting 833 ; TV lines, the NMI makes no use of the main registers. 834 ; The circuitry for the NMI generator is contained within the SCL (Sinclair 835 ; Computer Logic) chip. 836 ; ( It takes 32 clock cycles while incrementing towards zero ). 837 838 mark_0066: 839 NMI: 840 EX AF,AF' ; (4) switch in the NMI's copy of the 841 ; accumulator. 842 INC A ; (4) increment. 843 JP M,NMI_RET ; (10/10) jump, if minus, to NMI_RET as this is 844 ; part of a test to see if the NMI 845 ; generation is working or an intermediate 846 ; value for the ascending negated blank 847 ; line counter. 848 849 JR Z,NMI_CONT ; (12) forward to NMI_CONT 850 ; when line count has incremented to zero. 851 852 ; Note. the synchronizing NMI when A increments from zero to one takes this 853 ; 7 clock cycle route making 39 clock cycles in all. 854 855 mark_006D: 856 NMI_RET: 857 EX AF,AF' ; (4) switch out the incremented line counter 858 ; or test result $80 859 RET ; (10) return to User application for a while. 860 861 ; ___ 862 863 ; This branch is taken when the 55 (or 31) lines have been drawn. 864 865 mark_006F: 866 NMI_CONT: 867 EX AF,AF' ; (4) restore the main accumulator. 868 869 PUSH AF ; (11) * Save Main Registers 870 PUSH BC ; (11) ** 871 PUSH DE ; (11) *** 872 PUSH HL ; (11) **** 873 874 ; the next set-up procedure is only really applicable when the top set of 875 ; blank lines have been generated. 876 877 LD HL,(D_FILE) ; (16) fetch start of Display File from D_FILE 878 ; points to the HALT at beginning. 879 SET 7,H ; (8) point to upper 32K 'echo display file' 880 881 HALT ; (1) HALT synchronizes with NMI. 882 ; Used with special hardware connected to the 883 ; Z80 HALT and WAIT lines to take 1 clock cycle. 884 885 886 ; the NMI has been generated - start counting. 887 ; The cathode ray is at the RH side of the TV. 888 ; 889 ; First the NMI servicing, similar to CALL = 17 clock cycles. 890 ; Then the time taken by the NMI for zero-to-one path = 39 cycles 891 ; The HALT above = 01 cycles. 892 ; The two instructions below = 19 cycles. 893 ; The code at R_IX_1 up to and including the CALL = 43 cycles. 894 ; The Called routine at DISPLAY_5 = 24 cycles. 895 ; -------------------------------------- --- 896 ; Total Z80 instructions = 143 cycles. 897 ; 898 ; Meanwhile in TV world, 899 ; Horizontal retrace = 15 cycles. 900 ; Left blanking border 8 character positions = 32 cycles 901 ; Generation of 75% scanline from the first NEWLINE = 96 cycles 902 ; --------------------------------------- --- 903 ; = 143 cycles 904 ; 905 ; Since at the time the first JP (HL) is encountered to execute the echo 906 ; display another 8 character positions have to be put out, then the 907 ; Refresh register need to hold $F8. Working back and counteracting 908 ; the fact that every instruction increments the Refresh register then 909 ; the value that is loaded into R needs to be $F5. :-) 910 ; 911 ; 912 OUT (IO_PORT_NMI_GEN_OFF),A ; (11) Stop the NMI generator. 913 914 JP (IX) ; (8) forward to R_IX_1 (after top) or R_IX_2 915 916 ; **************** 917 ; ** KEY TABLES ** 918 ; **************** 919 920 921 ; THE 'UNSHIFTED' CHARACTER CODES 922 923 924 mark_007E: 925 K_UNSHIFT: 926 DEFB ZX_Z 927 DEFB ZX_X 928 DEFB ZX_C 929 DEFB ZX_V 930 931 DEFB ZX_A 932 DEFB ZX_S 933 DEFB ZX_D 934 DEFB ZX_F 935 DEFB ZX_G 936 937 DEFB ZX_Q 938 DEFB ZX_W 939 DEFB ZX_E 940 DEFB ZX_R 941 DEFB ZX_T 942 943 DEFB ZX_1 944 DEFB ZX_2 945 DEFB ZX_3 946 DEFB ZX_4 947 DEFB ZX_5 948 949 DEFB ZX_0 950 DEFB ZX_9 951 DEFB ZX_8 952 DEFB ZX_7 953 DEFB ZX_6 954 955 DEFB ZX_P 956 DEFB ZX_O 957 DEFB ZX_I 958 DEFB ZX_U 959 DEFB ZX_Y 960 961 DEFB ZX_NEWLINE 962 DEFB ZX_L 963 DEFB ZX_K 964 DEFB ZX_J 965 DEFB ZX_H 966 967 DEFB ZX_SPACE 968 DEFB ZX_PERIOD 969 DEFB ZX_M 970 DEFB ZX_N 971 DEFB ZX_B 972 973 974 975 ; THE 'SHIFTED' CHARACTER CODES 976 977 978 979 mark_00A5: 980 K_SHIFT: 981 DEFB ZX_COLON ; : 982 DEFB ZX_SEMICOLON ; ; 983 DEFB ZX_QUERY ; ? 984 DEFB ZX_SLASH ; / 985 DEFB ZX_STOP 986 DEFB ZX_LPRINT 987 DEFB ZX_SLOW 988 DEFB ZX_FAST 989 DEFB ZX_LLIST 990 DEFB $C0 ; "" 991 DEFB ZX_OR 992 DEFB ZX_STEP 993 DEFB $DB ; <= 994 DEFB $DD ; <> 995 DEFB ZX_EDIT 996 DEFB ZX_AND 997 DEFB ZX_THEN 998 DEFB ZX_TO 999 DEFB $72 ; cursor-left 1000 DEFB ZX_RUBOUT 1001 DEFB ZX_GRAPHICS 1002 DEFB $73 ; cursor-right 1003 DEFB $70 ; cursor-up 1004 DEFB $71 ; cursor-down 1005 DEFB ZX_QUOTE ; " 1006 DEFB $11 ; ) 1007 DEFB $10 ; ( 1008 DEFB ZX_DOLLAR ; $ 1009 DEFB $DC ; >= 1010 DEFB ZX_FUNCTION 1011 DEFB ZX_EQUAL 1012 DEFB ZX_PLUS 1013 DEFB ZX_MINUS 1014 DEFB ZX_POWER ; ** 1015 DEFB ZX_POUND ; � 1016 DEFB ZX_COMMA ; , 1017 DEFB ZX_GREATER_THAN ; > 1018 DEFB ZX_LESS_THAN ; < 1019 DEFB ZX_STAR ; * 1020 1021 1022 ; THE 'FUNCTION' CHARACTER CODES 1023 1024 1025 1026 mark_00CC: 1027 K_FUNCT: 1028 DEFB ZX_LN 1029 DEFB ZX_EXP 1030 DEFB ZX_AT 1031 DEFB ZX_KL 1032 DEFB ZX_ASN 1033 DEFB ZX_ACS 1034 DEFB ZX_ATN 1035 DEFB ZX_SGN 1036 DEFB ZX_ABS 1037 DEFB ZX_SIN 1038 DEFB ZX_COS 1039 DEFB ZX_TAN 1040 DEFB ZX_INT 1041 DEFB ZX_RND 1042 DEFB ZX_KL 1043 DEFB ZX_KL 1044 DEFB ZX_KL 1045 DEFB ZX_KL 1046 DEFB ZX_KL 1047 DEFB ZX_KL 1048 DEFB ZX_KL 1049 DEFB ZX_KL 1050 DEFB ZX_KL 1051 DEFB ZX_KL 1052 DEFB ZX_TAB 1053 DEFB ZX_PEEK 1054 DEFB ZX_CODE 1055 DEFB ZX_CHR_STR ; CHR$ 1056 DEFB ZX_STR_STR ; STR$ 1057 DEFB ZX_KL 1058 DEFB ZX_USR 1059 DEFB ZX_LEN 1060 DEFB ZX_VAL 1061 DEFB ZX_SQR 1062 DEFB ZX_KL 1063 DEFB ZX_KL 1064 DEFB ZX_PI 1065 DEFB ZX_NOT 1066 DEFB ZX_INKEY_STR 1067 1068 1069 1070 ; THE 'GRAPHIC' CHARACTER CODES 1071 1072 1073 1074 mark_00F3: 1075 K_GRAPH: 1076 DEFB $08 ; graphic 1077 DEFB $0A ; graphic 1078 DEFB $09 ; graphic 1079 DEFB $8A ; graphic 1080 DEFB $89 ; graphic 1081 1082 DEFB $81 ; graphic 1083 DEFB $82 ; graphic 1084 DEFB $07 ; graphic 1085 DEFB $84 ; graphic 1086 DEFB $06 ; graphic 1087 1088 DEFB $01 ; graphic 1089 DEFB $02 ; graphic 1090 DEFB $87 ; graphic 1091 DEFB $04 ; graphic 1092 DEFB $05 ; graphic 1093 1094 DEFB ZX_RUBOUT 1095 DEFB ZX_KL 1096 DEFB $85 ; graphic 1097 DEFB $03 ; graphic 1098 DEFB $83 ; graphic 1099 1100 DEFB $8B ; graphic 1101 DEFB $91 ; inverse ) 1102 DEFB $90 ; inverse ( 1103 DEFB $8D ; inverse $ 1104 DEFB $86 ; graphic 1105 1106 DEFB ZX_KL 1107 DEFB $92 ; inverse > 1108 DEFB $95 ; inverse + 1109 DEFB $96 ; inverse - 1110 DEFB $88 ; graphic 1111 1112 1113 ; THE 'TOKEN' TABLES 1114 1115 1116 1117 mark_0111: 1118 TOKEN_TABLE: 1119 DEFB ZX_QUERY +$80; '?' 1120 DEFB ZX_QUOTE, ZX_QUOTE +$80; "" 1121 DEFB ZX_A, ZX_T +$80; AT 1122 DEFB ZX_T, ZX_A, ZX_B +$80; TAB 1123 DEFB ZX_QUERY +$80; '?' 1124 DEFB ZX_C, ZX_O, ZX_D, ZX_E +$80; CODE 1125 DEFB ZX_V, ZX_A, ZX_L +$80; VAL 1126 DEFB ZX_L, ZX_E, ZX_N +$80; LEN 1127 DEFB ZX_S, ZX_I, ZX_N +$80; SIN 1128 DEFB ZX_C, ZX_O, ZX_S +$80; COS 1129 DEFB ZX_T, ZX_A, ZX_N +$80; TAN 1130 DEFB ZX_A, ZX_S, ZX_N +$80; ASN 1131 DEFB ZX_A, ZX_C, ZX_S +$80; ACS 1132 DEFB ZX_A, ZX_T, ZX_N +$80; ATN 1133 DEFB ZX_L, ZX_N +$80; LN 1134 DEFB ZX_E, ZX_X, ZX_P +$80; EXP 1135 DEFB ZX_I, ZX_N, ZX_T +$80; INT 1136 DEFB ZX_S, ZX_Q, ZX_R +$80; SQR 1137 DEFB ZX_S, ZX_G, ZX_N +$80; SGN 1138 DEFB ZX_A, ZX_B, ZX_S +$80; ABS 1139 DEFB ZX_P, ZX_E, ZX_E, ZX_K +$80; PEEK 1140 DEFB ZX_U, ZX_S, ZX_R +$80; USR 1141 DEFB ZX_S, ZX_T, ZX_R, ZX_DOLLAR +$80; STR$ 1142 DEFB ZX_C, ZX_H, ZX_R, ZX_DOLLAR +$80; CHR$ 1143 DEFB ZX_N, ZX_O, ZX_T +$80; NOT 1144 DEFB ZX_STAR, ZX_STAR +$80; ** 1145 DEFB ZX_O, ZX_R +$80; OR 1146 DEFB ZX_A, ZX_N, ZX_D +$80; AND 1147 DEFB ZX_LESS_THAN, ZX_EQUAL +$80; >= 1148 DEFB ZX_GREATER_THAN, ZX_EQUAL +$80; <= 1149 DEFB ZX_LESS_THAN, ZX_GREATER_THAN +$80; >< 1150 DEFB ZX_T, ZX_H, ZX_E, ZX_N +$80; THEN 1151 DEFB ZX_T, ZX_O +$80; TO 1152 DEFB ZX_S, ZX_T, ZX_E, ZX_P +$80; STEP 1153 DEFB ZX_L, ZX_P, ZX_R, ZX_I, ZX_N, ZX_T +$80; LPRINT 1154 DEFB ZX_L, ZX_L, ZX_I, ZX_S, ZX_T +$80; LLIST 1155 DEFB ZX_S, ZX_T, ZX_O, ZX_P +$80; STOP 1156 DEFB ZX_S, ZX_L, ZX_O, ZX_W +$80; SLOW 1157 DEFB ZX_F, ZX_A, ZX_S, ZX_T +$80; FAST 1158 DEFB ZX_N, ZX_E, ZX_W +$80; NEW 1159 DEFB ZX_S, ZX_C, ZX_R, ZX_O, ZX_L, ZX_L +$80; SCROLL 1160 DEFB ZX_C, ZX_O, ZX_N, ZX_T +$80; CONT 1161 DEFB ZX_D, ZX_I, ZX_M +$80; DIM 1162 DEFB ZX_R, ZX_E, ZX_M +$80; REM 1163 DEFB ZX_F, ZX_O, ZX_R +$80; FOR 1164 DEFB ZX_G, ZX_O, ZX_T, ZX_O +$80; GOTO 1165 DEFB ZX_G, ZX_O, ZX_S, ZX_U, ZX_B +$80; GOSUB 1166 DEFB ZX_I, ZX_N, ZX_P, ZX_U, ZX_T +$80; INPUT 1167 DEFB ZX_L, ZX_O, ZX_A, ZX_D +$80; LOAD 1168 DEFB ZX_L, ZX_I, ZX_S, ZX_T +$80; LIST 1169 DEFB ZX_L, ZX_E, ZX_T +$80; LET 1170 DEFB ZX_P, ZX_A, ZX_U, ZX_S, ZX_E +$80; PAUSE 1171 DEFB ZX_N, ZX_E, ZX_X, ZX_T +$80; NEXT 1172 DEFB ZX_P, ZX_O, ZX_K, ZX_E +$80; POKE 1173 DEFB ZX_P, ZX_R, ZX_I, ZX_N, ZX_T +$80; PRINT 1174 DEFB ZX_P, ZX_L, ZX_O, ZX_T +$80; PLOT 1175 DEFB ZX_R, ZX_U, ZX_N +$80; RUN 1176 DEFB ZX_S, ZX_A, ZX_V, ZX_E +$80; SAVE 1177 DEFB ZX_R, ZX_A, ZX_N, ZX_D +$80; RAND 1178 DEFB ZX_I, ZX_F +$80; IF 1179 DEFB ZX_C, ZX_L, ZX_S +$80; CLS 1180 DEFB ZX_U, ZX_N, ZX_P, ZX_L, ZX_O, ZX_T +$80; UNPLOT 1181 DEFB ZX_C, ZX_L, ZX_E, ZX_A, ZX_R +$80; CLEAR 1182 DEFB ZX_R, ZX_E, ZX_T, ZX_U, ZX_R, ZX_N +$80; RETURN 1183 DEFB ZX_C, ZX_O, ZX_P, ZX_Y +$80; COPY 1184 DEFB ZX_R, ZX_N, ZX_D +$80; RND 1185 DEFB ZX_I, ZX_N, ZX_K, ZX_E, ZX_Y, ZX_DOLLAR +$80; INKEY$ 1186 DEFB ZX_P, ZX_I +$80; PI 1187 1188 1189 ; THE 'LOAD_SAVE UPDATE' ROUTINE 1190 1191 ; 1192 ; 1193 1194 mark_01FC: 1195 LOAD_SAVE: 1196 INC HL ; 1197 EX DE,HL ; 1198 LD HL,(E_LINE) ; system variable edit line E_LINE. 1199 SCF ; set carry flag 1200 SBC HL,DE ; 1201 EX DE,HL ; 1202 RET NC ; return if more bytes to LOAD_SAVE. 1203 1204 POP HL ; else drop return address 1205 1206 1207 ; THE 'DISPLAY' ROUTINES 1208 1209 ; 1210 ; 1211 1212 mark_0207: 1213 SLOW_FAST: 1214 LD HL,CDFLAG ; Address the system variable CDFLAG. 1215 LD A,(HL) ; Load value to the accumulator. 1216 RLA ; rotate bit 6 to position 7. 1217 XOR (HL) ; exclusive or with original bit 7. 1218 RLA ; rotate result out to carry. 1219 RET NC ; return if both bits were the same. 1220 1221 ; Now test if this really is a ZX81 or a ZX80 running the upgraded ROM. 1222 ; The standard ZX80 did not have an NMI generator. 1223 1224 LD A,$7F ; Load accumulator with %011111111 1225 EX AF,AF' ; save in AF' 1226 1227 LD B,17 ; A counter within which an NMI should occur 1228 ; if this is a ZX81. 1229 OUT (IO_PORT_NMI_GEN_ON),A ; start the NMI generator. 1230 1231 ; Note that if this is a ZX81 then the NMI will increment AF'. 1232 1233 mark_0216: 1234 LOOP_11: 1235 1236 DJNZ LOOP_11 ; self loop to give the NMI a chance to kick in. 1237 ; = 16*13 clock cycles + 8 = 216 clock cycles. 1238 1239 OUT (IO_PORT_NMI_GEN_OFF),A ; Turn off the NMI generator. 1240 EX AF,AF' ; bring back the AF' value. 1241 RLA ; test bit 7. 1242 JR NC,NO_SLOW ; forward, if bit 7 is still reset, to NO_SLOW. 1243 1244 ; If the AF' was incremented then the NMI generator works and SLOW mode can 1245 ; be set. 1246 1247 SET 7,(HL) ; Indicate SLOW mode - Compute and Display. 1248 1249 PUSH AF ; * Save Main Registers 1250 PUSH BC ; ** 1251 PUSH DE ; *** 1252 PUSH HL ; **** 1253 1254 JR DISPLAY_1 ; skip forward - to DISPLAY_1. 1255 1256 ; ___ 1257 1258 mark_0226: 1259 NO_SLOW: 1260 RES 6,(HL) ; reset bit 6 of CDFLAG. 1261 RET ; return. 1262 1263 1264 ; THE 'MAIN DISPLAY' LOOP 1265 1266 ; This routine is executed once for every frame displayed. 1267 1268 mark_0229: 1269 DISPLAY_1: 1270 1271 LD HL,(FRAMES) ; fetch two-byte system variable FRAMES. 1272 DEC HL ; decrement frames counter. 1273 mark_022D: 1274 DISPLAY_P: 1275 LD A,$7F ; prepare a mask 1276 AND H ; pick up bits 6-0 of H. 1277 OR L ; and any bits of L. 1278 LD A,H ; reload A with all bits of H for PAUSE test. 1279 1280 ; Note both branches must take the same time. 1281 1282 JR NZ,ANOTHER ; (12/7) forward if bits 14-0 are not zero 1283 ; to ANOTHER 1284 1285 RLA ; (4) test bit 15 of FRAMES. 1286 JR OVER_NC ; (12) forward with result to OVER_NC 1287 1288 ; ___ 1289 1290 mark_0237: 1291 ANOTHER: 1292 LD B,(HL) ; (7) Note. Harmless Nonsensical Timing weight. 1293 SCF ; (4) Set Carry Flag. 1294 1295 ; Note. the branch to here takes either (12)(7)(4) cyles or (7)(4)(12) cycles. 1296 1297 mark_0239: 1298 OVER_NC: 1299 LD H,A ; (4) set H to zero 1300 LD (FRAMES),HL ; (16) update system variable FRAMES 1301 RET NC ; (11/5) return if FRAMES is in use by PAUSE 1302 ; command. 1303 1304 mark_023E: 1305 DISPLAY_2: 1306 CALL KEYBOARD ; gets the key row in H and the column in L. 1307 ; Reading the ports also starts 1308 ; the TV frame synchronization pulse. (VSYNC) 1309 1310 LD BC,(LAST_K) ; fetch the last key values 1311 LD (LAST_K),HL ; update LAST_K with new values. 1312 1313 LD A,B ; load A with previous column - will be $FF if 1314 ; there was no key. 1315 ADD A,2 ; adding two will set carry if no previous key. 1316 1317 SBC HL,BC ; subtract with the carry the two key values. 1318 1319 ; If the same key value has been returned twice then HL will be zero. 1320 1321 LD A,(DEBOUNCE_VAR) 1322 OR H ; and OR with both bytes of the difference 1323 OR L ; setting the zero flag for the upcoming branch. 1324 1325 LD E,B ; transfer the column value to E 1326 LD B,11 ; and load B with eleven 1327 1328 LD HL,CDFLAG ; address system variable CDFLAG 1329 RES 0,(HL) ; reset the rightmost bit of CDFLAG 1330 JR NZ,NO_KEY ; skip forward if debounce/diff >0 to NO_KEY 1331 1332 BIT 7,(HL) ; test compute and display bit of CDFLAG 1333 SET 0,(HL) ; set the rightmost bit of CDFLAG. 1334 RET Z ; return if bit 7 indicated fast mode. 1335 1336 DEC B ; (4) decrement the counter. 1337 NOP ; (4) Timing - 4 clock cycles. ?? 1338 SCF ; (4) Set Carry Flag 1339 1340 mark_0264: 1341 NO_KEY: 1342 1343 LD HL,DEBOUNCE_VAR ; 1344 CCF ; Complement Carry Flag 1345 RL B ; rotate left B picking up carry 1346 ; C<-76543210<-C 1347 1348 1349 1350 1351 1352 1353 mark_026A: 1354 LOOP_B: 1355 1356 DJNZ LOOP_B ; self-loop while B>0 to LOOP_B 1357 1358 LD B,(HL) ; fetch value of DEBOUNCE_VAR to B 1359 LD A,E ; transfer column value 1360 CP $FE ; 1361 SBC A,A ; A = A-A-C = 0-Carry 1362 if 1 1363 ; I think this truncating DEBOUNCE_VAR 1364 ; which would explain why the VSYNC time didn't match 1365 ; my calculations that assumed debouncing for 255 loops. 1366 ; 1367 ; 1368 LD B,$1F ; binary 000 11111 1369 OR (HL) ; 1370 AND B ; truncate column, 0 to 31 1371 endif 1372 RRA ; 1373 LD (HL),A ; 1374 1375 OUT (IO_PORT_SCREEN),A ; end the TV frame synchronization pulse. 1376 1377 LD HL,(D_FILE) ; (12) set HL to the Display File from D_FILE 1378 SET 7,H ; (8) set bit 15 to address the echo display. 1379 1380 CALL DISPLAY_3 ; (17) routine DISPLAY_3 displays the top set 1381 ; of blank lines. 1382 1383 1384 ; THE 'VIDEO_1' ROUTINE 1385 1386 1387 mark_0281: 1388 R_IX_1: 1389 LD A,R ; (9) Harmless Nonsensical Timing 1390 ; or something very clever? 1391 LD BC,25*256+1 ; (10) 25 lines, 1 scanline in first. ($1901) 1392 1393 ; 32 characters, use $F5 (i.e. minus 11) 1394 ; 40 characters, use $ED (i.e. minus 19) 1395 ; 1396 1397 mark_0286: 1398 LD A,277-CHARS_PER_LINE_WINDOW ; $F5 for 6.5MHz clocked machines 1399 ; (7) This value will be loaded into R and 1400 ; ensures that the cycle starts at the right 1401 ; part of the display - after last character 1402 ; position. 1403 1404 CALL DISPLAY_5 ; (17) routine DISPLAY_5 completes the current 1405 ; blank line and then generates the display of 1406 ; the live picture using INT interrupts 1407 ; The final interrupt returns to the next 1408 ; address. 1409 R_IX_1_LAST_NEWLINE: 1410 DEC HL ; point HL to the last NEWLINE/HALT. 1411 1412 CALL DISPLAY_3 ; displays the bottom set of blank lines. 1413 1414 ; ___ 1415 1416 mark_028F: 1417 R_IX_2: 1418 JP DISPLAY_1 ; JUMP back to DISPLAY_1 1419 1420 1421 ; THE 'DISPLAY BLANK LINES' ROUTINE 1422 1423 ; This subroutine is called twice (see above) to generate first the blank 1424 ; lines at the top of the television display and then the blank lines at the 1425 ; bottom of the display. 1426 ; 1427 ; It is actually pretty bad. 1428 ; PAL or NTSC = 312 or 1429 ; 1 to 5 = 5 long and 5 short sync 1430 ; 6 to 23 = blank 1431 ; 24 to 309 = image 1432 ; 310 to 312 = 6 short sync 1433 ; 1434 ; The ZX80 generates either 62 or 110 blank lines 1435 ; 1436 ; 262 - 31 - 31 = 200 1437 ; 312 - 55 - 55 = 202 1438 ; 1439 ; This does not include 'VSYNC' line periods. 1440 ; 1441 1442 mark_0292: 1443 DISPLAY_3: 1444 POP IX ; pop the return address to IX register. 1445 ; will be either R_IX_1 or R_IX_2 - see above. 1446 1447 LD C,(IY+MARGIN-RAMBASE) ; load C with value of system constant MARGIN. 1448 BIT 7,(IY+CDFLAG-RAMBASE) ; test CDFLAG for compute and display. 1449 JR Z,DISPLAY_4 ; forward, with FAST mode, to DISPLAY_4 1450 1451 LD A,C ; move MARGIN to A - 31d or 55d. 1452 NEG ; Negate 1453 INC A ; 1454 EX AF,AF' ; place negative count of blank lines in A' 1455 1456 OUT (IO_PORT_NMI_GEN_ON),A ; enable the NMI generator. 1457 1458 POP HL ; **** 1459 POP DE ; *** 1460 POP BC ; ** 1461 POP AF ; * Restore Main Registers 1462 1463 RET ; return - end of interrupt. Return is to 1464 ; user's program - BASIC or machine code. 1465 ; which will be interrupted by every NMI. 1466 1467 1468 ; THE 'FAST MODE' ROUTINES 1469 1470 1471 mark_02A9: 1472 1473 DISPLAY_4: 1474 1475 LD A,284-CHARS_PER_LINE_WINDOW ; $FC for 6.5MHz clocked machines 1476 ; (7) load A with first R delay value 1477 1478 LD B,1 ; (7) one row only. 1479 1480 CALL DISPLAY_5 ; (17) routine DISPLAY_5 1481 1482 DEC HL ; (6) point back to the HALT. 1483 EX (SP),HL ; (19) Harmless Nonsensical Timing if paired. 1484 EX (SP),HL ; (19) Harmless Nonsensical Timing. 1485 JP (IX) ; (8) to R_IX_1 or R_IX_2 1486 1487 1488 ; THE 'DISPLAY_5' SUBROUTINE 1489 1490 ; This subroutine is called from SLOW mode and FAST mode to generate the 1491 ; central TV picture. With SLOW mode the R register is incremented, with 1492 ; each instruction, to $F7 by the time it completes. With fast mode, the 1493 ; final R value will be $FF and an interrupt will occur as soon as the 1494 ; Program Counter reaches the HALT. (24 clock cycles) 1495 1496 mark_02B5: 1497 DISPLAY_5: 1498 LD R,A ; (9) Load R from A. R = slow: $F5 fast: $FC 1499 1500 ;; Original, for 32 column display: 1501 ;; 1502 ;; LD A,$DD ; (7) load future R value. $F6 $FD 1503 ;; 1504 ;; For other display widths, 1505 ;; need to count down three instructions then the number of characters 1506 ;; 1507 LD A,256-3-CHARS_PER_LINE_WINDOW ; (7) load future R value. $F6 $FD 1508 1509 EI ; (4) Enable Interrupts $F7 $FE 1510 1511 JP (HL) ; (4) jump to the echo display. $F8 $FF 1512 1513 1514 ; THE 'KEYBOARD SCANNING' SUBROUTINE 1515 1516 ; The keyboard is read during the vertical sync interval while no video is 1517 ; being displayed. Reading a port with address bit 0 low i.e. $FE starts the 1518 ; vertical sync pulse. 1519 1520 mark_02BB: 1521 KEYBOARD: 1522 LD HL,$FFFF ; (16) prepare a buffer to take key. 1523 LD BC,$FEFE ; (20) set BC to port $FEFE. The B register, 1524 ; with its single reset bit also acts as 1525 ; an 8-counter. 1526 IN A,(C) ; (11) read the port - all 16 bits are put on 1527 ; the address bus. Start VSYNC pulse. 1528 OR $01 ; (7) set the rightmost bit so as to ignore 1529 ; the SHIFT key. 1530 1531 mark_02C5: 1532 EACH_LINE: 1533 OR $E0 ; [7] OR %11100000 1534 LD D,A ; [4] transfer to D. 1535 CPL ; [4] complement - only bits 4-0 meaningful now. 1536 CP 1 ; [7] sets carry if A is zero. 1537 SBC A,A ; [4] $FF if $00 else zero. 1538 OR B ; [7] $FF or port FE,FD,FB.... 1539 AND L ; [4] unless more than one key, L will still be 1540 ; $FF. if more than one key is pressed then A is 1541 ; now invalid. 1542 LD L,A ; [4] transfer to L. 1543 1544 ; now consider the column identifier. 1545 1546 LD A,H ; [4] will be $FF if no previous keys. 1547 AND D ; [4] 111xxxxx 1548 LD H,A ; [4] transfer A to H 1549 1550 ; since only one key may be pressed, H will, if valid, be one of 1551 ; 11111110, 11111101, 11111011, 11110111, 11101111 1552 ; reading from the outer column, say Q, to the inner column, say T. 1553 1554 RLC B ; [8] rotate the 8-counter/port address. 1555 ; sets carry if more to do. 1556 IN A,(C) ; [10] read another half-row. 1557 ; all five bits this time. 1558 1559 JR C,EACH_LINE ; [12](7) loop back, until done, to EACH_LINE 1560 1561 ; The last row read is SHIFT,Z,X,C,V for the second time. 1562 1563 RRA ; (4) test the shift key - carry will be reset 1564 ; if the key is pressed. 1565 RL H ; (8) rotate left H picking up the carry giving 1566 ; column values - 1567 ; $FD, $FB, $F7, $EF, $DF. 1568 ; or $FC, $FA, $F6, $EE, $DE if shifted. 1569 1570 ; We now have H identifying the column and L identifying the row in the 1571 ; keyboard matrix. 1572 1573 ; This is a good time to test if this is an American or British machine. 1574 ; The US machine has an extra diode that causes bit 6 of a byte read from 1575 ; a port to be reset. 1576 1577 RLA ; (4) compensate for the shift test. 1578 RLA ; (4) rotate bit 7 out. 1579 RLA ; (4) test bit 6. 1580 1581 SBC A,A ; (4) $FF or 0 {USA} 1582 AND $18 ; (7) 24 or 0 1583 ADD A,31 ; (7) 55 or 31 1584 1585 ; result is either 31 (USA) or 55 (UK) blank lines above and below the TV 1586 ; picture. 1587 1588 LD (MARGIN),A ; (13) update system variable MARGIN 1589 1590 RET ; (10) return 1591 1592 1593 ; THE 'SET FAST MODE' SUBROUTINE 1594 1595 ; 1596 ; 1597 1598 mark_02E7: 1599 SET_FAST: 1600 BIT 7,(IY+CDFLAG-RAMBASE) 1601 RET Z ; 1602 1603 HALT ; Wait for Interrupt 1604 OUT (IO_PORT_NMI_GEN_OFF),A ; 1605 RES 7,(IY+CDFLAG-RAMBASE) 1606 RET ; return. 1607 1608 1609 1610 ; THE 'REPORT_F' 1611 1612 1613 mark_02F4: 1614 REPORT_F: 1615 RST _ERROR_1 1616 DEFB $0E ; Error Report: No Program Name supplied. 1617 1618 1619 ; THE 'SAVE COMMAND' ROUTINE 1620 1621 ; 1622 ; 1623 1624 mark_02F6: 1625 SAVE: 1626 CALL NAME 1627 JR C,REPORT_F ; back with null name 1628 1629 EX DE,HL ; 1630 1631 1632 1633 1634 ; 1635 ; The next 6 bytes differ 1636 ; 1637 if NOT_BODGED 1638 ; what ZASM assembled: 1639 ; 02FC: 11CB12 1640 LD DE,$12CB ; five seconds timing value (4811 decimal) 1641 ; 02FF: CD460F 1642 mark_02FF: 1643 HEADER: 1644 CALL BREAK_1 1645 1646 else 1647 ; what the SG ROM disassembled to: 1648 ; 02FC ED;FD 1649 LDIR ; Patch tape SAVE 1650 ; 02FE C3;07;02 1651 JP SLOW_FAST ; to $0207 1652 ; 0301 0F 1653 RRCA 1654 endif 1655 1656 1657 mark_0302: 1658 JR NC,BREAK_2 1659 1660 mark_0304: 1661 DELAY_1: 1662 DJNZ DELAY_1 1663 1664 DEC DE ; 1665 LD A,D ; 1666 OR E ; 1667 JR NZ,HEADER ; back for delay to HEADER 1668 1669 mark_030B: 1670 OUT_NAME: 1671 CALL OUT_BYTE 1672 BIT 7,(HL) ; test for inverted bit. 1673 INC HL ; address next character of name. 1674 JR Z,OUT_NAME ; back if not inverted to OUT_NAME 1675 1676 ; now start saving the system variables onwards. 1677 1678 LD HL,VERSN ; set start of area to VERSN thereby 1679 ; preserving RAMTOP etc. 1680 1681 mark_0316: 1682 OUT_PROG: 1683 CALL OUT_BYTE 1684 1685 CALL LOAD_SAVE ; >> 1686 JR OUT_PROG ; loop back 1687 1688 1689 ; THE 'OUT_BYTE' SUBROUTINE 1690 1691 ; This subroutine outputs a byte a bit at a time to a domestic tape recorder. 1692 1693 mark_031E: 1694 OUT_BYTE: 1695 LD E,(HL) ; fetch byte to be saved. 1696 SCF ; set carry flag - as a marker. 1697 1698 mark_0320: 1699 EACH_BIT: 1700 RL E ; C < 76543210 < C 1701 RET Z ; return when the marker bit has passed 1702 ; right through. >> 1703 1704 SBC A,A ; $FF if set bit or $00 with no carry. 1705 AND $05 ; $05 " " " " $00 1706 ADD A,$04 ; $09 " " " " $04 1707 LD C,A ; transfer timer to C. a set bit has a longer 1708 ; pulse than a reset bit. 1709 1710 mark_0329: 1711 PULSES: 1712 OUT (IO_PORT_TAPE),A ; pulse to cassette. 1713 LD B,$23 ; set timing constant 1714 1715 mark_032D: 1716 DELAY_2: 1717 DJNZ DELAY_2 ; self-loop 1718 1719 CALL BREAK_1 ; test for BREAK key. 1720 1721 mark_0332: 1722 BREAK_2: 1723 JR NC,REPORT_D ; forward with break to REPORT_D 1724 1725 LD B,$1E ; set timing value. 1726 1727 mark_0336: 1728 DELAY_3: 1729 1730 DJNZ DELAY_3 ; self-loop 1731 1732 DEC C ; decrement counter 1733 JR NZ,PULSES ; loop back 1734 1735 mark_033B: 1736 DELAY_4: 1737 AND A ; clear carry for next bit test. 1738 DJNZ DELAY_4 ; self loop (B is zero - 256) 1739 1740 JR EACH_BIT ; loop back 1741 1742 1743 ; THE 'LOAD COMMAND' ROUTINE 1744 1745 1746 mark_0340: 1747 LOAD: 1748 CALL NAME 1749 1750 ; DE points to start of name in RAM. 1751 1752 RL D ; pick up carry 1753 RRC D ; carry now in bit 7. 1754 1755 mark_0347: 1756 1757 1758 1759 if NOT_BODGED 1760 1761 LNEXT_PROG: 1762 CALL IN_BYTE 1763 JR LNEXT_PROG ; loop 1764 1765 1766 ; THE 'IN_BYTE' SUBROUTINE 1767 1768 1769 mark_034C: 1770 IN_BYTE: 1771 LD C,$01 ; prepare an eight counter 00000001. 1772 1773 mark_034E: 1774 NEXT_BIT: 1775 LD B,$00 ; set counter to 256 1776 1777 else 1778 ; what the SG ROM has: 1779 ;0347 EB 1780 EX DE,HL ; NEXT-PROG 1781 ;0348 ED;FC 1782 LDIR ; Patch tape LOAD 1783 ;034A C3;07;02 1784 JP SLOW_FAST 1785 ;034D 01;06;00 1786 LD BC,6 1787 endif 1788 1789 1790 1791 1792 mark_0350: 1793 BREAK_3: 1794 LD A,$7F ; read the keyboard row 1795 IN A,(IO_PORT_KEYBOARD_RD) ; with the SPACE key. 1796 1797 OUT (IO_PORT_SCREEN),A ; output signal to screen. 1798 1799 RRA ; test for SPACE pressed. 1800 JR NC,BREAK_4 ; forward if so to BREAK_4 1801 1802 RLA ; reverse above rotation 1803 RLA ; test tape bit. 1804 JR C,GET_BIT ; forward if set to GET_BIT 1805 1806 DJNZ BREAK_3 ; loop back 1807 1808 POP AF ; drop the return address. 1809 CP D ; ugh. 1810 1811 mark_0361: 1812 RESTART: 1813 JP NC,INITIAL ; jump forward to INITIAL if D is zero 1814 ; to reset the system 1815 ; if the tape signal has timed out for example 1816 ; if the tape is stopped. Not just a simple 1817 ; report as some system variables will have 1818 ; been overwritten. 1819 1820 LD H,D ; else transfer the start of name 1821 LD L,E ; to the HL register 1822 1823 mark_0366: 1824 IN_NAME: 1825 CALL IN_BYTE ; is sort of recursion for name 1826 ; part. received byte in C. 1827 BIT 7,D ; is name the null string ? 1828 LD A,C ; transfer byte to A. 1829 JR NZ,MATCHING ; forward with null string 1830 1831 CP (HL) ; else compare with string in memory. 1832 JR NZ,LNEXT_PROG ; back with mis-match 1833 ; (seemingly out of subroutine but return 1834 ; address has been dropped). 1835 1836 1837 mark_0371: 1838 MATCHING: 1839 INC HL ; address next character of name 1840 RLA ; test for inverted bit. 1841 JR NC,IN_NAME ; back if not 1842 1843 ; the name has been matched in full. 1844 ; proceed to load the data but first increment the high byte of E_LINE, which 1845 ; is one of the system variables to be loaded in. Since the low byte is loaded 1846 ; before the high byte, it is possible that, at the in-between stage, a false 1847 ; value could cause the load to end prematurely - see LOAD_SAVE check. 1848 1849 INC (IY+E_LINE_hi-RAMBASE) ; increment E_LINE_hi. 1850 LD HL,VERSN ; start loading at VERSN. 1851 1852 mark_037B: 1853 IN_PROG: 1854 LD D,B ; set D to zero as indicator. 1855 CALL IN_BYTE ; loads a byte 1856 LD (HL),C ; insert assembled byte in memory. 1857 CALL LOAD_SAVE ; >> 1858 JR IN_PROG ; loop back 1859 1860 ; ___ 1861 1862 ; this branch assembles a full byte before exiting normally 1863 ; from the IN_BYTE subroutine. 1864 1865 mark_0385: 1866 GET_BIT: 1867 PUSH DE ; save the 1868 LD E,$94 ; timing value. 1869 1870 mark_0388: 1871 TRAILER: 1872 LD B,26 ; counter to twenty six. 1873 1874 mark_038A: 1875 COUNTER: 1876 DEC E ; decrement the measuring timer. 1877 IN A,(IO_PORT_KEYBOARD_RD) ; read the tape input 1878 RLA ; 1879 BIT 7,E ; 1880 LD A,E ; 1881 JR C,TRAILER ; loop back with carry to TRAILER 1882 1883 DJNZ COUNTER 1884 1885 POP DE ; 1886 JR NZ,BIT_DONE 1887 1888 CP $56 ; 1889 JR NC,NEXT_BIT 1890 1891 mark_039C: 1892 BIT_DONE: 1893 CCF ; complement carry flag 1894 RL C ; 1895 JR NC,NEXT_BIT 1896 1897 RET ; return with full byte. 1898 1899 ; ___ 1900 1901 ; if break is pressed while loading data then perform a reset. 1902 ; if break pressed while waiting for program on tape then OK to break. 1903 1904 mark_03A2: 1905 BREAK_4: 1906 LD A,D ; transfer indicator to A. 1907 AND A ; test for zero. 1908 JR Z,RESTART ; back if so 1909 1910 1911 mark_03A6: 1912 REPORT_D: 1913 RST _ERROR_1 1914 DEFB $0C ; Error Report: BREAK - CONT repeats 1915 1916 1917 ; THE 'PROGRAM NAME' SUBROUTINE 1918 1919 1920 mark_03A8: 1921 NAME: 1922 CALL SCANNING 1923 LD A,(FLAGS) ; sv 1924 ADD A,A ; 1925 JP M,REPORT_C 1926 1927 POP HL ; 1928 RET NC ; 1929 1930 PUSH HL ; 1931 CALL SET_FAST 1932 CALL STK_FETCH 1933 LD H,D ; 1934 LD L,E ; 1935 DEC C ; 1936 RET M ; 1937 1938 ADD HL,BC ; 1939 SET 7,(HL) ; 1940 RET ; 1941 1942 1943 ; THE 'NEW' COMMAND ROUTINE 1944 1945 1946 mark_03C3: 1947 NEW: 1948 CALL SET_FAST 1949 LD BC,(RAMTOP) ; fetch value of system variable RAMTOP 1950 DEC BC ; point to last system byte. 1951 1952 1953 ; THE 'RAM CHECK' ROUTINE 1954 1955 1956 mark_03CB: 1957 RAM_CHECK: 1958 LD H,B ; 1959 LD L,C ; 1960 LD A,$3F ; 1961 1962 mark_03CF: 1963 RAM_FILL: 1964 LD (HL),$02 ; 1965 DEC HL ; 1966 CP H ; 1967 JR NZ,RAM_FILL 1968 1969 mark_03D5: 1970 RAM_READ: 1971 AND A ; 1972 SBC HL,BC ; 1973 ADD HL,BC ; 1974 INC HL ; 1975 JR NC,SET_TOP 1976 1977 DEC (HL) ; 1978 JR Z,SET_TOP 1979 1980 DEC (HL) ; 1981 JR Z,RAM_READ 1982 1983 mark_03E2: 1984 SET_TOP: 1985 LD (RAMTOP),HL ; set system variable RAMTOP to first byte 1986 ; above the BASIC system area. 1987 1988 1989 ; THE 'INITIALIZATION' ROUTINE 1990 1991 1992 mark_03E5: 1993 INITIAL: 1994 LD HL,(RAMTOP) ; fetch system variable RAMTOP. 1995 DEC HL ; point to last system byte. 1996 LD (HL),$3E ; make GO SUB end-marker $3E - too high for 1997 ; high order byte of line number. 1998 ; (was $3F on ZX80) 1999 DEC HL ; point to unimportant low-order byte. 2000 LD SP,HL ; and initialize the stack-pointer to this 2001 ; location. 2002 DEC HL ; point to first location on the machine stack 2003 DEC HL ; which will be filled by next CALL/PUSH. 2004 LD (ERR_SP),HL ; set the error stack pointer ERR_SP to 2005 ; the base of the now empty machine stack. 2006 2007 ; Now set the I register so that the video hardware knows where to find the 2008 ; character set. This ROM only uses the character set when printing to 2009 ; the ZX Printer. The TV picture is formed by the external video hardware. 2010 ; Consider also, that this 8K ROM can be retro-fitted to the ZX80 instead of 2011 ; its original 4K ROM so the video hardware could be on the ZX80. 2012 2013 LD A,$1E ; address for this ROM is $1E00. 2014 LD I,A ; set I register from A. 2015 IM 1 ; select Z80 Interrupt Mode 1. 2016 2017 LD IY,ERR_NR ; set IY to the start of RAM so that the 2018 ; system variables can be indexed. 2019 2020 LD (IY+CDFLAG-RAMBASE),%01000000 2021 ; Bit 6 indicates Compute and Display required. 2022 2023 LD HL,USER_RAM ; The first location after System Variables - 2024 ; 16509 decimal. 2025 LD (D_FILE),HL ; set system variable D_FILE to this value. 2026 LD B,$19 ; prepare minimal screen of 24 NEWLINEs 2027 ; following an initial NEWLINE. 2028 2029 mark_0408: 2030 LINE: 2031 LD (HL),ZX_NEWLINE ; insert NEWLINE (HALT instruction) 2032 INC HL ; point to next location. 2033 DJNZ LINE ; loop back for all twenty five to LINE 2034 2035 LD (VARS),HL ; set system variable VARS to next location 2036 2037 CALL CLEAR ; sets $80 end-marker and the 2038 ; dynamic memory pointers E_LINE, STKBOT and 2039 ; STKEND. 2040 2041 mark_0413: 2042 N_L_ONLY: 2043 CALL CURSOR_IN ; inserts the cursor and 2044 ; end-marker in the Edit Line also setting 2045 ; size of lower display to two lines. 2046 2047 CALL SLOW_FAST ; selects COMPUTE and DISPLAY 2048 2049 2050 ; THE 'BASIC LISTING' SECTION 2051 2052 2053 mark_0419: 2054 UPPER: 2055 CALL CLS 2056 LD HL,(E_PPC) ; sv 2057 LD DE,(S_TOP) ; sv 2058 AND A ; 2059 SBC HL,DE ; 2060 EX DE,HL ; 2061 JR NC,ADDR_TOP 2062 2063 ADD HL,DE ; 2064 LD (S_TOP),HL ; sv 2065 2066 mark_042D: 2067 ADDR_TOP: 2068 CALL LINE_ADDR 2069 JR Z,LIST_TOP 2070 2071 EX DE,HL ; 2072 2073 mark_0433: 2074 LIST_TOP: 2075 CALL LIST_PROG 2076 DEC (IY+BERG-RAMBASE) 2077 JR NZ,LOWER 2078 2079 LD HL,(E_PPC) ; sv 2080 CALL LINE_ADDR 2081 LD HL,(CH_ADD) ; sv 2082 SCF ; Set Carry Flag 2083 SBC HL,DE ; 2084 LD HL,S_TOP ; sv 2085 JR NC,INC_LINE 2086 2087 EX DE,HL ; 2088 LD A,(HL) ; 2089 INC HL ; 2090 LDI ; 2091 LD (DE),A ; 2092 JR UPPER 2093 ; ___ 2094 2095 mark_0454: 2096 DOWN_KEY: 2097 2098 LD HL,E_PPC ; sv 2099 2100 mark_0457: 2101 INC_LINE: 2102 LD E,(HL) ; 2103 INC HL ; 2104 LD D,(HL) ; 2105 PUSH HL ; 2106 EX DE,HL ; 2107 INC HL ; 2108 CALL LINE_ADDR 2109 CALL LINE_NUM 2110 POP HL ; 2111 2112 mark_0464: 2113 KEY_INPUT: 2114 BIT 5,(IY+FLAGX-RAMBASE) 2115 JR NZ,LOWER ; forward 2116 2117 LD (HL),D ; 2118 DEC HL ; 2119 LD (HL),E ; 2120 JR UPPER 2121 2122 2123 ; THE 'EDIT LINE COPY' SECTION 2124 2125 ; This routine sets the edit line to just the cursor when 2126 ; 1) There is not enough memory to edit a BASIC line. 2127 ; 2) The edit key is used during input. 2128 ; The entry point LOWER 2129 2130 2131 mark_046F: 2132 EDIT_INP: 2133 CALL CURSOR_IN ; sets cursor only edit line. 2134 2135 ; -> 2136 2137 mark_0472: 2138 LOWER: 2139 LD HL,(E_LINE) ; fetch edit line start from E_LINE. 2140 2141 mark_0475: 2142 EACH_CHAR: 2143 LD A,(HL) ; fetch a character from edit line. 2144 CP $7E ; compare to the number marker. 2145 JR NZ,END_LINE ; forward if not 2146 2147 LD BC,6 ; else six invisible bytes to be removed. 2148 CALL RECLAIM_2 2149 JR EACH_CHAR ; back 2150 ; ___ 2151 2152 mark_0482: 2153 END_LINE: 2154 CP ZX_NEWLINE ; 2155 INC HL ; 2156 JR NZ,EACH_CHAR 2157 2158 mark_0487: 2159 EDIT_LINE: 2160 CALL CURSOR ; sets cursor K or L. 2161 2162 mark_048A: 2163 EDIT_ROOM: 2164 CALL LINE_ENDS 2165 LD HL,(E_LINE) ; sv 2166 LD (IY+ERR_NR-RAMBASE),$FF 2167 CALL COPY_LINE 2168 BIT 7,(IY+ERR_NR-RAMBASE) 2169 JR NZ,DISPLAY_6 2170 2171 LD A,(DF_SZ) ; 2172 CP CHARS_VERTICAL ; $18 = 24 2173 JR NC,DISPLAY_6 2174 2175 INC A ; 2176 LD (DF_SZ),A ; 2177 LD B,A ; 2178 LD C,1 ; 2179 CALL LOC_ADDR 2180 LD D,H ; 2181 LD E,L ; 2182 LD A,(HL) ; 2183 2184 mark_04B1: 2185 FREE_LINE: 2186 DEC HL ; 2187 CP (HL) ; 2188 JR NZ,FREE_LINE 2189 2190 INC HL ; 2191 EX DE,HL ; 2192 LD A,(RAMTOP+1) ; sv RAMTOP_hi 2193 CP $4D ; 2194 CALL C,RECLAIM_1 2195 JR EDIT_ROOM 2196 2197 2198 ; THE 'WAIT FOR KEY' SECTION 2199 2200 2201 mark_04C1: 2202 DISPLAY_6: 2203 LD HL,$0000 ; 2204 LD (X_PTR),HL ; sv 2205 2206 LD HL,CDFLAG ; system variable CDFLAG 2207 2208 2209 2210 2211 if NOT_BODGED 2212 BIT 7,(HL) ; 2213 2214 CALL Z,DISPLAY_1 2215 2216 mark_04CF: 2217 SLOW_DISP: 2218 BIT 0,(HL) ; 2219 JR Z,SLOW_DISP 2220 2221 else 2222 ; 04CA D3;00 2223 OUT ($00),A ; PORT 0 2224 ; 04CC CB;46 2225 L04CC: 2226 BIT 0,(HL) 2227 ; 04CE 28;FC 2228 JR Z,L04CC 2229 ; 04D0 D3;01 2230 OUT ($01),A ; PORT 1 2231 ; 04D2 00 2232 NOP 2233 2234 2235 endif 2236 2237 2238 2239 2240 LD BC,(LAST_K) ; sv 2241 CALL DEBOUNCE 2242 CALL DECODE 2243 2244 JR NC,LOWER ; back 2245 2246 2247 ; THE 'KEYBOARD DECODING' SECTION 2248 2249 ; The decoded key value is in E and HL points to the position in the 2250 ; key table. D contains zero. 2251 2252 mark_04DF: 2253 K_DECODE: 2254 LD A,(MODE) ; Fetch value of system variable MODE 2255 DEC A ; test the three values together 2256 2257 JP M,FETCH_2 ; forward, if was zero 2258 2259 JR NZ,FETCH_1 ; forward, if was 2 2260 2261 ; The original value was one and is now zero. 2262 2263 LD (MODE),A ; update the system variable MODE 2264 2265 DEC E ; reduce E to range $00 - $7F 2266 LD A,E ; place in A 2267 SUB 39 ; subtract 39 setting carry if range 00 - 38 2268 JR C,FUNC_BASE ; forward, if so 2269 2270 LD E,A ; else set E to reduced value 2271 2272 mark_04F2: 2273 FUNC_BASE: 2274 LD HL,K_FUNCT ; address of K_FUNCT table for function keys. 2275 JR TABLE_ADD ; forward 2276 ; ___ 2277 mark_04F7: 2278 FETCH_1: 2279 LD A,(HL) ; 2280 CP ZX_NEWLINE ; 2281 JR Z,K_L_KEY 2282 2283 CP ZX_RND ; $40 2284 SET 7,A ; 2285 JR C,ENTER 2286 2287 LD HL,$00C7 ; (expr reqd) 2288 2289 mark_0505: 2290 TABLE_ADD: 2291 ADD HL,DE ; 2292 JR FETCH_3 2293 2294 ; ___ 2295 2296 mark_0508: 2297 FETCH_2: 2298 LD A,(HL) ; 2299 BIT 2,(IY+FLAGS-RAMBASE) ; K or L mode ? 2300 JR NZ,TEST_CURS 2301 2302 ADD A,$C0 ; 2303 CP $E6 ; 2304 JR NC,TEST_CURS 2305 2306 mark_0515: 2307 FETCH_3: 2308 LD A,(HL) ; 2309 2310 mark_0516: 2311 TEST_CURS: 2312 CP $F0 ; 2313 JP PE,KEY_SORT 2314 2315 mark_051B: 2316 ENTER: 2317 LD E,A ; 2318 CALL CURSOR 2319 2320 LD A,E ; 2321 CALL ADD_CHAR 2322 2323 mark_0523: 2324 BACK_NEXT: 2325 JP LOWER ; back 2326 2327 2328 ; THE 'ADD CHARACTER' SUBROUTINE 2329 2330 mark_0526: 2331 ADD_CHAR: 2332 CALL ONE_SPACE 2333 LD (DE),A ; 2334 RET ; 2335 2336 2337 ; THE 'CURSOR KEYS' ROUTINE 2338 2339 mark_052B: 2340 K_L_KEY: 2341 LD A,ZX_KL ; 2342 2343 mark_052D: 2344 KEY_SORT: 2345 LD E,A ; 2346 LD HL,$0482 ; base address of ED_KEYS (exp reqd) 2347 ADD HL,DE ; 2348 ADD HL,DE ; 2349 LD C,(HL) ; 2350 INC HL ; 2351 LD B,(HL) ; 2352 PUSH BC ; 2353 2354 mark_0537: 2355 CURSOR: 2356 LD HL,(E_LINE) ; sv 2357 BIT 5,(IY+FLAGX-RAMBASE) 2358 JR NZ,L_MODE 2359 2360 mark_0540: 2361 K_MODE: 2362 RES 2,(IY+FLAGS-RAMBASE) ; Signal use K mode 2363 2364 mark_0544: 2365 TEST_CHAR: 2366 LD A,(HL) ; 2367 CP ZX_CURSOR ; 2368 RET Z ; return 2369 2370 INC HL ; 2371 CALL NUMBER 2372 JR Z,TEST_CHAR 2373 2374 CP ZX_A ; $26 2375 JR C,TEST_CHAR 2376 2377 CP $DE ; ZX_THEN ?? 2378 JR Z,K_MODE 2379 2380 mark_0556: 2381 L_MODE: 2382 SET 2,(IY+FLAGS-RAMBASE) ; Signal use L mode 2383 JR TEST_CHAR 2384 2385 2386 ; THE 'CLEAR_ONE' SUBROUTINE 2387 2388 mark_055C: 2389 CLEAR_ONE: 2390 LD BC,$0001 ; 2391 JP RECLAIM_2 2392 2393 2394 ; THE 'EDITING KEYS' TABLE 2395 2396 mark_0562: 2397 ED_KEYS: 2398 DEFW UP_KEY 2399 DEFW DOWN_KEY 2400 DEFW LEFT_KEY 2401 DEFW RIGHT_KEY 2402 DEFW FUNCTION 2403 DEFW EDIT_KEY 2404 DEFW N_L_KEY 2405 DEFW RUBOUT 2406 DEFW FUNCTION 2407 DEFW FUNCTION 2408 2409 2410 2411 ; THE 'CURSOR LEFT' ROUTINE 2412 2413 ; 2414 ; 2415 2416 mark_LEFT_KEY: 2417 LEFT_KEY: 2418 CALL LEFT_EDGE 2419 LD A,(HL) ; 2420 LD (HL),ZX_CURSOR ; 2421 INC HL ; 2422 JR GET_CODE 2423 2424 2425 ; THE 'CURSOR RIGHT' ROUTINE 2426 2427 2428 mark_RIGHT_KEY: 2429 RIGHT_KEY: 2430 INC HL ; 2431 LD A,(HL) ; 2432 CP ZX_NEWLINE ; 2433 JR Z,ENDED_2 2434 2435 LD (HL),ZX_CURSOR ; 2436 DEC HL ; 2437 2438 mark_0588: 2439 GET_CODE: 2440 LD (HL),A ; 2441 2442 mark_0589: 2443 ENDED_1: 2444 JR BACK_NEXT 2445 2446 2447 ; THE 'RUBOUT' ROUTINE 2448 2449 2450 mark_058B: 2451 RUBOUT: 2452 CALL LEFT_EDGE 2453 CALL CLEAR_ONE 2454 JR ENDED_1 2455 2456 2457 ; THE 'ED_EDGE' SUBROUTINE 2458 2459 ; 2460 ; 2461 2462 mark_0593: 2463 LEFT_EDGE: 2464 DEC HL ; 2465 LD DE,(E_LINE) ; sv 2466 LD A,(DE) ; 2467 CP ZX_CURSOR ; 2468 RET NZ ; 2469 2470 POP DE ; 2471 2472 mark_059D: 2473 ENDED_2: 2474 JR ENDED_1 2475 2476 2477 ; THE 'CURSOR UP' ROUTINE 2478 2479 ; 2480 ; 2481 2482 mark_059F: 2483 UP_KEY: 2484 LD HL,(E_PPC) ; sv 2485 CALL LINE_ADDR 2486 EX DE,HL ; 2487 CALL LINE_NUM 2488 LD HL,E_PPC+1 ; point to system variable E_PPC_hi 2489 JP KEY_INPUT ; jump back 2490 2491 2492 ; THE 'FUNCTION KEY' ROUTINE 2493 2494 ; 2495 ; 2496 2497 mark_FUNCTION: 2498 FUNCTION: 2499 LD A,E ; 2500 AND $07 ; 2501 LD (MODE),A ; sv 2502 JR ENDED_2 ; back 2503 2504 2505 ; THE 'COLLECT LINE NUMBER' SUBROUTINE 2506 2507 mark_05B7: 2508 ZERO_DE: 2509 EX DE,HL ; 2510 LD DE,DISPLAY_6 + 1 ; $04C2 - a location addressing two zeros. 2511 2512 ; -> 2513 2514 mark_05BB: 2515 LINE_NUM: 2516 LD A,(HL) ; 2517 AND $C0 ; 2518 JR NZ,ZERO_DE 2519 2520 LD D,(HL) ; 2521 INC HL ; 2522 LD E,(HL) ; 2523 RET ; 2524 2525 2526 ; THE 'EDIT KEY' ROUTINE 2527 2528 2529 mark_EDIT_KEY: 2530 EDIT_KEY: 2531 CALL LINE_ENDS ; clears lower display. 2532 2533 LD HL,EDIT_INP ; Address: EDIT_INP 2534 PUSH HL ; ** is pushed as an error looping address. 2535 2536 BIT 5,(IY+FLAGX-RAMBASE) ; test FLAGX 2537 RET NZ ; indirect jump if in input mode 2538 ; to EDIT_INP (begin again). 2539 2540 ; 2541 2542 LD HL,(E_LINE) ; fetch E_LINE 2543 LD (DF_CC),HL ; and use to update the screen cursor DF_CC 2544 2545 ; so now RST $10 will print the line numbers to the edit line instead of screen. 2546 ; first make sure that no newline/out of screen can occur while sprinting the 2547 ; line numbers to the edit line. 2548 2549 ; prepare line 0, column 0. 2550 2551 LD HL,256*CHARS_VERTICAL + CHARS_HORIZONTAL + 1 2552 ; 2553 LD (S_POSN),HL ; update S_POSN with these dummy values. 2554 2555 LD HL,(E_PPC) ; fetch current line from E_PPC may be a 2556 ; non-existent line e.g. last line deleted. 2557 CALL LINE_ADDR ; gets address or that of 2558 ; the following line. 2559 CALL LINE_NUM ; gets line number if any in DE 2560 ; leaving HL pointing at second low byte. 2561 2562 LD A,D ; test the line number for zero. 2563 OR E ; 2564 RET Z ; return if no line number - no program to edit. 2565 2566 DEC HL ; point to high byte. 2567 CALL OUT_NO ; writes number to edit line. 2568 2569 INC HL ; point to length bytes. 2570 LD C,(HL) ; low byte to C. 2571 INC HL ; 2572 LD B,(HL) ; high byte to B. 2573 2574 INC HL ; point to first character in line. 2575 LD DE,(DF_CC) ; fetch display file cursor DF_CC 2576 2577 LD A,ZX_CURSOR ; prepare the cursor character. 2578 LD (DE),A ; and insert in edit line. 2579 INC DE ; increment intended destination. 2580 2581 PUSH HL ; * save start of BASIC. 2582 2583 LD HL,29 ; set an overhead of 29 bytes. 2584 ADD HL,DE ; add in the address of cursor. 2585 ADD HL,BC ; add the length of the line. 2586 SBC HL,SP ; subtract the stack pointer. 2587 2588 POP HL ; * restore pointer to start of BASIC. 2589 2590 RET NC ; return if not enough room to EDIT_INP EDIT_INP. 2591 ; the edit key appears not to work. 2592 2593 LDIR ; else copy bytes from program to edit line. 2594 ; Note. hidden floating point forms are also 2595 ; copied to edit line. 2596 2597 EX DE,HL ; transfer free location pointer to HL 2598 2599 POP DE ; ** remove address EDIT_INP from stack. 2600 2601 CALL SET_STK_B ; sets STKEND from HL. 2602 2603 JR ENDED_2 ; back to ENDED_2 and after 3 more jumps 2604 ; to LOWER, LOWER. 2605 ; Note. The LOWER routine removes the hidden 2606 ; floating-point numbers from the edit line. 2607 2608 2609 ; THE 'NEWLINE KEY' ROUTINE 2610 2611 2612 mark_060C: 2613 N_L_KEY: 2614 CALL LINE_ENDS 2615 2616 LD HL,LOWER ; prepare address: LOWER 2617 2618 BIT 5,(IY+FLAGX-RAMBASE) 2619 JR NZ,NOW_SCAN 2620 2621 LD HL,(E_LINE) ; sv 2622 LD A,(HL) ; 2623 CP $FF ; 2624 JR Z,STK_UPPER 2625 2626 CALL CLEAR_PRB 2627 CALL CLS 2628 2629 mark_0626: 2630 STK_UPPER: 2631 LD HL,UPPER ; Address: UPPER 2632 2633 mark_0629: 2634 NOW_SCAN: 2635 PUSH HL ; push routine address (LOWER or UPPER). 2636 CALL LINE_SCAN 2637 POP HL ; 2638 CALL CURSOR 2639 CALL CLEAR_ONE 2640 CALL E_LINE_NUM 2641 JR NZ,N_L_INP 2642 2643 LD A,B ; 2644 OR C ; 2645 JP NZ,N_L_LINE 2646 2647 DEC BC ; 2648 DEC BC ; 2649 LD (PPC),BC ; sv 2650 LD (IY+DF_SZ-RAMBASE),2 2651 LD DE,(D_FILE) ; sv 2652 2653 JR TEST_NULL ; forward 2654 2655 ; ___ 2656 2657 mark_064E: 2658 N_L_INP: 2659 CP ZX_NEWLINE ; 2660 JR Z,N_L_NULL 2661 2662 LD BC,(T_ADDR) ; 2663 CALL LOC_ADDR 2664 LD DE,(NXTLIN) ; 2665 LD (IY+DF_SZ-RAMBASE),2 2666 2667 mark_0661: 2668 TEST_NULL: 2669 RST _GET_CHAR 2670 CP ZX_NEWLINE ; 2671 2672 mark_0664: 2673 N_L_NULL: 2674 JP Z,N_L_ONLY 2675 2676 LD (IY+FLAGS-RAMBASE),$80 2677 EX DE,HL ; 2678 2679 mark_066C: 2680 NEXT_LINE: 2681 LD (NXTLIN),HL ; 2682 EX DE,HL ; 2683 CALL TEMP_PTR2 2684 CALL LINE_RUN 2685 RES 1,(IY+FLAGS-RAMBASE) ; Signal printer not in use 2686 LD A,$C0 ; 2687 ;; LD (IY+X_PTR_lo-RAMBASE),A ;; ERROR IN htm SOURCE! IY+$19 is X_PTR_hi 2688 LD (IY+X_PTR_hi-RAMBASE),A 2689 CALL X_TEMP 2690 RES 5,(IY+FLAGX-RAMBASE) 2691 BIT 7,(IY+ERR_NR-RAMBASE) 2692 JR Z,STOP_LINE 2693 2694 LD HL,(NXTLIN) ; 2695 AND (HL) ; 2696 JR NZ,STOP_LINE 2697 2698 LD D,(HL) ; 2699 INC HL ; 2700 LD E,(HL) ; 2701 LD (PPC),DE ; 2702 INC HL ; 2703 LD E,(HL) ; 2704 INC HL ; 2705 LD D,(HL) ; 2706 INC HL ; 2707 EX DE,HL ; 2708 ADD HL,DE ; 2709 CALL BREAK_1 2710 JR C,NEXT_LINE 2711 2712 LD HL,ERR_NR 2713 BIT 7,(HL) 2714 JR Z,STOP_LINE 2715 2716 LD (HL),$0C 2717 2718 mark_06AE: 2719 STOP_LINE: 2720 BIT 7,(IY+PR_CC-RAMBASE) 2721 CALL Z,COPY_BUFF 2722 ; 2723 if 0 2724 LD BC,$0121 ; 2725 else 2726 LD BC,256*1 + CHARS_HORIZONTAL + 1 2727 endif 2728 ; 2729 ; 2730 CALL LOC_ADDR 2731 LD A,(ERR_NR) 2732 LD BC,(PPC) 2733 INC A 2734 JR Z,REPORT 2735 2736 CP $09 2737 JR NZ,CONTINUE 2738 2739 INC BC 2740 2741 mark_06CA: 2742 CONTINUE: 2743 LD (OLDPPC),BC ; 2744 JR NZ,REPORT 2745 2746 DEC BC ; 2747 2748 mark_06D1: 2749 REPORT: 2750 CALL OUT_CODE 2751 LD A,ZX_SLASH 2752 2753 RST _PRINT_A 2754 CALL OUT_NUM 2755 CALL CURSOR_IN 2756 JP DISPLAY_6 2757 2758 ; ___ 2759 2760 mark_06E0: 2761 N_L_LINE: 2762 LD (E_PPC),BC ; 2763 LD HL,(CH_ADD) ; 2764 EX DE,HL ; 2765 LD HL,N_L_ONLY 2766 PUSH HL ; 2767 LD HL,(STKBOT) ; 2768 SBC HL,DE ; 2769 PUSH HL ; 2770 PUSH BC ; 2771 CALL SET_FAST 2772 CALL CLS 2773 POP HL ; 2774 CALL LINE_ADDR 2775 JR NZ,COPY_OVER 2776 2777 CALL NEXT_ONE 2778 CALL RECLAIM_2 2779 2780 mark_0705: 2781 COPY_OVER: 2782 POP BC ; 2783 LD A,C ; 2784 DEC A ; 2785 OR B ; 2786 RET Z ; 2787 2788 PUSH BC ; 2789 INC BC ; 2790 INC BC ; 2791 INC BC ; 2792 INC BC ; 2793 DEC HL ; 2794 CALL MAKE_ROOM 2795 CALL SLOW_FAST 2796 POP BC ; 2797 PUSH BC ; 2798 INC DE ; 2799 LD HL,(STKBOT) ; 2800 DEC HL ; 2801 LDDR ; copy bytes 2802 LD HL,(E_PPC) ; 2803 EX DE,HL ; 2804 POP BC ; 2805 LD (HL),B ; 2806 DEC HL ; 2807 LD (HL),C ; 2808 DEC HL ; 2809 LD (HL),E ; 2810 DEC HL ; 2811 LD (HL),D ; 2812 2813 RET ; return. 2814 2815 2816 ; THE 'LIST' AND 'LLIST' COMMAND ROUTINES 2817 2818 2819 mark_072C: 2820 LLIST: 2821 SET 1,(IY+FLAGS-RAMBASE) ; signal printer in use 2822 2823 mark_0730: 2824 LIST: 2825 CALL FIND_INT 2826 2827 LD A,B ; fetch high byte of user-supplied line number. 2828 AND $3F ; and crudely limit to range 1-16383. 2829 2830 LD H,A ; 2831 LD L,C ; 2832 LD (E_PPC),HL ; 2833 CALL LINE_ADDR 2834 2835 mark_073E: 2836 LIST_PROG: 2837 LD E,$00 ; 2838 2839 mark_0740: 2840 UNTIL_END: 2841 CALL OUT_LINE ; lists one line of BASIC 2842 ; making an early return when the screen is 2843 ; full or the end of program is reached. 2844 JR UNTIL_END ; loop back to UNTIL_END 2845 2846 2847 ; THE 'PRINT A BASIC LINE' SUBROUTINE 2848 2849 2850 mark_0745: 2851 OUT_LINE: 2852 LD BC,(E_PPC) ; sv 2853 CALL CP_LINES 2854 LD D,$92 ; 2855 JR Z,TEST_END 2856 2857 LD DE,$0000 ; 2858 RL E ; 2859 2860 mark_0755: 2861 TEST_END: 2862 LD (IY+BERG-RAMBASE),E 2863 LD A,(HL) ; 2864 CP $40 ; 2865 POP BC ; 2866 RET NC ; 2867 2868 PUSH BC ; 2869 CALL OUT_NO 2870 INC HL ; 2871 LD A,D ; 2872 2873 RST _PRINT_A 2874 INC HL ; 2875 INC HL ; 2876 2877 mark_0766: 2878 COPY_LINE: 2879 LD (CH_ADD),HL ; 2880 SET 0,(IY+FLAGS-RAMBASE) ; Suppress leading space 2881 2882 mark_076D: 2883 MORE_LINE: 2884 LD BC,(X_PTR) ; 2885 LD HL,(CH_ADD) ; 2886 AND A ; 2887 SBC HL,BC ; 2888 JR NZ,TEST_NUM 2889 2890 LD A,ZX_INV_S ; $B8 ; 2891 2892 RST _PRINT_A 2893 2894 mark_077C: 2895 TEST_NUM: 2896 LD HL,(CH_ADD) ; 2897 LD A,(HL) ; 2898 INC HL ; 2899 CALL NUMBER 2900 LD (CH_ADD),HL ; 2901 JR Z,MORE_LINE 2902 2903 CP ZX_CURSOR ; 2904 JR Z,OUT_CURS 2905 2906 CP ZX_NEWLINE ; 2907 JR Z,OUT_CH 2908 2909 BIT 6,A ; 2910 JR Z,NOT_TOKEN 2911 2912 CALL TOKENS 2913 JR MORE_LINE 2914 ; ___ 2915 2916 mark_079A: 2917 NOT_TOKEN: 2918 RST _PRINT_A 2919 JR MORE_LINE 2920 ; ___ 2921 2922 mark_079D: 2923 OUT_CURS: 2924 LD A,(MODE) ; Fetch value of system variable MODE 2925 LD B,$AB ; Prepare an inverse [F] for function cursor. 2926 2927 AND A ; Test for zero - 2928 JR NZ,FLAGS_2 ; forward if not to FLAGS_2 2929 2930 LD A,(FLAGS) ; Fetch system variable FLAGS. 2931 LD B,ZX_INV_K ; Prepare an inverse [K] for keyword cursor. 2932 2933 mark_07AA: 2934 FLAGS_2: 2935 RRA ; 00000?00 -> 000000?0 2936 RRA ; 000000?0 -> 0000000? 2937 AND $01 ; 0000000? 0000000x 2938 2939 ADD A,B ; Possibly [F] -> [G] or [K] -> [L] 2940 2941 CALL PRINT_SP 2942 JR MORE_LINE 2943 2944 2945 ; THE 'NUMBER' SUBROUTINE 2946 2947 2948 mark_07B4: 2949 NUMBER: 2950 CP $7E ; 2951 RET NZ ; 2952 2953 INC HL ; 2954 INC HL ; 2955 INC HL ; 2956 INC HL ; 2957 INC HL ; 2958 RET ; 2959 2960 2961 ; THE 'KEYBOARD DECODE' SUBROUTINE 2962 2963 2964 mark_07BD: 2965 DECODE: 2966 LD D,0 ; 2967 SRA B ; shift bit from B to Carry 2968 SBC A,A ; A = 0 - Carry 2969 OR $26 ; %00100110 2970 LD L,5 ; 2971 SUB L ; 2972 2973 mark_07C7: 2974 KEY_LINE: 2975 ADD A,L ; 2976 SCF ; Set Carry Flag 2977 RR C ; 2978 JR C,KEY_LINE 2979 2980 INC C ; 2981 RET NZ ; 2982 2983 LD C,B ; 2984 DEC L ; 2985 LD L,1 ; 2986 JR NZ,KEY_LINE 2987 2988 LD HL,$007D ; (expr reqd) 2989 LD E,A ; 2990 ADD HL,DE ; 2991 SCF ; Set Carry Flag 2992 RET ; 2993 2994 2995 ; THE 'PRINTING' SUBROUTINE 2996 2997 2998 mark_07DC: 2999 LEAD_SP: 3000 LD A,E ; 3001 AND A ; 3002 RET M ; 3003 3004 JR PRINT_CH 3005 3006 ; ___ 3007 ; HL is typically -10000, -1000, -100, -10 3008 ; and repeatedly subtracted from BC 3009 ; i.e. it print 3010 ; 3011 ; 3012 mark_07E1: 3013 OUT_DIGIT: 3014 XOR A ; assume the digit is zero to begin with 3015 3016 mark_07E2: 3017 DIGIT_INC: 3018 ADD HL,BC ; HL += -ve number 3019 INC A ; 3020 JR C,DIGIT_INC ; loop 3021 3022 SBC HL,BC ; undo last iteration 3023 DEC A ; undo last iteration 3024 JR Z,LEAD_SP ; leading zeros shown as spaces 3025 3026 mark_07EB: 3027 OUT_CODE: 3028 LD E,ZX_0 ; $1C 3029 ADD A,E ; 3030 3031 mark_07EE: 3032 OUT_CH: 3033 AND A ; 3034 JR Z,PRINT_SP 3035 3036 mark_07F1: 3037 PRINT_CH: 3038 RES 0,(IY+FLAGS-RAMBASE) ; signal leading space permitted 3039 3040 mark_07F5: 3041 PRINT_SP: 3042 EXX ; 3043 PUSH HL ; 3044 BIT 1,(IY+FLAGS-RAMBASE) ; is printer in use ? 3045 JR NZ,LPRINT_A 3046 3047 CALL ENTER_CH 3048 JR PRINT_EXX 3049 3050 ; ___ 3051 3052 mark_0802: 3053 LPRINT_A: 3054 CALL LPRINT_CH 3055 3056 mark_0805: 3057 PRINT_EXX: 3058 POP HL ; 3059 EXX ; 3060 RET ; 3061 3062 ; ___ 3063 3064 mark_0808: 3065 ENTER_CH: 3066 LD D,A ; 3067 LD BC,(S_POSN) ; 3068 LD A,C ; 3069 CP CHARS_HORIZONTAL+1 ; 3070 JR Z,TEST_LOW 3071 3072 mark_0812: 3073 TEST_N_L: 3074 LD A,ZX_NEWLINE ; 3075 CP D ; 3076 JR Z,WRITE_N_L 3077 3078 LD HL,(DF_CC) ; 3079 CP (HL) ; 3080 LD A,D ; 3081 JR NZ,WRITE_CH 3082 3083 DEC C ; 3084 JR NZ,EXPAND_1 3085 3086 INC HL ; 3087 LD (DF_CC),HL ; 3088 LD C,CHARS_HORIZONTAL+1 ; $21 = 33 normally 3089 DEC B ; 3090 LD (S_POSN),BC ; 3091 3092 mark_082C: 3093 TEST_LOW: 3094 LD A,B ; 3095 CP (IY+DF_SZ-RAMBASE) 3096 JR Z,REPORT_5 3097 3098 AND A ; 3099 JR NZ,TEST_N_L 3100 3101 mark_0835: 3102 REPORT_5: 3103 LD L,4 ; 'No more room on screen' 3104 JP ERROR_3 3105 3106 ; ___ 3107 3108 mark_083A: 3109 EXPAND_1: 3110 CALL ONE_SPACE 3111 EX DE,HL ; 3112 3113 mark_083E: 3114 WRITE_CH: 3115 LD (HL),A ; 3116 INC HL ; 3117 LD (DF_CC),HL ; 3118 DEC (IY+S_POSN_x-RAMBASE) 3119 RET ; 3120 3121 ; ___ 3122 3123 mark_0847: 3124 WRITE_N_L: 3125 LD C,CHARS_HORIZONTAL+1 ; $21 = 33 3126 DEC B ; 3127 SET 0,(IY+FLAGS-RAMBASE) ; Suppress leading space 3128 JP LOC_ADDR 3129 3130 3131 ; THE 'LPRINT_CH' SUBROUTINE 3132 3133 ; This routine sends a character to the ZX-Printer placing the code for the 3134 ; character in the Printer Buffer. 3135 ; Note. PR_CC contains the low byte of the buffer address. The high order byte 3136 ; is always constant. 3137 3138 3139 mark_0851: 3140 LPRINT_CH: 3141 CP ZX_NEWLINE ; compare to NEWLINE. 3142 JR Z,COPY_BUFF ; forward if so 3143 3144 LD C,A ; take a copy of the character in C. 3145 LD A,(PR_CC) ; fetch print location from PR_CC 3146 AND $7F ; ignore bit 7 to form true position. 3147 CP $5C ; compare to 33rd location 3148 3149 LD L,A ; form low-order byte. 3150 LD H,$40 ; the high-order byte is fixed. 3151 3152 CALL Z,COPY_BUFF ; to send full buffer to 3153 ; the printer if first 32 bytes full. 3154 ; (this will reset HL to start.) 3155 3156 LD (HL),C ; place character at location. 3157 INC L ; increment - will not cross a 256 boundary. 3158 LD (IY+PR_CC-RAMBASE),L ; update system variable PR_CC 3159 ; automatically resetting bit 7 to show that 3160 ; the buffer is not empty. 3161 RET ; return. 3162 3163 3164 ; THE 'COPY' COMMAND ROUTINE 3165 3166 ; The full character-mapped screen is copied to the ZX-Printer. 3167 ; All twenty-four text/graphic lines are printed. 3168 3169 mark_0869: 3170 COPY: 3171 ; 3172 ; check - is this $16==22 or 24? 3173 ; 3174 ;; LD D,$16 ; prepare to copy twenty four text lines. 3175 LD D,22 ; prepare to copy twenty four text lines. 3176 LD HL,(D_FILE) ; set HL to start of display file from D_FILE. 3177 INC HL ; 3178 JR COPY_D ; forward 3179 3180 ; ___ 3181 3182 ; A single character-mapped printer buffer is copied to the ZX-Printer. 3183 3184 mark_0871: 3185 COPY_BUFF: 3186 LD D,1 ; prepare to copy a single text line. 3187 LD HL,PRBUFF ; set HL to start of printer buffer PRBUFF. 3188 3189 ; both paths converge here. 3190 3191 mark_0876: 3192 COPY_D: 3193 CALL SET_FAST 3194 3195 PUSH BC ; *** preserve BC throughout. 3196 ; a pending character may be present 3197 ; in C from LPRINT_CH 3198 3199 mark_087A: 3200 COPY_LOOP: 3201 PUSH HL ; save first character of line pointer. (*) 3202 XOR A ; clear accumulator. 3203 LD E,A ; set pixel line count, range 0-7, to zero. 3204 3205 ; this inner loop deals with each horizontal pixel line. 3206 3207 mark_087D: 3208 COPY_TIME: 3209 OUT (IO_PORT_PRINTER),A ; bit 2 reset starts the printer motor 3210 ; with an inactive stylus - bit 7 reset. 3211 POP HL ; pick up first character of line pointer (*) 3212 ; on inner loop. 3213 3214 mark_0880: 3215 COPY_BRK: 3216 CALL BREAK_1 3217 JR C,COPY_CONT ; forward with no keypress to COPY_CONT 3218 3219 ; else A will hold 11111111 0 3220 3221 RRA ; 0111 1111 3222 OUT (IO_PORT_PRINTER),A ; stop ZX printer motor, de-activate stylus. 3223 3224 mark_0888: 3225 REPORT_D2: 3226 RST _ERROR_1 3227 DEFB $0C ; Error Report: BREAK - CONT repeats 3228 3229 ; ___ 3230 3231 mark_088A: 3232 COPY_CONT: 3233 IN A,(IO_PORT_PRINTER) ; read from printer port. 3234 ADD A,A ; test bit 6 and 7 3235 JP M,COPY_END ; jump forward with no printer to COPY_END 3236 3237 JR NC,COPY_BRK ; back if stylus not in position to COPY_BRK 3238 3239 PUSH HL ; save first character of line pointer (*) 3240 PUSH DE ; ** preserve character line and pixel line. 3241 3242 LD A,D ; text line count to A? 3243 CP 2 ; sets carry if last line. 3244 SBC A,A ; now $FF if last line else zero. 3245 3246 ; now cleverly prepare a printer control mask setting bit 2 (later moved to 1) 3247 ; of D to slow printer for the last two pixel lines ( E = 6 and 7) 3248 3249 AND E ; and with pixel line offset 0-7 3250 RLCA ; shift to left. 3251 AND E ; and again. 3252 LD D,A ; store control mask in D. 3253 3254 mark_089C: 3255 COPY_NEXT: 3256 LD C,(HL) ; load character from screen or buffer. 3257 LD A,C ; save a copy in C for later inverse test. 3258 INC HL ; update pointer for next time. 3259 CP ZX_NEWLINE ; is character a NEWLINE ? 3260 JR Z,COPY_N_L ; forward, if so, to COPY_N_L 3261 3262 PUSH HL ; * else preserve the character pointer. 3263 3264 SLA A ; (?) multiply by two 3265 ADD A,A ; multiply by four 3266 ADD A,A ; multiply by eight 3267 3268 LD H,$0F ; load H with half the address of character set. 3269 RL H ; now $1E or $1F (with carry) 3270 ADD A,E ; add byte offset 0-7 3271 LD L,A ; now HL addresses character source byte 3272 3273 RL C ; test character, setting carry if inverse. 3274 SBC A,A ; accumulator now $00 if normal, $FF if inverse. 3275 3276 XOR (HL) ; combine with bit pattern at end or ROM. 3277 LD C,A ; transfer the byte to C. 3278 LD B,8 ; count eight bits to output. 3279 3280 mark_08B5: 3281 COPY_BITS: 3282 LD A,D ; fetch speed control mask from D. 3283 RLC C ; rotate a bit from output byte to carry. 3284 RRA ; pick up in bit 7, speed bit to bit 1 3285 LD H,A ; store aligned mask in H register. 3286 3287 mark_08BA: 3288 COPY_WAIT: 3289 IN A,(IO_PORT_PRINTER) ; read the printer port 3290 RRA ; test for alignment signal from encoder. 3291 JR NC,COPY_WAIT ; loop if not present to COPY_WAIT 3292 3293 LD A,H ; control byte to A. 3294 OUT (IO_PORT_PRINTER),A ; and output to printer port. 3295 DJNZ COPY_BITS ; loop for all eight bits to COPY_BITS 3296 3297 POP HL ; * restore character pointer. 3298 JR COPY_NEXT ; back for adjacent character line to COPY_NEXT 3299 3300 ; ___ 3301 3302 ; A NEWLINE has been encountered either following a text line or as the 3303 ; first character of the screen or printer line. 3304 3305 mark_08C7: 3306 COPY_N_L: 3307 IN A,(IO_PORT_PRINTER) ; read printer port. 3308 RRA ; wait for encoder signal. 3309 JR NC,COPY_N_L ; loop back if not to COPY_N_L 3310 3311 LD A,D ; transfer speed mask to A. 3312 RRCA ; rotate speed bit to bit 1. 3313 ; bit 7, stylus control is reset. 3314 OUT (IO_PORT_PRINTER),A ; set the printer speed. 3315 3316 POP DE ; ** restore character line and pixel line. 3317 INC E ; increment pixel line 0-7. 3318 BIT 3,E ; test if value eight reached. 3319 JR Z,COPY_TIME ; back if not 3320 3321 ; eight pixel lines, a text line have been completed. 3322 3323 POP BC ; lose the now redundant first character 3324 ; pointer 3325 DEC D ; decrease text line count. 3326 JR NZ,COPY_LOOP ; back if not zero 3327 3328 LD A,$04 ; stop the already slowed printer motor. 3329 OUT (IO_PORT_PRINTER),A ; output to printer port. 3330 3331 mark_08DE: 3332 COPY_END: 3333 CALL SLOW_FAST 3334 POP BC ; *** restore preserved BC. 3335 3336 3337 ; THE 'CLEAR PRINTER BUFFER' SUBROUTINE 3338 3339 ; This subroutine sets 32 bytes of the printer buffer to zero (space) and 3340 ; the 33rd character is set to a NEWLINE. 3341 ; This occurs after the printer buffer is sent to the printer but in addition 3342 ; after the 24 lines of the screen are sent to the printer. 3343 ; Note. This is a logic error as the last operation does not involve the 3344 ; buffer at all. Logically one should be able to use 3345 ; 10 LPRINT "HELLO "; 3346 ; 20 COPY 3347 ; 30 LPRINT ; "WORLD" 3348 ; and expect to see the entire greeting emerge from the printer. 3349 ; Surprisingly this logic error was never discovered and although one can argue 3350 ; if the above is a bug, the repetition of this error on the Spectrum was most 3351 ; definitely a bug. 3352 ; Since the printer buffer is fixed at the end of the system variables, and 3353 ; the print position is in the range $3C - $5C, then bit 7 of the system 3354 ; variable is set to show the buffer is empty and automatically reset when 3355 ; the variable is updated with any print position - neat. 3356 3357 mark_08E2: 3358 CLEAR_PRB: 3359 LD HL,PRBUFF_END ; address fixed end of PRBUFF 3360 LD (HL),ZX_NEWLINE ; place a newline at last position. 3361 LD B,32 ; prepare to blank 32 preceding characters. 3362 ; 3363 ; NB the printer is fixed at 32 characters, maybe it can be tweaked ??? 3364 ; 3365 mark_08E9: 3366 PRB_BYTES: 3367 DEC HL ; decrement address - could be DEC L. 3368 LD (HL),0 ; place a zero byte. 3369 DJNZ PRB_BYTES ; loop for all thirty-two 3370 3371 LD A,L ; fetch character print position. 3372 SET 7,A ; signal the printer buffer is clear. 3373 LD (PR_CC),A ; update one-byte system variable PR_CC 3374 RET ; return. 3375 3376 3377 ; THE 'PRINT AT' SUBROUTINE 3378 3379 ; 3380 ; 3381 ; 3382 mark_08F5: 3383 PRINT_AT: 3384 3385 LD A,CHARS_VERTICAL-1 ; originally 23 3386 SUB B ; 3387 JR C,WRONG_VAL 3388 3389 mark_08FA: 3390 TEST_VAL: 3391 CP (IY+DF_SZ-RAMBASE) 3392 JP C,REPORT_5 3393 3394 INC A ; 3395 LD B,A ; 3396 LD A,CHARS_HORIZONTAL-1 ; originally 31 3397 3398 SUB C ; 3399 3400 mark_0905: 3401 WRONG_VAL: 3402 JP C,REPORT_B 3403 3404 ADD A,2 ; 3405 LD C,A ; 3406 3407 mark_090B: 3408 SET_FIELD: 3409 BIT 1,(IY+FLAGS-RAMBASE) ; Is printer in use? 3410 JR Z,LOC_ADDR 3411 3412 LD A,$5D ; 3413 SUB C ; 3414 LD (PR_CC),A ; 3415 RET ; 3416 3417 3418 ; THE 'LOCATE ADDRESS' ROUTINE 3419 3420 ; 3421 ; I'm guessing this locates the address of a character at X,Y 3422 ; on the screen, with 0,0 being on the bottom left? 3423 ; S_POSN_x: equ $4039 3424 ; S_POSN_y: equ $403A 3425 ; so when BC is stored there, B is Y and C is X 3426 ; 3427 mark_0918: 3428 LOC_ADDR: 3429 LD (S_POSN),BC ; 3430 LD HL,(VARS) ; 3431 LD D,C ; 3432 LD A,CHARS_HORIZONTAL+2 ; $22 == 34 originally. 3433 SUB C ; 3434 LD C,A ; 3435 LD A,ZX_NEWLINE ; 3436 INC B ; 3437 3438 mark_0927: 3439 LOOK_BACK: 3440 DEC HL ; 3441 CP (HL) ; 3442 JR NZ,LOOK_BACK 3443 3444 DJNZ LOOK_BACK 3445 3446 INC HL ; 3447 CPIR ; 3448 DEC HL ; 3449 LD (DF_CC),HL ; 3450 SCF ; Set Carry Flag 3451 RET PO ; 3452 3453 DEC D ; 3454 RET Z ; 3455 3456 PUSH BC ; 3457 CALL MAKE_ROOM 3458 POP BC ; 3459 LD B,C ; 3460 LD H,D ; HL := DE 3461 LD L,E ; 3462 3463 mark_0940: 3464 EXPAND_2: 3465 ; 3466 ; Writes B spaces to HL-- 3467 ; 3468 LD (HL),ZX_SPACE ; 3469 DEC HL ; 3470 DJNZ EXPAND_2 3471 3472 EX DE,HL ; restore HL 3473 INC HL ; 3474 LD (DF_CC),HL ; 3475 RET ; 3476 3477 3478 ; THE 'EXPAND TOKENS' SUBROUTINE 3479 3480 3481 mark_094B: 3482 TOKENS: 3483 PUSH AF ; 3484 CALL TOKEN_ADD 3485 JR NC,ALL_CHARS 3486 3487 BIT 0,(IY+FLAGS-RAMBASE) ; Leading space if set 3488 JR NZ,ALL_CHARS 3489 3490 XOR A ; A = 0 = ZX_SPACE 3491 3492 RST _PRINT_A 3493 3494 mark_0959: 3495 ALL_CHARS: 3496 LD A,(BC) ; 3497 AND $3F ; truncate to printable values ??? 3498 3499 RST _PRINT_A 3500 LD A,(BC) ; 3501 INC BC ; 3502 ADD A,A ; 3503 JR NC,ALL_CHARS 3504 3505 POP BC ; 3506 BIT 7,B ; 3507 RET Z ; 3508 3509 CP ZX_COMMA ; $1A == 26 3510 JR Z,TRAIL_SP 3511 3512 CP ZX_S ; $38 == 56 3513 RET C ; 3514 3515 mark_096D: 3516 TRAIL_SP: 3517 XOR A ; 3518 SET 0,(IY+FLAGS-RAMBASE) ; Suppress leading space 3519 JP PRINT_SP 3520 3521 ; ___ 3522 3523 mark_0975: 3524 TOKEN_ADD: 3525 PUSH HL ; 3526 LD HL,TOKEN_TABLE 3527 BIT 7,A ; 3528 JR Z,TEST_HIGH 3529 3530 AND $3F ; 3531 3532 mark_097F: 3533 TEST_HIGH: 3534 CP $43 ; 3535 JR NC,FOUND 3536 3537 LD B,A ; 3538 INC B ; 3539 3540 mark_0985: 3541 WORDS: 3542 BIT 7,(HL) ; 3543 INC HL ; 3544 JR Z,WORDS 3545 3546 DJNZ WORDS 3547 3548 BIT 6,A ; 3549 JR NZ,COMP_FLAG 3550 3551 CP $18 ; 3552 3553 mark_0992: 3554 COMP_FLAG: 3555 CCF ; Complement Carry Flag 3556 3557 mark_0993: 3558 FOUND: 3559 LD B,H ; 3560 LD C,L ; 3561 POP HL ; 3562 RET NC ; 3563 3564 LD A,(BC) ; 3565 ADD A,$E4 ; 3566 RET ; 3567 3568 3569 ; THE 'ONE_SPACE' SUBROUTINE 3570 3571 3572 mark_099B: 3573 ONE_SPACE: 3574 LD BC,$0001 ; 3575 3576 3577 ; THE 'MAKE ROOM' SUBROUTINE 3578 3579 ; 3580 ; 3581 3582 mark_099E: 3583 MAKE_ROOM: 3584 PUSH HL ; 3585 CALL TEST_ROOM 3586 POP HL ; 3587 CALL POINTERS 3588 LD HL,(STKEND) ; 3589 EX DE,HL ; 3590 LDDR ; Copy Bytes 3591 RET ; 3592 3593 3594 ; THE 'POINTERS' SUBROUTINE 3595 3596 3597 mark_09AD: 3598 POINTERS: 3599 PUSH AF ; 3600 PUSH HL ; 3601 LD HL,D_FILE ; 3602 LD A,$09 ; 3603 3604 mark_09B4: 3605 NEXT_PTR: 3606 LD E,(HL) ; 3607 INC HL ; 3608 LD D,(HL) ; 3609 EX (SP),HL ; 3610 AND A ; 3611 SBC HL,DE ; 3612 ADD HL,DE ; 3613 EX (SP),HL ; 3614 JR NC,PTR_DONE 3615 3616 PUSH DE ; 3617 EX DE,HL ; 3618 ADD HL,BC ; 3619 EX DE,HL ; 3620 LD (HL),D ; 3621 DEC HL ; 3622 LD (HL),E ; 3623 INC HL ; 3624 POP DE ; 3625 3626 mark_09C8: 3627 PTR_DONE: 3628 INC HL ; 3629 DEC A ; 3630 JR NZ,NEXT_PTR 3631 3632 EX DE,HL ; 3633 POP DE ; 3634 POP AF ; 3635 AND A ; 3636 SBC HL,DE ; 3637 LD B,H ; 3638 LD C,L ; 3639 INC BC ; 3640 ADD HL,DE ; 3641 EX DE,HL ; 3642 RET ; 3643 3644 3645 ; THE 'LINE ADDRESS' SUBROUTINE 3646 3647 3648 mark_09D8: 3649 LINE_ADDR: 3650 PUSH HL ; 3651 LD HL,USER_RAM ; 3652 LD D,H ; 3653 LD E,L ; 3654 3655 mark_09DE: 3656 NEXT_TEST: 3657 POP BC ; 3658 CALL CP_LINES 3659 RET NC ; 3660 3661 PUSH BC ; 3662 CALL NEXT_ONE 3663 EX DE,HL ; 3664 JR NEXT_TEST 3665 3666 3667 ; THE 'COMPARE LINE NUMBERS' SUBROUTINE 3668 3669 3670 mark_09EA: 3671 CP_LINES: 3672 LD A,(HL) ; 3673 CP B ; 3674 RET NZ ; 3675 3676 INC HL ; 3677 LD A,(HL) ; 3678 DEC HL ; 3679 CP C ; 3680 RET ; 3681 3682 3683 ; THE 'NEXT LINE OR VARIABLE' SUBROUTINE 3684 3685 3686 mark_09F2: 3687 NEXT_ONE: 3688 PUSH HL ; 3689 LD A,(HL) ; 3690 CP $40 ; 3691 JR C,LINES 3692 3693 BIT 5,A ; 3694 JR Z,NEXT_0_4 ; skip forward 3695 3696 ADD A,A ; 3697 JP M,NEXT_PLUS_FIVE 3698 3699 CCF ; Complement Carry Flag 3700 3701 mark_0A01: 3702 NEXT_PLUS_FIVE: 3703 LD BC,$0005 ; 3704 JR NC,NEXT_LETT 3705 3706 LD C,$11 ; 17 3707 3708 mark_0A08: 3709 NEXT_LETT: 3710 RLA ; 3711 INC HL ; 3712 LD A,(HL) ; 3713 JR NC,NEXT_LETT ; loop 3714 3715 JR NEXT_ADD 3716 ; ___ 3717 3718 mark_0A0F: 3719 LINES: 3720 INC HL ; 3721 3722 mark_0A10: 3723 NEXT_0_4: 3724 INC HL ; BC = word at HL++ 3725 LD C,(HL) ; 3726 INC HL ; 3727 LD B,(HL) ; 3728 INC HL ; 3729 3730 mark_0A15: 3731 NEXT_ADD: 3732 ADD HL,BC ; 3733 POP DE ; 3734 3735 3736 ; THE 'DIFFERENCE' SUBROUTINE 3737 3738 3739 mark_0A17: 3740 DIFFER: 3741 AND A ; 3742 SBC HL,DE ; 3743 LD B,H ; BC := (HL-DE) 3744 LD C,L ; 3745 ADD HL,DE ; 3746 EX DE,HL ; DE := old HL ??? 3747 RET ; 3748 3749 3750 ; THE 'LINE_ENDS' SUBROUTINE 3751 3752 3753 mark_0A1F: 3754 LINE_ENDS: 3755 LD B,(IY+DF_SZ-RAMBASE) 3756 PUSH BC ; 3757 CALL B_LINES 3758 POP BC ; 3759 DEC B ; 3760 JR B_LINES 3761 3762 3763 ; THE 'CLS' COMMAND ROUTINE 3764 3765 3766 mark_0A2A: 3767 CLS: 3768 LD B,CHARS_VERTICAL ; number of lines to clear. $18 = 24 originally. 3769 3770 mark_0A2C: 3771 B_LINES: 3772 RES 1,(IY+FLAGS-RAMBASE) ; Signal printer not in use 3773 LD C,CHARS_HORIZONTAL+1 ; $21 ; extra 1 is for HALT opcode ? 3774 PUSH BC ; 3775 CALL LOC_ADDR 3776 POP BC ; 3777 LD A,(RAMTOP+1) ; is RAMTOP_hi 3778 CP $4D ; 3779 JR C,COLLAPSED 3780 ; 3781 ; If RAMTOP less then 4D00, RAM less than D00 = 3.25 K, 3782 ; uses collapsed display. 3783 ; 3784 3785 SET 7,(IY+S_POSN_y-RAMBASE) 3786 3787 mark_0A42: 3788 CLEAR_LOC: 3789 XOR A ; prepare a space 3790 CALL PRINT_SP ; prints a space 3791 LD HL,(S_POSN) ; 3792 LD A,L ; 3793 OR H ; 3794 AND $7E ; 3795 JR NZ,CLEAR_LOC 3796 3797 JP LOC_ADDR 3798 3799 ; ___ 3800 3801 mark_0A52: 3802 COLLAPSED: 3803 LD D,H ; DE := HL 3804 LD E,L ; 3805 DEC HL ; 3806 LD C,B ; 3807 LD B,0 ; Will loop 256 times 3808 LDIR ; Copy Bytes 3809 LD HL,(VARS) ; 3810 3811 3812 ; THE 'RECLAIMING' SUBROUTINES 3813 3814 3815 mark_0A5D: 3816 RECLAIM_1: 3817 CALL DIFFER 3818 3819 mark_0A60: 3820 RECLAIM_2: 3821 PUSH BC ; 3822 LD A,B ; 3823 CPL ; 3824 LD B,A ; 3825 LD A,C ; 3826 CPL ; 3827 LD C,A ; 3828 INC BC ; 3829 CALL POINTERS 3830 EX DE,HL ; 3831 POP HL ; 3832 ADD HL,DE ; 3833 PUSH DE ; 3834 LDIR ; Copy Bytes 3835 POP HL ; 3836 RET ; 3837 3838 3839 ; THE 'E_LINE NUMBER' SUBROUTINE 3840 3841 3842 mark_0A73: 3843 E_LINE_NUM: 3844 LD HL,(E_LINE) ; 3845 CALL TEMP_PTR2 3846 3847 RST _GET_CHAR 3848 BIT 5,(IY+FLAGX-RAMBASE) 3849 RET NZ ; 3850 3851 LD HL,MEM_0_1st ; 3852 LD (STKEND),HL ; 3853 CALL INT_TO_FP 3854 CALL FP_TO_BC 3855 JR C,NO_NUMBER ; to NO_NUMBER 3856 3857 LD HL,-10000 ; $D8F0 ; value '-10000' 3858 ADD HL,BC ; 3859 3860 mark_0A91: 3861 NO_NUMBER: 3862 JP C,REPORT_C ; to REPORT_C 3863 3864 CP A ; 3865 JP SET_MIN 3866 3867 3868 ; THE 'REPORT AND LINE NUMBER' PRINTING SUBROUTINES 3869 3870 3871 mark_0A98: 3872 OUT_NUM: 3873 PUSH DE ; 3874 PUSH HL ; 3875 XOR A ; 3876 BIT 7,B ; 3877 JR NZ,UNITS 3878 3879 LD H,B ; HL := BC 3880 LD L,C ; 3881 LD E,$FF ; 3882 JR THOUSAND 3883 ; ___ 3884 3885 mark_0AA5: 3886 OUT_NO: 3887 PUSH DE ; 3888 LD D,(HL) ; 3889 INC HL ; 3890 LD E,(HL) ; 3891 PUSH HL ; 3892 EX DE,HL ; 3893 LD E,ZX_SPACE ; set E to leading space. 3894 3895 mark_0AAD: 3896 THOUSAND: 3897 LD BC,-1000 ; $FC18 ; 3898 CALL OUT_DIGIT 3899 LD BC,-100 ; $FF9C ; 3900 CALL OUT_DIGIT 3901 LD C,-10 ; $F6 ; B is already FF, so saves a byte. 3902 CALL OUT_DIGIT 3903 LD A,L ; 3904 3905 mark_0ABF: 3906 UNITS: 3907 CALL OUT_CODE 3908 POP HL ; 3909 POP DE ; 3910 RET ; 3911 3912 3913 ; THE 'UNSTACK_Z' SUBROUTINE 3914 3915 3916 ; This subroutine is used to return early from a routine when checking syntax. 3917 ; On the ZX81 the same routines that execute commands also check the syntax 3918 ; on line entry. This enables precise placement of the error marker in a line 3919 ; that fails syntax. 3920 ; The sequence CALL SYNTAX_Z ; RET Z can be replaced by a call to this routine 3921 ; although it has not replaced every occurrence of the above two instructions. 3922 ; Even on the ZX80 this routine was not fully utilized. 3923 3924 mark_0AC5: 3925 UNSTACK_Z: 3926 CALL SYNTAX_Z ; resets the ZERO flag if 3927 ; checking syntax. 3928 POP HL ; drop the return address. 3929 RET Z ; return to previous calling routine if 3930 ; checking syntax. 3931 3932 JP (HL) ; else jump to the continuation address in 3933 ; the calling routine as RET would have done. 3934 3935 3936 ; THE 'LPRINT' COMMAND ROUTINE 3937 3938 ; 3939 ; 3940 3941 mark_0ACB: 3942 LPRINT: 3943 SET 1,(IY+FLAGS-RAMBASE) ; Signal printer in use 3944 3945 3946 ; THE 'PRINT' COMMAND ROUTINE 3947 3948 3949 mark_0ACF: 3950 PRINT: 3951 LD A,(HL) ; 3952 CP ZX_NEWLINE ; 3953 JP Z,PRINT_END ; to PRINT_END 3954 3955 mark_0AD5: 3956 PRINT_1: 3957 SUB ZX_COMMA ; $1A == 26 3958 ADC A,$00 ; 3959 JR Z,SPACING ; to SPACING 3960 ; 3961 ; Compare with AT, 3962 ; less comma recently subtracted. 3963 ; 3964 CP ZX_AT-ZX_COMMA ; $A7 == 167 3965 JR NZ,NOT_AT ; 3966 3967 3968 RST _NEXT_CHAR 3969 CALL CLASS_6 3970 CP ZX_COMMA ; $1A = 26 3971 JP NZ,REPORT_C ; 3972 3973 RST _NEXT_CHAR 3974 CALL CLASS_6 3975 CALL SYNTAX_ON 3976 3977 RST _FP_CALC ;; 3978 DEFB __exchange ;; 3979 DEFB __end_calc ;; 3980 3981 CALL STK_TO_BC 3982 CALL PRINT_AT 3983 JR PRINT_ON 3984 ; ___ 3985 3986 mark_0AFA: 3987 NOT_AT: 3988 CP ZX_TAB-ZX_COMMA ; $A8 == 168 3989 JR NZ,NOT_TAB 3990 3991 3992 RST _NEXT_CHAR 3993 CALL CLASS_6 3994 CALL SYNTAX_ON 3995 CALL STK_TO_A 3996 JP NZ,REPORT_B 3997 3998 AND $1F ; truncate to 0 to 31 characters ??? 3999 LD C,A ; 4000 BIT 1,(IY+FLAGS-RAMBASE) ; Is printer in use 4001 JR Z,TAB_TEST 4002 4003 SUB (IY+PR_CC-RAMBASE) 4004 SET 7,A ; 4005 ADD A,$3C ; 60 4006 CALL NC,COPY_BUFF 4007 4008 mark_0B1E: 4009 TAB_TEST: 4010 ADD A,(IY+S_POSN_x-RAMBASE) ; screen position X 4011 CP CHARS_HORIZONTAL+1 ; 33 (characters horizontal plus newline ???) 4012 LD A,(S_POSN_y) ; screen position Y 4013 SBC A,1 ; 4014 CALL TEST_VAL 4015 SET 0,(IY+FLAGS-RAMBASE) ; sv FLAGS - Suppress leading space 4016 JR PRINT_ON 4017 ; ___ 4018 4019 mark_0B31: 4020 NOT_TAB: 4021 CALL SCANNING 4022 CALL PRINT_STK 4023 4024 mark_0B37: 4025 PRINT_ON: 4026 RST _GET_CHAR 4027 SUB ZX_COMMA ; $1A 4028 ADC A,0 ; 4029 JR Z,SPACING 4030 4031 CALL CHECK_END 4032 JP PRINT_END 4033 ; ___ 4034 mark_0B44: 4035 SPACING: 4036 CALL NC,FIELD 4037 4038 RST _NEXT_CHAR 4039 CP ZX_NEWLINE ; 4040 RET Z ; 4041 4042 JP PRINT_1 4043 ; ___ 4044 mark_0B4E: 4045 SYNTAX_ON: 4046 CALL SYNTAX_Z 4047 RET NZ ; 4048 4049 POP HL ; 4050 JR PRINT_ON 4051 ; ___ 4052 mark_0B55: 4053 PRINT_STK: 4054 CALL UNSTACK_Z 4055 BIT 6,(IY+FLAGS-RAMBASE) ; Numeric or string result? 4056 CALL Z,STK_FETCH 4057 JR Z,PR_STR_4 4058 4059 JP PRINT_FP ; jump forward 4060 ; ___ 4061 4062 mark_0B64: 4063 PR_STR_1: 4064 LD A,ZX_QUOTE ; $0B 4065 4066 mark_0B66: 4067 PR_STR_2: 4068 RST _PRINT_A 4069 4070 mark_0B67: 4071 PR_STR_3: 4072 LD DE,(X_PTR) ; 4073 4074 mark_0B6B: 4075 PR_STR_4: 4076 LD A,B ; 4077 OR C ; 4078 DEC BC ; 4079 RET Z ; 4080 4081 LD A,(DE) ; 4082 INC DE ; 4083 LD (X_PTR),DE ; 4084 BIT 6,A ; 4085 JR Z,PR_STR_2 4086 4087 CP $C0 ; 4088 JR Z,PR_STR_1 4089 4090 PUSH BC ; 4091 CALL TOKENS 4092 POP BC ; 4093 JR PR_STR_3 4094 4095 ; ___ 4096 4097 mark_0B84: 4098 PRINT_END: 4099 CALL UNSTACK_Z 4100 LD A,ZX_NEWLINE ; 4101 4102 RST _PRINT_A 4103 RET ; 4104 4105 ; ___ 4106 4107 mark_0B8B: 4108 FIELD: 4109 CALL UNSTACK_Z 4110 SET 0,(IY+FLAGS-RAMBASE) ; Suppress leading space 4111 XOR A ; 4112 4113 RST _PRINT_A 4114 LD BC,(S_POSN) ; 4115 LD A,C ; 4116 BIT 1,(IY+FLAGS-RAMBASE) ; Is printer in use 4117 JR Z,CENTRE 4118 4119 LD A,$5D ; 4120 SUB (IY+PR_CC-RAMBASE) 4121 4122 mark_0BA4: 4123 CENTRE: 4124 LD C,$11 ; 4125 CP C ; 4126 JR NC,RIGHT 4127 4128 LD C,$01 ; 4129 4130 mark_0BAB: 4131 RIGHT: 4132 CALL SET_FIELD 4133 RET ; 4134 4135 4136 ; THE 'PLOT AND UNPLOT' COMMAND ROUTINES 4137 4138 4139 mark_0BAF: 4140 PLOT_UNPLOT: 4141 ; 4142 ; Of the 24 lines, only top 22 ar used for plotting. 4143 ; 4144 CALL STK_TO_BC 4145 LD (COORDS_x),BC ; 4146 ;; LD A,$2B ; originally $2B == 32+11 = 43 = 2*22-1 4147 LD A,2*(CHARS_VERTICAL-2)-1 ; 4148 SUB B ; 4149 JP C,REPORT_B 4150 4151 LD B,A ; 4152 LD A,$01 ; 4153 SRA B ; 4154 JR NC,COLUMNS 4155 4156 LD A,$04 ; 4157 4158 mark_0BC5: 4159 COLUMNS: 4160 SRA C ; 4161 JR NC,FIND_ADDR 4162 4163 RLCA ; 4164 4165 mark_0BCA: 4166 FIND_ADDR: 4167 PUSH AF ; 4168 CALL PRINT_AT 4169 LD A,(HL) ; 4170 RLCA ; 4171 CP ZX_BRACKET_LEFT ; $10 4172 JR NC,TABLE_PTR 4173 4174 RRCA ; 4175 JR NC,SQ_SAVED 4176 4177 XOR $8F ; 4178 4179 mark_0BD9: 4180 SQ_SAVED: 4181 LD B,A ; 4182 4183 mark_0BDA: 4184 TABLE_PTR: 4185 LD DE,P_UNPLOT ; Address: P_UNPLOT 4186 LD A,(T_ADDR) ; get T_ADDR_lo 4187 SUB E ; 4188 JP M,PLOT 4189 4190 POP AF ; 4191 CPL ; 4192 AND B ; 4193 JR UNPLOT 4194 4195 ; ___ 4196 4197 mark_0BE9: 4198 PLOT: 4199 POP AF ; 4200 OR B ; 4201 4202 mark_0BEB: 4203 UNPLOT: 4204 CP 8 ; Only apply to graphic characters (0 to 7) 4205 JR C,PLOT_END 4206 4207 XOR $8F ; binary 1000 1111 4208 4209 mark_0BF1: 4210 PLOT_END: 4211 EXX ; 4212 4213 RST _PRINT_A 4214 EXX ; 4215 RET ; 4216 4217 4218 ; THE 'STACK_TO_BC' SUBROUTINE 4219 4220 mark_0BF5: 4221 STK_TO_BC: 4222 CALL STK_TO_A 4223 LD B,A ; 4224 PUSH BC ; 4225 CALL STK_TO_A 4226 LD E,C ; 4227 POP BC ; 4228 LD D,C ; 4229 LD C,A ; 4230 RET ; 4231 4232 4233 ; THE 'STACK_TO_A' SUBROUTINE 4234 4235 4236 mark_0C02: 4237 STK_TO_A: 4238 CALL FP_TO_A 4239 JP C,REPORT_B 4240 4241 LD C,$01 ; 4242 RET Z ; 4243 4244 LD C,$FF ; 4245 RET ; 4246 4247 4248 ; THE 'SCROLL' SUBROUTINE 4249 4250 4251 mark_0C0E: 4252 SCROLL: 4253 LD B,(IY+DF_SZ-RAMBASE) 4254 LD C,CHARS_HORIZONTAL+1 ; 4255 CALL LOC_ADDR 4256 CALL ONE_SPACE 4257 LD A,(HL) ; 4258 LD (DE),A ; 4259 INC (IY+S_POSN_y-RAMBASE) 4260 LD HL,(D_FILE) ; 4261 INC HL ; 4262 LD D,H ; 4263 LD E,L ; 4264 CPIR ; 4265 JP RECLAIM_1 4266 4267 4268 ; THE 'SYNTAX' TABLES 4269 4270 4271 ; i) The Offset table 4272 4273 mark_0C29: 4274 offset_t: 4275 DEFB P_LPRINT - $ ; 8B offset 4276 DEFB P_LLIST - $ ; 8D offset 4277 DEFB P_STOP - $ ; 2D offset 4278 DEFB P_SLOW - $ ; 7F offset 4279 DEFB P_FAST - $ ; 81 offset 4280 DEFB P_NEW - $ ; 49 offset 4281 DEFB P_SCROLL - $ ; 75 offset 4282 DEFB P_CONT - $ ; 5F offset 4283 DEFB P_DIM - $ ; 40 offset 4284 DEFB P_REM - $ ; 42 offset 4285 DEFB P_FOR - $ ; 2B offset 4286 DEFB P_GOTO - $ ; 17 offset 4287 DEFB P_GOSUB - $ ; 1F offset 4288 DEFB P_INPUT - $ ; 37 offset 4289 DEFB P_LOAD - $ ; 52 offset 4290 DEFB P_LIST - $ ; 45 offset 4291 DEFB P_LET - $ ; 0F offset 4292 DEFB P_PAUSE - $ ; 6D offset 4293 DEFB P_NEXT - $ ; 2B offset 4294 DEFB P_POKE - $ ; 44 offset 4295 DEFB P_PRINT - $ ; 2D offset 4296 DEFB P_PLOT - $ ; 5A offset 4297 DEFB P_RUN - $ ; 3B offset 4298 DEFB P_SAVE - $ ; 4C offset 4299 DEFB P_RAND - $ ; 45 offset 4300 DEFB P_IF - $ ; 0D offset 4301 DEFB P_CLS - $ ; 52 offset 4302 DEFB P_UNPLOT - $ ; 5A offset 4303 DEFB P_CLEAR - $ ; 4D offset 4304 DEFB P_RETURN - $ ; 15 offset 4305 DEFB P_COPY - $ ; 6A offset 4306 4307 ; ii) The parameter table. 4308 4309 mark_0C48: 4310 P_LET: 4311 DEFB _CLASS_01 ; A variable is required. 4312 DEFB ZX_EQUAL ; Separator: '=' 4313 DEFB _CLASS_02 ; An expression, numeric or string, 4314 ; must follow. 4315 4316 mark_0C4B: 4317 P_GOTO: 4318 DEFB _CLASS_06 ; A numeric expression must follow. 4319 DEFB _CLASS_00 ; No further operands. 4320 DEFW GOTO 4321 4322 mark_0C4F: 4323 P_IF: 4324 DEFB _CLASS_06 ; A numeric expression must follow. 4325 DEFB ZX_THEN ; Separator: 'THEN' 4326 DEFB _CLASS_05 ; Variable syntax checked entirely 4327 ; by routine. 4328 DEFW IF 4329 4330 mark_0C54: 4331 P_GOSUB: 4332 DEFB _CLASS_06 ; A numeric expression must follow. 4333 DEFB _CLASS_00 ; No further operands. 4334 DEFW GOSUB 4335 4336 mark_0C58: 4337 P_STOP: 4338 DEFB _CLASS_00 ; No further operands. 4339 DEFW STOP 4340 4341 mark_0C5B: 4342 P_RETURN: 4343 DEFB _CLASS_00 ; No further operands. 4344 DEFW RETURN 4345 4346 mark_0C5E: 4347 P_FOR: 4348 DEFB _CLASS_04 ; A single character variable must 4349 ; follow. 4350 DEFB ZX_EQUAL ; Separator: '=' 4351 DEFB _CLASS_06 ; A numeric expression must follow. 4352 DEFB ZX_TO ; Separator: 'TO' 4353 DEFB _CLASS_06 ; A numeric expression must follow. 4354 DEFB _CLASS_05 ; Variable syntax checked entirely 4355 ; by routine. 4356 DEFW FOR 4357 4358 mark_0C66: 4359 P_NEXT: 4360 DEFB _CLASS_04 ; A single character variable must 4361 ; follow. 4362 DEFB _CLASS_00 ; No further operands. 4363 DEFW NEXT 4364 4365 mark_0C6A: 4366 P_PRINT: 4367 DEFB _CLASS_05 ; Variable syntax checked entirely 4368 ; by routine. 4369 DEFW PRINT ; not LPRINT ??? 4370 4371 mark_0C6D: 4372 P_INPUT: 4373 DEFB _CLASS_01 ; A variable is required. 4374 DEFB _CLASS_00 ; No further operands. 4375 DEFW INPUT 4376 4377 mark_0C71: 4378 P_DIM: 4379 DEFB _CLASS_05 ; Variable syntax checked entirely 4380 ; by routine. 4381 DEFW DIM 4382 4383 mark_0C74: 4384 P_REM: 4385 DEFB _CLASS_05 ; Variable syntax checked entirely 4386 ; by routine. 4387 DEFW REM 4388 4389 mark_0C77: 4390 P_NEW: 4391 DEFB _CLASS_00 ; No further operands. 4392 DEFW NEW 4393 4394 mark_0C7A: 4395 P_RUN: 4396 DEFB _CLASS_03 ; A numeric expression may follow 4397 ; else default to zero. 4398 DEFW RUN 4399 4400 mark_0C7D: 4401 P_LIST: 4402 DEFB _CLASS_03 ; A numeric expression may follow 4403 ; else default to zero. 4404 DEFW LIST 4405 4406 mark_0C80: 4407 P_POKE: 4408 DEFB _CLASS_06 ; A numeric expression must follow. 4409 DEFB ZX_COMMA ; Separator: ',' 4410 DEFB _CLASS_06 ; A numeric expression must follow. 4411 DEFB _CLASS_00 ; No further operands. 4412 DEFW POKE 4413 4414 mark_0C86: 4415 P_RAND: 4416 DEFB _CLASS_03 ; A numeric expression may follow 4417 ; else default to zero. 4418 DEFW RAND 4419 4420 mark_0C89: 4421 P_LOAD: 4422 DEFB _CLASS_05 ; Variable syntax checked entirely 4423 ; by routine. 4424 DEFW LOAD 4425 4426 mark_0C8C: 4427 P_SAVE: 4428 DEFB _CLASS_05 ; Variable syntax checked entirely 4429 ; by routine. 4430 DEFW SAVE 4431 4432 mark_0C8F: 4433 P_CONT: 4434 DEFB _CLASS_00 ; No further operands. 4435 DEFW CONT 4436 4437 mark_0C92: 4438 P_CLEAR: 4439 DEFB _CLASS_00 ; No further operands. 4440 DEFW CLEAR 4441 4442 mark_0C95: 4443 P_CLS: 4444 DEFB _CLASS_00 ; No further operands. 4445 DEFW CLS 4446 4447 mark_0C98: 4448 P_PLOT: 4449 DEFB _CLASS_06 ; A numeric expression must follow. 4450 DEFB ZX_COMMA ; Separator: ',' 4451 DEFB _CLASS_06 ; A numeric expression must follow. 4452 DEFB _CLASS_00 ; No further operands. 4453 DEFW PLOT_UNPLOT 4454 4455 mark_0C9E: 4456 P_UNPLOT: 4457 DEFB _CLASS_06 ; A numeric expression must follow. 4458 DEFB ZX_COMMA ; Separator: ',' 4459 DEFB _CLASS_06 ; A numeric expression must follow. 4460 DEFB _CLASS_00 ; No further operands. 4461 DEFW PLOT_UNPLOT 4462 4463 mark_0CA4: 4464 P_SCROLL: 4465 DEFB _CLASS_00 ; No further operands. 4466 DEFW SCROLL 4467 4468 mark_0CA7: 4469 P_PAUSE: 4470 DEFB _CLASS_06 ; A numeric expression must follow. 4471 DEFB _CLASS_00 ; No further operands. 4472 DEFW PAUSE 4473 4474 mark_0CAB: 4475 P_SLOW: 4476 DEFB _CLASS_00 ; No further operands. 4477 DEFW SLOW 4478 4479 mark_0CAE: 4480 P_FAST: 4481 DEFB _CLASS_00 ; No further operands. 4482 DEFW FAST 4483 4484 mark_0CB1: 4485 P_COPY: 4486 DEFB _CLASS_00 ; No further operands. 4487 DEFW COPY 4488 4489 mark_0CB4: 4490 P_LPRINT: 4491 DEFB _CLASS_05 ; Variable syntax checked entirely 4492 ; by routine. 4493 DEFW LPRINT 4494 4495 mark_0CB7: 4496 P_LLIST: 4497 DEFB _CLASS_03 ; A numeric expression may follow 4498 ; else default to zero. 4499 DEFW LLIST 4500 4501 4502 4503 ; THE 'LINE SCANNING' ROUTINE 4504 4505 4506 mark_0CBA: 4507 LINE_SCAN: 4508 LD (IY+FLAGS-RAMBASE),1 4509 CALL E_LINE_NUM 4510 4511 mark_0CC1: 4512 LINE_RUN: 4513 CALL SET_MIN 4514 LD HL,ERR_NR ; 4515 LD (HL),$FF ; 4516 LD HL,FLAGX ; 4517 BIT 5,(HL) ; 4518 JR Z,LINE_NULL 4519 4520 CP $E3 ; 'STOP' ? 4521 LD A,(HL) ; 4522 JP NZ,INPUT_REP 4523 4524 CALL SYNTAX_Z 4525 RET Z ; 4526 4527 4528 RST _ERROR_1 4529 DEFB $0C ; Error Report: BREAK - CONT repeats 4530 4531 4532 4533 ; THE 'STOP' COMMAND ROUTINE 4534 4535 ; 4536 ; 4537 4538 mark_0CDC: 4539 STOP: 4540 RST _ERROR_1 4541 DEFB $08 ; Error Report: STOP statement 4542 ; ___ 4543 4544 ; the interpretation of a line continues with a check for just spaces 4545 ; followed by a carriage return. 4546 ; The IF command also branches here with a true value to execute the 4547 ; statement after the THEN but the statement can be null so 4548 ; 10 IF 1 = 1 THEN 4549 ; passes syntax (on all ZX computers). 4550 4551 mark_0CDE: 4552 LINE_NULL: 4553 RST _GET_CHAR 4554 LD B,$00 ; prepare to index - early. 4555 CP ZX_NEWLINE ; compare to NEWLINE. 4556 RET Z ; return if so. 4557 4558 4559 4560 4561 4562 LD C,A ; transfer character to C. 4563 4564 RST _NEXT_CHAR ; advances. 4565 LD A,C ; character to A 4566 SUB $E1 ; subtract 'LPRINT' - lowest command. 4567 JR C,REPORT_C2 ; forward if less 4568 4569 LD C,A ; reduced token to C 4570 LD HL,offset_t ; set HL to address of offset table. 4571 ADD HL,BC ; index into offset table. 4572 LD C,(HL) ; fetch offset 4573 ADD HL,BC ; index into parameter table. 4574 JR GET_PARAM 4575 ; ___ 4576 4577 mark_0CF4: 4578 SCAN_LOOP: 4579 LD HL,(T_ADDR) ; 4580 4581 ; -> Entry Point to Scanning Loop 4582 4583 mark_0CF7: 4584 GET_PARAM: 4585 LD A,(HL) ; 4586 INC HL ; 4587 LD (T_ADDR),HL ; 4588 4589 LD BC,SCAN_LOOP 4590 PUSH BC ; is pushed on machine stack. 4591 4592 LD C,A ; 4593 CP ZX_QUOTE ; $0B 4594 JR NC,SEPARATOR 4595 4596 LD HL,class_tbl ; class_tbl - the address of the class table. 4597 LD B,$00 ; 4598 ADD HL,BC ; 4599 LD C,(HL) ; 4600 ADD HL,BC ; 4601 PUSH HL ; 4602 4603 RST _GET_CHAR 4604 RET ; indirect jump to class routine and 4605 ; by subsequent RET to SCAN_LOOP. 4606 4607 4608 ; THE 'SEPARATOR' ROUTINE 4609 4610 4611 mark_0D10: 4612 SEPARATOR: 4613 RST _GET_CHAR 4614 CP C ; 4615 JR NZ,REPORT_C2 4616 ; 'Nonsense in BASIC' 4617 4618 RST _NEXT_CHAR 4619 RET ; return 4620 4621 4622 4623 ; THE 'COMMAND CLASS' TABLE 4624 4625 ; 4626 mark_0D16: 4627 class_tbl: 4628 DEFB CLASS_0 - $ ; 17 offset to; Address: CLASS_0 4629 DEFB CLASS_1 - $ ; 25 offset to; Address: CLASS_1 4630 DEFB CLASS_2 - $ ; 53 offset to; Address: CLASS_2 4631 DEFB CLASS_3 - $ ; 0F offset to; Address: CLASS_3 4632 DEFB CLASS_4 - $ ; 6B offset to; Address: CLASS_4 4633 DEFB CLASS_5 - $ ; 13 offset to; Address: CLASS_5 4634 DEFB CLASS_6 - $ ; 76 offset to; Address: CLASS_6 4635 4636 4637 ; THE 'CHECK END' SUBROUTINE 4638 4639 ; Check for end of statement and that no spurious characters occur after 4640 ; a correctly parsed statement. Since only one statement is allowed on each 4641 ; line, the only character that may follow a statement is a NEWLINE. 4642 ; 4643 mark_0D1D: 4644 CHECK_END: 4645 CALL SYNTAX_Z 4646 RET NZ ; return in runtime. 4647 4648 POP BC ; else drop return address. 4649 4650 mark_0D22: 4651 CHECK_2: 4652 LD A,(HL) ; fetch character. 4653 CP ZX_NEWLINE ; compare to NEWLINE. 4654 RET Z ; return if so. 4655 4656 mark_0D26: 4657 REPORT_C2: 4658 JR REPORT_C 4659 ; 'Nonsense in BASIC' 4660 4661 4662 ; COMMAND CLASSES 03, 00, 05 4663 4664 4665 mark_0D28: 4666 CLASS_3: 4667 CP ZX_NEWLINE ; 4668 CALL NUMBER_TO_STK 4669 4670 mark_0D2D: 4671 CLASS_0: 4672 CP A ; 4673 4674 mark_0D2E: 4675 CLASS_5: 4676 POP BC ; 4677 CALL Z,CHECK_END 4678 EX DE,HL ; 4679 LD HL,(T_ADDR) ; 4680 LD C,(HL) ; 4681 INC HL ; 4682 LD B,(HL) ; 4683 EX DE,HL ; 4684 4685 mark_0D3A: 4686 CLASS_END: 4687 PUSH BC ; 4688 RET ; 4689 4690 4691 ; COMMAND CLASSES 01, 02, 04, 06 4692 4693 4694 mark_0D3C: 4695 CLASS_1: 4696 CALL LOOK_VARS 4697 4698 mark_0D3F: 4699 CLASS_4_2: 4700 LD (IY+FLAGX-RAMBASE),$00 4701 JR NC,SET_STK 4702 4703 SET 1,(IY+FLAGX-RAMBASE) 4704 JR NZ,SET_STRLN 4705 4706 4707 mark_0D4B: 4708 REPORT_2: 4709 RST _ERROR_1 4710 DEFB $01 ; Error Report: Variable not found 4711 ; ___ 4712 4713 mark_0D4D: 4714 SET_STK: 4715 CALL Z,STK_VAR 4716 BIT 6,(IY+FLAGS-RAMBASE) ; Numeric or string result? 4717 JR NZ,SET_STRLN 4718 4719 XOR A ; 4720 CALL SYNTAX_Z 4721 CALL NZ,STK_FETCH 4722 LD HL,FLAGX ; 4723 OR (HL) ; 4724 LD (HL),A ; 4725 EX DE,HL ; 4726 4727 mark_0D63: 4728 SET_STRLN: 4729 LD (STRLEN),BC ; 4730 LD (DEST),HL ; 4731 4732 ; THE 'REM' COMMAND ROUTINE 4733 4734 mark_0D6A: 4735 REM: 4736 RET ; 4737 4738 ; ___ 4739 4740 mark_0D6B: 4741 CLASS_2: 4742 POP BC ; 4743 LD A,(FLAGS) ; sv 4744 4745 mark_0D6F: 4746 INPUT_REP: 4747 PUSH AF ; 4748 CALL SCANNING 4749 POP AF ; 4750 LD BC,LET ; Address: LET 4751 LD D,(IY+FLAGS-RAMBASE) 4752 XOR D ; 4753 AND $40 ; 4754 JR NZ,REPORT_C ; to REPORT_C 4755 4756 BIT 7,D ; 4757 JR NZ,CLASS_END ; to CLASS_END 4758 4759 JR CHECK_2 ; to CHECK_2 4760 ; ___ 4761 4762 mark_0D85: 4763 CLASS_4: 4764 CALL LOOK_VARS 4765 PUSH AF ; 4766 LD A,C ; 4767 OR $9F ; 4768 INC A ; 4769 JR NZ,REPORT_C ; to REPORT_C 4770 4771 POP AF ; 4772 JR CLASS_4_2 ; to CLASS_4_2 4773 4774 ; ___ 4775 4776 mark_0D92: 4777 CLASS_6: 4778 CALL SCANNING 4779 BIT 6,(IY+FLAGS-RAMBASE) ; Numeric or string result? 4780 RET NZ ; 4781 4782 4783 mark_0D9A: 4784 REPORT_C: 4785 RST _ERROR_1 4786 DEFB $0B ; Error Report: Nonsense in BASIC 4787 4788 4789 ; THE 'NUMBER TO STACK' SUBROUTINE 4790 4791 ; 4792 ; 4793 4794 mark_0D9C: 4795 NUMBER_TO_STK: 4796 JR NZ,CLASS_6 ; back to CLASS_6 with a non-zero number. 4797 4798 CALL SYNTAX_Z 4799 RET Z ; return if checking syntax. 4800 4801 ; in runtime a zero default is placed on the calculator stack. 4802 4803 RST _FP_CALC ;; 4804 DEFB __stk_zero ;; 4805 DEFB __end_calc ;; 4806 4807 RET ; return. 4808 4809 4810 ; THE 'SYNTAX_Z' SUBROUTINE 4811 4812 ; This routine returns with zero flag set if checking syntax. 4813 ; Calling this routine uses three instruction bytes compared to four if the 4814 ; bit test is implemented inline. 4815 4816 mark_0DA6: 4817 SYNTAX_Z: 4818 BIT 7,(IY+FLAGS-RAMBASE) ; checking syntax only? 4819 RET ; return. 4820 4821 4822 ; THE 'IF' COMMAND ROUTINE 4823 4824 ; In runtime, the class routines have evaluated the test expression and 4825 ; the result, true or false, is on the stack. 4826 4827 mark_0DAB: 4828 IF: 4829 CALL SYNTAX_Z 4830 JR Z,IF_END ; forward if checking syntax 4831 4832 ; else delete the Boolean value on the calculator stack. 4833 4834 RST _FP_CALC ;; 4835 DEFB __delete ;; 4836 DEFB __end_calc ;; 4837 4838 ; register DE points to exponent of floating point value. 4839 4840 LD A,(DE) ; fetch exponent. 4841 AND A ; test for zero - FALSE. 4842 RET Z ; return if so. 4843 4844 mark_0DB6: 4845 IF_END: 4846 JP LINE_NULL ; jump back 4847 4848 4849 ; THE 'FOR' COMMAND ROUTINE 4850 4851 ; 4852 ; 4853 4854 mark_0DB9: 4855 FOR: 4856 CP ZX_STEP ; is current character 'STEP' ? 4857 JR NZ,F_USE_ONE ; forward if not 4858 4859 4860 RST _NEXT_CHAR 4861 CALL CLASS_6 ; stacks the number 4862 CALL CHECK_END 4863 JR F_REORDER ; forward to F_REORDER 4864 ; ___ 4865 4866 mark_0DC6: 4867 F_USE_ONE: 4868 CALL CHECK_END 4869 4870 RST _FP_CALC ;; 4871 DEFB __stk_one ;; 4872 DEFB __end_calc ;; 4873 4874 4875 4876 mark_0DCC: 4877 F_REORDER: 4878 RST _FP_CALC ;; v, l, s. 4879 DEFB __st_mem_0 ;; v, l, s. 4880 DEFB __delete ;; v, l. 4881 DEFB __exchange ;; l, v. 4882 DEFB __get_mem_0 ;; l, v, s. 4883 DEFB __exchange ;; l, s, v. 4884 DEFB __end_calc ;; l, s, v. 4885 4886 CALL LET 4887 4888 LD (MEM),HL ; set MEM to address variable. 4889 DEC HL ; point to letter. 4890 LD A,(HL) ; 4891 SET 7,(HL) ; 4892 LD BC,$0006 ; 4893 ADD HL,BC ; 4894 RLCA ; 4895 JR C,F_LMT_STP 4896 4897 SLA C ; 4898 CALL MAKE_ROOM 4899 INC HL ; 4900 4901 mark_0DEA: 4902 F_LMT_STP: 4903 PUSH HL ; 4904 4905 RST _FP_CALC ;; 4906 DEFB __delete ;; 4907 DEFB __delete ;; 4908 DEFB __end_calc ;; 4909 4910 POP HL ; 4911 EX DE,HL ; 4912 4913 LD C,$0A ; ten bytes to be moved. 4914 LDIR ; copy bytes 4915 4916 LD HL,(PPC) ; set HL to system variable PPC current line. 4917 EX DE,HL ; transfer to DE, variable pointer to HL. 4918 INC DE ; loop start will be this line + 1 at least. 4919 LD (HL),E ; 4920 INC HL ; 4921 LD (HL),D ; 4922 CALL NEXT_LOOP ; considers an initial pass. 4923 RET NC ; return if possible. 4924 4925 ; else program continues from point following matching NEXT. 4926 4927 BIT 7,(IY+PPC_hi-RAMBASE) 4928 RET NZ ; return if over 32767 ??? 4929 4930 LD B,(IY+STRLEN_lo-RAMBASE) ; fetch variable name from STRLEN_lo 4931 RES 6,B ; make a true letter. 4932 LD HL,(NXTLIN) ; set HL from NXTLIN 4933 4934 ; now enter a loop to look for matching next. 4935 4936 mark_0E0E: 4937 NXTLIN_NO: 4938 LD A,(HL) ; fetch high byte of line number. 4939 AND $C0 ; mask off low bits $3F 4940 JR NZ,FOR_END ; forward at end of program 4941 4942 PUSH BC ; save letter 4943 CALL NEXT_ONE ; finds next line. 4944 POP BC ; restore letter 4945 4946 INC HL ; step past low byte 4947 INC HL ; past the 4948 INC HL ; line length. 4949 CALL TEMP_PTR1 ; sets CH_ADD 4950 4951 RST _GET_CHAR 4952 CP ZX_NEXT ; 4953 EX DE,HL ; next line to HL. 4954 JR NZ,NXTLIN_NO ; back with no match 4955 4956 ; 4957 4958 EX DE,HL ; restore pointer. 4959 4960 RST _NEXT_CHAR ; advances and gets letter in A. 4961 EX DE,HL ; save pointer 4962 CP B ; compare to variable name. 4963 JR NZ,NXTLIN_NO ; back with mismatch 4964 4965 mark_0E2A: 4966 FOR_END: 4967 LD (NXTLIN),HL ; update system variable NXTLIN 4968 RET ; return. 4969 4970 4971 ; THE 'NEXT' COMMAND ROUTINE 4972 4973 ; 4974 ; 4975 4976 mark_0E2E: 4977 NEXT: 4978 BIT 1,(IY+FLAGX-RAMBASE) 4979 JP NZ,REPORT_2 4980 4981 LD HL,(DEST) 4982 BIT 7,(HL) 4983 JR Z,REPORT_1 4984 4985 INC HL ; 4986 LD (MEM),HL ; 4987 4988 RST _FP_CALC ;; 4989 DEFB __get_mem_0 ;; 4990 DEFB __get_mem_2 ;; 4991 DEFB __addition ;; 4992 DEFB __st_mem_0 ;; 4993 DEFB __delete ;; 4994 DEFB __end_calc ;; 4995 4996 CALL NEXT_LOOP 4997 RET C ; 4998 4999 LD HL,(MEM) ; 5000 LD DE,$000F ; 5001 ADD HL,DE ; 5002 LD E,(HL) ; 5003 INC HL ; 5004 LD D,(HL) ; 5005 EX DE,HL ; 5006 JR GOTO_2 5007 ; ___ 5008 5009 mark_0E58: 5010 REPORT_1: 5011 RST _ERROR_1 5012 DEFB $00 ; Error Report: NEXT without FOR 5013 5014 5015 5016 ; THE 'NEXT_LOOP' SUBROUTINE 5017 5018 ; 5019 ; 5020 5021 mark_0E5A: 5022 NEXT_LOOP: 5023 RST _FP_CALC ;; 5024 DEFB __get_mem_1 ;; 5025 DEFB __get_mem_0 ;; 5026 DEFB __get_mem_2 ;; 5027 DEFB __less_0 ;; 5028 DEFB __jump_true ;; 5029 DEFB LMT_V_VAL - $ ;; 5030 5031 DEFB __exchange ;; 5032 5033 mark_0E62: 5034 LMT_V_VAL: 5035 DEFB __subtract ;; 5036 DEFB __greater_0 ;; 5037 DEFB __jump_true ;; 5038 DEFB IMPOSS - $ ;; 5039 5040 DEFB __end_calc ;; 5041 5042 AND A ; clear carry flag 5043 RET ; return. 5044 ; ___ 5045 5046 mark_0E69: 5047 IMPOSS: 5048 DEFB __end_calc ;; 5049 5050 SCF ; set carry flag 5051 RET ; return. 5052 5053 5054 ; THE 'RAND' COMMAND ROUTINE 5055 5056 ; The keyword was 'RANDOMISE' on the ZX80, is 'RAND' here on the ZX81 and 5057 ; becomes 'RANDOMIZE' on the ZX Spectrum. 5058 ; In all invocations the procedure is the same - to set the SEED system variable 5059 ; with a supplied integer value or to use a time-based value if no number, or 5060 ; zero, is supplied. 5061 5062 mark_0E6C: 5063 RAND: 5064 CALL FIND_INT 5065 LD A,B ; test value 5066 OR C ; for zero 5067 JR NZ,SET_SEED ; forward if not zero 5068 5069 LD BC,(FRAMES) ; fetch value of FRAMES system variable. 5070 5071 mark_0E77: 5072 SET_SEED: 5073 LD (SEED),BC ; update the SEED system variable. 5074 RET ; return. 5075 5076 5077 ; THE 'CONT' COMMAND ROUTINE 5078 5079 ; Another abbreviated command. ROM space was really tight. 5080 ; CONTINUE at the line number that was set when break was pressed. 5081 ; Sometimes the current line, sometimes the next line. 5082 5083 mark_0E7C: 5084 CONT: 5085 LD HL,(OLDPPC) ; set HL from system variable OLDPPC 5086 JR GOTO_2 ; forward 5087 5088 5089 ; THE 'GOTO' COMMAND ROUTINE 5090 5091 ; This token also suffered from the shortage of room and there is no space 5092 ; getween GO and TO as there is on the ZX80 and ZX Spectrum. The same also 5093 ; applies to the GOSUB keyword. 5094 5095 mark_0E81: 5096 GOTO: 5097 CALL FIND_INT 5098 LD H,B ; 5099 LD L,C ; 5100 5101 mark_0E86: 5102 GOTO_2: 5103 LD A,H ; 5104 CP $F0 ; ZX_LIST ??? 5105 JR NC,REPORT_B 5106 5107 CALL LINE_ADDR 5108 LD (NXTLIN),HL ; sv 5109 RET ; 5110 5111 5112 ; THE 'POKE' COMMAND ROUTINE 5113 5114 5115 mark_0E92: 5116 POKE: 5117 CALL FP_TO_A 5118 JR C,REPORT_B ; forward, with overflow 5119 5120 JR Z,POKE_SAVE ; forward, if positive 5121 5122 NEG ; negate 5123 5124 mark_0E9B: 5125 POKE_SAVE: 5126 PUSH AF ; preserve value. 5127 CALL FIND_INT ; gets address in BC 5128 ; invoking the error routine with overflow 5129 ; or a negative number. 5130 POP AF ; restore value. 5131 5132 ; Note. the next two instructions are legacy code from the ZX80 and 5133 ; inappropriate here. 5134 5135 BIT 7,(IY+ERR_NR-RAMBASE) ; test ERR_NR - is it still $FF ? 5136 RET Z ; return with error. 5137 5138 LD (BC),A ; update the address contents. 5139 RET ; return. 5140 5141 5142 ; THE 'FIND INTEGER' SUBROUTINE 5143 5144 5145 mark_0EA7: 5146 FIND_INT: 5147 CALL FP_TO_BC 5148 JR C,REPORT_B ; forward with overflow 5149 5150 RET Z ; return if positive (0-65535). 5151 5152 5153 mark_0EAD: 5154 REPORT_B: 5155 RST _ERROR_1 5156 DEFB $0A ; Error Report: Integer out of range 5157 ; 5158 ; Seems stupid, $0A is 10 but the ERROR_CODE_INTEGER_OUT_OF_RANGE is 11 5159 ; maybe gets incremented ??? 5160 5161 5162 ; THE 'RUN' COMMAND ROUTINE 5163 5164 5165 mark_0EAF: 5166 RUN: 5167 CALL GOTO 5168 JP CLEAR 5169 5170 5171 ; THE 'GOSUB' COMMAND ROUTINE 5172 5173 5174 mark_0EB5: 5175 GOSUB: 5176 LD HL,(PPC) ; 5177 INC HL ; 5178 EX (SP),HL ; 5179 PUSH HL ; 5180 LD (ERR_SP),SP ; set the error stack pointer - ERR_SP 5181 CALL GOTO 5182 LD BC,6 ; 5183 5184 5185 ; THE 'TEST ROOM' SUBROUTINE 5186 5187 ; 5188 ; checks ther is room for 36 bytes on the stack 5189 ; 5190 mark_0EC5: 5191 TEST_ROOM: 5192 LD HL,(STKEND) ; 5193 ADD HL,BC ; HL = STKEND + BC 5194 JR C,REPORT_4 5195 5196 EX DE,HL ; DE = STKEND + BC 5197 LD HL,$0024 ; 36 decimal 5198 ADD HL,DE ; HL = 36 + STKEND + BC 5199 SBC HL,SP ; HL = 36 + STKEND + BC - SP 5200 RET C ; 5201 5202 mark_0ED3: 5203 REPORT_4: 5204 LD L,3 ; 5205 JP ERROR_3 5206 5207 5208 ; THE 'RETURN' COMMAND ROUTINE 5209 5210 5211 mark_0ED8: 5212 RETURN: 5213 POP HL ; 5214 EX (SP),HL ; 5215 LD A,H ; 5216 CP $3E ; 5217 JR Z,REPORT_7 5218 5219 LD (ERR_SP),SP ; 5220 JR GOTO_2 ; back 5221 ; ___ 5222 5223 mark_0EE5: 5224 REPORT_7: 5225 EX (SP),HL ; 5226 PUSH HL ; 5227 5228 RST _ERROR_1 5229 DEFB 6 ; Error Report: RETURN without GOSUB 5230 5231 ; 5232 ; Contradicts BASIC manual: 5233 ; 7 is ERROR_CODE_RETURN_WITHOUT_GOSUB 5234 ; 6 is ERROR_CODE_ARITHMETIC_OVERFLOW 5235 ; 5236 5237 5238 ; THE 'INPUT' COMMAND ROUTINE 5239 5240 5241 mark_0EE9: 5242 INPUT: 5243 BIT 7,(IY+PPC_hi-RAMBASE) 5244 JR NZ,REPORT_8 ; to REPORT_8 5245 5246 CALL X_TEMP 5247 LD HL,FLAGX ; 5248 SET 5,(HL) ; 5249 RES 6,(HL) ; 5250 LD A,(FLAGS) ; 5251 AND $40 ; 64 5252 LD BC,2 ; 5253 JR NZ,PROMPT ; to PROMPT 5254 5255 LD C,$04 ; 5256 5257 mark_0F05: 5258 PROMPT: 5259 OR (HL) ; 5260 LD (HL),A ; 5261 5262 RST _BC_SPACES 5263 LD (HL),ZX_NEWLINE 5264 LD A,C ; 5265 RRCA ; 5266 RRCA ; 5267 JR C,ENTER_CUR 5268 5269 LD A,$0B ; ZX_QUOTE ??? 5270 LD (DE),A ; 5271 DEC HL ; 5272 LD (HL),A ; 5273 5274 mark_0F14: 5275 ENTER_CUR: 5276 DEC HL ; 5277 LD (HL),ZX_CURSOR ; 5278 LD HL,(S_POSN) ; 5279 LD (T_ADDR),HL ; 5280 POP HL ; 5281 JP LOWER 5282 5283 ; ___ 5284 5285 mark_0F21: 5286 REPORT_8: 5287 RST _ERROR_1 5288 DEFB 7 ; Error Report: End of file 5289 5290 5291 ; THE 'PAUSE' COMMAND ROUTINE 5292 5293 5294 mark_0F23: 5295 FAST: 5296 CALL SET_FAST 5297 RES 6,(IY+CDFLAG-RAMBASE) 5298 RET ; return. 5299 5300 5301 ; THE 'SLOW' COMMAND ROUTINE 5302 5303 5304 mark_0F2B: 5305 SLOW: 5306 SET 6,(IY+CDFLAG-RAMBASE) 5307 JP SLOW_FAST 5308 5309 5310 ; THE 'PAUSE' COMMAND ROUTINE 5311 5312 5313 mark_0F32: 5314 PAUSE: 5315 CALL FIND_INT 5316 CALL SET_FAST 5317 LD H,B ; 5318 LD L,C ; 5319 CALL DISPLAY_P 5320 5321 LD (IY+FRAMES_hi-RAMBASE),$FF 5322 5323 CALL SLOW_FAST 5324 JR DEBOUNCE 5325 5326 5327 ; THE 'BREAK' SUBROUTINE 5328 5329 5330 mark_0F46: 5331 BREAK_1: 5332 LD A,$7F ; read port $7FFE - keys B,N,M,.,SPACE. 5333 IN A,(IO_PORT_KEYBOARD_RD) ; 5334 RRA ; carry will be set if space not pressed. 5335 5336 5337 ; THE 'DEBOUNCE' SUBROUTINE 5338 5339 5340 mark_0F4B: 5341 DEBOUNCE: 5342 RES 0,(IY+CDFLAG-RAMBASE) ; update 5343 LD A,$FF ; 5344 LD (DEBOUNCE_VAR),A ; update 5345 RET ; return. 5346 5347 5348 5349 ; THE 'SCANNING' SUBROUTINE 5350 5351 ; This recursive routine is where the ZX81 gets its power. 5352 ; Provided there is enough memory it can evaluate 5353 ; an expression of unlimited complexity. 5354 ; Note. there is no unary plus so, as on the ZX80, PRINT +1 gives a syntax error. 5355 ; PRINT +1 works on the Spectrum but so too does PRINT + "STRING". 5356 5357 mark_0F55: 5358 SCANNING: 5359 RST _GET_CHAR 5360 LD B,0 ; set B register to zero. 5361 PUSH BC ; stack zero as a priority end-marker. 5362 5363 mark_0F59: 5364 S_LOOP_1: 5365 CP ZX_RND 5366 JR NZ,S_TEST_PI ; forward, if not, to S_TEST_PI 5367 5368 5369 ; THE 'RND' FUNCTION 5370 5371 RND: 5372 5373 CALL SYNTAX_Z 5374 JR Z,S_JPI_END ; forward if checking syntax to S_JPI_END 5375 5376 LD BC,(SEED) ; sv 5377 CALL STACK_BC 5378 5379 RST _FP_CALC ;; 5380 DEFB __stk_one ;; 5381 DEFB __addition ;; 5382 DEFB __stk_data ;; 5383 DEFB $37 ;;Exponent: $87, Bytes: 1 5384 DEFB $16 ;;(+00,+00,+00) 5385 DEFB __multiply ;; 5386 DEFB __stk_data ;; 5387 DEFB $80 ;;Bytes: 3 5388 DEFB $41 ;;Exponent $91 5389 DEFB $00,$00,$80 ;;(+00) 5390 DEFB __n_mod_m ;; 5391 DEFB __delete ;; 5392 DEFB __stk_one ;; 5393 DEFB __subtract ;; 5394 DEFB __duplicate ;; 5395 DEFB __end_calc ;; 5396 5397 CALL FP_TO_BC 5398 LD (SEED),BC ; update the SEED system variable. 5399 LD A,(HL) ; HL addresses the exponent of the last value. 5400 AND A ; test for zero 5401 JR Z,S_JPI_END ; forward, if so 5402 5403 SUB $10 ; else reduce exponent by sixteen 5404 LD (HL),A ; thus dividing by 65536 for last value. 5405 5406 mark_0F8A: 5407 S_JPI_END: 5408 JR S_PI_END ; forward 5409 5410 ; ___ 5411 5412 mark_0F8C: 5413 S_TEST_PI: 5414 CP ZX_PI ; the 'PI' character 5415 JR NZ,S_TST_INK ; forward, if not 5416 5417 5418 ; THE 'PI' EVALUATION 5419 5420 5421 CALL SYNTAX_Z 5422 JR Z,S_PI_END ; forward if checking syntax 5423 5424 5425 RST _FP_CALC ;; 5426 DEFB __stk_half_pi ;; 5427 DEFB __end_calc ;; 5428 5429 INC (HL) ; double the exponent giving PI on the stack. 5430 5431 mark_0F99: 5432 S_PI_END: 5433 RST _NEXT_CHAR ; advances character pointer. 5434 5435 JP S_NUMERIC ; jump forward to set the flag 5436 ; to signal numeric result before advancing. 5437 5438 ; ___ 5439 5440 mark_0F9D: 5441 S_TST_INK: 5442 CP ZX_INKEY_STR ; 5443 JR NZ,S_ALPHANUM ; forward, if not 5444 5445 5446 ; THE 'INKEY$' EVALUATION 5447 5448 5449 CALL KEYBOARD 5450 LD B,H ; 5451 LD C,L ; 5452 LD D,C ; 5453 INC D ; 5454 CALL NZ,DECODE 5455 LD A,D ; 5456 ADC A,D ; 5457 LD B,D ; 5458 LD C,A ; 5459 EX DE,HL ; 5460 JR S_STRING ; forward 5461 5462 ; ___ 5463 5464 mark_0FB2: 5465 S_ALPHANUM: 5466 CALL ALPHANUM 5467 JR C,S_LTR_DGT ; forward, if alphanumeric 5468 5469 CP ZX_PERIOD ; is character a '.' ? 5470 JP Z,S_DECIMAL ; jump forward if so 5471 5472 LD BC,$09D8 ; prepare priority 09, operation 'subtract' 5473 CP ZX_MINUS ; is character unary minus '-' ? 5474 JR Z,S_PUSH_PO ; forward, if so 5475 5476 CP ZX_BRACKET_LEFT ; is character a '(' ? 5477 JR NZ,S_QUOTE ; forward if not 5478 5479 CALL CH_ADD_PLUS_1 ; advances character pointer. 5480 5481 CALL SCANNING ; recursively call to evaluate the sub_expression. 5482 5483 CP ZX_BRACKET_RIGHT; is subsequent character a ')' ? 5484 JR NZ,S_RPT_C ; forward if not 5485 5486 5487 CALL CH_ADD_PLUS_1 ; advances. 5488 JR S_J_CONT_3 ; relative jump to S_JP_CONT3 and then S_CONT3 5489 5490 ; ___ 5491 5492 ; consider a quoted string e.g. PRINT "Hooray!" 5493 ; Note. quotes are not allowed within a string. 5494 5495 mark_0FD6: 5496 S_QUOTE: 5497 CP ZX_QUOTE ; is character a quote (") ? 5498 JR NZ,S_FUNCTION ; forward, if not 5499 5500 CALL CH_ADD_PLUS_1 ; advances 5501 PUSH HL ; * save start of string. 5502 JR S_QUOTE_S ; forward 5503 5504 ; ___ 5505 5506 5507 mark_0FE0: 5508 S_Q_AGAIN: 5509 CALL CH_ADD_PLUS_1 5510 5511 mark_0FE3: 5512 S_QUOTE_S: 5513 CP ZX_QUOTE ; is character a '"' ? 5514 JR NZ,S_Q_NL ; forward if not to S_Q_NL 5515 5516 POP DE ; * retrieve start of string 5517 AND A ; prepare to subtract. 5518 SBC HL,DE ; subtract start from current position. 5519 LD B,H ; transfer this length 5520 LD C,L ; to the BC register pair. 5521 5522 mark_0FED: 5523 S_STRING: 5524 LD HL,FLAGS ; address system variable FLAGS 5525 RES 6,(HL) ; signal string result 5526 BIT 7,(HL) ; test if checking syntax. 5527 5528 CALL NZ,STK_STO_STR ; in run-time stacks the 5529 ; string descriptor - start DE, length BC. 5530 5531 RST _NEXT_CHAR ; advances pointer. 5532 5533 mark_0FF8: 5534 S_J_CONT_3: 5535 JP S_CONT_3 5536 5537 ; ___ 5538 5539 ; A string with no terminating quote has to be considered. 5540 5541 mark_0FFB: 5542 S_Q_NL: 5543 CP ZX_NEWLINE 5544 JR NZ,S_Q_AGAIN ; loop back if not 5545 5546 mark_0FFF: 5547 S_RPT_C: 5548 JP REPORT_C 5549 ; ___ 5550 5551 mark_1002: 5552 S_FUNCTION: 5553 SUB $C4 ; subtract 'CODE' reducing codes 5554 ; CODE thru '<>' to range $00 - $XX 5555 JR C,S_RPT_C ; back, if less 5556 5557 ; test for NOT the last function in character set. 5558 5559 LD BC,$04EC ; prepare priority $04, operation 'not' 5560 CP $13 ; compare to 'NOT' ( - CODE) 5561 JR Z,S_PUSH_PO ; forward, if so 5562 5563 JR NC,S_RPT_C ; back with anything higher 5564 5565 ; else is a function 'CODE' thru 'CHR$' 5566 5567 LD B,$10 ; priority sixteen binds all functions to 5568 ; arguments removing the need for brackets. 5569 5570 ADD A,$D9 ; add $D9 to give range $D9 thru $EB 5571 ; bit 6 is set to show numeric argument. 5572 ; bit 7 is set to show numeric result. 5573 5574 ; now adjust these default argument/result indicators. 5575 5576 LD C,A ; save code in C 5577 5578 CP $DC ; separate 'CODE', 'VAL', 'LEN' 5579 JR NC,S_NUMBER_TO_STRING ; skip forward if string operand 5580 5581 RES 6,C ; signal string operand. 5582 5583 mark_101A: 5584 S_NUMBER_TO_STRING: 5585 CP $EA ; isolate top of range 'STR$' and 'CHR$' 5586 JR C,S_PUSH_PO ; skip forward with others 5587 5588 RES 7,C ; signal string result. 5589 5590 mark_1020: 5591 S_PUSH_PO: 5592 PUSH BC ; push the priority/operation 5593 5594 RST _NEXT_CHAR 5595 JP S_LOOP_1 ; jump back 5596 ; ___ 5597 5598 mark_1025: 5599 S_LTR_DGT: 5600 CP ZX_A ; compare to 'A'. 5601 JR C,S_DECIMAL ; forward if less to S_DECIMAL 5602 5603 CALL LOOK_VARS 5604 JP C,REPORT_2 ; back if not found 5605 ; a variable is always 'found' when checking 5606 ; syntax. 5607 5608 CALL Z,STK_VAR ; stacks string parameters or 5609 ; returns cell location if numeric. 5610 5611 LD A,(FLAGS) ; fetch FLAGS 5612 CP $C0 ; compare to numeric result/numeric operand 5613 JR C,S_CONT_2 ; forward if not numeric 5614 5615 INC HL ; address numeric contents of variable. 5616 LD DE,(STKEND) ; set destination to STKEND 5617 CALL MOVE_FP ; stacks the five bytes 5618 EX DE,HL ; transfer new free location from DE to HL. 5619 LD (STKEND),HL ; update STKEND system variable. 5620 JR S_CONT_2 ; forward 5621 ; ___ 5622 5623 ; The Scanning Decimal routine is invoked when a decimal point or digit is 5624 ; found in the expression. 5625 ; When checking syntax, then the 'hidden floating point' form is placed 5626 ; after the number in the BASIC line. 5627 ; In run-time, the digits are skipped and the floating point number is picked 5628 ; up. 5629 5630 mark_1047: 5631 S_DECIMAL: 5632 CALL SYNTAX_Z 5633 JR NZ,S_STK_DEC ; forward in run-time 5634 5635 CALL DEC_TO_FP 5636 5637 RST _GET_CHAR ; advances HL past digits 5638 LD BC,$0006 ; six locations are required. 5639 CALL MAKE_ROOM 5640 INC HL ; point to first new location 5641 LD (HL),$7E ; insert the number marker 126 decimal. 5642 INC HL ; increment 5643 EX DE,HL ; transfer destination to DE. 5644 LD HL,(STKEND) ; set HL from STKEND which points to the 5645 ; first location after the 'last value' 5646 LD C,$05 ; five bytes to move. 5647 AND A ; clear carry. 5648 SBC HL,BC ; subtract five pointing to 'last value'. 5649 LD (STKEND),HL ; update STKEND thereby 'deleting the value. 5650 5651 LDIR ; copy the five value bytes. 5652 5653 EX DE,HL ; basic pointer to HL which may be white-space 5654 ; following the number. 5655 DEC HL ; now points to last of five bytes. 5656 CALL TEMP_PTR1 ; advances the character 5657 ; address skipping any white-space. 5658 JR S_NUMERIC ; forward 5659 ; to signal a numeric result. 5660 ; ___ 5661 ; In run-time the branch is here when a digit or point is encountered. 5662 5663 mark_106F: 5664 S_STK_DEC: 5665 RST _NEXT_CHAR 5666 CP $7E ; compare to 'number marker' 5667 JR NZ,S_STK_DEC ; loop back until found 5668 ; skipping all the digits. 5669 5670 INC HL ; point to first of five hidden bytes. 5671 LD DE,(STKEND) ; set destination from STKEND system variable 5672 CALL MOVE_FP ; stacks the number. 5673 LD (STKEND),DE ; update system variable STKEND. 5674 LD (CH_ADD),HL ; update system variable CH_ADD. 5675 5676 mark_1083: 5677 S_NUMERIC: 5678 SET 6,(IY+FLAGS-RAMBASE) ; Signal numeric result 5679 5680 mark_1087: 5681 S_CONT_2: 5682 RST _GET_CHAR 5683 5684 mark_1088: 5685 S_CONT_3: 5686 CP ZX_BRACKET_LEFT ; compare to opening bracket '(' 5687 JR NZ,S_OPERTR ; forward if not 5688 5689 BIT 6,(IY+FLAGS-RAMBASE) ; Numeric or string result? 5690 JR NZ,S_LOOP ; forward if numeric 5691 5692 ; else is a string 5693 5694 CALL SLICING 5695 5696 RST _NEXT_CHAR 5697 JR S_CONT_3 ; back 5698 ; ___ 5699 ; the character is now manipulated to form an equivalent in the table of 5700 ; calculator literals. This is quite cumbersome and in the ZX Spectrum a 5701 ; simple look-up table was introduced at this point. 5702 5703 mark_1098: 5704 S_OPERTR: 5705 LD BC,$00C3 ; prepare operator 'subtract' as default. 5706 ; also set B to zero for later indexing. 5707 5708 CP ZX_GREATER_THAN ; is character '>' ? 5709 JR C,S_LOOP ; forward if less, as 5710 ; we have reached end of meaningful expression 5711 5712 SUB ZX_MINUS ; is character '-' ? 5713 JR NC,SUBMLTDIV ; forward with - * / and '**' '<>' 5714 5715 ADD A,13 ; increase others by thirteen 5716 ; $09 '>' thru $0C '+' 5717 JR GET_PRIO ; forward 5718 5719 ; ___ 5720 5721 mark_10A7: 5722 SUBMLTDIV: 5723 CP $03 ; isolate $00 '-', $01 '*', $02 '/' 5724 JR C,GET_PRIO ; forward if so 5725 5726 ; else possibly originally $D8 '**' thru $DD '<>' already reduced by $16 5727 5728 SUB $C2 ; giving range $00 to $05 5729 JR C,S_LOOP ; forward if less 5730 5731 CP $06 ; test the upper limit for nonsense also 5732 JR NC,S_LOOP ; forward if so 5733 5734 ADD A,$03 ; increase by 3 to give combined operators of 5735 5736 ; $00 '-' 5737 ; $01 '*' 5738 ; $02 '/' 5739 5740 ; $03 '**' 5741 ; $04 'OR' 5742 ; $05 'AND' 5743 ; $06 '<=' 5744 ; $07 '>=' 5745 ; $08 '<>' 5746 5747 ; $09 '>' 5748 ; $0A '<' 5749 ; $0B '=' 5750 ; $0C '+' 5751 5752 mark_10B5: 5753 GET_PRIO: 5754 ADD A,C ; add to default operation 'sub' ($C3) 5755 LD C,A ; and place in operator byte - C. 5756 5757 LD HL,tbl_pri - $C3 ; theoretical base of the priorities table. 5758 ADD HL,BC ; add C ( B is zero) 5759 LD B,(HL) ; pick up the priority in B 5760 5761 mark_10BC: 5762 S_LOOP: 5763 POP DE ; restore previous 5764 LD A,D ; load A with priority. 5765 CP B ; is present priority higher 5766 JR C,S_TIGHTER ; forward if so to S_TIGHTER 5767 5768 AND A ; are both priorities zero 5769 JP Z,GET_CHAR ; exit if zero via GET_CHAR 5770 5771 PUSH BC ; stack present values 5772 PUSH DE ; stack last values 5773 CALL SYNTAX_Z 5774 JR Z,S_SYNTEST ; forward is checking syntax 5775 5776 LD A,E ; fetch last operation 5777 AND $3F ; mask off the indicator bits to give true 5778 ; calculator literal. 5779 LD B,A ; place in the B register for BERG 5780 5781 ; perform the single operation 5782 5783 RST _FP_CALC ;; 5784 DEFB __fp_calc_2 ;; 5785 DEFB __end_calc ;; 5786 5787 JR S_RUNTEST ; forward 5788 5789 ; ___ 5790 5791 mark_10D5: 5792 S_SYNTEST: 5793 LD A,E ; transfer masked operator to A 5794 XOR (IY+FLAGS-RAMBASE) ; XOR with FLAGS like results will reset bit 6 5795 AND $40 ; test bit 6 5796 5797 mark_10DB: 5798 S_RPORT_C: 5799 JP NZ,REPORT_C ; back if results do not agree. 5800 5801 ; ___ 5802 5803 ; in run-time impose bit 7 of the operator onto bit 6 of the FLAGS 5804 5805 mark_10DE: 5806 S_RUNTEST: 5807 POP DE ; restore last operation. 5808 LD HL,FLAGS ; address system variable FLAGS 5809 SET 6,(HL) ; presume a numeric result 5810 BIT 7,E ; test expected result in operation 5811 JR NZ,S_LOOPEND ; forward if numeric 5812 5813 RES 6,(HL) ; reset to signal string result 5814 5815 mark_10EA: 5816 S_LOOPEND: 5817 POP BC ; restore present values 5818 JR S_LOOP ; back 5819 5820 ; ___ 5821 5822 mark_10ED: 5823 S_TIGHTER: 5824 PUSH DE ; push last values and consider these 5825 5826 LD A,C ; get the present operator. 5827 BIT 6,(IY+FLAGS-RAMBASE) ; Numeric or string result? 5828 JR NZ,S_NEXT ; forward if numeric to S_NEXT 5829 5830 AND $3F ; strip indicator bits to give clear literal. 5831 ADD A,$08 ; add eight - augmenting numeric to equivalent 5832 ; string literals. 5833 LD C,A ; place plain literal back in C. 5834 CP $10 ; compare to 'AND' 5835 JR NZ,S_NOT_AND ; forward if not 5836 5837 SET 6,C ; set the numeric operand required for 'AND' 5838 JR S_NEXT ; forward to S_NEXT 5839 5840 ; ___ 5841 5842 mark_1102: 5843 S_NOT_AND: 5844 JR C,S_RPORT_C ; back if less than 'AND' 5845 ; Nonsense if '-', '*' etc. 5846 5847 CP __strs_add ; compare to 'strs_add' literal 5848 JR Z,S_NEXT ; forward if so signaling string result 5849 5850 SET 7,C ; set bit to numeric (Boolean) for others. 5851 5852 mark_110A: 5853 S_NEXT: 5854 PUSH BC ; stack 'present' values 5855 5856 RST _NEXT_CHAR 5857 JP S_LOOP_1 ; jump back 5858 5859 5860 5861 5862 ; THE 'TABLE OF PRIORITIES' 5863 5864 5865 mark_110F: 5866 tbl_pri: 5867 DEFB 6 ; '-' 5868 DEFB 8 ; '*' 5869 DEFB 8 ; '/' 5870 DEFB 10 ; '**' 5871 DEFB 2 ; 'OR' 5872 DEFB 3 ; 'AND' 5873 DEFB 5 ; '<=' 5874 DEFB 5 ; '>=' 5875 DEFB 5 ; '<>' 5876 DEFB 5 ; '>' 5877 DEFB 5 ; '<' 5878 DEFB 5 ; '=' 5879 DEFB 6 ; '+' 5880 5881 5882 ; THE 'LOOK_VARS' SUBROUTINE 5883 5884 5885 mark_111C: 5886 LOOK_VARS: 5887 SET 6,(IY+FLAGS-RAMBASE) ; Signal numeric result 5888 5889 RST _GET_CHAR 5890 CALL ALPHA 5891 JP NC,REPORT_C ; to REPORT_C 5892 5893 PUSH HL ; 5894 LD C,A ; 5895 5896 RST _NEXT_CHAR 5897 PUSH HL ; 5898 RES 5,C ; 5899 CP $10 ; $10 5900 JR Z,V_RUN_SYN 5901 5902 SET 6,C ; 5903 CP ZX_DOLLAR ; $0D 5904 JR Z,V_STR_VAR ; forward 5905 5906 SET 5,C ; 5907 5908 mark_1139: 5909 V_CHAR: 5910 CALL ALPHANUM 5911 JR NC,V_RUN_SYN ; forward when not 5912 5913 RES 6,C ; 5914 5915 RST _NEXT_CHAR 5916 JR V_CHAR ; loop back 5917 5918 ; ___ 5919 5920 mark_1143: 5921 V_STR_VAR: 5922 RST _NEXT_CHAR 5923 RES 6,(IY+FLAGS-RAMBASE) ; Signal string result 5924 5925 mark_1148: 5926 V_RUN_SYN: 5927 LD B,C ; 5928 CALL SYNTAX_Z 5929 JR NZ,V_RUN ; forward 5930 5931 LD A,C ; 5932 AND $E0 ; 5933 SET 7,A ; 5934 LD C,A ; 5935 JR V_SYNTAX ; forward 5936 5937 ; ___ 5938 5939 mark_1156: 5940 V_RUN: 5941 LD HL,(VARS) ; sv 5942 5943 mark_1159: 5944 V_EACH: 5945 LD A,(HL) ; 5946 AND $7F ; 5947 JR Z,V_80_BYTE ; 5948 5949 CP C ; 5950 JR NZ,V_NEXT ; 5951 5952 RLA ; 5953 ADD A,A ; 5954 JP P,V_FOUND_2 5955 5956 JR C,V_FOUND_2 5957 5958 POP DE ; 5959 PUSH DE ; 5960 PUSH HL ; 5961 5962 mark_116B: 5963 V_MATCHES: 5964 INC HL ; 5965 5966 mark_116C: 5967 V_SPACES: 5968 LD A,(DE) ; 5969 INC DE ; 5970 AND A ; 5971 JR Z,V_SPACES ; back 5972 5973 CP (HL) ; 5974 JR Z,V_MATCHES ; back 5975 5976 OR $80 ; 5977 CP (HL) ; 5978 JR NZ,V_GET_PTR ; forward 5979 5980 LD A,(DE) ; 5981 CALL ALPHANUM 5982 JR NC,V_FOUND_1 ; forward 5983 5984 mark_117F: 5985 V_GET_PTR: 5986 POP HL ; 5987 5988 mark_1180: 5989 V_NEXT: 5990 PUSH BC ; 5991 CALL NEXT_ONE 5992 EX DE,HL ; 5993 POP BC ; 5994 JR V_EACH ; back 5995 5996 ; ___ 5997 5998 mark_1188: 5999 V_80_BYTE: 6000 SET 7,B ; 6001 6002 mark_118A: 6003 V_SYNTAX: 6004 POP DE ; 6005 6006 RST _GET_CHAR 6007 CP $10 ; 6008 JR Z,V_PASS ; forward 6009 6010 SET 5,B ; 6011 JR V_END ; forward 6012 6013 ; ___ 6014 6015 mark_1194: 6016 V_FOUND_1: 6017 POP DE ; 6018 6019 mark_1195: 6020 V_FOUND_2: 6021 POP DE ; 6022 POP DE ; 6023 PUSH HL ; 6024 6025 RST _GET_CHAR 6026 6027 mark_1199: 6028 V_PASS: 6029 CALL ALPHANUM 6030 JR NC,V_END ; forward if not alphanumeric 6031 6032 6033 RST _NEXT_CHAR 6034 JR V_PASS ; back 6035 6036 ; ___ 6037 6038 mark_11A1: 6039 V_END: 6040 POP HL ; 6041 RL B ; 6042 BIT 6,B ; 6043 RET ; 6044 6045 6046 ; THE 'STK_VAR' SUBROUTINE 6047 6048 6049 mark_11A7: 6050 STK_VAR: 6051 XOR A ; 6052 LD B,A ; 6053 BIT 7,C ; 6054 JR NZ,SV_COUNT ; forward 6055 6056 BIT 7,(HL) ; 6057 JR NZ,SV_ARRAYS ; forward 6058 6059 INC A ; 6060 6061 mark_11B2: 6062 SV_SIMPLE_STR: 6063 INC HL ; 6064 LD C,(HL) ; 6065 INC HL ; 6066 LD B,(HL) ; 6067 INC HL ; 6068 EX DE,HL ; 6069 CALL STK_STO_STR 6070 6071 RST _GET_CHAR 6072 JP SV_SLICE_QUERY ; jump forward 6073 6074 ; ___ 6075 6076 mark_11BF: 6077 SV_ARRAYS: 6078 INC HL ; 6079 INC HL ; 6080 INC HL ; 6081 LD B,(HL) ; 6082 BIT 6,C ; 6083 JR Z,SV_PTR ; forward 6084 6085 DEC B ; 6086 JR Z,SV_SIMPLE_STR ; forward 6087 6088 EX DE,HL ; 6089 6090 RST _GET_CHAR 6091 CP $10 ; 6092 JR NZ,REPORT_3 ; forward 6093 6094 EX DE,HL ; 6095 6096 mark_11D1: 6097 SV_PTR: 6098 EX DE,HL ; 6099 JR SV_COUNT ; forward 6100 ; ___ 6101 mark_11D4: 6102 SV_COMMA: 6103 PUSH HL ; 6104 6105 RST _GET_CHAR 6106 POP HL ; 6107 CP ZX_COMMA ; $1A == 26 6108 JR Z,SV_LOOP ; forward 6109 6110 BIT 7,C ; 6111 JR Z,REPORT_3 ; forward 6112 6113 BIT 6,C ; 6114 JR NZ,SV_CLOSE ; forward 6115 6116 CP ZX_BRACKET_RIGHT ; $11 6117 JR NZ,SV_RPT_C ; forward 6118 6119 6120 RST _NEXT_CHAR 6121 RET ; 6122 ; ___ 6123 mark_11E9: 6124 SV_CLOSE: 6125 CP ZX_BRACKET_RIGHT ; $11 6126 JR Z,SV_DIM ; forward 6127 6128 CP $DF ; 6129 JR NZ,SV_RPT_C ; forward 6130 6131 mark_11F1: 6132 SV_CH_ADD: 6133 RST _GET_CHAR 6134 DEC HL ; 6135 LD (CH_ADD),HL ; sv 6136 JR SV_SLICE ; forward 6137 6138 ; ___ 6139 6140 mark_11F8: 6141 SV_COUNT: 6142 LD HL,$0000 ; 6143 6144 mark_11FB: 6145 SV_LOOP: 6146 PUSH HL ; 6147 6148 RST _NEXT_CHAR 6149 POP HL ; 6150 LD A,C ; 6151 CP ZX_DOUBLE_QUOTE ; 6152 JR NZ,SV_MULT ; forward 6153 6154 6155 RST _GET_CHAR 6156 CP ZX_BRACKET_RIGHT 6157 JR Z,SV_DIM ; forward 6158 6159 CP ZX_TO ; 6160 JR Z,SV_CH_ADD ; back 6161 6162 mark_120C: 6163 SV_MULT: 6164 PUSH BC ; 6165 PUSH HL ; 6166 CALL DE_DE_PLUS_ONE 6167 EX (SP),HL ; 6168 EX DE,HL ; 6169 CALL INT_EXP1 6170 JR C,REPORT_3 6171 6172 DEC BC ; 6173 CALL GET_HL_TIMES_DE 6174 ADD HL,BC ; 6175 POP DE ; 6176 POP BC ; 6177 DJNZ SV_COMMA ; loop back 6178 6179 BIT 7,C ; 6180 6181 mark_1223: 6182 SV_RPT_C: 6183 JR NZ,SL_RPT_C 6184 6185 PUSH HL ; 6186 BIT 6,C ; 6187 JR NZ,SV_ELEM_STR 6188 6189 LD B,D ; 6190 LD C,E ; 6191 6192 RST _GET_CHAR 6193 CP ZX_BRACKET_RIGHT; is character a ')' ? 6194 JR Z,SV_NUMBER ; skip forward 6195 6196 6197 mark_1231: 6198 REPORT_3: 6199 RST _ERROR_1 6200 DEFB $02 ; Error Report: Subscript wrong 6201 6202 6203 mark_1233: 6204 SV_NUMBER: 6205 RST _NEXT_CHAR 6206 POP HL ; 6207 LD DE,$0005 ; 6208 CALL GET_HL_TIMES_DE 6209 ADD HL,BC ; 6210 RET ; return >> 6211 6212 ; ___ 6213 6214 mark_123D: 6215 SV_ELEM_STR: 6216 CALL DE_DE_PLUS_ONE 6217 EX (SP),HL ; 6218 CALL GET_HL_TIMES_DE 6219 POP BC ; 6220 ADD HL,BC ; 6221 INC HL ; 6222 LD B,D ; 6223 LD C,E ; 6224 EX DE,HL ; 6225 CALL STK_ST_0 6226 6227 RST _GET_CHAR 6228 CP ZX_BRACKET_RIGHT ; is it ')' ? 6229 JR Z,SV_DIM ; forward if so 6230 6231 CP ZX_COMMA ; $1A == 26 ; is it ',' ? 6232 JR NZ,REPORT_3 ; back if not 6233 6234 mark_1256: 6235 SV_SLICE: 6236 CALL SLICING 6237 6238 mark_1259: 6239 SV_DIM: 6240 RST _NEXT_CHAR 6241 6242 mark_125A: 6243 SV_SLICE_QUERY: 6244 CP $10 ; 6245 JR Z,SV_SLICE ; back 6246 6247 RES 6,(IY+FLAGS-RAMBASE) ; Signal string result 6248 RET ; return. 6249 6250 6251 ; THE 'SLICING' SUBROUTINE 6252 6253 ; 6254 ; 6255 6256 mark_1263: 6257 SLICING: 6258 CALL SYNTAX_Z 6259 CALL NZ,STK_FETCH 6260 6261 RST _NEXT_CHAR 6262 CP ZX_BRACKET_RIGHT; is it ')' ? 6263 JR Z,SL_STORE ; forward if so 6264 6265 PUSH DE ; 6266 XOR A ; 6267 PUSH AF ; 6268 PUSH BC ; 6269 LD DE,$0001 ; 6270 6271 RST _GET_CHAR 6272 POP HL ; 6273 CP ZX_TO ; is it 'TO' ? 6274 JR Z,SL_SECOND ; forward if so 6275 6276 POP AF ; 6277 CALL INT_EXP2 6278 PUSH AF ; 6279 LD D,B ; 6280 LD E,C ; 6281 PUSH HL ; 6282 6283 RST _GET_CHAR 6284 POP HL ; 6285 CP ZX_TO ; is it 'TO' ? 6286 JR Z,SL_SECOND ; forward if so 6287 6288 CP ZX_BRACKET_RIGHT; $11 6289 6290 mark_128B: 6291 SL_RPT_C: 6292 JP NZ,REPORT_C 6293 6294 LD H,D ; 6295 LD L,E ; 6296 JR SL_DEFINE ; forward 6297 6298 ; ___ 6299 6300 mark_1292: 6301 SL_SECOND: 6302 PUSH HL ; 6303 6304 RST _NEXT_CHAR 6305 POP HL ; 6306 CP ZX_BRACKET_RIGHT; is it ')' ? 6307 JR Z,SL_DEFINE ; forward if so 6308 6309 POP AF ; 6310 CALL INT_EXP2 6311 PUSH AF ; 6312 6313 RST _GET_CHAR 6314 LD H,B ; 6315 LD L,C ; 6316 CP ZX_BRACKET_RIGHT; is it ')' ? 6317 JR NZ,SL_RPT_C ; back if not 6318 6319 mark_12A5: 6320 SL_DEFINE: 6321 POP AF ; 6322 EX (SP),HL ; 6323 ADD HL,DE ; 6324 DEC HL ; 6325 EX (SP),HL ; 6326 AND A ; 6327 SBC HL,DE ; 6328 LD BC,$0000 ; 6329 JR C,SL_OVER ; forward 6330 6331 INC HL ; 6332 AND A ; 6333 JP M,REPORT_3 ; jump back 6334 6335 LD B,H ; 6336 LD C,L ; 6337 6338 mark_12B9: 6339 SL_OVER: 6340 POP DE ; 6341 RES 6,(IY+FLAGS-RAMBASE) ; Signal string result 6342 6343 mark_12BE: 6344 SL_STORE: 6345 CALL SYNTAX_Z 6346 RET Z ; return if checking syntax. 6347 6348 6349 ; THE 'STK_STORE' SUBROUTINE 6350 6351 ; 6352 ; 6353 6354 mark_12C2: 6355 STK_ST_0: 6356 XOR A ; 6357 6358 mark_12C3: 6359 STK_STO_STR: 6360 PUSH BC ; 6361 CALL TEST_5_SP 6362 POP BC ; 6363 LD HL,(STKEND) ; sv 6364 LD (HL),A ; 6365 INC HL ; 6366 LD (HL),E ; 6367 INC HL ; 6368 LD (HL),D ; 6369 INC HL ; 6370 LD (HL),C ; 6371 INC HL ; 6372 LD (HL),B ; 6373 INC HL ; 6374 LD (STKEND),HL ; sv 6375 RES 6,(IY+FLAGS-RAMBASE) ; Signal string result 6376 RET ; return. 6377 6378 6379 ; THE 'INT EXP' SUBROUTINES 6380 6381 ; 6382 ; 6383 6384 mark_12DD: 6385 INT_EXP1: 6386 XOR A ; 6387 6388 mark_12DE: 6389 INT_EXP2: 6390 PUSH DE ; 6391 PUSH HL ; 6392 PUSH AF ; 6393 CALL CLASS_6 6394 POP AF ; 6395 CALL SYNTAX_Z 6396 JR Z,I_RESTORE ; forward if checking syntax 6397 6398 PUSH AF ; 6399 CALL FIND_INT 6400 POP DE ; 6401 LD A,B ; 6402 OR C ; 6403 SCF ; Set Carry Flag 6404 JR Z,I_CARRY ; forward 6405 6406 POP HL ; 6407 PUSH HL ; 6408 AND A ; 6409 SBC HL,BC ; 6410 6411 mark_12F9: 6412 I_CARRY: 6413 LD A,D ; 6414 SBC A,$00 ; 6415 6416 mark_12FC: 6417 I_RESTORE: 6418 POP HL ; 6419 POP DE ; 6420 RET ; 6421 6422 6423 ; THE 'DE,(DE+1)' SUBROUTINE 6424 6425 ; INDEX and LOAD Z80 subroutine. 6426 ; This emulates the 6800 processor instruction LDX 1,X which loads a two_byte 6427 ; value from memory into the register indexing it. Often these are hardly worth 6428 ; the bother of writing as subroutines and this one doesn't save any time or 6429 ; memory. The timing and space overheads have to be offset against the ease of 6430 ; writing and the greater program readability from using such toolkit routines. 6431 6432 mark_12FF: 6433 DE_DE_PLUS_ONE: 6434 EX DE,HL ; move index address into HL. 6435 INC HL ; increment to address word. 6436 LD E,(HL) ; pick up word low_order byte. 6437 INC HL ; index high_order byte and 6438 LD D,(HL) ; pick it up. 6439 RET ; return with DE = word. 6440 6441 6442 ; THE 'GET_HL_TIMES_DE' SUBROUTINE 6443 6444 ; 6445 6446 mark_1305: 6447 GET_HL_TIMES_DE: 6448 CALL SYNTAX_Z 6449 RET Z ; 6450 6451 PUSH BC ; 6452 LD B,$10 ; 6453 LD A,H ; 6454 LD C,L ; 6455 LD HL,$0000 ; 6456 6457 mark_1311: 6458 HL_LOOP: 6459 ADD HL,HL ; 6460 JR C,HL_END ; forward with carry 6461 6462 RL C ; 6463 RLA ; 6464 JR NC,HL_AGAIN ; forward with no carry 6465 6466 ADD HL,DE ; 6467 6468 mark_131A: 6469 HL_END: 6470 JP C,REPORT_4 6471 6472 mark_131D: 6473 HL_AGAIN: 6474 DJNZ HL_LOOP ; loop back 6475 6476 POP BC ; 6477 RET ; return. 6478 6479 6480 ; THE 'LET' SUBROUTINE 6481 6482 ; 6483 ; 6484 6485 mark_1321: 6486 LET: 6487 LD HL,(DEST) 6488 BIT 1,(IY+FLAGX-RAMBASE) 6489 JR Z,L_EXISTS ; forward 6490 6491 LD BC,$0005 ; 6492 6493 mark_132D: 6494 L_EACH_CH: 6495 INC BC ; 6496 6497 ; check 6498 6499 mark_132E: 6500 L_NO_SP: 6501 INC HL ; 6502 LD A,(HL) ; 6503 AND A ; 6504 JR Z,L_NO_SP ; back 6505 6506 CALL ALPHANUM 6507 JR C,L_EACH_CH ; back 6508 6509 CP ZX_DOLLAR ; is it '$' ? 6510 JP Z,L_NEW_STR ; forward if so 6511 6512 6513 RST _BC_SPACES ; BC_SPACES 6514 PUSH DE ; 6515 LD HL,(DEST) ; 6516 DEC DE ; 6517 LD A,C ; 6518 SUB $06 ; 6519 LD B,A ; 6520 LD A,$40 ; 6521 JR Z,L_SINGLE 6522 6523 mark_134B: 6524 L_CHAR: 6525 INC HL ; 6526 LD A,(HL) ; 6527 AND A ; is it a space ? 6528 JR Z,L_CHAR ; back 6529 6530 INC DE ; 6531 LD (DE),A ; 6532 DJNZ L_CHAR ; loop back 6533 6534 OR $80 ; 6535 LD (DE),A ; 6536 LD A,$80 ; 6537 6538 mark_1359: 6539 L_SINGLE: 6540 LD HL,(DEST) ; 6541 XOR (HL) ; 6542 POP HL ; 6543 CALL L_FIRST 6544 6545 mark_1361: 6546 L_NUMERIC: 6547 PUSH HL ; 6548 6549 RST _FP_CALC ;; 6550 DEFB __delete ;; 6551 DEFB __end_calc ;; 6552 6553 POP HL ; 6554 LD BC,$0005 ; 6555 AND A ; 6556 SBC HL,BC ; 6557 JR L_ENTER ; forward 6558 6559 ; ___ 6560 6561 mark_136E: 6562 L_EXISTS: 6563 BIT 6,(IY+FLAGS-RAMBASE) ; Numeric or string result? 6564 JR Z,L_DELETE_STR ; forward 6565 6566 LD DE,$0006 ; 6567 ADD HL,DE ; 6568 JR L_NUMERIC ; back 6569 6570 ; ___ 6571 6572 mark_137A: 6573 L_DELETE_STR: 6574 LD HL,(DEST) ; 6575 LD BC,(STRLEN) ; 6576 BIT 0,(IY+FLAGX-RAMBASE) 6577 JR NZ,L_ADD_STR ; forward 6578 6579 LD A,B ; 6580 OR C ; 6581 RET Z ; 6582 6583 PUSH HL ; 6584 6585 RST _BC_SPACES 6586 PUSH DE ; 6587 PUSH BC ; 6588 LD D,H ; 6589 LD E,L ; 6590 INC HL ; 6591 LD (HL),$00 ; 6592 LDDR ; Copy Bytes 6593 PUSH HL ; 6594 CALL STK_FETCH 6595 POP HL ; 6596 EX (SP),HL ; 6597 AND A ; 6598 SBC HL,BC ; 6599 ADD HL,BC ; 6600 JR NC,L_LENGTH ; forward 6601 6602 LD B,H ; 6603 LD C,L ; 6604 6605 mark_13A3: 6606 L_LENGTH: 6607 EX (SP),HL ; 6608 EX DE,HL ; 6609 LD A,B ; 6610 OR C ; 6611 JR Z,L_IN_W_S ; forward if zero 6612 6613 LDIR ; Copy Bytes 6614 6615 mark_13AB: 6616 L_IN_W_S: 6617 POP BC ; 6618 POP DE ; 6619 POP HL ; 6620 6621 6622 ; THE 'L_ENTER' SUBROUTINE 6623 ; 6624 ; Part of the LET command contains a natural subroutine which is a 6625 ; conditional LDIR. The copy only occurs of BC is non-zero. 6626 6627 mark_13AE: 6628 L_ENTER: 6629 EX DE,HL ; 6630 if ORIGINAL 6631 else 6632 COND_MV: 6633 endif 6634 LD A,B ; 6635 OR C ; 6636 RET Z ; 6637 6638 PUSH DE ; 6639 LDIR ; Copy Bytes 6640 POP HL ; 6641 RET ; return. 6642 6643 mark_13B7: 6644 L_ADD_STR: 6645 DEC HL ; 6646 DEC HL ; 6647 DEC HL ; 6648 LD A,(HL) ; 6649 PUSH HL ; 6650 PUSH BC ; 6651 6652 CALL L_STRING 6653 6654 POP BC ; 6655 POP HL ; 6656 INC BC ; 6657 INC BC ; 6658 INC BC ; 6659 JP RECLAIM_2 ; jump back to exit via RECLAIM_2 6660 6661 ; ___ 6662 6663 mark_13C8: 6664 L_NEW_STR: 6665 LD A,$60 ; prepare mask %01100000 6666 LD HL,(DEST) ; 6667 XOR (HL) ; 6668 6669 6670 ; THE 'L_STRING' SUBROUTINE 6671 6672 ; 6673 6674 mark_13CE: 6675 L_STRING: 6676 PUSH AF ; 6677 CALL STK_FETCH 6678 EX DE,HL ; 6679 ADD HL,BC ; 6680 PUSH HL ; 6681 INC BC ; 6682 INC BC ; 6683 INC BC ; 6684 6685 RST _BC_SPACES 6686 EX DE,HL ; 6687 POP HL ; 6688 DEC BC ; 6689 DEC BC ; 6690 PUSH BC ; 6691 LDDR ; Copy Bytes 6692 EX DE,HL ; 6693 POP BC ; 6694 DEC BC ; 6695 LD (HL),B ; 6696 DEC HL ; 6697 LD (HL),C ; 6698 POP AF ; 6699 6700 mark_13E7: 6701 L_FIRST: 6702 PUSH AF ; 6703 CALL REC_V80 6704 POP AF ; 6705 DEC HL ; 6706 LD (HL),A ; 6707 LD HL,(STKBOT) ; sv 6708 LD (E_LINE),HL ; sv 6709 DEC HL ; 6710 LD (HL),$80 ; 6711 RET ; 6712 6713 6714 ; THE 'STK_FETCH' SUBROUTINE 6715 6716 ; This routine fetches a five-byte value from the calculator stack 6717 ; reducing the pointer to the end of the stack by five. 6718 ; For a floating-point number the exponent is in A and the mantissa 6719 ; is the thirty-two bits EDCB. 6720 ; For strings, the start of the string is in DE and the length in BC. 6721 ; A is unused. 6722 6723 mark_13F8: 6724 STK_FETCH: 6725 LD HL,(STKEND) ; load HL from system variable STKEND 6726 6727 DEC HL ; 6728 LD B,(HL) ; 6729 DEC HL ; 6730 LD C,(HL) ; 6731 DEC HL ; 6732 LD D,(HL) ; 6733 DEC HL ; 6734 LD E,(HL) ; 6735 DEC HL ; 6736 LD A,(HL) ; 6737 6738 LD (STKEND),HL ; set system variable STKEND to lower value. 6739 RET ; return. 6740 6741 6742 ; THE 'DIM' COMMAND ROUTINE 6743 6744 ; An array is created and initialized to zeros which is also the space 6745 ; character on the ZX81. 6746 6747 mark_1409: 6748 DIM: 6749 CALL LOOK_VARS 6750 6751 mark_140C: 6752 D_RPORT_C: 6753 JP NZ,REPORT_C 6754 6755 CALL SYNTAX_Z 6756 JR NZ,D_RUN ; forward 6757 6758 RES 6,C ; 6759 CALL STK_VAR 6760 CALL CHECK_END 6761 6762 mark_141C: 6763 D_RUN: 6764 JR C,D_LETTER ; forward 6765 6766 PUSH BC ; 6767 CALL NEXT_ONE 6768 CALL RECLAIM_2 6769 POP BC ; 6770 6771 mark_1426: 6772 D_LETTER: 6773 SET 7,C ; 6774 LD B,$00 ; 6775 PUSH BC ; 6776 LD HL,$0001 ; 6777 BIT 6,C ; 6778 JR NZ,D_SIZE ; forward 6779 6780 LD L,$05 ; 6781 6782 mark_1434: 6783 D_SIZE: 6784 EX DE,HL ; 6785 6786 mark_1435: 6787 D_NO_LOOP: 6788 RST _NEXT_CHAR 6789 LD H,$40 ; 6790 CALL INT_EXP1 6791 JP C,REPORT_3 6792 6793 POP HL ; 6794 PUSH BC ; 6795 INC H ; 6796 PUSH HL ; 6797 LD H,B ; 6798 LD L,C ; 6799 CALL GET_HL_TIMES_DE 6800 EX DE,HL ; 6801 6802 RST _GET_CHAR 6803 CP ZX_COMMA ; $1A == 26 6804 JR Z,D_NO_LOOP ; back 6805 6806 CP ZX_BRACKET_RIGHT; is it ')' ? 6807 JR NZ,D_RPORT_C ; back if not 6808 6809 6810 RST _NEXT_CHAR 6811 POP BC ; 6812 LD A,C ; 6813 LD L,B ; 6814 LD H,$00 ; 6815 INC HL ; 6816 INC HL ; 6817 ADD HL,HL ; 6818 ADD HL,DE ; 6819 JP C,REPORT_4 6820 6821 PUSH DE ; 6822 PUSH BC ; 6823 PUSH HL ; 6824 LD B,H ; 6825 LD C,L ; 6826 LD HL,(E_LINE) ; sv 6827 DEC HL ; 6828 CALL MAKE_ROOM 6829 INC HL ; 6830 LD (HL),A ; 6831 POP BC ; 6832 DEC BC ; 6833 DEC BC ; 6834 DEC BC ; 6835 INC HL ; 6836 LD (HL),C ; 6837 INC HL ; 6838 LD (HL),B ; 6839 POP AF ; 6840 INC HL ; 6841 LD (HL),A ; 6842 LD H,D ; 6843 LD L,E ; 6844 DEC DE ; 6845 LD (HL),0 ; 6846 POP BC ; 6847 LDDR ; Copy Bytes 6848 6849 mark_147F: 6850 DIM_SIZES: 6851 POP BC ; 6852 LD (HL),B ; 6853 DEC HL ; 6854 LD (HL),C ; 6855 DEC HL ; 6856 DEC A ; 6857 JR NZ,DIM_SIZES ; back 6858 6859 RET ; return. 6860 6861 6862 ; THE 'RESERVE' ROUTINE 6863 6864 ; 6865 ; 6866 6867 mark_1488: 6868 RESERVE: 6869 LD HL,(STKBOT) ; address STKBOT 6870 DEC HL ; now last byte of workspace 6871 CALL MAKE_ROOM 6872 INC HL ; 6873 INC HL ; 6874 POP BC ; 6875 LD (E_LINE),BC ; sv 6876 POP BC ; 6877 EX DE,HL ; 6878 INC HL ; 6879 RET ; 6880 6881 6882 ; THE 'CLEAR' COMMAND ROUTINE 6883 6884 ; 6885 ; 6886 6887 mark_149A: 6888 CLEAR: 6889 LD HL,(VARS) ; sv 6890 LD (HL),$80 ; 6891 INC HL ; 6892 LD (E_LINE),HL ; sv 6893 6894 6895 ; THE 'X_TEMP' SUBROUTINE 6896 6897 ; 6898 ; 6899 6900 mark_14A3: 6901 X_TEMP: 6902 LD HL,(E_LINE) ; sv 6903 6904 6905 ; THE 'SET_STK' ROUTINES 6906 6907 ; 6908 ; 6909 6910 mark_14A6: 6911 SET_STK_B: 6912 LD (STKBOT),HL ; sv 6913 6914 ; 6915 6916 mark_14A9: 6917 SET_STK_E: 6918 LD (STKEND),HL ; sv 6919 RET ; 6920 6921 6922 ; THE 'CURSOR_IN' ROUTINE 6923 6924 ; This routine is called to set the edit line to the minimum cursor/newline 6925 ; and to set STKEND, the start of free space, at the next position. 6926 6927 mark_14AD: 6928 CURSOR_IN: 6929 LD HL,(E_LINE) ; fetch start of edit line 6930 LD (HL),ZX_CURSOR ; insert cursor character 6931 6932 INC HL ; point to next location. 6933 LD (HL),ZX_NEWLINE ; insert NEWLINE character 6934 INC HL ; point to next free location. 6935 6936 LD (IY+DF_SZ-RAMBASE),2 ; set lower screen display file size 6937 6938 JR SET_STK_B ; exit via SET_STK_B above 6939 6940 6941 ; THE 'SET_MIN' SUBROUTINE 6942 6943 ; 6944 ; 6945 6946 mark_14BC: 6947 SET_MIN: 6948 LD HL,$405D ; normal location of calculator's memory area 6949 LD (MEM),HL ; update system variable MEM 6950 LD HL,(STKBOT) ; 6951 JR SET_STK_E ; back 6952 6953 6954 6955 ; THE 'RECLAIM THE END_MARKER' ROUTINE 6956 6957 6958 mark_14C7: 6959 REC_V80: 6960 LD DE,(E_LINE) ; sv 6961 JP RECLAIM_1 6962 6963 6964 ; THE 'ALPHA' SUBROUTINE 6965 6966 6967 mark_14CE: 6968 ALPHA: 6969 CP ZX_A ; $26 6970 JR ALPHA_2 ; skip forward 6971 6972 6973 6974 ; THE 'ALPHANUM' SUBROUTINE 6975 6976 6977 mark_14D2: 6978 ALPHANUM: 6979 CP ZX_0 ; 6980 6981 6982 mark_14D4: 6983 ALPHA_2: 6984 CCF ; Complement Carry Flag 6985 RET NC ; 6986 6987 CP $40 ; 6988 RET ; 6989 6990 6991 6992 ; THE 'DECIMAL TO FLOATING POINT' SUBROUTINE 6993 6994 ; 6995 6996 mark_14D9: 6997 DEC_TO_FP: 6998 CALL INT_TO_FP ; gets first part 6999 CP ZX_PERIOD ; is character a '.' ? 7000 JR NZ,E_FORMAT ; forward if not 7001 7002 7003 RST _FP_CALC ;; 7004 DEFB __stk_one ;; 7005 DEFB __st_mem_0 ;; 7006 DEFB __delete ;; 7007 DEFB __end_calc ;; 7008 7009 7010 mark_14E5: 7011 7012 NXT_DGT_1: 7013 RST _NEXT_CHAR 7014 CALL STK_DIGIT 7015 JR C,E_FORMAT ; forward 7016 7017 7018 RST _FP_CALC ;; 7019 DEFB __get_mem_0 ;; 7020 DEFB __stk_ten ;; 7021 if ORIGINAL 7022 DEFB __division ; 7023 DEFB $C0 ;;st-mem-0 7024 DEFB __multiply ;; 7025 else 7026 DEFB $04 ;;+multiply 7027 DEFB $C0 ;;st-mem-0 7028 DEFB $05 ;;+division 7029 endif 7030 DEFB __addition ;; 7031 DEFB __end_calc ;; 7032 7033 JR NXT_DGT_1 ; loop back till exhausted 7034 7035 ; ___ 7036 7037 mark_14F5: 7038 E_FORMAT: 7039 CP ZX_E ; is character 'E' ? 7040 RET NZ ; return if not 7041 7042 LD (IY+MEM_0_1st-RAMBASE),$FF ; initialize sv MEM_0_1st to $FF TRUE 7043 7044 RST _NEXT_CHAR 7045 CP ZX_PLUS ; is character a '+' ? 7046 JR Z,SIGN_DONE ; forward if so 7047 7048 CP ZX_MINUS ; is it a '-' ? 7049 JR NZ,ST_E_PART ; forward if not 7050 7051 INC (IY+MEM_0_1st-RAMBASE) ; sv MEM_0_1st change to FALSE 7052 7053 mark_1508: 7054 SIGN_DONE: 7055 RST _NEXT_CHAR 7056 7057 mark_1509: 7058 ST_E_PART: 7059 CALL INT_TO_FP 7060 7061 RST _FP_CALC ;; m, e. 7062 DEFB __get_mem_0 ;; m, e, (1/0) TRUE/FALSE 7063 DEFB __jump_true ;; 7064 DEFB E_POSTVE - $ ;; 7065 DEFB __negate ;; m, _e 7066 7067 mark_1511: 7068 E_POSTVE: 7069 DEFB __e_to_fp ;; x. 7070 DEFB __end_calc ;; x. 7071 7072 RET ; return. 7073 7074 7075 7076 ; THE 'STK_DIGIT' SUBROUTINE 7077 7078 ; 7079 7080 mark_1514: 7081 STK_DIGIT: 7082 CP ZX_0 ; 7083 RET C ; 7084 7085 CP ZX_A ; $26 7086 CCF ; Complement Carry Flag 7087 RET C ; 7088 7089 SUB ZX_0 ; 7090 7091 7092 ; THE 'STACK_A' SUBROUTINE 7093 7094 ; 7095 7096 7097 mark_151D: 7098 STACK_A: 7099 LD C,A ; 7100 LD B,0 ; 7101 7102 7103 ; THE 'STACK_BC' SUBROUTINE 7104 7105 ; The ZX81 does not have an integer number format so the BC register contents 7106 ; must be converted to their full floating-point form. 7107 7108 mark_1520: 7109 STACK_BC: 7110 LD IY,ERR_NR ; re-initialize the system variables pointer. 7111 PUSH BC ; save the integer value. 7112 7113 ; now stack zero, five zero bytes as a starting point. 7114 7115 RST _FP_CALC ;; 7116 DEFB __stk_zero ;; 0. 7117 DEFB __end_calc ;; 7118 7119 POP BC ; restore integer value. 7120 7121 LD (HL),$91 ; place $91 in exponent 65536. 7122 ; this is the maximum possible value 7123 7124 LD A,B ; fetch hi-byte. 7125 AND A ; test for zero. 7126 JR NZ,STK_BC_2 ; forward if not zero 7127 7128 LD (HL),A ; else make exponent zero again 7129 OR C ; test lo-byte 7130 RET Z ; return if BC was zero - done. 7131 7132 ; else there has to be a set bit if only the value one. 7133 7134 LD B,C ; save C in B. 7135 LD C,(HL) ; fetch zero to C 7136 LD (HL),$89 ; make exponent $89 256. 7137 7138 mark_1536: 7139 STK_BC_2: 7140 DEC (HL) ; decrement exponent - halving number 7141 SLA C ; C<-76543210<-0 7142 RL B ; C<-76543210<-C 7143 JR NC,STK_BC_2 ; loop back if no carry 7144 7145 SRL B ; 0->76543210->C 7146 RR C ; C->76543210->C 7147 7148 INC HL ; address first byte of mantissa 7149 LD (HL),B ; insert B 7150 INC HL ; address second byte of mantissa 7151 LD (HL),C ; insert C 7152 7153 DEC HL ; point to the 7154 DEC HL ; exponent again 7155 RET ; return. 7156 7157 7158 ; THE 'INTEGER TO FLOATING POINT' SUBROUTINE 7159 7160 ; 7161 ; 7162 7163 mark_1548: 7164 INT_TO_FP: 7165 PUSH AF ; 7166 7167 RST _FP_CALC ;; 7168 DEFB __stk_zero ;; 7169 DEFB __end_calc ;; 7170 7171 POP AF ; 7172 7173 mark_154D: 7174 NXT_DGT_2: 7175 CALL STK_DIGIT 7176 RET C ; 7177 7178 RST _FP_CALC ;; 7179 DEFB __exchange ;; 7180 DEFB __stk_ten ;; 7181 DEFB __multiply ;; 7182 DEFB __addition ;; 7183 DEFB __end_calc ;; 7184 7185 RST _NEXT_CHAR 7186 JR NXT_DGT_2 7187 7188 7189 7190 ; THE 'E_FORMAT TO FLOATING POINT' SUBROUTINE 7191 7192 ; (Offset $38: 'e_to_fp') 7193 ; invoked from DEC_TO_FP and PRINT_FP. 7194 ; e.g. 2.3E4 is 23000. 7195 ; This subroutine evaluates xEm where m is a positive or negative integer. 7196 ; At a simple level x is multiplied by ten for every unit of m. 7197 ; If the decimal exponent m is negative then x is divided by ten for each unit. 7198 ; A short-cut is taken if the exponent is greater than seven and in this 7199 ; case the exponent is reduced by seven and the value is multiplied or divided 7200 ; by ten million. 7201 ; Note. for the ZX Spectrum an even cleverer method was adopted which involved 7202 ; shifting the bits out of the exponent so the result was achieved with six 7203 ; shifts at most. The routine below had to be completely re-written mostly 7204 ; in Z80 machine code. 7205 ; Although no longer operable, the calculator literal was retained for old 7206 ; times sake, the routine being invoked directly from a machine code CALL. 7207 ; 7208 ; On entry in the ZX81, m, the exponent, is the 'last value', and the 7209 ; floating-point decimal mantissa is beneath it. 7210 7211 7212 mark_155A: 7213 e_to_fp: 7214 RST _FP_CALC ;; x, m. 7215 DEFB __duplicate ;; x, m, m. 7216 DEFB __less_0 ;; x, m, (1/0). 7217 DEFB __st_mem_0 ;; x, m, (1/0). 7218 DEFB __delete ;; x, m. 7219 DEFB __abs ;; x, +m. 7220 7221 mark_1560: 7222 E_LOOP: 7223 DEFB __stk_one ;; x, m,1. 7224 DEFB __subtract ;; x, m-1. 7225 DEFB __duplicate ;; x, m-1,m-1. 7226 DEFB __less_0 ;; x, m-1, (1/0). 7227 DEFB __jump_true ;; x, m-1. 7228 DEFB E_END - $ ;; x, m-1. 7229 7230 DEFB __duplicate ;; x, m-1, m-1. 7231 DEFB __stk_data ;; 7232 DEFB $33 ;;Exponent: $83, Bytes: 1 7233 7234 DEFB $40 ;;(+00,+00,+00) x, m-1, m-1, 6. 7235 DEFB __subtract ;; x, m-1, m-7. 7236 DEFB __duplicate ;; x, m-1, m-7, m-7. 7237 DEFB __less_0 ;; x, m-1, m-7, (1/0). 7238 DEFB __jump_true ;; x, m-1, m-7. 7239 DEFB E_LOW - $ ;; 7240 7241 ; but if exponent m is higher than 7 do a bigger chunk. 7242 ; multiplying (or dividing if negative) by 10 million - 1e7. 7243 7244 DEFB __exchange ;; x, m-7, m-1. 7245 DEFB __delete ;; x, m-7. 7246 DEFB __exchange ;; m-7, x. 7247 DEFB __stk_data ;; 7248 DEFB $80 ;;Bytes: 3 7249 DEFB $48 ;;Exponent $98 7250 DEFB $18,$96,$80 ;;(+00) m-7, x, 10,000,000 (=f) 7251 DEFB __jump ;; 7252 DEFB E_CHUNK - $ ;; 7253 7254 ; ___ 7255 7256 mark_157A: 7257 E_LOW: 7258 DEFB __delete ;; x, m-1. 7259 DEFB __exchange ;; m-1, x. 7260 DEFB __stk_ten ;; m-1, x, 10 (=f). 7261 7262 mark_157D: 7263 E_CHUNK: 7264 DEFB __get_mem_0 ;; m-1, x, f, (1/0) 7265 DEFB __jump_true ;; m-1, x, f 7266 DEFB E_DIVSN - $ ;; 7267 7268 DEFB __multiply ;; m-1, x*f. 7269 DEFB __jump ;; 7270 DEFB E_SWAP - $ ;; 7271 7272 ; ___ 7273 7274 mark_1583: 7275 E_DIVSN: 7276 DEFB __division ;; m-1, x/f (= new x). 7277 7278 mark_1584: 7279 E_SWAP: 7280 DEFB __exchange ;; x, m-1 (= new m). 7281 DEFB __jump ;; x, m. 7282 DEFB E_LOOP - $ ;; 7283 7284 ; ___ 7285 7286 mark_1587: 7287 E_END: 7288 DEFB __delete ;; x. (-1) 7289 DEFB __end_calc ;; x. 7290 7291 RET ; return. 7292 7293 7294 ; THE 'FLOATING-POINT TO BC' SUBROUTINE 7295 7296 ; The floating-point form on the calculator stack is compressed directly into 7297 ; the BC register rounding up if necessary. 7298 ; Valid range is 0 to 65535.4999 7299 7300 mark_158A: 7301 FP_TO_BC: 7302 CALL STK_FETCH ; exponent to A 7303 ; mantissa to EDCB. 7304 AND A ; test for value zero. 7305 JR NZ,FPBC_NZRO ; forward if not 7306 7307 ; else value is zero 7308 7309 LD B,A ; zero to B 7310 LD C,A ; also to C 7311 PUSH AF ; save the flags on machine stack 7312 JR FPBC_END ; forward 7313 7314 ; ___ 7315 7316 ; EDCB => BCE 7317 7318 mark_1595: 7319 FPBC_NZRO: 7320 LD B,E ; transfer the mantissa from EDCB 7321 LD E,C ; to BCE. Bit 7 of E is the 17th bit which 7322 LD C,D ; will be significant for rounding if the 7323 ; number is already normalized. 7324 7325 SUB $91 ; subtract 65536 7326 CCF ; complement carry flag 7327 BIT 7,B ; test sign bit 7328 PUSH AF ; push the result 7329 7330 SET 7,B ; set the implied bit 7331 JR C,FPBC_END ; forward with carry from SUB/CCF 7332 ; number is too big. 7333 7334 INC A ; increment the exponent and 7335 NEG ; negate to make range $00 - $0F 7336 7337 CP $08 ; test if one or two bytes 7338 JR C,BIG_INT ; forward with two 7339 7340 LD E,C ; shift mantissa 7341 LD C,B ; 8 places right 7342 LD B,$00 ; insert a zero in B 7343 SUB $08 ; reduce exponent by eight 7344 7345 mark_15AF: 7346 BIG_INT: 7347 AND A ; test the exponent 7348 LD D,A ; save exponent in D. 7349 7350 LD A,E ; fractional bits to A 7351 RLCA ; rotate most significant bit to carry for 7352 ; rounding of an already normal number. 7353 7354 JR Z,EXP_ZERO ; forward if exponent zero 7355 ; the number is normalized 7356 7357 mark_15B5: 7358 FPBC_NORM: 7359 SRL B ; 0->76543210->C 7360 RR C ; C->76543210->C 7361 7362 DEC D ; decrement exponent 7363 7364 JR NZ,FPBC_NORM ; loop back till zero 7365 7366 mark_15BC: 7367 EXP_ZERO: 7368 JR NC,FPBC_END ; forward without carry to NO_ROUND ??? 7369 7370 INC BC ; round up. 7371 LD A,B ; test result 7372 OR C ; for zero 7373 JR NZ,FPBC_END ; forward if not to GRE_ZERO ??? 7374 7375 POP AF ; restore sign flag 7376 SCF ; set carry flag to indicate overflow 7377 PUSH AF ; save combined flags again 7378 7379 mark_15C6: 7380 FPBC_END: 7381 PUSH BC ; save BC value 7382 7383 ; set HL and DE to calculator stack pointers. 7384 7385 RST _FP_CALC ;; 7386 DEFB __end_calc ;; 7387 7388 7389 POP BC ; restore BC value 7390 POP AF ; restore flags 7391 LD A,C ; copy low byte to A also. 7392 RET ; return 7393 7394 7395 ; THE 'FLOATING-POINT TO A' SUBROUTINE 7396 7397 ; 7398 ; 7399 7400 mark_15CD: 7401 FP_TO_A: 7402 CALL FP_TO_BC 7403 RET C ; 7404 7405 PUSH AF ; 7406 DEC B ; 7407 INC B ; 7408 JR Z,FP_A_END ; forward if in range 7409 7410 POP AF ; fetch result 7411 SCF ; set carry flag signaling overflow 7412 RET ; return 7413 7414 mark_15D9: 7415 FP_A_END: 7416 POP AF ; 7417 RET ; 7418 7419 7420 7421 ; THE 'PRINT A FLOATING-POINT NUMBER' SUBROUTINE 7422 7423 ; prints 'last value' x on calculator stack. 7424 ; There are a wide variety of formats see Chapter 4. 7425 ; e.g. 7426 ; PI prints as 3.1415927 7427 ; .123 prints as 0.123 7428 ; .0123 prints as .0123 7429 ; 999999999999 prints as 1000000000000 7430 ; 9876543210123 prints as 9876543200000 7431 7432 ; Begin by isolating zero and just printing the '0' character 7433 ; for that case. For negative numbers print a leading '-' and 7434 ; then form the absolute value of x. 7435 7436 mark_15DB: 7437 PRINT_FP: 7438 RST _FP_CALC ;; x. 7439 DEFB __duplicate ;; x, x. 7440 DEFB __less_0 ;; x, (1/0). 7441 DEFB __jump_true ;; 7442 DEFB PF_NEGTVE - $ ;; x. 7443 7444 DEFB __duplicate ;; x, x 7445 DEFB __greater_0 ;; x, (1/0). 7446 DEFB __jump_true ;; 7447 DEFB PF_POSTVE - $ ;; x. 7448 7449 DEFB __delete ;; . 7450 DEFB __end_calc ;; . 7451 7452 LD A,ZX_0 ; load accumulator with character '0' 7453 7454 RST _PRINT_A 7455 RET ; return. >> 7456 7457 ; ___ 7458 7459 mark_15EA: 7460 PF_NEGTVE: 7461 DEFB __abs ;; +x. 7462 DEFB __end_calc ;; x. 7463 7464 LD A,ZX_MINUS ; load accumulator with '-' 7465 7466 RST _PRINT_A 7467 7468 RST _FP_CALC ;; x. 7469 7470 mark_15F0: 7471 PF_POSTVE: 7472 DEFB __end_calc ;; x. 7473 7474 ; register HL addresses the exponent of the floating-point value. 7475 ; if positive, and point floats to left, then bit 7 is set. 7476 7477 LD A,(HL) ; pick up the exponent byte 7478 CALL STACK_A ; places on calculator stack. 7479 7480 ; now calculate roughly the number of digits, n, before the decimal point by 7481 ; subtracting a half from true exponent and multiplying by log to 7482 ; the base 10 of 2. 7483 ; The true number could be one higher than n, the integer result. 7484 7485 RST _FP_CALC ;; x, e. 7486 DEFB __stk_data ;; 7487 DEFB $78 ;;Exponent: $88, Bytes: 2 7488 DEFB $00,$80 ;;(+00,+00) x, e, 128.5. 7489 DEFB __subtract ;; x, e -.5. 7490 DEFB __stk_data ;; 7491 DEFB $EF ;;Exponent: $7F, Bytes: 4 7492 DEFB $1A,$20,$9A,$85 ;; .30103 (log10 2) 7493 DEFB __multiply ;; x, 7494 DEFB __int ;; 7495 DEFB __st_mem_1 ;; x, n. 7496 7497 7498 DEFB __stk_data ;; 7499 DEFB $34 ;;Exponent: $84, Bytes: 1 7500 DEFB $00 ;;(+00,+00,+00) x, n, 8. 7501 7502 DEFB __subtract ;; x, n-8. 7503 DEFB __negate ;; x, 8-n. 7504 DEFB __e_to_fp ;; x * (10^n) 7505 7506 ; finally the 8 or 9 digit decimal is rounded. 7507 ; a ten-digit integer can arise in the case of, say, 999999999.5 7508 ; which gives 1000000000. 7509 7510 DEFB __stk_half ;; 7511 DEFB __addition ;; 7512 DEFB __int ;; i. 7513 DEFB __end_calc ;; 7514 7515 ; If there were 8 digits then final rounding will take place on the calculator 7516 ; stack above and the next two instructions insert a masked zero so that 7517 ; no further rounding occurs. If the result is a 9 digit integer then 7518 ; rounding takes place within the buffer. 7519 7520 LD HL,$406B ; address system variable MEM_2_5th 7521 ; which could be the 'ninth' digit. 7522 LD (HL),$90 ; insert the value $90 10010000 7523 7524 ; now starting from lowest digit lay down the 8, 9 or 10 digit integer 7525 ; which represents the significant portion of the number 7526 ; e.g. PI will be the nine-digit integer 314159265 7527 7528 LD B,10 ; count is ten digits. 7529 7530 mark_1615: 7531 PF_LOOP: 7532 INC HL ; increase pointer 7533 7534 PUSH HL ; preserve buffer address. 7535 PUSH BC ; preserve counter. 7536 7537 RST _FP_CALC ;; i. 7538 DEFB __stk_ten ;; i, 10. 7539 DEFB __n_mod_m ;; i mod 10, i/10 7540 DEFB __exchange ;; i/10, remainder. 7541 DEFB __end_calc ;; 7542 7543 CALL FP_TO_A ; $00-$09 7544 7545 OR $90 ; make left hand nibble 9 7546 7547 POP BC ; restore counter 7548 POP HL ; restore buffer address. 7549 7550 LD (HL),A ; insert masked digit in buffer. 7551 DJNZ PF_LOOP ; loop back for all ten 7552 7553 ; the most significant digit will be last but if the number is exhausted then 7554 ; the last one or two positions will contain zero ($90). 7555 7556 ; e.g. for 'one' we have zero as estimate of leading digits. 7557 ; 1*10^8 100000000 as integer value 7558 ; 90 90 90 90 90 90 90 90 91 90 as buffer mem3/mem4 contents. 7559 7560 7561 INC HL ; advance pointer to one past buffer 7562 LD BC,$0008 ; set C to 8 ( B is already zero ) 7563 PUSH HL ; save pointer. 7564 7565 mark_162C: 7566 PF_NULL: 7567 DEC HL ; decrease pointer 7568 LD A,(HL) ; fetch masked digit 7569 CP $90 ; is it a leading zero ? 7570 JR Z,PF_NULL ; loop back if so 7571 7572 ; at this point a significant digit has been found. carry is reset. 7573 7574 SBC HL,BC ; subtract eight from the address. 7575 PUSH HL ; ** save this pointer too 7576 LD A,(HL) ; fetch addressed byte 7577 ADD A,$6B ; add $6B - forcing a round up ripple 7578 ; if $95 or over. 7579 PUSH AF ; save the carry result. 7580 7581 ; now enter a loop to round the number. After rounding has been considered 7582 ; a zero that has arisen from rounding or that was present at that position 7583 ; originally is changed from $90 to $80. 7584 7585 mark_1639: 7586 PF_RND_LP: 7587 POP AF ; retrieve carry from machine stack. 7588 INC HL ; increment address 7589 LD A,(HL) ; fetch new byte 7590 ADC A,0 ; add in any carry 7591 7592 DAA ; decimal adjust accumulator 7593 ; carry will ripple through the '9' 7594 7595 PUSH AF ; save carry on machine stack. 7596 AND $0F ; isolate character 0 - 9 AND set zero flag 7597 ; if zero. 7598 LD (HL),A ; place back in location. 7599 SET 7,(HL) ; set bit 7 to show printable. 7600 ; but not if trailing zero after decimal point. 7601 JR Z,PF_RND_LP ; back if a zero 7602 ; to consider further rounding and/or trailing 7603 ; zero identification. 7604 7605 POP AF ; balance stack 7606 POP HL ; ** retrieve lower pointer 7607 7608 ; now insert 6 trailing zeros which are printed if before the decimal point 7609 ; but mark the end of printing if after decimal point. 7610 ; e.g. 9876543210123 is printed as 9876543200000 7611 ; 123.456001 is printed as 123.456 7612 7613 LD B,6 ; the count is six. 7614 7615 mark_164B: 7616 PF_ZERO_6: 7617 LD (HL),$80 ; insert a masked zero 7618 DEC HL ; decrease pointer. 7619 DJNZ PF_ZERO_6 ; loop back for all six 7620 7621 ; n-mod-m reduced the number to zero and this is now deleted from the calculator 7622 ; stack before fetching the original estimate of leading digits. 7623 7624 7625 RST _FP_CALC ;; 0. 7626 DEFB __delete ;; . 7627 DEFB __get_mem_1 ;; n. 7628 DEFB __end_calc ;; n. 7629 7630 CALL FP_TO_A 7631 JR Z,PF_POS ; skip forward if positive 7632 7633 NEG ; negate makes positive 7634 7635 mark_165B: 7636 PF_POS: 7637 LD E,A ; transfer count of digits to E 7638 INC E ; increment twice 7639 INC E ; 7640 POP HL ; * retrieve pointer to one past buffer. 7641 7642 mark_165F: 7643 GET_FIRST: 7644 DEC HL ; decrement address. 7645 DEC E ; decrement digit counter. 7646 LD A,(HL) ; fetch masked byte. 7647 AND $0F ; isolate right-hand nibble. 7648 JR Z,GET_FIRST ; back with leading zero 7649 7650 ; now determine if E-format printing is needed 7651 7652 LD A,E ; transfer now accurate number count to A. 7653 SUB 5 ; subtract five 7654 CP 8 ; compare with 8 as maximum digits is 13. 7655 JP P,PF_E_FMT ; forward if positive to PF_E_FMT 7656 7657 CP $F6 ; test for more than four zeros after point. 7658 JP M,PF_E_FMT ; forward if so to PF_E_FMT 7659 7660 ADD A,6 ; test for zero leading digits, e.g. 0.5 7661 JR Z,PF_ZERO_1 ; forward if so to PF_ZERO_1 7662 7663 JP M,PF_ZEROS ; forward if more than one zero to PF_ZEROS 7664 7665 ; else digits before the decimal point are to be printed 7666 7667 LD B,A ; count of leading characters to B. 7668 7669 mark_167B: 7670 PF_NIB_LP: 7671 CALL PF_NIBBLE 7672 DJNZ PF_NIB_LP ; loop back for counted numbers 7673 7674 JR PF_DC_OUT ; forward to consider decimal part to PF_DC_OUT 7675 7676 ; ___ 7677 7678 mark_1682: 7679 PF_E_FMT: 7680 LD B,E ; count to B 7681 CALL PF_NIBBLE ; prints one digit. 7682 CALL PF_DC_OUT ; considers fractional part. 7683 7684 LD A,ZX_E ; 7685 RST _PRINT_A 7686 7687 LD A,B ; transfer exponent to A 7688 AND A ; test the sign. 7689 JP P,PF_E_POS ; forward if positive to PF_E_POS 7690 7691 NEG ; negate the negative exponent. 7692 LD B,A ; save positive exponent in B. 7693 7694 LD A,ZX_MINUS ; 7695 JR PF_E_SIGN ; skip forward to PF_E_SIGN 7696 7697 ; ___ 7698 7699 mark_1698: 7700 PF_E_POS: 7701 LD A,ZX_PLUS ; 7702 7703 mark_169A: 7704 PF_E_SIGN: 7705 RST _PRINT_A 7706 7707 ; now convert the integer exponent in B to two characters. 7708 ; it will be less than 99. 7709 7710 LD A,B ; fetch positive exponent. 7711 LD B,$FF ; initialize left hand digit to minus one. 7712 7713 mark_169E: 7714 PF_E_TENS: 7715 INC B ; increment ten count 7716 SUB 10 ; subtract ten from exponent 7717 JR NC,PF_E_TENS ; loop back if greater than ten 7718 7719 ADD A,10 ; reverse last subtraction 7720 LD C,A ; transfer remainder to C 7721 7722 LD A,B ; transfer ten value to A. 7723 AND A ; test for zero. 7724 JR Z,PF_E_LOW ; skip forward if so to PF_E_LOW 7725 7726 CALL OUT_CODE ; prints as digit '1' - '9' 7727 7728 mark_16AD: 7729 PF_E_LOW: 7730 LD A,C ; low byte to A 7731 CALL OUT_CODE ; prints final digit of the 7732 ; exponent. 7733 RET ; return. >> 7734 7735 7736 ; THE 'FLOATING POINT PRINT ZEROS' LOOP 7737 ; ------------------------------------- 7738 ; This branch deals with zeros after decimal point. 7739 ; e.g. .01 or .0000999 7740 ; Note. that printing to the ZX Printer destroys A and that A should be 7741 ; initialized to '0' at each stage of the loop. 7742 ; Originally LPRINT .00001 printed as .0XYZ1 7743 7744 mark_16B2: 7745 PF_ZEROS: 7746 NEG ; negate makes number positive 1 to 4. 7747 LD B,A ; zero count to B. 7748 7749 LD A,ZX_PERIOD ; prepare character '.' 7750 RST _PRINT_A 7751 7752 7753 if ORIGINAL 7754 LD A,ZX_0 ; prepare a '0' 7755 PFZROLP: 7756 RST _PRINT_A 7757 DJNZ PFZROLP ; obsolete loop back to PFZROLP 7758 else 7759 PF_ZRO_LP: 7760 LD A,ZX_0 ; prepare a '0' in the accumulator each time. 7761 RST _PRINT_A 7762 DJNZ PF_ZRO_LP ;+ New loop back to PF-ZRO-LP 7763 endif 7764 7765 JR PF_FRAC_LP ; forward 7766 7767 ; there is a need to print a leading zero e.g. 0.1 but not with .01 7768 7769 mark_16BF: 7770 PF_ZERO_1: 7771 LD A,ZX_0 ; prepare character '0'. 7772 RST _PRINT_A 7773 7774 ; this subroutine considers the decimal point and any trailing digits. 7775 ; if the next character is a marked zero, $80, then nothing more to print. 7776 7777 mark_16C2: 7778 PF_DC_OUT: 7779 DEC (HL) ; decrement addressed character 7780 INC (HL) ; increment it again 7781 RET PE ; return with overflow (was 128) >> 7782 ; as no fractional part 7783 7784 ; else there is a fractional part so print the decimal point. 7785 7786 LD A,ZX_PERIOD ; prepare character '.' 7787 RST _PRINT_A 7788 7789 ; now enter a loop to print trailing digits 7790 7791 mark_16C8: 7792 PF_FRAC_LP: 7793 DEC (HL) ; test for a marked zero. 7794 INC (HL) ; 7795 RET PE ; return when digits exhausted >> 7796 7797 CALL PF_NIBBLE 7798 JR PF_FRAC_LP ; back for all fractional digits 7799 7800 ; ___ 7801 7802 ; subroutine to print right-hand nibble 7803 7804 mark_16D0: 7805 PF_NIBBLE: 7806 LD A,(HL) ; fetch addressed byte 7807 AND $0F ; mask off lower 4 bits 7808 CALL OUT_CODE 7809 DEC HL ; decrement pointer. 7810 RET ; return. 7811 7812 7813 7814 ; THE 'PREPARE TO ADD' SUBROUTINE 7815 7816 ; This routine is called twice to prepare each floating point number for 7817 ; addition, in situ, on the calculator stack. 7818 ; The exponent is picked up from the first byte which is then cleared to act 7819 ; as a sign byte and accept any overflow. 7820 ; If the exponent is zero then the number is zero and an early return is made. 7821 ; The now redundant sign bit of the mantissa is set and if the number is 7822 ; negative then all five bytes of the number are twos-complemented to prepare 7823 ; the number for addition. 7824 ; On the second invocation the exponent of the first number is in B. 7825 7826 7827 mark_16D8: 7828 PREP_ADD: 7829 LD A,(HL) ; fetch exponent. 7830 LD (HL),0 ; make this byte zero to take any overflow and 7831 ; default to positive. 7832 AND A ; test stored exponent for zero. 7833 RET Z ; return with zero flag set if number is zero. 7834 7835 INC HL ; point to first byte of mantissa. 7836 BIT 7,(HL) ; test the sign bit. 7837 SET 7,(HL) ; set it to its implied state. 7838 DEC HL ; set pointer to first byte again. 7839 RET Z ; return if bit indicated number is positive.>> 7840 7841 ; if negative then all five bytes are twos complemented starting at LSB. 7842 7843 PUSH BC ; save B register contents. 7844 LD BC,$0005 ; set BC to five. 7845 ADD HL,BC ; point to location after 5th byte. 7846 LD B,C ; set the B counter to five. 7847 LD C,A ; store original exponent in C. 7848 SCF ; set carry flag so that one is added. 7849 7850 ; now enter a loop to twos_complement the number. 7851 ; The first of the five bytes becomes $FF to denote a negative number. 7852 7853 mark_16EC: 7854 NEG_BYTE: 7855 DEC HL ; point to first or more significant byte. 7856 LD A,(HL) ; fetch to accumulator. 7857 CPL ; complement. 7858 ADC A,0 ; add in initial carry or any subsequent carry. 7859 LD (HL),A ; place number back. 7860 DJNZ NEG_BYTE ; loop back five times 7861 7862 LD A,C ; restore the exponent to accumulator. 7863 POP BC ; restore B register contents. 7864 7865 RET ; return. 7866 7867 7868 ; THE 'FETCH TWO NUMBERS' SUBROUTINE 7869 7870 ; This routine is used by addition, multiplication and division to fetch 7871 ; the two five_byte numbers addressed by HL and DE from the calculator stack 7872 ; into the Z80 registers. 7873 ; The HL register may no longer point to the first of the two numbers. 7874 ; Since the 32-bit addition operation is accomplished using two Z80 16-bit 7875 ; instructions, it is important that the lower two bytes of each mantissa are 7876 ; in one set of registers and the other bytes all in the alternate set. 7877 ; 7878 ; In: HL = highest number, DE= lowest number 7879 ; 7880 ; : alt': 7881 ; : 7882 ; Out: 7883 ; :H,B-C:C,B: num1 7884 ; :L,D-E:D-E: num2 7885 7886 mark_16F7: 7887 FETCH_TWO: 7888 PUSH HL ; save HL 7889 PUSH AF ; save A - result sign when used from division. 7890 7891 LD C,(HL) ; 7892 INC HL ; 7893 LD B,(HL) ; 7894 LD (HL),A ; insert sign when used from multiplication. 7895 INC HL ; 7896 LD A,C ; m1 7897 LD C,(HL) ; 7898 PUSH BC ; PUSH m2 m3 7899 7900 INC HL ; 7901 LD C,(HL) ; m4 7902 INC HL ; 7903 LD B,(HL) ; m5 BC holds m5 m4 7904 7905 EX DE,HL ; make HL point to start of second number. 7906 7907 LD D,A ; m1 7908 LD E,(HL) ; 7909 PUSH DE ; PUSH m1 n1 7910 7911 INC HL ; 7912 LD D,(HL) ; 7913 INC HL ; 7914 LD E,(HL) ; 7915 PUSH DE ; PUSH n2 n3 7916 7917 EXX ; - - - - - - - 7918 7919 POP DE ; POP n2 n3 7920 POP HL ; POP m1 n1 7921 POP BC ; POP m2 m3 7922 7923 EXX ; - - - - - - - 7924 7925 INC HL ; 7926 LD D,(HL) ; 7927 INC HL ; 7928 LD E,(HL) ; DE holds n4 n5 7929 7930 POP AF ; restore saved 7931 POP HL ; registers. 7932 RET ; return. 7933 7934 7935 ; THE 'SHIFT ADDEND' SUBROUTINE 7936 7937 ; The accumulator A contains the difference between the two exponents. 7938 ; This is the lowest of the two numbers to be added 7939 7940 mark_171A: 7941 SHIFT_FP: 7942 AND A ; test difference between exponents. 7943 RET Z ; return if zero. both normal. 7944 7945 CP 33 ; compare with 33 bits. 7946 JR NC,ADDEND_0 ; forward if greater than 32 7947 7948 PUSH BC ; preserve BC - part 7949 LD B,A ; shift counter to B. 7950 7951 ; Now perform B right shifts on the addend L'D'E'D E 7952 ; to bring it into line with the augend H'B'C'C B 7953 7954 mark_1722: 7955 ONE_SHIFT: 7956 EXX ; - - - 7957 SRA L ; 76543210->C bit 7 unchanged. 7958 RR D ; C->76543210->C 7959 RR E ; C->76543210->C 7960 EXX ; - - - 7961 RR D ; C->76543210->C 7962 RR E ; C->76543210->C 7963 DJNZ ONE_SHIFT ; loop back B times 7964 7965 POP BC ; restore BC 7966 RET NC ; return if last shift produced no carry. >> 7967 7968 ; if carry flag was set then accuracy is being lost so round up the addend. 7969 7970 CALL ADD_BACK 7971 RET NZ ; return if not FF 00 00 00 00 7972 7973 ; this branch makes all five bytes of the addend zero and is made during 7974 ; addition when the exponents are too far apart for the addend bits to 7975 ; affect the result. 7976 7977 mark_1736: 7978 ADDEND_0: 7979 EXX ; select alternate set for more significant 7980 ; bytes. 7981 XOR A ; clear accumulator. 7982 7983 7984 ; this entry point (from multiplication) sets four of the bytes to zero or if 7985 ; continuing from above, during addition, then all five bytes are set to zero. 7986 7987 mark_1738: 7988 ZEROS_4_OR_5: 7989 LD L,0 ; set byte 1 to zero. 7990 LD D,A ; set byte 2 to A. 7991 LD E,L ; set byte 3 to zero. 7992 EXX ; select main set 7993 LD DE,$0000 ; set lower bytes 4 and 5 to zero. 7994 RET ; return. 7995 7996 7997 ; THE 'ADD_BACK' SUBROUTINE 7998 7999 ; Called from SHIFT_FP above during addition and after normalization from 8000 ; multiplication. 8001 ; This is really a 32_bit increment routine which sets the zero flag according 8002 ; to the 32-bit result. 8003 ; During addition, only negative numbers like FF FF FF FF FF, 8004 ; the twos-complement version of xx 80 00 00 01 say 8005 ; will result in a full ripple FF 00 00 00 00. 8006 ; FF FF FF FF FF when shifted right is unchanged by SHIFT_FP but sets the 8007 ; carry invoking this routine. 8008 8009 mark_1741: 8010 ADD_BACK: 8011 INC E ; 8012 RET NZ ; 8013 8014 INC D ; 8015 RET NZ ; 8016 8017 EXX ; 8018 INC E ; 8019 JR NZ,ALL_ADDED ; forward if no overflow 8020 8021 INC D ; 8022 8023 mark_174A: 8024 ALL_ADDED: 8025 EXX ; 8026 RET ; return with zero flag set for zero mantissa. 8027 8028 8029 8030 ; THE 'SUBTRACTION' OPERATION 8031 8032 ; just switch the sign of subtrahend and do an add. 8033 8034 mark_174C: 8035 SUBTRACT: 8036 LD A,(DE) ; fetch exponent byte of second number the 8037 ; subtrahend. 8038 AND A ; test for zero 8039 RET Z ; return if zero - first number is result. 8040 8041 INC DE ; address the first mantissa byte. 8042 LD A,(DE) ; fetch to accumulator. 8043 XOR $80 ; toggle the sign bit. 8044 LD (DE),A ; place back on calculator stack. 8045 DEC DE ; point to exponent byte. 8046 ; continue into addition routine. 8047 8048 8049 ; THE 'ADDITION' OPERATION 8050 8051 ; The addition operation pulls out all the stops and uses most of the Z80's 8052 ; registers to add two floating-point numbers. 8053 ; This is a binary operation and on entry, HL points to the first number 8054 ; and DE to the second. 8055 8056 mark_1755: 8057 ADDITION: 8058 EXX ; - - - 8059 PUSH HL ; save the pointer to the next literal. 8060 EXX ; - - - 8061 8062 PUSH DE ; save pointer to second number 8063 PUSH HL ; save pointer to first number - will be the 8064 ; result pointer on calculator stack. 8065 8066 CALL PREP_ADD 8067 LD B,A ; save first exponent byte in B. 8068 EX DE,HL ; switch number pointers. 8069 CALL PREP_ADD 8070 LD C,A ; save second exponent byte in C. 8071 CP B ; compare the exponent bytes. 8072 JR NC,SHIFT_LEN ; forward if second higher 8073 8074 LD A,B ; else higher exponent to A 8075 LD B,C ; lower exponent to B 8076 EX DE,HL ; switch the number pointers. 8077 8078 mark_1769: 8079 SHIFT_LEN: 8080 PUSH AF ; save higher exponent 8081 SUB B ; subtract lower exponent 8082 8083 CALL FETCH_TWO 8084 CALL SHIFT_FP 8085 8086 POP AF ; restore higher exponent. 8087 POP HL ; restore result pointer. 8088 LD (HL),A ; insert exponent byte. 8089 PUSH HL ; save result pointer again. 8090 8091 ; now perform the 32-bit addition using two 16-bit Z80 add instructions. 8092 8093 LD L,B ; transfer low bytes of mantissa individually 8094 LD H,C ; to HL register 8095 8096 ADD HL,DE ; the actual binary addition of lower bytes 8097 8098 ; now the two higher byte pairs that are in the alternate register sets. 8099 8100 EXX ; switch in set 8101 EX DE,HL ; transfer high mantissa bytes to HL register. 8102 8103 ADC HL,BC ; the actual addition of higher bytes with 8104 ; any carry from first stage. 8105 8106 EX DE,HL ; result in DE, sign bytes ($FF or $00) to HL 8107 8108 ; now consider the two sign bytes 8109 8110 LD A,H ; fetch sign byte of num1 8111 8112 ADC A,L ; add including any carry from mantissa 8113 ; addition. 00 or 01 or FE or FF 8114 8115 LD L,A ; result in L. 8116 8117 ; possible outcomes of signs and overflow from mantissa are 8118 ; 8119 ; H + L + carry = L RRA XOR L RRA 8120 8121 ; 00 + 00 = 00 00 00 8122 ; 00 + 00 + carry = 01 00 01 carry 8123 ; FF + FF = FE C FF 01 carry 8124 ; FF + FF + carry = FF C FF 00 8125 ; FF + 00 = FF FF 00 8126 ; FF + 00 + carry = 00 C 80 80 8127 8128 RRA ; C->76543210->C 8129 XOR L ; set bit 0 if shifting required. 8130 8131 EXX ; switch back to main set 8132 EX DE,HL ; full mantissa result now in D'E'D E registers. 8133 POP HL ; restore pointer to result exponent on 8134 ; the calculator stack. 8135 8136 RRA ; has overflow occurred ? 8137 JR NC,TEST_NEG ; skip forward if not 8138 8139 ; if the addition of two positive mantissas produced overflow or if the 8140 ; addition of two negative mantissas did not then the result exponent has to 8141 ; be incremented and the mantissa shifted one place to the right. 8142 8143 LD A,1 ; one shift required. 8144 CALL SHIFT_FP ; performs a single shift 8145 ; rounding any lost bit 8146 INC (HL) ; increment the exponent. 8147 JR Z,ADD_REP_6 ; forward to ADD_REP_6 if the exponent 8148 ; wraps round from FF to zero as number is too 8149 ; big for the system. 8150 8151 ; at this stage the exponent on the calculator stack is correct. 8152 8153 mark_1790: 8154 TEST_NEG: 8155 EXX ; switch in the alternate set. 8156 LD A,L ; load result sign to accumulator. 8157 AND $80 ; isolate bit 7 from sign byte setting zero 8158 ; flag if positive. 8159 EXX ; back to main set. 8160 8161 INC HL ; point to first byte of mantissa 8162 LD (HL),A ; insert $00 positive or $80 negative at 8163 ; position on calculator stack. 8164 8165 DEC HL ; point to exponent again. 8166 JR Z,GO_NC_MLT ; forward if positive to GO_NC_MLT 8167 8168 ; a negative number has to be twos-complemented before being placed on stack. 8169 8170 LD A,E ; fetch lowest (rightmost) mantissa byte. 8171 NEG ; Negate 8172 CCF ; Complement Carry Flag 8173 LD E,A ; place back in register 8174 8175 LD A,D ; ditto 8176 CPL ; 8177 ADC A,0 ; 8178 LD D,A ; 8179 8180 EXX ; switch to higher (leftmost) 16 bits. 8181 8182 LD A,E ; ditto 8183 CPL ; 8184 ADC A,0 ; 8185 LD E,A ; 8186 8187 LD A,D ; ditto 8188 CPL ; 8189 ADC A,0 ; 8190 JR NC,END_COMPL ; forward without overflow to END_COMPL 8191 8192 ; else entire mantissa is now zero. 00 00 00 00 8193 8194 RRA ; set mantissa to 80 00 00 00 8195 EXX ; switch. 8196 INC (HL) ; increment the exponent. 8197 8198 mark_17B3: 8199 ADD_REP_6: 8200 JP Z,REPORT_6 ; jump forward if exponent now zero to REPORT_6 8201 ; 'Number too big' 8202 8203 EXX ; switch back to alternate set. 8204 8205 mark_17B7: 8206 END_COMPL: 8207 LD D,A ; put first byte of mantissa back in DE. 8208 EXX ; switch to main set. 8209 8210 mark_17B9: 8211 GO_NC_MLT: 8212 XOR A ; clear carry flag and 8213 ; clear accumulator so no extra bits carried 8214 ; forward as occurs in multiplication. 8215 8216 JR TEST_NORM ; forward to common code at TEST_NORM 8217 ; but should go straight to NORMALIZE. 8218 8219 8220 8221 ; THE 'PREPARE TO MULTIPLY OR DIVIDE' SUBROUTINE 8222 8223 ; this routine is called twice from multiplication and twice from division 8224 ; to prepare each of the two numbers for the operation. 8225 ; Initially the accumulator holds zero and after the second invocation bit 7 8226 ; of the accumulator will be the sign bit of the result. 8227 8228 mark_17BC: 8229 PREP_MULTIPLY_OR_DIVIDE: 8230 SCF ; set carry flag to signal number is zero. 8231 DEC (HL) ; test exponent 8232 INC (HL) ; for zero. 8233 RET Z ; return if zero with carry flag set. 8234 8235 INC HL ; address first mantissa byte. 8236 XOR (HL) ; exclusive or the running sign bit. 8237 SET 7,(HL) ; set the implied bit. 8238 DEC HL ; point to exponent byte. 8239 RET ; return. 8240 8241 8242 ; THE 'MULTIPLICATION' OPERATION 8243 8244 ; 8245 ; 8246 8247 mark_17C6: 8248 MULTIPLY: 8249 XOR A ; reset bit 7 of running sign flag. 8250 CALL PREP_MULTIPLY_OR_DIVIDE 8251 RET C ; return if number is zero. 8252 ; zero * anything = zero. 8253 8254 EXX ; - - - 8255 PUSH HL ; save pointer to 'next literal' 8256 EXX ; - - - 8257 8258 PUSH DE ; save pointer to second number 8259 8260 EX DE,HL ; make HL address second number. 8261 8262 CALL PREP_MULTIPLY_OR_DIVIDE 8263 8264 EX DE,HL ; HL first number, DE - second number 8265 JR C,ZERO_RESULT ; forward with carry to ZERO_RESULT 8266 ; anything * zero = zero. 8267 8268 PUSH HL ; save pointer to first number. 8269 8270 CALL FETCH_TWO ; fetches two mantissas from 8271 ; calc stack to B'C'C,B D'E'D E 8272 ; (HL will be overwritten but the result sign 8273 ; in A is inserted on the calculator stack) 8274 8275 LD A,B ; transfer low mantissa byte of first number 8276 AND A ; clear carry. 8277 SBC HL,HL ; a short form of LD HL,$0000 to take lower 8278 ; two bytes of result. (2 program bytes) 8279 EXX ; switch in alternate set 8280 PUSH HL ; preserve HL 8281 SBC HL,HL ; set HL to zero also to take higher two bytes 8282 ; of the result and clear carry. 8283 EXX ; switch back. 8284 8285 LD B,33 ; register B can now be used to count 33 shifts. 8286 JR STRT_MLT ; forward to loop entry point STRT_MLT 8287 8288 ; ___ 8289 8290 ; The multiplication loop is entered at STRT_LOOP. 8291 8292 mark_17E7: 8293 MLT_LOOP: 8294 JR NC,NO_ADD ; forward if no carry 8295 8296 ; else add in the multiplicand. 8297 8298 ADD HL,DE ; add the two low bytes to result 8299 EXX ; switch to more significant bytes. 8300 ADC HL,DE ; add high bytes of multiplicand and any carry. 8301 EXX ; switch to main set. 8302 8303 ; in either case shift result right into B'C'C A 8304 8305 mark_17EE: 8306 NO_ADD: 8307 EXX ; switch to alternate set 8308 RR H ; C > 76543210 > C 8309 RR L ; C > 76543210 > C 8310 EXX ; 8311 RR H ; C > 76543210 > C 8312 RR L ; C > 76543210 > C 8313 8314 mark_17F8: 8315 STRT_MLT: 8316 EXX ; switch in alternate set. 8317 RR B ; C > 76543210 > C 8318 RR C ; C > 76543210 > C 8319 EXX ; now main set 8320 RR C ; C > 76543210 > C 8321 RRA ; C > 76543210 > C 8322 DJNZ MLT_LOOP ; loop back 33 timeS 8323 8324 ; 8325 8326 EX DE,HL ; 8327 EXX ; 8328 EX DE,HL ; 8329 EXX ; 8330 POP BC ; 8331 POP HL ; 8332 LD A,B ; 8333 ADD A,C ; 8334 JR NZ,MAKE_EXPT ; forward 8335 8336 AND A ; 8337 8338 mark_180E: 8339 MAKE_EXPT: 8340 DEC A ; 8341 CCF ; Complement Carry Flag 8342 8343 mark_1810: 8344 DIVN_EXPT: 8345 RLA ; 8346 CCF ; Complement Carry Flag 8347 RRA ; 8348 JP P,OFLW1_CLR 8349 8350 JR NC,REPORT_6 8351 8352 AND A ; 8353 8354 mark_1819: 8355 OFLW1_CLR: 8356 INC A ; 8357 JR NZ,OFLW2_CLR 8358 8359 JR C,OFLW2_CLR 8360 8361 EXX ; 8362 BIT 7,D ; 8363 EXX ; 8364 JR NZ,REPORT_6 8365 8366 mark_1824: 8367 OFLW2_CLR: 8368 LD (HL),A ; 8369 EXX ; 8370 LD A,B ; 8371 EXX ; 8372 8373 ; addition joins here with carry flag clear. 8374 8375 mark_1828: 8376 TEST_NORM: 8377 JR NC,NORMALIZE ; forward 8378 8379 LD A,(HL) ; 8380 AND A ; 8381 8382 mark_182C: 8383 NEAR_ZERO: 8384 LD A,$80 ; prepare to rescue the most significant bit 8385 ; of the mantissa if it is set. 8386 JR Z,SKIP_ZERO ; skip forward 8387 8388 mark_1830: 8389 ZERO_RESULT: 8390 XOR A ; make mask byte zero signaling set five 8391 ; bytes to zero. 8392 8393 mark_1831: 8394 SKIP_ZERO: 8395 EXX ; switch in alternate set 8396 AND D ; isolate most significant bit (if A is $80). 8397 8398 CALL ZEROS_4_OR_5 ; sets mantissa without 8399 ; affecting any flags. 8400 8401 RLCA ; test if MSB set. bit 7 goes to bit 0. 8402 ; either $00 -> $00 or $80 -> $01 8403 LD (HL),A ; make exponent $01 (lowest) or $00 zero 8404 JR C,OFLOW_CLR ; forward if first case 8405 8406 INC HL ; address first mantissa byte on the 8407 ; calculator stack. 8408 LD (HL),A ; insert a zero for the sign bit. 8409 DEC HL ; point to zero exponent 8410 JR OFLOW_CLR ; forward 8411 8412 ; ___ 8413 8414 ; this branch is common to addition and multiplication with the mantissa 8415 ; result still in registers D'E'D E . 8416 8417 mark_183F: 8418 NORMALIZE: 8419 LD B,32 ; a maximum of thirty-two left shifts will be 8420 ; needed. 8421 8422 mark_1841: 8423 SHIFT_ONE: 8424 EXX ; address higher 16 bits. 8425 BIT 7,D ; test the leftmost bit 8426 EXX ; address lower 16 bits. 8427 8428 JR NZ,NORML_NOW ; forward if leftmost bit was set 8429 8430 RLCA ; this holds zero from addition, 33rd bit 8431 ; from multiplication. 8432 8433 RL E ; C < 76543210 < C 8434 RL D ; C < 76543210 < C 8435 8436 EXX ; address higher 16 bits. 8437 8438 RL E ; C < 76543210 < C 8439 RL D ; C < 76543210 < C 8440 8441 EXX ; switch to main set. 8442 8443 DEC (HL) ; decrement the exponent byte on the calculator 8444 ; stack. 8445 8446 JR Z,NEAR_ZERO ; back if exponent becomes zero 8447 ; it's just possible that the last rotation 8448 ; set bit 7 of D. We shall see. 8449 8450 DJNZ SHIFT_ONE ; loop back 8451 8452 ; if thirty-two left shifts were performed without setting the most significant 8453 ; bit then the result is zero. 8454 8455 JR ZERO_RESULT ; back 8456 8457 ; ___ 8458 8459 mark_1859: 8460 NORML_NOW: 8461 RLA ; for the addition path, A is always zero. 8462 ; for the mult path, ... 8463 8464 JR NC,OFLOW_CLR ; forward 8465 8466 ; this branch is taken only with multiplication. 8467 8468 CALL ADD_BACK 8469 8470 JR NZ,OFLOW_CLR ; forward 8471 8472 EXX ; 8473 LD D,$80 ; 8474 EXX ; 8475 INC (HL) ; 8476 JR Z,REPORT_6 ; forward 8477 8478 ; now transfer the mantissa from the register sets to the calculator stack 8479 ; incorporating the sign bit already there. 8480 8481 mark_1868: 8482 OFLOW_CLR: 8483 PUSH HL ; save pointer to exponent on stack. 8484 INC HL ; address first byte of mantissa which was 8485 ; previously loaded with sign bit $00 or $80. 8486 8487 EXX ; - - - 8488 PUSH DE ; push the most significant two bytes. 8489 EXX ; - - - 8490 8491 POP BC ; pop - true mantissa is now BCDE. 8492 8493 ; now pick up the sign bit. 8494 8495 LD A,B ; first mantissa byte to A 8496 RLA ; rotate out bit 7 which is set 8497 RL (HL) ; rotate sign bit on stack into carry. 8498 RRA ; rotate sign bit into bit 7 of mantissa. 8499 8500 ; and transfer mantissa from main registers to calculator stack. 8501 8502 LD (HL),A ; 8503 INC HL ; 8504 LD (HL),C ; 8505 INC HL ; 8506 LD (HL),D ; 8507 INC HL ; 8508 LD (HL),E ; 8509 8510 POP HL ; restore pointer to num1 now result. 8511 POP DE ; restore pointer to num2 now STKEND. 8512 8513 EXX ; - - - 8514 POP HL ; restore pointer to next calculator literal. 8515 EXX ; - - - 8516 8517 RET ; return. 8518 8519 ; ___ 8520 8521 mark_1880: 8522 REPORT_6: 8523 RST _ERROR_1 8524 DEFB 5 ; Error Report: Arithmetic overflow. 8525 8526 8527 ; THE 'DIVISION' OPERATION 8528 8529 ; "Of all the arithmetic subroutines, division is the most complicated and 8530 ; the least understood. It is particularly interesting to note that the 8531 ; Sinclair programmer himself has made a mistake in his programming ( or has 8532 ; copied over someone else's mistake!) for 8533 ; PRINT PEEK 6352 [ $18D0 ] ('unimproved' ROM, 6351 [ $18CF ] ) 8534 ; should give 218 not 225." 8535 ; - Dr. Ian Logan, Syntax magazine Jul/Aug 1982. 8536 ; [ i.e. the jump should be made to div-34th ] 8537 8538 ; First check for division by zero. 8539 8540 mark_1882: 8541 DIVISION: 8542 EX DE,HL ; consider the second number first. 8543 XOR A ; set the running sign flag. 8544 CALL PREP_MULTIPLY_OR_DIVIDE 8545 JR C,REPORT_6 ; back if zero 8546 ; 'Arithmetic overflow' 8547 8548 EX DE,HL ; now prepare first number and check for zero. 8549 CALL PREP_MULTIPLY_OR_DIVIDE 8550 RET C ; return if zero, 0/anything is zero. 8551 8552 EXX ; - - - 8553 PUSH HL ; save pointer to the next calculator literal. 8554 EXX ; - - - 8555 8556 PUSH DE ; save pointer to divisor - will be STKEND. 8557 PUSH HL ; save pointer to dividend - will be result. 8558 8559 CALL FETCH_TWO ; fetches the two numbers 8560 ; into the registers H'B'C'C B 8561 ; L'D'E'D E 8562 EXX ; - - - 8563 PUSH HL ; save the two exponents. 8564 8565 LD H,B ; transfer the dividend to H'L'H L 8566 LD L,C ; 8567 EXX ; 8568 LD H,C ; 8569 LD L,B ; 8570 8571 XOR A ; clear carry bit and accumulator. 8572 LD B,$DF ; count upwards from -33 decimal 8573 JR DIVISION_START ; forward to mid-loop entry point 8574 8575 ; ___ 8576 8577 mark_18A2: 8578 DIV_LOOP: 8579 RLA ; multiply partial quotient by two 8580 RL C ; setting result bit from carry. 8581 EXX ; 8582 RL C ; 8583 RL B ; 8584 EXX ; 8585 8586 mark_18AB: 8587 DIV_34TH: 8588 ADD HL,HL ; 8589 EXX ; 8590 ADC HL,HL ; 8591 EXX ; 8592 JR C,SUBN_ONLY ; forward 8593 8594 mark_18B2: 8595 DIVISION_START: 8596 SBC HL,DE ; subtract divisor part. 8597 EXX ; 8598 SBC HL,DE ; 8599 EXX ; 8600 JR NC,NUM_RESTORE ; forward if subtraction goes 8601 8602 ADD HL,DE ; else restore 8603 EXX ; 8604 ADC HL,DE ; 8605 EXX ; 8606 AND A ; clear carry 8607 JR COUNT_ONE ; forward 8608 8609 ; ___ 8610 8611 mark_18C2: 8612 SUBN_ONLY: 8613 AND A ; 8614 SBC HL,DE ; 8615 EXX ; 8616 SBC HL,DE ; 8617 EXX ; 8618 8619 mark_18C9: 8620 NUM_RESTORE: 8621 SCF ; set carry flag 8622 8623 mark_18CA: 8624 COUNT_ONE: 8625 INC B ; increment the counter 8626 JP M,DIV_LOOP ; back while still minus to DIV_LOOP 8627 8628 PUSH AF ; 8629 JR Z,DIVISION_START ; back to DIV_START 8630 8631 ; "This jump is made to the wrong place. No 34th bit will ever be obtained 8632 ; without first shifting the dividend. Hence important results like 1/10 and 8633 ; 1/1000 are not rounded up as they should be. Rounding up never occurs when 8634 ; it depends on the 34th bit. The jump should be made to div_34th above." 8635 ; - Dr. Frank O'Hara, "The Complete Spectrum ROM Disassembly", 1983, 8636 ; published by Melbourne House. 8637 ; (Note. on the ZX81 this would be JR Z,DIV_34TH) 8638 ; 8639 ; However if you make this change, then while (1/2=.5) will now evaluate as 8640 ; true, (.25=1/4), which did evaluate as true, no longer does. 8641 8642 LD E,A ; 8643 LD D,C ; 8644 EXX ; 8645 LD E,C ; 8646 LD D,B ; 8647 8648 POP AF ; 8649 RR B ; 8650 POP AF ; 8651 RR B ; 8652 8653 EXX ; 8654 POP BC ; 8655 POP HL ; 8656 LD A,B ; 8657 SUB C ; 8658 JP DIVN_EXPT ; jump back 8659 8660 8661 ; THE 'INTEGER TRUNCATION TOWARDS ZERO' SUBROUTINE 8662 8663 ; 8664 8665 mark_18E4: 8666 TRUNCATE: 8667 LD A,(HL) ; fetch exponent 8668 CP $81 ; compare to +1 8669 JR NC,T_GR_ZERO ; forward, if 1 or more 8670 8671 ; else the number is smaller than plus or minus 1 and can be made zero. 8672 8673 LD (HL),$00 ; make exponent zero. 8674 LD A,$20 ; prepare to set 32 bits of mantissa to zero. 8675 JR NIL_BYTES ; forward 8676 8677 ; ___ 8678 8679 mark_18EF: 8680 T_GR_ZERO: 8681 SUB $A0 ; subtract +32 from exponent 8682 RET P ; return if result is positive as all 32 bits 8683 ; of the mantissa relate to the integer part. 8684 ; The floating point is somewhere to the right 8685 ; of the mantissa 8686 8687 NEG ; else negate to form number of rightmost bits 8688 ; to be blanked. 8689 8690 ; for instance, disregarding the sign bit, the number 3.5 is held as 8691 ; exponent $82 mantissa .11100000 00000000 00000000 00000000 8692 ; we need to set $82 - $A0 = $E2 NEG = $1E (thirty) bits to zero to form the 8693 ; integer. 8694 ; The sign of the number is never considered as the first bit of the mantissa 8695 ; must be part of the integer. 8696 8697 mark_18F4: 8698 NIL_BYTES: 8699 PUSH DE ; save pointer to STKEND 8700 EX DE,HL ; HL points at STKEND 8701 DEC HL ; now at last byte of mantissa. 8702 LD B,A ; Transfer bit count to B register. 8703 SRL B ; divide by 8704 SRL B ; eight 8705 SRL B ; 8706 JR Z,BITS_ZERO ; forward if zero 8707 8708 ; else the original count was eight or more and whole bytes can be blanked. 8709 8710 mark_1900: 8711 BYTE_ZERO: 8712 LD (HL),0 ; set eight bits to zero. 8713 DEC HL ; point to more significant byte of mantissa. 8714 DJNZ BYTE_ZERO ; loop back 8715 8716 ; now consider any residual bits. 8717 8718 mark_1905: 8719 BITS_ZERO: 8720 AND $07 ; isolate the remaining bits 8721 JR Z,IX_END ; forward if none 8722 8723 LD B,A ; transfer bit count to B counter. 8724 LD A,$FF ; form a mask 11111111 8725 8726 mark_190C: 8727 LESS_MASK: 8728 SLA A ; 1 <- 76543210 <- o slide mask leftwards. 8729 DJNZ LESS_MASK ; loop back for bit count 8730 8731 AND (HL) ; lose the unwanted rightmost bits 8732 LD (HL),A ; and place in mantissa byte. 8733 8734 mark_1912: 8735 IX_END: 8736 EX DE,HL ; restore result pointer from DE. 8737 POP DE ; restore STKEND from stack. 8738 RET ; return. 8739 8740 8741 ; Up to this point all routine addresses have been maintained so that the 8742 ; modified ROM is compatible with any machine-code software that uses ROM 8743 ; routines. 8744 ; The final section does not maintain address entry points as the routines 8745 ; within are not generally called directly. 8746 8747 ;** FLOATING-POINT CALCULATOR ** 8748 ;******************************** 8749 ; As a general rule the calculator avoids using the IY register. 8750 ; Exceptions are val and str$. 8751 ; So an assembly language programmer who has disabled interrupts to use IY 8752 ; for other purposes can still use the calculator for mathematical 8753 ; purposes. 8754 8755 ; THE 'TABLE OF CONSTANTS' 8756 8757 ; The ZX81 has only floating-point number representation. 8758 ; Both the ZX80 and the ZX Spectrum have integer numbers in some form. 8759 8760 8761 TAB_CNST: 8762 8763 if ORIGINAL 8764 mark_1915: 8765 ; 00 00 00 00 00 8766 stk_zero: 8767 DEFB $00 ;;Bytes: 1 8768 DEFB $B0 ;;Exponent $00 8769 DEFB $00 ;;(+00,+00,+00) 8770 8771 mark_1918: 8772 ; 81 00 00 00 00 8773 stk_one: 8774 DEFB $31 ;;Exponent $81, Bytes: 1 8775 DEFB $00 ;;(+00,+00,+00) 8776 8777 8778 mark_191A: 8779 ; 80 00 00 00 00 8780 stk_half: 8781 DEFB $30 ;;Exponent: $80, Bytes: 1 8782 DEFB $00 ;;(+00,+00,+00) 8783 8784 8785 mark_191C: 8786 ; 81 49 0F DA A2 8787 stk_half_pi: 8788 DEFB $F1 ;;Exponent: $81, Bytes: 4 8789 DEFB $49,$0F,$DA,$A2 ;; 8790 8791 mark_1921: 8792 ; 84 20 00 00 00 8793 stk_ten: 8794 DEFB $34 ;;Exponent: $84, Bytes: 1 8795 DEFB $20 ;;(+00,+00,+00) 8796 else 8797 ; This table has been modified so that the constants are held in their 8798 ; uncompressed, ready-to-use, 5-byte form. 8799 8800 DEFB $00 ; the value zero. 8801 DEFB $00 ; 8802 DEFB $00 ; 8803 DEFB $00 ; 8804 DEFB $00 ; 8805 8806 DEFB $81 ; the floating point value 1. 8807 DEFB $00 ; 8808 DEFB $00 ; 8809 DEFB $00 ; 8810 DEFB $00 ; 8811 8812 DEFB $80 ; the floating point value 1/2. 8813 DEFB $00 ; 8814 DEFB $00 ; 8815 DEFB $00 ; 8816 DEFB $00 ; 8817 8818 DEFB $81 ; the floating point value pi/2. 8819 DEFB $49 ; 8820 DEFB $0F ; 8821 DEFB $DA ; 8822 DEFB $A2 ; 8823 8824 DEFB $84 ; the floating point value ten. 8825 DEFB $20 ; 8826 DEFB $00 ; 8827 DEFB $00 ; 8828 DEFB $00 ; 8829 endif 8830 8831 8832 ; THE 'TABLE OF ADDRESSES' 8833 8834 ; 8835 ; starts with binary operations which have two operands and one result. 8836 ; three pseudo binary operations first. 8837 8838 if ORIGINAL 8839 mark_1923: 8840 else 8841 endif 8842 8843 tbl_addrs: 8844 8845 DEFW jump_true ; $00 Address: $1C2F - jump_true 8846 DEFW exchange ; $01 Address: $1A72 - exchange 8847 DEFW delete ; $02 Address: $19E3 - delete 8848 8849 ; true binary operations. 8850 8851 DEFW SUBTRACT ; $03 Address: $174C - subtract 8852 DEFW MULTIPLY ; $04 Address: $176C - multiply 8853 DEFW DIVISION ; $05 Address: $1882 - division 8854 DEFW to_power ; $06 Address: $1DE2 - to_power 8855 DEFW or ; $07 Address: $1AED - or 8856 8857 DEFW boolean_num_and_num ; $08 Address: $1AF3 - boolean_num_and_num 8858 DEFW num_l_eql ; $09 Address: $1B03 - num_l_eql 8859 DEFW num_gr_eql ; $0A Address: $1B03 - num_gr_eql 8860 DEFW nums_neql ; $0B Address: $1B03 - nums_neql 8861 DEFW num_grtr ; $0C Address: $1B03 - num_grtr 8862 DEFW num_less ; $0D Address: $1B03 - num_less 8863 DEFW nums_eql ; $0E Address: $1B03 - nums_eql 8864 DEFW ADDITION ; $0F Address: $1755 - addition 8865 8866 DEFW strs_and_num ; $10 Address: $1AF8 - str_and_num 8867 DEFW str_l_eql ; $11 Address: $1B03 - str_l_eql 8868 DEFW str_gr_eql ; $12 Address: $1B03 - str_gr_eql 8869 DEFW strs_neql ; $13 Address: $1B03 - strs_neql 8870 DEFW str_grtr ; $14 Address: $1B03 - str_grtr 8871 DEFW str_less ; $15 Address: $1B03 - str_less 8872 DEFW strs_eql ; $16 Address: $1B03 - strs_eql 8873 DEFW strs_add ; $17 Address: $1B62 - strs_add 8874 8875 ; unary follow 8876 8877 DEFW neg ; $18 8878 DEFW code ; $19 8879 DEFW val ; $1A 8880 DEFW len ; $1B 8881 DEFW sin ; $1C 8882 DEFW cos ; $1D 8883 DEFW tan ; $1E 8884 DEFW asn ; $1F 8885 DEFW acs ; $20 8886 DEFW atn ; $21 8887 DEFW ln ; $22 8888 DEFW exp ; $23 8889 DEFW int ; $24 8890 DEFW sqr ; $25 8891 DEFW sgn ; $26 8892 DEFW abs ; $27 8893 DEFW PEEK ; $28 Address: $1A1B - peek !!!! 8894 DEFW usr_num ; $29 8895 DEFW str_dollar ; $2A 8896 DEFW chr_dollar ; $2B 8897 DEFW not ; $2C 8898 8899 ; end of true unary 8900 8901 DEFW duplicate ; $2D 8902 DEFW n_mod_m ; $2E 8903 8904 DEFW jump ; $2F 8905 DEFW stk_data ; $30 8906 8907 DEFW dec_jr_nz ; $31 8908 DEFW less_0 ; $32 8909 DEFW greater_0 ; $33 8910 DEFW end_calc ; $34 8911 DEFW get_argt ; $35 8912 DEFW TRUNCATE ; $36 8913 DEFW FP_CALC_2 ; $37 8914 DEFW e_to_fp ; $38 8915 8916 ; the following are just the next available slots for the 128 compound literals 8917 ; which are in range $80 - $FF. 8918 8919 DEFW series_xx ; $39 : $80 - $9F. 8920 DEFW stk_const_xx ; $3A : $A0 - $BF. 8921 DEFW st_mem_xx ; $3B : $C0 - $DF. 8922 DEFW get_mem_xx ; $3C : $E0 - $FF. 8923 8924 ; Aside: 3D - 7F are therefore unused calculator literals. 8925 ; 39 - 7B would be available for expansion. 8926 8927 8928 ; THE 'FLOATING POINT CALCULATOR' 8929 8930 ; 8931 ; 8932 8933 mark_199D: 8934 CALCULATE: 8935 CALL STACK_POINTERS ; is called to set up the 8936 ; calculator stack pointers for a default 8937 ; unary operation. HL = last value on stack. 8938 ; DE = STKEND first location after stack. 8939 8940 ; the calculate routine is called at this point by the series generator... 8941 8942 mark_19A0: 8943 GEN_ENT_1: 8944 LD A,B ; fetch the Z80 B register to A 8945 LD (BERG),A ; and store value in system variable BERG. 8946 ; this will be the counter for dec_jr_nz 8947 ; or if used from FP_CALC2 the calculator 8948 ; instruction. 8949 8950 ; ... and again later at this point 8951 8952 mark_19A4: 8953 GEN_ENT_2: 8954 EXX ; switch sets 8955 EX (SP),HL ; and store the address of next instruction, 8956 ; the return address, in H'L'. 8957 ; If this is a recursive call then the H'L' 8958 ; of the previous invocation goes on stack. 8959 ; c.f. end_calc. 8960 EXX ; switch back to main set. 8961 8962 ; this is the re-entry looping point when handling a string of literals. 8963 8964 mark_19A7: 8965 RE_ENTRY: 8966 LD (STKEND),DE ; save end of stack 8967 EXX ; switch to alt 8968 LD A,(HL) ; get next literal 8969 INC HL ; increase pointer' 8970 8971 ; single operation jumps back to here 8972 8973 mark_19AE: 8974 SCAN_ENT: 8975 PUSH HL ; save pointer on stack * 8976 AND A ; now test the literal 8977 JP P,FIRST_3D ; forward if in range $00 - $3D 8978 ; anything with bit 7 set will be one of 8979 ; 128 compound literals. 8980 8981 ; compound literals have the following format. 8982 ; bit 7 set indicates compound. 8983 ; bits 6-5 the subgroup 0-3. 8984 ; bits 4-0 the embedded parameter $00 - $1F. 8985 ; The subgroup 0-3 needs to be manipulated to form the next available four 8986 ; address places after the simple literals in the address table. 8987 8988 LD D,A ; save literal in D 8989 AND $60 ; and with 01100000 to isolate subgroup 8990 RRCA ; rotate bits 8991 RRCA ; 4 places to right 8992 RRCA ; not five as we need offset * 2 8993 RRCA ; 00000xx0 8994 ADD A,$72 ; add ($39 * 2) to give correct offset. 8995 ; alter above if you add more literals. 8996 LD L,A ; store in L for later indexing. 8997 LD A,D ; bring back compound literal 8998 AND $1F ; use mask to isolate parameter bits 8999 JR ENT_TABLE ; forward 9000 9001 ; ___ 9002 9003 ; the branch was here with simple literals. 9004 9005 mark_19C2: 9006 FIRST_3D: 9007 CP $18 ; compare with first unary operations. 9008 JR NC,DOUBLE_A ; with unary operations 9009 9010 ; it is binary so adjust pointers. 9011 9012 EXX ; 9013 LD BC,-5 9014 LD D,H ; transfer HL, the last value, to DE. 9015 LD E,L ; 9016 ADD HL,BC ; subtract 5 making HL point to second 9017 ; value. 9018 EXX ; 9019 9020 mark_19CE: 9021 DOUBLE_A: 9022 RLCA ; double the literal 9023 LD L,A ; and store in L for indexing 9024 9025 mark_19D0: 9026 ENT_TABLE: 9027 LD DE,tbl_addrs ; Address: tbl_addrs 9028 LD H,$00 ; prepare to index 9029 ADD HL,DE ; add to get address of routine 9030 LD E,(HL) ; low byte to E 9031 INC HL ; 9032 LD D,(HL) ; high byte to D 9033 9034 LD HL,RE_ENTRY 9035 EX (SP),HL ; goes on machine stack 9036 ; address of next literal goes to HL. * 9037 9038 9039 PUSH DE ; now the address of routine is stacked. 9040 EXX ; back to main set 9041 ; avoid using IY register. 9042 LD BC,(STKEND+1) ; STKEND_hi 9043 ; nothing much goes to C but BERG to B 9044 ; and continue into next ret instruction 9045 ; which has a dual identity 9046 9047 9048 9049 ; THE 'DELETE' SUBROUTINE 9050 9051 ; offset $02: 'delete' 9052 ; A simple return but when used as a calculator literal this 9053 ; deletes the last value from the calculator stack. 9054 ; On entry, as always with binary operations, 9055 ; HL=first number, DE=second number 9056 ; On exit, HL=result, DE=stkend. 9057 ; So nothing to do 9058 9059 mark_19E3: 9060 delete: 9061 RET ; return - indirect jump if from above. 9062 9063 9064 ; THE 'SINGLE OPERATION' SUBROUTINE 9065 9066 ; offset $37: 'FP_CALC_2' 9067 ; this single operation is used, in the first instance, to evaluate most 9068 ; of the mathematical and string functions found in BASIC expressions. 9069 9070 mark_19E4: 9071 FP_CALC_2: 9072 POP AF ; drop return address. 9073 LD A,(BERG) ; load accumulator from system variable BERG 9074 ; value will be literal eg. 'tan' 9075 EXX ; switch to alt 9076 JR SCAN_ENT ; back 9077 ; next literal will be end_calc in scanning 9078 9079 9080 ; THE 'TEST 5 SPACES' SUBROUTINE 9081 9082 ; This routine is called from MOVE_FP, STK_CONST and STK_STORE to 9083 ; test that there is enough space between the calculator stack and the 9084 ; machine stack for another five_byte value. It returns with BC holding 9085 ; the value 5 ready for any subsequent LDIR. 9086 9087 mark_19EB: 9088 TEST_5_SP: 9089 PUSH DE ; save 9090 PUSH HL ; registers 9091 LD BC,5 ; an overhead of five bytes 9092 CALL TEST_ROOM ; tests free RAM raising 9093 ; an error if not. 9094 POP HL ; else restore 9095 POP DE ; registers. 9096 RET ; return with BC set at 5. 9097 9098 9099 9100 ; THE 'MOVE A FLOATING POINT NUMBER' SUBROUTINE 9101 9102 ; offset $2D: 'duplicate' 9103 ; This simple routine is a 5-byte LDIR instruction 9104 ; that incorporates a memory check. 9105 ; When used as a calculator literal it duplicates the last value on the 9106 ; calculator stack. 9107 ; Unary so on entry HL points to last value, DE to stkend 9108 9109 mark_19F6: 9110 duplicate: 9111 MOVE_FP: 9112 CALL TEST_5_SP ; test free memory 9113 ; and sets BC to 5. 9114 LDIR ; copy the five bytes. 9115 RET ; return with DE addressing new STKEND 9116 ; and HL addressing new last value. 9117 9118 9119 ; THE 'STACK LITERALS' SUBROUTINE 9120 9121 ; offset $30: 'stk_data' 9122 ; When a calculator subroutine needs to put a value on the calculator 9123 ; stack that is not a regular constant this routine is called with a 9124 ; variable number of following data bytes that convey to the routine 9125 ; the floating point form as succinctly as is possible. 9126 9127 mark_19FC: 9128 stk_data: 9129 LD H,D ; transfer STKEND 9130 LD L,E ; to HL for result. 9131 9132 mark_19FE: 9133 STK_CONST: 9134 CALL TEST_5_SP ; tests that room exists 9135 ; and sets BC to $05. 9136 9137 EXX ; switch to alternate set 9138 PUSH HL ; save the pointer to next literal on stack 9139 EXX ; switch back to main set 9140 9141 EX (SP),HL ; pointer to HL, destination to stack. 9142 9143 if ORIGINAL 9144 PUSH BC ; save BC - value 5 from test room ??. 9145 else 9146 ;; PUSH BC ; save BC - value 5 from test room. No need. 9147 endif 9148 LD A,(HL) ; fetch the byte following 'stk_data' 9149 AND $C0 ; isolate bits 7 and 6 9150 RLCA ; rotate 9151 RLCA ; to bits 1 and 0 range $00 - $03. 9152 LD C,A ; transfer to C 9153 INC C ; and increment to give number of bytes 9154 ; to read. $01 - $04 9155 LD A,(HL) ; reload the first byte 9156 AND $3F ; mask off to give possible exponent. 9157 JR NZ,FORM_EXP ; forward to FORM_EXP if it was possible to 9158 ; include the exponent. 9159 9160 ; else byte is just a byte count and exponent comes next. 9161 9162 INC HL ; address next byte and 9163 LD A,(HL) ; pick up the exponent ( - $50). 9164 9165 mark_1A14: 9166 FORM_EXP: 9167 ADD A,$50 ; now add $50 to form actual exponent 9168 LD (DE),A ; and load into first destination byte. 9169 LD A,$05 ; load accumulator with $05 and 9170 SUB C ; subtract C to give count of trailing 9171 ; zeros plus one. 9172 INC HL ; increment source 9173 INC DE ; increment destination 9174 9175 9176 if ORIGINAL 9177 LD B,$00 ; prepare to copy. Note. B is zero. 9178 LDIR ; copy C bytes 9179 POP BC ; restore 5 counter to BC. 9180 else 9181 LDIR ; copy C bytes 9182 endif 9183 9184 EX (SP),HL ; put HL on stack as next literal pointer 9185 ; and the stack value - result pointer - 9186 ; to HL. 9187 9188 EXX ; switch to alternate set. 9189 POP HL ; restore next literal pointer from stack 9190 ; to H'L'. 9191 EXX ; switch back to main set. 9192 9193 LD B,A ; zero count to B 9194 XOR A ; clear accumulator 9195 9196 mark_1A27: 9197 STK_ZEROS: 9198 DEC B ; decrement B counter 9199 RET Z ; return if zero. >> 9200 ; DE points to new STKEND 9201 ; HL to new number. 9202 9203 LD (DE),A ; else load zero to destination 9204 INC DE ; increase destination 9205 JR STK_ZEROS ; loop back until done. 9206 9207 9208 ; THE 'SKIP CONSTANTS' SUBROUTINE 9209 9210 ; This routine traverses variable-length entries in the table of constants, 9211 ; stacking intermediate, unwanted constants onto a dummy calculator stack, 9212 ; in the first five bytes of the ZX81 ROM. 9213 9214 if ORIGINAL 9215 mark_1A2D: 9216 SKIP_CONS: 9217 AND A ; test if initially zero. 9218 9219 mark_1A2E: 9220 SKIP_NEXT: 9221 RET Z ; return if zero. >> 9222 9223 PUSH AF ; save count. 9224 PUSH DE ; and normal STKEND 9225 9226 LD DE,$0000 ; dummy value for STKEND at start of ROM 9227 ; Note. not a fault but this has to be 9228 ; moved elsewhere when running in RAM. 9229 ; 9230 CALL STK_CONST ; works through variable 9231 ; length records. 9232 9233 POP DE ; restore real STKEND 9234 POP AF ; restore count 9235 DEC A ; decrease 9236 JR SKIP_NEXT ; loop back 9237 else 9238 ; Since the table now uses uncompressed values, some extra ROM space is 9239 ; required for the table but much more is released by getting rid of routines 9240 ; like this. 9241 endif 9242 9243 9244 ; THE 'MEMORY LOCATION' SUBROUTINE 9245 9246 ; This routine, when supplied with a base address in HL and an index in A, 9247 ; will calculate the address of the A'th entry, where each entry occupies 9248 ; five bytes. It is used for addressing floating-point numbers in the 9249 ; calculator's memory area. 9250 9251 mark_1A3C: 9252 LOC_MEM: 9253 LD C,A ; store the original number $00-$1F. 9254 RLCA ; double. 9255 RLCA ; quadruple. 9256 ADD A,C ; now add original value to multiply by five. 9257 9258 LD C,A ; place the result in C. 9259 LD B,$00 ; set B to 0. 9260 ADD HL,BC ; add to form address of start of number in HL. 9261 9262 RET ; return. 9263 9264 9265 ; THE 'GET FROM MEMORY AREA' SUBROUTINE 9266 9267 ; offsets $E0 to $FF: 'get_mem_0', 'get_mem_1' etc. 9268 ; A holds $00-$1F offset. 9269 ; The calculator stack increases by 5 bytes. 9270 9271 mark_1A45: 9272 get_mem_xx: 9273 9274 if ORIGINAL 9275 PUSH DE ; save STKEND 9276 LD HL,(MEM) ; MEM is base address of the memory cells. 9277 else 9278 ; Note. first two instructions have been swapped to create a subroutine. 9279 LD HL,(MEM) ; MEM is base address of the memory cells. 9280 INDEX_5: ; new label 9281 PUSH DE ; save STKEND 9282 endif 9283 CALL LOC_MEM ; so that HL = first byte 9284 CALL MOVE_FP ; moves 5 bytes with memory 9285 ; check. 9286 ; DE now points to new STKEND. 9287 POP HL ; the original STKEND is now RESULT pointer. 9288 RET ; return. 9289 9290 9291 ; THE 'STACK A CONSTANT' SUBROUTINE 9292 9293 9294 stk_const_xx: 9295 if ORIGINAL 9296 9297 ; offset $A0: 'stk_zero' 9298 ; offset $A1: 'stk_one' 9299 ; offset $A2: 'stk_half' 9300 ; offset $A3: 'stk_half_pi' 9301 ; offset $A4: 'stk_ten' 9302 ; 9303 ; This routine allows a one-byte instruction to stack up to 32 constants 9304 ; held in short form in a table of constants. In fact only 5 constants are 9305 ; required. On entry the A register holds the literal ANDed with $1F. 9306 ; It isn't very efficient and it would have been better to hold the 9307 ; numbers in full, five byte form and stack them in a similar manner 9308 ; to that which would be used later for semi-tone table values. 9309 9310 mark_1A51: 9311 9312 LD H,D ; save STKEND - required for result 9313 LD L,E ; 9314 EXX ; swap 9315 PUSH HL ; save pointer to next literal 9316 LD HL,stk_zero ; Address: stk_zero - start of table of 9317 ; constants 9318 EXX ; 9319 CALL SKIP_CONS 9320 CALL STK_CONST 9321 EXX ; 9322 POP HL ; restore pointer to next literal. 9323 EXX ; 9324 RET ; return. 9325 else 9326 stk_con_x: 9327 LD HL,TAB_CNST ; Address: Table of constants. 9328 9329 JR INDEX_5 ; and join subroutine above. 9330 endif 9331 9332 9333 9334 9335 ; THE 'STORE IN A MEMORY AREA' SUBROUTINE 9336 9337 ; Offsets $C0 to $DF: 'st_mem_0', 'st_mem_1' etc. 9338 ; Although 32 memory storage locations can be addressed, only six 9339 ; $C0 to $C5 are required by the ROM and only the thirty bytes (6*5) 9340 ; required for these are allocated. ZX81 programmers who wish to 9341 ; use the floating point routines from assembly language may wish to 9342 ; alter the system variable MEM to point to 160 bytes of RAM to have 9343 ; use the full range available. 9344 ; A holds derived offset $00-$1F. 9345 ; Unary so on entry HL points to last value, DE to STKEND. 9346 9347 mark_1A63: 9348 st_mem_xx: 9349 PUSH HL ; save the result pointer. 9350 EX DE,HL ; transfer to DE. 9351 LD HL,(MEM) ; fetch MEM the base of memory area. 9352 CALL LOC_MEM ; sets HL to the destination. 9353 EX DE,HL ; swap - HL is start, DE is destination. 9354 9355 if ORIGINAL 9356 CALL MOVE_FP 9357 ; note. a short ld bc,5; ldir 9358 ; the embedded memory check is not required 9359 ; so these instructions would be faster! 9360 else 9361 LD C,5 ;+ one extra byte but 9362 LDIR ;+ faster and no memory check. 9363 endif 9364 9365 9366 EX DE,HL ; DE = STKEND 9367 POP HL ; restore original result pointer 9368 RET ; return. 9369 9370 9371 ; THE 'EXCHANGE' SUBROUTINE 9372 9373 ; offset $01: 'exchange' 9374 ; This routine exchanges the last two values on the calculator stack 9375 ; On entry, as always with binary operations, 9376 ; HL=first number, DE=second number 9377 ; On exit, HL=result, DE=stkend. 9378 9379 mark_1A72: 9380 exchange: 9381 LD B,$05 ; there are five bytes to be swapped 9382 9383 ; start of loop. 9384 9385 mark_1A74: 9386 SWAP_BYTE: 9387 LD A,(DE) ; each byte of second 9388 if ORIGINAL 9389 LD C,(HL) ; each byte of first 9390 EX DE,HL ; swap pointers 9391 else 9392 LD C,A ;+ 9393 LD A,(HL) 9394 endif 9395 LD (DE),A ; store each byte of first 9396 LD (HL),C ; store each byte of second 9397 INC HL ; advance both 9398 INC DE ; pointers. 9399 DJNZ SWAP_BYTE ; loop back until all 5 done. 9400 9401 if ORIGINAL 9402 EX DE,HL ; even up the exchanges so that DE addresses STKEND. 9403 else 9404 ; omit 9405 endif 9406 RET ; return. 9407 9408 9409 ; THE 'SERIES GENERATOR' SUBROUTINE 9410 9411 9412 ; The ZX81 uses Chebyshev polynomials to generate approximations for 9413 ; SIN, ATN, LN and EXP. These are named after the Russian mathematician 9414 ; Pafnuty Chebyshev, born in 1821, who did much pioneering work on numerical 9415 ; series. As far as calculators are concerned, Chebyshev polynomials have an 9416 ; advantage over other series, for example the Taylor series, as they can 9417 ; reach an approximation in just six iterations for SIN, eight for EXP and 9418 ; twelve for LN and ATN. The mechanics of the routine are interesting but 9419 ; for full treatment of how these are generated with demonstrations in 9420 ; Sinclair BASIC see "The Complete Spectrum ROM Disassembly" by Dr Ian Logan 9421 ; and Dr Frank O'Hara, published 1983 by Melbourne House. 9422 9423 mark_1A7F: 9424 series_xx: 9425 LD B,A ; parameter $00 - $1F to B counter 9426 CALL GEN_ENT_1 9427 ; A recursive call to a special entry point 9428 ; in the calculator that puts the B register 9429 ; in the system variable BERG. The return 9430 ; address is the next location and where 9431 ; the calculator will expect its first 9432 ; instruction - now pointed to by HL'. 9433 ; The previous pointer to the series of 9434 ; five-byte numbers goes on the machine stack. 9435 9436 ; The initialization phase. 9437 9438 DEFB __duplicate ;; x,x 9439 DEFB __addition ;; x+x 9440 DEFB __st_mem_0 ;; x+x 9441 DEFB __delete ;; . 9442 DEFB __stk_zero ;; 0 9443 DEFB __st_mem_2 ;; 0 9444 9445 ; a loop is now entered to perform the algebraic calculation for each of 9446 ; the numbers in the series 9447 9448 mark_1A89: 9449 G_LOOP: 9450 DEFB __duplicate ;; v,v. 9451 DEFB __get_mem_0 ;; v,v,x+2 9452 DEFB __multiply ;; v,v*x+2 9453 DEFB __get_mem_2 ;; v,v*x+2,v 9454 DEFB __st_mem_1 ;; 9455 DEFB __subtract ;; 9456 DEFB __end_calc ;; 9457 9458 ; the previous pointer is fetched from the machine stack to H'L' where it 9459 ; addresses one of the numbers of the series following the series literal. 9460 9461 CALL stk_data ; is called directly to 9462 ; push a value and advance H'L'. 9463 CALL GEN_ENT_2 ; recursively re-enters 9464 ; the calculator without disturbing 9465 ; system variable BERG 9466 ; H'L' value goes on the machine stack and is 9467 ; then loaded as usual with the next address. 9468 9469 DEFB __addition ;; 9470 DEFB __exchange ;; 9471 DEFB __st_mem_2 ;; 9472 DEFB __delete ;; 9473 9474 DEFB __dec_jr_nz ;; 9475 DEFB $EE ;;back to G_LOOP, G_LOOP 9476 9477 ; when the counted loop is complete the final subtraction yields the result 9478 ; for example SIN X. 9479 9480 DEFB __get_mem_1 ;; 9481 DEFB __subtract ;; 9482 DEFB __end_calc ;; 9483 9484 RET ; return with H'L' pointing to location 9485 ; after last number in series. 9486 9487 9488 ; Handle unary minus (18) 9489 9490 ; Unary so on entry HL points to last value, DE to STKEND. 9491 9492 mark_1AA0: 9493 neg: 9494 LD A, (HL) ; fetch exponent of last value on the 9495 ; calculator stack. 9496 AND A ; test it. 9497 RET Z ; return if zero. 9498 9499 INC HL ; address the byte with the sign bit. 9500 LD A,(HL) ; fetch to accumulator. 9501 XOR $80 ; toggle the sign bit. 9502 LD (HL),A ; put it back. 9503 DEC HL ; point to last value again. 9504 RET ; return. 9505 9506 9507 ; Absolute magnitude (27) 9508 9509 ; This calculator literal finds the absolute value of the last value, 9510 ; floating point, on calculator stack. 9511 9512 mark_1AAA: 9513 abs: 9514 INC HL ; point to byte with sign bit. 9515 RES 7,(HL) ; make the sign positive. 9516 DEC HL ; point to last value again. 9517 RET ; return. 9518 9519 9520 ; Signum (26) 9521 9522 ; This routine replaces the last value on the calculator stack, 9523 ; which is in floating point form, with one if positive and with -minus one 9524 ; if negative. If it is zero then it is left as such. 9525 9526 mark_1AAF: 9527 sgn: 9528 INC HL ; point to first byte of 4-byte mantissa. 9529 LD A,(HL) ; pick up the byte with the sign bit. 9530 DEC HL ; point to exponent. 9531 DEC (HL) ; test the exponent for 9532 INC (HL) ; the value zero. 9533 9534 SCF ; set the carry flag. 9535 CALL NZ,FP_0_OR_1 ; replaces last value with one 9536 ; if exponent indicates the value is non-zero. 9537 ; in either case mantissa is now four zeros. 9538 9539 INC HL ; point to first byte of 4-byte mantissa. 9540 RLCA ; rotate original sign bit to carry. 9541 RR (HL) ; rotate the carry into sign. 9542 DEC HL ; point to last value. 9543 RET ; return. 9544 9545 9546 9547 ; Handle PEEK function (28) 9548 9549 ; This function returns the contents of a memory address. 9550 ; The entire address space can be peeked including the ROM. 9551 9552 mark_1ABE: 9553 PEEK: 9554 CALL FIND_INT ; puts address in BC. 9555 LD A,(BC) ; load contents into A register. 9556 9557 mark_1AC2: 9558 IN_PK_STK: 9559 JP STACK_A ; exit via STACK_A to put value on the 9560 ; calculator stack. 9561 9562 9563 ; USR number (29) 9564 9565 ; The USR function followed by a number 0-65535 is the method by which 9566 ; the ZX81 invokes machine code programs. This function returns the 9567 ; contents of the BC register pair. 9568 ; Note. that STACK_BC re-initializes the IY register to ERR_NR if a user-written 9569 ; program has altered it. 9570 9571 mark_1AC5: 9572 usr_num: 9573 CALL FIND_INT ; to fetch the 9574 ; supplied address into BC. 9575 9576 LD HL,STACK_BC ; address: STACK_BC is 9577 PUSH HL ; pushed onto the machine stack. 9578 PUSH BC ; then the address of the machine code 9579 ; routine. 9580 9581 RET ; make an indirect jump to the routine 9582 ; and, hopefully, to STACK_BC also. 9583 9584 9585 9586 ; Greater than zero ($33) 9587 9588 ; Test if the last value on the calculator stack is greater than zero. 9589 ; This routine is also called directly from the end-tests of the comparison 9590 ; routine. 9591 9592 mark_1ACE: 9593 greater_0: 9594 LD A,(HL) ; fetch exponent. 9595 AND A ; test it for zero. 9596 RET Z ; return if so. 9597 9598 9599 LD A,$FF ; prepare XOR mask for sign bit 9600 JR SIGN_TO_C ; forward to SIGN_TO_C 9601 ; to put sign in carry 9602 ; (carry will become set if sign is positive) 9603 ; and then overwrite location with 1 or 0 9604 ; as appropriate. 9605 9606 9607 ; Handle NOT operator ($2C) 9608 9609 ; This overwrites the last value with 1 if it was zero else with zero 9610 ; if it was any other value. 9611 ; 9612 ; e.g. NOT 0 returns 1, NOT 1 returns 0, NOT -3 returns 0. 9613 ; 9614 ; The subroutine is also called directly from the end-tests of the comparison 9615 ; operator. 9616 9617 mark_1AD5: 9618 not: 9619 LD A,(HL) ; get exponent byte. 9620 NEG ; negate - sets carry if non-zero. 9621 CCF ; complement so carry set if zero, else reset. 9622 JR FP_0_OR_1 ; forward to FP_0_OR_1. 9623 9624 9625 ; Less than zero (32) 9626 9627 ; Destructively test if last value on calculator stack is less than zero. 9628 ; Bit 7 of second byte will be set if so. 9629 9630 mark_1ADB: 9631 less_0: 9632 XOR A ; set xor mask to zero 9633 ; (carry will become set if sign is negative). 9634 9635 ; transfer sign of mantissa to Carry Flag. 9636 9637 mark_1ADC: 9638 SIGN_TO_C: 9639 INC HL ; address 2nd byte. 9640 XOR (HL) ; bit 7 of HL will be set if number is negative. 9641 DEC HL ; address 1st byte again. 9642 RLCA ; rotate bit 7 of A to carry. 9643 9644 9645 ; Zero or one 9646 9647 ; This routine places an integer value zero or one at the addressed location 9648 ; of calculator stack or MEM area. The value one is written if carry is set on 9649 ; entry else zero. 9650 9651 mark_1AE0: 9652 FP_0_OR_1: 9653 PUSH HL ; save pointer to the first byte 9654 LD B,$05 ; five bytes to do. 9655 9656 mark_1AE3: 9657 FP_loop: 9658 LD (HL),$00 ; insert a zero. 9659 INC HL ; 9660 DJNZ FP_loop ; repeat. 9661 9662 POP HL ; 9663 RET NC ; 9664 9665 LD (HL),$81 ; make value 1 9666 RET ; return. 9667 9668 9669 9670 ; Handle OR operator (07) 9671 9672 ; The Boolean OR operator. eg. X OR Y 9673 ; The result is zero if both values are zero else a non-zero value. 9674 ; 9675 ; e.g. 0 OR 0 returns 0. 9676 ; -3 OR 0 returns -3. 9677 ; 0 OR -3 returns 1. 9678 ; -3 OR 2 returns 1. 9679 ; 9680 ; A binary operation. 9681 ; On entry HL points to first operand (X) and DE to second operand (Y). 9682 9683 mark_1AED: 9684 or: 9685 LD A,(DE) ; fetch exponent of second number 9686 AND A ; test it. 9687 RET Z ; return if zero. 9688 9689 SCF ; set carry flag 9690 JR FP_0_OR_1 ; back to FP_0_OR_1 to overwrite the first operand 9691 ; with the value 1. 9692 9693 9694 9695 ; Handle number AND number (08) 9696 9697 ; The Boolean AND operator. 9698 ; 9699 ; e.g. -3 AND 2 returns -3. 9700 ; -3 AND 0 returns 0. 9701 ; 0 and -2 returns 0. 9702 ; 0 and 0 returns 0. 9703 ; 9704 ; Compare with OR routine above. 9705 9706 boolean_num_and_num: 9707 LD A,(DE) ; fetch exponent of second number. 9708 AND A ; test it. 9709 RET NZ ; return if not zero. 9710 9711 JR FP_0_OR_1 ; back to FP_0_OR_1 to overwrite the first operand 9712 ; with zero for return value. 9713 9714 9715 ; Handle string AND number (10) 9716 9717 ; e.g. "YOU WIN" AND SCORE>99 will return the string if condition is true 9718 ; or the null string if false. 9719 9720 strs_and_num: 9721 LD A,(DE) ; fetch exponent of second number. 9722 AND A ; test it. 9723 RET NZ ; return if number was not zero - the string 9724 ; is the result. 9725 9726 ; if the number was zero (false) then the null string must be returned by 9727 ; altering the length of the string on the calculator stack to zero. 9728 9729 PUSH DE ; save pointer to the now obsolete number 9730 ; (which will become the new STKEND) 9731 9732 DEC DE ; point to the 5th byte of string descriptor. 9733 XOR A ; clear the accumulator. 9734 LD (DE),A ; place zero in high byte of length. 9735 DEC DE ; address low byte of length. 9736 LD (DE),A ; place zero there - now the null string. 9737 9738 POP DE ; restore pointer - new STKEND. 9739 RET ; return. 9740 9741 9742 ; Perform comparison ($09-$0E, $11-$16) 9743 9744 ; True binary operations. 9745 ; 9746 ; A single entry point is used to evaluate six numeric and six string 9747 ; comparisons. On entry, the calculator literal is in the B register and 9748 ; the two numeric values, or the two string parameters, are on the 9749 ; calculator stack. 9750 ; The individual bits of the literal are manipulated to group similar 9751 ; operations although the SUB 8 instruction does nothing useful and merely 9752 ; alters the string test bit. 9753 ; Numbers are compared by subtracting one from the other, strings are 9754 ; compared by comparing every character until a mismatch, or the end of one 9755 ; or both, is reached. 9756 ; 9757 ; Numeric Comparisons. 9758 9759 ; The 'x>y' example is the easiest as it employs straight-thru logic. 9760 ; Number y is subtracted from x and the result tested for greater_0 yielding 9761 ; a final value 1 (true) or 0 (false). 9762 ; For 'x<y' the same logic is used but the two values are first swapped on the 9763 ; calculator stack. 9764 ; For 'x=y' NOT is applied to the subtraction result yielding true if the 9765 ; difference was zero and false with anything else. 9766 ; The first three numeric comparisons are just the opposite of the last three 9767 ; so the same processing steps are used and then a final NOT is applied. 9768 ; 9769 ; literal Test No sub 8 ExOrNot 1st RRCA exch sub ? End-Tests 9770 ; ========= ==== == ======== === ======== ======== ==== === = === === === 9771 ; num_l_eql x<=y 09 00000001 dec 00000000 00000000 ---- x-y ? --- >0? NOT 9772 ; num_gr_eql x>=y 0A 00000010 dec 00000001 10000000c swap y-x ? --- >0? NOT 9773 ; nums_neql x<>y 0B 00000011 dec 00000010 00000001 ---- x-y ? NOT --- NOT 9774 ; num_grtr x>y 0C 00000100 - 00000100 00000010 ---- x-y ? --- >0? --- 9775 ; num_less x<y 0D 00000101 - 00000101 10000010c swap y-x ? --- >0? --- 9776 ; nums_eql x=y 0E 00000110 - 00000110 00000011 ---- x-y ? NOT --- --- 9777 ; 9778 ; comp -> C/F 9779 ; ==== === 9780 ; str_l_eql x$<=y$ 11 00001001 dec 00001000 00000100 ---- x$y$ 0 !or >0? NOT 9781 ; str_gr_eql x$>=y$ 12 00001010 dec 00001001 10000100c swap y$x$ 0 !or >0? NOT 9782 ; strs_neql x$<>y$ 13 00001011 dec 00001010 00000101 ---- x$y$ 0 !or >0? NOT 9783 ; str_grtr x$>y$ 14 00001100 - 00001100 00000110 ---- x$y$ 0 !or >0? --- 9784 ; str_less x$<y$ 15 00001101 - 00001101 10000110c swap y$x$ 0 !or >0? --- 9785 ; strs_eql x$=y$ 16 00001110 - 00001110 00000111 ---- x$y$ 0 !or >0? --- 9786 ; 9787 ; String comparisons are a little different in that the eql/neql carry flag 9788 ; from the 2nd RRCA is, as before, fed into the first of the end tests but 9789 ; along the way it gets modified by the comparison process. The result on the 9790 ; stack always starts off as zero and the carry fed in determines if NOT is 9791 ; applied to it. So the only time the greater-0 test is applied is if the 9792 ; stack holds zero which is not very efficient as the test will always yield 9793 ; zero. The most likely explanation is that there were once separate end tests 9794 ; for numbers and strings. 9795 9796 ; $1B03 SAME ADDRESS FOR MULTIPLE ROUTINES ??? 9797 9798 num_l_eql: 9799 num_gr_eql: 9800 nums_neql: 9801 num_grtr: 9802 num_less: 9803 nums_eql: 9804 str_l_eql: 9805 str_gr_eql: 9806 strs_neql: 9807 str_grtr: 9808 str_less: 9809 strs_eql: 9810 num_lt_eql: 9811 if ORIGINAL 9812 mark_1B03: 9813 LD A,B ; transfer literal to accumulator. 9814 SUB $08 ; subtract eight - which is not useful. 9815 else 9816 LD A,B ; transfer literal to accumulator. 9817 ;; SUB $08 ; subtract eight - which is not useful. 9818 endif 9819 BIT 2,A ; isolate '>', '<', '='. 9820 9821 JR NZ,EX_OR_NOT ; skip to EX_OR_NOT with these. 9822 9823 DEC A ; else make $00-$02, $08-$0A to match bits 0-2. 9824 9825 EX_OR_NOT: 9826 if ORIGINAL 9827 mark_1B0B: 9828 endif 9829 RRCA ; the first RRCA sets carry for a swap. 9830 JR NC,NUM_OR_STR ; forward to NUM_OR_STR with other 8 cases 9831 9832 ; for the other 4 cases the two values on the calculator stack are exchanged. 9833 9834 PUSH AF ; save A and carry. 9835 PUSH HL ; save HL - pointer to first operand. 9836 ; (DE points to second operand). 9837 9838 CALL exchange ; routine exchange swaps the two values. 9839 ; (HL = second operand, DE = STKEND) 9840 9841 POP DE ; DE = first operand 9842 EX DE,HL ; as we were. 9843 POP AF ; restore A and carry. 9844 9845 ; Note. it would be better if the 2nd RRCA preceded the string test. 9846 ; It would save two duplicate bytes and if we also got rid of that sub 8 9847 ; at the beginning we wouldn't have to alter which bit we test. 9848 9849 NUM_OR_STR: 9850 if ORIGINAL 9851 mark_1B16: 9852 9853 BIT 2,A ; test if a string comparison. 9854 JR NZ,STRINGS ; forward to STRINGS if so. 9855 9856 ; continue with numeric comparisons. 9857 9858 RRCA ; 2nd RRCA causes eql/neql to set carry. 9859 PUSH AF ; save A and carry 9860 else 9861 RRCA ;+ causes 'eql/neql' to set carry. 9862 PUSH AF ;+ save the carry flag. 9863 9864 BIT 2,A ; test if a string comparison. 9865 JR NZ,STRINGS ; forward to STRINGS if so. 9866 9867 endif 9868 9869 CALL SUBTRACT ; leaves result on stack. 9870 JR END_TESTS ; forward to END_TESTS 9871 9872 ; ___ 9873 9874 9875 STRINGS: 9876 if ORIGINAL 9877 mark_1B21: 9878 RRCA ; 2nd RRCA causes eql/neql to set carry. 9879 PUSH AF ; save A and carry. 9880 else 9881 ;; RRCA ; 2nd RRCA causes eql/neql to set carry. 9882 ;; PUSH AF ; save A and carry. 9883 endif 9884 CALL STK_FETCH ; gets 2nd string params 9885 PUSH DE ; save start2 *. 9886 PUSH BC ; and the length. 9887 9888 CALL STK_FETCH ; gets 1st string 9889 ; parameters - start in DE, length in BC. 9890 POP HL ; restore length of second to HL. 9891 9892 ; A loop is now entered to compare, by subtraction, each corresponding character 9893 ; of the strings. For each successful match, the pointers are incremented and 9894 ; the lengths decreased and the branch taken back to here. If both string 9895 ; remainders become null at the same time, then an exact match exists. 9896 9897 if ORIGINAL 9898 mark_1B2C: 9899 endif 9900 BYTE_COMP: 9901 LD A,H ; test if the second string 9902 OR L ; is the null string and hold flags. 9903 9904 EX (SP),HL ; put length2 on stack, bring start2 to HL *. 9905 LD A,B ; hi byte of length1 to A 9906 9907 JR NZ,SEC_PLUS ; forward to SEC_PLUS if second not null. 9908 9909 OR C ; test length of first string. 9910 9911 if ORIGINAL 9912 mark_1B33: 9913 endif 9914 9915 SECOND_LOW: 9916 POP BC ; pop the second length off stack. 9917 JR Z,BOTH_NULL ; forward if first string is also 9918 ; of zero length. 9919 9920 ; the true condition - first is longer than second (SECOND_LESS) 9921 9922 POP AF ; restore carry (set if eql/neql) 9923 CCF ; complement carry flag. 9924 ; Note. equality becomes false. 9925 ; Inequality is true. By swapping or applying 9926 ; a terminal 'not', all comparisons have been 9927 ; manipulated so that this is success path. 9928 JR STR_TEST ; forward to leave via STR_TEST 9929 9930 ; ___ 9931 ; the branch was here with a match 9932 9933 if ORIGINAL 9934 mark_1B3A: 9935 endif 9936 9937 BOTH_NULL: 9938 POP AF ; restore carry - set for eql/neql 9939 JR STR_TEST ; forward to STR_TEST 9940 9941 ; ___ 9942 ; the branch was here when 2nd string not null and low byte of first is yet 9943 ; to be tested. 9944 9945 9946 mark_1B3D: 9947 SEC_PLUS: 9948 OR C ; test the length of first string. 9949 JR Z,FRST_LESS ; forward to FRST_LESS if length is zero. 9950 9951 ; both strings have at least one character left. 9952 9953 LD A,(DE) ; fetch character of first string. 9954 SUB (HL) ; subtract with that of 2nd string. 9955 JR C,FRST_LESS ; forward to FRST_LESS if carry set 9956 9957 JR NZ,SECOND_LOW ; back to SECOND_LOW and then STR_TEST 9958 ; if not exact match. 9959 9960 DEC BC ; decrease length of 1st string. 9961 INC DE ; increment 1st string pointer. 9962 9963 INC HL ; increment 2nd string pointer. 9964 EX (SP),HL ; swap with length on stack 9965 DEC HL ; decrement 2nd string length 9966 JR BYTE_COMP ; back to BYTE_COMP 9967 9968 ; ___ 9969 ; the false condition. 9970 9971 mark_1B4D: 9972 FRST_LESS: 9973 POP BC ; discard length 9974 POP AF ; pop A 9975 AND A ; clear the carry for false result. 9976 9977 ; ___ 9978 ; exact match and x$>y$ rejoin here 9979 9980 mark_1B50: 9981 STR_TEST: 9982 PUSH AF ; save A and carry 9983 9984 RST _FP_CALC ;; 9985 DEFB __stk_zero ;; an initial false value. 9986 DEFB __end_calc ;; 9987 9988 ; both numeric and string paths converge here. 9989 9990 mark_1B54: 9991 END_TESTS: 9992 POP AF ; pop carry - will be set if eql/neql 9993 PUSH AF ; save it again. 9994 9995 CALL C,not ; sets true(1) if equal(0) 9996 ; or, for strings, applies true result. 9997 CALL greater_0 ; ?????????? 9998 9999 10000 POP AF ; pop A 10001 RRCA ; the third RRCA - test for '<=', '>=' or '<>'. 10002 CALL NC,not ; apply a terminal NOT if so. 10003 RET ; return. 10004 10005 ; String concatenation ($17) 10006 10007 ; This literal combines two strings into one e.g. LET A$ = B$ + C$ 10008 ; The two parameters of the two strings to be combined are on the stack. 10009 10010 mark_1B62: 10011 strs_add: 10012 CALL STK_FETCH ; fetches string parameters 10013 ; and deletes calculator stack entry. 10014 PUSH DE ; save start address. 10015 PUSH BC ; and length. 10016 10017 CALL STK_FETCH ; for first string 10018 POP HL ; re-fetch first length 10019 PUSH HL ; and save again 10020 PUSH DE ; save start of second string 10021 PUSH BC ; and its length. 10022 10023 ADD HL,BC ; add the two lengths. 10024 LD B,H ; transfer to BC 10025 LD C,L ; and create 10026 RST _BC_SPACES ; BC_SPACES in workspace. 10027 ; DE points to start of space. 10028 10029 CALL STK_STO_STR ; stores parameters 10030 ; of new string updating STKEND. 10031 POP BC ; length of first 10032 POP HL ; address of start 10033 10034 if ORIGINAL 10035 LD A,B ; test for 10036 OR C ; zero length. 10037 JR Z,OTHER_STR ; to OTHER_STR if null string 10038 LDIR ; copy string to workspace. 10039 else 10040 CALL COND_MV ;+ a conditional (NZ) ldir routine. 10041 endif 10042 10043 mark_1B7D: 10044 OTHER_STR: 10045 POP BC ; now second length 10046 POP HL ; and start of string 10047 if ORIGINAL 10048 LD A,B ; test this one 10049 OR C ; for zero length 10050 JR Z,STACK_POINTERS ; skip forward to STACK_POINTERS if so as complete. 10051 10052 LDIR ; else copy the bytes. 10053 ; and continue into next routine which 10054 ; sets the calculator stack pointers. 10055 else 10056 CALL COND_MV ;+ a conditional (NZ) ldir routine. 10057 endif 10058 10059 10060 ; Check stack pointers 10061 10062 ; Register DE is set to STKEND and HL, the result pointer, is set to five 10063 ; locations below this. 10064 ; This routine is used when it is inconvenient to save these values at the 10065 ; time the calculator stack is manipulated due to other activity on the 10066 ; machine stack. 10067 ; This routine is also used to terminate the VAL routine for 10068 ; the same reason and to initialize the calculator stack at the start of 10069 ; the CALCULATE routine. 10070 10071 mark_1B85: 10072 STACK_POINTERS: 10073 LD HL,(STKEND) ; fetch STKEND value from system variable. 10074 LD DE,-5 10075 PUSH HL ; push STKEND value. 10076 10077 ADD HL,DE ; subtract 5 from HL. 10078 10079 POP DE ; pop STKEND to DE. 10080 RET ; return. 10081 10082 10083 ; Handle CHR$ (2B) 10084 10085 ; This function returns a single character string that is a result of 10086 ; converting a number in the range 0-255 to a string e.g. CHR$ 38 = "A". 10087 ; Note. the ZX81 does not have an ASCII character set. 10088 10089 mark_1B8F: 10090 chr_dollar: 10091 CALL FP_TO_A ; puts the number in A. 10092 10093 JR C,REPORT_Bd ; forward if overflow 10094 JR NZ,REPORT_Bd ; forward if negative 10095 if ORIGINAL 10096 PUSH AF ; save the argument. 10097 endif 10098 LD BC,1 ; one space required. 10099 RST _BC_SPACES ; BC_SPACES makes DE point to start 10100 if ORIGINAL 10101 POP AF ; restore the number. 10102 endif 10103 LD (DE),A ; and store in workspace 10104 10105 if ORIGINAL 10106 CALL STK_STO_STR ; stacks descriptor. 10107 10108 EX DE,HL ; make HL point to result and DE to STKEND. 10109 RET ; return. 10110 else 10111 JR str_STK ;+ relative jump to similar sequence in str$. 10112 endif 10113 ; ___ 10114 10115 mark_1BA2: 10116 REPORT_Bd: 10117 RST _ERROR_1 10118 DEFB $0A ; Error Report: Integer out of range 10119 10120 10121 ; Handle VAL ($1A) 10122 10123 ; VAL treats the characters in a string as a numeric expression. 10124 ; e.g. VAL "2.3" = 2.3, VAL "2+4" = 6, VAL ("2" + "4") = 24. 10125 10126 val: 10127 if ORIGINAL 10128 mark_1BA4: 10129 LD HL,(CH_ADD) ; fetch value of system variable CH_ADD 10130 else 10131 RST _GET_CHAR ;+ shorter way to fetch CH_ADD. 10132 endif 10133 PUSH HL ; and save on the machine stack. 10134 10135 CALL STK_FETCH ; fetches the string operand 10136 ; from calculator stack. 10137 10138 PUSH DE ; save the address of the start of the string. 10139 INC BC ; increment the length for a carriage return. 10140 10141 RST _BC_SPACES ; BC_SPACES creates the space in workspace. 10142 POP HL ; restore start of string to HL. 10143 LD (CH_ADD),DE ; load CH_ADD with start DE in workspace. 10144 10145 PUSH DE ; save the start in workspace 10146 LDIR ; copy string from program or variables or 10147 ; workspace to the workspace area. 10148 EX DE,HL ; end of string + 1 to HL 10149 DEC HL ; decrement HL to point to end of new area. 10150 LD (HL),ZX_NEWLINE ; insert a carriage return at end. 10151 ; ZX81 has a non-ASCII character set 10152 RES 7,(IY+FLAGS-RAMBASE) ; signal checking syntax. 10153 CALL CLASS_6 ; evaluates string 10154 ; expression and checks for integer result. 10155 10156 CALL CHECK_2 ; checks for carriage return. 10157 10158 10159 POP HL ; restore start of string in workspace. 10160 10161 LD (CH_ADD),HL ; set CH_ADD to the start of the string again. 10162 SET 7,(IY+FLAGS-RAMBASE) ; signal running program. 10163 CALL SCANNING ; evaluates the string 10164 ; in full leaving result on calculator stack. 10165 10166 POP HL ; restore saved character address in program. 10167 LD (CH_ADD),HL ; and reset the system variable CH_ADD. 10168 10169 JR STACK_POINTERS ; back to exit via STACK_POINTERS. 10170 ; resetting the calculator stack pointers 10171 ; HL and DE from STKEND as it wasn't possible 10172 ; to preserve them during this routine. 10173 10174 10175 ; Handle STR$ (2A) 10176 10177 ; This function returns a string representation of a numeric argument. 10178 ; The method used is to trick the PRINT_FP routine into thinking it 10179 ; is writing to a collapsed display file when in fact it is writing to 10180 ; string workspace. 10181 ; If there is already a newline at the intended print position and the 10182 ; column count has not been reduced to zero then the print routine 10183 ; assumes that there is only 1K of RAM and the screen memory, like the rest 10184 ; of dynamic memory, expands as necessary using calls to the ONE_SPACE 10185 ; routine. The screen is character-mapped not bit-mapped. 10186 10187 mark_1BD5: 10188 str_dollar: 10189 LD BC,1 ; create an initial byte in workspace 10190 RST _BC_SPACES ; using BC_SPACES restart. 10191 10192 LD (HL),ZX_NEWLINE ; place a carriage return there. 10193 10194 LD HL,(S_POSN) ; fetch value of S_POSN column/line 10195 PUSH HL ; and preserve on stack. 10196 10197 LD L,$FF ; make column value high to create a 10198 ; contrived buffer of length 254. 10199 LD (S_POSN),HL ; and store in system variable S_POSN. 10200 10201 LD HL,(DF_CC) ; fetch value of DF_CC 10202 PUSH HL ; and preserve on stack also. 10203 10204 LD (DF_CC),DE ; now set DF_CC which normally addresses 10205 ; somewhere in the display file to the start 10206 ; of workspace. 10207 PUSH DE ; save the start of new string. 10208 10209 CALL PRINT_FP 10210 10211 POP DE ; retrieve start of string. 10212 10213 LD HL,(DF_CC) ; fetch end of string from DF_CC. 10214 AND A ; prepare for true subtraction. 10215 SBC HL,DE ; subtract to give length. 10216 10217 LD B,H ; and transfer to the BC 10218 LD C,L ; register. 10219 10220 POP HL ; restore original 10221 LD (DF_CC),HL ; DF_CC value 10222 10223 POP HL ; restore original 10224 LD (S_POSN),HL ; S_POSN values. 10225 10226 if ORIGINAL 10227 else 10228 str_STK: ; New entry-point to exploit similarities and save 3 bytes of code. 10229 endif 10230 10231 CALL STK_STO_STR ; stores the string 10232 ; descriptor on the calculator stack. 10233 10234 EX DE,HL ; HL = last value, DE = STKEND. 10235 RET ; return. 10236 10237 10238 10239 ; THE 'CODE' FUNCTION 10240 10241 ; (offset $19: 'code') 10242 ; Returns the code of a character or first character of a string 10243 ; e.g. CODE "AARDVARK" = 38 (not 65 as the ZX81 does not have an ASCII 10244 ; character set). 10245 10246 10247 mark_1C06: 10248 code: 10249 CALL STK_FETCH ; fetch and delete the string parameters. 10250 ; DE points to the start, BC holds the length. 10251 LD A,B ; test length 10252 OR C ; of the string. 10253 JR Z,STK_CODE ; skip with zero if the null string. 10254 10255 LD A,(DE) ; else fetch the first character. 10256 10257 mark_1C0E: 10258 STK_CODE: 10259 JP STACK_A ; jump back (with memory check) 10260 10261 10262 ; THE 'LEN' SUBROUTINE 10263 10264 ; (offset $1b: 'len') 10265 ; Returns the length of a string. 10266 ; In Sinclair BASIC strings can be more than twenty thousand characters long 10267 ; so a sixteen-bit register is required to store the length 10268 10269 mark_1C11: 10270 len: 10271 CALL STK_FETCH ; fetch and delete the 10272 ; string parameters from the calculator stack. 10273 ; register BC now holds the length of string. 10274 10275 JP STACK_BC ; jump back to save result on the 10276 ; calculator stack (with memory check). 10277 10278 10279 ; THE 'DECREASE THE COUNTER' SUBROUTINE 10280 10281 ; (offset $31: 'dec_jr_nz') 10282 ; The calculator has an instruction that decrements a single-byte 10283 ; pseudo-register and makes consequential relative jumps just like 10284 ; the Z80's DJNZ instruction. 10285 10286 mark_1C17: 10287 dec_jr_nz: 10288 EXX ; switch in set that addresses code 10289 10290 PUSH HL ; save pointer to offset byte 10291 LD HL,BERG ; address BERG in system variables 10292 DEC (HL) ; decrement it 10293 POP HL ; restore pointer 10294 10295 JR NZ,JUMP_2 ; to JUMP_2 if not zero 10296 10297 INC HL ; step past the jump length. 10298 EXX ; switch in the main set. 10299 RET ; return. 10300 10301 ; Note. as a general rule the calculator avoids using the IY register 10302 ; otherwise the cumbersome 4 instructions in the middle could be replaced by 10303 ; dec (iy+$xx) - using three instruction bytes instead of six. 10304 10305 10306 10307 ; THE 'JUMP' SUBROUTINE 10308 10309 ; (Offset $2F; 'jump') 10310 ; This enables the calculator to perform relative jumps just like 10311 ; the Z80 chip's JR instruction. 10312 ; This is one of the few routines to be polished for the ZX Spectrum. 10313 ; See, without looking at the ZX Spectrum ROM, if you can get rid of the 10314 ; relative jump. 10315 10316 mark_1C23: 10317 jump: 10318 EXX ;switch in pointer set 10319 JUMP_2: 10320 LD E,(HL) ; the jump byte 0-127 forward, 128-255 back. 10321 10322 if ORIGINAL 10323 mark_1C24: 10324 XOR A ; clear accumulator. 10325 BIT 7,E ; test if negative jump 10326 JR Z,JUMP_3 ; skip, if positive 10327 CPL ; else change to $FF. 10328 else 10329 ; Note. Elegance from the ZX Spectrum. 10330 LD A,E ;+ 10331 RLA ;+ 10332 SBC A,A ;+ 10333 endif 10334 10335 mark_1C2B: 10336 JUMP_3: 10337 LD D,A ; transfer to high byte. 10338 ADD HL,DE ; advance calculator pointer forward or back. 10339 10340 EXX ; switch out pointer set. 10341 RET ; return. 10342 10343 10344 ; THE 'JUMP ON TRUE' SUBROUTINE 10345 10346 ; (Offset $00; 'jump_true') 10347 ; This enables the calculator to perform conditional relative jumps 10348 ; dependent on whether the last test gave a true result 10349 ; On the ZX81, the exponent will be zero for zero or else $81 for one. 10350 10351 mark_1C2F: 10352 jump_true: 10353 LD A,(DE) ; collect exponent byte 10354 10355 AND A ; is result 0 or 1 ? 10356 JR NZ,jump ; back to JUMP if true (1). 10357 10358 EXX ; else switch in the pointer set. 10359 INC HL ; step past the jump length. 10360 EXX ; switch in the main set. 10361 RET ; return. 10362 10363 10364 10365 ; THE 'MODULUS' SUBROUTINE 10366 10367 ; ( Offset $2E: 'n_mod_m' ) 10368 ; ( i1, i2 -- i3, i4 ) 10369 ; The subroutine calculate N mod M where M is the positive integer, the 10370 ; 'last value' on the calculator stack and N is the integer beneath. 10371 ; The subroutine returns the integer quotient as the last value and the 10372 ; remainder as the value beneath. 10373 ; e.g. 17 MOD 3 = 5 remainder 2 10374 ; It is invoked during the calculation of a random number and also by 10375 ; the PRINT_FP routine. 10376 10377 mark_1C37: 10378 n_mod_m: 10379 RST _FP_CALC ;; 17, 3. 10380 DEFB __st_mem_0 ;; 17, 3. 10381 DEFB __delete ;; 17. 10382 DEFB __duplicate ;; 17, 17. 10383 DEFB __get_mem_0 ;; 17, 17, 3. 10384 DEFB __division ;; 17, 17/3. 10385 DEFB __int ;; 17, 5. 10386 DEFB __get_mem_0 ;; 17, 5, 3. 10387 DEFB __exchange ;; 17, 3, 5. 10388 DEFB __st_mem_0 ;; 17, 3, 5. 10389 DEFB __multiply ;; 17, 15. 10390 DEFB __subtract ;; 2. 10391 DEFB __get_mem_0 ;; 2, 5. 10392 DEFB __end_calc ;; 2, 5. 10393 10394 RET ; return. 10395 10396 10397 10398 ; THE 'INTEGER' FUNCTION 10399 10400 ; (offset $24: 'int') 10401 ; This function returns the integer of x, which is just the same as truncate 10402 ; for positive numbers. The truncate literal truncates negative numbers 10403 ; upwards so that -3.4 gives -3 whereas the BASIC INT function has to 10404 ; truncate negative numbers down so that INT -3.4 is 4. 10405 ; It is best to work through using, say, plus or minus 3.4 as examples. 10406 10407 mark_1C46: 10408 int: 10409 RST _FP_CALC ;; x. (= 3.4 or -3.4). 10410 DEFB __duplicate ;; x, x. 10411 DEFB __less_0 ;; x, (1/0) 10412 DEFB __jump_true ;; x, (1/0) 10413 DEFB X_NEG - $ ;; X_NEG 10414 10415 DEFB __truncate ;; trunc 3.4 = 3. 10416 DEFB __end_calc ;; 3. 10417 10418 RET ; return with + int x on stack. 10419 10420 10421 mark_1C4E: 10422 X_NEG: 10423 DEFB __duplicate ;; -3.4, -3.4. 10424 DEFB __truncate ;; -3.4, -3. 10425 DEFB __st_mem_0 ;; -3.4, -3. 10426 DEFB __subtract ;; -.4 10427 DEFB __get_mem_0 ;; -.4, -3. 10428 DEFB __exchange ;; -3, -.4. 10429 DEFB __not ;; -3, (0). 10430 DEFB __jump_true ;; -3. 10431 DEFB EXIT - $ ;; -3. 10432 10433 DEFB __stk_one ;; -3, 1. 10434 DEFB __subtract ;; -4. 10435 10436 mark_1C59: 10437 EXIT: 10438 DEFB __end_calc ;; -4. 10439 10440 RET ; return. 10441 10442 10443 10444 ; Exponential (23) 10445 10446 ; 10447 ; 10448 10449 mark_1C5B: 10450 exp: 10451 RST _FP_CALC ;; 10452 DEFB __stk_data ;; 10453 DEFB $F1 ;;Exponent: $81, Bytes: 4 10454 DEFB $38,$AA,$3B,$29 ;; 10455 DEFB __multiply ;; 10456 DEFB __duplicate ;; 10457 DEFB __int ;; 10458 DEFB $C3 ;;st_mem_3 10459 DEFB __subtract ;; 10460 DEFB __duplicate ;; 10461 DEFB __addition ;; 10462 DEFB __stk_one ;; 10463 DEFB __subtract ;; 10464 DEFB __series_08 ;; 10465 DEFB $13 ;;Exponent: $63, Bytes: 1 10466 DEFB $36 ;;(+00,+00,+00) 10467 DEFB $58 ;;Exponent: $68, Bytes: 2 10468 DEFB $65,$66 ;;(+00,+00) 10469 DEFB $9D ;;Exponent: $6D, Bytes: 3 10470 DEFB $78,$65,$40 ;;(+00) 10471 DEFB $A2 ;;Exponent: $72, Bytes: 3 10472 DEFB $60,$32,$C9 ;;(+00) 10473 DEFB $E7 ;;Exponent: $77, Bytes: 4 10474 DEFB $21,$F7,$AF,$24 ;; 10475 DEFB $EB ;;Exponent: $7B, Bytes: 4 10476 DEFB $2F,$B0,$B0,$14 ;; 10477 DEFB $EE ;;Exponent: $7E, Bytes: 4 10478 DEFB $7E,$BB,$94,$58 ;; 10479 DEFB $F1 ;;Exponent: $81, Bytes: 4 10480 DEFB $3A,$7E,$F8,$CF ;; 10481 DEFB $E3 ;;get_mem_3 10482 DEFB __end_calc ;; 10483 10484 CALL FP_TO_A 10485 JR NZ,N_NEGTV 10486 10487 JR C,REPORT_6b 10488 10489 ADD A,(HL) ; 10490 JR NC,RESULT_OK 10491 10492 10493 mark_1C99: 10494 REPORT_6b: 10495 RST _ERROR_1 10496 DEFB $05 ; Error Report: Number too big 10497 10498 mark_1C9B: 10499 N_NEGTV: 10500 JR C,RESULT_ZERO 10501 10502 SUB (HL) ; 10503 JR NC,RESULT_ZERO 10504 10505 NEG ; Negate 10506 10507 mark_1CA2: 10508 RESULT_OK: 10509 LD (HL),A ; 10510 RET ; return. 10511 10512 10513 mark_1CA4: 10514 RESULT_ZERO: 10515 RST _FP_CALC ;; 10516 DEFB __delete ;; 10517 DEFB __stk_zero ;; 10518 DEFB __end_calc ;; 10519 10520 RET ; return. 10521 10522 10523 10524 ; THE 'NATURAL LOGARITHM' FUNCTION 10525 10526 ; (offset $22: 'ln') 10527 ; Like the ZX81 itself, 'natural' logarithms came from Scotland. 10528 ; They were devised in 1614 by well-traveled Scotsman John Napier who noted 10529 ; "Nothing doth more molest and hinder calculators than the multiplications, 10530 ; divisions, square and cubical extractions of great numbers". 10531 ; 10532 ; Napier's logarithms enabled the above operations to be accomplished by 10533 ; simple addition and subtraction simplifying the navigational and 10534 ; astronomical calculations which beset his age. 10535 ; Napier's logarithms were quickly overtaken by logarithms to the base 10 10536 ; devised, in conjunction with Napier, by Henry Briggs a Cambridge-educated 10537 ; professor of Geometry at Oxford University. These simplified the layout 10538 ; of the tables enabling humans to easily scale calculations. 10539 ; 10540 ; It is only recently with the introduction of pocket calculators and 10541 ; computers like the ZX81 that natural logarithms are once more at the fore, 10542 ; although some computers retain logarithms to the base ten. 10543 ; 'Natural' logarithms are powers to the base 'e', which like 'pi' is a 10544 ; naturally occurring number in branches of mathematics. 10545 ; Like 'pi' also, 'e' is an irrational number and starts 2.718281828... 10546 ; 10547 ; The tabular use of logarithms was that to multiply two numbers one looked 10548 ; up their two logarithms in the tables, added them together and then looked 10549 ; for the result in a table of antilogarithms to give the desired product. 10550 ; 10551 ; The EXP function is the BASIC equivalent of a calculator's 'antiln' function 10552 ; and by picking any two numbers, 1.72 and 6.89 say, 10553 ; 10 PRINT EXP ( LN 1.72 + LN 6.89 ) 10554 ; will give just the same result as 10555 ; 20 PRINT 1.72 * 6.89. 10556 ; Division is accomplished by subtracting the two logs. 10557 ; 10558 ; Napier also mentioned "square and cubicle extractions". 10559 ; To raise a number to the power 3, find its 'ln', multiply by 3 and find the 10560 ; 'antiln'. e.g. PRINT EXP( LN 4 * 3 ) gives 64. 10561 ; Similarly to find the n'th root divide the logarithm by 'n'. 10562 ; The ZX81 ROM used PRINT EXP ( LN 9 / 2 ) to find the square root of the 10563 ; number 9. The Napieran square root function is just a special case of 10564 ; the 'to_power' function. A cube root or indeed any root/power would be just 10565 ; as simple. 10566 10567 ; First test that the argument to LN is a positive, non-zero number. 10568 10569 mark_1CA9: 10570 ln: 10571 RST _FP_CALC ;; 10572 DEFB __duplicate ;; 10573 DEFB __greater_0 ;; 10574 DEFB __jump_true ;; 10575 DEFB VALID - $ ;;to VALID 10576 10577 DEFB __end_calc ;; 10578 10579 10580 mark_1CAF: 10581 REPORT_Ab: 10582 RST _ERROR_1 10583 DEFB $09 ; Error Report: Invalid argument 10584 10585 VALID: 10586 if ORIGINAL 10587 mark_1CB1: 10588 DEFB __stk_zero ;; Note. not necessary. 10589 DEFB __delete ;; 10590 endif 10591 DEFB __end_calc ;; 10592 LD A,(HL) ; 10593 10594 LD (HL),$80 ; 10595 CALL STACK_A 10596 10597 RST _FP_CALC ;; 10598 DEFB __stk_data ;; 10599 DEFB $38 ;;Exponent: $88, Bytes: 1 10600 DEFB $00 ;;(+00,+00,+00) 10601 DEFB __subtract ;; 10602 DEFB __exchange ;; 10603 DEFB __duplicate ;; 10604 DEFB __stk_data ;; 10605 DEFB $F0 ;;Exponent: $80, Bytes: 4 10606 DEFB $4C,$CC,$CC,$CD ;; 10607 DEFB __subtract ;; 10608 DEFB __greater_0 ;; 10609 DEFB __jump_true ;; 10610 DEFB GRE_8 - $ ;; 10611 10612 DEFB __exchange ;; 10613 DEFB __stk_one ;; 10614 DEFB __subtract ;; 10615 DEFB __exchange ;; 10616 DEFB __end_calc ;; 10617 10618 INC (HL) ; 10619 10620 RST _FP_CALC ;; 10621 10622 mark_1CD2: 10623 GRE_8: 10624 DEFB __exchange ;; 10625 DEFB __stk_data ;; 10626 DEFB $F0 ;;Exponent: $80, Bytes: 4 10627 DEFB $31,$72,$17,$F8 ;; 10628 DEFB __multiply ;; 10629 DEFB __exchange ;; 10630 DEFB __stk_half ;; 10631 DEFB __subtract ;; 10632 DEFB __stk_half ;; 10633 DEFB __subtract ;; 10634 DEFB __duplicate ;; 10635 DEFB __stk_data ;; 10636 DEFB $32 ;;Exponent: $82, Bytes: 1 10637 DEFB $20 ;;(+00,+00,+00) 10638 DEFB __multiply ;; 10639 DEFB __stk_half ;; 10640 DEFB __subtract ;; 10641 DEFB __series_0C ;; 10642 DEFB $11 ;;Exponent: $61, Bytes: 1 10643 DEFB $AC ;;(+00,+00,+00) 10644 DEFB $14 ;;Exponent: $64, Bytes: 1 10645 DEFB $09 ;;(+00,+00,+00) 10646 DEFB $56 ;;Exponent: $66, Bytes: 2 10647 DEFB $DA,$A5 ;;(+00,+00) 10648 DEFB $59 ;;Exponent: $69, Bytes: 2 10649 DEFB $30,$C5 ;;(+00,+00) 10650 DEFB $5C ;;Exponent: $6C, Bytes: 2 10651 DEFB $90,$AA ;;(+00,+00) 10652 DEFB $9E ;;Exponent: $6E, Bytes: 3 10653 DEFB $70,$6F,$61 ;;(+00) 10654 DEFB $A1 ;;Exponent: $71, Bytes: 3 10655 DEFB $CB,$DA,$96 ;;(+00) 10656 DEFB $A4 ;;Exponent: $74, Bytes: 3 10657 DEFB $31,$9F,$B4 ;;(+00) 10658 DEFB $E7 ;;Exponent: $77, Bytes: 4 10659 DEFB $A0,$FE,$5C,$FC ;; 10660 DEFB $EA ;;Exponent: $7A, Bytes: 4 10661 DEFB $1B,$43,$CA,$36 ;; 10662 DEFB $ED ;;Exponent: $7D, Bytes: 4 10663 DEFB $A7,$9C,$7E,$5E ;; 10664 DEFB $F0 ;;Exponent: $80, Bytes: 4 10665 DEFB $6E,$23,$80,$93 ;; 10666 DEFB __multiply ;; 10667 DEFB __addition ;; 10668 DEFB __end_calc ;; 10669 10670 RET ; return. 10671 10672 if ORIGINAL 10673 else 10674 ; ------------------------------ 10675 ; THE NEW 'SQUARE ROOT' FUNCTION 10676 ; ------------------------------ 10677 ; (Offset $25: 'sqr') 10678 ; "If I have seen further, it is by standing on the shoulders of giants" - 10679 ; Sir Isaac Newton, Cambridge 1676. 10680 ; The sqr function has been re-written to use the Newton-Raphson method. 10681 ; Joseph Raphson was a student of Sir Isaac Newton at Cambridge University 10682 ; and helped publicize his work. 10683 ; Although Newton's method is centuries old, this routine, appropriately, is 10684 ; based on a FORTH word written by Steven Vickers in the Jupiter Ace manual. 10685 ; Whereas that method uses an initial guess of one, this one manipulates 10686 ; the exponent byte to obtain a better starting guess. 10687 ; First test for zero and return zero, if so, as the result. 10688 ; If the argument is negative, then produce an error. 10689 ; 10690 sqr: RST _FP_CALC ;; x 10691 DEFB __st_mem_3 ;; x. (seed for guess) 10692 DEFB __end_calc ;; x. 10693 10694 ; HL now points to exponent of argument on calculator stack. 10695 10696 LD A,(HL) ; Test for zero argument 10697 AND A ; 10698 10699 RET Z ; Return with zero on the calculator stack. 10700 10701 ; Test for a positive argument 10702 10703 INC HL ; Address byte with sign bit. 10704 BIT 7,(HL) ; Test the bit. 10705 10706 JR NZ,REPORT_Ab ; back to REPORT_A 10707 ; 'Invalid argument' 10708 10709 ; This guess is based on a Usenet discussion. 10710 ; Halve the exponent to achieve a good guess.(accurate with .25 16 64 etc.) 10711 10712 LD HL,$4071 ; Address first byte of mem-3 10713 10714 LD A,(HL) ; fetch exponent of mem-3 10715 XOR $80 ; toggle sign of exponent of mem-3 10716 SRA A ; shift right, bit 7 unchanged. 10717 INC A ; 10718 JR Z,ASIS ; forward with say .25 -> .5 10719 JP P,ASIS ; leave increment if value > .5 10720 DEC A ; restore to shift only. 10721 ASIS: XOR $80 ; restore sign. 10722 LD (HL),A ; and put back 'halved' exponent. 10723 10724 ; Now re-enter the calculator. 10725 10726 RST 28H ;; FP-CALC x 10727 10728 SLOOP: DEFB __duplicate ;; x,x. 10729 DEFB __get_mem_3 ;; x,x,guess 10730 DEFB __st_mem_4 ;; x,x,guess 10731 DEFB __division ;; x,x/guess. 10732 DEFB __get_mem_3 ;; x,x/guess,guess 10733 DEFB __addition ;; x,x/guess+guess 10734 DEFB __stk_half ;; x,x/guess+guess,.5 10735 DEFB __multiply ;; x,(x/guess+guess)*.5 10736 DEFB __st_mem_3 ;; x,newguess 10737 DEFB __get_mem_4 ;; x,newguess,oldguess 10738 DEFB __subtract ;; x,newguess-oldguess 10739 DEFB __abs ;; x,difference. 10740 DEFB __greater_0 ;; x,(0/1). 10741 DEFB __jump_true ;; x. 10742 10743 DEFB SLOOP - $ ;; x. 10744 10745 DEFB __delete ;; . 10746 DEFB __get_mem_3 ;; retrieve final guess. 10747 DEFB __end_calc ;; sqr x. 10748 10749 RET ; return with square root on stack 10750 10751 ; or in ZX81 BASIC 10752 ; 10753 ; 5 PRINT "NEWTON RAPHSON SQUARE ROOTS" 10754 ; 10 INPUT "NUMBER ";N 10755 ; 20 INPUT "GUESS ";G 10756 ; 30 PRINT " NUMBER "; N ;" GUESS "; G 10757 ; 40 FOR I = 1 TO 10 10758 ; 50 LET B = N/G 10759 ; 60 LET C = B+G 10760 ; 70 LET G = C/2 10761 ; 80 PRINT I; " VALUE "; G 10762 ; 90 NEXT I 10763 ; 100 PRINT "NAPIER METHOD"; SQR N 10764 endif 10765 10766 10767 ; THE 'TRIGONOMETRIC' FUNCTIONS 10768 10769 ; Trigonometry is rocket science. It is also used by carpenters and pyramid 10770 ; builders. 10771 ; Some uses can be quite abstract but the principles can be seen in simple 10772 ; right-angled triangles. Triangles have some special properties - 10773 ; 10774 ; 1) The sum of the three angles is always PI radians (180 degrees). 10775 ; Very helpful if you know two angles and wish to find the third. 10776 ; 2) In any right-angled triangle the sum of the squares of the two shorter 10777 ; sides is equal to the square of the longest side opposite the right-angle. 10778 ; Very useful if you know the length of two sides and wish to know the 10779 ; length of the third side. 10780 ; 3) Functions sine, cosine and tangent enable one to calculate the length 10781 ; of an unknown side when the length of one other side and an angle is 10782 ; known. 10783 ; 4) Functions arcsin, arccosine and arctan enable one to calculate an unknown 10784 ; angle when the length of two of the sides is known. 10785 10786 10787 ; THE 'REDUCE ARGUMENT' SUBROUTINE 10788 10789 ; (offset $35: 'get_argt') 10790 ; 10791 ; This routine performs two functions on the angle, in radians, that forms 10792 ; the argument to the sine and cosine functions. 10793 ; First it ensures that the angle 'wraps round'. That if a ship turns through 10794 ; an angle of, say, 3*PI radians (540 degrees) then the net effect is to turn 10795 ; through an angle of PI radians (180 degrees). 10796 ; Secondly it converts the angle in radians to a fraction of a right angle, 10797 ; depending within which quadrant the angle lies, with the periodicity 10798 ; resembling that of the desired sine value. 10799 ; The result lies in the range -1 to +1. 10800 ; 10801 ; 90 deg. 10802 ; 10803 ; (pi/2) 10804 ; II +1 I 10805 ; | 10806 ; sin+ |\ | /| sin+ 10807 ; cos- | \ | / | cos+ 10808 ; tan- | \ | / | tan+ 10809 ; | \|/) | 10810 ; 180 deg. (pi) 0 |----+----|-- 0 (0) 0 degrees 10811 ; | /|\ | 10812 ; sin- | / | \ | sin- 10813 ; cos- | / | \ | cos+ 10814 ; tan+ |/ | \| tan- 10815 ; | 10816 ; III -1 IV 10817 ; (3pi/2) 10818 ; 10819 ; 270 deg. 10820 10821 mark_1D18: 10822 get_argt: 10823 RST _FP_CALC ;; X. 10824 DEFB __stk_data ;; 10825 DEFB $EE ;;Exponent: $7E, 10826 ;;Bytes: 4 10827 DEFB $22,$F9,$83,$6E ;; X, 1/(2*PI) 10828 DEFB __multiply ;; X/(2*PI) = fraction 10829 10830 DEFB __duplicate ;; 10831 DEFB __stk_half ;; 10832 DEFB __addition ;; 10833 DEFB __int ;; 10834 10835 DEFB __subtract ;; now range -.5 to .5 10836 10837 DEFB __duplicate ;; 10838 DEFB __addition ;; now range -1 to 1. 10839 DEFB __duplicate ;; 10840 DEFB __addition ;; now range -2 to 2. 10841 10842 ; quadrant I (0 to +1) and quadrant IV (-1 to 0) are now correct. 10843 ; quadrant II ranges +1 to +2. 10844 ; quadrant III ranges -2 to -1. 10845 10846 DEFB __duplicate ;; Y, Y. 10847 DEFB __abs ;; Y, abs(Y). range 1 to 2 10848 DEFB __stk_one ;; Y, abs(Y), 1. 10849 DEFB __subtract ;; Y, abs(Y)-1. range 0 to 1 10850 DEFB __duplicate ;; Y, Z, Z. 10851 DEFB __greater_0 ;; Y, Z, (1/0). 10852 10853 DEFB __st_mem_0 ;; store as possible sign 10854 ;; for cosine function. 10855 10856 DEFB __jump_true ;; 10857 DEFB Z_PLUS - $ ;; with quadrants II and III 10858 10859 ; else the angle lies in quadrant I or IV and value Y is already correct. 10860 10861 DEFB __delete ;; Y delete test value. 10862 DEFB __end_calc ;; Y. 10863 10864 RET ; return. with Q1 and Q4 >>> 10865 10866 ; The branch was here with quadrants II (0 to 1) and III (1 to 0). 10867 ; Y will hold -2 to -1 if this is quadrant III. 10868 10869 mark_1D35: 10870 Z_PLUS: 10871 DEFB __stk_one ;; Y, Z, 1 10872 DEFB __subtract ;; Y, Z-1. Q3 = 0 to -1 10873 DEFB __exchange ;; Z-1, Y. 10874 DEFB __less_0 ;; Z-1, (1/0). 10875 DEFB __jump_true ;; Z-1. 10876 DEFB YNEG - $ ;; 10877 ;;if angle in quadrant III 10878 10879 ; else angle is within quadrant II (-1 to 0) 10880 10881 DEFB __negate ; range +1 to 0 10882 10883 10884 mark_1D3C: 10885 YNEG: 10886 DEFB __end_calc ;; quadrants II and III correct. 10887 10888 RET ; return. 10889 10890 10891 10892 ; THE 'COSINE' FUNCTION 10893 10894 ; (offset $1D: 'cos') 10895 ; Cosines are calculated as the sine of the opposite angle rectifying the 10896 ; sign depending on the quadrant rules. 10897 ; 10898 ; 10899 ; /| 10900 ; h /y| 10901 ; / |o 10902 ; / x | 10903 ; /----| 10904 ; a 10905 ; 10906 ; The cosine of angle x is the adjacent side (a) divided by the hypotenuse 1. 10907 ; However if we examine angle y then a/h is the sine of that angle. 10908 ; Since angle x plus angle y equals a right-angle, we can find angle y by 10909 ; subtracting angle x from pi/2. 10910 ; However it's just as easy to reduce the argument first and subtract the 10911 ; reduced argument from the value 1 (a reduced right-angle). 10912 ; It's even easier to subtract 1 from the angle and rectify the sign. 10913 ; In fact, after reducing the argument, the absolute value of the argument 10914 ; is used and rectified using the test result stored in mem-0 by 'get-argt' 10915 ; for that purpose. 10916 10917 mark_1D3E: 10918 cos: 10919 RST _FP_CALC ;; angle in radians. 10920 DEFB __get_argt ;; X reduce -1 to +1 10921 10922 DEFB __abs ;; ABS X 0 to 1 10923 DEFB __stk_one ;; ABS X, 1. 10924 DEFB __subtract ;; now opposite angle 10925 ;; though negative sign. 10926 DEFB __get_mem_0 ;; fetch sign indicator. 10927 DEFB __jump_true ;; 10928 DEFB C_ENT - $ ;;fwd to C_ENT 10929 ;;forward to common code if in QII or QIII 10930 10931 10932 DEFB __negate ;; else make positive. 10933 DEFB __jump ;; 10934 DEFB C_ENT - $ ;;fwd to C_ENT 10935 ;;with quadrants QI and QIV 10936 10937 10938 ; THE 'SINE' FUNCTION 10939 10940 ; (offset $1C: 'sin') 10941 ; This is a fundamental transcendental function from which others such as cos 10942 ; and tan are directly, or indirectly, derived. 10943 ; It uses the series generator to produce Chebyshev polynomials. 10944 ; 10945 ; 10946 ; /| 10947 ; 1 / | 10948 ; / |x 10949 ; /a | 10950 ; /----| 10951 ; y 10952 ; 10953 ; The 'get-argt' function is designed to modify the angle and its sign 10954 ; in line with the desired sine value and afterwards it can launch straight 10955 ; into common code. 10956 10957 mark_1D49: 10958 sin: 10959 RST _FP_CALC ;; angle in radians 10960 DEFB __get_argt ;; reduce - sign now correct. 10961 10962 mark_1D4B: 10963 C_ENT: 10964 DEFB __duplicate ;; 10965 DEFB __duplicate ;; 10966 DEFB __multiply ;; 10967 DEFB __duplicate ;; 10968 DEFB __addition ;; 10969 DEFB __stk_one ;; 10970 DEFB __subtract ;; 10971 10972 DEFB __series_06 ;; 10973 DEFB $14 ;;Exponent: $64, Bytes: 1 10974 DEFB $E6 ;;(+00,+00,+00) 10975 DEFB $5C ;;Exponent: $6C, Bytes: 2 10976 DEFB $1F,$0B ;;(+00,+00) 10977 DEFB $A3 ;;Exponent: $73, Bytes: 3 10978 DEFB $8F,$38,$EE ;;(+00) 10979 DEFB $E9 ;;Exponent: $79, Bytes: 4 10980 DEFB $15,$63,$BB,$23 ;; 10981 DEFB $EE ;;Exponent: $7E, Bytes: 4 10982 DEFB $92,$0D,$CD,$ED ;; 10983 DEFB $F1 ;;Exponent: $81, Bytes: 4 10984 DEFB $23,$5D,$1B,$EA ;; 10985 10986 DEFB __multiply ;; 10987 DEFB __end_calc ;; 10988 10989 RET ; return. 10990 10991 10992 10993 ; THE 'TANGENT' FUNCTION 10994 10995 ; (offset $1E: 'tan') 10996 ; 10997 ; Evaluates tangent x as sin(x) / cos(x). 10998 ; 10999 ; 11000 ; /| 11001 ; h / | 11002 ; / |o 11003 ; /x | 11004 ; /----| 11005 ; a 11006 ; 11007 ; The tangent of angle x is the ratio of the length of the opposite side 11008 ; divided by the length of the adjacent side. As the opposite length can 11009 ; be calculates using sin(x) and the adjacent length using cos(x) then 11010 ; the tangent can be defined in terms of the previous two functions. 11011 11012 ; Error 6 if the argument, in radians, is too close to one like pi/2 11013 ; which has an infinite tangent. e.g. PRINT TAN (PI/2) evaluates as 1/0. 11014 ; Similarly PRINT TAN (3*PI/2), TAN (5*PI/2) etc. 11015 11016 mark_1D6E: 11017 tan: 11018 RST _FP_CALC ;; x. 11019 DEFB __duplicate ;; x, x. 11020 DEFB __sin ;; x, sin x. 11021 DEFB __exchange ;; sin x, x. 11022 DEFB __cos ;; sin x, cos x. 11023 DEFB __division ;; sin x/cos x (= tan x). 11024 DEFB __end_calc ;; tan x. 11025 11026 RET ; return. 11027 11028 11029 ; THE 'ARCTAN' FUNCTION 11030 11031 ; (Offset $21: 'atn') 11032 ; The inverse tangent function with the result in radians. 11033 ; This is a fundamental transcendental function from which others such as 11034 ; asn and acs are directly, or indirectly, derived. 11035 ; It uses the series generator to produce Chebyshev polynomials. 11036 11037 mark_1D76: 11038 atn: 11039 LD A,(HL) ; fetch exponent 11040 CP $81 ; compare to that for 'one' 11041 JR C,SMALL ; forward, if less 11042 11043 RST _FP_CALC ;; X. 11044 DEFB __stk_one ;; 11045 DEFB __negate ;; 11046 DEFB __exchange ;; 11047 DEFB __division ;; 11048 DEFB __duplicate ;; 11049 DEFB __less_0 ;; 11050 DEFB __stk_half_pi ;; 11051 DEFB __exchange ;; 11052 DEFB __jump_true ;; 11053 DEFB CASES - $ ;; 11054 11055 DEFB __negate ;; 11056 DEFB __jump ;; 11057 DEFB CASES - $ ;; 11058 11059 ; ___ 11060 11061 mark_1D89: 11062 SMALL: 11063 RST _FP_CALC ;; 11064 DEFB __stk_zero ;; 11065 11066 mark_1D8B: 11067 CASES: 11068 DEFB __exchange ;; 11069 DEFB __duplicate ;; 11070 DEFB __duplicate ;; 11071 DEFB __multiply ;; 11072 DEFB __duplicate ;; 11073 DEFB __addition ;; 11074 DEFB __stk_one ;; 11075 DEFB __subtract ;; 11076 11077 DEFB __series_0C ;; 11078 DEFB $10 ;;Exponent: $60, Bytes: 1 11079 DEFB $B2 ;;(+00,+00,+00) 11080 DEFB $13 ;;Exponent: $63, Bytes: 1 11081 DEFB $0E ;;(+00,+00,+00) 11082 DEFB $55 ;;Exponent: $65, Bytes: 2 11083 DEFB $E4,$8D ;;(+00,+00) 11084 DEFB $58 ;;Exponent: $68, Bytes: 2 11085 DEFB $39,$BC ;;(+00,+00) 11086 DEFB $5B ;;Exponent: $6B, Bytes: 2 11087 DEFB $98,$FD ;;(+00,+00) 11088 DEFB $9E ;;Exponent: $6E, Bytes: 3 11089 DEFB $00,$36,$75 ;;(+00) 11090 DEFB $A0 ;;Exponent: $70, Bytes: 3 11091 DEFB $DB,$E8,$B4 ;;(+00) 11092 DEFB $63 ;;Exponent: $73, Bytes: 2 11093 DEFB $42,$C4 ;;(+00,+00) 11094 DEFB $E6 ;;Exponent: $76, Bytes: 4 11095 DEFB $B5,$09,$36,$BE ;; 11096 DEFB $E9 ;;Exponent: $79, Bytes: 4 11097 DEFB $36,$73,$1B,$5D ;; 11098 DEFB $EC ;;Exponent: $7C, Bytes: 4 11099 DEFB $D8,$DE,$63,$BE ;; 11100 DEFB $F0 ;;Exponent: $80, Bytes: 4 11101 DEFB $61,$A1,$B3,$0C ;; 11102 11103 DEFB __multiply ;; 11104 DEFB __addition ;; 11105 DEFB __end_calc ;; 11106 11107 RET ; return. 11108 11109 11110 11111 ; THE 'ARCSIN' FUNCTION 11112 11113 ; (Offset $1F: 'asn') 11114 ; The inverse sine function with result in radians. 11115 ; Derived from arctan function above. 11116 ; Error A unless the argument is between -1 and +1 inclusive. 11117 ; Uses an adaptation of the formula asn(x) = atn(x/sqr(1-x*x)) 11118 ; 11119 ; 11120 ; /| 11121 ; / | 11122 ; 1/ |x 11123 ; /a | 11124 ; /----| 11125 ; y 11126 ; 11127 ; e.g. We know the opposite side (x) and hypotenuse (1) 11128 ; and we wish to find angle a in radians. 11129 ; We can derive length y by Pythagoras and then use ATN instead. 11130 ; Since y*y + x*x = 1*1 (Pythagoras Theorem) then 11131 ; y=sqr(1-x*x) - no need to multiply 1 by itself. 11132 ; So, asn(a) = atn(x/y) 11133 ; or more fully, 11134 ; asn(a) = atn(x/sqr(1-x*x)) 11135 11136 ; Close but no cigar. 11137 11138 ; While PRINT ATN (x/SQR (1-x*x)) gives the same results as PRINT ASN x, 11139 ; it leads to division by zero when x is 1 or -1. 11140 ; To overcome this, 1 is added to y giving half the required angle and the 11141 ; result is then doubled. 11142 ; That is, PRINT ATN (x/(SQR (1-x*x) +1)) *2 11143 ; 11144 ; 11145 ; . /| 11146 ; . c/ | 11147 ; . /1 |x 11148 ; . c b /a | 11149 ; ---------/----| 11150 ; 1 y 11151 ; 11152 ; By creating an isosceles triangle with two equal sides of 1, angles c and 11153 ; c are also equal. If b+c+d = 180 degrees and b+a = 180 degrees then c=a/2. 11154 ; 11155 ; A value higher than 1 gives the required error as attempting to find the 11156 ; square root of a negative number generates an error in Sinclair BASIC. 11157 11158 mark_1DC4: 11159 asn: 11160 RST _FP_CALC ;; x. 11161 DEFB __duplicate ;; x, x. 11162 DEFB __duplicate ;; x, x, x. 11163 DEFB __multiply ;; x, x*x. 11164 DEFB __stk_one ;; x, x*x, 1. 11165 DEFB __subtract ;; x, x*x-1. 11166 DEFB __negate ;; x, 1-x*x. 11167 DEFB __sqr ;; x, sqr(1-x*x) = y. 11168 DEFB __stk_one ;; x, y, 1. 11169 DEFB __addition ;; x, y+1. 11170 DEFB __division ;; x/y+1. 11171 DEFB __atn ;; a/2 (half the angle) 11172 DEFB __duplicate ;; a/2, a/2. 11173 DEFB __addition ;; a. 11174 DEFB __end_calc ;; a. 11175 11176 RET ; return. 11177 11178 11179 11180 ; THE 'ARCCOS' FUNCTION 11181 11182 ; (Offset $20: 'acs') 11183 ; The inverse cosine function with the result in radians. 11184 ; Error A unless the argument is between -1 and +1. 11185 ; Result in range 0 to pi. 11186 ; Derived from asn above which is in turn derived from the preceding atn. It 11187 ; could have been derived directly from atn using acs(x) = atn(sqr(1-x*x)/x). 11188 ; However, as sine and cosine are horizontal translations of each other, 11189 ; uses acs(x) = pi/2 - asn(x) 11190 11191 ; e.g. the arccosine of a known x value will give the required angle b in 11192 ; radians. 11193 ; We know, from above, how to calculate the angle a using asn(x). 11194 ; Since the three angles of any triangle add up to 180 degrees, or pi radians, 11195 ; and the largest angle in this case is a right-angle (pi/2 radians), then 11196 ; we can calculate angle b as pi/2 (both angles) minus asn(x) (angle a). 11197 ; 11198 ;; 11199 ; /| 11200 ; 1 /b| 11201 ; / |x 11202 ; /a | 11203 ; /----| 11204 ; y 11205 11206 mark_1DD4: 11207 acs: 11208 RST _FP_CALC ;; x. 11209 DEFB __asn ;; asn(x). 11210 DEFB __stk_half_pi ;; asn(x), pi/2. 11211 DEFB __subtract ;; asn(x) - pi/2. 11212 DEFB __negate ;; pi/2 - asn(x) = acs(x). 11213 DEFB __end_calc ;; acs(x) 11214 11215 RET ; return. 11216 11217 if ORIGINAL 11218 11219 ; THE 'SQUARE ROOT' FUNCTION 11220 11221 ; (Offset $25: 'sqr') 11222 ; Error A if argument is negative. 11223 ; This routine is remarkable for its brevity - 7 bytes. 11224 ; 11225 ; The ZX81 code was originally 9K and various techniques had to be 11226 ; used to shoe-horn it into an 8K Rom chip. 11227 11228 ; This routine uses Napier's method for calculating square roots which was 11229 ; devised in 1614 and calculates the value as EXP (LN 'x' * 0.5). 11230 ; 11231 ; This is a little on the slow side as it involves two polynomial series. 11232 ; A series of 12 for LN and a series of 8 for EXP. 11233 ; This was of no concern to John Napier since his tables were 'compiled forever'. 11234 11235 mark_1DDB: 11236 sqr: 11237 RST _FP_CALC ;; x. 11238 DEFB __duplicate ;; x, x. 11239 DEFB __not ;; x, 1/0 11240 DEFB __jump_true ;; x, (1/0). 11241 DEFB LAST - $ ;; exit if argument zero 11242 ;; with zero result. 11243 11244 ; else continue to calculate as x ** .5 11245 11246 DEFB __stk_half ;; x, .5. 11247 DEFB __end_calc ;; x, .5. 11248 11249 endif 11250 11251 11252 ; THE 'EXPONENTIATION' OPERATION 11253 11254 ; (Offset $06: 'to_power') 11255 ; This raises the first number X to the power of the second number Y. 11256 ; As with the ZX80, 11257 ; 0 ** 0 = 1 11258 ; 0 ** +n = 0 11259 ; 0 ** -n = arithmetic overflow. 11260 11261 mark_1DE2: 11262 to_power: 11263 RST _FP_CALC ;; X,Y. 11264 DEFB __exchange ;; Y,X. 11265 DEFB __duplicate ;; Y,X,X. 11266 DEFB __not ;; Y,X,(1/0). 11267 DEFB __jump_true ;; 11268 DEFB XISO - $ ;;forward to XISO if X is zero. 11269 11270 ; else X is non-zero. function 'ln' will catch a negative value of X. 11271 11272 DEFB __ln ;; Y, LN X. 11273 DEFB __multiply ;; Y * LN X 11274 DEFB __end_calc ;; 11275 11276 JP exp ; jump back to EXP routine. -> 11277 11278 ; ___ 11279 11280 ; These routines form the three simple results when the number is zero. 11281 ; begin by deleting the known zero to leave Y the power factor. 11282 11283 mark_1DEE: 11284 XISO: 11285 DEFB __delete ;; Y. 11286 DEFB __duplicate ;; Y, Y. 11287 DEFB __not ;; Y, (1/0). 11288 DEFB __jump_true ;; 11289 DEFB ONE - $ ;; if Y is zero. 11290 11291 ; the power factor is not zero. If negative then an error exists. 11292 11293 DEFB __stk_zero ;; Y, 0. 11294 DEFB __exchange ;; 0, Y. 11295 DEFB __greater_0 ;; 0, (1/0). 11296 DEFB __jump_true ;; 0 11297 DEFB LAST - $ ;; if Y was any positive 11298 ;; number. 11299 11300 ; else force division by zero thereby raising an Arithmetic overflow error. 11301 ; There are some one and two-byte alternatives but perhaps the most formal 11302 ; might have been to use end_calc; rst 08; defb 05. 11303 11304 ; if ORIGINAL 11305 11306 ; the SG ROM seems to want it the old way! 11307 if 1 11308 DEFB __stk_one ;; 0, 1. 11309 DEFB __exchange ;; 1, 0. 11310 DEFB __division ;; 1/0 >> error 11311 else 11312 DEFB $34 ;+ end-calc 11313 REPORT_6c 11314 RST 08H ;+ ERROR-1 11315 DEFB $05 ;+ Error Report: Number too big 11316 endif 11317 11318 11319 ; ___ 11320 11321 mark_1DFB: 11322 ONE: 11323 DEFB __delete ;; . 11324 DEFB __stk_one ;; 1. 11325 11326 mark_1DFD: 11327 LAST: 11328 DEFB __end_calc ;; last value 1 or 0. 11329 11330 RET ; return. 11331 11332 11333 ; THE 'SPARE LOCATIONS' 11334 11335 SPARE: 11336 11337 if ORIGINAL 11338 mark_1DFF: 11339 DEFB $FF ; That's all folks. 11340 else 11341 mark_1DFE: 11342 L1DFE: 11343 11344 ;; DEFB $FF, $FF ; Two spare bytes. 11345 DEFB $00, $00 ; Two spare bytes (as per the Shoulders of Giants ROM) 11346 endif 11347 11348 11349 11350 ; THE 'ZX81 CHARACTER SET' 11351 11352 11353 mark_1E00: 11354 char_set: ; - begins with space character. 11355 11356 ; $00 - Character: ' ' CHR$(0) 11357 11358 DEFB %00000000 11359 DEFB %00000000 11360 DEFB %00000000 11361 DEFB %00000000 11362 DEFB %00000000 11363 DEFB %00000000 11364 DEFB %00000000 11365 DEFB %00000000 11366 11367 ; $01 - Character: mosaic CHR$(1) 11368 11369 DEFB %11110000 11370 DEFB %11110000 11371 DEFB %11110000 11372 DEFB %11110000 11373 DEFB %00000000 11374 DEFB %00000000 11375 DEFB %00000000 11376 DEFB %00000000 11377 11378 11379 ; $02 - Character: mosaic CHR$(2) 11380 11381 DEFB %00001111 11382 DEFB %00001111 11383 DEFB %00001111 11384 DEFB %00001111 11385 DEFB %00000000 11386 DEFB %00000000 11387 DEFB %00000000 11388 DEFB %00000000 11389 11390 11391 ; $03 - Character: mosaic CHR$(3) 11392 11393 DEFB %11111111 11394 DEFB %11111111 11395 DEFB %11111111 11396 DEFB %11111111 11397 DEFB %00000000 11398 DEFB %00000000 11399 DEFB %00000000 11400 DEFB %00000000 11401 11402 ; $04 - Character: mosaic CHR$(4) 11403 11404 DEFB %00000000 11405 DEFB %00000000 11406 DEFB %00000000 11407 DEFB %00000000 11408 DEFB %11110000 11409 DEFB %11110000 11410 DEFB %11110000 11411 DEFB %11110000 11412 11413 ; $05 - Character: mosaic CHR$(5) 11414 11415 DEFB %11110000 11416 DEFB %11110000 11417 DEFB %11110000 11418 DEFB %11110000 11419 DEFB %11110000 11420 DEFB %11110000 11421 DEFB %11110000 11422 DEFB %11110000 11423 11424 ; $06 - Character: mosaic CHR$(6) 11425 11426 DEFB %00001111 11427 DEFB %00001111 11428 DEFB %00001111 11429 DEFB %00001111 11430 DEFB %11110000 11431 DEFB %11110000 11432 DEFB %11110000 11433 DEFB %11110000 11434 11435 ; $07 - Character: mosaic CHR$(7) 11436 11437 DEFB %11111111 11438 DEFB %11111111 11439 DEFB %11111111 11440 DEFB %11111111 11441 DEFB %11110000 11442 DEFB %11110000 11443 DEFB %11110000 11444 DEFB %11110000 11445 11446 ; $08 - Character: mosaic CHR$(8) 11447 11448 DEFB %10101010 11449 DEFB %01010101 11450 DEFB %10101010 11451 DEFB %01010101 11452 DEFB %10101010 11453 DEFB %01010101 11454 DEFB %10101010 11455 DEFB %01010101 11456 ; $09 - Character: mosaic CHR$(9) 11457 11458 DEFB %00000000 11459 DEFB %00000000 11460 DEFB %00000000 11461 DEFB %00000000 11462 DEFB %10101010 11463 DEFB %01010101 11464 DEFB %10101010 11465 DEFB %01010101 11466 ; $0A - Character: mosaic CHR$(10) 11467 11468 DEFB %10101010 11469 DEFB %01010101 11470 DEFB %10101010 11471 DEFB %01010101 11472 DEFB %00000000 11473 DEFB %00000000 11474 DEFB %00000000 11475 DEFB %00000000 11476 11477 ; $0B - Character: '"' CHR$(11) 11478 11479 DEFB %00000000 11480 DEFB %00100100 11481 DEFB %00100100 11482 DEFB %00000000 11483 DEFB %00000000 11484 DEFB %00000000 11485 DEFB %00000000 11486 DEFB %00000000 11487 11488 ; $0C - Character: � CHR$(12) 11489 11490 DEFB %00000000 11491 DEFB %00011100 11492 DEFB %00100010 11493 DEFB %01111000 11494 DEFB %00100000 11495 DEFB %00100000 11496 DEFB %01111110 11497 DEFB %00000000 11498 11499 ; $0D - Character: '$' CHR$(13) 11500 11501 DEFB %00000000 11502 DEFB %00001000 11503 DEFB %00111110 11504 DEFB %00101000 11505 DEFB %00111110 11506 DEFB %00001010 11507 DEFB %00111110 11508 DEFB %00001000 11509 11510 ; $0E - Character: ':' CHR$(14) 11511 11512 DEFB %00000000 11513 DEFB %00000000 11514 DEFB %00000000 11515 DEFB %00010000 11516 DEFB %00000000 11517 DEFB %00000000 11518 DEFB %00010000 11519 DEFB %00000000 11520 11521 ; $0F - Character: '?' CHR$(15) 11522 11523 DEFB %00000000 11524 DEFB %00111100 11525 DEFB %01000010 11526 DEFB %00000100 11527 DEFB %00001000 11528 DEFB %00000000 11529 DEFB %00001000 11530 DEFB %00000000 11531 11532 ; $10 - Character: '(' CHR$(16) 11533 11534 DEFB %00000000 11535 DEFB %00000100 11536 DEFB %00001000 11537 DEFB %00001000 11538 DEFB %00001000 11539 DEFB %00001000 11540 DEFB %00000100 11541 DEFB %00000000 11542 11543 ; $11 - Character: ')' CHR$(17) 11544 11545 DEFB %00000000 11546 DEFB %00100000 11547 DEFB %00010000 11548 DEFB %00010000 11549 DEFB %00010000 11550 DEFB %00010000 11551 DEFB %00100000 11552 DEFB %00000000 11553 11554 ; $12 - Character: '>' CHR$(18) 11555 11556 DEFB %00000000 11557 DEFB %00000000 11558 DEFB %00010000 11559 DEFB %00001000 11560 DEFB %00000100 11561 DEFB %00001000 11562 DEFB %00010000 11563 DEFB %00000000 11564 11565 ; $13 - Character: '<' CHR$(19) 11566 11567 DEFB %00000000 11568 DEFB %00000000 11569 DEFB %00000100 11570 DEFB %00001000 11571 DEFB %00010000 11572 DEFB %00001000 11573 DEFB %00000100 11574 DEFB %00000000 11575 11576 ; $14 - Character: '=' CHR$(20) 11577 11578 DEFB %00000000 11579 DEFB %00000000 11580 DEFB %00000000 11581 DEFB %00111110 11582 DEFB %00000000 11583 DEFB %00111110 11584 DEFB %00000000 11585 DEFB %00000000 11586 11587 ; $15 - Character: '+' CHR$(21) 11588 11589 DEFB %00000000 11590 DEFB %00000000 11591 DEFB %00001000 11592 DEFB %00001000 11593 DEFB %00111110 11594 DEFB %00001000 11595 DEFB %00001000 11596 DEFB %00000000 11597 11598 ; $16 - Character: '-' CHR$(22) 11599 11600 DEFB %00000000 11601 DEFB %00000000 11602 DEFB %00000000 11603 DEFB %00000000 11604 DEFB %00111110 11605 DEFB %00000000 11606 DEFB %00000000 11607 DEFB %00000000 11608 11609 ; $17 - Character: '*' CHR$(23) 11610 11611 DEFB %00000000 11612 DEFB %00000000 11613 DEFB %00010100 11614 DEFB %00001000 11615 DEFB %00111110 11616 DEFB %00001000 11617 DEFB %00010100 11618 DEFB %00000000 11619 11620 ; $18 - Character: '/' CHR$(24) 11621 11622 DEFB %00000000 11623 DEFB %00000000 11624 DEFB %00000010 11625 DEFB %00000100 11626 DEFB %00001000 11627 DEFB %00010000 11628 DEFB %00100000 11629 DEFB %00000000 11630 11631 ; $19 - Character: ';' CHR$(25) 11632 11633 DEFB %00000000 11634 DEFB %00000000 11635 DEFB %00010000 11636 DEFB %00000000 11637 DEFB %00000000 11638 DEFB %00010000 11639 DEFB %00010000 11640 DEFB %00100000 11641 11642 ; $1A - Character: ',' CHR$(26) 11643 11644 DEFB %00000000 11645 DEFB %00000000 11646 DEFB %00000000 11647 DEFB %00000000 11648 DEFB %00000000 11649 DEFB %00001000 11650 DEFB %00001000 11651 DEFB %00010000 11652 11653 ; $1B - Character: '"' CHR$(27) 11654 11655 DEFB %00000000 11656 DEFB %00000000 11657 DEFB %00000000 11658 DEFB %00000000 11659 DEFB %00000000 11660 DEFB %00011000 11661 DEFB %00011000 11662 DEFB %00000000 11663 11664 ; $1C - Character: '0' CHR$(28) 11665 11666 DEFB %00000000 11667 DEFB %00111100 11668 DEFB %01000110 11669 DEFB %01001010 11670 DEFB %01010010 11671 DEFB %01100010 11672 DEFB %00111100 11673 DEFB %00000000 11674 11675 ; $1D - Character: '1' CHR$(29) 11676 11677 DEFB %00000000 11678 DEFB %00011000 11679 DEFB %00101000 11680 DEFB %00001000 11681 DEFB %00001000 11682 DEFB %00001000 11683 DEFB %00111110 11684 DEFB %00000000 11685 11686 ; $1E - Character: '2' CHR$(30) 11687 11688 DEFB %00000000 11689 DEFB %00111100 11690 DEFB %01000010 11691 DEFB %00000010 11692 DEFB %00111100 11693 DEFB %01000000 11694 DEFB %01111110 11695 DEFB %00000000 11696 11697 ; $1F - Character: '3' CHR$(31) 11698 11699 DEFB %00000000 11700 DEFB %00111100 11701 DEFB %01000010 11702 DEFB %00001100 11703 DEFB %00000010 11704 DEFB %01000010 11705 DEFB %00111100 11706 DEFB %00000000 11707 11708 ; $20 - Character: '4' CHR$(32) 11709 11710 DEFB %00000000 11711 DEFB %00001000 11712 DEFB %00011000 11713 DEFB %00101000 11714 DEFB %01001000 11715 DEFB %01111110 11716 DEFB %00001000 11717 DEFB %00000000 11718 11719 ; $21 - Character: '5' CHR$(33) 11720 11721 DEFB %00000000 11722 DEFB %01111110 11723 DEFB %01000000 11724 DEFB %01111100 11725 DEFB %00000010 11726 DEFB %01000010 11727 DEFB %00111100 11728 DEFB %00000000 11729 11730 ; $22 - Character: '6' CHR$(34) 11731 11732 DEFB %00000000 11733 DEFB %00111100 11734 DEFB %01000000 11735 DEFB %01111100 11736 DEFB %01000010 11737 DEFB %01000010 11738 DEFB %00111100 11739 DEFB %00000000 11740 11741 ; $23 - Character: '7' CHR$(35) 11742 11743 DEFB %00000000 11744 DEFB %01111110 11745 DEFB %00000010 11746 DEFB %00000100 11747 DEFB %00001000 11748 DEFB %00010000 11749 DEFB %00010000 11750 DEFB %00000000 11751 11752 ; $24 - Character: '8' CHR$(36) 11753 11754 DEFB %00000000 11755 DEFB %00111100 11756 DEFB %01000010 11757 DEFB %00111100 11758 DEFB %01000010 11759 DEFB %01000010 11760 DEFB %00111100 11761 DEFB %00000000 11762 11763 ; $25 - Character: '9' CHR$(37) 11764 11765 DEFB %00000000 11766 DEFB %00111100 11767 DEFB %01000010 11768 DEFB %01000010 11769 DEFB %00111110 11770 DEFB %00000010 11771 DEFB %00111100 11772 DEFB %00000000 11773 11774 ; $26 - Character: 'A' CHR$(38) 11775 11776 DEFB %00000000 11777 DEFB %00111100 11778 DEFB %01000010 11779 DEFB %01000010 11780 DEFB %01111110 11781 DEFB %01000010 11782 DEFB %01000010 11783 DEFB %00000000 11784 11785 ; $27 - Character: 'B' CHR$(39) 11786 11787 DEFB %00000000 11788 DEFB %01111100 11789 DEFB %01000010 11790 DEFB %01111100 11791 DEFB %01000010 11792 DEFB %01000010 11793 DEFB %01111100 11794 DEFB %00000000 11795 11796 ; $28 - Character: 'C' CHR$(40) 11797 11798 DEFB %00000000 11799 DEFB %00111100 11800 DEFB %01000010 11801 DEFB %01000000 11802 DEFB %01000000 11803 DEFB %01000010 11804 DEFB %00111100 11805 DEFB %00000000 11806 11807 ; $29 - Character: 'D' CHR$(41) 11808 11809 DEFB %00000000 11810 DEFB %01111000 11811 DEFB %01000100 11812 DEFB %01000010 11813 DEFB %01000010 11814 DEFB %01000100 11815 DEFB %01111000 11816 DEFB %00000000 11817 11818 ; $2A - Character: 'E' CHR$(42) 11819 11820 DEFB %00000000 11821 DEFB %01111110 11822 DEFB %01000000 11823 DEFB %01111100 11824 DEFB %01000000 11825 DEFB %01000000 11826 DEFB %01111110 11827 DEFB %00000000 11828 11829 ; $2B - Character: 'F' CHR$(43) 11830 11831 DEFB %00000000 11832 DEFB %01111110 11833 DEFB %01000000 11834 DEFB %01111100 11835 DEFB %01000000 11836 DEFB %01000000 11837 DEFB %01000000 11838 DEFB %00000000 11839 11840 ; $2C - Character: 'G' CHR$(44) 11841 11842 DEFB %00000000 11843 DEFB %00111100 11844 DEFB %01000010 11845 DEFB %01000000 11846 DEFB %01001110 11847 DEFB %01000010 11848 DEFB %00111100 11849 DEFB %00000000 11850 11851 ; $2D - Character: 'H' CHR$(45) 11852 11853 DEFB %00000000 11854 DEFB %01000010 11855 DEFB %01000010 11856 DEFB %01111110 11857 DEFB %01000010 11858 DEFB %01000010 11859 DEFB %01000010 11860 DEFB %00000000 11861 11862 ; $2E - Character: 'I' CHR$(46) 11863 11864 DEFB %00000000 11865 DEFB %00111110 11866 DEFB %00001000 11867 DEFB %00001000 11868 DEFB %00001000 11869 DEFB %00001000 11870 DEFB %00111110 11871 DEFB %00000000 11872 11873 ; $2F - Character: 'J' CHR$(47) 11874 11875 DEFB %00000000 11876 DEFB %00000010 11877 DEFB %00000010 11878 DEFB %00000010 11879 DEFB %01000010 11880 DEFB %01000010 11881 DEFB %00111100 11882 DEFB %00000000 11883 11884 ; $30 - Character: 'K' CHR$(48) 11885 11886 DEFB %00000000 11887 DEFB %01000100 11888 DEFB %01001000 11889 DEFB %01110000 11890 DEFB %01001000 11891 DEFB %01000100 11892 DEFB %01000010 11893 DEFB %00000000 11894 11895 ; $31 - Character: 'L' CHR$(49) 11896 11897 DEFB %00000000 11898 DEFB %01000000 11899 DEFB %01000000 11900 DEFB %01000000 11901 DEFB %01000000 11902 DEFB %01000000 11903 DEFB %01111110 11904 DEFB %00000000 11905 11906 ; $32 - Character: 'M' CHR$(50) 11907 11908 DEFB %00000000 11909 DEFB %01000010 11910 DEFB %01100110 11911 DEFB %01011010 11912 DEFB %01000010 11913 DEFB %01000010 11914 DEFB %01000010 11915 DEFB %00000000 11916 11917 ; $33 - Character: 'N' CHR$(51) 11918 11919 DEFB %00000000 11920 DEFB %01000010 11921 DEFB %01100010 11922 DEFB %01010010 11923 DEFB %01001010 11924 DEFB %01000110 11925 DEFB %01000010 11926 DEFB %00000000 11927 11928 ; $34 - Character: 'O' CHR$(52) 11929 11930 DEFB %00000000 11931 DEFB %00111100 11932 DEFB %01000010 11933 DEFB %01000010 11934 DEFB %01000010 11935 DEFB %01000010 11936 DEFB %00111100 11937 DEFB %00000000 11938 11939 ; $35 - Character: 'P' CHR$(53) 11940 11941 DEFB %00000000 11942 DEFB %01111100 11943 DEFB %01000010 11944 DEFB %01000010 11945 DEFB %01111100 11946 DEFB %01000000 11947 DEFB %01000000 11948 DEFB %00000000 11949 11950 ; $36 - Character: 'Q' CHR$(54) 11951 11952 DEFB %00000000 11953 DEFB %00111100 11954 DEFB %01000010 11955 DEFB %01000010 11956 DEFB %01010010 11957 DEFB %01001010 11958 DEFB %00111100 11959 DEFB %00000000 11960 11961 ; $37 - Character: 'R' CHR$(55) 11962 11963 DEFB %00000000 11964 DEFB %01111100 11965 DEFB %01000010 11966 DEFB %01000010 11967 DEFB %01111100 11968 DEFB %01000100 11969 DEFB %01000010 11970 DEFB %00000000 11971 11972 ; $38 - Character: 'S' CHR$(56) 11973 11974 DEFB %00000000 11975 DEFB %00111100 11976 DEFB %01000000 11977 DEFB %00111100 11978 DEFB %00000010 11979 DEFB %01000010 11980 DEFB %00111100 11981 DEFB %00000000 11982 11983 ; $39 - Character: 'T' CHR$(57) 11984 11985 DEFB %00000000 11986 DEFB %11111110 11987 DEFB %00010000 11988 DEFB %00010000 11989 DEFB %00010000 11990 DEFB %00010000 11991 DEFB %00010000 11992 DEFB %00000000 11993 11994 ; $3A - Character: 'U' CHR$(58) 11995 11996 DEFB %00000000 11997 DEFB %01000010 11998 DEFB %01000010 11999 DEFB %01000010 12000 DEFB %01000010 12001 DEFB %01000010 12002 DEFB %00111100 12003 DEFB %00000000 12004 12005 ; $3B - Character: 'V' CHR$(59) 12006 12007 DEFB %00000000 12008 DEFB %01000010 12009 DEFB %01000010 12010 DEFB %01000010 12011 DEFB %01000010 12012 DEFB %00100100 12013 DEFB %00011000 12014 DEFB %00000000 12015 12016 ; $3C - Character: 'W' CHR$(60) 12017 12018 DEFB %00000000 12019 DEFB %01000010 12020 DEFB %01000010 12021 DEFB %01000010 12022 DEFB %01000010 12023 DEFB %01011010 12024 DEFB %00100100 12025 DEFB %00000000 12026 12027 ; $3D - Character: 'X' CHR$(61) 12028 12029 DEFB %00000000 12030 DEFB %01000010 12031 DEFB %00100100 12032 DEFB %00011000 12033 DEFB %00011000 12034 DEFB %00100100 12035 DEFB %01000010 12036 DEFB %00000000 12037 12038 ; $3E - Character: 'Y' CHR$(62) 12039 12040 DEFB %00000000 12041 DEFB %10000010 12042 DEFB %01000100 12043 DEFB %00101000 12044 DEFB %00010000 12045 DEFB %00010000 12046 DEFB %00010000 12047 DEFB %00000000 12048 12049 ; $3F - Character: 'Z' CHR$(63) 12050 12051 DEFB %00000000 12052 DEFB %01111110 12053 DEFB %00000100 12054 DEFB %00001000 12055 DEFB %00010000 12056 DEFB %00100000 12057 DEFB %01111110 12058 DEFB %00000000 12059 12060