Subversion Repositories pentevo

Rev

Rev 665 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
384 savelij 1
 
2
;www.fruitcake.plus.com
3
 
678 savelij 4
;LAST UPDATE: 17.11.2014 savelij
384 savelij 5
 
678 savelij 6
                include ../../macros.a80
7
                include ../../global_vars.a80
585 savelij 8
                include ../../define.a80
385 savelij 9
 
678 savelij 10
;BAS48_ONLY     EQU 0
550 savelij 11
TAP_EMU_BORDER  EQU 0
384 savelij 12
 
13
; **************************************
14
; *** SPECTRUM 128 ROM 1 DISASSEMBLY ***
15
; **************************************
16
 
17
; The Spectrum ROMs are copyright Amstrad, who have kindly given permission
18
; to reverse engineer and publish ROM disassemblies.
19
 
20
 
21
; =====
22
; NOTES
23
; =====
24
 
25
; ------------
26
; Release Date
27
; ------------
28
; 23rd May 2009
29
 
30
 
31
; =================
32
; ASSEMBLER DEFINES
33
; =================
34
 
35
;TASM directives:
36
 
37
;#define DB .BYTE      
38
;#define DEFW .WORD
39
;#define DEFM .TEXT
40
;#DEFINE DEFS .FILL
41
;#define END  .END
42
;#define EQU  .EQU
43
;#define ORG  .ORG
44
 
45
; The Sinclair Interface1 ROM written by Dr. Ian Logan calls numerous
46
; routines in this ROM. Non-standard entry points have a label beginning
47
; with X. 
48
 
49
        ORG     $0000
50
 
51
;*****************************************
52
;** Part 1. RESTART ROUTINES AND TABLES **
53
;*****************************************
54
 
55
; -----------
56
; THE 'START'
57
; -----------
58
; At switch on, the Z80 chip is in interrupt mode 0.
59
; This location can also be 'called' to reset the machine.
60
; Typically with PRINT USR 0.
61
 
62
;; START
63
L0000:  DI                      ; disable interrupts.
64
        XOR     A               ; signal coming from START.
65
        LD      DE,$FFFF        ; top of possible physical RAM.
66
        JP      L11CB           ; jump forward to common code at START-NEW.
67
 
68
; -------------------
69
; THE 'ERROR' RESTART
70
; -------------------
71
; The error pointer is made to point to the position of the error to enable
72
; the editor to show the error if it occurred during syntax checking.
73
; It is used at 37 places in the program.
74
; An instruction fetch on address $0008 may page in a peripheral ROM
75
; such as the Sinclair Interface 1 or Disciple Disk Interface. 
76
; This was not however an original design concept and not all errors pass
77
; through here.
78
 
79
;; ERROR-1
573 savelij 80
L0008           NOP
384 savelij 81
                JP RST8_CMP
678 savelij 82
                DUPL 0X0010-$,0XFF
384 savelij 83
 
84
; -----------------------------
85
; THE 'PRINT CHARACTER' RESTART
86
; -----------------------------
87
; The A register holds the code of the character that is to be sent to
88
; the output stream of the current channel.
89
; The alternate register set is used to output a character in the A register
90
; so there is no need to preserve any of the current registers (HL,DE,BC).
91
; This restart is used 21 times.
92
 
93
;; PRINT-A
94
L0010:  JP      L15F2           ; jump forward to continue at PRINT-A-2.
95
 
96
; ---
97
 
533 savelij 98
                DUPL ADR_SEL_ROM-$,0XFF
99
L0014           OUT (C),A
384 savelij 100
                NOP
101
                RET
102
 
103
; -------------------------------
104
; THE 'COLLECT CHARACTER' RESTART
105
; -------------------------------
106
; The contents of the location currently addressed by CH_ADD are fetched.
107
; A return is made if the value represents a character that has
108
; relevance to the BASIC parser. Otherwise CH_ADD is incremented and the
109
; tests repeated. CH_ADD will be addressing somewhere -
110
; 1) in the BASIC program area during line execution.
111
; 2) in workspace if evaluating, for example, a string expression.
112
; 3) in the edit buffer if parsing a direct command or a new BASIC line.
113
; 4) in workspace if accepting input but not that from INPUT LINE.
114
 
115
;; GET-CHAR
116
L0018:  LD      HL,($5C5D)      ; fetch the address from CH_ADD.
117
        LD      A,(HL)          ; use it to pick up current character.
118
 
119
;; TEST-CHAR
120
L001C:  CALL    L007D           ; routine SKIP-OVER tests if the character
121
        RET     NC              ; is relevant. Return if it is so.
122
 
123
; ------------------------------------
124
; THE 'COLLECT NEXT CHARACTER' RESTART
125
; ------------------------------------
126
; As the BASIC commands and expressions are interpreted, this routine is
127
; called repeatedly to step along the line. It is used 83 times.
128
 
129
;; NEXT-CHAR
130
L0020:  CALL    L0074           ; routine CH-ADD+1 fetches the next immediate
131
                                ; character.
132
        JR      L001C           ; jump back to TEST-CHAR until a valid
133
                                ; character is found.
134
 
135
; ---
136
 
137
        DB    $FF, $FF, $FF   ; unused
138
 
139
; -----------------------
140
; THE 'CALCULATE' RESTART
141
; -----------------------
142
; This restart enters the Spectrum's internal, floating-point,
143
; stack-based, FORTH-like language.
144
; It is further used recursively from within the calculator.
145
; It is used on 77 occasions.
146
 
147
;; FP-CALC
148
L0028:  JP      L335B           ; jump forward to the CALCULATE routine.
149
 
150
; ---
151
 
152
        DB    $FF, $FF, $FF   ; spare - note that on the ZX81, space being a 
153
        DB    $FF, $FF        ; little cramped, these same locations were
154
                                ; used for the five-byte end-calc literal.
155
 
156
; ------------------------------
157
; THE 'CREATE BC SPACES' RESTART
158
; ------------------------------
159
; This restart is used on only 12 occasions to create BC spaces
160
; between workspace and the calculator stack.
161
 
162
;; BC-SPACES
163
L0030:  PUSH    BC              ; save number of spaces.
164
        LD      HL,($5C61)      ; fetch WORKSP.
165
        PUSH    HL              ; save address of workspace.
166
        JP      L169E           ; jump forward to continuation code RESERVE.
167
 
168
; --------------------------------
169
; THE 'MASKABLE INTERRUPT' ROUTINE
170
; --------------------------------
171
; This routine increments the Spectrum's three-byte FRAMES counter
172
; fifty times a second (sixty times a second in the USA ).
173
; Both this routine and the called KEYBOARD subroutine use 
174
; the IY register to access system variables and flags so a user-written
175
; program must disable interrupts to make use of the IY register.
176
 
177
;; MASK-INT
178
L0038:  PUSH    AF              ; save the registers.
179
        PUSH    HL              ; but not IY unfortunately.
180
        LD      HL,($5C78)      ; fetch two bytes at FRAMES1.
181
        INC     HL              ; increment lowest two bytes of counter.
182
        LD      ($5C78),HL      ; place back in FRAMES1.
183
        LD      A,H             ; test if the result
184
        OR      L               ; was zero.
185
        JR      NZ,L0048        ; forward to KEY-INT if not.
186
 
187
        INC     (IY+$40)        ; otherwise increment FRAMES3 the third byte.
188
 
189
; now save the rest of the main registers and read and decode the keyboard.
190
 
191
;; KEY-INT
192
L0048:  PUSH    BC              ; save the other
193
        PUSH    DE              ; main registers.
194
 
195
                CALL L386E              ; Spectrum 128 patch: read the keypad and keyboard
196
                                        ; in the process of reading a key-press.
197
 
198
L004D:  POP     DE              ;
199
        POP     BC              ; restore registers.
200
 
201
        POP     HL              ;
202
        POP     AF              ;
203
        EI                      ; enable interrupts.
204
        RET                     ; return.
205
 
206
; ---------------------
207
; THE 'ERROR-2' ROUTINE
208
; ---------------------
209
; A continuation of the code at 0008.
210
; The error code is stored and after clearing down stacks,
211
; an indirect jump is made to MAIN-4, etc. to handle the error.
212
 
213
;; ERROR-2
214
L0053:  POP     HL              ; drop the return address - the location
215
                                ; after the RST 08H instruction.
216
        LD      L,(HL)          ; fetch the error code that follows.
217
                                ; (nice to see this instruction used.)
218
 
219
; Note. this entry point is used when out of memory at REPORT-4.
220
; The L register has been loaded with the report code but X-PTR is not
221
; updated.
222
 
223
;; ERROR-3
224
L0055:  LD      (IY+$00),L      ; store it in the system variable ERR_NR.
225
        LD      SP,($5C3D)      ; ERR_SP points to an error handler on the
226
                                ; machine stack. There may be a hierarchy
227
                                ; of routines.
228
                                ; to MAIN-4 initially at base.
229
                                ; or REPORT-G on line entry.
230
                                ; or  ED-ERROR when editing.
231
                                ; or   ED-FULL during ed-enter.
232
                                ; or  IN-VAR-1 during runtime input etc.
233
 
234
        JP      L16C5           ; jump to SET-STK to clear the calculator
235
                                ; stack and reset MEM to usual place in the
236
                                ; systems variables area.
237
                                ; and then indirectly to MAIN-4, etc.
238
 
239
; ---
240
 
573 savelij 241
        DB    $FF, $FF, $FF   ; unused locations
242
        DB    $FF, $FF, $FF   ; before the fixed-position
243
        DB    $FF             ; NMI routine.
384 savelij 244
 
245
; ------------------------------------
246
; THE 'NON-MASKABLE INTERRUPT' ROUTINE
247
; ------------------------------------
248
; There is no NMI switch on the standard Spectrum.
249
; When activated, a location in the system variables is tested
250
; and if the contents are zero a jump made to that location else
251
; a return is made. Perhaps a disabled development feature but
252
; if the logic was reversed, no program would be safe from
253
; copy-protection and the Spectrum would have had no software base.
254
; The location NMIADD was later used by Interface 1 for other purposes.
255
; On later Spectrums, and the Brazilian Spectrum, the logic of this
256
; routine was reversed.
257
 
258
;; RESET
678 savelij 259
L0066           PUSH AF
385 savelij 260
                PUSH HL         ; registers.
261
                LD HL,($5CB0)   ; fetch the system variable NMIADD.
262
                LD A,H          ; test address
263
                OR L            ; for zero.
678 savelij 264
                JR NZ,L0070     ; skip to NO-RESET if NOT ZERO
385 savelij 265
                JP (HL)         ; jump to routine ( i.e. L0000 )
384 savelij 266
 
267
;; NO-RESET
573 savelij 268
L0070           POP HL          ; restore the
269
                POP AF          ; registers.
270
                RETN            ; return to previous interrupt state.
384 savelij 271
 
272
; ---------------------------
273
; THE 'CH ADD + 1' SUBROUTINE
274
; ---------------------------
275
; This subroutine is called from RST 20, and three times from elsewhere
276
; to fetch the next immediate character following the current valid character
277
; address and update the associated system variable.
278
; The entry point TEMP-PTR1 is used from the SCANNING routine.
279
; Both TEMP-PTR1 and TEMP-PTR2 are used by the READ command routine.
280
 
281
;; CH-ADD+1
282
L0074:  LD      HL,($5C5D)      ; fetch address from CH_ADD.
283
 
284
;; TEMP-PTR1
285
L0077:  INC     HL              ; increase the character address by one.
286
 
287
;; TEMP-PTR2
288
L0078:  LD      ($5C5D),HL      ; update CH_ADD with character address.
289
 
290
X007B:  LD      A,(HL)          ; load character to A from HL.
291
        RET                     ; and return.
292
 
293
; --------------------------
294
; THE 'SKIP OVER' SUBROUTINE
295
; --------------------------
296
; This subroutine is called once from RST 18 to skip over white-space and
297
; other characters irrelevant to the parsing of a BASIC line etc. .
298
; Initially the A register holds the character to be considered
299
; and HL holds its address which will not be within quoted text
300
; when a BASIC line is parsed.
301
; Although the 'tab' and 'at' characters will not appear in a BASIC line,
302
; they could be present in a string expression, and in other situations.
303
; Note. although white-space is usually placed in a program to indent loops
304
; and make it more readable, it can also be used for the opposite effect and
305
; spaces may appear in variable names although the parser never sees them.
306
; It is this routine that helps make the variables 'Anum bEr5 3BUS' and
307
; 'a number 53 bus' appear the same to the parser.
308
 
309
;; SKIP-OVER
310
L007D:  CP      $21             ; test if higher than space.
311
        RET     NC              ; return with carry clear if so.
312
 
313
        CP      $0D             ; carriage return ?
314
        RET     Z               ; return also with carry clear if so.
315
 
316
                                ; all other characters have no relevance
317
                                ; to the parser and must be returned with
318
                                ; carry set.
319
 
320
        CP      $10             ; test if 0-15d
321
        RET     C               ; return, if so, with carry set.
322
 
323
        CP      $18             ; test if 24-32d
324
        CCF                     ; complement carry flag.
325
        RET     C               ; return with carry set if so.
326
 
327
                                ; now leaves 16d-23d
328
 
329
        INC     HL              ; all above have at least one extra character
330
                                ; to be stepped over.
331
 
332
        CP      $16             ; controls 22d ('at') and 23d ('tab') have two.
333
        JR      C,L0090         ; forward to SKIPS with ink, paper, flash,
334
                                ; bright, inverse or over controls.
335
                                ; Note. the high byte of tab is for RS232 only.
336
                                ; it has no relevance on this machine.
337
 
338
        INC     HL              ; step over the second character of 'at'/'tab'.
339
 
340
;; SKIPS
341
L0090:  SCF                     ; set the carry flag
342
        LD      ($5C5D),HL      ; update the CH_ADD system variable.
343
        RET                     ; return with carry set.
344
 
345
 
346
; ------------------
347
; THE 'TOKEN TABLES'
348
; ------------------
349
; The tokenized characters 134d (RND) to 255d (COPY) are expanded using
350
; this table. The last byte of a token is inverted to denote the end of
351
; the word. The first is an inverted step-over byte.
352
 
353
;; TKN-TABLE
354
L0095           DC "?"          ;DB    '?'+$80
355
                DC "RND"        ;DEFM    "RN"
356
                                ;DB    'D'+$80
357
                DC "INKEY$"     ;DEFM    "INKEY"
358
                                ;DB    '$'+$80
359
                DC "PI"         ;DB    'P','I'+$80
360
                DC "FN"         ;DB    'F','N'+$80
361
                DC "POINT"      ;DEFM    "POIN"
362
                                ;DB    'T'+$80
363
                DC "SCREEN$"    ;DEFM    "SCREEN"
364
                                ;DB    '$'+$80
365
                DC "ATTR"       ;DEFM    "ATT"
366
                                ;DB    'R'+$80
367
                DC "AT"         ;DB    'A','T'+$80
368
                DC "TAB"        ;DEFM    "TA"
369
                                ;DB    'B'+$80
370
                DC "VAL$"       ;DEFM    "VAL"
371
                                ;DB    '$'+$80
372
                DC "CODE"       ;DEFM    "COD"
373
                                ;DB    'E'+$80
374
                DC "VAL"        ;DEFM    "VA"
375
                                ;DB    'L'+$80
376
                DC "LEN"        ;DEFM    "LE"
377
                                ;DB    'N'+$80
378
                DC "SIN"        ;DEFM    "SI"
379
                                ;DB    'N'+$80
380
                DC "COS"        ;DEFM    "CO"
381
                                ;DB    'S'+$80
382
                DC "TAN"        ;DEFM    "TA"
383
                                ;DB    'N'+$80
384
                DC "ASN"        ;DEFM    "AS"
385
                                ;DB    'N'+$80
386
                DC "ACS"        ;DEFM    "AC"
387
                                ;DB    'S'+$80
388
                DC "ATN"        ;DEFM    "AT"
389
                                ;DB    'N'+$80
390
                DC "LN"         ;DB    'L','N'+$80
391
                DC "EXP"        ;DEFM    "EX"
392
                                ;DB    'P'+$80
393
                DC "INT"        ;DEFM    "IN"
394
                                ;DB    'T'+$80
395
                DC "SQR"        ;DEFM    "SQ"
396
                                ;DB    'R'+$80
397
                DC "SGN"        ;DEFM    "SG"
398
                                ;DB    'N'+$80
399
                DC "ABS"        ;DEFM    "AB"
400
                                ;DB    'S'+$80
401
                DC "PEEK"       ;DEFM    "PEE"
402
                                ;DB    'K'+$80
403
                DC "IN"         ;DB    'I','N'+$80
404
                DC "USR"        ;DEFM    "US"
405
                                ;DB    'R'+$80
406
                DC "STR$"       ;DEFM    "STR"
407
                                ;DB    '$'+$80
408
                DC "CHR$"       ;DEFM    "CHR"
409
                                ;DB    '$'+$80
410
                DC "NOT"        ;DEFM    "NO"
411
                                ;DB    'T'+$80
412
                DC "BIN"        ;DEFM    "BI"
413
                                ;DB    'N'+$80
414
 
415
;   The previous 32 function-type words are printed without a leading space
416
;   The following have a leading space if they begin with a letter
417
 
418
                DC "OR"         ;DB    'O','R'+$80
419
                DC "AND"        ;DEFM    "AN"
420
                                ;DB    'D'+$80
421
                DC "<="         ;DB    $3C,'='+$80             ; <=
422
                DC ">="         ;DB    $3E,'='+$80             ; >=
423
                DC "<>"         ;DB    $3C,$3E+$80             ; <>
424
                DC "LINE"       ;DEFM    "LIN"
425
                                ;DB    'E'+$80
426
                DC "THEN"       ;DEFM    "THE"
427
                                ;DB    'N'+$80
428
                DC "TO"         ;DB    'T','O'+$80
429
                DC "STEP"       ;DEFM    "STE"
430
                                ;DB    'P'+$80
431
                DC "DEF FN"     ;DEFM    "DEF F"
432
                                ;DB    'N'+$80
433
                DC "CAT"        ;DEFM    "CA"
434
                                ;DB    'T'+$80
435
                DC "FORMAT"     ;DEFM    "FORMA"
436
                                ;DB    'T'+$80
437
                DC "MOVE"       ;DEFM    "MOV"
438
                                ;DB    'E'+$80
439
                DC "ERASE"      ;DEFM    "ERAS"
440
                                ;DB    'E'+$80
441
                DC "OPEN #"     ;DEFM    "OPEN "
442
                                ;DB    '#'+$80
443
                DC "CLOSE #"    ;DEFM    "CLOSE "
444
                                ;DB    '#'+$80
445
                DC "MERGE"      ;DEFM    "MERG"
446
                                ;DB    'E'+$80
447
                DC "VERIFY"     ;DEFM    "VERIF"
448
                                ;DB    'Y'+$80
449
                DC "BEEP"       ;DEFM    "BEE"
450
                                ;DB    'P'+$80
451
                DC "CIRCLE"     ;DEFM    "CIRCL"
452
                                ;DB    'E'+$80
453
                DC "INK"        ;DEFM    "IN"
454
                                ;DB    'K'+$80
455
                DC "PAPER"      ;DEFM    "PAPE"
456
                                ;DB    'R'+$80
457
                DC "FLASH"      ;DEFM    "FLAS"
458
                                ;DB    'H'+$80
459
                DC "BRIGHT"     ;DEFM    "BRIGH"
460
                                ;DB    'T'+$80
461
                DC "INVERSE"    ;DEFM    "INVERS"
462
                                ;DB    'E'+$80
463
                DC "OVER"       ;DEFM    "OVE"
464
                                ;DB    'R'+$80
465
                DC "OUT"        ;DEFM    "OU"
466
                                ;DB    'T'+$80
467
                DC "LPRINT"     ;DEFM    "LPRIN"
468
                                ;DB    'T'+$80
469
                DC "LLIST"      ;DEFM    "LLIS"
470
                                ;DB    'T'+$80
471
                DC "STOP"       ;DEFM    "STO"
472
                                ;DB    'P'+$80
473
                DC "READ"       ;DEFM    "REA"
474
                                ;DB    'D'+$80
475
                DC "DATA"       ;DEFM    "DAT"
476
                                ;DB    'A'+$80
477
                DC "RESTORE"    ;DEFM    "RESTOR"
478
                                ;DB    'E'+$80
479
                DC "NEW"        ;DEFM    "NE"
480
                                ;DB    'W'+$80
481
                DC "BORDER"     ;DEFM    "BORDE"
482
                                ;DB    'R'+$80
483
                DC "CONTINUE"   ;DEFM    "CONTINU"
484
                                ;DB    'E'+$80
485
                DC "DIM"        ;DEFM    "DI"
486
                                ;DB    'M'+$80
487
                DC "REM"        ;DEFM    "RE"
488
                                ;DB    'M'+$80
489
                DC "FOR"        ;DEFM    "FO"
490
                                ;DB    'R'+$80
491
                DC "GO TO"      ;DEFM    "GO T"
492
                                ;DB    'O'+$80
493
                DC "GO SUB"     ;DEFM    "GO SU"
494
                                ;DB    'B'+$80
495
                DC "INPUT"      ;DEFM    "INPU"
496
                                ;DB    'T'+$80
497
                DC "LOAD"       ;DEFM    "LOA"
498
                                ;DB    'D'+$80
499
                DC "LIST"       ;DEFM    "LIS"
500
                                ;DB    'T'+$80
501
                DC "LET"        ;DEFM    "LE"
502
                                ;DB    'T'+$80
503
                DC "PAUSE"      ;DEFM    "PAUS"
504
                                ;DB    'E'+$80
505
                DC "NEXT"       ;DEFM    "NEX"
506
                                ;DB    'T'+$80
507
                DC "POKE"       ;DEFM    "POK"
508
                                ;DB    'E'+$80
509
                DC "PRINT"      ;DEFM    "PRIN"
510
                                ;DB    'T'+$80
511
                DC "PLOT"       ;DEFM    "PLO"
512
                                ;DB    'T'+$80
513
                DC "RUN"        ;DEFM    "RU"
514
                                ;DB    'N'+$80
515
                DC "SAVE"       ;DEFM    "SAV"
516
                                ;DB    'E'+$80
517
                DC "RANDOMIZE"  ;DEFM    "RANDOMIZ"
518
                                ;DB    'E'+$80
519
                DC "IF"         ;DB    'I','F'+$80
520
                DC "CLS"        ;DEFM    "CL"
521
                                ;DB    'S'+$80
522
                DC "DRAW"       ;DEFM    "DRA"
523
                                ;DB    'W'+$80
524
                DC "CLEAR"      ;DEFM    "CLEA"
525
                                ;DB    'R'+$80
526
                DC "RETURN"     ;DEFM    "RETUR"
527
                                ;DB    'N'+$80
528
                DC "COPY"       ;DEFM    "COP"
529
                                ;DB    'Y'+$80
530
 
531
; ----------------
532
; THE 'KEY' TABLES
533
; ----------------
534
; These six look-up tables are used by the keyboard reading routine
535
; to decode the key values.
536
 
537
; The first table contains the maps for the 39 keys of the standard
538
; 40-key Spectrum keyboard. The remaining key [SHIFT $27] is read directly.
539
; The keys consist of the 26 upper-case alphabetic characters, the 10 digit
540
; keys and the space, ENTER and symbol shift key.
541
; Unshifted alphabetic keys have $20 added to the value.
542
; The keywords for the main alphabetic keys are obtained by adding $A5 to
543
; the values obtained from this table.
544
 
545
;; MAIN-KEYS
546
L0205:  DB    $42             ; B
547
        DB    $48             ; H
548
        DB    $59             ; Y
549
        DB    $36             ; 6
550
        DB    $35             ; 5
551
        DB    $54             ; T
552
        DB    $47             ; G
553
        DB    $56             ; V
554
        DB    $4E             ; N
555
        DB    $4A             ; J
556
        DB    $55             ; U
557
        DB    $37             ; 7
558
        DB    $34             ; 4
559
        DB    $52             ; R
560
        DB    $46             ; F
561
        DB    $43             ; C
562
        DB    $4D             ; M
563
        DB    $4B             ; K
564
        DB    $49             ; I
565
        DB    $38             ; 8
566
        DB    $33             ; 3
567
        DB    $45             ; E
568
        DB    $44             ; D
569
        DB    $58             ; X
570
        DB    $0E             ; SYMBOL SHIFT
571
        DB    $4C             ; L
572
        DB    $4F             ; O
573
        DB    $39             ; 9
574
        DB    $32             ; 2
575
        DB    $57             ; W
576
        DB    $53             ; S
577
        DB    $5A             ; Z
578
        DB    $20             ; SPACE
579
        DB    $0D             ; ENTER
580
        DB    $50             ; P
581
        DB    $30             ; 0
582
        DB    $31             ; 1
583
        DB    $51             ; Q
584
        DB    $41             ; A
585
 
586
 
587
;; E-UNSHIFT
588
;  The 26 unshifted extended mode keys for the alphabetic characters.
589
;  The green keywords on the original keyboard.
590
L022C:  DB    $E3             ; READ
591
        DB    $C4             ; BIN
592
        DB    $E0             ; LPRINT
593
        DB    $E4             ; DATA
594
        DB    $B4             ; TAN
595
        DB    $BC             ; SGN
596
        DB    $BD             ; ABS
597
        DB    $BB             ; SQR
598
        DB    $AF             ; CODE
599
        DB    $B0             ; VAL
600
        DB    $B1             ; LEN
601
        DB    $C0             ; USR
602
        DB    $A7             ; PI
603
        DB    $A6             ; INKEY$
604
        DB    $BE             ; PEEK
605
        DB    $AD             ; TAB
606
        DB    $B2             ; SIN
607
        DB    $BA             ; INT
608
        DB    $E5             ; RESTORE
609
        DB    $A5             ; RND
610
        DB    $C2             ; CHR$
611
        DB    $E1             ; LLIST
612
        DB    $B3             ; COS
613
        DB    $B9             ; EXP
614
        DB    $C1             ; STR$
615
        DB    $B8             ; LN
616
 
617
 
618
;; EXT-SHIFT
619
;  The 26 shifted extended mode keys for the alphabetic characters.
620
;  The red keywords below keys on the original keyboard.
621
L0246:  DB    $7E             ; ~
622
        DB    $DC             ; BRIGHT
623
        DB    $DA             ; PAPER
624
        DB    $5C             ;
625
        DB    $B7             ; ATN
626
        DB    $7B             ; {
627
        DB    $7D             ; }
628
        DB    $D8             ; CIRCLE
629
        DB    $BF             ; IN
630
        DB    $AE             ; VAL$
631
        DB    $AA             ; SCREEN$
632
        DB    $AB             ; ATTR
633
        DB    $DD             ; INVERSE
634
        DB    $DE             ; OVER
635
        DB    $DF             ; OUT
636
        DB    $7F             ; (Copyright character)
637
        DB    $B5             ; ASN
638
        DB    $D6             ; VERIFY
639
        DB    $7C             ; |
640
        DB    $D5             ; MERGE
641
        DB    $5D             ; ]
642
        DB    $DB             ; FLASH
643
        DB    $B6             ; ACS
644
        DB    $D9             ; INK
645
        DB    $5B             ; [
646
        DB    $D7             ; BEEP
647
 
648
 
649
;; CTL-CODES
650
;  The ten control codes assigned to the top line of digits when the shift 
651
;  key is pressed.
652
L0260:  DB    $0C             ; DELETE
653
        DB    $07             ; EDIT
654
        DB    $06             ; CAPS LOCK
655
        DB    $04             ; TRUE VIDEO
656
        DB    $05             ; INVERSE VIDEO
657
        DB    $08             ; CURSOR LEFT
658
        DB    $0A             ; CURSOR DOWN
659
        DB    $0B             ; CURSOR UP
660
        DB    $09             ; CURSOR RIGHT
661
        DB    $0F             ; GRAPHICS
662
 
663
 
664
;; SYM-CODES
665
;  The 26 red symbols assigned to the alphabetic characters of the keyboard.
666
;  The ten single-character digit symbols are converted without the aid of
667
;  a table using subtraction and minor manipulation. 
668
L026A:  DB    $E2             ; STOP
669
        DB    $2A             ; *
670
        DB    $3F             ; ?
671
        DB    $CD             ; STEP
672
        DB    $C8             ; >=
673
        DB    $CC             ; TO
674
        DB    $CB             ; THEN
675
        DB    $5E             ; ^
676
        DB    $AC             ; AT
677
        DB    $2D             ; -
678
        DB    $2B             ; +
679
        DB    $3D             ; =
680
        DB    $2E             ; .
681
        DB    $2C             ; ,
682
        DB    $3B             ; ;
683
        DB    $22             ; "
684
        DB    $C7             ; <=
685
        DB    $3C             ; <
686
        DB    $C3             ; NOT
687
        DB    $3E             ; >
688
        DB    $C5             ; OR
689
        DB    $2F             ; /
690
        DB    $C9             ; <>
691
        DB    $60             ; pound
692
        DB    $C6             ; AND
693
        DB    $3A             ; :
694
 
695
;; E-DIGITS
696
;  The ten keywords assigned to the digits in extended mode.
697
;  The remaining red keywords below the keys.
698
L0284:  DB    $D0             ; FORMAT
699
        DB    $CE             ; DEF FN
700
        DB    $A8             ; FN
701
        DB    $CA             ; LINE
702
        DB    $D3             ; OPEN#
703
        DB    $D4             ; CLOSE#
704
        DB    $D1             ; MOVE
705
        DB    $D2             ; ERASE
706
        DB    $A9             ; POINT
707
        DB    $CF             ; CAT
708
 
709
 
710
;*******************************
711
;** Part 2. KEYBOARD ROUTINES **
712
;*******************************
713
 
714
; Using shift keys and a combination of modes the Spectrum 40-key keyboard
715
; can be mapped to 256 input characters
716
 
717
; ---------------------------------------------------------------------------
718
;
719
;         0     1     2     3     4 -Bits-  4     3     2     1     0
720
; PORT                                                                    PORT
721
;
722
; F7FE  [ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ]  |  [ 6 ] [ 7 ] [ 8 ] [ 9 ] [ 0 ]   EFFE
723
;  ^                                   |                                   v
724
; FBFE  [ Q ] [ W ] [ E ] [ R ] [ T ]  |  [ Y ] [ U ] [ I ] [ O ] [ P ]   DFFE
725
;  ^                                   |                                   v
726
; FDFE  [ A ] [ S ] [ D ] [ F ] [ G ]  |  [ H ] [ J ] [ K ] [ L ] [ ENT ] BFFE
727
;  ^                                   |                                   v
728
; FEFE  [SHI] [ Z ] [ X ] [ C ] [ V ]  |  [ B ] [ N ] [ M ] [sym] [ SPC ] 7FFE
729
;  ^     $27                                                 $18           v
730
; Start                                                                   End
731
;        00100111                                            00011000
732
;
733
; ---------------------------------------------------------------------------
734
; The above map may help in reading.
735
; The neat arrangement of ports means that the B register need only be
736
; rotated left to work up the left hand side and then down the right
737
; hand side of the keyboard. When the reset bit drops into the carry
738
; then all 8 half-rows have been read. Shift is the first key to be
739
; read. The lower six bits of the shifts are unambiguous.
740
 
741
; -------------------------------
742
; THE 'KEYBOARD SCANNING' ROUTINE
743
; -------------------------------
744
; from keyboard and s-inkey$
745
; returns 1 or 2 keys in DE, most significant shift first if any
746
; key values 0-39 else 255
747
 
748
;; KEY-SCAN
749
L028E:  LD      L,$2F           ; initial key value
750
                                ; valid values are obtained by subtracting
751
                                ; eight five times.
752
        LD      DE,$FFFF        ; a buffer to receive 2 keys.
753
 
754
        LD      BC,$FEFE        ; the commencing port address
755
                                ; B holds 11111110 initially and is also
756
                                ; used to count the 8 half-rows
757
;; KEY-LINE
758
L0296:  IN      A,(C)           ; read the port to A - bits will be reset
759
                                ; if a key is pressed else set.
760
        CPL                     ; complement - pressed key-bits are now set
761
        AND     $1F             ; apply 00011111 mask to pick up the
762
                                ; relevant set bits.
763
 
764
        JR      Z,L02AB         ; forward to KEY-DONE if zero and therefore
765
                                ; no keys pressed in row at all.
766
 
767
        LD      H,A             ; transfer row bits to H
768
        LD      A,L             ; load the initial key value to A
769
 
770
;; KEY-3KEYS
771
L029F:  INC     D               ; now test the key buffer
772
        RET     NZ              ; if we have collected 2 keys already
773
                                ; then too many so quit.
774
 
775
;; KEY-BITS
776
L02A1:  SUB     $08             ; subtract 8 from the key value
777
                                ; cycling through key values (top = $27)
778
                                ; e.g. 2F>   27>1F>17>0F>07
779
                                ;      2E>   26>1E>16>0E>06
780
        SRL     H               ; shift key bits right into carry.
781
        JR      NC,L02A1        ; back to KEY-BITS if not pressed
782
                                ; but if pressed we have a value (0-39d)
783
 
784
        LD      D,E             ; transfer a possible previous key to D
785
        LD      E,A             ; transfer the new key to E
786
        JR      NZ,L029F        ; back to KEY-3KEYS if there were more
787
                                ; set bits - H was not yet zero.
788
 
789
;; KEY-DONE
790
L02AB:  DEC     L               ; cycles 2F>2E>2D>2C>2B>2A>29>28 for
791
                                ; each half-row.
792
        RLC     B               ; form next port address e.g. FEFE > FDFE
793
        JR      C,L0296         ; back to KEY-LINE if still more rows to do.
794
 
795
        LD      A,D             ; now test if D is still FF ?
796
        INC     A               ; if it is zero we have at most 1 key
797
                                ; range now $01-$28  (1-40d)
798
        RET     Z               ; return if one key or no key.
799
 
800
        CP      $28             ; is it capsshift (was $27) ?
801
        RET     Z               ; return if so.
802
 
803
        CP      $19             ; is it symbol shift (was $18) ?
804
        RET     Z               ; return also
805
 
806
        LD      A,E             ; now test E
807
        LD      E,D             ; but first switch
808
        LD      D,A             ; the two keys.
809
        CP      $18             ; is it symbol shift ?
810
        RET                     ; return (with zero set if it was).
811
                                ; but with symbol shift now in D
812
 
813
; ------------------------------
814
; Scan keyboard and decode value
815
; ------------------------------
816
; from interrupt 50 times a second
817
;
818
 
819
;; KEYBOARD
820
L02BF:  CALL    L028E           ; routine KEY-SCAN
821
        RET     NZ              ; return if invalid combinations
822
 
823
; then decrease the counters within the two key-state maps
824
; as this could cause one to become free.
825
; if the keyboard has not been pressed during the last five interrupts
826
; then both sets will be free.
827
 
828
 
829
        LD      HL,$5C00        ; point to KSTATE-0
830
 
831
;; K-ST-LOOP
832
L02C6:  BIT     7,(HL)          ; is it free ?  ($FF)
833
        JR      NZ,L02D1        ; forward to K-CH-SET if so
834
 
835
        INC     HL              ; address 5-counter
836
        DEC     (HL)            ; decrease counter
837
        DEC     HL              ; step back
838
        JR      NZ,L02D1        ; forward to K-CH-SET if not at end of count
839
 
840
        LD      (HL),$FF        ; else mark it free.
841
 
842
;; K-CH-SET
843
L02D1:  LD      A,L             ; store low address byte.
844
        LD      HL,$5C04        ; point to KSTATE-4
845
                                ; (ld l, $04)
846
        CP      L               ; have 2 been done ?
847
        JR      NZ,L02C6        ; back to K-ST-LOOP to consider this 2nd set
848
 
849
; now the raw key (0-38) is converted to a main key (uppercase).
850
 
851
        CALL    L031E           ; routine K-TEST to get main key in A
852
        RET     NC              ; return if single shift
853
 
854
        LD      HL,$5C00        ; point to KSTATE-0
855
        CP      (HL)            ; does it match ?
856
        JR      Z,L0310         ; forward to K-REPEAT if so
857
 
858
; if not consider the second key map.
859
 
860
        EX      DE,HL           ; save kstate-0 in de
861
        LD      HL,$5C04        ; point to KSTATE-4
862
        CP      (HL)            ; does it match ?
863
        JR      Z,L0310         ; forward to K-REPEAT if so
864
 
865
; having excluded a repeating key we can now consider a new key.
866
; the second set is always examined before the first.
867
 
868
        BIT     7,(HL)          ; is it free ?
869
        JR      NZ,L02F1        ; forward to K-NEW if so.
870
 
871
        EX      DE,HL           ; bring back kstate-0
872
        BIT     7,(HL)          ; is it free ?
873
        RET     Z               ; return if not.
874
                                ; as we have a key but nowhere to put it yet.
875
 
876
; continue or jump to here if one of the buffers was free.
877
 
878
;; K-NEW
879
L02F1:  LD      E,A             ; store key in E
880
        LD      (HL),A          ; place in free location
881
        INC     HL              ; advance to interrupt counter
882
        LD      (HL),$05        ; and initialize to 5
883
        INC     HL              ; advance to delay
884
        LD      A,($5C09)       ; pick up system variable REPDEL
885
        LD      (HL),A          ; and insert that for first repeat delay.
886
        INC     HL              ; advance to last location of state map.
887
 
888
        LD      C,(IY+$07)      ; pick up MODE  (3 bytes)
889
        LD      D,(IY+$01)      ; pick up FLAGS (3 bytes)
890
        PUSH    HL              ; save state map location
891
                                ; Note. could now have used.
892
                                ; ld l,$41; ld c,(hl); ld l,$3B; ld d,(hl).
893
                                ; six and two threes of course.
894
        CALL    L0333           ; routine K-DECODE
895
        POP     HL              ; restore map pointer
896
        LD      (HL),A          ; put decoded key in last location of map.
897
 
898
;; K-END
899
L0308:  LD      ($5C08),A       ; update LASTK system variable.
900
        SET     5,(IY+$01)      ; update FLAGS  - signal new key.
901
        RET                     ; done
902
 
903
; ---------------------------
904
; THE 'REPEAT KEY' SUBROUTINE
905
; ---------------------------
906
; A possible repeat has been identified. HL addresses the raw (main) key.
907
; The last location holds the decoded key (from the first context).
908
 
909
;; K-REPEAT
910
L0310:  INC     HL              ; advance
911
        LD      (HL),$05        ; maintain interrupt counter at 5
912
        INC     HL              ; advance
913
        DEC     (HL)            ; decrease REPDEL value.
914
        RET     NZ              ; return if not yet zero.
915
 
916
        LD      A,($5C0A)       ; REPPER
917
        LD      (HL),A          ; but for subsequent repeats REPPER will be used.
918
        INC     HL              ; advance
919
                                ;
920
        LD      A,(HL)          ; pick up the key decoded possibly in another
921
                                ; context.
922
        JR      L0308           ; back to K-END
923
 
924
; --------------
925
; Test key value
926
; --------------
927
; also called from s-inkey$
928
; begin by testing for a shift with no other.
929
 
930
;; K-TEST
931
L031E:  LD      B,D             ; load most significant key to B
932
                                ; will be $FF if not shift.
933
        LD      D,$00           ; and reset D to index into main table
934
        LD      A,E             ; load least significant key from E
935
        CP      $27             ; is it higher than 39d   i.e. FF
936
        RET     NC              ; return with just a shift (in B now)
937
 
938
        CP      $18             ; is it symbol shift ?
939
        JR      NZ,L032C        ; forward to K-MAIN if not
940
 
941
; but we could have just symbol shift and no other
942
 
943
        BIT     7,B             ; is other key $FF (ie not shift)
944
        RET     NZ              ; return with solitary symbol shift
945
 
946
 
947
;; K-MAIN
948
L032C:  LD      HL,L0205        ; address: MAIN-KEYS
949
        ADD     HL,DE           ; add offset 0-38
950
        LD      A,(HL)          ; pick up main key value
951
        SCF                     ; set carry flag
952
        RET                     ; return    (B has other key still)
953
 
954
; -----------------
955
; Keyboard decoding
956
; -----------------
957
; also called from s-inkey$
958
 
959
;; K-DECODE
960
L0333:  LD      A,E             ; pick up the stored main key
961
        CP      $3A             ; an arbitrary point between digits and letters
962
        JR      C,L0367         ; forward to K-DIGIT with digits, space, enter.
963
 
964
        DEC     C               ; decrease MODE ( 0='KLC', 1='E', 2='G')
965
 
966
        JP      M,L034F         ; to K-KLC-LET if was zero
967
 
968
        JR      Z,L0341         ; to K-E-LET if was 1 for extended letters.
969
 
970
; proceed with graphic codes.
971
; Note. should selectively drop return address if code > 'U' ($55).
972
; i.e. abort the KEYBOARD call.
973
; e.g. cp 'V'; jr c addit; pop af; ;;addit etc. (5 bytes of instruction).
974
; (s-inkey$ never gets into graphics mode.)
975
 
976
;; addit
977
        ADD     A,$4F           ; add offset to augment 'A' to graphics A say.
978
        RET                     ; return.
979
                                ; Note. ( but [GRAPH] V gives RND, etc ).
980
 
981
; ---
982
 
983
; the jump was to here with extended mode with uppercase A-Z.
984
 
985
;; K-E-LET
986
L0341:  LD      HL,L022C-$41    ; base address of E-UNSHIFT L022c
987
                                ; ( $01EB in standard ROM ) 
988
        INC     B               ; test B is it empty i.e. not a shift
989
        JR      Z,L034A         ; forward to K-LOOK-UP if neither shift
990
 
991
        LD      HL,L0246-$41    ; Address: $0205 L0246-$41 EXT-SHIFT base
992
 
993
;; K-LOOK-UP
994
L034A:  LD      D,$00           ; prepare to index
995
        ADD     HL,DE           ; add the main key value
996
        LD      A,(HL)          ; pick up other mode value
997
        RET                     ; return
998
 
999
; ---
1000
 
1001
; the jump was here with mode = 0
1002
 
1003
;; K-KLC-LET
1004
L034F:  LD      HL,L026A-$41    ; prepare base of sym-codes
1005
        BIT     0,B             ; shift=$27 sym-shift=$18
1006
        JR      Z,L034A         ; back to K-LOOK-UP with symbol-shift
1007
 
1008
        BIT     3,D             ; test FLAGS is it 'K' mode (from OUT-CURS)
1009
        JR      Z,L0364         ; skip to K-TOKENS if so
1010
 
1011
        BIT     3,(IY+$30)      ; test FLAGS2 - consider CAPS LOCK ?
1012
        RET     NZ              ; return if so with main code.
1013
 
1014
        INC     B               ; is shift being pressed ?
1015
                                ; result zero if not
1016
        RET     NZ              ; return if shift pressed.
1017
 
1018
        ADD     A,$20           ; else convert the code to lower case.
1019
        RET                     ; return.
1020
 
1021
; ---
1022
 
1023
; the jump was here for tokens
1024
 
1025
;; K-TOKENS
1026
L0364:  ADD     A,$A5           ; add offset to main code so that 'A'
1027
                                ; becomes 'NEW' etc.
1028
        RET                     ; return
1029
 
1030
; ---
1031
 
1032
; the jump was here with digits, space, enter and symbol shift (< $xx)
1033
 
1034
;; K-DIGIT
1035
L0367:  CP      $30             ; is it '0' or higher ?
1036
        RET     C               ; return with space, enter and symbol-shift
1037
 
1038
        DEC     C               ; test MODE (was 0='KLC', 1='E', 2='G')
1039
        JP      M,L039D         ; jump to K-KLC-DGT if was 0.
1040
 
1041
        JR      NZ,L0389        ; forward to K-GRA-DGT if mode was 2.
1042
 
1043
; continue with extended digits 0-9.
1044
 
1045
        LD      HL,L0284-$30    ; $0254 - base of E-DIGITS
1046
        BIT     5,B             ; test - shift=$27 sym-shift=$18
1047
        JR      Z,L034A         ; to K-LOOK-UP if sym-shift
1048
 
1049
        CP      $38             ; is character '8' ?
1050
        JR      NC,L0382        ; to K-8-&-9 if greater than '7'
1051
 
1052
        SUB     $20             ; reduce to ink range $10-$17
1053
        INC     B               ; shift ?
1054
        RET     Z               ; return if not.
1055
 
1056
        ADD     A,$08           ; add 8 to give paper range $18 - $1F
1057
        RET                     ; return
1058
 
1059
; ---
1060
 
1061
; 89
1062
 
1063
;; K-8-&-9
1064
L0382:  SUB     $36             ; reduce to 02 and 03  bright codes
1065
        INC     B               ; test if shift pressed.
1066
        RET     Z               ; return if not.
1067
 
1068
        ADD     A,$FE           ; subtract 2 setting carry
1069
        RET                     ; to give 0 and 1    flash codes.
1070
 
1071
; ---
1072
 
1073
;  graphics mode with digits
1074
 
1075
;; K-GRA-DGT
1076
L0389:  LD      HL,L0260-$30    ; $0230 base address of CTL-CODES
1077
 
1078
        CP      $39             ; is key '9' ?
1079
        JR      Z,L034A         ; back to K-LOOK-UP - changed to $0F, GRAPHICS.
1080
 
1081
        CP      $30             ; is key '0' ?
1082
        JR      Z,L034A         ; back to K-LOOK-UP - changed to $0C, delete.
1083
 
1084
; for keys '0' - '7' we assign a mosaic character depending on shift.
1085
 
1086
        AND     $07             ; convert character to number. 0 - 7.
1087
        ADD     A,$80           ; add offset - they start at $80
1088
 
1089
        INC     B               ; destructively test for shift
1090
        RET     Z               ; and return if not pressed.
1091
 
1092
        XOR     $0F             ; toggle bits becomes range $88-$8F
1093
        RET                     ; return.
1094
 
1095
; ---
1096
 
1097
; now digits in 'KLC' mode
1098
 
1099
;; K-KLC-DGT
1100
L039D:  INC     B               ; return with digit codes if neither
1101
        RET     Z               ; shift key pressed.
1102
 
1103
        BIT     5,B             ; test for caps shift.
1104
 
1105
        LD      HL,L0260-$30    ; prepare base of table CTL-CODES.
1106
        JR      NZ,L034A        ; back to K-LOOK-UP if shift pressed.
1107
 
1108
; must have been symbol shift
1109
 
1110
        SUB     $10             ; for ASCII most will now be correct
1111
                                ; on a standard typewriter.
1112
        CP      $22             ; but '@' is not - see below.
1113
        JR      Z,L03B2         ; forward to to K-@-CHAR if so
1114
 
1115
        CP      $20             ; '_' is the other one that fails
1116
        RET     NZ              ; return if not.
1117
 
1118
        LD      A,$5F           ; substitute ASCII '_'
1119
        RET                     ; return.
1120
 
1121
; ---
1122
 
1123
;; K-@-CHAR
1124
L03B2:  LD      A,$40           ; substitute ASCII '@'
1125
        RET                     ; return.
1126
 
1127
 
1128
; ------------------------------------------------------------------------
1129
; The Spectrum Input character keys. One or two are abbreviated.
1130
; From $00 Flash 0 to $FF COPY. The routine above has decoded all these.
1131
 
1132
;  | 00 Fl0| 01 Fl1| 02 Br0| 03 Br1| 04 In0| 05 In1| 06 CAP| 07 EDT|
1133
;  | 08 LFT| 09 RIG| 0A DWN| 0B UP | 0C DEL| 0D ENT| 0E SYM| 0F GRA|
1134
;  | 10 Ik0| 11 Ik1| 12 Ik2| 13 Ik3| 14 Ik4| 15 Ik5| 16 Ik6| 17 Ik7|
1135
;  | 18 Pa0| 19 Pa1| 1A Pa2| 1B Pa3| 1C Pa4| 1D Pa5| 1E Pa6| 1F Pa7|
1136
;  | 20 SP | 21  ! | 22  " | 23  # | 24  $ | 25  % | 26  & | 27  ' |
1137
;  | 28  ( | 29  ) | 2A  * | 2B  + | 2C  , | 2D  - | 2E  . | 2F  / |
1138
;  | 30  0 | 31  1 | 32  2 | 33  3 | 34  4 | 35  5 | 36  6 | 37  7 |
1139
;  | 38  8 | 39  9 | 3A  : | 3B  ; | 3C  < | 3D  = | 3E  > | 3F  ? |
1140
;  | 40  @ | 41  A | 42  B | 43  C | 44  D | 45  E | 46  F | 47  G |
1141
;  | 48  H | 49  I | 4A  J | 4B  K | 4C  L | 4D  M | 4E  N | 4F  O |
1142
;  | 50  P | 51  Q | 52  R | 53  S | 54  T | 55  U | 56  V | 57  W |
1143
;  | 58  X | 59  Y | 5A  Z | 5B  [ | 5C  \ | 5D  ] | 5E  ^ | 5F  _ |
1144
;  | 60 ukp| 61  a | 62  b | 63  c | 64  d | 65  e | 66  f | 67  g |
1145
;  | 68  h | 69  i | 6A  j | 6B  k | 6C  l | 6D  m | 6E  n | 6F  o |
1146
;  | 70  p | 71  q | 72  r | 73  s | 74  t | 75  u | 76  v | 77  w |
1147
;  | 78  x | 79  y | 7A  z | 7B  { | 7C  | | 7D  } | 7E  ~ | 7F (c)|
1148
;  | 80 128| 81 129| 82 130| 83 131| 84 132| 85 133| 86 134| 87 135|
1149
;  | 88 136| 89 137| 8A 138| 8B 139| 8C 140| 8D 141| 8E 142| 8F 143|
1150
;  | 90 [A]| 91 [B]| 92 [C]| 93 [D]| 94 [E]| 95 [F]| 96 [G]| 97 [H]|
1151
;  | 98 [I]| 99 [J]| 9A [K]| 9B [L]| 9C [M]| 9D [N]| 9E [O]| 9F [P]|
1152
;  | A0 [Q]| A1 [R]| A2 [S]| A3 [T]| A4 [U]| A5 RND| A6 IK$| A7 PI |
1153
;  | A8 FN | A9 PNT| AA SC$| AB ATT| AC AT | AD TAB| AE VL$| AF COD|
1154
;  | B0 VAL| B1 LEN| B2 SIN| B3 COS| B4 TAN| B5 ASN| B6 ACS| B7 ATN|
1155
;  | B8 LN | B9 EXP| BA INT| BB SQR| BC SGN| BD ABS| BE PEK| BF IN |
1156
;  | C0 USR| C1 ST$| C2 CH$| C3 NOT| C4 BIN| C5 OR | C6 AND| C7 <= |
1157
;  | C8 >= | C9 <> | CA LIN| CB THN| CC TO | CD STP| CE DEF| CF CAT|
1158
;  | D0 FMT| D1 MOV| D2 ERS| D3 OPN| D4 CLO| D5 MRG| D6 VFY| D7 BEP|
1159
;  | D8 CIR| D9 INK| DA PAP| DB FLA| DC BRI| DD INV| DE OVR| DF OUT|
1160
;  | E0 LPR| E1 LLI| E2 STP| E3 REA| E4 DAT| E5 RES| E6 NEW| E7 BDR|
1161
;  | E8 CON| E9 DIM| EA REM| EB FOR| EC GTO| ED GSB| EE INP| EF LOA|
1162
;  | F0 LIS| F1 LET| F2 PAU| F3 NXT| F4 POK| F5 PRI| F6 PLO| F7 RUN|
1163
;  | F8 SAV| F9 RAN| FA IF | FB CLS| FC DRW| FD CLR| FE RET| FF CPY|
1164
 
1165
; Note that for simplicity, Sinclair have located all the control codes
1166
; below the space character.
1167
; ASCII DEL, $7F, has been made a copyright symbol.
1168
; Also $60, '`', not used in BASIC but used in other languages, has been
1169
; allocated the local currency symbol for the relevant country -
1170
; ukp in most Spectrums.
1171
 
1172
; ------------------------------------------------------------------------
1173
 
1174
;**********************************
1175
;** Part 3. LOUDSPEAKER ROUTINES **
1176
;**********************************
1177
 
1178
 
1179
; Documented by Alvin Albrecht.
1180
 
1181
 
1182
; ------------------------------
1183
; Routine to control loudspeaker
1184
; ------------------------------
1185
; Outputs a square wave of given duration and frequency
1186
; to the loudspeaker.
1187
;   Enter with: DE = #cycles - 1
1188
;               HL = tone period as described next
1189
;
1190
; The tone period is measured in T states and consists of
1191
; three parts: a coarse part (H register), a medium part
1192
; (bits 7..2 of L) and a fine part (bits 1..0 of L) which
1193
; contribute to the waveform timing as follows:
1194
;
1195
;                          coarse    medium       fine
1196
; duration of low  = 118 + 1024*H + 16*(L>>2) + 4*(L&0x3)
1197
; duration of hi   = 118 + 1024*H + 16*(L>>2) + 4*(L&0x3)
1198
; Tp = tone period = 236 + 2048*H + 32*(L>>2) + 8*(L&0x3)
1199
;                  = 236 + 2048*H + 8*L = 236 + 8*HL
1200
;
1201
; As an example, to output five seconds of middle C (261.624 Hz):
1202
;   (a) Tone period = 1/261.624 = 3.822ms
1203
;   (b) Tone period in T-States = 3.822ms*fCPU = 13378
1204
;         where fCPU = clock frequency of the CPU = 3.5MHz
1205
;   (c) Find H and L for desired tone period:
1206
;         HL = (Tp - 236) / 8 = (13378 - 236) / 8 = 1643 = 0x066B
1207
;   (d) Tone duration in cycles = 5s/3.822ms = 1308 cycles
1208
;         DE = 1308 - 1 = 0x051B
1209
;
1210
; The resulting waveform has a duty ratio of exactly 50%.
1211
;
1212
;
1213
;; BEEPER
1214
L03B5:  DI                      ; Disable Interrupts so they don't disturb timing
1215
        LD      A,L             ;
1216
        SRL     L               ;
1217
        SRL     L               ; L = medium part of tone period
1218
        CPL                     ;
1219
        AND     $03             ; A = 3 - fine part of tone period
1220
        LD      C,A             ;
1221
        LD      B,$00           ;
1222
        LD      IX,L03D1        ; Address: BE-IX+3
1223
        ADD     IX,BC           ;   IX holds address of entry into the loop
1224
                                ;   the loop will contain 0-3 NOPs, implementing
1225
                                ;   the fine part of the tone period.
1226
        LD      A,($5C48)       ; BORDCR
1227
        AND     $38             ; bits 5..3 contain border colour
1228
        RRCA                    ; border colour bits moved to 2..0
1229
        RRCA                    ;   to match border bits on port #FE
1230
        RRCA                    ;
1231
        OR       $08            ; bit 3 set (tape output bit on port #FE)
1232
                                ;   for loud sound output
1233
;; BE-IX+3
1234
L03D1:  NOP              ;(4)   ; optionally executed NOPs for small
1235
                                ;   adjustments to tone period
1236
;; BE-IX+2
1237
L03D2:  NOP              ;(4)   ;
1238
 
1239
;; BE-IX+1
1240
L03D3:  NOP              ;(4)   ;
1241
 
1242
;; BE-IX+0
1243
L03D4:  INC     B        ;(4)   ;
1244
        INC     C        ;(4)   ;
1245
 
1246
;; BE-H&L-LP
1247
L03D6:  DEC     C        ;(4)   ; timing loop for duration of
1248
        JR      NZ,L03D6 ;(12/7);   high or low pulse of waveform
1249
 
1250
        LD      C,$3F    ;(7)   ;
1251
        DEC     B        ;(4)   ;
1252
        JP      NZ,L03D6 ;(10)  ; to BE-H&L-LP
1253
 
1254
        XOR     $10      ;(7)   ; toggle output beep bit
1255
        OUT     ($FE),A  ;(11)  ; output pulse
1256
        LD      B,H      ;(4)   ; B = coarse part of tone period
1257
        LD      C,A      ;(4)   ; save port #FE output byte
1258
        BIT     4,A      ;(8)   ; if new output bit is high, go
1259
        JR      NZ,L03F2 ;(12/7);   to BE-AGAIN
1260
 
1261
        LD      A,D      ;(4)   ; one cycle of waveform has completed
1262
        OR      E        ;(4)   ;   (low->low). if cycle countdown = 0
1263
        JR      Z,L03F6  ;(12/7);   go to BE-END
1264
 
1265
        LD      A,C      ;(4)   ; restore output byte for port #FE
1266
        LD      C,L      ;(4)   ; C = medium part of tone period
1267
        DEC     DE       ;(6)   ; decrement cycle count
1268
        JP      (IX)     ;(8)   ; do another cycle
1269
 
1270
;; BE-AGAIN                     ; halfway through cycle
1271
L03F2:  LD      C,L      ;(4)   ; C = medium part of tone period
1272
        INC     C        ;(4)   ; adds 16 cycles to make duration of high = duration of low
1273
        JP      (IX)     ;(8)   ; do high pulse of tone
1274
 
1275
;; BE-END
1276
L03F6:  EI                      ; Enable Interrupts
1277
        RET                     ;
1278
 
1279
 
1280
; -------------------
1281
; Handle BEEP command
1282
; -------------------
1283
; BASIC interface to BEEPER subroutine.
1284
; Invoked in BASIC with:
1285
;   BEEP dur, pitch
1286
;   where dur   = duration in seconds
1287
;         pitch = # of semitones above/below middle C
1288
;
1289
; Enter with: pitch on top of calculator stack
1290
;             duration next on calculator stack
1291
;
1292
;; beep
1293
L03F8:  RST     28H             ;; FP-CALC
1294
        DB    $31             ;;duplicate                  ; duplicate pitch
1295
        DB    $27             ;;int                        ; convert to integer
1296
        DB    $C0             ;;st-mem-0                   ; store integer pitch to memory 0
1297
        DB    $03             ;;subtract                   ; calculate fractional part of pitch = fp_pitch - int_pitch
1298
        DB    $34             ;;stk-data                   ; push constant
1299
        DB    $EC             ;;Exponent: $7C, Bytes: 4    ; constant = 0.05762265
1300
        DB    $6C,$98,$1F,$F5 ;;($6C,$98,$1F,$F5)
1301
        DB    $04             ;;multiply                   ; compute:
1302
        DB    $A1             ;;stk-one                    ; 1 + 0.05762265 * fraction_part(pitch)
1303
        DB    $0F             ;;addition
1304
        DB    $38             ;;end-calc                   ; leave on calc stack
1305
 
1306
        LD      HL,$5C92        ; MEM-0: number stored here is in 16 bit integer format (pitch)
1307
                                ;   0, 0/FF (pos/neg), LSB, MSB, 0
1308
                                ;   LSB/MSB is stored in two's complement
1309
                                ; In the following, the pitch is checked if it is in the range -128<=p<=127
1310
        LD      A,(HL)          ; First byte must be zero, otherwise
1311
        AND     A               ;   error in integer conversion
1312
        JR      NZ,L046C        ; to REPORT-B
1313
 
1314
        INC     HL              ;
1315
        LD      C,(HL)          ; C = pos/neg flag = 0/FF
1316
        INC     HL              ;
1317
        LD      B,(HL)          ; B = LSB, two's complement
1318
        LD      A,B             ;
1319
        RLA                     ;
1320
        SBC     A,A             ; A = 0/FF if B is pos/neg
1321
        CP      C               ; must be the same as C if the pitch is -128<=p<=127
1322
        JR      NZ,L046C        ; if no, error REPORT-B
1323
 
1324
        INC     HL              ; if -128<=p<=127, MSB will be 0/FF if B is pos/neg
1325
        CP      (HL)            ; verify this
1326
        JR      NZ,L046C        ; if no, error REPORT-B
1327
                                ; now we know -128<=p<=127
1328
        LD      A,B             ; A = pitch + 60
1329
        ADD     A,$3C           ; if -60<=pitch<=67,
1330
        JP      P,L0425         ;   goto BE-i-OK
1331
 
1332
        JP      PO,L046C        ; if pitch <= 67 goto REPORT-B
1333
                                ;   lower bound of pitch set at -60
1334
 
1335
;; BE-I-OK                      ; here, -60<=pitch<=127
1336
                                ; and A=pitch+60 -> 0<=A<=187
1337
 
1338
L0425:  LD      B,$FA           ; 6 octaves below middle C
1339
 
1340
;; BE-OCTAVE                    ; A=# semitones above 5 octaves below middle C
1341
L0427:  INC     B               ; increment octave
1342
        SUB     $0C             ; 12 semitones = one octave
1343
        JR      NC,L0427        ; to BE-OCTAVE
1344
 
1345
        ADD     A,$0C           ; A = # semitones above C (0-11)
1346
        PUSH    BC              ; B = octave displacement from middle C, 2's complement: -5<=B<=10
1347
        LD      HL,L046E        ; Address: semi-tone
1348
        CALL    L3406           ; routine LOC-MEM
1349
                                ;   HL = 5*A + $046E
1350
        CALL    L33B4           ; routine STACK-NUM
1351
                                ;   read FP value (freq) from semitone table (HL) and push onto calc stack
1352
 
1353
        RST     28H             ;; FP-CALC
1354
        DB    $04             ;;multiply   mult freq by 1 + 0.0576 * fraction_part(pitch) stacked earlier
1355
                                ;;             thus taking into account fractional part of pitch.
1356
                                ;;           the number 0.0576*frequency is the distance in Hz to the next
1357
                                ;;             note (verify with the frequencies recorded in the semitone
1358
                                ;;             table below) so that the fraction_part of the pitch does
1359
                                ;;             indeed represent a fractional distance to the next note.
1360
        DB    $38             ;;end-calc   HL points to first byte of fp num on stack = middle frequency to generate
1361
 
1362
        POP     AF              ; A = octave displacement from middle C, 2's complement: -5<=A<=10
1363
        ADD     A,(HL)          ; increase exponent by A (equivalent to multiplying by 2^A)
1364
        LD      (HL),A          ;
1365
 
1366
        RST     28H             ;; FP-CALC
1367
        DB    $C0             ;;st-mem-0          ; store frequency in memory 0
1368
        DB    $02             ;;delete            ; remove from calc stack
1369
        DB    $31             ;;duplicate         ; duplicate duration (seconds)
1370
        DB    $38             ;;end-calc
1371
 
1372
        CALL    L1E94           ; routine FIND-INT1 ; FP duration to A
1373
        CP      $0B             ; if dur > 10 seconds,
1374
        JR      NC,L046C        ;   goto REPORT-B
1375
 
1376
        ;;; The following calculation finds the tone period for HL and the cycle count
1377
        ;;; for DE expected in the BEEPER subroutine.  From the example in the BEEPER comments,
1378
        ;;;
1379
        ;;; HL = ((fCPU / f) - 236) / 8 = fCPU/8/f - 236/8 = 437500/f -29.5
1380
        ;;; DE = duration * frequency - 1
1381
        ;;;
1382
        ;;; Note the different constant (30.125) used in the calculation of HL
1383
        ;;; below.  This is probably an error.
1384
 
1385
        RST     28H             ;; FP-CALC
1386
        DB    $E0             ;;get-mem-0                 ; push frequency
1387
        DB    $04             ;;multiply                  ; result1: #cycles = duration * frequency
1388
        DB    $E0             ;;get-mem-0                 ; push frequency
1389
        DB    $34             ;;stk-data                  ; push constant
1390
        DB    $80             ;;Exponent $93, Bytes: 3    ; constant = 437500
1391
        DB    $43,$55,$9F,$80 ;;($55,$9F,$80,$00)
1392
        DB    $01             ;;exchange                  ; frequency on top
1393
        DB    $05             ;;division                  ; 437500 / frequency
1394
        DB    $34             ;;stk-data                  ; push constant
1395
        DB    $35             ;;Exponent: $85, Bytes: 1   ; constant = 30.125
1396
        DB    $71             ;;($71,$00,$00,$00)
1397
        DB    $03             ;;subtract                  ; result2: tone_period(HL) = 437500 / freq - 30.125
1398
        DB    $38             ;;end-calc
1399
 
1400
        CALL    L1E99           ; routine FIND-INT2
1401
        PUSH    BC              ;   BC = tone_period(HL)
1402
        CALL    L1E99           ; routine FIND-INT2, BC = #cycles to generate
1403
        POP     HL              ; HL = tone period
1404
        LD      D,B             ;
1405
        LD      E,C             ; DE = #cycles
1406
        LD      A,D             ;
1407
        OR      E               ;
1408
        RET     Z               ; if duration = 0, skip BEEP and avoid 65536 cycle
1409
                                ;   boondoggle that would occur next
1410
        DEC     DE              ; DE = #cycles - 1
1411
        JP      L03B5           ; to BEEPER
1412
 
1413
; ---
1414
 
1415
 
1416
;; REPORT-B
1417
L046C:  RST     08H             ; ERROR-1
1418
        DB    $0A             ; Error Report: Integer out of range
1419
 
1420
 
1421
 
1422
; ---------------
1423
; Semi-tone table
1424
; ---------------
1425
;
1426
; Holds frequencies corresponding to semitones in middle octave.
1427
; To move n octaves higher or lower, frequencies are multiplied by 2^n.
1428
 
1429
;; semi-tone         five byte fp         decimal freq     note (middle)
1430
L046E:  DB    $89, $02, $D0, $12, $86;  261.625565290         C
1431
        DB    $89, $0A, $97, $60, $75;  277.182631135         C#
1432
        DB    $89, $12, $D5, $17, $1F;  293.664768100         D
1433
        DB    $89, $1B, $90, $41, $02;  311.126983881         D#
1434
        DB    $89, $24, $D0, $53, $CA;  329.627557039         E
1435
        DB    $89, $2E, $9D, $36, $B1;  349.228231549         F
1436
        DB    $89, $38, $FF, $49, $3E;  369.994422674         F#
1437
        DB    $89, $43, $FF, $6A, $73;  391.995436072         G
1438
        DB    $89, $4F, $A7, $00, $54;  415.304697513         G#
1439
        DB    $89, $5C, $00, $00, $00;  440.000000000         A
1440
        DB    $89, $69, $14, $F6, $24;  466.163761616         A#
1441
        DB    $89, $76, $F1, $10, $05;  493.883301378         B
1442
 
1443
 
1444
;****************************************
1445
;** Part 4. CASSETTE HANDLING ROUTINES **
1446
;****************************************
1447
 
1448
; These routines begin with the service routines followed by a single
1449
; command entry point.
1450
; The first of these service routines is a curiosity.
1451
 
1452
; -----------------------
1453
; THE 'ZX81 NAME' ROUTINE
1454
; -----------------------
1455
;   This routine fetches a filename in ZX81 format and is not used by the 
1456
;   cassette handling routines in this ROM.
1457
 
1458
;; zx81-name
1459
L04AA:  CALL    L24FB           ; routine SCANNING to evaluate expression.
1460
        LD      A,($5C3B)       ; fetch system variable FLAGS.
1461
        ADD     A,A             ; test bit 7 - syntax, bit 6 - result type.
1462
        JP      M,L1C8A         ; to REPORT-C if not string result
1463
                                ; 'Nonsense in BASIC'.
1464
 
1465
        POP     HL              ; drop return address.
1466
        RET     NC              ; return early if checking syntax.
1467
 
1468
        PUSH    HL              ; re-save return address.
1469
        CALL    L2BF1           ; routine STK-FETCH fetches string parameters.
1470
        LD      H,D             ; transfer start of filename
1471
        LD      L,E             ; to the HL register.
1472
        DEC     C               ; adjust to point to last character and
1473
        RET     M               ; return if the null string.
1474
                                ; or multiple of 256!
1475
 
1476
        ADD     HL,BC           ; find last character of the filename.
1477
                                ; and also clear carry.
1478
        SET     7,(HL)          ; invert it.
1479
        RET                     ; return.
1480
 
1481
; =========================================
1482
;
1483
; PORT 254 ($FE)
1484
;
1485
;                      spk mic { border  }  
1486
;          ___ ___ ___ ___ ___ ___ ___ ___ 
1487
; PORT    |   |   |   |   |   |   |   |   |
1488
; 254     |   |   |   |   |   |   |   |   |
1489
; $FE     |___|___|___|___|___|___|___|___|
1490
;           7   6   5   4   3   2   1   0
1491
;
1492
 
1493
; ----------------------------------
1494
; Save header and program/data bytes
1495
; ----------------------------------
1496
; This routine saves a section of data. It is called from SA-CTRL to save the
1497
; seventeen bytes of header data. It is also the exit route from that routine
1498
; when it is set up to save the actual data.
1499
; On entry -
1500
; HL points to start of data.
1501
; IX points to descriptor.
1502
; The accumulator is set to  $00 for a header, $FF for data.
1503
 
1504
;; SA-BYTES
1505
L04C2:  LD      HL,L053F        ; address: SA/LD-RET
1506
        PUSH    HL              ; is pushed as common exit route.
1507
                                ; however there is only one non-terminal exit 
1508
                                ; point.
1509
 
1510
        LD      HL,$1F80        ; a timing constant H=$1F, L=$80
1511
                                ; inner and outer loop counters
1512
                                ; a five second lead-in is used for a header.
1513
 
1514
        BIT     7,A             ; test one bit of accumulator.
1515
                                ; (AND A ?)
1516
        JR      Z,L04D0         ; skip to SA-FLAG if a header is being saved.
1517
 
1518
; else is data bytes and a shorter lead-in is used.
1519
 
1520
        LD      HL,$0C98        ; another timing value H=$0C, L=$98.
1521
                                ; a two second lead-in is used for the data.
1522
 
1523
 
1524
;; SA-FLAG
1525
L04D0:  EX      AF,AF'          ; save flag
1526
        INC     DE              ; increase length by one.
1527
        DEC     IX              ; decrease start.
1528
 
1529
        DI                      ; disable interrupts
1530
 
1531
        LD      A,$02           ; select red for border, microphone bit on.
1532
        LD      B,A             ; also does as an initial slight counter value.
1533
 
1534
;; SA-LEADER
1535
L04D8:  DJNZ    L04D8           ; self loop to SA-LEADER for delay.
1536
                                ; after initial loop, count is $A4 (or $A3)
1537
 
1538
        OUT     ($FE),A         ; output byte $02/$0D to tape port.
1539
 
1540
        XOR     $0F             ; switch from RED (mic on) to CYAN (mic off).
1541
 
1542
        LD      B,$A4           ; hold count. also timed instruction.
1543
 
1544
        DEC     L               ; originally $80 or $98.
1545
                                ; but subsequently cycles 256 times.
1546
        JR      NZ,L04D8        ; back to SA-LEADER until L is zero.
1547
 
1548
; the outer loop is counted by H
1549
 
1550
        DEC     B               ; decrement count
1551
        DEC     H               ; originally  twelve or thirty-one.
1552
        JP      P,L04D8         ; back to SA-LEADER until H becomes $FF
1553
 
1554
; now send a synch pulse. At this stage mic is off and A holds value
1555
; for mic on.
1556
; A synch pulse is much shorter than the steady pulses of the lead-in.
1557
 
1558
        LD      B,$2F           ; another short timed delay.
1559
 
1560
;; SA-SYNC-1
1561
L04EA:  DJNZ    L04EA           ; self loop to SA-SYNC-1
1562
 
1563
        OUT     ($FE),A         ; switch to mic on and red.
1564
        LD      A,$0D           ; prepare mic off - cyan
1565
        LD      B,$37           ; another short timed delay.
1566
 
1567
;; SA-SYNC-2
1568
L04F2:  DJNZ    L04F2           ; self loop to SA-SYNC-2
1569
 
1570
        OUT     ($FE),A         ; output mic off, cyan border.
1571
        LD      BC,$3B0E        ; B=$3B time(*), C=$0E, YELLOW, MIC OFF.
1572
 
1573
;
1574
 
1575
        EX      AF,AF'          ; restore saved flag
1576
                                ; which is 1st byte to be saved.
1577
 
1578
        LD      L,A             ; and transfer to L.
1579
                                ; the initial parity is A, $FF or $00.
1580
        JP      L0507           ; JUMP forward to SA-START     ->
1581
                                ; the mid entry point of loop.
1582
 
1583
; -------------------------
1584
; During the save loop a parity byte is maintained in H.
1585
; the save loop begins by testing if reduced length is zero and if so
1586
; the final parity byte is saved reducing count to $FFFF.
1587
 
1588
;; SA-LOOP
1589
L04FE:  LD      A,D             ; fetch high byte
1590
        OR      E               ; test against low byte.
1591
        JR      Z,L050E         ; forward to SA-PARITY if zero.
1592
 
1593
        LD      L,(IX+$00)      ; load currently addressed byte to L.
1594
 
1595
;; SA-LOOP-P
1596
L0505:  LD      A,H             ; fetch parity byte.
1597
        XOR     L               ; exclusive or with new byte.
1598
 
1599
; -> the mid entry point of loop.
1600
 
1601
;; SA-START
1602
L0507:  LD      H,A             ; put parity byte in H.
1603
        LD      A,$01           ; prepare blue, mic=on.
1604
        SCF                     ; set carry flag ready to rotate in.
1605
        JP      L0525           ; JUMP forward to SA-8-BITS            -8->
1606
 
1607
; ---
1608
 
1609
;; SA-PARITY
1610
L050E:  LD      L,H             ; transfer the running parity byte to L and
1611
        JR      L0505           ; back to SA-LOOP-P 
1612
                                ; to output that byte before quitting normally.
1613
 
1614
; ---
1615
 
1616
; entry point to save yellow part of bit.
1617
; a bit consists of a period with mic on and blue border followed by 
1618
; a period of mic off with yellow border. 
1619
; Note. since the DJNZ instruction does not affect flags, the zero flag is used
1620
; to indicate which of the two passes is in effect and the carry maintains the
1621
; state of the bit to be saved.
1622
 
1623
;; SA-BIT-2
1624
L0511:  LD      A,C             ; fetch 'mic on and yellow' which is 
1625
                                ; held permanently in C.
1626
        BIT     7,B             ; set the zero flag. B holds $3E.
1627
 
1628
; entry point to save 1 entire bit. For first bit B holds $3B(*).
1629
; Carry is set if saved bit is 1. zero is reset NZ on entry.
1630
 
1631
;; SA-BIT-1
1632
L0514:  DJNZ    L0514           ; self loop for delay to SA-BIT-1
1633
 
1634
        JR      NC,L051C        ; forward to SA-OUT if bit is 0.
1635
 
1636
; but if bit is 1 then the mic state is held for longer.
1637
 
1638
        LD      B,$42           ; set timed delay. (66 decimal)
1639
 
1640
;; SA-SET
1641
L051A:  DJNZ    L051A           ; self loop to SA-SET 
1642
                                ; (roughly an extra 66*13 clock cycles)
1643
 
1644
;; SA-OUT
1645
L051C:  OUT     ($FE),A         ; blue and mic on OR  yellow and mic off.
1646
 
1647
        LD      B,$3E           ; set up delay
1648
        JR      NZ,L0511        ; back to SA-BIT-2 if zero reset NZ (first pass)
1649
 
1650
; proceed when the blue and yellow bands have been output.
1651
 
1652
        DEC     B               ; change value $3E to $3D.
1653
        XOR     A               ; clear carry flag (ready to rotate in).
1654
        INC     A               ; reset zero flag ie. NZ.
1655
 
1656
; -8-> 
1657
 
1658
;; SA-8-BITS
1659
L0525:  RL      L               ; rotate left through carry
1660
                                ; C<76543210<C  
1661
        JP      NZ,L0514        ; JUMP back to SA-BIT-1 
1662
                                ; until all 8 bits done.
1663
 
1664
; when the initial set carry is passed out again then a byte is complete.
1665
 
1666
        DEC     DE              ; decrease length
1667
        INC     IX              ; increase byte pointer
1668
        LD      B,$31           ; set up timing.
1669
 
1670
        LD      A,$7F           ; test the space key and
1671
        IN      A,($FE)         ; return to common exit (to restore border)
1672
        RRA                     ; if a space is pressed
1673
        RET     NC              ; return to SA/LD-RET.   - - >
1674
 
1675
; now test if byte counter has reached $FFFF.
1676
 
1677
        LD      A,D             ; fetch high byte
1678
        INC     A               ; increment.
1679
        JP      NZ,L04FE        ; JUMP to SA-LOOP if more bytes.
1680
 
1681
        LD      B,$3B           ; a final delay. 
1682
 
1683
;; SA-DELAY
1684
L053C:  DJNZ    L053C           ; self loop to SA-DELAY
1685
 
1686
        RET                     ; return - - >
1687
 
1688
; --------------------------------------------------
1689
; Reset border and check BREAK key for LOAD and SAVE
1690
; --------------------------------------------------
1691
; the address of this routine is pushed on the stack prior to any load/save
1692
; operation and it handles normal completion with the restoration of the
1693
; border and also abnormal termination when the break key, or to be more
1694
; precise the space key is pressed during a tape operation.
1695
; - - >
1696
 
1697
;; SA/LD-RET
1698
L053F:  PUSH    AF              ; preserve accumulator throughout.
1699
        LD      A,($5C48)       ; fetch border colour from BORDCR.
1700
        AND     $38             ; mask off paper bits.
1701
        RRCA                    ; rotate
1702
        RRCA                    ; to the
1703
        RRCA                    ; range 0-7.
1704
 
1705
;===============================
550 savelij 1706
                IF TAP_EMU_BORDER=1
1707
                DB 0,0
1708
                ELSE
384 savelij 1709
                OUT ($FE),A     ; change the border colour.
550 savelij 1710
                ENDIF
384 savelij 1711
;===============================
1712
 
1713
        LD      A,$7F           ; read from port address $7FFE the
1714
        IN      A,($FE)         ; row with the space key at outside.
1715
 
1716
        RRA                     ; test for space key pressed.
1717
        EI                      ; enable interrupts
1718
        JR      C,L0554         ; forward to SA/LD-END if not
1719
 
1720
 
1721
;; REPORT-Da
1722
L0552:  RST     08H             ; ERROR-1
1723
        DB    $0C             ; Error Report: BREAK - CONT repeats
1724
 
1725
; ---
1726
 
1727
;; SA/LD-END
1728
L0554:  POP     AF              ; restore the accumulator.
1729
        RET                     ; return.
1730
 
1731
; ------------------------------------
1732
; Load header or block of information
1733
; ------------------------------------
1734
; This routine is used to load bytes and on entry A is set to $00 for a 
1735
; header or to $FF for data.  IX points to the start of receiving location 
1736
; and DE holds the length of bytes to be loaded. If, on entry the carry flag 
1737
; is set then data is loaded, if reset then it is verified.
1738
 
1739
;; LD-BYTES
1740
L0556:  INC     D               ; reset the zero flag without disturbing carry.
1741
        EX      AF,AF'          ; preserve entry flags.
1742
        DEC     D               ; restore high byte of length.
1743
 
1744
        DI                      ; disable interrupts
1745
 
1746
        LD      A,$0F           ; make the border white and mic off.
1747
 
1748
;===============================
550 savelij 1749
                IF TAP_EMU_BORDER=1
1750
                DB 0,0
1751
                ELSE
384 savelij 1752
                OUT ($FE),A     ; output to port.
550 savelij 1753
                ENDIF
384 savelij 1754
;===============================
1755
 
1756
        LD      HL,L053F        ; Address: SA/LD-RET
1757
        PUSH    HL              ; is saved on stack as terminating routine.
1758
 
1759
; the reading of the EAR bit (D6) will always be preceded by a test of the
1760
; space key (D0), so store the initial post-test state.
1761
 
1762
        IN      A,($FE)         ; read the ear state - bit 6.
1763
        RRA                     ; rotate to bit 5.
1764
        AND     $20             ; isolate this bit.
1765
        OR      $02             ; combine with red border colour.
1766
 
1767
;===============================
585 savelij 1768
                RST 8
1769
                DB _TAPE_EMUL
384 savelij 1770
;===============================
1771
 
1772
;; LD-BREAK
1773
L056B:  RET     NZ              ; return if at any time space is pressed.
1774
 
1775
;; LD-START
1776
L056C:  CALL    L05E7           ; routine LD-EDGE-1
1777
        JR      NC,L056B        ; back to LD-BREAK with time out and no
1778
                                ; edge present on tape.
1779
 
1780
; but continue when a transition is found on tape.
1781
 
1782
        LD      HL,$0415        ; set up 16-bit outer loop counter for
1783
                                ; approx 1 second delay.
1784
 
1785
;; LD-WAIT
1786
L0574:  DJNZ    L0574           ; self loop to LD-WAIT (for 256 times)
1787
 
1788
        DEC     HL              ; decrease outer loop counter.
1789
        LD      A,H             ; test for
1790
        OR      L               ; zero.
1791
        JR      NZ,L0574        ; back to LD-WAIT, if not zero, with zero in B.
1792
 
1793
; continue after delay with H holding zero and B also.
1794
; sample 256 edges to check that we are in the middle of a lead-in section.
1795
 
1796
        CALL    L05E3           ; routine LD-EDGE-2
1797
        JR      NC,L056B        ; back to LD-BREAK
1798
                                ; if no edges at all.
1799
 
1800
;; LD-LEADER
1801
L0580:  LD      B,$9C           ; set timing value.
1802
        CALL    L05E3           ; routine LD-EDGE-2
1803
        JR      NC,L056B        ; back to LD-BREAK if time-out
1804
 
1805
        LD      A,$C6           ; two edges must be spaced apart.
1806
        CP      B               ; compare
1807
        JR      NC,L056C        ; back to LD-START if too close together for a
1808
                                ; lead-in.
1809
 
1810
        INC     H               ; proceed to test 256 edged sample.
1811
        JR      NZ,L0580        ; back to LD-LEADER while more to do.
1812
 
1813
; sample indicates we are in the middle of a two or five second lead-in.
1814
; Now test every edge looking for the terminal synch signal.
1815
 
1816
;; LD-SYNC
1817
L058F:  LD      B,$C9           ; initial timing value in B.
1818
        CALL    L05E7           ; routine LD-EDGE-1
1819
        JR      NC,L056B        ; back to LD-BREAK with time-out.
1820
 
1821
        LD      A,B             ; fetch augmented timing value from B.
1822
        CP      $D4             ; compare
1823
        JR      NC,L058F        ; back to LD-SYNC if gap too big, that is,
1824
                                ; a normal lead-in edge gap.
1825
 
1826
; but a short gap will be the synch pulse.
1827
; in which case another edge should appear before B rises to $FF
1828
 
1829
        CALL    L05E7           ; routine LD-EDGE-1
1830
        RET     NC              ; return with time-out.
1831
 
1832
; proceed when the synch at the end of the lead-in is found.
1833
; We are about to load data so change the border colours.
1834
 
1835
        LD      A,C             ; fetch long-term mask from C
1836
        XOR     $03             ; and make blue/yellow.
1837
 
1838
        LD      C,A             ; store the new long-term byte.
1839
 
1840
        LD      H,$00           ; set up parity byte as zero.
1841
        LD      B,$B0           ; timing.
1842
        JR      L05C8           ; forward to LD-MARKER
1843
                                ; the loop mid entry point with the alternate
1844
                                ; zero flag reset to indicate first byte
1845
                                ; is discarded.
1846
 
1847
; --------------
1848
; the loading loop loads each byte and is entered at the mid point.
1849
 
1850
;; LD-LOOP
1851
L05A9:  EX      AF,AF'          ; restore entry flags and type in A.
1852
        JR      NZ,L05B3        ; forward to LD-FLAG if awaiting initial flag
1853
                                ; which is to be discarded.
1854
 
1855
        JR      NC,L05BD        ; forward to LD-VERIFY if not to be loaded.
1856
 
1857
        LD      (IX+$00),L      ; place loaded byte at memory location.
1858
        JR      L05C2           ; forward to LD-NEXT
1859
 
1860
; ---
1861
 
1862
;; LD-FLAG
1863
L05B3:  RL      C               ; preserve carry (verify) flag in long-term
1864
                                ; state byte. Bit 7 can be lost.
1865
 
1866
        XOR     L               ; compare type in A with first byte in L.
1867
        RET     NZ              ; return if no match e.g. CODE vs DATA.
1868
 
1869
; continue when data type matches.
1870
 
1871
        LD      A,C             ; fetch byte with stored carry
1872
        RRA                     ; rotate it to carry flag again
1873
        LD      C,A             ; restore long-term port state.
1874
 
1875
        INC     DE              ; increment length ??
1876
        JR      L05C4           ; forward to LD-DEC.
1877
                                ; but why not to location after ?
1878
 
1879
; ---
1880
; for verification the byte read from tape is compared with that in memory.
1881
 
1882
;; LD-VERIFY
1883
L05BD:  LD      A,(IX+$00)      ; fetch byte from memory.
1884
        XOR     L               ; compare with that on tape
1885
        RET     NZ              ; return if not zero. 
1886
 
1887
;; LD-NEXT
1888
L05C2:  INC     IX              ; increment byte pointer.
1889
 
1890
;; LD-DEC
1891
L05C4:  DEC     DE              ; decrement length.
1892
        EX      AF,AF'          ; store the flags.
1893
        LD      B,$B2           ; timing.
1894
 
1895
; when starting to read 8 bits the receiving byte is marked with bit at right.
1896
; when this is rotated out again then 8 bits have been read.
1897
 
1898
;; LD-MARKER
1899
L05C8:  LD      L,$01           ; initialize as %00000001
1900
 
1901
;; LD-8-BITS
1902
L05CA:  CALL    L05E3           ; routine LD-EDGE-2 increments B relative to
1903
                                ; gap between 2 edges.
1904
        RET     NC              ; return with time-out.
1905
 
1906
        LD      A,$CB           ; the comparison byte.
1907
        CP      B               ; compare to incremented value of B.
1908
                                ; if B is higher then bit on tape was set.
1909
                                ; if <= then bit on tape is reset.
1910
 
1911
        RL      L               ; rotate the carry bit into L.
1912
 
1913
        LD      B,$B0           ; reset the B timer byte.
1914
        JP      NC,L05CA        ; JUMP back to LD-8-BITS
1915
 
1916
; when carry set then marker bit has been passed out and byte is complete.
1917
 
1918
        LD      A,H             ; fetch the running parity byte.
1919
        XOR     L               ; include the new byte.
1920
        LD      H,A             ; and store back in parity register.
1921
 
1922
        LD      A,D             ; check length of
1923
        OR      E               ; expected bytes.
1924
        JR      NZ,L05A9        ; back to LD-LOOP
1925
                                ; while there are more.
1926
 
1927
; when all bytes loaded then parity byte should be zero.
1928
 
1929
        LD      A,H             ; fetch parity byte.
1930
        CP      $01             ; set carry if zero.
1931
        RET                     ; return
1932
                                ; in no carry then error as checksum disagrees.
1933
 
1934
; -------------------------
1935
; Check signal being loaded
1936
; -------------------------
1937
; An edge is a transition from one mic state to another.
1938
; More specifically a change in bit 6 of value input from port $FE.
1939
; Graphically it is a change of border colour, say, blue to yellow.
1940
; The first entry point looks for two adjacent edges. The second entry point
1941
; is used to find a single edge.
1942
; The B register holds a count, up to 256, within which the edge (or edges)
1943
; must be found. The gap between two edges will be more for a '1' than a '0'
1944
; so the value of B denotes the state of the bit (two edges) read from tape.
1945
 
1946
; ->
1947
 
1948
;; LD-EDGE-2
1949
L05E3:  CALL    L05E7           ; call routine LD-EDGE-1 below.
1950
        RET     NC              ; return if space pressed or time-out.
1951
                                ; else continue and look for another adjacent
1952
                                ; edge which together represent a bit on the
1953
                                ; tape.
1954
 
1955
; ->
1956
; this entry point is used to find a single edge from above but also
1957
; when detecting a read-in signal on the tape.
1958
 
1959
;; LD-EDGE-1
1960
L05E7:  LD      A,$16           ; a delay value of twenty two.
1961
 
1962
;; LD-DELAY
1963
L05E9:  DEC     A               ; decrement counter
1964
        JR      NZ,L05E9        ; loop back to LD-DELAY 22 times.
1965
 
1966
        AND      A              ; clear carry.
1967
 
1968
;; LD-SAMPLE
1969
L05ED:  INC     B               ; increment the time-out counter.
1970
        RET     Z               ; return with failure when $FF passed.
1971
 
1972
        LD      A,$7F           ; prepare to read keyboard and EAR port
1973
        IN      A,($FE)         ; row $7FFE. bit 6 is EAR, bit 0 is SPACE key.
1974
        RRA                     ; test outer key the space. (bit 6 moves to 5)
1975
        RET     NC              ; return if space pressed.  >>>
1976
 
1977
        XOR     C               ; compare with initial long-term state.
1978
        AND     $20             ; isolate bit 5
1979
        JR      Z,L05ED         ; back to LD-SAMPLE if no edge.
1980
 
1981
; but an edge, a transition of the EAR bit, has been found so switch the
1982
; long-term comparison byte containing both border colour and EAR bit.
1983
 
1984
        LD      A,C             ; fetch comparison value.
1985
        CPL                     ; switch the bits
1986
        LD      C,A             ; and put back in C for long-term.
1987
 
1988
        AND     $07             ; isolate new colour bits.
1989
        OR      $08             ; set bit 3 - MIC off.
1990
        OUT     ($FE),A         ; send to port to effect change of colour.
1991
 
1992
        SCF                     ; set carry flag signaling edge found within
1993
                                ; time allowed.
1994
        RET                     ; return.
1995
 
1996
; ---------------------------------
1997
; Entry point for all tape commands
1998
; ---------------------------------
1999
; This is the single entry point for the four tape commands.
2000
; The routine first determines in what context it has been called by examining
2001
; the low byte of the Syntax table entry which was stored in T_ADDR.
2002
; Subtracting $EO (the present arrangement) gives a value of
2003
; $00 - SAVE
2004
; $01 - LOAD
2005
; $02 - VERIFY
2006
; $03 - MERGE
2007
; As with all commands the address STMT-RET is on the stack.
2008
 
2009
;; SAVE-ETC
2010
L0605:  POP     AF              ; discard address STMT-RET.
2011
        LD      A,($5C74)       ; fetch T_ADDR
2012
 
2013
; Now reduce the low byte of the Syntax table entry to give command.
2014
; Note. For ZASM use SUB $E0 as next instruction.
2015
 
2016
L0609           SUB LOW (L1ADF)+1       ; subtract the known offset.
2017
                                ; ( is SUB $E0 in standard ROM )
2018
 
2019
        LD      ($5C74),A       ; and put back in T_ADDR as 0,1,2, or 3
2020
                                ; for future reference.
2021
 
2022
        CALL    L1C8C           ; routine EXPT-EXP checks that a string
2023
                                ; expression follows and stacks the
2024
                                ; parameters in run-time.
2025
 
2026
        CALL    L2530           ; routine SYNTAX-Z
2027
        JR      Z,L0652         ; forward to SA-DATA if checking syntax.
2028
 
2029
        LD      BC,$0011        ; presume seventeen bytes for a header.
2030
        LD      A,($5C74)       ; fetch command from T_ADDR.
2031
        AND     A               ; test for zero - SAVE.
2032
        JR      Z,L0621         ; forward to SA-SPACE if so.
2033
 
2034
        LD      C,$22           ; else double length to thirty four.
2035
 
2036
;; SA-SPACE
2037
L0621:  RST     30H             ; BC-SPACES creates 17/34 bytes in workspace.
2038
 
2039
        PUSH    DE              ; transfer the start of new space to
2040
        POP     IX              ; the available index register.
2041
 
2042
; ten spaces are required for the default filename but it is simpler to
2043
; overwrite the first file-type indicator byte as well.
2044
 
2045
        LD      B,$0B           ; set counter to eleven.
2046
        LD      A,$20           ; prepare a space.
2047
 
2048
;; SA-BLANK
2049
L0629:  LD      (DE),A          ; set workspace location to space.
2050
        INC     DE              ; next location.
2051
        DJNZ    L0629           ; loop back to SA-BLANK till all eleven done.
2052
 
2053
        LD      (IX+$01),$FF    ; set first byte of ten character filename
2054
                                ; to $FF as a default to signal null string.
2055
 
2056
        CALL    L2BF1           ; routine STK-FETCH fetches the filename
2057
                                ; parameters from the calculator stack.
2058
                                ; length of string in BC.
2059
                                ; start of string in DE.
2060
 
2061
        LD      HL,$FFF6        ; prepare the value minus ten.
2062
        DEC     BC              ; decrement length.
2063
                                ; ten becomes nine, zero becomes $FFFF.
2064
        ADD     HL,BC           ; trial addition.
2065
        INC     BC              ; restore true length.
2066
        JR      NC,L064B        ; forward to SA-NAME if length is one to ten.
2067
 
2068
; the filename is more than ten characters in length or the null string.
2069
 
2070
        LD      A,($5C74)       ; fetch command from T_ADDR.
2071
        AND     A               ; test for zero - SAVE.
2072
        JR      NZ,L0644        ; forward to SA-NULL if not the SAVE command.
2073
 
2074
; but no more than ten characters are allowed for SAVE.
2075
; The first ten characters of any other command parameter are acceptable.
2076
; Weird, but necessary, if saving to sectors.
2077
; Note. the golden rule that there are no restriction on anything is broken.
2078
 
2079
;; REPORT-Fa
2080
L0642:  RST     08H             ; ERROR-1
2081
        DB    $0E             ; Error Report: Invalid file name
2082
 
2083
; continue with LOAD, MERGE, VERIFY and also SAVE within ten character limit.
2084
 
2085
;; SA-NULL
2086
L0644:  LD      A,B             ; test length of filename
2087
        OR      C               ; for zero.
2088
        JR      Z,L0652         ; forward to SA-DATA if so using the 255
2089
                                ; indicator followed by spaces.
2090
 
2091
        LD      BC,$000A        ; else trim length to ten.
2092
 
2093
; other paths rejoin here with BC holding length in range 1 - 10.
2094
 
2095
;; SA-NAME
2096
L064B:  PUSH    IX              ; push start of file descriptor.
2097
        POP     HL              ; and pop into HL.
2098
 
2099
        INC     HL              ; HL now addresses first byte of filename.
2100
        EX      DE,HL           ; transfer destination address to DE, start
2101
                                ; of string in command to HL.
2102
        LDIR                    ; copy up to ten bytes
2103
                                ; if less than ten then trailing spaces follow.
2104
 
2105
; the case for the null string rejoins here.
2106
 
2107
;; SA-DATA
2108
L0652:  RST     18H             ; GET-CHAR
2109
        CP      $E4             ; is character after filename the token 'DATA' ?
2110
        JR      NZ,L06A0        ; forward to SA-SCR$ to consider SCREEN$ if
2111
                                ; not.
2112
 
2113
; continue to consider DATA.
2114
 
2115
        LD      A,($5C74)       ; fetch command from T_ADDR
2116
        CP      $03             ; is it 'VERIFY' ?
2117
        JP      Z,L1C8A         ; jump forward to REPORT-C if so.
2118
                                ; 'Nonsense in BASIC'
2119
                                ; VERIFY "d" DATA is not allowed.
2120
 
2121
; continue with SAVE, LOAD, MERGE of DATA.
2122
 
2123
        RST     20H             ; NEXT-CHAR
2124
        CALL    L28B2           ; routine LOOK-VARS searches variables area
2125
                                ; returning with carry reset if found or
2126
                                ; checking syntax.
2127
        SET     7,C             ; this converts a simple string to a
2128
                                ; string array. The test for an array or string
2129
                                ; comes later.
2130
        JR      NC,L0672        ; forward to SA-V-OLD if variable found.
2131
 
2132
        LD      HL,$0000        ; set destination to zero as not fixed.
2133
        LD      A,($5C74)       ; fetch command from T_ADDR
2134
        DEC     A               ; test for 1 - LOAD
2135
        JR      Z,L0685         ; forward to SA-V-NEW with LOAD DATA.
2136
                                ; to load a new array.
2137
 
2138
; otherwise the variable was not found in run-time with SAVE/MERGE.
2139
 
2140
;; REPORT-2a
2141
L0670:  RST     08H             ; ERROR-1
2142
        DB    $01             ; Error Report: Variable not found
2143
 
2144
; continue with SAVE/LOAD  DATA
2145
 
2146
;; SA-V-OLD
2147
L0672:  JP      NZ,L1C8A        ; to REPORT-C if not an array variable.
2148
                                ; or erroneously a simple string.
2149
                                ; 'Nonsense in BASIC'
2150
 
2151
 
2152
        CALL    L2530           ; routine SYNTAX-Z
2153
        JR      Z,L0692         ; forward to SA-DATA-1 if checking syntax.
2154
 
2155
        INC     HL              ; step past single character variable name.
2156
        LD      A,(HL)          ; fetch low byte of length.
2157
        LD      (IX+$0B),A      ; place in descriptor.
2158
        INC     HL              ; point to high byte.
2159
        LD      A,(HL)          ; and transfer that
2160
        LD      (IX+$0C),A      ; to descriptor.
2161
        INC     HL              ; increase pointer within variable.
2162
 
2163
;; SA-V-NEW
2164
L0685:  LD      (IX+$0E),C      ; place character array name in  header.
2165
        LD      A,$01           ; default to type numeric.
2166
        BIT     6,C             ; test result from look-vars.
2167
        JR      Z,L068F         ; forward to SA-V-TYPE if numeric.
2168
 
2169
        INC     A               ; set type to 2 - string array.
2170
 
2171
;; SA-V-TYPE
2172
L068F:  LD      (IX+$00),A      ; place type 0, 1 or 2 in descriptor.
2173
 
2174
;; SA-DATA-1
2175
L0692:  EX      DE,HL           ; save var pointer in DE
2176
 
2177
        RST     20H             ; NEXT-CHAR
2178
        CP      $29             ; is character ')' ?
2179
        JR      NZ,L0672        ; back if not to SA-V-OLD to report
2180
                                ; 'Nonsense in BASIC'
2181
 
2182
        RST     20H             ; NEXT-CHAR advances character address.
2183
        CALL    L1BEE           ; routine CHECK-END errors if not end of
2184
                                ; the statement.
2185
 
2186
        EX      DE,HL           ; bring back variables data pointer.
2187
        JP      L075A           ; jump forward to SA-ALL
2188
 
2189
; ---
2190
; the branch was here to consider a 'SCREEN$', the display file.
2191
 
2192
;; SA-SCR$
2193
L06A0:  CP      $AA             ; is character the token 'SCREEN$' ?
2194
        JR      NZ,L06C3        ; forward to SA-CODE if not.
2195
 
2196
        LD      A,($5C74)       ; fetch command from T_ADDR
2197
        CP      $03             ; is it MERGE ?
2198
        JP       Z,L1C8A        ; jump to REPORT-C if so.
2199
                                ; 'Nonsense in BASIC'
2200
 
2201
; continue with SAVE/LOAD/VERIFY SCREEN$.
2202
 
2203
        RST     20H             ; NEXT-CHAR
2204
        CALL    L1BEE           ; routine CHECK-END errors if not at end of
2205
                                ; statement.
2206
 
2207
; continue in runtime.
2208
 
2209
        LD      (IX+$0B),$00    ; set descriptor length
2210
        LD      (IX+$0C),$1B    ; to $1b00 to include bitmaps and attributes.
2211
 
2212
        LD      HL,$4000        ; set start to display file start.
2213
        LD      (IX+$0D),L      ; place start in
2214
        LD      (IX+$0E),H      ; the descriptor.
2215
        JR      L0710           ; forward to SA-TYPE-3
2216
 
2217
; ---
2218
; the branch was here to consider CODE.
2219
 
2220
;; SA-CODE
2221
L06C3:  CP      $AF             ; is character the token 'CODE' ?
2222
        JR      NZ,L0716        ; forward if not to SA-LINE to consider an
2223
                                ; auto-started BASIC program.
2224
 
2225
        LD      A,($5C74)       ; fetch command from T_ADDR
2226
        CP      $03             ; is it MERGE ?
2227
        JP      Z,L1C8A         ; jump forward to REPORT-C if so.
2228
                                ; 'Nonsense in BASIC'
2229
 
2230
 
2231
        RST     20H             ; NEXT-CHAR advances character address.
2232
        CALL    L2048           ; routine PR-ST-END checks if a carriage
2233
                                ; return or ':' follows.
2234
        JR      NZ,L06E1        ; forward to SA-CODE-1 if there are parameters.
2235
 
2236
        LD      A,($5C74)       ; else fetch the command from T_ADDR.
2237
        AND     A               ; test for zero - SAVE without a specification.
2238
        JP      Z,L1C8A         ; jump to REPORT-C if so.
2239
                                ; 'Nonsense in BASIC'
2240
 
2241
; for LOAD/VERIFY put zero on stack to signify handle at location saved from.
2242
 
2243
        CALL    L1CE6           ; routine USE-ZERO
2244
        JR      L06F0           ; forward to SA-CODE-2
2245
 
2246
; ---
2247
; if there are more characters after CODE expect start and possibly length.
2248
 
2249
;; SA-CODE-1
2250
L06E1:  CALL    L1C82           ; routine EXPT-1NUM checks for numeric
2251
                                ; expression and stacks it in run-time.
2252
 
2253
        RST     18H             ; GET-CHAR
2254
        CP      $2C             ; does a comma follow ?
2255
        JR      Z,L06F5         ; forward if so to SA-CODE-3
2256
 
2257
; else allow saved code to be loaded to a specified address.
2258
 
2259
        LD      A,($5C74)       ; fetch command from T_ADDR.
2260
        AND     A               ; is the command SAVE which requires length ?
2261
        JP      Z,L1C8A         ; jump to REPORT-C if so.
2262
                                ; 'Nonsense in BASIC'
2263
 
2264
; the command LOAD code may rejoin here with zero stacked as start.
2265
 
2266
;; SA-CODE-2
2267
L06F0:  CALL    L1CE6           ; routine USE-ZERO stacks zero for length.
2268
        JR      L06F9           ; forward to SA-CODE-4
2269
 
2270
; ---
2271
; the branch was here with SAVE CODE start,
2272
 
2273
;; SA-CODE-3
2274
L06F5:  RST     20H             ; NEXT-CHAR advances character address.
2275
        CALL    L1C82           ; routine EXPT-1NUM checks for expression
2276
                                ; and stacks in run-time.
2277
 
2278
; paths converge here and nothing must follow.
2279
 
2280
;; SA-CODE-4
2281
L06F9:  CALL    L1BEE           ; routine CHECK-END errors with extraneous
2282
                                ; characters and quits if checking syntax.
2283
 
2284
; in run-time there are two 16-bit parameters on the calculator stack.
2285
 
2286
        CALL    L1E99           ; routine FIND-INT2 gets length.
2287
        LD      (IX+$0B),C      ; place length
2288
        LD      (IX+$0C),B      ; in descriptor.
2289
        CALL    L1E99           ; routine FIND-INT2 gets start.
2290
        LD      (IX+$0D),C      ; place start
2291
        LD      (IX+$0E),B      ; in descriptor.
2292
        LD      H,B             ; transfer the
2293
        LD      L,C             ; start to HL also.
2294
 
2295
;; SA-TYPE-3
2296
L0710:  LD      (IX+$00),$03    ; place type 3 - code in descriptor.
2297
        JR      L075A           ; forward to SA-ALL.
2298
 
2299
; ---
2300
; the branch was here with BASIC to consider an optional auto-start line
2301
; number.
2302
 
2303
;; SA-LINE
2304
L0716:  CP      $CA             ; is character the token 'LINE' ?
2305
        JR      Z,L0723         ; forward to SA-LINE-1 if so.
2306
 
2307
; else all possibilities have been considered and nothing must follow.
2308
 
2309
        CALL    L1BEE           ; routine CHECK-END
2310
 
2311
; continue in run-time to save BASIC without auto-start.
2312
 
2313
        LD      (IX+$0E),$80    ; place high line number in descriptor to
2314
                                ; disable auto-start.
2315
        JR      L073A           ; forward to SA-TYPE-0 to save program.
2316
 
2317
; ---
2318
; the branch was here to consider auto-start.
2319
 
2320
;; SA-LINE-1
2321
L0723:  LD      A,($5C74)       ; fetch command from T_ADDR
2322
        AND     A               ; test for SAVE.
2323
        JP      NZ,L1C8A        ; jump forward to REPORT-C with anything else.
2324
                                ; 'Nonsense in BASIC'
2325
 
2326
;
2327
 
2328
        RST     20H             ; NEXT-CHAR
2329
        CALL    L1C82           ; routine EXPT-1NUM checks for numeric
2330
                                ; expression and stacks in run-time.
2331
        CALL    L1BEE           ; routine CHECK-END quits if syntax path.
2332
        CALL    L1E99           ; routine FIND-INT2 fetches the numeric
2333
                                ; expression.
2334
        LD      (IX+$0D),C      ; place the auto-start
2335
        LD      (IX+$0E),B      ; line number in the descriptor.
2336
 
2337
; Note. this isn't checked, but is subsequently handled by the system.
2338
; If the user typed 40000 instead of 4000 then it won't auto-start
2339
; at line 4000, or indeed, at all.
2340
 
2341
; continue to save program and any variables.
2342
 
2343
;; SA-TYPE-0
2344
L073A:  LD      (IX+$00),$00    ; place type zero - program in descriptor.
2345
        LD      HL,($5C59)      ; fetch E_LINE to HL.
2346
        LD      DE,($5C53)      ; fetch PROG to DE.
585 savelij 2347
        SCF                     ; set carry flag to calculate from end of
384 savelij 2348
                                ; variables E_LINE -1.
585 savelij 2349
        SBC     HL,DE           ; subtract to give total length.
384 savelij 2350
 
2351
        LD      (IX+$0B),L      ; place total length
2352
        LD      (IX+$0C),H      ; in descriptor.
2353
        LD      HL,($5C4B)      ; load HL from system variable VARS
2354
        SBC     HL,DE           ; subtract to give program length.
2355
        LD      (IX+$0F),L      ; place length of program
2356
        LD      (IX+$10),H      ; in the descriptor.
2357
        EX      DE,HL           ; start to HL, length to DE.
2358
 
2359
;; SA-ALL
2360
L075A:  LD      A,($5C74)       ; fetch command from T_ADDR
2361
        AND     A               ; test for zero - SAVE.
2362
        JP      Z,L0970         ; jump forward to SA-CONTRL with SAVE  ->
2363
 
2364
; ---
2365
; continue with LOAD, MERGE and VERIFY.
2366
 
2367
        PUSH    HL              ; save start.
2368
        LD      BC,$0011        ; prepare to add seventeen
2369
        ADD     IX,BC           ; to point IX at second descriptor.
2370
 
2371
;; LD-LOOK-H
2372
L0767:  PUSH    IX              ; save IX
2373
        LD      DE,$0011        ; seventeen bytes
2374
        XOR     A               ; reset zero flag
2375
        SCF                     ; set carry flag
2376
        CALL    L0556           ; routine LD-BYTES loads a header from tape
2377
                                ; to second descriptor.
2378
        POP     IX              ; restore IX.
2379
        JR      NC,L0767        ; loop back to LD-LOOK-H until header found.
2380
 
2381
        LD      A,$FE           ; select system channel 'S'
2382
        CALL    L1601           ; routine CHAN-OPEN opens it.
2383
 
2384
        LD      (IY+$52),$03    ; set SCR_CT to 3 lines.
2385
 
2386
        LD      C,$80           ; C has bit 7 set to indicate type mismatch as
2387
                                ; a default startpoint.
2388
 
2389
        LD      A,(IX+$00)      ; fetch loaded header type to A
2390
        CP      (IX-$11)        ; compare with expected type.
2391
        JR      NZ,L078A        ; forward to LD-TYPE with mis-match.
2392
 
2393
        LD      C,$F6           ; set C to minus ten - will count characters
2394
                                ; up to zero.
2395
 
2396
;; LD-TYPE
2397
L078A:  CP      $04             ; check if type in acceptable range 0 - 3.
2398
        JR      NC,L0767        ; back to LD-LOOK-H with 4 and over.
2399
 
2400
; else A indicates type 0-3.
2401
 
2402
        LD      DE,L09C0        ; address base of last 4 tape messages
2403
        PUSH    BC              ; save BC
2404
        CALL    L0C0A           ; routine PO-MSG outputs relevant message.
2405
                                ; Note. all messages have a leading newline.
2406
        POP     BC              ; restore BC
2407
 
2408
        PUSH    IX              ; transfer IX,
2409
        POP     DE              ; the 2nd descriptor, to DE.
2410
        LD      HL,$FFF0        ; prepare minus seventeen.
2411
        ADD     HL,DE           ; add to point HL to 1st descriptor.
2412
        LD      B,$0A           ; the count will be ten characters for the
2413
                                ; filename.
2414
 
2415
        LD      A,(HL)          ; fetch first character and test for 
2416
        INC     A               ; value 255.
2417
        JR      NZ,L07A6        ; forward to LD-NAME if not the wildcard.
2418
 
2419
; but if it is the wildcard, then add ten to C which is minus ten for a type
2420
; match or -128 for a type mismatch. Although characters have to be counted
2421
; bit 7 of C will not alter from state set here.
2422
 
2423
        LD      A,C             ; transfer $F6 or $80 to A
2424
        ADD     A,B             ; add $0A
2425
        LD      C,A             ; place result, zero or -118, in C.
2426
 
2427
; At this point we have either a type mismatch, a wildcard match or ten
2428
; characters to be counted. The characters must be shown on the screen.
2429
 
2430
;; LD-NAME
2431
L07A6:  INC     DE              ; address next input character
2432
        LD      A,(DE)          ; fetch character
2433
        CP      (HL)            ; compare to expected
2434
        INC     HL              ; address next expected character
2435
        JR      NZ,L07AD        ; forward to LD-CH-PR with mismatch
2436
 
2437
        INC     C               ; increment matched character count
2438
 
2439
;; LD-CH-PR
2440
L07AD:  RST     10H             ; PRINT-A prints character
2441
        DJNZ    L07A6           ; loop back to LD-NAME for ten characters.
2442
 
2443
; if ten characters matched and the types previously matched then C will 
2444
; now hold zero.
2445
 
2446
        BIT     7,C             ; test if all matched
2447
        JR      NZ,L0767        ; back to LD-LOOK-H if not
2448
 
2449
; else print a terminal carriage return.
2450
 
2451
        LD      A,$0D           ; prepare carriage return.
2452
        RST     10H             ; PRINT-A outputs it.
2453
 
2454
; The various control routines for LOAD, VERIFY and MERGE are executed 
2455
; during the one-second gap following the header on tape.
2456
 
2457
        POP     HL              ; restore xx
2458
        LD      A,(IX+$00)      ; fetch incoming type 
2459
        CP      $03             ; compare with CODE
2460
        JR      Z,L07CB         ; forward to VR-CONTROL if it is CODE.
2461
 
2462
;  type is a program or an array.
2463
 
2464
        LD      A,($5C74)       ; fetch command from T_ADDR
2465
        DEC     A               ; was it LOAD ?
2466
        JP      Z,L0808         ; JUMP forward to LD-CONTRL if so to 
2467
                                ; load BASIC or variables.
2468
 
2469
        CP      $02             ; was command MERGE ?
2470
        JP      Z,L08B6         ; jump forward to ME-CONTRL if so.
2471
 
2472
; else continue into VERIFY control routine to verify.
2473
 
2474
; ---------------------
2475
; Handle VERIFY control
2476
; ---------------------
2477
; There are two branches to this routine.
2478
; 1) From above to verify a program or array
2479
; 2) from earlier with no carry to load or verify code.
2480
 
2481
;; VR-CONTROL
2482
L07CB:  PUSH    HL              ; save pointer to data.
2483
        LD      L,(IX-$06)      ; fetch length of old data 
2484
        LD      H,(IX-$05)      ; to HL.
2485
        LD      E,(IX+$0B)      ; fetch length of new data
2486
        LD      D,(IX+$0C)      ; to DE.
2487
        LD      A,H             ; check length of old
2488
        OR      L               ; for zero.
2489
        JR      Z,L07E9         ; forward to VR-CONT-1 if length unspecified
2490
                                ; e.g LOAD "x" CODE
2491
 
2492
; as opposed to, say, LOAD 'x' CODE 32768,300.
2493
 
2494
        SBC     HL,DE           ; subtract the two lengths.
2495
        JR      C,L0806         ; forward to REPORT-R if the length on tape is 
2496
                                ; larger than that specified in command.
2497
                                ; 'Tape loading error'
2498
 
2499
        JR      Z,L07E9         ; forward to VR-CONT-1 if lengths match.
2500
 
2501
; a length on tape shorter than expected is not allowed for CODE
2502
 
2503
        LD      A,(IX+$00)      ; else fetch type from tape.
2504
        CP      $03             ; is it CODE ?
2505
        JR      NZ,L0806        ; forward to REPORT-R if so
2506
                                ; 'Tape loading error'
2507
 
2508
;; VR-CONT-1
2509
L07E9:  POP     HL              ; pop pointer to data
2510
        LD      A,H             ; test for zero
2511
        OR      L               ; e.g. LOAD 'x' CODE
2512
        JR      NZ,L07F4        ; forward to VR-CONT-2 if destination specified.
2513
 
2514
        LD      L,(IX+$0D)      ; else use the destination in the header
2515
        LD      H,(IX+$0E)      ; and load code at address saved from.
2516
 
2517
;; VR-CONT-2
2518
L07F4:  PUSH    HL              ; push pointer to start of data block.
2519
        POP     IX              ; transfer to IX.
2520
        LD      A,($5C74)       ; fetch reduced command from T_ADDR
2521
        CP      $02             ; is it VERIFY ?
2522
        SCF                     ; prepare a set carry flag
2523
        JR      NZ,L0800        ; skip to VR-CONT-3 if not
2524
 
2525
        AND     A               ; clear carry flag for VERIFY so that 
2526
                                ; data is not loaded.
2527
 
2528
;; VR-CONT-3
2529
L0800:  LD      A,$FF           ; signal data block to be loaded
2530
 
2531
; -----------------
2532
; Load a data block
2533
; -----------------
2534
; This routine is called from 3 places other than above to load a data block.
2535
; In all cases the accumulator is first set to $FF so the routine could be 
2536
; called at the previous instruction.
2537
 
2538
;; LD-BLOCK
2539
L0802:  CALL    L0556           ; routine LD-BYTES
2540
        RET     C               ; return if successful.
2541
 
2542
 
2543
;; REPORT-R
2544
L0806:  RST     08H             ; ERROR-1
2545
        DB    $1A             ; Error Report: Tape loading error
2546
 
2547
; -------------------
2548
; Handle LOAD control
2549
; -------------------
2550
; This branch is taken when the command is LOAD with type 0, 1 or 2. 
2551
 
2552
;; LD-CONTRL
2553
L0808:  LD      E,(IX+$0B)      ; fetch length of found data block 
2554
        LD      D,(IX+$0C)      ; from 2nd descriptor.
2555
        PUSH    HL              ; save destination
2556
        LD      A,H             ; test for zero
2557
        OR      L               ;
2558
        JR      NZ,L0819        ; forward if not to LD-CONT-1
2559
 
2560
        INC     DE              ; increase length
2561
        INC     DE              ; for letter name
2562
        INC     DE              ; and 16-bit length
2563
        EX      DE,HL           ; length to HL, 
2564
        JR      L0825           ; forward to LD-CONT-2
2565
 
2566
; ---
2567
 
2568
;; LD-CONT-1
2569
L0819:  LD      L,(IX-$06)      ; fetch length from 
2570
        LD      H,(IX-$05)      ; the first header.
2571
        EX      DE,HL           ;
2572
        SCF                     ; set carry flag
2573
        SBC     HL,DE           ;
2574
        JR      C,L082E         ; to LD-DATA
2575
 
2576
;; LD-CONT-2
2577
L0825:  LD      DE,$0005        ; allow overhead of five bytes.
2578
        ADD     HL,DE           ; add in the difference in data lengths.
2579
        LD      B,H             ; transfer to
2580
        LD      C,L             ; the BC register pair
2581
        CALL    L1F05           ; routine TEST-ROOM fails if not enough room.
2582
 
2583
;; LD-DATA
2584
L082E:  POP     HL              ; pop destination
2585
        LD      A,(IX+$00)      ; fetch type 0, 1 or 2.
2586
        AND     A               ; test for program and variables.
2587
        JR      Z,L0873         ; forward if so to LD-PROG
2588
 
2589
; the type is a numeric or string array.
2590
 
2591
        LD      A,H             ; test the destination for zero
2592
        OR      L               ; indicating variable does not already exist.
2593
        JR      Z,L084C         ; forward if so to LD-DATA-1
2594
 
2595
; else the destination is the first dimension within the array structure
2596
 
2597
        DEC     HL              ; address high byte of total length
2598
        LD      B,(HL)          ; transfer to B.
2599
        DEC     HL              ; address low byte of total length.
2600
        LD      C,(HL)          ; transfer to C.
2601
        DEC     HL              ; point to letter of variable.
2602
        INC     BC              ; adjust length to
2603
        INC     BC              ; include these
2604
        INC     BC              ; three bytes also.
2605
        LD      ($5C5F),IX      ; save header pointer in X_PTR.
2606
        CALL    L19E8           ; routine RECLAIM-2 reclaims the old variable
2607
                                ; sliding workspace including the two headers 
2608
                                ; downwards.
2609
        LD      IX,($5C5F)      ; reload IX from X_PTR which will have been
2610
                                ; adjusted down by POINTERS routine.
2611
 
2612
;; LD-DATA-1
2613
L084C:  LD      HL,($5C59)      ; address E_LINE
2614
        DEC     HL              ; now point to the $80 variables end-marker.
2615
        LD      C,(IX+$0B)      ; fetch new data length 
2616
        LD      B,(IX+$0C)      ; from 2nd header.
2617
        PUSH    BC              ; * save it.
2618
        INC     BC              ; adjust the 
2619
        INC     BC              ; length to include
2620
        INC     BC              ; letter name and total length.
2621
        LD      A,(IX-$03)      ; fetch letter name from old header.
2622
        PUSH    AF              ; preserve accumulator though not corrupted.
2623
 
2624
        CALL    L1655           ; routine MAKE-ROOM creates space for variable
2625
                                ; sliding workspace up. IX no longer addresses
2626
                                ; anywhere meaningful.
2627
        INC     HL              ; point to first new location.
2628
 
2629
        POP     AF              ; fetch back the letter name.
2630
        LD      (HL),A          ; place in first new location.
2631
        POP     DE              ; * pop the data length.
2632
        INC     HL              ; address 2nd location
2633
        LD      (HL),E          ; store low byte of length.
2634
        INC     HL              ; address next.
2635
        LD      (HL),D          ; store high byte.
2636
        INC     HL              ; address start of data.
2637
        PUSH    HL              ; transfer address
2638
        POP     IX              ; to IX register pair.
2639
        SCF                     ; set carry flag indicating load not verify.
2640
        LD      A,$FF           ; signal data not header.
2641
        JP      L0802           ; JUMP back to LD-BLOCK
2642
 
2643
; -----------------
2644
; the branch is here when a program as opposed to an array is to be loaded.
2645
 
2646
;; LD-PROG
2647
L0873:  EX      DE,HL           ; transfer dest to DE.
2648
        LD      HL,($5C59)      ; address E_LINE
2649
        DEC     HL              ; now variables end-marker.
2650
        LD      ($5C5F),IX      ; place the IX header pointer in X_PTR
2651
        LD      C,(IX+$0B)      ; get new length
2652
        LD      B,(IX+$0C)      ; from 2nd header
2653
        PUSH    BC              ; and save it.
2654
 
2655
        CALL    L19E5           ; routine RECLAIM-1 reclaims program and vars.
2656
                                ; adjusting X-PTR.
2657
 
2658
        POP     BC              ; restore new length.
2659
        PUSH    HL              ; * save start
2660
        PUSH    BC              ; ** and length.
2661
 
2662
        CALL    L1655           ; routine MAKE-ROOM creates the space.
2663
 
2664
        LD      IX,($5C5F)      ; reload IX from adjusted X_PTR
2665
        INC     HL              ; point to start of new area.
2666
        LD      C,(IX+$0F)      ; fetch length of BASIC on tape
2667
        LD      B,(IX+$10)      ; from 2nd descriptor
2668
        ADD     HL,BC           ; add to address the start of variables.
2669
        LD      ($5C4B),HL      ; set system variable VARS
2670
 
2671
        LD      H,(IX+$0E)      ; fetch high byte of autostart line number.
2672
        LD      A,H             ; transfer to A
2673
        AND     $C0             ; test if greater than $3F.
2674
        JR      NZ,L08AD        ; forward to LD-PROG-1 if so with no autostart.
2675
 
2676
        LD      L,(IX+$0D)      ; else fetch the low byte.
2677
        LD      ($5C42),HL      ; set sytem variable to line number NEWPPC
2678
        LD      (IY+$0A),$00    ; set statement NSPPC to zero.
2679
 
2680
;; LD-PROG-1
2681
L08AD:  POP     DE              ; ** pop the length
2682
        POP     IX              ; * and start.
2683
        SCF                     ; set carry flag
2684
        LD      A,$FF           ; signal data as opposed to a header.
2685
        JP      L0802           ; jump back to LD-BLOCK
2686
 
2687
; --------------------
2688
; Handle MERGE control
2689
; --------------------
2690
; the branch was here to merge a program and its variables or an array.
2691
;
2692
 
2693
;; ME-CONTRL
2694
L08B6:  LD      C,(IX+$0B)      ; fetch length
2695
        LD      B,(IX+$0C)      ; of data block on tape.
2696
        PUSH    BC              ; save it.
2697
        INC     BC              ; one for the pot.
2698
 
2699
        RST     30H             ; BC-SPACES creates room in workspace.
2700
                                ; HL addresses last new location.
2701
        LD      (HL),$80        ; place end-marker at end.
2702
        EX      DE,HL           ; transfer first location to HL.
2703
        POP     DE              ; restore length to DE.
2704
        PUSH    HL              ; save start.
2705
 
2706
        PUSH    HL              ; and transfer it
2707
        POP     IX              ; to IX register.
2708
        SCF                     ; set carry flag to load data on tape.
2709
        LD      A,$FF           ; signal data not a header.
2710
        CALL    L0802           ; routine LD-BLOCK loads to workspace.
2711
        POP     HL              ; restore first location in workspace to HL.
2712
X08CE   LD      DE,($5C53)      ; set DE from system variable PROG.
2713
 
2714
; now enter a loop to merge the data block in workspace with the program and 
2715
; variables. 
2716
 
2717
;; ME-NEW-LP
2718
L08D2:  LD      A,(HL)          ; fetch next byte from workspace.
2719
        AND     $C0             ; compare with $3F.
2720
        JR      NZ,L08F0        ; forward to ME-VAR-LP if a variable or 
2721
                                ; end-marker.
2722
 
2723
; continue when HL addresses a BASIC line number.
2724
 
2725
;; ME-OLD-LP
2726
L08D7:  LD      A,(DE)          ; fetch high byte from program area.
2727
        INC     DE              ; bump prog address.
2728
        CP      (HL)            ; compare with that in workspace.
2729
        INC     HL              ; bump workspace address.
2730
        JR      NZ,L08DF        ; forward to ME-OLD-L1 if high bytes don't match
2731
 
2732
        LD      A,(DE)          ; fetch the low byte of program line number.
2733
        CP      (HL)            ; compare with that in workspace.
2734
 
2735
;; ME-OLD-L1
2736
L08DF:  DEC     DE              ; point to start of
2737
        DEC     HL              ; respective lines again.
2738
        JR      NC,L08EB        ; forward to ME-NEW-L2 if line number in 
2739
                                ; workspace is less than or equal to current
2740
                                ; program line as has to be added to program.
2741
 
2742
        PUSH    HL              ; else save workspace pointer. 
2743
        EX      DE,HL           ; transfer prog pointer to HL
2744
        CALL    L19B8           ; routine NEXT-ONE finds next line in DE.
2745
        POP     HL              ; restore workspace pointer
2746
        JR      L08D7           ; back to ME-OLD-LP until destination position 
2747
                                ; in program area found.
2748
 
2749
; ---
2750
; the branch was here with an insertion or replacement point.
2751
 
2752
;; ME-NEW-L2
2753
L08EB:  CALL    L092C           ; routine ME-ENTER enters the line
2754
        JR      L08D2           ; loop back to ME-NEW-LP.
2755
 
2756
; ---
2757
; the branch was here when the location in workspace held a variable.
2758
 
2759
;; ME-VAR-LP
2760
L08F0:  LD      A,(HL)          ; fetch first byte of workspace variable.
2761
        LD      C,A             ; copy to C also.
2762
        CP      $80             ; is it the end-marker ?
2763
        RET     Z               ; return if so as complete.  >>>>>
2764
 
2765
        PUSH    HL              ; save workspace area pointer.
2766
        LD      HL,($5C4B)      ; load HL with VARS - start of variables area.
2767
 
2768
;; ME-OLD-VP
2769
L08F9:  LD      A,(HL)          ; fetch first byte.
2770
        CP      $80             ; is it the end-marker ?
2771
        JR      Z,L0923         ; forward if so to ME-VAR-L2 to add
2772
                                ; variable at end of variables area.
2773
 
2774
        CP      C               ; compare with variable in workspace area.
2775
        JR      Z,L0909         ; forward to ME-OLD-V2 if a match to replace.
2776
 
2777
; else entire variables area has to be searched.
2778
 
2779
;; ME-OLD-V1
2780
L0901:  PUSH    BC              ; save character in C.
2781
        CALL    L19B8           ; routine NEXT-ONE gets following variable 
2782
                                ; address in DE.
2783
        POP     BC              ; restore character in C
2784
        EX      DE,HL           ; transfer next address to HL.
2785
        JR      L08F9           ; loop back to ME-OLD-VP
2786
 
2787
; --- 
2788
; the branch was here when first characters of name matched. 
2789
 
2790
;; ME-OLD-V2
2791
L0909:  AND     $E0             ; keep bits 11100000
2792
        CP      $A0             ; compare   10100000 - a long-named variable.
2793
 
2794
        JR      NZ,L0921        ; forward to ME-VAR-L1 if just one-character.
2795
 
2796
; but long-named variables have to be matched character by character.
2797
 
2798
        POP     DE              ; fetch workspace 1st character pointer
2799
        PUSH    DE              ; and save it on the stack again.
2800
        PUSH    HL              ; save variables area pointer on stack.
2801
 
2802
;; ME-OLD-V3
2803
L0912:  INC     HL              ; address next character in vars area.
2804
        INC     DE              ; address next character in workspace area.
2805
        LD      A,(DE)          ; fetch workspace character.
2806
        CP      (HL)            ; compare to variables character.
2807
        JR      NZ,L091E        ; forward to ME-OLD-V4 with a mismatch.
2808
 
2809
        RLA                     ; test if the terminal inverted character.
2810
        JR      NC,L0912        ; loop back to ME-OLD-V3 if more to test.
2811
 
2812
; otherwise the long name matches in its entirety.
2813
 
2814
        POP     HL              ; restore pointer to first character of variable
2815
        JR      L0921           ; forward to ME-VAR-L1
2816
 
2817
; ---
2818
; the branch is here when two characters don't match
2819
 
2820
;; ME-OLD-V4
2821
L091E:  POP     HL              ; restore the prog/vars pointer.
2822
        JR      L0901           ; back to ME-OLD-V1 to resume search.
2823
 
2824
; ---
2825
; branch here when variable is to replace an existing one
2826
 
2827
;; ME-VAR-L1
2828
L0921:  LD      A,$FF           ; indicate a replacement.
2829
 
2830
; this entry point is when A holds $80 indicating a new variable.
2831
 
2832
;; ME-VAR-L2
2833
L0923:  POP     DE              ; pop workspace pointer.
2834
        EX      DE,HL           ; now make HL workspace pointer, DE vars pointer
2835
        INC     A               ; zero flag set if replacement.
2836
        SCF                     ; set carry flag indicating a variable not a
2837
                                ; program line.
2838
        CALL    L092C           ; routine ME-ENTER copies variable in.
2839
        JR      L08F0           ; loop back to ME-VAR-LP
2840
 
2841
; ------------------------
2842
; Merge a Line or Variable
2843
; ------------------------
2844
; A BASIC line or variable is inserted at the current point. If the line numbers
2845
; or variable names match (zero flag set) then a replacement takes place.
2846
 
2847
;; ME-ENTER
2848
L092C:  JR      NZ,L093E        ; forward to ME-ENT-1 for insertion only.
2849
 
2850
; but the program line or variable matches so old one is reclaimed.
2851
 
2852
        EX      AF,AF'          ; save flag??
2853
        LD      ($5C5F),HL      ; preserve workspace pointer in dynamic X_PTR
2854
        EX      DE,HL           ; transfer program dest pointer to HL.
2855
        CALL    L19B8           ; routine NEXT-ONE finds following location
2856
                                ; in program or variables area.
2857
        CALL    L19E8           ; routine RECLAIM-2 reclaims the space between.
2858
        EX      DE,HL           ; transfer program dest pointer back to DE.
2859
        LD      HL,($5C5F)      ; fetch adjusted workspace pointer from X_PTR
2860
        EX      AF,AF'          ; restore flags.
2861
 
2862
; now the new line or variable is entered.
2863
 
2864
;; ME-ENT-1
2865
L093E:  EX      AF,AF'          ; save or re-save flags.
2866
        PUSH    DE              ; save dest pointer in prog/vars area.
2867
        CALL    L19B8           ; routine NEXT-ONE finds next in workspace.
2868
                                ; gets next in DE, difference in BC.
2869
                                ; prev addr in HL
2870
        LD      ($5C5F),HL      ; store pointer in X_PTR
2871
        LD      HL,($5C53)      ; load HL from system variable PROG
2872
        EX      (SP),HL         ; swap with prog/vars pointer on stack.
2873
        PUSH    BC              ; ** save length of new program line/variable.
2874
        EX      AF,AF'          ; fetch flags back.
2875
        JR      C,L0955         ; skip to ME-ENT-2 if variable
2876
 
2877
        DEC     HL              ; address location before pointer
2878
        CALL    L1655           ; routine MAKE-ROOM creates room for BASIC line
2879
        INC     HL              ; address next.
2880
        JR      L0958           ; forward to ME-ENT-3
2881
 
2882
; ---
2883
 
2884
;; ME-ENT-2
2885
L0955:  CALL    L1655           ; routine MAKE-ROOM creates room for variable.
2886
 
2887
;; ME-ENT-3
2888
L0958:  INC     HL              ; address next?
2889
 
2890
        POP     BC              ; ** pop length
2891
        POP     DE              ; * pop value for PROG which may have been 
2892
                                ; altered by POINTERS if first line.
2893
        LD      ($5C53),DE      ; set PROG to original value.
2894
        LD      DE,($5C5F)      ; fetch adjusted workspace pointer from X_PTR
2895
        PUSH    BC              ; save length
2896
        PUSH    DE              ; and workspace pointer
2897
        EX      DE,HL           ; make workspace pointer source, prog/vars
2898
                                ; pointer the destination
2899
        LDIR                    ; copy bytes of line or variable into new area.
2900
        POP     HL              ; restore workspace pointer.
2901
        POP     BC              ; restore length.
2902
        PUSH    DE              ; save new prog/vars pointer.
2903
        CALL    L19E8           ; routine RECLAIM-2 reclaims the space used
2904
                                ; by the line or variable in workspace block
2905
                                ; as no longer required and space could be 
2906
                                ; useful for adding more lines.
2907
        POP     DE              ; restore the prog/vars pointer
2908
        RET                     ; return.
2909
 
2910
; -------------------
2911
; Handle SAVE control
2912
; -------------------
2913
; A branch from the main SAVE-ETC routine at SAVE-ALL.
2914
; First the header data is saved. Then after a wait of 1 second
2915
; the data itself is saved.
2916
; HL points to start of data.
2917
; IX points to start of descriptor.
2918
 
2919
;; SA-CONTRL
2920
L0970:  PUSH    HL              ; save start of data
2921
 
2922
        LD      A,$FD           ; select system channel 'S'
2923
        CALL    L1601           ; routine CHAN-OPEN
2924
 
2925
        XOR     A               ; clear to address table directly
2926
        LD      DE,L09A1        ; address: tape-msgs
2927
        CALL    L0C0A           ; routine PO-MSG -
2928
                                ; 'Start tape then press any key.'
2929
 
2930
        SET     5,(IY+$02)      ; TV_FLAG  - Signal lower screen requires
2931
                                ; clearing
2932
        CALL    L15D4           ; routine WAIT-KEY
2933
 
2934
        PUSH    IX              ; save pointer to descriptor.
2935
        LD      DE,$0011        ; there are seventeen bytes.
2936
        XOR     A               ; signal a header.
2937
        CALL    L04C2           ; routine SA-BYTES
2938
 
2939
        POP     IX              ; restore descriptor pointer.
2940
 
2941
        LD      B,$32           ; wait for a second - 50 interrupts.
2942
 
2943
;; SA-1-SEC
2944
L0991:  HALT                    ; wait for interrupt
2945
        DJNZ    L0991           ; back to SA-1-SEC until pause complete.
2946
 
2947
        LD      E,(IX+$0B)      ; fetch length of bytes from the
2948
        LD      D,(IX+$0C)      ; descriptor.
2949
 
2950
        LD      A,$FF           ; signal data bytes.
2951
 
2952
        POP     IX              ; retrieve pointer to start
2953
        JP      L04C2           ; jump back to SA-BYTES
2954
 
2955
 
2956
; Arrangement of two headers in workspace.
2957
; Originally IX addresses first location and only one header is required
2958
; when saving.
2959
;
2960
;   OLD     NEW         PROG   DATA  DATA  CODE 
2961
;   HEADER  HEADER             num   chr          NOTES.
2962
;   ------  ------      ----   ----  ----  ----   -----------------------------
2963
;   IX-$11  IX+$00      0      1     2     3      Type.
2964
;   IX-$10  IX+$01      x      x     x     x      F  ($FF if filename is null).
2965
;   IX-$0F  IX+$02      x      x     x     x      i
2966
;   IX-$0E  IX+$03      x      x     x     x      l
2967
;   IX-$0D  IX+$04      x      x     x     x      e
2968
;   IX-$0C  IX+$05      x      x     x     x      n
2969
;   IX-$0B  IX+$06      x      x     x     x      a
2970
;   IX-$0A  IX+$07      x      x     x     x      m
2971
;   IX-$09  IX+$08      x      x     x     x      e
2972
;   IX-$08  IX+$09      x      x     x     x      .
2973
;   IX-$07  IX+$0A      x      x     x     x      (terminal spaces).
2974
;   IX-$06  IX+$0B      lo     lo    lo    lo     Total  
2975
;   IX-$05  IX+$0C      hi     hi    hi    hi     Length of datablock.
2976
;   IX-$04  IX+$0D      Auto   -     -     Start  Various
2977
;   IX-$03  IX+$0E      Start  a-z   a-z   addr   ($80 if no autostart).
2978
;   IX-$02  IX+$0F      lo     -     -     -      Length of Program 
2979
;   IX-$01  IX+$10      hi     -     -     -      only i.e. without variables.
2980
;
2981
 
2982
 
2983
; ------------------------
2984
; Canned cassette messages
2985
; ------------------------
2986
; The last-character-inverted Cassette messages.
2987
; Starts with normal initial step-over byte.
2988
 
2989
;; tape-msgs
2990
L09A1           DB $80
2991
                DC "Start tape, then press any key."    ;DEFM    "Start tape, then press any key"
2992
L09C0           EQU $-1                                 ;DB    '.'+$80
2993
                DB $0D
2994
                DC "Program: "                          ;DEFM    "Program:"
2995
                                                        ;DB    ' '+$80
2996
                DB $0D
2997
                DC "Number array: "                     ;DEFM    "Number array:"
2998
                                                        ;DB    ' '+$80
2999
                DB $0D
3000
                DC "Character array: "                  ;DEFM    "Character array:"
3001
                                                        ;DB    ' '+$80
3002
                DB $0D
3003
                DC "Bytes: "                            ;DEFM    "Bytes:"
3004
                                                        ;DB    ' '+$80
3005
;               DB    ' '+$80
3006
 
3007
 
3008
;**************************************************
3009
;** Part 5. SCREEN AND PRINTER HANDLING ROUTINES **
3010
;**************************************************
3011
 
3012
; ---------------------
3013
; General PRINT routine
3014
; ---------------------
3015
; This is the routine most often used by the RST 10 restart although the
3016
; subroutine is on two occasions called directly when it is known that
3017
; output will definitely be to the lower screen.
3018
 
3019
;; PRINT-OUT
3020
L09F4:  CALL    L0B03           ; routine PO-FETCH fetches print position
3021
                                ; to HL register pair.
3022
        CP      $20             ; is character a space or higher ?
3023
        JP      NC,L0AD9        ; jump forward to PO-ABLE if so.
3024
 
3025
        CP      $06             ; is character in range 00-05 ?
3026
        JR      C,L0A69         ; to PO-QUEST to print '?' if so.
3027
 
3028
        CP      $18             ; is character in range 24d - 31d ?
3029
        JR      NC,L0A69        ; to PO-QUEST to also print '?' if so.
3030
 
3031
        LD      HL,L0A11 - 6    ; address 0A0B - the base address of control
3032
                                ; character table - where zero would be.
3033
        LD      E,A             ; control character 06 - 23d
3034
        LD      D,$00           ; is transferred to DE.
3035
 
3036
        ADD     HL,DE           ; index into table.
3037
 
3038
        LD      E,(HL)          ; fetch the offset to routine.
3039
        ADD     HL,DE           ; add to make HL the address.
3040
        PUSH    HL              ; push the address.
3041
        JP      L0B03           ; to PO-FETCH, as the screen/printer position
3042
                                ; has been disturbed, and indirectly to
3043
                                ; routine on stack.
3044
 
3045
; -----------------------
3046
; Control character table
3047
; -----------------------
3048
; For control characters in the range 6 - 23d the following table
3049
; is indexed to provide an offset to the handling routine that
3050
; follows the table.
3051
 
3052
;; ctlchrtab
3053
L0A11:  DB    L0A5F - $       ; 06d offset $4E to Address: PO-COMMA
3054
        DB    L0A69 - $       ; 07d offset $57 to Address: PO-QUEST
3055
        DB    L0A23 - $       ; 08d offset $10 to Address: PO-BACK-1
3056
        DB    L0A3D - $       ; 09d offset $29 to Address: PO-RIGHT
3057
        DB    L0A69 - $       ; 10d offset $54 to Address: PO-QUEST
3058
        DB    L0A69 - $       ; 11d offset $53 to Address: PO-QUEST
3059
        DB    L0A69 - $       ; 12d offset $52 to Address: PO-QUEST
3060
        DB    L0A4F - $       ; 13d offset $37 to Address: PO-ENTER
3061
        DB    L0A69 - $       ; 14d offset $50 to Address: PO-QUEST
3062
        DB    L0A69 - $       ; 15d offset $4F to Address: PO-QUEST
3063
        DB    L0A7A - $       ; 16d offset $5F to Address: PO-1-OPER
3064
        DB    L0A7A - $       ; 17d offset $5E to Address: PO-1-OPER
3065
        DB    L0A7A - $       ; 18d offset $5D to Address: PO-1-OPER
3066
        DB    L0A7A - $       ; 19d offset $5C to Address: PO-1-OPER
3067
        DB    L0A7A - $       ; 20d offset $5B to Address: PO-1-OPER
3068
        DB    L0A7A - $       ; 21d offset $5A to Address: PO-1-OPER
3069
        DB    L0A75 - $       ; 22d offset $54 to Address: PO-2-OPER
3070
        DB    L0A75 - $       ; 23d offset $53 to Address: PO-2-OPER
3071
 
3072
 
3073
; -------------------
3074
; Cursor left routine
3075
; -------------------
3076
; Backspace and up a line if that action is from the left of screen.
3077
; For ZX printer backspace up to first column but not beyond.
3078
 
3079
;; PO-BACK-1
3080
L0A23:  INC     C               ; move left one column.
3081
        LD      A,$22           ; value $21 is leftmost column.
3082
        CP      C               ; have we passed ?
3083
        JR      NZ,L0A3A        ; to PO-BACK-3 if not and store new position.
3084
 
3085
        BIT     1,(IY+$01)      ; test FLAGS  - is printer in use ?
3086
        JR      NZ,L0A38        ; to PO-BACK-2 if so, as we are unable to
3087
                                ; backspace from the leftmost position.
3088
 
3089
 
3090
        INC     B               ; move up one screen line
3091
        LD      C,$02           ; the rightmost column position.
3092
        LD      A,$18           ; Note. This should be $19
3093
                                ; credit. Dr. Frank O'Hara, 1982
3094
 
3095
        CP      B               ; has position moved past top of screen ?
3096
        JR      NZ,L0A3A        ; to PO-BACK-3 if not and store new position.
3097
 
3098
        DEC     B               ; else back to $18.
3099
 
3100
;; PO-BACK-2
3101
L0A38:  LD      C,$21           ; the leftmost column position.
3102
 
3103
;; PO-BACK-3
3104
L0A3A:  JP      L0DD9           ; to CL-SET and PO-STORE to save new
3105
                                ; position in system variables.
3106
 
3107
; --------------------
3108
; Cursor right routine
3109
; --------------------
3110
; This moves the print position to the right leaving a trail in the
3111
; current background colour.
3112
; "However the programmer has failed to store the new print position
3113
;  so CHR$ 9 will only work if the next print position is at a newly
3114
;  defined place.
3115
;   e.g. PRINT PAPER 2; CHR$ 9; AT 4,0;
3116
;  does work but is not very helpful"
3117
; - Dr. Ian Logan, Understanding Your Spectrum, 1982.
3118
 
3119
;; PO-RIGHT
3120
L0A3D:  LD      A,($5C91)       ; fetch P_FLAG value
3121
        PUSH    AF              ; and save it on stack.
3122
 
3123
        LD      (IY+$57),$01    ; temporarily set P_FLAG 'OVER 1'.
3124
        LD      A,$20           ; prepare a space.
3125
        CALL    L0B65           ; routine PO-CHAR to print it.
3126
                                ; Note. could be PO-ABLE which would update
3127
                                ; the column position.
3128
 
3129
        POP     AF              ; restore the permanent flag.
3130
        LD      ($5C91),A       ; and restore system variable P_FLAG
3131
 
3132
        RET                     ; return without updating column position
3133
 
3134
; -----------------------
3135
; Perform carriage return
3136
; -----------------------
3137
; A carriage return is 'printed' to screen or printer buffer.
3138
 
3139
;; PO-ENTER
3140
L0A4F:  BIT     1,(IY+$01)      ; test FLAGS  - is printer in use ?
3141
        JP      NZ,L0ECD        ; to COPY-BUFF if so, to flush buffer and reset
3142
                                ; the print position.
3143
 
3144
        LD      C,$21           ; the leftmost column position.
3145
        CALL    L0C55           ; routine PO-SCR handles any scrolling required.
3146
        DEC     B               ; to next screen line.
3147
        JP      L0DD9           ; jump forward to CL-SET to store new position.
3148
 
3149
; -----------
3150
; Print comma
3151
; -----------
3152
; The comma control character. The 32 column screen has two 16 character
3153
; tabstops.  The routine is only reached via the control character table.
3154
 
3155
;; PO-COMMA
3156
L0A5F:  CALL    L0B03           ; routine PO-FETCH - seems unnecessary.
3157
 
3158
        LD      A,C             ; the column position. $21-$01
3159
        DEC     A               ; move right. $20-$00
3160
        DEC     A               ; and again   $1F-$00 or $FF if trailing
3161
        AND     $10             ; will be $00 or $10.
3162
        JR      L0AC3           ; forward to PO-FILL
3163
 
3164
; -------------------
3165
; Print question mark
3166
; -------------------
3167
; This routine prints a question mark which is commonly
3168
; used to print an unassigned control character in range 0-31d.
3169
; there are a surprising number yet to be assigned.
3170
 
3171
;; PO-QUEST
3172
L0A69:  LD      A,$3F           ; prepare the character '?'.
3173
        JR      L0AD9           ; forward to PO-ABLE.
3174
 
3175
; --------------------------------
3176
; Control characters with operands
3177
; --------------------------------
3178
; Certain control characters are followed by 1 or 2 operands.
3179
; The entry points from control character table are PO-2-OPER and PO-1-OPER.
3180
; The routines alter the output address of the current channel so that
3181
; subsequent RST $10 instructions take the appropriate action
3182
; before finally resetting the output address back to PRINT-OUT.
3183
 
3184
;; PO-TV-2
3185
L0A6D:  LD      DE,L0A87        ; address: PO-CONT will be next output routine
3186
        LD      ($5C0F),A       ; store first operand in TVDATA-hi
3187
        JR      L0A80           ; forward to PO-CHANGE >>
3188
 
3189
; ---
3190
 
3191
; -> This initial entry point deals with two operands - AT or TAB.
3192
 
3193
;; PO-2-OPER
3194
L0A75:  LD      DE,L0A6D        ; address: PO-TV-2 will be next output routine
3195
        JR      L0A7D           ; forward to PO-TV-1
3196
 
3197
; ---
3198
 
3199
; -> This initial entry point deals with one operand INK to OVER.
3200
 
3201
;; PO-1-OPER
3202
L0A7A:  LD      DE,L0A87        ; address: PO-CONT will be next output routine
3203
 
3204
;; PO-TV-1
3205
L0A7D:  LD      ($5C0E),A       ; store control code in TVDATA-lo
3206
 
3207
;; PO-CHANGE
3208
L0A80:  LD      HL,($5C51)      ; use CURCHL to find current output channel.
3209
        LD      (HL),E          ; make it
3210
        INC     HL              ; the supplied
3211
        LD      (HL),D          ; address from DE.
3212
        RET                     ; return.
3213
 
3214
; ---
3215
 
3216
;; PO-CONT
3217
L0A87:  LD      DE,L09F4        ; Address: PRINT-OUT
3218
        CALL    L0A80           ; routine PO-CHANGE to restore normal channel.
3219
        LD      HL,($5C0E)      ; TVDATA gives control code and possible
3220
                                ; subsequent character
3221
        LD      D,A             ; save current character
3222
        LD      A,L             ; the stored control code
3223
        CP      $16             ; was it INK to OVER (1 operand) ?
3224
        JP      C,L2211         ; to CO-TEMP-5
3225
 
3226
        JR      NZ,L0AC2        ; to PO-TAB if not 22d i.e. 23d TAB.
3227
 
3228
                                ; else must have been 22d AT.
3229
        LD      B,H             ; line to H   (0-23d)
3230
        LD      C,D             ; column to C (0-31d)
3231
        LD      A,$1F           ; the value 31d
3232
        SUB     C               ; reverse the column number.
3233
        JR      C,L0AAC         ; to PO-AT-ERR if C was greater than 31d.
3234
 
3235
        ADD     A,$02           ; transform to system range $02-$21
3236
        LD      C,A             ; and place in column register.
3237
 
3238
        BIT     1,(IY+$01)      ; test FLAGS  - is printer in use ?
3239
        JR      NZ,L0ABF        ; to PO-AT-SET as line can be ignored.
3240
 
3241
        LD      A,$16           ; 22 decimal
3242
        SUB     B               ; subtract line number to reverse
3243
                                ; 0 - 22 becomes 22 - 0.
3244
 
3245
;; PO-AT-ERR
3246
L0AAC:  JP      C,L1E9F         ; to REPORT-B if higher than 22 decimal
3247
                                ; Integer out of range.
3248
 
3249
        INC     A               ; adjust for system range $01-$17
3250
        LD      B,A             ; place in line register
3251
        INC     B               ; adjust to system range  $02-$18
3252
        BIT     0,(IY+$02)      ; TV_FLAG  - Lower screen in use ?
3253
        JP      NZ,L0C55        ; exit to PO-SCR to test for scrolling
3254
 
3255
        CP      (IY+$31)        ; Compare against DF_SZ
3256
        JP      C,L0C86         ; to REPORT-5 if too low
3257
                                ; Out of screen.
3258
 
3259
;; PO-AT-SET
3260
L0ABF:  JP      L0DD9           ; print position is valid so exit via CL-SET
3261
 
3262
; Continue here when dealing with TAB.
3263
; Note. In BASIC, TAB is followed by a 16-bit number and was initially
3264
; designed to work with any output device.
3265
 
3266
;; PO-TAB
3267
L0AC2:  LD      A,H             ; transfer parameter to A
3268
                                ; Losing current character -
3269
                                ; High byte of TAB parameter.
3270
 
3271
 
3272
;; PO-FILL
3273
L0AC3:  CALL    L0B03           ; routine PO-FETCH, HL-addr, BC=line/column.
3274
                                ; column 1 (right), $21 (left)
3275
        ADD     A,C             ; add operand to current column
3276
        DEC     A               ; range 0 - 31+
3277
        AND     $1F             ; make range 0 - 31d
3278
        RET     Z               ; return if result zero
3279
 
3280
        LD      D,A             ; Counter to D
3281
        SET     0,(IY+$01)      ; update FLAGS  - signal suppress leading space.
3282
 
3283
;; PO-SPACE
3284
L0AD0:  LD      A,$20           ; space character.
3285
        CALL    L0C3B           ; routine PO-SAVE prints the character
3286
                                ; using alternate set (normal output routine)
3287
        DEC     D               ; decrement counter.
3288
        JR      NZ,L0AD0        ; to PO-SPACE until done
3289
 
3290
        RET                     ; return
3291
 
3292
; ----------------------
3293
; Printable character(s)
3294
; ----------------------
3295
; This routine prints printable characters and continues into
3296
; the position store routine
3297
 
3298
;; PO-ABLE
3299
L0AD9:  CALL    L0B24           ; routine PO-ANY
3300
                                ; and continue into position store routine.
3301
 
3302
; -------------------------------------
3303
; Store line, column, and pixel address
3304
; -------------------------------------
3305
; This routine updates the system variables associated with
3306
; The main screen, lower screen/input buffer or ZX printer.
3307
 
3308
;; PO-STORE
3309
L0ADC:  BIT     1,(IY+$01)      ; test FLAGS  - Is printer in use ?
3310
        JR      NZ,L0AFC        ; to PO-ST-PR if so
3311
 
3312
        BIT     0,(IY+$02)      ; TV_FLAG  - Lower screen in use ?
3313
        JR      NZ,L0AF0        ; to PO-ST-E if so
3314
 
3315
        LD      ($5C88),BC      ; S_POSN line/column upper screen
3316
        LD      ($5C84),HL      ; DF_CC  display file address
3317
        RET                     ;
3318
 
3319
; ---
3320
 
3321
;; PO-ST-E
3322
L0AF0:  LD      ($5C8A),BC      ; SPOSNL line/column lower screen
3323
        LD      ($5C82),BC      ; ECHO_E line/column input buffer
3324
        LD      ($5C86),HL      ; DFCCL  lower screen memory address
3325
        RET                     ;
3326
 
3327
; ---
3328
 
3329
;; PO-ST-PR
3330
L0AFC:  LD      (IY+$45),C      ; P_POSN column position printer
3331
        LD      ($5C80),HL      ; PR_CC  full printer buffer memory address
3332
        RET                     ;
3333
 
3334
; -------------------------
3335
; Fetch position parameters
3336
; -------------------------
3337
; This routine fetches the line/column and display file address
3338
; of the upper and lower screen or, if the printer is in use,
3339
; the column position and absolute memory address.
3340
; Note. that PR-CC-hi (23681) is used by this routine and the one above
3341
; and if, in accordance with the manual (that says this is unused), the
3342
; location has been used for other purposes, then subsequent output
3343
; to the printer buffer could corrupt a 256-byte section of memory.
3344
 
3345
;; PO-FETCH
3346
L0B03:  BIT     1,(IY+$01)      ; test FLAGS  - Is printer in use
3347
        JR      NZ,L0B1D        ; to PO-F-PR if so
3348
 
3349
                                ; assume upper screen
3350
        LD      BC,($5C88)      ; S_POSN
3351
        LD      HL,($5C84)      ; DF_CC display file address
3352
        BIT     0,(IY+$02)      ; TV_FLAG  - Lower screen in use ?
3353
        RET     Z               ; return if upper screen
3354
 
3355
                                ; ah well, was lower screen
3356
        LD      BC,($5C8A)      ; SPOSNL
3357
        LD      HL,($5C86)      ; DFCCL
3358
        RET                     ; return
3359
 
3360
; ---
3361
 
3362
;; PO-F-PR
3363
L0B1D:  LD      C,(IY+$45)      ; P_POSN column only
3364
        LD      HL,($5C80)      ; PR_CC printer buffer address
3365
        RET                     ; return
3366
 
3367
; -------------------
3368
; Print any character
3369
; -------------------
3370
; This routine is used to print any character in range 32d - 255d
3371
; It is only called from PO-ABLE which continues into PO-STORE
3372
 
3373
;; PO-ANY
3374
L0B24:  CP      $80             ; ASCII ?
3375
        JR      C,L0B65         ; to PO-CHAR is so.
3376
 
3377
        CP      $90             ; test if a block graphic character.
3378
        JR      NC,L0B52        ; to PO-T&UDG to print tokens and udg's
3379
 
3380
; The 16 2*2 mosaic characters 128-143 decimal are formed from
3381
; bits 0-3 of the character.
3382
 
3383
        LD      B,A             ; save character
3384
        CALL    L0B38           ; routine PO-GR-1 to construct top half
3385
                                ; then bottom half.
3386
        CALL    L0B03           ; routine PO-FETCH fetches print position.
3387
        LD      DE,$5C92        ; MEM-0 is location of 8 bytes of character
3388
        JR      L0B7F           ; to PR-ALL to print to screen or printer
3389
 
3390
; ---
3391
 
3392
;; PO-GR-1
3393
L0B38:  LD      HL,$5C92        ; address MEM-0 - a temporary buffer in
3394
                                ; systems variables which is normally used
3395
                                ; by the calculator.
3396
        CALL    L0B3E           ; routine PO-GR-2 to construct top half
3397
                                ; and continue into routine to construct
3398
                                ; bottom half.
3399
 
3400
;; PO-GR-2
3401
L0B3E:  RR      B               ; rotate bit 0/2 to carry
3402
        SBC     A,A             ; result $00 or $FF
3403
        AND     $0F             ; mask off right hand side
3404
        LD      C,A             ; store part in C
3405
        RR      B               ; rotate bit 1/3 of original chr to carry
3406
        SBC     A,A             ; result $00 or $FF
3407
        AND     $F0             ; mask off left hand side
3408
        OR      C               ; combine with stored pattern
3409
        LD      C,$04           ; four bytes for top/bottom half
3410
 
3411
;; PO-GR-3
3412
L0B4C:  LD      (HL),A          ; store bit patterns in temporary buffer
3413
        INC     HL              ; next address
3414
        DEC     C               ; jump back to
3415
        JR      NZ,L0B4C        ; to PO-GR-3 until byte is stored 4 times
3416
 
3417
        RET                     ; return
3418
 
3419
; ---
3420
 
3421
; Tokens and User defined graphics are now separated.
3422
 
3423
;; PO-T&UDG
678 savelij 3424
L0B52           JP L3B9F                ;Spectrum 128 patch
384 savelij 3425
                NOP
3426
 
3427
L0B56:  ADD     A,$15           ; add 21d to restore to 0 - 20
3428
        PUSH    BC              ; save current print position
3429
        LD      BC,($5C7B)      ; fetch UDG to address bit patterns
3430
        JR      L0B6A           ; to PO-CHAR-2 - common code to lay down
3431
                                ; a bit patterned character
3432
 
3433
; ---
3434
 
3435
;; PO-T
3436
L0B5F:  CALL    L0C10           ; routine PO-TOKENS prints tokens
3437
        JP      L0B03           ; exit via a JUMP to PO-FETCH as this routine 
3438
                                ; must continue into PO-STORE. 
3439
                                ; A JR instruction could be used.
3440
 
3441
; This point is used to print ASCII characters  32d - 127d.
3442
 
3443
;; PO-CHAR
3444
L0B65:  PUSH    BC              ; save print position
3445
        LD      BC,($5C36)      ; address CHARS
3446
 
3447
; This common code is used to transfer the character bytes to memory.
3448
 
3449
;; PO-CHAR-2
3450
L0B6A:  EX      DE,HL           ; transfer destination address to DE
3451
        LD      HL,$5C3B        ; point to FLAGS
3452
        RES     0,(HL)          ; allow for leading space
3453
        CP      $20             ; is it a space ?
3454
        JR      NZ,L0B76        ; to PO-CHAR-3 if not
3455
 
3456
        SET     0,(HL)          ; signal no leading space to FLAGS
3457
 
3458
;; PO-CHAR-3
3459
L0B76:  LD      H,$00           ; set high byte to 0
3460
        LD      L,A             ; character to A
3461
                                ; 0-21 UDG or 32-127 ASCII.
3462
        ADD     HL,HL           ; multiply
3463
        ADD     HL,HL           ; by
3464
        ADD     HL,HL           ; eight
3465
        ADD     HL,BC           ; HL now points to first byte of character
3466
        POP     BC              ; the source address CHARS or UDG
3467
        EX      DE,HL           ; character address to DE
3468
 
3469
; --------------------
3470
; Print all characters
3471
; --------------------
3472
; This entry point entered from above to print ASCII and UDGs
3473
; but also from earlier to print mosaic characters.
3474
; HL=destination
3475
; DE=character source
3476
; BC=line/column
3477
 
3478
;; PR-ALL
3479
L0B7F:  LD      A,C             ; column to A
3480
        DEC     A               ; move right
3481
        LD      A,$21           ; pre-load with leftmost position
3482
        JR      NZ,L0B93        ; but if not zero to PR-ALL-1
3483
 
3484
        DEC     B               ; down one line
3485
        LD      C,A             ; load C with $21
3486
        BIT     1,(IY+$01)      ; test FLAGS  - Is printer in use
3487
        JR      Z,L0B93         ; to PR-ALL-1 if not
3488
 
3489
        PUSH    DE              ; save source address
3490
        CALL    L0ECD           ; routine COPY-BUFF outputs line to printer
3491
        POP     DE              ; restore character source address
3492
        LD      A,C             ; the new column number ($21) to C
3493
 
3494
;; PR-ALL-1
3495
L0B93:  CP      C               ; this test is really for screen - new line ?
3496
        PUSH    DE              ; save source
3497
 
3498
        CALL    Z,L0C55         ; routine PO-SCR considers scrolling
3499
 
3500
        POP     DE              ; restore source
3501
        PUSH    BC              ; save line/column
3502
        PUSH    HL              ; and destination
3503
        LD      A,($5C91)       ; fetch P_FLAG to accumulator
3504
        LD      B,$FF           ; prepare OVER mask in B.
3505
        RRA                     ; bit 0 set if OVER 1
3506
        JR      C,L0BA4         ; to PR-ALL-2
3507
 
3508
        INC     B               ; set OVER mask to 0
3509
 
3510
;; PR-ALL-2
3511
L0BA4:  RRA                     ; skip bit 1 of P_FLAG
3512
        RRA                     ; bit 2 is INVERSE
3513
        SBC     A,A             ; will be FF for INVERSE 1 else zero
3514
        LD      C,A             ; transfer INVERSE mask to C
3515
        LD      A,$08           ; prepare to count 8 bytes
3516
        AND     A               ; clear carry to signal screen
3517
        BIT     1,(IY+$01)      ; test FLAGS  - is printer in use ?
3518
        JR      Z,L0BB6         ; to PR-ALL-3 if screen
3519
 
3520
        SET     1,(IY+$30)      ; update FLAGS2  - signal printer buffer has 
3521
                                ; been used.
3522
        SCF                     ; set carry flag to signal printer.
3523
 
3524
;; PR-ALL-3
3525
L0BB6:  EX      DE,HL           ; now HL=source, DE=destination
3526
 
3527
;; PR-ALL-4
3528
L0BB7:  EX      AF,AF'          ; save printer/screen flag
3529
        LD      A,(DE)          ; fetch existing destination byte
3530
        AND     B               ; consider OVER
3531
        XOR     (HL)            ; now XOR with source
3532
        XOR     C               ; now with INVERSE MASK
3533
        LD      (DE),A          ; update screen/printer
3534
        EX      AF,AF'          ; restore flag
3535
        JR      C,L0BD3         ; to PR-ALL-6 - printer address update
3536
 
3537
        INC     D               ; gives next pixel line down screen
3538
 
3539
;; PR-ALL-5
3540
L0BC1:  INC     HL              ; address next character byte
3541
        DEC     A               ; the byte count is decremented
3542
        JR      NZ,L0BB7        ; back to PR-ALL-4 for all 8 bytes
3543
 
3544
        EX      DE,HL           ; destination to HL
3545
        DEC     H               ; bring back to last updated screen position
3546
        BIT     1,(IY+$01)      ; test FLAGS  - is printer in use ?
3547
        CALL    Z,L0BDB         ; if not, call routine PO-ATTR to update
3548
                                ; corresponding colour attribute.
3549
        POP     HL              ; restore original screen/printer position
3550
        POP     BC              ; and line column
3551
        DEC     C               ; move column to right
3552
        INC     HL              ; increase screen/printer position
3553
        RET                     ; return and continue into PO-STORE
3554
                                ; within PO-ABLE
3555
 
3556
; ---
3557
 
3558
; This branch is used to update the printer position by 32 places
3559
; Note. The high byte of the address D remains constant (which it should).
3560
 
3561
;; PR-ALL-6
3562
L0BD3:  EX      AF,AF'          ; save the flag
3563
        LD      A,$20           ; load A with 32 decimal
3564
        ADD     A,E             ; add this to E
3565
        LD      E,A             ; and store result in E
3566
        EX      AF,AF'          ; fetch the flag
3567
        JR      L0BC1           ; back to PR-ALL-5
3568
 
3569
; -------------
3570
; Set attribute
3571
; -------------
3572
; This routine is entered with the HL register holding the last screen
3573
; address to be updated by PRINT or PLOT.
3574
; The Spectrum screen arrangement leads to the L register holding
3575
; the correct value for the attribute file and it is only necessary
3576
; to manipulate H to form the correct colour attribute address.
3577
 
3578
;; PO-ATTR
3579
L0BDB:  LD       A,H            ; fetch high byte $40 - $57
3580
        RRCA                    ; shift
3581
        RRCA                    ; bits 3 and 4
3582
        RRCA                    ; to right.
3583
        AND     $03             ; range is now 0 - 2
3584
        OR      $58             ; form correct high byte for third of screen
3585
        LD      H,A             ; HL is now correct
3586
        LD      DE,($5C8F)      ; make D hold ATTR_T, E hold MASK-T
3587
        LD      A,(HL)          ; fetch existing attribute
3588
        XOR     E               ; apply masks
3589
        AND     D               ;
3590
        XOR     E               ;
3591
        BIT     6,(IY+$57)      ; test P_FLAG  - is this PAPER 9 ??
3592
        JR      Z,L0BFA         ; skip to PO-ATTR-1 if not.
3593
 
3594
        AND     $C7             ; set paper
3595
        BIT     2,A             ; to contrast with ink
3596
        JR      NZ,L0BFA        ; skip to PO-ATTR-1
3597
 
3598
        XOR     $38             ;
3599
 
3600
;; PO-ATTR-1
3601
L0BFA:  BIT     4,(IY+$57)      ; test P_FLAG  - Is this INK 9 ??
3602
        JR      Z,L0C08         ; skip to PO-ATTR-2 if not
3603
 
3604
        AND     $F8             ; make ink
3605
        BIT     5,A             ; contrast with paper.
3606
        JR      NZ,L0C08        ; to PO-ATTR-2
3607
 
3608
        XOR     $07             ;
3609
 
3610
;; PO-ATTR-2
3611
L0C08:  LD      (HL),A          ; save the new attribute.
3612
        RET                     ; return.
3613
 
3614
; ----------------
3615
; Message printing
3616
; ----------------
3617
; This entry point is used to print tape, boot-up, scroll? and error messages
3618
; On entry the DE register points to an initial step-over byte or
3619
; the inverted end-marker of the previous entry in the table.
3620
; A contains the message number, often zero to print first message.
3621
; (HL has nothing important usually P_FLAG)
3622
 
3623
;; PO-MSG
3624
L0C0A:  PUSH    HL              ; put hi-byte zero on stack to suppress
3625
        LD      H,$00           ; trailing spaces
3626
        EX      (SP),HL         ; ld h,0; push hl would have done ?.
3627
        JR      L0C14           ; forward to PO-TABLE.
3628
 
3629
; ---
3630
 
3631
; This entry point prints the BASIC keywords, '<>' etc. from alt set
3632
 
3633
;; PO-TOKENS
3634
L0C10:  LD      DE,L0095        ; address: TKN-TABLE
3635
        PUSH    AF              ; save the token number to control
3636
                                ; trailing spaces - see later *
3637
 
3638
;; PO-TABLE
3639
L0C14:  CALL    L0C41           ; routine PO-SEARCH will set carry for
3640
                                ; all messages and function words.
3641
L0C17:  JR      C,L0C22         ; forward to PO-EACH if not a command,
3642
                                ; '<>' etc.
3643
 
3644
        LD      A,$20           ; prepare leading space
3645
        BIT     0,(IY+$01)      ; test FLAGS  - leading space if not set
3646
        CALL    Z,L0C3B         ; routine PO-SAVE to print a space
3647
                                ; without disturbing registers
3648
 
3649
;; PO-EACH
3650
L0C22:  LD      A,(DE)          ; fetch character
3651
        AND     $7F             ; remove any inverted bit
3652
        CALL    L0C3B           ; routine PO-SAVE to print using alternate
3653
                                ; set of registers.
3654
        LD      A,(DE)          ; re-fetch character.
3655
        INC     DE              ; address next
3656
        ADD     A,A             ; was character inverted ?
3657
                                ; (this also doubles character)
3658
        JR      NC,L0C22        ; back to PO-EACH if not
3659
 
3660
        POP     DE              ; * re-fetch trailing space flag to D (was A)
3661
        CP      $48             ; was last character '$' ($24*2)
3662
        JR      Z,L0C35         ; forward to PO-TR-SP to consider trailing
3663
                                ; space if so.
3664
 
3665
        CP      $82             ; was it < 'A' i.e. '#','>','=' from tokens
3666
                                ; or ' ','.' (from tape) or '?' from scroll
3667
        RET     C               ; no trailing space
3668
 
3669
;; PO-TR-SP
3670
L0C35:  LD      A,D             ; the trailing space flag (zero if an error msg)
3671
        CP      $03             ; test against RND, INKEY$ and PI
3672
                                ; which have no parameters and
3673
        RET     C               ; therefore no trailing space so return.
3674
 
3675
        LD      A,$20           ; else continue and print a trailing space.
3676
 
3677
; -------------------------
3678
; Handle recursive printing
3679
; -------------------------
3680
; This routine which is part of PRINT-OUT allows RST $10 to be
3681
; used recursively to print tokens and the spaces associated with them.
3682
 
3683
;; PO-SAVE
3684
L0C3B:  PUSH    DE              ; save DE as CALL-SUB doesn't.
3685
        EXX                     ; switch in main set
3686
 
3687
        RST     10H             ; PRINT-A prints using this alternate set.
3688
 
3689
        EXX                     ; back to this alternate set.
3690
        POP     DE              ; restore initial DE.
3691
        RET                     ; return.
3692
 
3693
; ------------
3694
; Table search
3695
; ------------
3696
; This subroutine searches a message or the token table for the
3697
; message number held in A. DE holds the address of the table.
3698
 
3699
;; PO-SEARCH
3700
L0C41:  PUSH    AF              ; save the message/token number
3701
        EX      DE,HL           ; transfer DE to HL
3702
        INC     A               ; adjust for initial step-over byte
3703
 
3704
;; PO-STEP
3705
L0C44:  BIT     7,(HL)          ; is character inverted ?
3706
        INC     HL              ; address next
3707
        JR      Z,L0C44         ; back to PO-STEP if not inverted.
3708
 
3709
        DEC     A               ; decrease counter
3710
        JR      NZ,L0C44        ; back to PO-STEP if not zero
3711
 
3712
        EX      DE,HL           ; transfer address to DE
3713
        POP     AF              ; restore message/token number
3714
        CP      $20             ; return with carry set
3715
        RET     C               ; for all messages and function tokens
3716
 
3717
        LD      A,(DE)          ; test first character of token
3718
        SUB     $41             ; and return with carry set
3719
        RET                     ; if it is less that 'A'
3720
                                ; i.e. '<>', '<=', '>='
3721
 
3722
; ---------------
3723
; Test for scroll
3724
; ---------------
3725
; This test routine is called when printing carriage return, when considering
3726
; PRINT AT and from the general PRINT ALL characters routine to test if
3727
; scrolling is required, prompting the user if necessary.
3728
; This is therefore using the alternate set.
3729
; The B register holds the current line.
3730
 
3731
;; PO-SCR
3732
L0C55:  BIT     1,(IY+$01)      ; test FLAGS  - is printer in use ?
3733
        RET     NZ              ; return immediately if so.
3734
 
3735
        LD      DE,L0DD9        ; set DE to address: CL-SET
3736
        PUSH    DE              ; and push for return address.
3737
        LD      A,B             ; transfer the line to A.
3738
        BIT     0,(IY+$02)      ; test TV_FLAG  - Lower screen in use ?
3739
        JP      NZ,L0D02        ; jump forward to PO-SCR-4 if so.
3740
 
3741
        CP      (IY+$31)        ; greater than DF_SZ display file size ?
3742
        JR      C,L0C86         ; forward to REPORT-5 if less.
3743
                                ; 'Out of screen'
3744
 
3745
        RET     NZ              ; return (via CL-SET) if greater
3746
 
3747
        BIT     4,(IY+$02)      ; test TV_FLAG  - Automatic listing ?
3748
        JR      Z,L0C88         ; forward to PO-SCR-2 if not.
3749
 
3750
        LD      E,(IY+$2D)      ; fetch BREG - the count of scroll lines to E.
3751
        DEC     E               ; decrease and jump
3752
        JR      Z,L0CD2         ; to PO-SCR-3 if zero and scrolling required.
3753
 
3754
        LD      A,$00           ; explicit - select channel zero.
3755
        CALL    L1601           ; routine CHAN-OPEN opens it.
3756
 
3757
        LD      SP,($5C3F)      ; set stack pointer to LIST_SP
3758
 
3759
        RES     4,(IY+$02)      ; reset TV_FLAG  - signal auto listing finished.
3760
        RET                     ; return ignoring pushed value, CL-SET
3761
                                ; to MAIN or EDITOR without updating
3762
                                ; print position                         ->
3763
 
3764
; ---
3765
 
3766
 
3767
;; REPORT-5
3768
L0C86:  RST     08H             ; ERROR-1
3769
        DB    $04             ; Error Report: Out of screen
3770
 
3771
; continue here if not an automatic listing.
3772
 
3773
;; PO-SCR-2
3774
L0C88:  DEC     (IY+$52)        ; decrease SCR_CT
3775
        JR      NZ,L0CD2        ; forward to PO-SCR-3 to scroll display if
3776
                                ; result not zero.
3777
 
3778
; now produce prompt.
3779
 
3780
        LD      A,$18           ; reset
3781
        SUB     B               ; the
3782
        LD      ($5C8C),A       ; SCR_CT scroll count
3783
        LD      HL,($5C8F)      ; L=ATTR_T, H=MASK_T
3784
        PUSH    HL              ; save on stack
3785
        LD      A,($5C91)       ; P_FLAG
3786
        PUSH    AF              ; save on stack to prevent lower screen
3787
                                ; attributes (BORDCR etc.) being applied.
3788
        LD      A,$FD           ; select system channel 'K'
3789
        CALL    L1601           ; routine CHAN-OPEN opens it
3790
        XOR     A               ; clear to address message directly
3791
        LD      DE,L0CF8        ; make DE address: scrl-mssg
3792
        CALL    L0C0A           ; routine PO-MSG prints to lower screen
3793
        SET     5,(IY+$02)      ; set TV_FLAG  - signal lower screen requires
3794
                                ; clearing
3795
        LD      HL,$5C3B        ; make HL address FLAGS
3796
        SET     3,(HL)          ; signal 'L' mode.
3797
        RES     5,(HL)          ; signal 'no new key'.
3798
        EXX                     ; switch to main set.
3799
                                ; as calling chr input from alternative set.
3800
        CALL    L15D4           ; routine WAIT-KEY waits for new key
3801
                                ; Note. this is the right routine but the
3802
                                ; stream in use is unsatisfactory. From the
3803
                                ; choices available, it is however the best.
3804
 
3805
        EXX                     ; switch back to alternate set.
3806
        CP      $20             ; space is considered as BREAK
3807
        JR      Z,L0D00         ; forward to REPORT-D if so
3808
                                ; 'BREAK - CONT repeats'
3809
 
3810
        CP      $E2             ; is character 'STOP' ?
3811
        JR      Z,L0D00         ; forward to REPORT-D if so
3812
 
3813
        OR      $20             ; convert to lower-case
3814
        CP      $6E             ; is character 'n' ?
3815
        JR      Z,L0D00         ; forward to REPORT-D if so else scroll.
3816
 
3817
        LD      A,$FE           ; select system channel 'S'
3818
        CALL    L1601           ; routine CHAN-OPEN
3819
        POP     AF              ; restore original P_FLAG
3820
        LD      ($5C91),A       ; and save in P_FLAG.
3821
        POP     HL              ; restore original ATTR_T, MASK_T
3822
        LD      ($5C8F),HL      ; and reset ATTR_T, MASK-T as 'scroll?' has
3823
                                ; been printed.
3824
 
3825
;; PO-SCR-3
3826
L0CD2:  CALL    L0DFE           ; routine CL-SC-ALL to scroll whole display
3827
        LD      B,(IY+$31)      ; fetch DF_SZ to B
3828
        INC     B               ; increase to address last line of display
3829
        LD      C,$21           ; set C to $21 (was $21 from above routine)
3830
        PUSH    BC              ; save the line and column in BC.
3831
 
3832
        CALL    L0E9B           ; routine CL-ADDR finds display address.
3833
 
3834
        LD      A,H             ; now find the corresponding attribute byte
3835
        RRCA                    ; (this code sequence is used twice
3836
        RRCA                    ; elsewhere and is a candidate for
3837
        RRCA                    ; a subroutine.)
3838
        AND     $03             ;
3839
        OR      $58             ;
3840
        LD      H,A             ;
3841
 
3842
        LD      DE,$5AE0        ; start of last 'line' of attribute area
3843
        LD      A,(DE)          ; get attribute for last line
3844
        LD      C,(HL)          ; transfer to base line of upper part
3845
        LD      B,$20           ; there are thirty two bytes
3846
        EX      DE,HL           ; swap the pointers.
3847
 
3848
;; PO-SCR-3A
3849
L0CF0:  LD      (DE),A          ; transfer
3850
        LD      (HL),C          ; attributes.
3851
        INC     DE              ; address next.
3852
        INC     HL              ; address next.
3853
        DJNZ    L0CF0           ; loop back to PO-SCR-3A for all adjacent
3854
                                ; attribute lines.
3855
 
3856
        POP     BC              ; restore the line/column.
3857
        RET                     ; return via CL-SET (was pushed on stack).
3858
 
3859
; ---
3860
 
3861
; The message 'scroll?' appears here with last byte inverted.
3862
 
3863
;; scrl-mssg
3864
L0CF8           DB $80             ; initial step-over byte.
3865
                DC "scroll?"    ;DEFM    "scroll"
3866
                                ;DB    '?'+$80
3867
 
3868
;; REPORT-D
3869
L0D00:  RST     08H             ; ERROR-1
3870
        DB    $0C             ; Error Report: BREAK - CONT repeats
3871
 
3872
; continue here if using lower display - A holds line number.
3873
 
3874
;; PO-SCR-4
3875
L0D02:  CP      $02             ; is line number less than 2 ?
3876
        JR      C,L0C86         ; to REPORT-5 if so
3877
                                ; 'Out of Screen'.
3878
 
3879
        ADD     A,(IY+$31)      ; add DF_SZ
3880
        SUB     $19             ;
3881
        RET     NC              ; return if scrolling unnecessary
3882
 
3883
        NEG                     ; Negate to give number of scrolls required.
3884
        PUSH    BC              ; save line/column
3885
        LD      B,A             ; count to B
3886
        LD      HL,($5C8F)      ; fetch current ATTR_T, MASK_T to HL.
3887
        PUSH    HL              ; and save
3888
        LD      HL,($5C91)      ; fetch P_FLAG
3889
        PUSH    HL              ; and save.
3890
                                ; to prevent corruption by input AT
3891
 
3892
        CALL    L0D4D           ; routine TEMPS sets to BORDCR etc
3893
        LD      A,B             ; transfer scroll number to A.
3894
 
3895
;; PO-SCR-4A
3896
L0D1C:  PUSH    AF              ; save scroll number.
3897
        LD      HL,$5C6B        ; address DF_SZ
3898
        LD      B,(HL)          ; fetch old value
3899
        LD      A,B             ; transfer to A
3900
        INC     A               ; and increment
3901
        LD      (HL),A          ; then put back.
3902
        LD      HL,$5C89        ; address S_POSN_hi - line
3903
        CP      (HL)            ; compare
3904
        JR      C,L0D2D         ; forward to PO-SCR-4B if scrolling required
3905
 
3906
        INC     (HL)            ; else increment S_POSN_hi
3907
        LD      B,$18           ; set count to whole display ??
3908
                                ; Note. should be $17 and the top line
3909
                                ; will be scrolled into the ROM which
3910
                                ; is harmless on the standard set up.
3911
 
3912
;; PO-SCR-4B
3913
L0D2D:  CALL    L0E00           ; routine CL-SCROLL scrolls B lines
3914
        POP     AF              ; restore scroll counter.
3915
        DEC     A               ; decrease
3916
        JR      NZ,L0D1C        ; back to to PO-SCR-4A until done
3917
 
3918
        POP     HL              ; restore original P_FLAG.
3919
        LD      (IY+$57),L      ; and overwrite system variable P_FLAG.
3920
 
3921
        POP     HL              ; restore original ATTR_T/MASK_T.
3922
        LD      ($5C8F),HL      ; and update system variables.
3923
 
3924
        LD      BC,($5C88)      ; fetch S_POSN to BC.
3925
        RES     0,(IY+$02)      ; signal to TV_FLAG  - main screen in use.
3926
        CALL    L0DD9           ; call routine CL-SET for upper display.
3927
 
3928
        SET     0,(IY+$02)      ; signal to TV_FLAG  - lower screen in use.
3929
        POP     BC              ; restore line/column
3930
        RET                     ; return via CL-SET for lower display.
3931
 
3932
; ----------------------
3933
; Temporary colour items
3934
; ----------------------
3935
; This subroutine is called 11 times to copy the permanent colour items
3936
; to the temporary ones.
3937
 
3938
;; TEMPS
3939
L0D4D:  XOR     A               ; clear the accumulator
3940
        LD      HL,($5C8D)      ; fetch L=ATTR_P and H=MASK_P
3941
        BIT     0,(IY+$02)      ; test TV_FLAG  - is lower screen in use ?
3942
        JR      Z,L0D5B         ; skip to TEMPS-1 if not
3943
 
3944
        LD      H,A             ; set H, MASK P, to 00000000.
3945
        LD      L,(IY+$0E)      ; fetch BORDCR to L which is used for lower
3946
                                ; screen.
3947
 
3948
;; TEMPS-1
3949
L0D5B:  LD      ($5C8F),HL      ; transfer values to ATTR_T and MASK_T
3950
 
3951
; for the print flag the permanent values are odd bits, temporary even bits.
3952
 
3953
        LD      HL,$5C91        ; address P_FLAG.
3954
        JR      NZ,L0D65        ; skip to TEMPS-2 if lower screen using A=0.
3955
 
3956
        LD      A,(HL)          ; else pick up flag bits.
3957
        RRCA                    ; rotate permanent bits to temporary bits.
3958
 
3959
;; TEMPS-2
3960
L0D65:  XOR     (HL)            ;
3961
        AND     $55             ; BIN 01010101
3962
        XOR     (HL)            ; permanent now as original
3963
        LD      (HL),A          ; apply permanent bits to temporary bits.
3964
        RET                     ; and return.
3965
 
3966
; ------------------
3967
; Handle CLS command
3968
; ------------------
3969
; clears the display.
3970
; if it's difficult to write it should be difficult to read.
3971
 
3972
;; CLS
3973
L0D6B:  CALL    L0DAF           ; routine CL-ALL  clears display and
3974
                                ; resets attributes to permanent.
3975
                                ; re-attaches it to this computer.
3976
 
3977
; this routine called from INPUT, **
3978
 
3979
;; CLS-LOWER
3980
L0D6E:  LD      HL,$5C3C        ; address System Variable TV_FLAG.
3981
        RES     5,(HL)          ; TV_FLAG - signal do not clear lower screen.
3982
        SET     0,(HL)          ; TV_FLAG - signal lower screen in use.
3983
        CALL    L0D4D           ; routine TEMPS picks up temporary colours.
3984
        LD      B,(IY+$31)      ; fetch lower screen DF_SZ
3985
        CALL    L0E44           ; routine CL-LINE clears lower part
3986
                                ; and sets permanent attributes.
3987
 
3988
        LD      HL,$5AC0        ; fetch attribute address leftmost cell,
3989
                                ; second line up.
3990
        LD      A,($5C8D)       ; fetch permanent attribute from ATTR_P.
3991
        DEC     B               ; decrement lower screen display file size
3992
        JR      L0D8E           ; forward to CLS-3 ->
3993
 
3994
; ---
3995
 
3996
;; CLS-1
3997
L0D87:  LD      C,$20           ; set counter to 32 characters per line
3998
 
3999
;; CLS-2
4000
L0D89:  DEC     HL              ; decrease attribute address.
4001
        LD      (HL),A          ; and place attributes in next line up.
4002
        DEC     C               ; decrease 32 counter.
4003
        JR      NZ,L0D89        ; loop back to CLS-2 until all 32 done.
4004
 
4005
;; CLS-3
4006
L0D8E:  DJNZ    L0D87           ; decrease B counter and back to CLS-1
4007
                                ; if not zero.
4008
 
4009
        LD      (IY+$31),$02    ; set DF_SZ lower screen to 2
4010
 
4011
; This entry point is called from CL-ALL below to
4012
; reset the system channel input and output addresses to normal.
4013
 
4014
;; CL-CHAN
4015
L0D94:  LD      A,$FD           ; select system channel 'K'
4016
        CALL    L1601           ; routine CHAN-OPEN opens it.
4017
        LD      HL,($5C51)      ; fetch CURCHL to HL to address current channel
4018
        LD      DE,L09F4        ; set address to PRINT-OUT for first pass.
4019
        AND     A               ; clear carry for first pass.
4020
 
4021
;; CL-CHAN-A
4022
L0DA0:  LD      (HL),E          ; insert output address first pass.
4023
        INC     HL              ; or input address on second pass.
4024
        LD      (HL),D          ;
4025
        INC     HL              ;
4026
        LD      DE,L10A8        ; fetch address KEY-INPUT for second pass
4027
        CCF                     ; complement carry flag - will set on pass 1.
4028
 
4029
        JR      C,L0DA0         ; back to CL-CHAN-A if first pass else done.
4030
 
4031
        LD      BC,$1721        ; line 23 for lower screen
4032
        JR      L0DD9           ; exit via CL-SET to set column
4033
                                ; for lower display
4034
 
4035
; ---------------------------
4036
; Clearing whole display area
4037
; ---------------------------
4038
; This subroutine called from CLS, AUTO-LIST and MAIN-3
4039
; clears 24 lines of the display and resets the relevant system variables
4040
; and system channels.
4041
 
4042
;; CL-ALL
4043
L0DAF:  LD      HL,$0000        ; initialize plot coordinates.
4044
        LD      ($5C7D),HL      ; set COORDS to 0,0.
4045
        RES     0,(IY+$30)      ; update FLAGS2  - signal main screen is clear.
4046
 
4047
        CALL    L0D94           ; routine CL-CHAN makes channel 'K' 'normal'.
4048
 
4049
        LD      A,$FE           ; select system channel 'S'
4050
        CALL    L1601           ; routine CHAN-OPEN opens it
4051
        CALL    L0D4D           ; routine TEMPS picks up permanent values.
4052
        LD      B,$18           ; There are 24 lines.
4053
        CALL    L0E44           ; routine CL-LINE clears 24 text lines
4054
                                ; (and sets BC to $1821)
4055
 
4056
        LD      HL,($5C51)      ; fetch CURCHL make HL address current
4057
                                ; channel 'S'
4058
        LD      DE,L09F4        ; address: PRINT-OUT
4059
        LD      (HL),E          ; is made
4060
        INC     HL              ; the normal
4061
        LD      (HL),D          ; output address.
4062
 
4063
        LD      (IY+$52),$01    ; set SCR_CT - scroll count is set to default.
4064
                                ; Note. BC already contains $1821.
4065
        LD      BC,$1821        ; reset column and line to 0,0
4066
                                ; and continue into CL-SET, below, exiting
4067
                                ; via PO-STORE (for upper screen).
4068
 
4069
; ---------------------------
4070
; Set line and column numbers
4071
; ---------------------------
4072
; This important subroutine is used to calculate the character output
4073
; address for screens or printer based on the line/column for screens
4074
; or the column for printer.
4075
 
4076
;; CL-SET
4077
L0DD9:  LD      HL,$5B00        ; the base address of printer buffer
4078
        BIT     1,(IY+$01)      ; test FLAGS  - is printer in use ?
4079
        JR      NZ,L0DF4        ; forward to CL-SET-2 if so.
4080
 
4081
        LD      A,B             ; transfer line to A.
4082
        BIT     0,(IY+$02)      ; test TV_FLAG  - lower screen in use ?
4083
        JR      Z,L0DEE         ; skip to CL-SET-1 if handling upper part
4084
 
4085
        ADD     A,(IY+$31)      ; add DF_SZ for lower screen
4086
        SUB     $18             ; and adjust.
4087
 
4088
;; CL-SET-1
4089
L0DEE:  PUSH    BC              ; save the line/column.
4090
        LD      B,A             ; transfer line to B
4091
                                ; (adjusted if lower screen)
4092
 
4093
        CALL    L0E9B           ; routine CL-ADDR calculates address at left
4094
                                ; of screen.
4095
        POP     BC              ; restore the line/column.
4096
 
4097
;; CL-SET-2
4098
L0DF4:  LD      A,$21           ; the column $1-$21 is reversed
4099
        SUB     C               ; to range $00 - $20
4100
        LD      E,A             ; now transfer to DE
4101
        LD      D,$00           ; prepare for addition
4102
        ADD     HL,DE           ; and add to base address
4103
        JP      L0ADC           ; exit via PO-STORE to update relevant
4104
                                ; system variables.
4105
; ----------------
4106
; Handle scrolling
4107
; ----------------
4108
; The routine CL-SC-ALL is called once from PO to scroll all the display
4109
; and from the routine CL-SCROLL, once, to scroll part of the display.
4110
 
4111
;; CL-SC-ALL
4112
L0DFE:  LD      B,$17           ; scroll 23 lines, after 'scroll?'.
4113
 
4114
;; CL-SCROLL
4115
L0E00:  CALL    L0E9B           ; routine CL-ADDR gets screen address in HL.
4116
        LD      C,$08           ; there are 8 pixel lines to scroll.
4117
 
4118
;; CL-SCR-1
4119
L0E05:  PUSH    BC              ; save counters.
4120
        PUSH    HL              ; and initial address.
4121
        LD      A,B             ; get line count.
4122
        AND     $07             ; will set zero if all third to be scrolled.
4123
        LD      A,B             ; re-fetch the line count.
4124
        JR      NZ,L0E19        ; forward to CL-SCR-3 if partial scroll.
4125
 
4126
; HL points to top line of third and must be copied to bottom of previous 3rd.
4127
; ( so HL = $4800 or $5000 ) ( but also sometimes $4000 )
4128
 
4129
;; CL-SCR-2
4130
L0E0D:  EX      DE,HL           ; copy HL to DE.
4131
        LD      HL,$F8E0        ; subtract $08 from H and add $E0 to L -
4132
        ADD     HL,DE           ; to make destination bottom line of previous
4133
                                ; third.
4134
        EX      DE,HL           ; restore the source and destination.
4135
        LD      BC,$0020        ; thirty-two bytes are to be copied.
4136
        DEC     A               ; decrement the line count.
4137
        LDIR                    ; copy a pixel line to previous third.
4138
 
4139
;; CL-SCR-3
4140
L0E19:  EX      DE,HL           ; save source in DE.
4141
        LD      HL,$FFE0        ; load the value -32.
4142
        ADD     HL,DE           ; add to form destination in HL.
4143
        EX      DE,HL           ; switch source and destination
4144
        LD      B,A             ; save the count in B.
4145
        AND     $07             ; mask to find count applicable to current
4146
        RRCA                    ; third and
4147
        RRCA                    ; multiply by
4148
        RRCA                    ; thirty two (same as 5 RLCAs)
4149
 
4150
        LD      C,A             ; transfer byte count to C ($E0 at most)
4151
        LD      A,B             ; store line count to A
4152
        LD      B,$00           ; make B zero
4153
        LDIR                    ; copy bytes (BC=0, H incremented, L=0)
4154
        LD      B,$07           ; set B to 7, C is zero.
4155
        ADD     HL,BC           ; add 7 to H to address next third.
4156
        AND     $F8             ; has last third been done ?
4157
        JR      NZ,L0E0D        ; back to CL-SCR-2 if not
4158
 
4159
        POP     HL              ; restore topmost address.
4160
        INC     H               ; next pixel line down.
4161
        POP     BC              ; restore counts.
4162
        DEC     C               ; reduce pixel line count.
4163
        JR      NZ,L0E05        ; back to CL-SCR-1 if all eight not done.
4164
 
4165
        CALL    L0E88           ; routine CL-ATTR gets address in attributes
4166
                                ; from current 'ninth line', count in BC.
4167
        LD      HL,$FFE0        ; set HL to the 16-bit value -32.
4168
        ADD     HL,DE           ; and add to form destination address.
4169
        EX      DE,HL           ; swap source and destination addresses.
4170
        LDIR                    ; copy bytes scrolling the linear attributes.
4171
        LD      B,$01           ; continue to clear the bottom line.
4172
 
4173
; ---------------------------
4174
; Clear text lines of display
4175
; ---------------------------
4176
; This subroutine, called from CL-ALL, CLS-LOWER and AUTO-LIST and above,
4177
; clears text lines at bottom of display.
4178
; The B register holds on entry the number of lines to be cleared 1-24.
4179
 
4180
;; CL-LINE
4181
L0E44:  PUSH    BC              ; save line count
4182
        CALL    L0E9B           ; routine CL-ADDR gets top address
4183
        LD      C,$08           ; there are eight screen lines to a text line.
4184
 
4185
;; CL-LINE-1
4186
L0E4A:  PUSH    BC              ; save pixel line count
4187
        PUSH    HL              ; and save the address
4188
        LD      A,B             ; transfer the line to A (1-24).
4189
 
4190
;; CL-LINE-2
4191
L0E4D:  AND     $07             ; mask 0-7 to consider thirds at a time
4192
        RRCA                    ; multiply
4193
        RRCA                    ; by 32  (same as five RLCA instructions)
4194
        RRCA                    ; now 32 - 256(0)
4195
        LD      C,A             ; store result in C
4196
        LD      A,B             ; save line in A (1-24)
4197
        LD      B,$00           ; set high byte to 0, prepare for ldir.
4198
        DEC     C               ; decrement count 31-255.
4199
        LD      D,H             ; copy HL
4200
        LD      E,L             ; to DE.
4201
        LD      (HL),$00        ; blank the first byte.
4202
        INC     DE              ; make DE point to next byte.
4203
        LDIR                    ; ldir will clear lines.
4204
        LD      DE,$0701        ; now address next third adjusting
4205
        ADD     HL,DE           ; register E to address left hand side
4206
        DEC     A               ; decrease the line count.
4207
        AND     $F8             ; will be 16, 8 or 0  (AND $18 will do).
4208
        LD      B,A             ; transfer count to B.
4209
        JR      NZ,L0E4D        ; back to CL-LINE-2 if 16 or 8 to do
4210
                                ; the next third.
4211
 
4212
        POP     HL              ; restore start address.
4213
        INC     H               ; address next line down.
4214
        POP     BC              ; fetch counts.
4215
        DEC     C               ; decrement pixel line count
4216
        JR      NZ,L0E4A        ; back to CL-LINE-1 till all done.
4217
 
4218
        CALL    L0E88           ; routine CL-ATTR gets attribute address
4219
                                ; in DE and B * 32 in BC.
4220
        LD      H,D             ; transfer the address
4221
        LD      L,E             ; to HL.
4222
 
4223
        INC     DE              ; make DE point to next location.
4224
 
4225
        LD      A,($5C8D)       ; fetch ATTR_P - permanent attributes
4226
        BIT     0,(IY+$02)      ; test TV_FLAG  - lower screen in use ?
4227
        JR      Z,L0E80         ; skip to CL-LINE-3 if not.
4228
 
4229
        LD      A,($5C48)       ; else lower screen uses BORDCR as attribute.
4230
 
4231
;; CL-LINE-3
4232
L0E80:  LD      (HL),A          ; put attribute in first byte.
4233
        DEC     BC              ; decrement the counter.
4234
        LDIR                    ; copy bytes to set all attributes.
4235
        POP     BC              ; restore the line $01-$24.
4236
        LD      C,$21           ; make column $21. (No use is made of this)
4237
        RET                     ; return to the calling routine.
4238
 
4239
; ------------------
4240
; Attribute handling
4241
; ------------------
4242
; This subroutine is called from CL-LINE or CL-SCROLL with the HL register
4243
; pointing to the 'ninth' line and H needs to be decremented before or after
4244
; the division. Had it been done first then either present code or that used
4245
; at the start of PO-ATTR could have been used.
4246
; The Spectrum screen arrangement leads to the L register holding already
4247
; the correct value for the attribute file and it is only necessary
4248
; to manipulate H to form the correct colour attribute address.
4249
 
4250
;; CL-ATTR
4251
L0E88:  LD      A,H             ; fetch H to A - $48, $50, or $58.
4252
        RRCA                    ; divide by
4253
        RRCA                    ; eight.
4254
        RRCA                    ; $09, $0A or $0B.
4255
        DEC     A               ; $08, $09 or $0A.
4256
        OR      $50             ; $58, $59 or $5A.
4257
        LD      H,A             ; save high byte of attributes.
4258
 
4259
        EX      DE,HL           ; transfer attribute address to DE
4260
        LD      H,C             ; set H to zero - from last LDIR.
4261
        LD      L,B             ; load L with the line from B.
4262
        ADD     HL,HL           ; multiply
4263
        ADD     HL,HL           ; by
4264
        ADD     HL,HL           ; thirty two
4265
        ADD     HL,HL           ; to give count of attribute
4266
        ADD     HL,HL           ; cells to end of display.
4267
 
4268
        LD      B,H             ; transfer result
4269
        LD      C,L             ; to register BC.
4270
 
4271
        RET                     ; and return.
4272
 
4273
; -------------------------------
4274
; Handle display with line number
4275
; -------------------------------
4276
; This subroutine is called from four places to calculate the address
4277
; of the start of a screen character line which is supplied in B.
4278
 
4279
;; CL-ADDR
4280
L0E9B:  LD      A,$18           ; reverse the line number
4281
        SUB     B               ; to range $00 - $17.
4282
        LD      D,A             ; save line in D for later.
4283
        RRCA                    ; multiply
4284
        RRCA                    ; by
4285
        RRCA                    ; thirty-two.
4286
 
4287
        AND     $E0             ; mask off low bits to make
4288
        LD      L,A             ; L a multiple of 32.
4289
 
4290
        LD      A,D             ; bring back the line to A.
4291
 
4292
        AND     $18             ; now $00, $08 or $10.
4293
 
4294
        OR      $40             ; add the base address of screen.
4295
 
4296
        LD      H,A             ; HL now has the correct address.
4297
        RET                     ; return.
4298
 
4299
; -------------------
4300
; Handle COPY command
4301
; -------------------
4302
; This command copies the top 176 lines to the ZX Printer
4303
; It is popular to call this from machine code at point
4304
; L0EAF with B holding 192 (and interrupts disabled) for a full-screen
4305
; copy. This particularly applies to 16K Spectrums as time-critical
4306
; machine code routines cannot be written in the first 16K of RAM as
4307
; it is shared with the ULA which has precedence over the Z80 chip.
4308
 
4309
;; COPY
4310
 
4311
L0EAC:  DI                      ; disable interrupts as this is time-critical.
4312
;===============================
585 savelij 4313
                RST 8
4314
                DB _AY_PRN_SCR
384 savelij 4315
;===============================
4316
 
403 savelij 4317
L0EAF           LD HL,$4000             ; address start of the display file.
4318
 
384 savelij 4319
; now enter a loop to handle each pixel line.
4320
 
4321
;; COPY-1
4322
L0EB2:  PUSH    HL              ; save the screen address.
4323
        PUSH    BC              ; and the line counter.
4324
 
4325
        CALL    L0EF4           ; routine COPY-LINE outputs one line.
4326
 
4327
        POP     BC              ; restore the line counter.
4328
        POP     HL              ; and display address.
4329
        INC     H               ; next line down screen within 'thirds'.
4330
        LD      A,H             ; high byte to A.
4331
        AND     $07             ; result will be zero if we have left third.
4332
        JR      NZ,L0EC9        ; forward to COPY-2 if not to continue loop.
4333
 
4334
        LD      A,L             ; consider low byte first.
4335
        ADD     A,$20           ; increase by 32 - sets carry if back to zero.
4336
        LD      L,A             ; will be next group of 8.
4337
        CCF                     ; complement - carry set if more lines in
4338
                                ; the previous third.
4339
        SBC     A,A             ; will be FF, if more, else 00.
4340
        AND     $F8             ; will be F8 (-8) or 00.
4341
        ADD     A,H             ; that is subtract 8, if more to do in third.
4342
        LD      H,A             ; and reset address.
4343
 
4344
;; COPY-2
4345
L0EC9:  DJNZ    L0EB2           ; back to COPY-1 for all lines.
4346
 
4347
        JR      L0EDA           ; forward to COPY-END to switch off the printer
4348
                                ; motor and enable interrupts.
4349
                                ; Note. Nothing else required.
4350
 
4351
; ------------------------------
4352
; Pass printer buffer to printer
4353
; ------------------------------
4354
; This routine is used to copy 8 text lines from the printer buffer
4355
; to the ZX Printer. These text lines are mapped linearly so HL does
4356
; not need to be adjusted at the end of each line.
4357
 
4358
;; COPY-BUFF
4359
L0ECD:  DI                      ; disable interrupts
4360
        LD      HL,$5B00        ; the base address of the Printer Buffer.
4361
        LD      B,$08           ; set count to 8 lines of 32 bytes.
4362
 
4363
;; COPY-3
4364
L0ED3:  PUSH    BC              ; save counter.
4365
        CALL    L0EF4           ; routine COPY-LINE outputs 32 bytes
4366
        POP     BC              ; restore counter.
4367
        DJNZ    L0ED3           ; loop back to COPY-3 for all 8 lines.
4368
                                ; then stop motor and clear buffer.
4369
 
4370
; Note. the COPY command rejoins here, essentially to execute the next
4371
; three instructions.
4372
 
4373
;; COPY-END
403 savelij 4374
L0EDA           LD A,$04                ; output value 4 to port
384 savelij 4375
                OUT ($FB),A             ; to stop the slowed printer motor.
403 savelij 4376
L0EDE           EI                      ; enable interrupts.
384 savelij 4377
 
4378
; --------------------
4379
; Clear Printer Buffer
4380
; --------------------
4381
; This routine clears an arbitrary 256 bytes of memory.
4382
; Note. The routine seems designed to clear a buffer that follows the
4383
; system variables.
4384
; The routine should check a flag or HL address and simply return if COPY
4385
; is in use.
4386
; (T-ADDR-lo would work for the system but not if COPY called externally.)
4387
; As a consequence of this omission the buffer will needlessly
4388
; be cleared when COPY is used and the screen/printer position may be set to
4389
; the start of the buffer and the line number to 0 (B)
4390
; giving an 'Out of Screen' error.
4391
; There seems to have been an unsuccessful attempt to circumvent the use
4392
; of PR_CC_hi.
4393
 
4394
;; CLEAR-PRB
4395
L0EDF:  LD      HL,$5B00        ; the location of the buffer.
4396
        LD      (IY+$46),L      ; update PR_CC_lo - set to zero - superfluous.
4397
        XOR     A               ; clear the accumulator.
4398
        LD      B,A             ; set count to 256 bytes.
4399
 
4400
;; PRB-BYTES
4401
L0EE7:  LD      (HL),A          ; set addressed location to zero.
4402
        INC     HL              ; address next byte - Note. not INC L.
4403
        DJNZ    L0EE7           ; back to PRB-BYTES. repeat for 256 bytes.
4404
 
4405
        RES     1,(IY+$30)      ; set FLAGS2 - signal printer buffer is clear.
4406
        LD      C,$21           ; set the column position .
4407
        JP      L0DD9           ; exit via CL-SET and then PO-STORE.
4408
 
4409
; -----------------
4410
; Copy line routine
4411
; -----------------
4412
; This routine is called from COPY and COPY-BUFF to output a line of
4413
; 32 bytes to the ZX Printer.
4414
; Output to port $FB -
4415
; bit 7 set - activate stylus.
4416
; bit 7 low - deactivate stylus.
4417
; bit 2 set - stops printer.
4418
; bit 2 reset - starts printer
4419
; bit 1 set - slows printer.
4420
; bit 1 reset - normal speed.
4421
 
4422
;; COPY-LINE
4423
;===============================
678 savelij 4424
L0EF4           LD A,(HL)
585 savelij 4425
                RST 8
4426
                DB _AY_PRN_A_
384 savelij 4427
                RET
403 savelij 4428
;===============================
384 savelij 4429
                AND $02                 ; result is 02 now else 00.
4430
                                        ; bit 1 set slows the printer.
4431
                OUT ($FB),A             ; slow the printer for the
4432
                                        ; last two lines.
4433
        LD      D,A             ; save the mask to control the printer later.
4434
 
4435
;; COPY-L-1
4436
L0EFD:  CALL    L1F54           ; call BREAK-KEY to read keyboard immediately.
4437
        JR      C,L0F0C         ; forward to COPY-L-2 if 'break' not pressed.
4438
 
4439
        LD      A,$04           ; else stop the
4440
        OUT     ($FB),A         ; printer motor.
4441
        EI                      ; enable interrupts.
4442
        CALL    L0EDF           ; call routine CLEAR-PRB.
4443
                                ; Note. should not be cleared if COPY in use.
4444
 
4445
;; REPORT-Dc
4446
L0F0A:  RST     08H             ; ERROR-1
4447
        DB      $0C             ; Error Report: BREAK - CONT repeats
4448
 
4449
;; COPY-L-2
4450
L0F0C:  IN      A,($FB)         ; test now to see if
4451
        ADD     A,A             ; a printer is attached.
4452
        RET     M               ; return if not - but continue with parent
4453
                                ; command.
4454
 
4455
        JR      NC,L0EFD        ; back to COPY-L-1 if stylus of printer not
4456
                                ; in position.
4457
 
4458
        LD      C,$20           ; set count to 32 bytes.
4459
 
4460
;; COPY-L-3
4461
L0F14:  LD      E,(HL)          ; fetch a byte from line.
4462
        INC     HL              ; address next location. Note. not INC L.
4463
        LD      B,$08           ; count the bits.
4464
 
4465
;; COPY-L-4
4466
L0F18:  RL      D               ; prepare mask to receive bit.
4467
        RL      E               ; rotate leftmost print bit to carry
4468
        RR      D               ; and back to bit 7 of D restoring bit 1
4469
 
4470
;; COPY-L-5
4471
L0F1E:  IN      A,($FB)         ; read the port.
4472
        RRA                     ; bit 0 to carry.
4473
        JR      NC,L0F1E        ; back to COPY-L-5 if stylus not in position.
4474
 
4475
        LD      A,D             ; transfer command bits to A.
4476
        OUT     ($FB),A         ; and output to port.
4477
        DJNZ    L0F18           ; loop back to COPY-L-4 for all 8 bits.
4478
 
4479
        DEC     C               ; decrease the byte count.
4480
        JR      NZ,L0F14        ; back to COPY-L-3 until 256 bits done.
4481
 
4482
        RET                     ; return to calling routine COPY/COPY-BUFF.
4483
 
4484
; ----------------------------------
4485
; Editor routine for BASIC and INPUT
4486
; ----------------------------------
4487
; The editor is called to prepare or edit a BASIC line.
4488
; It is also called from INPUT to input a numeric or string expression.
4489
; The behaviour and options are quite different in the various modes
4490
; and distinguished by bit 5 of FLAGX.
4491
;
4492
; This is a compact and highly versatile routine.
4493
 
4494
;; EDITOR
4495
L0F2C:  LD      HL,($5C3D)      ; fetch ERR_SP
4496
        PUSH    HL              ; save on stack
4497
 
4498
;; ED-AGAIN
4499
L0F30:  LD      HL,L107F        ; address: ED-ERROR
4500
        PUSH    HL              ; save address on stack and
4501
        LD      ($5C3D),SP      ; make ERR_SP point to it.
4502
 
4503
; Note. While in editing/input mode should an error occur then RST 08 will
4504
; update X_PTR to the location reached by CH_ADD and jump to ED-ERROR
4505
; where the error will be cancelled and the loop begin again from ED-AGAIN
4506
; above. The position of the error will be apparent when the lower screen is
4507
; reprinted. If no error then the re-iteration is to ED-LOOP below when
4508
; input is arriving from the keyboard.
4509
 
4510
;; ED-LOOP
4511
L0F38:  CALL    L15D4           ; routine WAIT-KEY gets key possibly
4512
                                ; changing the mode.
4513
        PUSH    AF              ; save key.
4514
        LD      D,$00           ; and give a short click based
4515
        LD      E,(IY-$01)      ; on PIP value for duration.
4516
        LD      HL,$00C8        ; and pitch.
4517
        CALL    L03B5           ; routine BEEPER gives click - effective
4518
                                ; with rubber keyboard.
4519
        POP     AF              ; get saved key value.
4520
        LD      HL,L0F38        ; address: ED-LOOP is loaded to HL.
4521
        PUSH    HL              ; and pushed onto stack.
4522
 
4523
; At this point there is a looping return address on the stack, an error
4524
; handler and an input stream set up to supply characters.
4525
; The character that has been received can now be processed.
4526
 
4527
        CP      $18             ; range 24 to 255 ?
4528
        JR      NC,L0F81        ; forward to ADD-CHAR if so.
4529
 
4530
        CP      $07             ; lower than 7 ?
4531
        JR      C,L0F81         ; forward to ADD-CHAR also.
4532
                                ; Note. This is a 'bug' and chr$ 6, the comma
4533
                                ; control character, should have had an
4534
                                ; entry in the ED-KEYS table.
4535
                                ; Steven Vickers, 1984, Pitman.
4536
 
4537
        CP      $10             ; less than 16 ?
4538
        JR      C,L0F92         ; forward to ED-KEYS if editing control
4539
                                ; range 7 to 15 dealt with by a table
4540
 
4541
        LD      BC,$0002        ; prepare for ink/paper etc.
4542
        LD      D,A             ; save character in D
4543
        CP      $16             ; is it ink/paper/bright etc. ?
4544
        JR      C,L0F6C         ; forward to ED-CONTR if so
4545
 
4546
                                ; leaves 22d AT and 23d TAB
4547
                                ; which can't be entered via KEY-INPUT.
4548
                                ; so this code is never normally executed
4549
                                ; when the keyboard is used for input.
4550
 
4551
        INC     BC              ; if it was AT/TAB - 3 locations required
4552
        BIT     7,(IY+$37)      ; test FLAGX  - Is this INPUT LINE ?
4553
        JP      Z,L101E         ; jump to ED-IGNORE if not, else 
4554
 
4555
        CALL    L15D4           ; routine WAIT-KEY - input address is KEY-NEXT
4556
                                ; but is reset to KEY-INPUT
4557
        LD      E,A             ; save first in E
4558
 
4559
;; ED-CONTR
4560
L0F6C:  CALL    L15D4           ; routine WAIT-KEY for control.
4561
                                ; input address will be key-next.
4562
 
4563
        PUSH    DE              ; saved code/parameters
4564
        LD      HL,($5C5B)      ; fetch address of keyboard cursor from K_CUR
4565
        RES     0,(IY+$07)      ; set MODE to 'L'
4566
 
4567
        CALL    L1655           ; routine MAKE-ROOM makes 2/3 spaces at cursor
4568
 
4569
        POP     BC              ; restore code/parameters
4570
        INC     HL              ; address first location
4571
        LD      (HL),B          ; place code (ink etc.)
4572
        INC     HL              ; address next
4573
        LD      (HL),C          ; place possible parameter. If only one
4574
                                ; then DE points to this location also.
4575
        JR      L0F8B           ; forward to ADD-CH-1
4576
 
4577
; ------------------------
4578
; Add code to current line
4579
; ------------------------
4580
; this is the branch used to add normal non-control characters
4581
; with ED-LOOP as the stacked return address.
4582
; it is also the OUTPUT service routine for system channel 'R'.
4583
 
4584
;; ADD-CHAR
4585
L0F81:  RES     0,(IY+$07)      ; set MODE to 'L'
4586
 
4587
X0F85:  LD      HL,($5C5B)      ; fetch address of keyboard cursor from K_CUR
4588
        CALL    L1652           ; routine ONE-SPACE creates one space.
4589
 
4590
; either a continuation of above or from ED-CONTR with ED-LOOP on stack.
4591
 
4592
;; ADD-CH-1
4593
L0F8B:  LD      (DE),A          ; load current character to last new location.
4594
        INC     DE              ; address next
4595
        LD      ($5C5B),DE      ; and update K_CUR system variable.
4596
        RET                     ; return - either a simple return
4597
                                ; from ADD-CHAR or to ED-LOOP on stack.
4598
 
4599
; ---
4600
 
4601
; a branch of the editing loop to deal with control characters
4602
; using a look-up table.
4603
 
4604
;; ED-KEYS
4605
L0F92:  LD      E,A             ; character to E.
4606
        LD      D,$00           ; prepare to add.
4607
        LD      HL,L0FA0 - 7    ; base address of editing keys table. $0F99
4608
        ADD     HL,DE           ; add E
4609
        LD      E,(HL)          ; fetch offset to E
4610
        ADD     HL,DE           ; add offset for address of handling routine.
4611
        PUSH    HL              ; push the address on machine stack.
4612
        LD      HL,($5C5B)      ; load address of cursor from K_CUR.
4613
        RET                     ; an make an indirect jump forward to routine.
4614
 
4615
; ------------------
4616
; Editing keys table
4617
; ------------------
4618
; For each code in the range $07 to $0F this table contains a
4619
; single offset byte to the routine that services that code.
4620
; Note. for what was intended there should also have been an
4621
; entry for chr$ 6 with offset to ed-symbol.
4622
 
4623
;; ed-keys-t
4624
L0FA0:  DB    L0FA9 - $  ; 07d offset $09 to Address: ED-EDIT
4625
        DB    L1007 - $  ; 08d offset $66 to Address: ED-LEFT
4626
        DB    L100C - $  ; 09d offset $6A to Address: ED-RIGHT
4627
        DB    L0FF3 - $  ; 10d offset $50 to Address: ED-DOWN
4628
        DB    L1059 - $  ; 11d offset $B5 to Address: ED-UP
4629
        DB    L1015 - $  ; 12d offset $70 to Address: ED-DELETE
4630
        DB    L1024 - $  ; 13d offset $7E to Address: ED-ENTER
4631
        DB    L1076 - $  ; 14d offset $CF to Address: ED-SYMBOL
4632
        DB    L107C - $  ; 15d offset $D4 to Address: ED-GRAPH
4633
 
4634
; ---------------
4635
; Handle EDIT key
4636
; ---------------
4637
; The user has pressed SHIFT 1 to bring edit line down to bottom of screen.
4638
; Alternatively the user wishes to clear the input buffer and start again.
4639
; Alternatively ...
4640
 
4641
;; ED-EDIT
4642
L0FA9:  LD      HL,($5C49)      ; fetch E_PPC the last line number entered.
4643
                                ; Note. may not exist and may follow program.
4644
        BIT     5,(IY+$37)      ; test FLAGX  - input mode ?
4645
        JP      NZ,L1097        ; jump forward to CLEAR-SP if not in editor.
4646
 
4647
        CALL    L196E           ; routine LINE-ADDR to find address of line
4648
                                ; or following line if it doesn't exist.
4649
        CALL    L1695           ; routine LINE-NO will get line number from
4650
                                ; address or previous line if at end-marker.
4651
        LD      A,D             ; if there is no program then DE will
4652
        OR      E               ; contain zero so test for this.
4653
        JP      Z,L1097         ; jump to to CLEAR-SP if so.
4654
 
4655
; Note. at this point we have a validated line number, not just an
4656
; approximation and it would be best to update E_PPC with the true
4657
; cursor line value which would enable the line cursor to be suppressed
4658
; in all situations - see shortly.
4659
 
4660
        PUSH    HL              ; save address of line.
4661
        INC     HL              ; address low byte of length.
4662
        LD      C,(HL)          ; transfer to C
4663
        INC     HL              ; next to high byte
4664
        LD      B,(HL)          ; transfer to B.
4665
        LD      HL,$000A        ; an overhead of ten bytes
4666
        ADD     HL,BC           ; is added to length.
4667
        LD      B,H             ; transfer adjusted value
4668
        LD      C,L             ; to BC register.
4669
        CALL    L1F05           ; routine TEST-ROOM checks free memory.
4670
        CALL    L1097           ; routine CLEAR-SP clears editing area.
4671
        LD      HL,($5C51)      ; address CURCHL
4672
        EX      (SP),HL         ; swap with line address on stack
4673
        PUSH    HL              ; save line address underneath
4674
 
4675
        LD      A,$FF           ; select system channel 'R'
4676
        CALL    L1601           ; routine CHAN-OPEN opens it
4677
 
4678
        POP     HL              ; drop line address
4679
        DEC     HL              ; make it point to first byte of line num.
4680
        DEC     (IY+$0F)        ; decrease E_PPC_lo to suppress line cursor.
4681
                                ; Note. ineffective when E_PPC is one
4682
                                ; greater than last line of program perhaps
4683
                                ; as a result of a delete.
4684
                                ; credit. Paul Harrison 1982.
4685
 
4686
        CALL    L1855           ; routine OUT-LINE outputs the BASIC line
4687
                                ; to the editing area.
4688
        INC     (IY+$0F)        ; restore E_PPC_lo to the previous value.
4689
        LD      HL,($5C59)      ; address E_LINE in editing area.
4690
        INC     HL              ; advance
4691
        INC     HL              ; past space
4692
        INC     HL              ; and digit characters
4693
        INC     HL              ; of line number.
4694
 
4695
        LD      ($5C5B),HL      ; update K_CUR to address start of BASIC.
4696
        POP     HL              ; restore the address of CURCHL.
4697
        CALL    L1615           ; routine CHAN-FLAG sets flags for it.
4698
        RET                     ; RETURN to ED-LOOP.
4699
 
4700
; -------------------
4701
; Cursor down editing
4702
; -------------------
4703
; The BASIC lines are displayed at the top of the screen and the user
4704
; wishes to move the cursor down one line in edit mode.
4705
; In input mode this key can be used as an alternative to entering STOP.
4706
 
4707
;; ED-DOWN
4708
L0FF3:  BIT     5,(IY+$37)      ; test FLAGX  - Input Mode ?
4709
        JR      NZ,L1001        ; skip to ED-STOP if so
4710
 
4711
        LD      HL,$5C49        ; address E_PPC - 'current line'
4712
        CALL    L190F           ; routine LN-FETCH fetches number of next
4713
                                ; line or same if at end of program.
4714
        JR      L106E           ; forward to ED-LIST to produce an
4715
                                ; automatic listing.
4716
 
4717
; ---
4718
 
4719
;; ED-STOP
4720
L1001:  LD      (IY+$00),$10    ; set ERR_NR to 'STOP in INPUT' code
4721
        JR      L1024           ; forward to ED-ENTER to produce error.
4722
 
4723
; -------------------
4724
; Cursor left editing
4725
; -------------------
4726
; This acts on the cursor in the lower section of the screen in both
4727
; editing and input mode.
4728
 
4729
;; ED-LEFT
4730
L1007:  CALL    L1031           ; routine ED-EDGE moves left if possible
4731
        JR      L1011           ; forward to ED-CUR to update K-CUR
4732
                                ; and return to ED-LOOP.
4733
 
4734
; --------------------
4735
; Cursor right editing
4736
; --------------------
4737
; This acts on the cursor in the lower screen in both editing and input
4738
; mode and moves it to the right.
4739
 
4740
;; ED-RIGHT
4741
L100C:  LD      A,(HL)          ; fetch addressed character.
4742
        CP      $0D             ; is it carriage return ?
4743
        RET     Z               ; return if so to ED-LOOP
4744
 
4745
        INC     HL              ; address next character
4746
 
4747
;; ED-CUR
4748
L1011:  LD      ($5C5B),HL      ; update K_CUR system variable
4749
        RET                     ; return to ED-LOOP
4750
 
4751
; --------------
4752
; DELETE editing
4753
; --------------
4754
; This acts on the lower screen and deletes the character to left of
4755
; cursor. If control characters are present these are deleted first
4756
; leaving the naked parameter (0-7) which appears as a '?' except in the
4757
; case of chr$ 6 which is the comma control character. It is not mandatory
4758
; to delete these second characters.
4759
 
4760
;; ED-DELETE
4761
L1015:  CALL    L1031           ; routine ED-EDGE moves cursor to left.
4762
        LD      BC,$0001        ; of character to be deleted.
4763
        JP      L19E8           ; to RECLAIM-2 reclaim the character.
4764
 
4765
; ------------------------------------------
4766
; Ignore next 2 codes from key-input routine
4767
; ------------------------------------------
4768
; Since AT and TAB cannot be entered this point is never reached
4769
; from the keyboard. If inputting from a tape device or network then
4770
; the control and two following characters are ignored and processing
4771
; continues as if a carriage return had been received.
4772
; Here, perhaps, another Spectrum has said print #15; AT 0,0; "This is yellow"
4773
; and this one is interpreting input #15; a$.
4774
 
4775
;; ED-IGNORE
4776
L101E:  CALL    L15D4           ; routine WAIT-KEY to ignore keystroke.
4777
        CALL    L15D4           ; routine WAIT-KEY to ignore next key.
4778
 
4779
; -------------
4780
; Enter/newline
4781
; -------------
4782
; The enter key has been pressed to have BASIC line or input accepted.
4783
 
4784
;; ED-ENTER
4785
L1024:  POP     HL              ; discard address ED-LOOP
4786
        POP     HL              ; drop address ED-ERROR
4787
 
4788
;; ED-END
4789
L1026:  POP     HL              ; the previous value of ERR_SP
4790
        LD      ($5C3D),HL      ; is restored to ERR_SP system variable
4791
        BIT     7,(IY+$00)      ; is ERR_NR $FF (= 'OK') ?
4792
        RET     NZ              ; return if so
4793
 
4794
        LD      SP,HL           ; else put error routine on stack
4795
        RET                     ; and make an indirect jump to it.
4796
 
4797
; -----------------------------
4798
; Move cursor left when editing
4799
; -----------------------------
4800
; This routine moves the cursor left. The complication is that it must
4801
; not position the cursor between control codes and their parameters.
4802
; It is further complicated in that it deals with TAB and AT characters
4803
; which are never present from the keyboard.
4804
; The method is to advance from the beginning of the line each time,
4805
; jumping one, two, or three characters as necessary saving the original
4806
; position at each jump in DE. Once it arrives at the cursor then the next
4807
; legitimate leftmost position is in DE.
4808
 
4809
;; ED-EDGE
4810
L1031:  SCF                     ; carry flag must be set to call the nested
4811
        CALL    L1195           ; subroutine SET-DE.
4812
                                ; if input   then DE=WORKSP
4813
                                ; if editing then DE=E_LINE
4814
        SBC     HL,DE           ; subtract address from start of line
4815
        ADD     HL,DE           ; and add back.
4816
        INC     HL              ; adjust for carry.
4817
        POP     BC              ; drop return address
4818
        RET     C               ; return to ED-LOOP if already at left
4819
                                ; of line.
4820
 
4821
        PUSH    BC              ; resave return address - ED-LOOP.
4822
        LD      B,H             ; transfer HL - cursor address
4823
        LD      C,L             ; to BC register pair.
4824
                                ; at this point DE addresses start of line.
4825
 
4826
;; ED-EDGE-1
4827
L103E:  LD      H,D             ; transfer DE - leftmost pointer
4828
        LD      L,E             ; to HL
4829
        INC     HL              ; address next leftmost character to
4830
                                ; advance position each time.
4831
        LD      A,(DE)          ; pick up previous in A
4832
        AND     $F0             ; lose the low bits
4833
        CP      $10             ; is it INK to TAB $10-$1F ?
4834
                                ; that is, is it followed by a parameter ?
4835
        JR      NZ,L1051        ; to ED-EDGE-2 if not
4836
                                ; HL has been incremented once
4837
 
4838
        INC     HL              ; address next as at least one parameter.
4839
 
4840
; in fact since 'tab' and 'at' cannot be entered the next section seems
4841
; superfluous.
4842
; The test will always fail and the jump to ED-EDGE-2 will be taken.
4843
 
4844
        LD      A,(DE)          ; reload leftmost character
4845
        SUB     $17             ; decimal 23 ('tab')
4846
        ADC     A,$00           ; will be 0 for 'tab' and 'at'.
4847
        JR      NZ,L1051        ; forward to ED-EDGE-2 if not
4848
                                ; HL has been incremented twice
4849
 
4850
        INC     HL              ; increment a third time for 'at'/'tab'
4851
 
4852
;; ED-EDGE-2
4853
L1051:  AND     A               ; prepare for true subtraction
4854
        SBC     HL,BC           ; subtract cursor address from pointer
4855
        ADD     HL,BC           ; and add back
4856
                                ; Note when HL matches the cursor position BC,
4857
                                ; there is no carry and the previous
4858
                                ; position is in DE.
4859
        EX      DE,HL           ; transfer result to DE if looping again.
4860
                                ; transfer DE to HL to be used as K-CUR
4861
                                ; if exiting loop.
4862
        JR      C,L103E         ; back to ED-EDGE-1 if cursor not matched.
4863
 
4864
        RET                     ; return.
4865
 
4866
; -----------------
4867
; Cursor up editing
4868
; -----------------
4869
; The main screen displays part of the BASIC program and the user wishes
4870
; to move up one line scrolling if necessary.
4871
; This has no alternative use in input mode.
4872
 
4873
;; ED-UP
4874
L1059:  BIT     5,(IY+$37)      ; test FLAGX  - input mode ?
4875
        RET     NZ              ; return if not in editor - to ED-LOOP.
4876
 
4877
        LD      HL,($5C49)      ; get current line from E_PPC
4878
        CALL    L196E           ; routine LINE-ADDR gets address
4879
        EX      DE,HL           ; and previous in DE
4880
        CALL    L1695           ; routine LINE-NO gets prev line number
4881
        LD      HL,$5C4A        ; set HL to E_PPC_hi as next routine stores
4882
                                ; top first.
4883
        CALL    L191C           ; routine LN-STORE loads DE value to HL
4884
                                ; high byte first - E_PPC_lo takes E
4885
 
4886
; this branch is also taken from ed-down.
4887
 
4888
;; ED-LIST
4889
L106E:  CALL    L1795           ; routine AUTO-LIST lists to upper screen
4890
                                ; including adjusted current line.
4891
        LD      A,$00           ; select lower screen again
4892
        JP      L1601           ; exit via CHAN-OPEN to ED-LOOP
4893
 
4894
; --------------------------------
4895
; Use of symbol and graphics codes
4896
; --------------------------------
4897
; These will not be encountered with the keyboard but would be handled
4898
; otherwise as follows.
4899
; As noted earlier, Vickers says there should have been an entry in
4900
; the KEYS table for chr$ 6 which also pointed here.
4901
; If, for simplicity, two Spectrums were both using #15 as a bi-directional
4902
; channel connected to each other:-
4903
; then when the other Spectrum has said PRINT #15; x, y
4904
; input #15; i ; j  would treat the comma control as a newline and the
4905
; control would skip to input j.
4906
; You can get round the missing chr$ 6 handler by sending multiple print
4907
; items separated by a newline '.
4908
 
4909
; chr$14 would have the same functionality.
4910
 
4911
; This is chr$ 14.
4912
;; ED-SYMBOL
4913
L1076:  BIT     7,(IY+$37)      ; test FLAGX - is this INPUT LINE ?
4914
        JR      Z,L1024         ; back to ED-ENTER if not to treat as if
4915
                                ; enter had been pressed.
4916
                                ; else continue and add code to buffer.
4917
 
4918
; Next is chr$ 15
4919
; Note that ADD-CHAR precedes the table so we can't offset to it directly.
4920
 
4921
;; ED-GRAPH
4922
L107C:  JP      L0F81           ; jump back to ADD-CHAR
4923
 
4924
; --------------------
4925
; Editor error routine
4926
; --------------------
4927
; If an error occurs while editing, or inputting, then ERR_SP
4928
; points to the stack location holding address ED_ERROR.
4929
 
4930
;; ED-ERROR
4931
L107F:  BIT     4,(IY+$30)      ; test FLAGS2  - is K channel in use ?
4932
        JR      Z,L1026         ; back to ED-END if not.
4933
 
4934
; but as long as we're editing lines or inputting from the keyboard, then
4935
; we've run out of memory so give a short rasp.
4936
 
4937
        LD      (IY+$00),$FF    ; reset ERR_NR to 'OK'.
4938
        LD      D,$00           ; prepare for beeper.
4939
        LD      E,(IY-$02)      ; use RASP value.
4940
        LD      HL,$1A90        ; set a duration.
4941
        CALL    L03B5           ; routine BEEPER emits a warning rasp.
4942
        JP      L0F30           ; to ED-AGAIN to re-stack address of
4943
                                ; this routine and make ERR_SP point to it.
4944
 
4945
; ---------------------
4946
; Clear edit/work space
4947
; ---------------------
4948
; The editing area or workspace is cleared depending on context.
4949
; This is called from ED-EDIT to clear workspace if edit key is
4950
; used during input, to clear editing area if no program exists
4951
; and to clear editing area prior to copying the edit line to it.
4952
; It is also used by the error routine to clear the respective
4953
; area depending on FLAGX.
4954
 
4955
;; CLEAR-SP
4956
L1097:  PUSH    HL              ; preserve HL
4957
        CALL    L1190           ; routine SET-HL
4958
                                ; if in edit   HL = WORKSP-1, DE = E_LINE
4959
                                ; if in input  HL = STKBOT,   DE = WORKSP
4960
        DEC     HL              ; adjust
4961
        CALL    L19E5           ; routine RECLAIM-1 reclaims space
4962
        LD      ($5C5B),HL      ; set K_CUR to start of empty area
4963
        LD      (IY+$07),$00    ; set MODE to 'KLC'
4964
        POP     HL              ; restore HL.
4965
        RET                     ; return.
4966
 
4967
; ---------------------
4968
; Handle keyboard input
4969
; ---------------------
4970
; This is the service routine for the input stream of the keyboard
4971
; channel 'K'.
4972
 
4973
;; KEY-INPUT
4974
L10A8:  BIT     3,(IY+$02)      ; test TV_FLAG  - has a key been pressed in
4975
                                ; editor ?
4976
        CALL    NZ,L111D        ; routine ED-COPY if so to reprint the lower
4977
                                ; screen at every keystroke.
4978
        AND     A               ; clear carry - required exit condition.
4979
        BIT     5,(IY+$01)      ; test FLAGS  - has a new key been pressed ?
4980
        RET     Z               ; return if not.
4981
 
4982
        LD      A,($5C08)       ; system variable LASTK will hold last key -
4983
                                ; from the interrupt routine.
4984
        RES     5,(IY+$01)      ; update FLAGS  - reset the new key flag.
4985
        PUSH    AF              ; save the input character.
4986
        BIT     5,(IY+$02)      ; test TV_FLAG  - clear lower screen ?
4987
        CALL    NZ,L0D6E        ; routine CLS-LOWER if so.
4988
 
4989
        POP     AF              ; restore the character code.
4990
        CP      $20             ; if space or higher then
4991
        JR      NC,L111B        ; forward to KEY-DONE2 and return with carry
4992
                                ; set to signal key-found.
4993
 
4994
        CP      $10             ; with 16d INK and higher skip
4995
        JR      NC,L10FA        ; forward to KEY-CONTR.
4996
 
4997
        CP      $06             ; for 6 - 15d
4998
        JR      NC,L10DB        ; skip forward to KEY-M-CL to handle Modes
4999
                                ; and CapsLock.
5000
 
5001
; that only leaves 0-5, the flash bright inverse switches.
5002
 
5003
        LD      B,A             ; save character in B
5004
        AND     $01             ; isolate the embedded parameter (0/1).
5005
        LD      C,A             ; and store in C
5006
        LD      A,B             ; re-fetch copy (0-5)
5007
        RRA                     ; halve it 0, 1 or 2.
5008
        ADD     A,$12           ; add 18d gives 'flash', 'bright'
5009
                                ; and 'inverse'.
5010
        JR      L1105           ; forward to KEY-DATA with the 
5011
                                ; parameter (0/1) in C.
5012
 
5013
; ---
5014
 
5015
; Now separate capslock 06 from modes 7-15.
5016
 
5017
;; KEY-M-CL
5018
L10DB:  JR      NZ,L10E6        ; forward to KEY-MODE if not 06 (capslock)
5019
 
5020
        LD      HL,$5C6A        ; point to FLAGS2
5021
        LD      A,$08           ; value 00000100
5022
        XOR     (HL)            ; toggle BIT 2 of FLAGS2 the capslock bit
5023
        LD      (HL),A          ; and store result in FLAGS2 again.
5024
        JR      L10F4           ; forward to KEY-FLAG to signal no-key.
5025
 
5026
; ---
5027
 
5028
;; KEY-MODE
5029
L10E6:  CP      $0E             ; compare with chr 14d
5030
        RET     C               ; return with carry set "key found" for
5031
                                ; codes 7 - 13d leaving 14d and 15d
5032
                                ; which are converted to mode codes.
5033
 
5034
        SUB     $0D             ; subtract 13d leaving 1 and 2
5035
                                ; 1 is 'E' mode, 2 is 'G' mode.
5036
        LD      HL,$5C41        ; address the MODE system variable.
5037
        CP      (HL)            ; compare with existing value before
5038
        LD      (HL),A          ; inserting the new value.
5039
        JR      NZ,L10F4        ; forward to KEY-FLAG if it has changed.
5040
 
5041
        LD      (HL),$00        ; else make MODE zero - KLC mode
5042
                                ; Note. while in Extended/Graphics mode,
5043
                                ; the Extended Mode/Graphics key is pressed
5044
                                ; again to get out.
5045
 
5046
;; KEY-FLAG
5047
L10F4:  SET     3,(IY+$02)      ; update TV_FLAG  - show key state has changed
5048
        CP      A               ; clear carry and reset zero flags -
5049
                                ; no actual key returned.
5050
        RET                     ; make the return.
5051
 
5052
; ---
5053
 
5054
; now deal with colour controls - 16-23 ink, 24-31 paper
5055
 
5056
;; KEY-CONTR
5057
L10FA:  LD      B,A             ; make a copy of character.
5058
        AND     $07             ; mask to leave bits 0-7
5059
        LD      C,A             ; and store in C.
5060
        LD      A,$10           ; initialize to 16d - INK.
5061
        BIT     3,B             ; was it paper ?
5062
        JR      NZ,L1105        ; forward to KEY-DATA with INK 16d and
5063
                                ; colour in C.
5064
 
5065
        INC     A               ; else change from INK to PAPER (17d) if so.
5066
 
5067
;; KEY-DATA
5068
L1105:  LD      (IY-$2D),C      ; put the colour (0-7)/state(0/1) in KDATA
5069
        LD      DE,L110D        ; address: KEY-NEXT will be next input stream
5070
        JR      L1113           ; forward to KEY-CHAN to change it ...
5071
 
5072
; ---
5073
 
5074
; ... so that INPUT_AD directs control to here at next call to WAIT-KEY
5075
 
5076
;; KEY-NEXT
5077
L110D:  LD      A,($5C0D)       ; pick up the parameter stored in KDATA.
5078
        LD      DE,L10A8        ; address: KEY-INPUT will be next input stream
5079
                                ; continue to restore default channel and
5080
                                ; make a return with the control code.
5081
 
5082
;; KEY-CHAN
5083
L1113:  LD      HL,($5C4F)      ; address start of CHANNELS area using CHANS
5084
                                ; system variable.
5085
                                ; Note. One might have expected CURCHL to
5086
                                ; have been used.
5087
        INC     HL              ; step over the
5088
        INC     HL              ; output address
5089
        LD      (HL),E          ; and update the input
5090
        INC     HL              ; routine address for
5091
        LD      (HL),D          ; the next call to WAIT-KEY.
5092
 
5093
;; KEY-DONE2
5094
L111B:  SCF                     ; set carry flag to show a key has been found
5095
        RET                     ; and return.
5096
 
5097
; --------------------
5098
; Lower screen copying
5099
; --------------------
5100
; This subroutine is called whenever the line in the editing area or
5101
; input workspace is required to be printed to the lower screen.
5102
; It is by calling this routine after any change that the cursor, for
5103
; instance, appears to move to the left.
5104
; Remember the edit line will contain characters and tokens
5105
; e.g. "1000 LET a = 1" is 12 characters.
5106
 
5107
;; ED-COPY
5108
L111D:  CALL    L0D4D           ; routine TEMPS sets temporary attributes.
5109
        RES     3,(IY+$02)      ; update TV_FLAG  - signal no change in mode
5110
        RES     5,(IY+$02)      ; update TV_FLAG  - signal don't clear lower
5111
                                ; screen.
5112
        LD      HL,($5C8A)      ; fetch SPOSNL
5113
        PUSH    HL              ; and save on stack.
5114
 
5115
        LD      HL,($5C3D)      ; fetch ERR_SP
5116
        PUSH    HL              ; and save also
5117
        LD      HL,L1167        ; address: ED-FULL
5118
        PUSH    HL              ; is pushed as the error routine
5119
        LD      ($5C3D),SP      ; and ERR_SP made to point to it.
5120
 
5121
        LD      HL,($5C82)      ; fetch ECHO_E
5122
        PUSH    HL              ; and push also
5123
 
5124
        SCF                     ; set carry flag to control SET-DE
5125
        CALL    L1195           ; call routine SET-DE
5126
                                ; if in input DE = WORKSP
5127
                                ; if in edit  DE = E_LINE
5128
        EX      DE,HL           ; start address to HL
5129
 
5130
        CALL    L187D           ; routine OUT-LINE2 outputs entire line up to
5131
                                ; carriage return including initial
5132
                                ; characterized line number when present.
5133
        EX      DE,HL           ; transfer new address to DE
5134
        CALL    L18E1           ; routine OUT-CURS considers a
5135
                                ; terminating cursor.
5136
 
5137
        LD      HL,($5C8A)      ; fetch updated SPOSNL
5138
        EX      (SP),HL         ; exchange with ECHO_E on stack
5139
        EX      DE,HL           ; transfer ECHO_E to DE
5140
        CALL    L0D4D           ; routine TEMPS to re-set attributes
5141
                                ; if altered.
5142
 
5143
; the lower screen was not cleared, at the outset, so if deleting then old
5144
; text from a previous print may follow this line and requires blanking.
5145
 
5146
;; ED-BLANK
5147
L1150:  LD      A,($5C8B)       ; fetch SPOSNL_hi is current line
5148
        SUB     D               ; compare with old
5149
        JR      C,L117C         ; forward to ED-C-DONE if no blanking
5150
 
5151
        JR      NZ,L115E        ; forward to ED-SPACES if line has changed
5152
 
5153
        LD      A,E             ; old column to A
5154
        SUB     (IY+$50)        ; subtract new in SPOSNL_lo
5155
        JR      NC,L117C        ; forward to ED-C-DONE if no backfilling.
5156
 
5157
;; ED-SPACES
5158
L115E:  LD      A,$20           ; prepare a space.
5159
        PUSH    DE              ; save old line/column.
5160
        CALL    L09F4           ; routine PRINT-OUT prints a space over
5161
                                ; any text from previous print.
5162
                                ; Note. Since the blanking only occurs when
5163
                                ; using $09F4 to print to the lower screen,
5164
                                ; there is no need to vector via a RST 10
5165
                                ; and we can use this alternate set.
5166
        POP     DE              ; restore the old line column.
5167
        JR      L1150           ; back to ED-BLANK until all old text blanked.
5168
 
5169
; -------
5170
; ED-FULL
5171
; -------
5172
; this is the error routine addressed by ERR_SP. This is not for the out of
5173
; memory situation as we're just printing. The pitch and duration are exactly
5174
; the same as used by ED-ERROR from which this has been augmented. The
5175
; situation is that the lower screen is full and a rasp is given to suggest
5176
; that this is perhaps not the best idea you've had that day.
5177
 
5178
;; ED-FULL
5179
L1167:  LD      D,$00           ; prepare to moan.
5180
        LD      E,(IY-$02)      ; fetch RASP value.
5181
        LD      HL,$1A90        ; set duration.
5182
        CALL    L03B5           ; routine BEEPER.
5183
        LD      (IY+$00),$FF    ; clear ERR_NR.
5184
        LD      DE,($5C8A)      ; fetch SPOSNL.
5185
        JR      L117E           ; forward to ED-C-END
5186
 
5187
; -------
5188
 
5189
; the exit point from line printing continues here.
5190
 
5191
;; ED-C-DONE
5192
L117C:  POP     DE              ; fetch new line/column.
5193
        POP     HL              ; fetch the error address.
5194
 
5195
; the error path rejoins here.
5196
 
5197
;; ED-C-END
5198
L117E:  POP     HL              ; restore the old value of ERR_SP.
5199
        LD      ($5C3D),HL      ; update the system variable ERR_SP
5200
        POP     BC              ; old value of SPOSN_L
5201
        PUSH    DE              ; save new value
5202
        CALL    L0DD9           ; routine CL-SET and PO-STORE
5203
                                ; update ECHO_E and SPOSN_L from BC
5204
        POP     HL              ; restore new value
5205
        LD      ($5C82),HL      ; and update ECHO_E
5206
        LD      (IY+$26),$00    ; make error pointer X_PTR_hi out of bounds
5207
        RET                     ; return
5208
 
5209
; -----------------------------------------------
5210
; Point to first and last locations of work space
5211
; -----------------------------------------------
5212
; These two nested routines ensure that the appropriate pointers are
5213
; selected for the editing area or workspace. The routines that call
5214
; these routines are designed to work on either area.
5215
 
5216
; this routine is called once
5217
;; SET-HL
5218
L1190:  LD      HL,($5C61)      ; fetch WORKSP to HL.
5219
        DEC     HL              ; point to last location of editing area.
5220
        AND     A               ; clear carry to limit exit points to first
5221
                                ; or last.
5222
 
5223
; this routine is called with carry set and exits at a conditional return.
5224
 
5225
;; SET-DE
5226
L1195:  LD      DE,($5C59)      ; fetch E_LINE to DE
5227
        BIT     5,(IY+$37)      ; test FLAGX  - Input Mode ?
5228
        RET     Z               ; return now if in editing mode
5229
 
5230
        LD      DE,($5C61)      ; fetch WORKSP to DE
5231
        RET     C               ; return if carry set ( entry = set-de)
5232
 
5233
        LD      HL,($5C63)      ; fetch STKBOT to HL as well
5234
        RET                     ; and return  (entry = set-hl (in input))
5235
 
5236
; -------------------------------
5237
; Remove floating point from line
5238
; -------------------------------
5239
; When a BASIC LINE or the INPUT BUFFER is parsed any numbers will have
5240
; an invisible chr 14d inserted after them and the 5-byte integer or
5241
; floating point form inserted after that. Similar invisible value holders
5242
; are also created after the numeric and string variables in a DEF FN list.
5243
; This routine removes these 'compiled' numbers from the edit line or
5244
; input workspace.
5245
 
5246
;; REMOVE-FP
5247
L11A7:  LD      A,(HL)          ; fetch character
5248
        CP      $0E             ; is it the number marker ?
5249
        LD      BC,$0006        ; prepare for six bytes
5250
        CALL    Z,L19E8         ; routine RECLAIM-2 reclaims space if $0E
5251
        LD      A,(HL)          ; reload next (or same) character
5252
        INC     HL              ; and advance address
5253
        CP      $0D             ; end of line or input buffer ?
5254
        JR      NZ,L11A7        ; back to REMOVE-FP until entire line done.
5255
 
5256
        RET                     ; return
5257
 
5258
 
5259
;*********************************
5260
;** Part 6. EXECUTIVE ROUTINES  **
5261
;*********************************
5262
 
5263
 
5264
; The memory.
5265
;
5266
; +---------+-----------+------------+--------------+-------------+--
5267
; | BASIC   |  Display  | Attributes | ZX Printer   |    System   | 
5268
; |  ROM    |   File    |    File    |   Buffer     |  Variables  | 
5269
; +---------+-----------+------------+--------------+-------------+--
5270
; ^         ^           ^            ^              ^             ^
5271
; $0000   $4000       $5800        $5B00          $5C00         $5CB6 = CHANS 
5272
;
5273
;
5274
;  --+----------+---+---------+-----------+---+------------+--+---+--
5275
;    | Channel  |$80|  BASIC  | Variables |$80| Edit Line  |NL|$80|
5276
;    |   Info   |   | Program |   Area    |   | or Command |  |   |
5277
;  --+----------+---+---------+-----------+---+------------+--+---+--
5278
;    ^              ^         ^               ^                   ^
5279
;  CHANS           PROG      VARS           E_LINE              WORKSP
5280
;
5281
;
5282
;                             ---5-->         <---2---  <--3---
5283
;  --+-------+--+------------+-------+-------+---------+-------+-+---+------+
5284
;    | INPUT |NL| Temporary  | Calc. | Spare | Machine | GOSUB |?|$3E| UDGs |
5285
;    | data  |  | Work Space | Stack |       |  Stack  | Stack | |   |      |
5286
;  --+-------+--+------------+-------+-------+---------+-------+-+---+------+
5287
;    ^                       ^       ^       ^                   ^   ^      ^
5288
;  WORKSP                  STKBOT  STKEND   sp               RAMTOP UDG  P_RAMT
5289
;                                                                         
5290
 
5291
; -------------------
5292
; Handle NEW command
5293
; -------------------
5294
; The NEW command is about to set all RAM below RAMTOP to zero and
5295
; then re-initialize the system. All RAM above RAMTOP should, and will be,
5296
; preserved.
5297
; There is nowhere to store values in RAM or on the stack which becomes
5298
; inoperable. Similarly PUSH and CALL instructions cannot be used to
5299
; store values or section common code. The alternate register set is the only
5300
; place available to store 3 persistent 16-bit system variables.
5301
 
5302
;; NEW
5303
L11B7:  DI                      ; disable interrupts - machine stack will be
5304
                                ; cleared.
5305
        LD      A,$FF           ; flag coming from NEW.
5306
        LD      DE,($5CB2)      ; fetch RAMTOP as top value.
5307
        EXX                     ; switch in alternate set.
5308
        LD      BC,($5CB4)      ; fetch P-RAMT differs on 16K/48K machines.
5309
        LD      DE,($5C38)      ; fetch RASP/PIP.
5310
        LD      HL,($5C7B)      ; fetch UDG    differs on 16K/48K machines.
5311
        EXX                     ; switch back to main set and continue into...
5312
 
5313
; ---------------------------
5314
; Main entry (initialization)
5315
; ---------------------------
5316
; This common code tests ram and sets it to zero re-initializing
5317
; all the non-zero system variables and channel information.
5318
; The A register tells if coming from START or NEW
5319
 
5320
;; START-NEW
5321
L11CB:  LD      B,A             ; save the flag for later branching.
5322
 
5323
        LD      A,$07           ; select a white border
5324
        OUT     ($FE),A         ; and set it now.
5325
 
5326
        LD      A,$3F           ; load accumulator with last page in ROM.
5327
        LD      I,A             ; set the I register - this remains constant
5328
                                ; and can't be in range $40 - $7F as 'snow'
5329
                                ; appears on the screen.
5330
        NOP                     ; these seem unnecessary.
5331
        NOP                     ;
5332
        NOP                     ;
5333
        NOP                     ;
585 savelij 5334
                RST 8
5335
                DB _TAPE_INIT
384 savelij 5336
 
5337
; ------------
5338
; Check RAM
5339
; ------------
5340
; Typically a Spectrum will have 16K or 48K of Ram and this code will
5341
; test it all till it finds an unpopulated location or, less likely, a
5342
; faulty location. Usually it stops when it reaches the top $FFFF or
5343
; in the case of NEW the supplied top value. The entire screen turns
5344
; black with sometimes red stripes on black paper visible.
5345
 
5346
;; ram-check
5347
L11DA:  LD      H,D             ; transfer the top value to
5348
        LD      L,E             ; the HL register pair.
5349
 
5350
;; RAM-FILL
5351
L11DC:  LD      (HL),$02        ; load with 2 - red ink on black paper
5352
        DEC     HL              ; next lower
5353
        CP      H               ; have we reached ROM - $3F ?
5354
        JR      NZ,L11DC        ; back to RAM-FILL if not.
5355
 
5356
;; RAM-READ
5357
L11E2:  AND     A               ; clear carry - prepare to subtract
5358
        SBC     HL,DE           ; subtract and add back setting
5359
        ADD     HL,DE           ; carry when back at start.
5360
        INC     HL              ; and increment for next iteration.
5361
        JR      NC,L11EF        ; forward to RAM-DONE if we've got back to
5362
                                ; starting point with no errors.
5363
 
5364
        DEC     (HL)            ; decrement to 1.
5365
        JR      Z,L11EF         ; forward to RAM-DONE if faulty.
5366
 
5367
        DEC     (HL)            ; decrement to zero.
5368
        JR      Z,L11E2         ; back to RAM-READ if zero flag was set.
5369
 
5370
;; RAM-DONE
5371
L11EF:  DEC     HL              ; step back to last valid location.
5372
        EXX                     ; regardless of state, set up possibly
5373
                                ; stored system variables in case from NEW.
5374
        LD      ($5CB4),BC      ; insert P-RAMT.
5375
        LD      ($5C38),DE      ; insert RASP/PIP.
5376
        LD      ($5C7B),HL      ; insert UDG.
5377
        EXX                     ; switch in main set.
5378
        INC     B               ; now test if we arrived here from NEW.
5379
        JR      Z,L1219         ; forward to RAM-SET if we did.
5380
 
5381
; this section applies to START only.
5382
 
5383
        LD      ($5CB4),HL      ; set P-RAMT to the highest working RAM
5384
                                ; address.
5385
        LD      DE,$3EAF        ; address of last byte of 'U' bitmap in ROM.
5386
        LD      BC,$00A8        ; there are 21 user defined graphics.
5387
        EX      DE,HL           ; switch pointers and make the UDGs a
5388
        LDDR                    ; copy of the standard characters A - U.
5389
        EX      DE,HL           ; switch the pointer to HL.
5390
        INC     HL              ; update to start of 'A' in RAM.
5391
        LD      ($5C7B),HL      ; make UDG system variable address the first
5392
                                ; bitmap.
5393
        DEC     HL              ; point at RAMTOP again.
5394
 
5395
        LD      BC,$0040        ; set the values of
5396
        LD      ($5C38),BC      ; the PIP and RASP system variables.
5397
 
5398
; the NEW command path rejoins here.
5399
 
5400
;; RAM-SET
5401
L1219:  LD      ($5CB2),HL      ; set system variable RAMTOP to HL.
5402
 
5403
                LD HL,CHARS-0X100       ;$3C00        ; a strange place to set the pointer to the 
5404
        LD      ($5C36),HL      ; character set, CHARS - as no printing yet.
5405
 
5406
        LD      HL,($5CB2)      ; fetch RAMTOP to HL again as we've lost it.
5407
 
5408
        LD      (HL),$3E        ; top of user ram holds GOSUB end marker
5409
                                ; an impossible line number - see RETURN.
5410
                                ; no significance in the number $3E. It has
5411
                                ; been traditional since the ZX80.
5412
 
5413
        DEC     HL              ; followed by empty byte (not important).
5414
        LD      SP,HL           ; set up the machine stack pointer.
5415
        DEC     HL              ;
5416
        DEC     HL              ;
5417
        LD      ($5C3D),HL      ; ERR_SP is where the error pointer is
5418
                                ; at moment empty - will take address MAIN-4
5419
                                ; at the call preceding that address,
5420
                                ; although interrupts and calls will make use
5421
                                ; of this location in meantime.
5422
 
5423
        IM      1               ; select interrupt mode 1.
5424
        LD      IY,$5C3A        ; set IY to ERR_NR. IY can reach all standard
5425
                                ; system variables but shadow ROM system
5426
                                ; variables will be mostly out of range.
5427
 
5428
        EI                      ; enable interrupts now that we have a stack.
5429
 
5430
        LD      HL,$5CB6        ; the address of the channels - initially
5431
                                ; following system variables.
5432
        LD      ($5C4F),HL      ; set the CHANS system variable.
5433
 
5434
        LD      DE,L15AF        ; address: init-chan in ROM.
5435
        LD      BC,$0015        ; there are 21 bytes of initial data in ROM.
5436
        EX      DE,HL           ; swap the pointers.
5437
        LDIR                    ; copy the bytes to RAM.
5438
 
5439
        EX      DE,HL           ; swap pointers. HL points to program area.
5440
        DEC     HL              ; decrement address.
5441
        LD      ($5C57),HL      ; set DATADD to location before program area.
5442
        INC     HL              ; increment again.
5443
 
5444
        LD      ($5C53),HL      ; set PROG the location where BASIC starts.
5445
        LD      ($5C4B),HL      ; set VARS to same location with a
5446
        LD      (HL),$80        ; variables end-marker.
5447
        INC     HL              ; advance address.
5448
        LD      ($5C59),HL      ; set E_LINE, where the edit line
5449
                                ; will be created.
5450
                                ; Note. it is not strictly necessary to
5451
                                ; execute the next fifteen bytes of code
5452
                                ; as this will be done by the call to SET-MIN.
5453
                                ; --
5454
        LD      (HL),$0D        ; initially just has a carriage return
5455
        INC     HL              ; followed by
5456
        LD      (HL),$80        ; an end-marker.
5457
        INC     HL              ; address the next location.
5458
        LD      ($5C61),HL      ; set WORKSP - empty workspace.
5459
        LD      ($5C63),HL      ; set STKBOT - bottom of the empty stack.
5460
        LD      ($5C65),HL      ; set STKEND to the end of the empty stack.
5461
                                ; --
5462
        LD      A,$38           ; the colour system is set to white paper,
5463
                                ; black ink, no flash or bright.
5464
        LD      ($5C8D),A       ; set ATTR_P permanent colour attributes.
5465
        LD      ($5C8F),A       ; set ATTR_T temporary colour attributes.
5466
        LD      ($5C48),A       ; set BORDCR the border colour/lower screen
5467
                                ; attributes.
5468
 
5469
        LD      HL,$0523        ; The keyboard repeat and delay values
5470
        LD      ($5C09),HL      ; are loaded to REPDEL and REPPER.
5471
 
5472
        DEC     (IY-$3A)        ; set KSTATE-0 to $FF.
5473
        DEC     (IY-$36)        ; set KSTATE-4 to $FF.
5474
                                ; thereby marking both available.
5475
 
5476
        LD      HL,L15C6        ; set source to ROM Address: init-strm
5477
        LD      DE,$5C10        ; set destination to system variable STRMS-FD
5478
        LD      BC,$000E        ; copy the 14 bytes of initial 7 streams data
5479
        LDIR                    ; from ROM to RAM.
5480
 
5481
        SET     1,(IY+$01)      ; update FLAGS  - signal printer in use.
5482
 
5483
;===============================
5484
                CALL PRINTER_INITER
5485
;===============================
5486
 
5487
        LD      (IY+$31),$02    ; set DF_SZ the lower screen display size to
5488
                                ; two lines
5489
        CALL    L0D6B           ; call routine CLS to set up system
5490
                                ; variables associated with screen and clear
5491
                                ; the screen and set attributes.
5492
        XOR     A               ; clear accumulator so that we can address
5493
        LD      DE,L1539 - 1    ; the message table directly.
5494
        CALL    L0C0A           ; routine PO-MSG puts
5495
                                ; '(c) 1982 Sinclair Research Ltd'
5496
                                ; at bottom of display.
5497
        SET     5,(IY+$02)      ; update TV_FLAG  - signal lower screen will
5498
                                ; require clearing.
5499
 
5500
        JR      L12A9           ; forward to MAIN-1
5501
 
5502
; -------------------
5503
; Main execution loop
5504
; -------------------
5505
;
5506
;
5507
 
5508
;; MAIN-EXEC
5509
L12A2:  LD      (IY+$31),$02    ; set DF_SZ lower screen display file
5510
                                ; size to 2 lines.
5511
        CALL    L1795           ; routine AUTO-LIST
5512
 
5513
;; MAIN-1
5514
L12A9:  CALL    L16B0           ; routine SET-MIN clears work areas.
5515
 
5516
;; MAIN-2
5517
L12AC:  LD      A,$00           ; select channel 'K' the keyboard
5518
        CALL    L1601           ; routine CHAN-OPEN opens it
5519
        CALL    L0F2C           ; routine EDITOR is called.
5520
                                ; Note the above routine is where the Spectrum
5521
                                ; waits for user-interaction. Perhaps the
5522
                                ; most common input at this stage
5523
                                ; is LOAD "".
5524
        CALL    L1B17           ; routine LINE-SCAN scans the input.
5525
        BIT     7,(IY+$00)      ; test ERR_NR - will be $FF if syntax
5526
                                ; is correct.
5527
        JR      NZ,L12CF        ; forward, if correct, to MAIN-3.
5528
 
5529
; 
5530
 
5531
        BIT     4,(IY+$30)      ; test FLAGS2 - K channel in use ?
5532
        JR      Z,L1303         ; forward to MAIN-4 if not.
5533
 
5534
;
5535
 
5536
        LD      HL,($5C59)      ; an editing error so address E_LINE.
5537
        CALL    L11A7           ; routine REMOVE-FP removes the hidden
5538
                                ; floating-point forms.
5539
        LD      (IY+$00),$FF    ; system variable ERR_NR is reset to 'OK'.
5540
        JR      L12AC           ; back to MAIN-2 to allow user to correct.
5541
 
5542
; ---
5543
 
5544
; the branch was here if syntax has passed test.
5545
 
5546
;; MAIN-3
5547
L12CF:  LD      HL,($5C59)      ; fetch the edit line address from E_LINE.
5548
        LD      ($5C5D),HL      ; system variable CH_ADD is set to first
5549
                                ; character of edit line.
5550
                                ; Note. the above two instructions are a little
5551
                                ; inadequate. 
5552
                                ; They are repeated with a subtle difference 
5553
                                ; at the start of the next subroutine and are 
5554
                                ; therefore not required above.
5555
 
5556
        CALL    L19FB           ; routine E-LINE-NO will fetch any line
5557
                                ; number to BC if this is a program line.
5558
 
5559
        LD      A,B             ; test if the number of
5560
        OR      C               ; the line is non-zero.
5561
        JP      NZ,L155D        ; jump forward to MAIN-ADD if so to add the 
5562
                                ; line to the BASIC program.
5563
 
5564
; Has the user just pressed the ENTER key ?
5565
 
5566
        RST     18H             ; GET-CHAR gets character addressed by CH_ADD.
5567
        CP      $0D             ; is it a carriage return ?
5568
        JR      Z,L12A2         ; back to MAIN-EXEC if so for an automatic
5569
                                ; listing.
5570
 
5571
; this must be a direct command.
5572
 
5573
        BIT     0,(IY+$30)      ; test FLAGS2 - clear the main screen ?
5574
        CALL    NZ,L0DAF        ; routine CL-ALL, if so, e.g. after listing.
5575
        CALL    L0D6E           ; routine CLS-LOWER anyway.
5576
        LD      A,$19           ; compute scroll count to 25 minus
5577
        SUB     (IY+$4F)        ; value of S_POSN_hi.
5578
        LD      ($5C8C),A       ; update SCR_CT system variable.
5579
        SET     7,(IY+$01)      ; update FLAGS - signal running program.
5580
        LD      (IY+$00),$FF    ; set ERR_NR to 'OK'.
5581
        LD      (IY+$0A),$01    ; set NSPPC to one for first statement.
5582
        CALL    L1B8A           ; call routine LINE-RUN to run the line.
5583
                                ; sysvar ERR_SP therefore addresses MAIN-4
5584
 
5585
; Examples of direct commands are RUN, CLS, LOAD "", PRINT USR 40000,
5586
; LPRINT "A"; etc..
5587
; If a user written machine-code program disables interrupts then it
5588
; must enable them to pass the next step. We also jumped to here if the
5589
; keyboard was not being used.
5590
 
5591
;; MAIN-4
5592
L1303:  HALT                    ; wait for interrupt.
5593
 
5594
        RES     5,(IY+$01)      ; update FLAGS - signal no new key.
5595
        BIT     1,(IY+$30)      ; test FLAGS2 - is printer buffer clear ?
5596
        CALL    NZ,L0ECD        ; call routine COPY-BUFF if not.
5597
                                ; Note. the programmer has neglected
5598
                                ; to set bit 1 of FLAGS first.
5599
 
5600
        LD      A,($5C3A)       ; fetch ERR_NR
5601
        INC     A               ; increment to give true code.
5602
 
5603
; Now deal with a runtime error as opposed to an editing error.
5604
; However if the error code is now zero then the OK message will be printed.
5605
 
5606
;; MAIN-G
5607
L1313:  PUSH    AF              ; save the error number.
5608
 
5609
        LD      HL,$0000        ; prepare to clear some system variables.
5610
        LD      (IY+$37),H      ; clear all the bits of FLAGX.
5611
        LD      (IY+$26),H      ; blank X_PTR_hi to suppress error marker.
5612
        LD      ($5C0B),HL      ; blank DEFADD to signal that no defined
5613
                                ; function is currently being evaluated.
5614
 
5615
        LD      HL,$0001        ; explicit - inc hl would do.
5616
        LD      ($5C16),HL      ; ensure STRMS-00 is keyboard.
5617
 
5618
        CALL    L16B0           ; routine SET-MIN clears workspace etc.
5619
        RES     5,(IY+$37)      ; update FLAGX - signal in EDIT not INPUT mode.
5620
                                ; Note. all the bits were reset earlier.
5621
 
5622
        CALL    L0D6E           ; call routine CLS-LOWER.
5623
        SET     5,(IY+$02)      ; update TV_FLAG - signal lower screen
5624
                                ; requires clearing.
5625
 
5626
        POP     AF              ; bring back the error number
5627
        LD      B,A             ; and make a copy in B.
5628
        CP      $0A             ; is it a print-ready digit ?
5629
        JR      C,L133C         ; forward to MAIN-5 if so.
5630
 
5631
        ADD     A,$07           ; add ASCII offset to letters.
5632
 
5633
;; MAIN-5
5634
L133C:  CALL    L15EF           ; call routine OUT-CODE to print the code.
5635
 
5636
        LD      A,$20           ; followed by a space.
5637
        RST     10H             ; PRINT-A
5638
 
5639
        LD      A,B             ; fetch stored report code.
5640
        LD      DE,L1391        ; address: rpt-mesgs.
5641
        CALL    L0C0A           ; call routine PO-MSG to print.
5642
 
678 savelij 5643
X1349           CALL L3B3B              ; Spectrum 128 patch
384 savelij 5644
                NOP
5645
 
5646
L134D:  CALL    L0C0A           ; routine PO-MSG prints them although it would
5647
                                ; be more succinct to use RST $10.
5648
 
5649
        LD      BC,($5C45)      ; fetch PPC the current line number.
5650
        CALL    L1A1B           ; routine OUT-NUM-1 will print that
5651
        LD      A,$3A           ; then a ':'.
5652
        RST     10H             ; PRINT-A
5653
 
5654
        LD      C,(IY+$0D)      ; then SUBPPC for statement
5655
        LD      B,$00           ; limited to 127
5656
        CALL    L1A1B           ; routine OUT-NUM-1
5657
 
5658
        CALL    L1097           ; routine CLEAR-SP clears editing area.
5659
                                ; which probably contained 'RUN'.
5660
        LD      A,($5C3A)       ; fetch ERR_NR again
5661
        INC     A               ; test for no error originally $FF.
5662
        JR      Z,L1386         ; forward to MAIN-9 if no error.
5663
 
5664
        CP      $09             ; is code Report 9 STOP ?
5665
        JR      Z,L1373         ; forward to MAIN-6 if so
5666
 
5667
        CP      $15             ; is code Report L Break ?
5668
        JR      NZ,L1376        ; forward to MAIN-7 if not
5669
 
5670
; Stop or Break was encountered so consider CONTINUE.
5671
 
5672
;; MAIN-6
5673
L1373:  INC     (IY+$0D)        ; increment SUBPPC to next statement.
5674
 
5675
;; MAIN-7
5676
L1376:  LD      BC,$0003        ; prepare to copy 3 system variables to
5677
        LD      DE,$5C70        ; address OSPPC - statement for CONTINUE.
5678
                                ; also updating OLDPPC line number below.
5679
 
5680
        LD      HL,$5C44        ; set source top to NSPPC next statement.
5681
        BIT     7,(HL)          ; did BREAK occur before the jump ?
5682
                                ; e.g. between GO TO and next statement.
5683
        JR      Z,L1384         ; skip forward to MAIN-8, if not, as setup
5684
                                ; is correct.
5685
 
5686
        ADD     HL,BC           ; set source to SUBPPC number of current
5687
                                ; statement/line which will be repeated.
5688
 
5689
;; MAIN-8
5690
L1384:  LDDR                    ; copy PPC to OLDPPC and SUBPPC to OSPCC
5691
                                ; or NSPPC to OLDPPC and NEWPPC to OSPCC
5692
 
5693
;; MAIN-9
5694
L1386:  LD      (IY+$0A),$FF    ; update NSPPC - signal 'no jump'.
5695
        RES     3,(IY+$01)      ; update FLAGS  - signal use 'K' mode for
5696
                                ; the first character in the editor and
5697
        JP      L12AC           ; jump back to MAIN-2.
5698
 
5699
 
5700
; ----------------------
5701
; Canned report messages
5702
; ----------------------
5703
; The Error reports with the last byte inverted. The first entry
5704
; is a dummy entry. The last, which begins with $7F, the Spectrum
5705
; character for copyright symbol, is placed here for convenience
5706
; as is the preceding comma and space.
5707
; The report line must accommodate a 4-digit line number and a 3-digit
5708
; statement number which limits the length of the message text to twenty 
5709
; characters.
5710
; e.g.  "B Integer out of range, 1000:127"
5711
 
5712
;; rpt-mesgs
5713
L1391           DB $80
5714
                DC "OK"                         ;DB    'O','K'+$80              ; 0
5715
                DC "NEXT without FOR"           ;DEFM    "NEXT without FO"
5716
                                                ;DB    'R'+$80          ; 1
5717
                DC "Variable not found"         ;DEFM    "Variable not foun"
5718
                                                ;DB    'd'+$80          ; 2
5719
                DC "Subscript wrong"            ;DEFM    "Subscript wron"
5720
                                                ;DB    'g'+$80          ; 3
5721
                DC "Out of memory"              ;DEFM    "Out of memor"
5722
                                                ;DB    'y'+$80          ; 4
5723
                DC "Out of screen"              ;DEFM    "Out of scree"
5724
                                                ;DB    'n'+$80          ; 5
5725
                DC "Number too big"             ;DEFM    "Number too bi"
5726
                                                ;DB    'g'+$80          ; 6
5727
                DC "RETURN without GOSUB"       ;DEFM    "RETURN without GOSU"
5728
                                                ;DB    'B'+$80          ; 7
5729
                DC "End of file"                ;DEFM    "End of fil"
5730
                                                ;DB    'e'+$80          ; 8
5731
                DC "STOP statement"             ;DEFM    "STOP statemen"
5732
                                                ;DB    't'+$80          ; 9
5733
                DC "Invalid argument"           ;DEFM    "Invalid argumen"
5734
                                                ;DB    't'+$80          ; A
5735
                DC "Integer out of range"       ;DEFM    "Integer out of rang"
5736
                                                ;DB    'e'+$80          ; B
5737
                DC "Nonsense in BASIC"          ;DEFM    "Nonsense in BASI"
5738
                                                ;DB    'C'+$80          ; C
5739
                DC "BREAK - CONT repeats"       ;DEFM    "BREAK - CONT repeat"
5740
                                                ;DB    's'+$80          ; D
5741
                DC "Out of DATA"                ;DEFM    "Out of DAT"
5742
                                                ;DB    'A'+$80          ; E
5743
                DC "Invalid file name"          ;DEFM    "Invalid file nam"
5744
                                                ;DB    'e'+$80          ; F
5745
                DC "No room for line"           ;DEFM    "No room for lin"
5746
                                                ;DB    'e'+$80          ; G
5747
                DC "STOP in INPUT"              ;DEFM    "STOP in INPU"
5748
                                                ;DB    'T'+$80          ; H
5749
                DC "FOR without NEXT"           ;DEFM    "FOR without NEX"
5750
                                                ;DB    'T'+$80          ; I
5751
                DC "Invalid I/O device"         ;DEFM    "Invalid I/O devic"
5752
                                                ;DB    'e'+$80          ; J
5753
                DC "Invalid colour"             ;DEFM    "Invalid colou"
5754
                                                ;DB    'r'+$80          ; K
5755
                DC "BREAK into program"         ;DEFM    "BREAK into progra"
5756
                                                ;DB    'm'+$80          ; L
5757
                DC "RAMTOP no good"             ;DEFM    "RAMTOP no goo"
5758
                                                ;DB    'd'+$80          ; M
5759
                DC "Statement lost"             ;DEFM    "Statement los"
5760
                                                ;DB    't'+$80          ; N
5761
                DC "Invalid stream"             ;DEFM    "Invalid strea"
5762
                                                ;DB    'm'+$80          ; O
5763
                DC "FN without DEF"             ;DEFM    "FN without DE"
5764
                                                ;DB    'F'+$80          ; P
5765
                DC "Parameter error"            ;DEFM    "Parameter erro"
5766
                                                ;DB    'r'+$80          ; Q
5767
                DC "Tape loading error"         ;DEFM    "Tape loading erro"
5768
                                                ;DB    'r'+$80          ; R
5769
L1536           EQU $-1
5770
;; comma-sp   
5771
L1537           DC ", "                         ;DB    ',',' '+$80              ; used in report line.
5772
;; copyright
5773
L1539           DB $7F                          ; copyright
5774
                DC " 1982 Sinclair Research Ltd"        ;DEFM    " 1982 Sinclair Research Lt"
5775
                                                        ;DB    'd'+$80
5776
 
5777
 
5778
; -------------
5779
; REPORT-G
5780
; -------------
5781
; Note ERR_SP points here during line entry which allows the
5782
; normal 'Out of Memory' report to be augmented to the more
5783
; precise 'No Room for line' report.
5784
 
5785
;; REPORT-G
5786
; No Room for line
5787
L1555:  LD      A,$10           ; i.e. 'G' -$30 -$07
5788
        LD      BC,$0000        ; this seems unnecessary.
5789
        JP      L1313           ; jump back to MAIN-G
5790
 
5791
; -----------------------------
5792
; Handle addition of BASIC line
5793
; -----------------------------
5794
; Note this is not a subroutine but a branch of the main execution loop.
5795
; System variable ERR_SP still points to editing error handler.
5796
; A new line is added to the BASIC program at the appropriate place.
5797
; An existing line with same number is deleted first.
5798
; Entering an existing line number deletes that line.
5799
; Entering a non-existent line allows the subsequent line to be edited next.
5800
 
5801
;; MAIN-ADD
5802
L155D:  LD      ($5C49),BC      ; set E_PPC to extracted line number.
5803
        LD      HL,($5C5D)      ; fetch CH_ADD - points to location after the
5804
                                ; initial digits (set in E_LINE_NO).
5805
        EX      DE,HL           ; save start of BASIC in DE.
5806
 
5807
        LD      HL,L1555        ; Address: REPORT-G
5808
        PUSH    HL              ; is pushed on stack and addressed by ERR_SP.
5809
                                ; the only error that can occur is
5810
                                ; 'Out of memory'.
5811
 
5812
        LD      HL,($5C61)      ; fetch WORKSP - end of line.
5813
        SCF                     ; prepare for true subtraction.
5814
        SBC     HL,DE           ; find length of BASIC and
5815
        PUSH    HL              ; save it on stack.
5816
        LD      H,B             ; transfer line number
5817
        LD      L,C             ; to HL register.
5818
        CALL    L196E           ; routine LINE-ADDR will see if
5819
                                ; a line with the same number exists.
5820
        JR      NZ,L157D        ; forward if no existing line to MAIN-ADD1.
5821
 
5822
        CALL    L19B8           ; routine NEXT-ONE finds the existing line.
5823
        CALL    L19E8           ; routine RECLAIM-2 reclaims it.
5824
 
5825
;; MAIN-ADD1
5826
L157D:  POP     BC              ; retrieve the length of the new line.
5827
        LD      A,C             ; and test if carriage return only
5828
        DEC     A               ; i.e. one byte long.
5829
        OR      B               ; result would be zero.
5830
        JR      Z,L15AB         ; forward to MAIN-ADD2 is so.
5831
 
5832
        PUSH    BC              ; save the length again.
5833
        INC     BC              ; adjust for inclusion
5834
        INC     BC              ; of line number (two bytes)
5835
        INC     BC              ; and line length
5836
        INC     BC              ; (two bytes).
5837
        DEC     HL              ; HL points to location before the destination
5838
 
5839
        LD      DE,($5C53)      ; fetch the address of PROG
5840
        PUSH    DE              ; and save it on the stack
5841
        CALL    L1655           ; routine MAKE-ROOM creates BC spaces in
5842
                                ; program area and updates pointers.
5843
        POP     HL              ; restore old program pointer.
5844
        LD      ($5C53),HL      ; and put back in PROG as it may have been
5845
                                ; altered by the POINTERS routine.
5846
 
5847
        POP     BC              ; retrieve BASIC length
5848
        PUSH    BC              ; and save again.
5849
 
5850
        INC     DE              ; points to end of new area.
5851
        LD      HL,($5C61)      ; set HL to WORKSP - location after edit line.
5852
        DEC     HL              ; decrement to address end marker.
5853
        DEC     HL              ; decrement to address carriage return.
5854
        LDDR                    ; copy the BASIC line back to initial command.
5855
 
5856
        LD      HL,($5C49)      ; fetch E_PPC - line number.
5857
        EX      DE,HL           ; swap it to DE, HL points to last of
5858
                                ; four locations.
5859
        POP     BC              ; retrieve length of line.
5860
        LD      (HL),B          ; high byte last.
5861
        DEC     HL              ;
5862
        LD      (HL),C          ; then low byte of length.
5863
        DEC     HL              ;
5864
        LD      (HL),E          ; then low byte of line number.
5865
        DEC     HL              ;
5866
        LD      (HL),D          ; then high byte range $0 - $27 (1-9999).
5867
 
5868
;; MAIN-ADD2
5869
L15AB:  POP     AF              ; drop the address of Report G
5870
        JP      L12A2           ; and back to MAIN-EXEC producing a listing
5871
                                ; and to reset ERR_SP in EDITOR.
5872
 
5873
 
5874
; ---------------------------
5875
; Initial channel information
5876
; ---------------------------
5877
; This initial channel information is copied from ROM to RAM,
5878
; during initialization. It's new location is after the system
5879
; variables and is addressed by the system variable CHANS
5880
; which means that it can slide up and down in memory.
5881
; The table is never searched and the last character which could be anything
5882
; other than a comma provides a convenient resting place for DATADD.
5883
 
5884
;; init-chan
5885
L15AF           DW L09F4                ; PRINT-OUT
5886
                DW L10A8                ; KEY-INPUT
5887
                DB "K"
5888
                DW L09F4                ; PRINT-OUT
5889
                DW L15C4                ; REPORT-J
5890
                DB "S"
5891
                DW L0F81                ; ADD-CHAR
5892
                DW L15C4                ; REPORT-J
5893
                DB "R"
5894
;=======================================
5895
                DW PRN_TOKEN
5896
;=======================================
5897
                DW L15C4                ; REPORT-J
5898
                DB "P"
5899
 
5900
        DB    $80             ; End Marker
5901
 
5902
;; REPORT-J
5903
L15C4:  RST     08H             ; ERROR-1
5904
        DB    $12             ; Error Report: Invalid I/O device
5905
 
5906
 
5907
; -------------------
5908
; Initial stream data
5909
; -------------------
5910
; This is the initial stream data for the seven streams $FD - $03 that is
5911
; copied from ROM to the STRMS system variables area during initialization.
5912
; There are reserved locations there for another 12 streams.
5913
; Each location contains an offset to the second byte of a channel.
5914
; The first byte of a channel can't be used as that would result in an
5915
; offset of zero for some and zero is used to denote that a stream is closed.
5916
 
5917
;; init-strm
5918
L15C6:  DB    $01, $00        ; stream $FD offset to channel 'K'
5919
        DB    $06, $00        ; stream $FE offset to channel 'S'
5920
        DB    $0B, $00        ; stream $FF offset to channel 'R'
5921
 
5922
        DB    $01, $00        ; stream $00 offset to channel 'K'
5923
        DB    $01, $00        ; stream $01 offset to channel 'K'
5924
        DB    $06, $00        ; stream $02 offset to channel 'S'
5925
        DB    $10, $00        ; stream $03 offset to channel 'P'
5926
 
5927
; ----------------------------
5928
; Control for input subroutine
5929
; ----------------------------
5930
;
5931
 
5932
;; WAIT-KEY
5933
L15D4:  BIT     5,(IY+$02)      ; test TV_FLAG - clear lower screen ?
5934
        JR      NZ,L15DE        ; forward to WAIT-KEY1 if so.
5935
 
5936
        SET     3,(IY+$02)      ; update TV_FLAG - signal reprint the edit
5937
                                ; line to the lower screen.
5938
 
5939
;; WAIT-KEY1
5940
L15DE:  CALL    L15E6           ; routine INPUT-AD is called.
5941
        RET     C               ; return with acceptable keys.
5942
 
5943
        JR      Z,L15DE         ; back to WAIT-KEY1 if no key is pressed
5944
                                ; or it has been handled within INPUT-AD.
5945
 
5946
; Note. When inputting from the keyboard all characters are returned with
5947
; above conditions so this path is never taken.
5948
 
5949
;; REPORT-8
5950
L15E4:  RST     08H             ; ERROR-1
5951
        DB    $07             ; Error Report: End of file
5952
 
5953
; ------------------------------
5954
; Make HL point to input address
5955
; ------------------------------
5956
; This routine fetches the address of the input stream from the current
5957
; channel area using system variable CURCHL.
5958
 
5959
;; INPUT-AD
5960
L15E6:  EXX                     ; switch in alternate set.
5961
        PUSH    HL              ; save HL register
5962
        LD      HL,($5C51)      ; fetch address of CURCHL - current channel.
5963
        INC     HL              ; step over output routine
5964
        INC     HL              ; to point to low byte of input routine.
5965
        JR      L15F7           ; forward to CALL-SUB.
5966
 
5967
; -------------------
5968
; Main Output Routine
5969
; -------------------
5970
; The entry point OUT-CODE is called on five occasions to print
5971
; the ASCII equivalent of a value 0-9.
5972
;
5973
; PRINT-A-2 is a continuation of the RST 10 to print any character.
5974
; Both print to the current channel and the printing of control codes
5975
; may alter that channel to divert subsequent RST 10 instructions
5976
; to temporary routines. The normal channel is $09F4.
5977
 
5978
;; OUT-CODE
5979
L15EF:  LD      E,$30           ; add 48 decimal to give ASCII
5980
        ADD     A,E             ; character '0' to '9'.
5981
 
5982
;; PRINT-A-2
5983
L15F2:  EXX                     ; switch in alternate set
5984
        PUSH    HL              ; save HL register
5985
        LD      HL,($5C51)      ; fetch CURCHL the current channel.
5986
 
5987
; input-ad rejoins here also.
5988
 
5989
;; CALL-SUB
5990
L15F7:  LD      E,(HL)          ; put the low byte in E.
5991
        INC     HL              ; advance address.
5992
        LD      D,(HL)          ; put the high byte to D.
5993
        EX      DE,HL           ; transfer the stream to HL.
5994
        CALL    L162C           ; use routine CALL-JUMP.
5995
                                ; in effect CALL (HL).
5996
 
5997
        POP     HL              ; restore saved HL register.
5998
        EXX                     ; switch back to the main set and
5999
        RET                     ; return.
6000
 
6001
; ------------
6002
; Open channel
6003
; ------------
6004
; This subroutine is used by the ROM to open a channel 'K', 'S', 'R' or 'P'.
6005
; This is either for its own use or in response to a user's request, for
6006
; example, when '#' is encountered with output - PRINT, LIST etc.
6007
; or with input - INPUT, INKEY$ etc.
6008
; it is entered with a system stream $FD - $FF, or a user stream $00 - $0F
6009
; in the accumulator.
6010
 
6011
;; CHAN-OPEN
6012
L1601:  ADD     A,A             ; double the stream ($FF will become $FE etc.)
6013
        ADD     A,$16           ; add the offset to stream 0 from $5C00
6014
        LD      L,A             ; result to L
6015
        LD      H,$5C           ; now form the address in STRMS area.
6016
        LD      E,(HL)          ; fetch low byte of CHANS offset
6017
        INC     HL              ; address next
6018
        LD      D,(HL)          ; fetch high byte of offset
6019
        LD      A,D             ; test that the stream is open.
6020
        OR      E               ; zero if closed.
6021
        JR      NZ,L1610        ; forward to CHAN-OP-1 if open.
6022
 
6023
;; REPORT-Oa
6024
L160E:  RST     08H             ; ERROR-1
6025
        DB    $17             ; Error Report: Invalid stream
6026
 
6027
; continue here if stream was open. Note that the offset is from CHANS
6028
; to the second byte of the channel.
6029
 
6030
;; CHAN-OP-1
6031
L1610:  DEC     DE              ; reduce offset so it points to the channel.
6032
        LD      HL,($5C4F)      ; fetch CHANS the location of the base of
6033
                                ; the channel information area
6034
        ADD     HL,DE           ; and add the offset to address the channel.
6035
                                ; and continue to set flags.
6036
 
6037
; -----------------
6038
; Set channel flags
6039
; -----------------
6040
; This subroutine is used from ED-EDIT, str$ and read-in to reset the
6041
; current channel when it has been temporarily altered.
6042
 
6043
;; CHAN-FLAG
6044
L1615:  LD      ($5C51),HL      ; set CURCHL system variable to the
6045
                                ; address in HL
6046
        RES     4,(IY+$30)      ; update FLAGS2  - signal K channel not in use.
6047
                                ; Note. provide a default for channel 'R'.
6048
        INC     HL              ; advance past
6049
        INC     HL              ; output routine.
6050
        INC     HL              ; advance past
6051
        INC     HL              ; input routine.
6052
        LD      C,(HL)          ; pick up the letter.
6053
        LD      HL,L162D        ; address: chn-cd-lu
6054
        CALL    L16DC           ; routine INDEXER finds offset to a
6055
                                ; flag-setting routine.
6056
 
6057
        RET     NC              ; but if the letter wasn't found in the
6058
                                ; table just return now. - channel 'R'.
6059
 
6060
        LD      D,$00           ; prepare to add
6061
        LD      E,(HL)          ; offset to E
6062
        ADD     HL,DE           ; add offset to location of offset to form
6063
                                ; address of routine
6064
 
6065
;; CALL-JUMP
6066
L162C:  JP      (HL)            ; jump to the routine
6067
 
6068
; Footnote. calling any location that holds JP (HL) is the equivalent to
6069
; a pseudo Z80 instruction CALL (HL). The ROM uses the instruction above.
6070
 
6071
; --------------------------
6072
; Channel code look-up table
6073
; --------------------------
6074
; This table is used by the routine above to find one of the three
6075
; flag setting routines below it.
6076
; A zero end-marker is required as channel 'R' is not present.
6077
 
6078
;; chn-cd-lu
6079
L162D:  DB    'K', L1634-$-1  ; offset $06 to CHAN-K
6080
        DB    'S', L1642-$-1  ; offset $12 to CHAN-S
6081
        DB    'P', L164D-$-1  ; offset $1B to CHAN-P
6082
 
6083
        DB    $00             ; end marker.
6084
 
6085
; --------------
6086
; Channel K flag
6087
; --------------
6088
; routine to set flags for lower screen/keyboard channel.
6089
 
6090
;; CHAN-K
6091
L1634:  SET     0,(IY+$02)      ; update TV_FLAG  - signal lower screen in use
6092
        RES     5,(IY+$01)      ; update FLAGS    - signal no new key
6093
        SET     4,(IY+$30)      ; update FLAGS2   - signal K channel in use
6094
        JR      L1646           ; forward to CHAN-S-1 for indirect exit
6095
 
6096
; --------------
6097
; Channel S flag
6098
; --------------
6099
; routine to set flags for upper screen channel.
6100
 
6101
;; CHAN-S
6102
L1642:  RES     0,(IY+$02)      ; TV_FLAG  - signal main screen in use
6103
 
6104
;; CHAN-S-1
6105
L1646:  RES     1,(IY+$01)      ; update FLAGS  - signal printer not in use
6106
        JP      L0D4D           ; jump back to TEMPS and exit via that
6107
                                ; routine after setting temporary attributes.
6108
; --------------
6109
; Channel P flag
6110
; --------------
6111
; This routine sets a flag so that subsequent print related commands
6112
; print to printer or update the relevant system variables.
6113
; This status remains in force until reset by the routine above.
6114
 
6115
;; CHAN-P
6116
L164D:  SET     1,(IY+$01)      ; update FLAGS  - signal printer in use
6117
        RET                     ; return
6118
 
6119
; -----------------------
6120
; Just one space required
6121
; -----------------------
6122
; This routine is called once only to create a single space
6123
; in workspace by ADD-CHAR. It is slightly quicker than using a RST $30.
6124
; There are several instances in the calculator where the sequence
6125
; ld bc, 1; rst $30 could be replaced by a call to this routine but it
6126
; only gives a saving of one byte each time.
6127
 
6128
;; ONE-SPACE
6129
L1652:  LD      BC,$0001        ; create space for a single character.
6130
 
6131
; ---------
6132
; Make Room
6133
; ---------
6134
; This entry point is used to create BC spaces in various areas such as
6135
; program area, variables area, workspace etc..
6136
; The entire free RAM is available to each BASIC statement.
6137
; On entry, HL addresses where the first location is to be created.
6138
; Afterwards, HL will point to the location before this.
6139
 
6140
;; MAKE-ROOM
6141
L1655:  PUSH    HL              ; save the address pointer.
6142
        CALL    L1F05           ; routine TEST-ROOM checks if room
6143
                                ; exists and generates an error if not.
6144
        POP     HL              ; restore the address pointer.
6145
        CALL    L1664           ; routine POINTERS updates the
6146
                                ; dynamic memory location pointers.
6147
                                ; DE now holds the old value of STKEND.
6148
        LD      HL,($5C65)      ; fetch new STKEND the top destination.
6149
 
6150
        EX      DE,HL           ; HL now addresses the top of the area to
6151
                                ; be moved up - old STKEND.
6152
        LDDR                    ; the program, variables, etc are moved up.
6153
        RET                     ; return with new area ready to be populated.
6154
                                ; HL points to location before new area,
6155
                                ; and DE to last of new locations.
6156
 
6157
; -----------------------------------------------
6158
; Adjust pointers before making or reclaiming room
6159
; -----------------------------------------------
6160
; This routine is called by MAKE-ROOM to adjust upwards and by RECLAIM to
6161
; adjust downwards the pointers within dynamic memory.
6162
; The fourteen pointers to dynamic memory, starting with VARS and ending 
6163
; with STKEND, are updated adding BC if they are higher than the position
6164
; in HL.  
6165
; The system variables are in no particular order except that STKEND, the first
6166
; free location after dynamic memory must be the last encountered.
6167
 
6168
;; POINTERS
6169
L1664:  PUSH    AF              ; preserve accumulator.
6170
        PUSH    HL              ; put pos pointer on stack.
6171
        LD      HL,$5C4B        ; address VARS the first of the
6172
        LD      A,$0E           ; fourteen variables to consider.
6173
 
6174
;; PTR-NEXT
6175
L166B:  LD      E,(HL)          ; fetch the low byte of the system variable.
6176
        INC     HL              ; advance address.
6177
        LD      D,(HL)          ; fetch high byte of the system variable.
6178
        EX      (SP),HL         ; swap pointer on stack with the variable
6179
                                ; pointer.
6180
        AND     A               ; prepare to subtract.
6181
        SBC     HL,DE           ; subtract variable address
6182
        ADD     HL,DE           ; and add back
6183
        EX      (SP),HL         ; swap pos with system variable pointer
6184
        JR      NC,L167F        ; forward to PTR-DONE if var before pos
6185
 
6186
        PUSH    DE              ; save system variable address.
6187
        EX      DE,HL           ; transfer to HL
6188
        ADD     HL,BC           ; add the offset
6189
        EX      DE,HL           ; back to DE
6190
        LD      (HL),D          ; load high byte
6191
        DEC     HL              ; move back
6192
        LD      (HL),E          ; load low byte
6193
        INC     HL              ; advance to high byte
6194
        POP     DE              ; restore old system variable address.
6195
 
6196
;; PTR-DONE
6197
L167F:  INC     HL              ; address next system variable.
6198
        DEC     A               ; decrease counter.
6199
        JR      NZ,L166B        ; back to PTR-NEXT if more.
6200
        EX      DE,HL           ; transfer old value of STKEND to HL.
6201
                                ; Note. this has always been updated.
6202
        POP     DE              ; pop the address of the position.
6203
 
6204
        POP     AF              ; pop preserved accumulator.
6205
        AND     A               ; clear carry flag preparing to subtract.
6206
 
6207
        SBC     HL,DE           ; subtract position from old stkend 
6208
        LD      B,H             ; to give number of data bytes
6209
        LD      C,L             ; to be moved.
6210
        INC     BC              ; increment as we also copy byte at old STKEND.
6211
        ADD     HL,DE           ; recompute old stkend.
6212
        EX      DE,HL           ; transfer to DE.
6213
        RET                     ; return.
6214
 
6215
 
6216
 
6217
; -------------------
6218
; Collect line number
6219
; -------------------
6220
; This routine extracts a line number, at an address that has previously
6221
; been found using LINE-ADDR, and it is entered at LINE-NO. If it encounters
6222
; the program 'end-marker' then the previous line is used and if that
6223
; should also be unacceptable then zero is used as it must be a direct
6224
; command. The program end-marker is the variables end-marker $80, or
6225
; if variables exist, then the first character of any variable name.
6226
 
6227
;; LINE-ZERO
6228
L168F:  DB    $00, $00        ; dummy line number used for direct commands
6229
 
6230
 
6231
;; LINE-NO-A
6232
L1691:  EX      DE,HL           ; fetch the previous line to HL and set
6233
        LD      DE,$168F        ; DE to LINE-ZERO should HL also fail.
6234
 
6235
; -> The Entry Point.
6236
 
6237
;; LINE-NO
6238
L1695:  LD      A,(HL)          ; fetch the high byte - max $2F
6239
        AND     $C0             ; mask off the invalid bits.
6240
        JR      NZ,L1691        ; to LINE-NO-A if an end-marker.
6241
 
6242
        LD      D,(HL)          ; reload the high byte.
6243
        INC     HL              ; advance address.
6244
        LD      E,(HL)          ; pick up the low byte.
6245
        RET                     ; return from here.
6246
 
6247
; -------------------
6248
; Handle reserve room
6249
; -------------------
6250
; This is a continuation of the restart BC-SPACES
6251
 
6252
;; RESERVE
6253
L169E:  LD      HL,($5C63)      ; STKBOT first location of calculator stack
6254
        DEC     HL              ; make one less than new location
6255
        CALL    L1655           ; routine MAKE-ROOM creates the room.
6256
        INC     HL              ; address the first new location
6257
        INC     HL              ; advance to second
6258
        POP     BC              ; restore old WORKSP
6259
        LD      ($5C61),BC      ; system variable WORKSP was perhaps
6260
                                ; changed by POINTERS routine.
6261
        POP     BC              ; restore count for return value.
6262
        EX      DE,HL           ; switch. DE = location after first new space
6263
        INC     HL              ; HL now location after new space
6264
        RET                     ; return.
6265
 
6266
; ---------------------------
6267
; Clear various editing areas
6268
; ---------------------------
6269
; This routine sets the editing area, workspace and calculator stack
6270
; to their minimum configurations as at initialization and indeed this
6271
; routine could have been relied on to perform that task.
6272
; This routine uses HL only and returns with that register holding
6273
; WORKSP/STKBOT/STKEND though no use is made of this. The routines also
6274
; reset MEM to its usual place in the systems variable area should it
6275
; have been relocated to a FOR-NEXT variable. The main entry point
6276
; SET-MIN is called at the start of the MAIN-EXEC loop and prior to
6277
; displaying an error.
6278
 
6279
;; SET-MIN
6280
L16B0:  LD      HL,($5C59)      ; fetch E_LINE
6281
        LD      (HL),$0D        ; insert carriage return
6282
        LD      ($5C5B),HL      ; make K_CUR keyboard cursor point there.
6283
        INC     HL              ; next location
6284
        LD      (HL),$80        ; holds end-marker $80
6285
        INC     HL              ; next location becomes
6286
        LD      ($5C61),HL      ; start of WORKSP
6287
 
6288
; This entry point is used prior to input and prior to the execution,
6289
; or parsing, of each statement.
6290
 
6291
;; SET-WORK
6292
L16BF:  LD      HL,($5C61)      ; fetch WORKSP value
6293
        LD      ($5C63),HL      ; and place in STKBOT
6294
 
6295
; This entry point is used to move the stack back to its normal place
6296
; after temporary relocation during line entry and also from ERROR-3
6297
 
6298
;; SET-STK
6299
L16C5:  LD      HL,($5C63)      ; fetch STKBOT value 
6300
        LD      ($5C65),HL      ; and place in STKEND.
6301
 
6302
        PUSH    HL              ; perhaps an obsolete entry point.
6303
        LD      HL,$5C92        ; normal location of MEM-0
6304
        LD      ($5C68),HL      ; is restored to system variable MEM.
6305
        POP     HL              ; saved value not required.
6306
        RET                     ; return.
6307
 
6308
; ------------------
6309
; Reclaim edit-line?
6310
; ------------------
6311
; This seems to be legacy code from the ZX80/ZX81 as it is 
6312
; not used in this ROM.
6313
; That task, in fact, is performed here by the dual-area routine CLEAR-SP.
6314
; This routine is designed to deal with something that is known to be in the
6315
; edit buffer and not workspace.
6316
; On entry, HL must point to the end of the something to be deleted.
6317
 
6318
;; REC-EDIT
6319
L16D4:  LD      DE,($5C59)      ; fetch start of edit line from E_LINE.
6320
        JP      L19E5           ; jump forward to RECLAIM-1.
6321
 
6322
; --------------------------
6323
; The Table INDEXING routine
6324
; --------------------------
6325
; This routine is used to search two-byte hash tables for a character
6326
; held in C, returning the address of the following offset byte.
6327
; if it is known that the character is in the table e.g. for priorities,
6328
; then the table requires no zero end-marker. If this is not known at the
6329
; outset then a zero end-marker is required and carry is set to signal
6330
; success.
6331
 
6332
;; INDEXER-1
6333
L16DB:  INC     HL              ; address the next pair of values.
6334
 
6335
; -> The Entry Point.
6336
 
6337
;; INDEXER
6338
L16DC:  LD      A,(HL)          ; fetch the first byte of pair
6339
        AND     A               ; is it the end-marker ?
6340
        RET     Z               ; return with carry reset if so.
6341
 
6342
        CP      C               ; is it the required character ?
6343
        INC     HL              ; address next location.
6344
        JR      NZ,L16DB        ; back to INDEXER-1 if no match.
6345
 
6346
        SCF                     ; else set the carry flag.
6347
        RET                     ; return with carry set
6348
 
6349
; --------------------------------
6350
; The Channel and Streams Routines
6351
; --------------------------------
6352
; A channel is an input/output route to a hardware device
6353
; and is identified to the system by a single letter e.g. 'K' for
6354
; the keyboard. A channel can have an input and output route
6355
; associated with it in which case it is bi-directional like
6356
; the keyboard. Others like the upper screen 'S' are output
6357
; only and the input routine usually points to a report message.
6358
; Channels 'K' and 'S' are system channels and it would be inappropriate
6359
; to close the associated streams so a mechanism is provided to
6360
; re-attach them. When the re-attachment is no longer required, then
6361
; closing these streams resets them as at initialization.
6362
; The same also would have applied to channel 'R', the RS232 channel
6363
; as that is used by the system. It's input stream seems to have been
6364
; removed and it is not available to the user. However the channel could
6365
; not be removed entirely as its output routine was used by the system.
6366
; As a result of removing this channel, channel 'P', the printer is
6367
; erroneously treated as a system channel.
6368
; Ironically the tape streamer is not accessed through streams and
6369
; channels.
6370
; Early demonstrations of the Spectrum showed a single microdrive being
6371
; controlled by this ROM. Adverts also said that the network and RS232
6372
; were in this ROM. Channels 'M' and 'N' are user channels and have been
6373
; removed successfully if, as seems vaguely possible, they existed.
6374
 
6375
; ---------------------
6376
; Handle CLOSE# command
6377
; ---------------------
6378
; This command allows streams to be closed after use.
6379
; Any temporary memory areas used by the stream would be reclaimed and
6380
; finally flags set or reset if necessary.
6381
 
6382
;; CLOSE
6383
L16E5:  CALL    L171E           ; routine STR-DATA fetches parameter
6384
                                ; from calculator stack and gets the
6385
                                ; existing STRMS data pointer address in HL
6386
                                ; and stream offset from CHANS in BC.
6387
 
6388
                                ; Note. this offset could be zero if the
6389
                                ; stream is already closed. A check for this
6390
                                ; should occur now and an error should be
6391
                                ; generated, for example,
6392
                                ; Report S 'Stream already closed'.
6393
 
6394
        CALL    L1701           ; routine CLOSE-2 would perform any actions
6395
                                ; peculiar to that stream without disturbing
6396
                                ; data pointer to STRMS entry in HL.
6397
 
6398
        LD      BC,$0000        ; the stream is to be blanked.
6399
        LD      DE,$A3E2        ; the number of bytes from stream 4, $5C1E,
6400
                                ; to $10000
6401
        EX      DE,HL           ; transfer offset to HL, STRMS data pointer
6402
                                ; to DE.
6403
        ADD     HL,DE           ; add the offset to the data pointer.  
6404
        JR      C,L16FC         ; forward to CLOSE-1 if a non-system stream.
6405
                                ; i.e. higher than 3. 
6406
 
6407
; proceed with a negative result.
6408
 
6409
        LD      BC,L15C6 + 14   ; prepare the address of the byte after
6410
                                ; the initial stream data in ROM. ($15D4)
6411
        ADD     HL,BC           ; index into the data table with negative value.
6412
        LD      C,(HL)          ; low byte to C
6413
        INC     HL              ; address next.
6414
        LD      B,(HL)          ; high byte to B.
6415
 
6416
; and for streams 0 - 3 just enter the initial data back into the STRMS entry
6417
; streams 0 - 2 can't be closed as they are shared by the operating system.
6418
; -> for streams 4 - 15 then blank the entry.
6419
 
6420
;; CLOSE-1
6421
L16FC:  EX      DE,HL           ; address of stream to HL.
6422
        LD      (HL),C          ; place zero (or low byte).
6423
        INC     HL              ; next address.
6424
        LD      (HL),B          ; place zero (or high byte).
6425
        RET                     ; return.
6426
 
6427
; ------------------
6428
; CLOSE-2 Subroutine
6429
; ------------------
6430
; There is not much point in coming here.
6431
; The purpose was once to find the offset to a special closing routine,
6432
; in this ROM and within 256 bytes of the close stream look up table that
6433
; would reclaim any buffers associated with a stream. At least one has been
6434
; removed.
6435
 
6436
;; CLOSE-2
6437
L1701:  PUSH    HL              ; * save address of stream data pointer
6438
                                ; in STRMS on the machine stack.
6439
        LD      HL,($5C4F)      ; fetch CHANS address to HL
6440
        ADD     HL,BC           ; add the offset to address the second
6441
                                ; byte of the output routine hopefully.
6442
        INC     HL              ; step past
6443
        INC     HL              ; the input routine.
6444
        INC     HL              ; to address channel's letter
6445
        LD      C,(HL)          ; pick it up in C.
6446
                                ; Note. but if stream is already closed we
6447
                                ; get the value $10 (the byte preceding 'K').
6448
        EX      DE,HL           ; save the pointer to the letter in DE.
6449
        LD      HL,L1716        ; address: cl-str-lu in ROM.
6450
        CALL    L16DC           ; routine INDEXER uses the code to get 
6451
                                ; the 8-bit offset from the current point to
6452
                                ; the address of the closing routine in ROM.
6453
                                ; Note. it won't find $10 there!
6454
        LD      C,(HL)          ; transfer the offset to C.
6455
        LD      B,$00           ; prepare to add.
6456
        ADD     HL,BC           ; add offset to point to the address of the
6457
                                ; routine that closes the stream.
6458
                                ; (and presumably removes any buffers that
6459
                                ; are associated with it.)
6460
        JP      (HL)            ; jump to that routine.
6461
 
6462
; --------------------------
6463
; CLOSE stream look-up table
6464
; --------------------------
6465
; This table contains an entry for a letter found in the CHANS area.
6466
; followed by an 8-bit displacement, from that byte's address in the
6467
; table to the routine that performs any ancillary actions associated
6468
; with closing the stream of that channel.
6469
; The table doesn't require a zero end-marker as the letter has been
6470
; picked up from a channel that has an open stream.
6471
 
6472
;; cl-str-lu
6473
L1716:  DB    'K', L171C-$-1  ; offset 5 to CLOSE-STR
6474
        DB    'S', L171C-$-1  ; offset 3 to CLOSE-STR
6475
        DB    'P', L171C-$-1  ; offset 1 to CLOSE-STR
6476
 
6477
 
6478
; ------------------------
6479
; Close Stream Subroutines
6480
; ------------------------
6481
; The close stream routines in fact have no ancillary actions to perform
6482
; which is not surprising with regard to 'K' and 'S'.
6483
 
6484
;; CLOSE-STR                    
6485
L171C:  POP     HL              ; * now just restore the stream data pointer
6486
        RET                     ; in STRMS and return.
6487
 
6488
; -----------
6489
; Stream data
6490
; -----------
6491
; This routine finds the data entry in the STRMS area for the specified
6492
; stream which is passed on the calculator stack. It returns with HL
6493
; pointing to this system variable and BC holding a displacement from
6494
; the CHANS area to the second byte of the stream's channel. If BC holds
6495
; zero, then that signifies that the stream is closed.
6496
 
6497
;; STR-DATA
6498
L171E:  CALL    L1E94           ; routine FIND-INT1 fetches parameter to A
6499
        CP      $10             ; is it less than 16d ?
6500
        JR      C,L1727         ; skip forward to STR-DATA1 if so.
6501
 
6502
;; REPORT-Ob
6503
L1725:  RST     08H             ; ERROR-1
6504
        DB    $17             ; Error Report: Invalid stream
6505
 
6506
;; STR-DATA1
6507
L1727:  ADD     A,$03           ; add the offset for 3 system streams.
6508
                                ; range 00 - 15d becomes 3 - 18d.
6509
        RLCA                    ; double as there are two bytes per 
6510
                                ; stream - now 06 - 36d
6511
        LD      HL,$5C10        ; address STRMS - the start of the streams
6512
                                ; data area in system variables.
6513
        LD      C,A             ; transfer the low byte to A.
6514
        LD      B,$00           ; prepare to add offset.
6515
        ADD     HL,BC           ; add to address the data entry in STRMS.
6516
 
6517
; the data entry itself contains an offset from CHANS to the address of the
6518
; stream
6519
 
6520
        LD      C,(HL)          ; low byte of displacement to C.
6521
        INC     HL              ; address next.
6522
        LD      B,(HL)          ; high byte of displacement to B.
6523
        DEC     HL              ; step back to leave HL pointing to STRMS
6524
                                ; data entry.
6525
        RET                     ; return with CHANS displacement in BC
6526
                                ; and address of stream data entry in HL.
6527
 
6528
; --------------------
6529
; Handle OPEN# command
6530
; --------------------
6531
; Command syntax example: OPEN #5,"s"
6532
; On entry the channel code entry is on the calculator stack with the next
6533
; value containing the stream identifier. They have to swapped.
6534
 
6535
;; OPEN
6536
L1736:  RST     28H             ;; FP-CALC    ;s,c.
6537
        DB    $01             ;;exchange    ;c,s.
6538
        DB    $38             ;;end-calc
6539
 
6540
        CALL    L171E           ; routine STR-DATA fetches the stream off
6541
                                ; the stack and returns with the CHANS
6542
                                ; displacement in BC and HL addressing 
6543
                                ; the STRMS data entry.
6544
        LD      A,B             ; test for zero which
6545
        OR      C               ; indicates the stream is closed.
6546
        JR      Z,L1756         ; skip forward to OPEN-1 if so.
6547
 
6548
; if it is a system channel then it can re-attached.
6549
 
6550
        EX      DE,HL           ; save STRMS address in DE.
6551
        LD      HL,($5C4F)      ; fetch CHANS.
6552
        ADD     HL,BC           ; add the offset to address the second 
6553
                                ; byte of the channel.
6554
        INC     HL              ; skip over the
6555
        INC     HL              ; input routine.
6556
        INC     HL              ; and address the letter.
6557
        LD      A,(HL)          ; pick up the letter.
6558
        EX      DE,HL           ; save letter pointer and bring back
6559
                                ; the STRMS pointer.
6560
 
6561
        CP      $4B             ; is it 'K' ?
6562
        JR      Z,L1756         ; forward to OPEN-1 if so
6563
 
6564
        CP      $53             ; is it 'S' ?
6565
        JR      Z,L1756         ; forward to OPEN-1 if so
6566
 
6567
        CP      $50             ; is it 'P' ?
6568
        JR      NZ,L1725        ; back to REPORT-Ob if not.
6569
                                ; to report 'Invalid stream'.
6570
 
6571
; continue if one of the upper-case letters was found.
6572
; and rejoin here from above if stream was closed.
6573
 
6574
;; OPEN-1
6575
L1756:  CALL    L175D           ; routine OPEN-2 opens the stream.
6576
 
6577
; it now remains to update the STRMS variable.
6578
 
6579
        LD      (HL),E          ; insert or overwrite the low byte.
6580
        INC     HL              ; address high byte in STRMS.
6581
        LD      (HL),D          ; insert or overwrite the high byte.
6582
        RET                     ; return.
6583
 
6584
; -----------------
6585
; OPEN-2 Subroutine
6586
; -----------------
6587
; There is some point in coming here as, as well as once creating buffers,
6588
; this routine also sets flags.
6589
 
6590
;; OPEN-2
6591
L175D:  PUSH    HL              ; * save the STRMS data entry pointer.
6592
        CALL    L2BF1           ; routine STK-FETCH now fetches the
6593
                                ; parameters of the channel string.
6594
                                ; start in DE, length in BC.
6595
 
6596
        LD      A,B             ; test that it is not
6597
        OR      C               ; the null string.
6598
        JR      NZ,L1767        ; skip forward to OPEN-3 with 1 character
6599
                                ; or more!
6600
 
6601
;; REPORT-Fb
6602
L1765:  RST     08H             ; ERROR-1
6603
        DB    $0E             ; Error Report: Invalid file name
6604
 
6605
;; OPEN-3
6606
L1767:  PUSH    BC              ; save the length of the string.
6607
        LD      A,(DE)          ; pick up the first character.
6608
                                ; Note. if the second character is used to
6609
                                ; distinguish between a binary or text
6610
                                ; channel then it will be simply a matter
6611
                                ; of setting bit 7 of FLAGX.
6612
        AND     $DF             ; make it upper-case.
6613
        LD      C,A             ; place it in C.
6614
        LD      HL,L177A        ; address: op-str-lu is loaded.
6615
        CALL    L16DC           ; routine INDEXER will search for letter.
6616
        JR      NC,L1765        ; back to REPORT-F if not found
6617
                                ; 'Invalid filename'
6618
 
6619
        LD      C,(HL)          ; fetch the displacement to opening routine.
6620
        LD      B,$00           ; prepare to add.
6621
        ADD     HL,BC           ; now form address of opening routine.
6622
        POP     BC              ; restore the length of string.
6623
        JP      (HL)            ; now jump forward to the relevant routine.
6624
 
6625
; -------------------------
6626
; OPEN stream look-up table
6627
; -------------------------
6628
; The open stream look-up table consists of matched pairs.
6629
; The channel letter is followed by an 8-bit displacement to the
6630
; associated stream-opening routine in this ROM.
6631
; The table requires a zero end-marker as the letter has been
6632
; provided by the user and not the operating system.
6633
 
6634
;; op-str-lu
6635
L177A:  DB    'K', L1781-$-1  ; $06 offset to OPEN-K
6636
        DB    'S', L1785-$-1  ; $08 offset to OPEN-S
6637
        DB    'P', L1789-$-1  ; $0A offset to OPEN-P
6638
 
6639
        DB    $00             ; end-marker.
6640
 
6641
; ----------------------------
6642
; The Stream Opening Routines.
6643
; ----------------------------
6644
; These routines would have opened any buffers associated with the stream
6645
; before jumping forward to to OPEN-END with the displacement value in E
6646
; and perhaps a modified value in BC. The strange pathing does seem to
6647
; provide for flexibility in this respect.
6648
;
6649
; There is no need to open the printer buffer as it is there already
6650
; even if you are still saving up for a ZX Printer or have moved onto
6651
; something bigger. In any case it would have to be created after
6652
; the system variables but apart from that it is a simple task
6653
; and all but one of the ROM routines can handle a buffer in that position.
6654
; (PR-ALL-6 would require an extra 3 bytes of code).
6655
; However it wouldn't be wise to have two streams attached to the ZX Printer
6656
; as you can now, so one assumes that if PR_CC_hi was non-zero then
6657
; the OPEN-P routine would have refused to attach a stream if another
6658
; stream was attached.
6659
 
6660
; Something of significance is being passed to these ghost routines in the
6661
; second character. Strings 'RB', 'RT' perhaps or a drive/station number.
6662
; The routine would have to deal with that and exit to OPEN_END with BC
6663
; containing $0001 or more likely there would be an exit within the routine.
6664
; Anyway doesn't matter, these routines are long gone.
6665
 
6666
; -----------------
6667
; OPEN-K Subroutine
6668
; -----------------
6669
; Open Keyboard stream.
6670
 
6671
;; OPEN-K
6672
L1781:  LD      E,$01           ; 01 is offset to second byte of channel 'K'.
6673
        JR      L178B           ; forward to OPEN-END
6674
 
6675
; -----------------
6676
; OPEN-S Subroutine
6677
; -----------------
6678
; Open Screen stream.
6679
 
6680
;; OPEN-S
6681
L1785:  LD      E,$06           ; 06 is offset to 2nd byte of channel 'S'
6682
        JR      L178B           ; to OPEN-END
6683
 
6684
; -----------------
6685
; OPEN-P Subroutine
6686
; -----------------
6687
; Open Printer stream.
6688
 
6689
;; OPEN-P
6690
L1789:  LD      E,$10           ; 16d is offset to 2nd byte of channel 'P'
6691
 
6692
;; OPEN-END
6693
L178B:  DEC     BC              ; the stored length of 'K','S','P' or
6694
                                ; whatever is now tested. ??
6695
        LD      A,B             ; test now if initial or residual length
6696
        OR      C               ; is one character.
6697
        JR      NZ,L1765        ; to REPORT-Fb 'Invalid file name' if not.
6698
 
6699
        LD      D,A             ; load D with zero to form the displacement
6700
                                ; in the DE register.
6701
        POP     HL              ; * restore the saved STRMS pointer.
6702
        RET                     ; return to update STRMS entry thereby 
6703
                                ; signaling stream is open.
6704
 
6705
; ----------------------------------------
6706
; Handle CAT, ERASE, FORMAT, MOVE commands
6707
; ----------------------------------------
6708
; These just generate an error report as the ROM is 'incomplete'.
6709
;
6710
; Luckily this provides a mechanism for extending these in a shadow ROM
6711
; but without the powerful mechanisms set up in this ROM.
6712
; An instruction fetch on $0008 may page in a peripheral ROM,
6713
; e.g. the Sinclair Interface 1 ROM, to handle these commands.
6714
; However that wasn't the plan.
6715
; Development of this ROM continued for another three months until the cost
6716
; of replacing it and the manual became unfeasible.
6717
; The ultimate power of channels and streams died at birth.
6718
 
6719
;; CAT-ETC
6720
L1793:  JR      L1725           ; to REPORT-Ob
6721
 
6722
; -----------------
6723
; Perform AUTO-LIST
6724
; -----------------
6725
; This produces an automatic listing in the upper screen.
6726
 
6727
;; AUTO-LIST
6728
L1795:  LD      ($5C3F),SP      ; save stack pointer in LIST_SP
6729
        LD      (IY+$02),$10    ; update TV_FLAG set bit 3
6730
        CALL    L0DAF           ; routine CL-ALL.
6731
        SET     0,(IY+$02)      ; update TV_FLAG  - signal lower screen in use
6732
 
6733
        LD      B,(IY+$31)      ; fetch DF_SZ to B.
6734
        CALL    L0E44           ; routine CL-LINE clears lower display
6735
                                ; preserving B.
6736
        RES     0,(IY+$02)      ; update TV_FLAG  - signal main screen in use
6737
        SET     0,(IY+$30)      ; update FLAGS2  - signal unnecessary to
6738
                                ; clear main screen.
6739
        LD      HL,($5C49)      ; fetch E_PPC current edit line to HL.
6740
        LD      DE,($5C6C)      ; fetch S_TOP to DE, the current top line
6741
                                ; (initially zero)
6742
        AND     A               ; prepare for true subtraction.
6743
        SBC     HL,DE           ; subtract and
6744
        ADD     HL,DE           ; add back.
6745
        JR      C,L17E1         ; to AUTO-L-2 if S_TOP higher than E_PPC
6746
                                ; to set S_TOP to E_PPC
6747
 
6748
        PUSH    DE              ; save the top line number.
6749
        CALL    L196E           ; routine LINE-ADDR gets address of E_PPC.
6750
        LD      DE,$02C0        ; prepare known number of characters in
6751
                                ; the default upper screen.
6752
        EX      DE,HL           ; offset to HL, program address to DE.
6753
        SBC     HL,DE           ; subtract high value from low to obtain
6754
                                ; negated result used in addition.
6755
        EX      (SP),HL         ; swap result with top line number on stack.
6756
        CALL    L196E           ; routine LINE-ADDR  gets address of that
6757
                                ; top line in HL and next line in DE.
6758
        POP     BC              ; restore the result to balance stack.
6759
 
6760
;; AUTO-L-1
6761
L17CE:  PUSH    BC              ; save the result.
6762
        CALL    L19B8           ; routine NEXT-ONE gets address in HL of
6763
                                ; line after auto-line (in DE).
6764
        POP     BC              ; restore result.
6765
        ADD     HL,BC           ; compute back.
6766
        JR      C,L17E4         ; to AUTO-L-3 if line 'should' appear
6767
 
6768
        EX      DE,HL           ; address of next line to HL.
6769
        LD      D,(HL)          ; get line
6770
        INC     HL              ; number
6771
        LD      E,(HL)          ; in DE.
6772
        DEC     HL              ; adjust back to start.
6773
        LD      ($5C6C),DE      ; update S_TOP.
6774
        JR      L17CE           ; to AUTO-L-1 until estimate reached.
6775
 
6776
; ---
6777
 
6778
; the jump was to here if S_TOP was greater than E_PPC
6779
 
6780
;; AUTO-L-2
6781
L17E1:  LD      ($5C6C),HL      ; make S_TOP the same as E_PPC.
6782
 
6783
; continue here with valid starting point from above or good estimate
6784
; from computation
6785
 
6786
;; AUTO-L-3
6787
L17E4:  LD      HL,($5C6C)      ; fetch S_TOP line number to HL.
6788
        CALL    L196E           ; routine LINE-ADDR gets address in HL.
6789
                                ; address of next in DE.
6790
        JR      Z,L17ED         ; to AUTO-L-4 if line exists.
6791
 
6792
        EX      DE,HL           ; else use address of next line.
6793
 
6794
;; AUTO-L-4
6795
L17ED:  CALL    L1833           ; routine LIST-ALL                >>>
6796
 
6797
; The return will be to here if no scrolling occurred
6798
 
6799
        RES     4,(IY+$02)      ; update TV_FLAG  - signal no auto listing.
6800
        RET                     ; return.
6801
 
6802
; ------------
6803
; Handle LLIST
6804
; ------------
6805
; A short form of LIST #3. The listing goes to stream 3 - default printer.
6806
 
6807
;; LLIST
6808
L17F5:  LD      A,$03           ; the usual stream for ZX Printer
6809
        JR      L17FB           ; forward to LIST-1
6810
 
6811
; -----------
6812
; Handle LIST
6813
; -----------
6814
; List to any stream.
6815
; Note. While a starting line can be specified it is
6816
; not possible to specify an end line.
6817
; Just listing a line makes it the current edit line.
6818
 
6819
;; LIST
6820
L17F9:  LD      A,$02           ; default is stream 2 - the upper screen.
6821
 
6822
;; LIST-1
6823
L17FB:  LD      (IY+$02),$00    ; the TV_FLAG is initialized with bit 0 reset
6824
                                ; indicating upper screen in use.
6825
        CALL    L2530           ; routine SYNTAX-Z - checking syntax ?
6826
        CALL    NZ,L1601        ; routine CHAN-OPEN if in run-time.
6827
 
6828
        RST     18H             ; GET-CHAR
6829
        CALL    L2070           ; routine STR-ALTER will alter if '#'.
6830
        JR      C,L181F         ; forward to LIST-4 not a '#' .
6831
 
6832
 
6833
        RST     18H             ; GET-CHAR
6834
        CP      $3B             ; is it ';' ?
6835
        JR      Z,L1814         ; skip to LIST-2 if so.
6836
 
6837
        CP      $2C             ; is it ',' ?
6838
        JR      NZ,L181A        ; forward to LIST-3 if neither separator.
6839
 
6840
; we have, say,  LIST #15, and a number must follow the separator.
6841
 
6842
;; LIST-2
6843
L1814:  RST     20H             ; NEXT-CHAR
6844
        CALL    L1C82           ; routine EXPT-1NUM
6845
        JR      L1822           ; forward to LIST-5
6846
 
6847
; ---
6848
 
6849
; the branch was here with just LIST #3 etc.
6850
 
6851
;; LIST-3
6852
L181A:  CALL    L1CE6           ; routine USE-ZERO
6853
        JR      L1822           ; forward to LIST-5
6854
 
6855
; ---
6856
 
6857
; the branch was here with LIST
6858
 
6859
;; LIST-4
6860
L181F:  CALL    L1CDE           ; routine FETCH-NUM checks if a number 
6861
                                ; follows else uses zero.
6862
 
6863
;; LIST-5
6864
L1822:  CALL    L1BEE           ; routine CHECK-END quits if syntax OK >>>
6865
 
6866
        CALL    L1E99           ; routine FIND-INT2 fetches the number
6867
                                ; from the calculator stack in run-time.
6868
        LD      A,B             ; fetch high byte of line number and
6869
        AND     $3F             ; make less than $40 so that NEXT-ONE
6870
                                ; (from LINE-ADDR) doesn't lose context.
6871
                                ; Note. this is not satisfactory and the typo
6872
                                ; LIST 20000 will list an entirely different
6873
                                ; section than LIST 2000. Such typos are not
6874
                                ; available for checking if they are direct
6875
                                ; commands.
6876
 
6877
        LD      H,A             ; transfer the modified
6878
        LD      L,C             ; line number to HL.
6879
        LD      ($5C49),HL      ; update E_PPC to new line number.
6880
        CALL    L196E           ; routine LINE-ADDR gets the address of the
6881
                                ; line.
6882
 
6883
; This routine is called from AUTO-LIST
6884
 
6885
;; LIST-ALL
6886
L1833:  LD      E,$01           ; signal current line not yet printed
6887
 
6888
;; LIST-ALL-2
6889
L1835:  CALL    L1855           ; routine OUT-LINE outputs a BASIC line
6890
                                ; using PRINT-OUT and makes an early return
6891
                                ; when no more lines to print. >>>
6892
 
6893
        RST     10H             ; PRINT-A prints the carriage return (in A)
6894
 
6895
        BIT     4,(IY+$02)      ; test TV_FLAG  - automatic listing ?
6896
        JR      Z,L1835         ; back to LIST-ALL-2 if not
6897
                                ; (loop exit is via OUT-LINE)
6898
 
6899
; continue here if an automatic listing required.
6900
 
6901
        LD      A,($5C6B)       ; fetch DF_SZ lower display file size.
6902
        SUB     (IY+$4F)        ; subtract S_POSN_hi ithe current line number.
6903
        JR      NZ,L1835        ; back to LIST-ALL-2 if upper screen not full.
6904
 
6905
        XOR     E               ; A contains zero, E contains one if the
6906
                                ; current edit line has not been printed
6907
                                ; or zero if it has (from OUT-LINE).
6908
        RET     Z               ; return if the screen is full and the line
6909
                                ; has been printed.
6910
 
6911
; continue with automatic listings if the screen is full and the current
6912
; edit line is missing. OUT-LINE will scroll automatically.
6913
 
6914
        PUSH    HL              ; save the pointer address.
6915
        PUSH    DE              ; save the E flag.
6916
        LD      HL,$5C6C        ; fetch S_TOP the rough estimate.
6917
        CALL    L190F           ; routine LN-FETCH updates S_TOP with
6918
                                ; the number of the next line.
6919
        POP     DE              ; restore the E flag.
6920
        POP     HL              ; restore the address of the next line.
6921
        JR      L1835           ; back to LIST-ALL-2.
6922
 
6923
; ------------------------
6924
; Print a whole BASIC line
6925
; ------------------------
6926
; This routine prints a whole BASIC line and it is called
6927
; from LIST-ALL to output the line to current channel
6928
; and from ED-EDIT to 'sprint' the line to the edit buffer.
6929
 
6930
;; OUT-LINE
6931
L1855:  LD      BC,($5C49)      ; fetch E_PPC the current line which may be
6932
                                ; unchecked and not exist.
6933
        CALL    L1980           ; routine CP-LINES finds match or line after.
6934
        LD      D,$3E           ; prepare cursor '>' in D.
6935
        JR      Z,L1865         ; to OUT-LINE1 if matched or line after.
6936
 
6937
        LD      DE,$0000        ; put zero in D, to suppress line cursor.
6938
        RL      E               ; pick up carry in E if line before current
6939
                                ; leave E zero if same or after.
6940
 
6941
;; OUT-LINE1
6942
L1865:  LD      (IY+$2D),E      ; save flag in BREG which is spare.
6943
        LD      A,(HL)          ; get high byte of line number.
6944
        CP      $40             ; is it too high ($2F is maximum possible) ?
6945
        POP     BC              ; drop the return address and
6946
        RET     NC              ; make an early return if so >>>
6947
 
6948
        PUSH    BC              ; save return address
6949
        CALL    L1A28           ; routine OUT-NUM-2 to print addressed number
6950
                                ; with leading space.
6951
        INC     HL              ; skip low number byte.
6952
        INC     HL              ; and the two
6953
        INC     HL              ; length bytes.
6954
        RES     0,(IY+$01)      ; update FLAGS - signal leading space required.
6955
        LD      A,D             ; fetch the cursor.
6956
        AND     A               ; test for zero.
6957
        JR      Z,L1881         ; to OUT-LINE3 if zero.
6958
 
6959
 
6960
        RST     10H             ; PRINT-A prints '>' the current line cursor.
6961
 
6962
; this entry point is called from ED-COPY
6963
 
6964
;; OUT-LINE2
6965
L187D:  SET     0,(IY+$01)      ; update FLAGS - suppress leading space.
6966
 
6967
;; OUT-LINE3
6968
L1881:  PUSH    DE              ; save flag E for a return value.
6969
        EX      DE,HL           ; save HL address in DE.
6970
        RES     2,(IY+$30)      ; update FLAGS2 - signal NOT in QUOTES.
6971
 
6972
        LD      HL,$5C3B        ; point to FLAGS.
6973
        RES     2,(HL)          ; signal 'K' mode. (starts before keyword)
6974
        BIT     5,(IY+$37)      ; test FLAGX - input mode ?
6975
        JR      Z,L1894         ; forward to OUT-LINE4 if not.
6976
 
6977
        SET     2,(HL)          ; signal 'L' mode. (used for input)
6978
 
6979
;; OUT-LINE4
6980
L1894:  LD      HL,($5C5F)      ; fetch X_PTR - possibly the error pointer
6981
                                ; address.
6982
        AND     A               ; clear the carry flag.
6983
        SBC     HL,DE           ; test if an error address has been reached.
6984
        JR      NZ,L18A1        ; forward to OUT-LINE5 if not.
6985
 
6986
        LD      A,$3F           ; load A with '?' the error marker.
6987
        CALL    L18C1           ; routine OUT-FLASH to print flashing marker.
6988
 
6989
;; OUT-LINE5
6990
L18A1:  CALL    L18E1           ; routine OUT-CURS will print the cursor if
6991
                                ; this is the right position.
6992
        EX      DE,HL           ; restore address pointer to HL.
6993
        LD      A,(HL)          ; fetch the addressed character.
6994
        CALL    L18B6           ; routine NUMBER skips a hidden floating 
6995
                                ; point number if present.
6996
        INC     HL              ; now increment the pointer.
6997
        CP      $0D             ; is character end-of-line ?
6998
        JR      Z,L18B4         ; to OUT-LINE6, if so, as line is finished.
6999
 
7000
        EX      DE,HL           ; save the pointer in DE.
7001
        CALL    L1937           ; routine OUT-CHAR to output character/token.
7002
 
7003
        JR      L1894           ; back to OUT-LINE4 until entire line is done.
7004
 
7005
; ---
7006
 
7007
;; OUT-LINE6
7008
L18B4:  POP     DE              ; bring back the flag E, zero if current
7009
                                ; line printed else 1 if still to print.
7010
        RET                     ; return with A holding $0D
7011
 
7012
; -------------------------
7013
; Check for a number marker
7014
; -------------------------
7015
; this subroutine is called from two processes. while outputting BASIC lines
7016
; and while searching statements within a BASIC line.
7017
; during both, this routine will pass over an invisible number indicator
7018
; and the five bytes floating-point number that follows it.
7019
; Note that this causes floating point numbers to be stripped from
7020
; the BASIC line when it is fetched to the edit buffer by OUT_LINE.
7021
; the number marker also appears after the arguments of a DEF FN statement
7022
; and may mask old 5-byte string parameters.
7023
 
7024
;; NUMBER
7025
L18B6:  CP      $0E             ; character fourteen ?
7026
        RET     NZ              ; return if not.
7027
 
7028
        INC     HL              ; skip the character
7029
        INC     HL              ; and five bytes
7030
        INC     HL              ; following.
7031
        INC     HL              ;
7032
        INC     HL              ;
7033
        INC     HL              ;
7034
        LD      A,(HL)          ; fetch the following character
7035
        RET                     ; for return value.
7036
 
7037
; --------------------------
7038
; Print a flashing character
7039
; --------------------------
7040
; This subroutine is called from OUT-LINE to print a flashing error
7041
; marker '?' or from the next routine to print a flashing cursor e.g. 'L'.
7042
; However, this only gets called from OUT-LINE when printing the edit line
7043
; or the input buffer to the lower screen so a direct call to $09F4 can
7044
; be used, even though out-line outputs to other streams.
7045
; In fact the alternate set is used for the whole routine.
7046
 
7047
;; OUT-FLASH
7048
L18C1:  EXX                     ; switch in alternate set
7049
 
7050
        LD      HL,($5C8F)      ; fetch L = ATTR_T, H = MASK-T
7051
        PUSH    HL              ; save masks.
7052
        RES     7,H             ; reset flash mask bit so active. 
7053
        SET     7,L             ; make attribute FLASH.
7054
        LD      ($5C8F),HL      ; resave ATTR_T and MASK-T
7055
 
7056
        LD      HL,$5C91        ; address P_FLAG
7057
        LD      D,(HL)          ; fetch to D
7058
        PUSH    DE              ; and save.
7059
        LD      (HL),$00        ; clear inverse, over, ink/paper 9
7060
 
7061
        CALL    L09F4           ; routine PRINT-OUT outputs character
7062
                                ; without the need to vector via RST 10.
7063
 
7064
        POP     HL              ; pop P_FLAG to H.
7065
        LD      (IY+$57),H      ; and restore system variable P_FLAG.
7066
        POP     HL              ; restore temporary masks
7067
        LD      ($5C8F),HL      ; and restore system variables ATTR_T/MASK_T
7068
 
7069
        EXX                     ; switch back to main set
7070
        RET                     ; return
7071
 
7072
; ----------------
7073
; Print the cursor
7074
; ----------------
7075
; This routine is called before any character is output while outputting
7076
; a BASIC line or the input buffer. This includes listing to a printer
7077
; or screen, copying a BASIC line to the edit buffer and printing the
7078
; input buffer or edit buffer to the lower screen. It is only in the
7079
; latter two cases that it has any relevance and in the last case it
7080
; performs another very important function also.
7081
 
7082
;; OUT-CURS
7083
L18E1:  LD      HL,($5C5B)      ; fetch K_CUR the current cursor address
7084
        AND     A               ; prepare for true subtraction.
7085
        SBC     HL,DE           ; test against pointer address in DE and
7086
        RET     NZ              ; return if not at exact position.
7087
 
7088
; the value of MODE, maintained by KEY-INPUT, is tested and if non-zero
7089
; then this value 'E' or 'G' will take precedence.
7090
 
7091
        LD      A,($5C41)       ; fetch MODE  0='KLC', 1='E', 2='G'.
7092
                DB 0XCB
7093
                RLCA               ; double the value and set flags.
7094
        JR      Z,L18F3         ; to OUT-C-1 if still zero ('KLC').
7095
 
7096
        ADD     A,$43           ; add 'C' - will become 'E' if originally 1
7097
                                ; or 'G' if originally 2.
7098
        JR      L1909           ; forward to OUT-C-2 to print.
7099
 
7100
; ---
7101
 
7102
; If mode was zero then, while printing a BASIC line, bit 2 of flags has been
7103
; set if 'THEN' or ':' was encountered as a main character and reset otherwise.
7104
; This is now used to determine if the 'K' cursor is to be printed but this
7105
; transient state is also now transferred permanently to bit 3 of FLAGS
7106
; to let the interrupt routine know how to decode the next key.
7107
 
7108
;; OUT-C-1
7109
L18F3:  LD      HL,$5C3B        ; Address FLAGS
7110
        RES     3,(HL)          ; signal 'K' mode initially.
7111
        LD      A,$4B           ; prepare letter 'K'.
7112
        BIT     2,(HL)          ; test FLAGS - was the
7113
                                ; previous main character ':' or 'THEN' ?
7114
        JR      Z,L1909         ; forward to OUT-C-2 if so to print.
7115
 
7116
        SET     3,(HL)          ; signal 'L' mode to interrupt routine.
7117
                                ; Note. transient bit has been made permanent.
7118
        INC     A               ; augment from 'K' to 'L'.
7119
 
7120
        BIT     3,(IY+$30)      ; test FLAGS2 - consider caps lock ?
7121
                                ; which is maintained by KEY-INPUT.
7122
        JR      Z,L1909         ; forward to OUT-C-2 if not set to print.
7123
 
7124
        LD      A,$43           ; alter 'L' to 'C'.
7125
 
7126
;; OUT-C-2
7127
L1909:  PUSH    DE              ; save address pointer but OK as OUT-FLASH
7128
                                ; uses alternate set without RST 10.
7129
 
7130
        CALL    L18C1           ; routine OUT-FLASH to print.
7131
 
7132
        POP     DE              ; restore and
7133
        RET                     ; return.
7134
 
7135
; ----------------------------
7136
; Get line number of next line
7137
; ----------------------------
7138
; These two subroutines are called while editing.
7139
; This entry point is from ED-DOWN with HL addressing E_PPC
7140
; to fetch the next line number.
7141
; Also from AUTO-LIST with HL addressing S_TOP just to update S_TOP
7142
; with the value of the next line number. It gets fetched but is discarded.
7143
; These routines never get called while the editor is being used for input.
7144
 
7145
;; LN-FETCH
7146
L190F:  LD      E,(HL)          ; fetch low byte
7147
        INC     HL              ; address next
7148
        LD      D,(HL)          ; fetch high byte.
7149
        PUSH    HL              ; save system variable hi pointer.
7150
        EX      DE,HL           ; line number to HL,
7151
        INC     HL              ; increment as a starting point.
7152
        CALL    L196E           ; routine LINE-ADDR gets address in HL.
7153
        CALL    L1695           ; routine LINE-NO gets line number in DE.
7154
        POP     HL              ; restore system variable hi pointer.
7155
 
7156
; This entry point is from the ED-UP with HL addressing E_PPC_hi
7157
 
7158
;; LN-STORE
7159
L191C:  BIT     5,(IY+$37)      ; test FLAGX - input mode ?
7160
        RET     NZ              ; return if so.
7161
                                ; Note. above already checked by ED-UP/ED-DOWN.
7162
 
7163
        LD      (HL),D          ; save high byte of line number.
7164
        DEC     HL              ; address lower
7165
        LD      (HL),E          ; save low byte of line number.
7166
        RET                     ; return.
7167
 
7168
; -----------------------------------------
7169
; Outputting numbers at start of BASIC line
7170
; -----------------------------------------
7171
; This routine entered at OUT-SP-NO is used to compute then output the first
7172
; three digits of a 4-digit BASIC line printing a space if necessary.
7173
; The line number, or residual part, is held in HL and the BC register
7174
; holds a subtraction value -1000, -100 or -10.
7175
; Note. for example line number 200 -
7176
; space(out_char), 2(out_code), 0(out_char) final number always out-code.
7177
 
7178
;; OUT-SP-2
7179
L1925:  LD      A,E             ; will be space if OUT-CODE not yet called.
7180
                                ; or $FF if spaces are suppressed.
7181
                                ; else $30 ('0').
7182
                                ; (from the first instruction at OUT-CODE)
7183
                                ; this guy is just too clever.
7184
        AND     A               ; test bit 7 of A.
7185
        RET     M               ; return if $FF, as leading spaces not
7186
                                ; required. This is set when printing line
7187
                                ; number and statement in MAIN-5.
7188
 
7189
        JR      L1937           ; forward to exit via OUT-CHAR.
7190
 
7191
; ---
7192
 
7193
; -> the single entry point.
7194
 
7195
;; OUT-SP-NO
7196
L192A:  XOR     A               ; initialize digit to 0
7197
 
7198
;; OUT-SP-1
7199
L192B:  ADD     HL,BC           ; add negative number to HL.
7200
        INC     A               ; increment digit
7201
        JR      C,L192B         ; back to OUT-SP-1 until no carry from
7202
                                ; the addition.
7203
 
7204
        SBC     HL,BC           ; cancel the last addition
7205
        DEC     A               ; and decrement the digit.
7206
        JR      Z,L1925         ; back to OUT-SP-2 if it is zero.
7207
 
7208
        JP      L15EF           ; jump back to exit via OUT-CODE.    ->
7209
 
7210
 
7211
; -------------------------------------
7212
; Outputting characters in a BASIC line
7213
; -------------------------------------
7214
; This subroutine ...
7215
 
7216
;; OUT-CHAR
7217
L1937:  CALL    L2D1B           ; routine NUMERIC tests if it is a digit ?
7218
        JR      NC,L196C        ; to OUT-CH-3 to print digit without
7219
                                ; changing mode. Will be 'K' mode if digits
7220
                                ; are at beginning of edit line.
7221
 
7222
        CP      $21             ; less than quote character ?
7223
        JR      C,L196C         ; to OUT-CH-3 to output controls and space.
7224
 
7225
        RES     2,(IY+$01)      ; initialize FLAGS to 'K' mode and leave
7226
                                ; unchanged if this character would precede
7227
                                ; a keyword.
7228
 
7229
        CP      $CB             ; is character 'THEN' token ?
7230
        JR      Z,L196C         ; to OUT-CH-3 to output if so.
7231
 
7232
        CP      $3A             ; is it ':' ?
7233
        JR      NZ,L195A        ; to OUT-CH-1 if not statement separator
7234
                                ; to change mode back to 'L'.
7235
 
7236
        BIT     5,(IY+$37)      ; FLAGX  - Input Mode ??
7237
        JR      NZ,L1968        ; to OUT-CH-2 if in input as no statements.
7238
                                ; Note. this check should seemingly be at
7239
                                ; the start. Commands seem inappropriate in
7240
                                ; INPUT mode and are rejected by the syntax
7241
                                ; checker anyway.
7242
                                ; unless INPUT LINE is being used.
7243
 
7244
        BIT     2,(IY+$30)      ; test FLAGS2 - is the ':' within quotes ?
7245
        JR      Z,L196C         ; to OUT-CH-3 if ':' is outside quoted text.
7246
 
7247
        JR      L1968           ; to OUT-CH-2 as ':' is within quotes
7248
 
7249
; ---
7250
 
7251
;; OUT-CH-1
7252
L195A:  CP      $22             ; is it quote character '"'  ?
7253
        JR      NZ,L1968        ; to OUT-CH-2 with others to set 'L' mode.
7254
 
7255
        PUSH    AF              ; save character.
7256
        LD      A,($5C6A)       ; fetch FLAGS2.
7257
        XOR     $04             ; toggle the quotes flag.
7258
        LD      ($5C6A),A       ; update FLAGS2
7259
        POP     AF              ; and restore character.
7260
 
7261
;; OUT-CH-2
7262
L1968:  SET     2,(IY+$01)      ; update FLAGS - signal L mode if the cursor
7263
                                ; is next.
7264
 
7265
;; OUT-CH-3
7266
L196C:  RST     10H             ; PRINT-A vectors the character to
7267
                                ; channel 'S', 'K', 'R' or 'P'.
7268
        RET                     ; return.
7269
 
7270
; -------------------------------------------
7271
; Get starting address of line, or line after
7272
; -------------------------------------------
7273
; This routine is used often to get the address, in HL, of a BASIC line
7274
; number supplied in HL, or failing that the address of the following line
7275
; and the address of the previous line in DE.
7276
 
7277
;; LINE-ADDR
7278
L196E:  PUSH    HL              ; save line number in HL register
7279
        LD      HL,($5C53)      ; fetch start of program from PROG
7280
        LD      D,H             ; transfer address to
7281
        LD      E,L             ; the DE register pair.
7282
 
7283
;; LINE-AD-1
7284
L1974:  POP     BC              ; restore the line number to BC
7285
        CALL    L1980           ; routine CP-LINES compares with that
7286
                                ; addressed by HL
7287
        RET     NC              ; return if line has been passed or matched.
7288
                                ; if NZ, address of previous is in DE
7289
 
7290
        PUSH    BC              ; save the current line number
7291
        CALL    L19B8           ; routine NEXT-ONE finds address of next
7292
                                ; line number in DE, previous in HL.
7293
        EX      DE,HL           ; switch so next in HL
7294
        JR      L1974           ; back to LINE-AD-1 for another comparison
7295
 
7296
; --------------------
7297
; Compare line numbers
7298
; --------------------
7299
; This routine compares a line number supplied in BC with an addressed
7300
; line number pointed to by HL.
7301
 
7302
;; CP-LINES
7303
L1980:  LD      A,(HL)          ; Load the high byte of line number and
7304
        CP      B               ; compare with that of supplied line number.
7305
        RET     NZ              ; return if yet to match (carry will be set).
7306
 
7307
        INC     HL              ; address low byte of
7308
        LD      A,(HL)          ; number and pick up in A.
7309
        DEC     HL              ; step back to first position.
7310
        CP      C               ; now compare.
7311
        RET                     ; zero set if exact match.
7312
                                ; carry set if yet to match.
7313
                                ; no carry indicates a match or
7314
                                ; next available BASIC line or
7315
                                ; program end marker.
7316
 
7317
; -------------------
7318
; Find each statement
7319
; -------------------
7320
; The single entry point EACH-STMT is used to
7321
; 1) To find the D'th statement in a line.
7322
; 2) To find a token in held E.
7323
 
7324
;; not-used
7325
L1988:  INC     HL              ;
7326
        INC     HL              ;
7327
        INC     HL              ;
7328
 
7329
; -> entry point.
7330
 
7331
;; EACH-STMT
7332
L198B:  LD      ($5C5D),HL      ; save HL in CH_ADD
7333
        LD      C,$00           ; initialize quotes flag
7334
 
7335
;; EACH-S-1
7336
L1990:  DEC     D               ; decrease statement count
7337
        RET     Z               ; return if zero
7338
 
7339
 
7340
        RST     20H             ; NEXT-CHAR
7341
        CP      E               ; is it the search token ?
7342
        JR      NZ,L199A        ; forward to EACH-S-3 if not
7343
 
7344
        AND     A               ; clear carry
7345
        RET                     ; return signalling success.
7346
 
7347
; ---
7348
 
7349
;; EACH-S-2
7350
L1998:  INC     HL              ; next address
7351
        LD      A,(HL)          ; next character
7352
 
7353
;; EACH-S-3
7354
L199A:  CALL    L18B6           ; routine NUMBER skips if number marker
7355
        LD      ($5C5D),HL      ; save in CH_ADD
7356
        CP      $22             ; is it quotes '"' ?
7357
        JR      NZ,L19A5        ; to EACH-S-4 if not
7358
 
7359
        DEC     C               ; toggle bit 0 of C
7360
 
7361
;; EACH-S-4
7362
L19A5:  CP      $3A             ; is it ':'
7363
        JR      Z,L19AD         ; to EACH-S-5
7364
 
7365
        CP      $CB             ; 'THEN'
7366
        JR      NZ,L19B1        ; to EACH-S-6
7367
 
7368
;; EACH-S-5
7369
L19AD:  BIT     0,C             ; is it in quotes
7370
        JR      Z,L1990         ; to EACH-S-1 if not
7371
 
7372
;; EACH-S-6
7373
L19B1:  CP      $0D             ; end of line ?
7374
        JR      NZ,L1998        ; to EACH-S-2
7375
 
7376
        DEC     D               ; decrease the statement counter
7377
                                ; which should be zero else
7378
                                ; 'Statement Lost'.
7379
        SCF                     ; set carry flag - not found
7380
        RET                     ; return
7381
 
7382
; -----------------------------------------------------------------------
7383
; Storage of variables. For full details - see chapter 24.
7384
; ZX Spectrum BASIC Programming by Steven Vickers 1982.
7385
; It is bits 7-5 of the first character of a variable that allow
7386
; the six types to be distinguished. Bits 4-0 are the reduced letter.
7387
; So any variable name is higher that $3F and can be distinguished
7388
; also from the variables area end-marker $80.
7389
;
7390
; 76543210 meaning                               brief outline of format.
7391
; -------- ------------------------              -----------------------
7392
; 010      string variable.                      2 byte length + contents.
7393
; 110      string array.                         2 byte length + contents.
7394
; 100      array of numbers.                     2 byte length + contents.
7395
; 011      simple numeric variable.              5 bytes.
7396
; 101      variable length named numeric.        5 bytes.
7397
; 111      for-next loop variable.               18 bytes.
7398
; 10000000 the variables area end-marker.
7399
;
7400
; Note. any of the above seven will serve as a program end-marker.
7401
;
7402
; -----------------------------------------------------------------------
7403
 
7404
; ------------
7405
; Get next one
7406
; ------------
7407
; This versatile routine is used to find the address of the next line
7408
; in the program area or the next variable in the variables area.
7409
; The reason one routine is made to handle two apparently unrelated tasks
7410
; is that it can be called indiscriminately when merging a line or a
7411
; variable.
7412
 
7413
;; NEXT-ONE
7414
L19B8:  PUSH    HL              ; save the pointer address.
7415
        LD      A,(HL)          ; get first byte.
7416
        CP      $40             ; compare with upper limit for line numbers.
7417
        JR      C,L19D5         ; forward to NEXT-O-3 if within BASIC area.
7418
 
7419
; the continuation here is for the next variable unless the supplied
7420
; line number was erroneously over 16383. see RESTORE command.
7421
 
7422
        BIT     5,A             ; is it a string or an array variable ?
7423
        JR      Z,L19D6         ; forward to NEXT-O-4 to compute length.
7424
 
7425
        ADD     A,A             ; test bit 6 for single-character variables.
7426
        JP      M,L19C7         ; forward to NEXT-O-1 if so
7427
 
7428
        CCF                     ; clear the carry for long-named variables.
7429
                                ; it remains set for for-next loop variables.
7430
 
7431
;; NEXT-O-1
7432
L19C7:  LD      BC,$0005        ; set BC to 5 for floating point number
7433
        JR      NC,L19CE        ; forward to NEXT-O-2 if not a for/next
7434
                                ; variable.
7435
 
7436
        LD      C,$12           ; set BC to eighteen locations.
7437
                                ; value, limit, step, line and statement.
7438
 
7439
; now deal with long-named variables
7440
 
7441
;; NEXT-O-2
7442
L19CE:  RLA                     ; test if character inverted. carry will also
7443
                                ; be set for single character variables
7444
        INC     HL              ; address next location.
7445
        LD      A,(HL)          ; and load character.
7446
        JR      NC,L19CE        ; back to NEXT-O-2 if not inverted bit.
7447
                                ; forward immediately with single character
7448
                                ; variable names.
7449
 
7450
        JR      L19DB           ; forward to NEXT-O-5 to add length of
7451
                                ; floating point number(s etc.).
7452
 
7453
; ---
7454
 
7455
; this branch is for line numbers.
7456
 
7457
;; NEXT-O-3
7458
L19D5:  INC     HL              ; increment pointer to low byte of line no.
7459
 
7460
; strings and arrays rejoin here
7461
 
7462
;; NEXT-O-4
7463
L19D6:  INC     HL              ; increment to address the length low byte.
7464
        LD      C,(HL)          ; transfer to C and
7465
        INC     HL              ; point to high byte of length.
7466
        LD      B,(HL)          ; transfer that to B
7467
        INC     HL              ; point to start of BASIC/variable contents.
7468
 
7469
; the three types of numeric variables rejoin here
7470
 
7471
;; NEXT-O-5
7472
L19DB:  ADD     HL,BC           ; add the length to give address of next
7473
                                ; line/variable in HL.
7474
        POP     DE              ; restore previous address to DE.
7475
 
7476
; ------------------
7477
; Difference routine
7478
; ------------------
7479
; This routine terminates the above routine and is also called from the
7480
; start of the next routine to calculate the length to reclaim.
7481
 
7482
;; DIFFER
7483
L19DD:  AND     A               ; prepare for true subtraction.
7484
        SBC     HL,DE           ; subtract the two pointers.
7485
        LD      B,H             ; transfer result
7486
        LD      C,L             ; to BC register pair.
7487
        ADD     HL,DE           ; add back
7488
        EX      DE,HL           ; and switch pointers
7489
        RET                     ; return values are the length of area in BC,
7490
                                ; low pointer (previous) in HL,
7491
                                ; high pointer (next) in DE.
7492
 
7493
; -----------------------
7494
; Handle reclaiming space
7495
; -----------------------
7496
;
7497
 
7498
;; RECLAIM-1
7499
L19E5:  CALL    L19DD           ; routine DIFFER immediately above
7500
 
7501
;; RECLAIM-2
7502
L19E8:  PUSH    BC              ;
7503
 
7504
        LD      A,B             ;
7505
        CPL                     ;
7506
        LD      B,A             ;
7507
        LD      A,C             ;
7508
        CPL                     ;
7509
        LD      C,A             ;
7510
        INC     BC              ;
7511
 
7512
        CALL    L1664           ; routine POINTERS
7513
        EX      DE,HL           ;
7514
        POP     HL              ;
7515
 
7516
        ADD     HL,DE           ;
7517
        PUSH    DE              ;
7518
        LDIR                    ; copy bytes
7519
 
7520
        POP     HL              ;
7521
        RET                     ;
7522
 
7523
; ----------------------------------------
7524
; Read line number of line in editing area
7525
; ----------------------------------------
7526
; This routine reads a line number in the editing area returning the number
7527
; in the BC register or zero if no digits exist before commands.
7528
; It is called from LINE-SCAN to check the syntax of the digits.
7529
; It is called from MAIN-3 to extract the line number in preparation for
7530
; inclusion of the line in the BASIC program area.
7531
;
7532
; Interestingly the calculator stack is moved from its normal place at the
7533
; end of dynamic memory to an adequate area within the system variables area.
7534
; This ensures that in a low memory situation, that valid line numbers can
7535
; be extracted without raising an error and that memory can be reclaimed
7536
; by deleting lines. If the stack was in its normal place then a situation
7537
; arises whereby the Spectrum becomes locked with no means of reclaiming space.
7538
 
7539
;; E-LINE-NO
7540
L19FB:  LD      HL,($5C59)      ; load HL from system variable E_LINE.
7541
 
7542
        DEC     HL              ; decrease so that NEXT_CHAR can be used
7543
                                ; without skipping the first digit.
7544
 
7545
        LD      ($5C5D),HL      ; store in the system variable CH_ADD.
7546
 
7547
        RST     20H             ; NEXT-CHAR skips any noise and white-space
7548
                                ; to point exactly at the first digit.
7549
 
7550
        LD      HL,$5C92        ; use MEM-0 as a temporary calculator stack
7551
                                ; an overhead of three locations are needed.
7552
        LD      ($5C65),HL      ; set new STKEND.
7553
 
7554
        CALL    L2D3B           ; routine INT-TO-FP will read digits till
7555
                                ; a non-digit found.
7556
        CALL    L2DA2           ; routine FP-TO-BC will retrieve number
7557
                                ; from stack at membot.
7558
        JR      C,L1A15         ; forward to E-L-1 if overflow i.e. > 65535.
7559
                                ; 'Nonsense in BASIC'
7560
 
7561
        LD      HL,$D8F0        ; load HL with value -9999
7562
        ADD     HL,BC           ; add to line number in BC
7563
 
7564
;; E-L-1
7565
L1A15:  JP      C,L1C8A         ; to REPORT-C 'Nonsense in BASIC' if over.
7566
                                ; Note. As ERR_SP points to ED_ERROR
7567
                                ; the report is never produced although
7568
                                ; the RST $08 will update X_PTR leading to
7569
                                ; the error marker being displayed when
7570
                                ; the ED_LOOP is reiterated.
7571
                                ; in fact, since it is immediately
7572
                                ; cancelled, any report will do.
7573
 
7574
; a line in the range 0 - 9999 has been entered.
7575
 
7576
        JP      L16C5           ; jump back to SET-STK to set the calculator 
7577
                                ; stack back to its normal place and exit 
7578
                                ; from there.
7579
 
7580
; ---------------------------------
7581
; Report and line number outputting
7582
; ---------------------------------
7583
; Entry point OUT-NUM-1 is used by the Error Reporting code to print
7584
; the line number and later the statement number held in BC.
7585
; If the statement was part of a direct command then -2 is used as a
7586
; dummy line number so that zero will be printed in the report.
7587
; This routine is also used to print the exponent of E-format numbers.
7588
;
7589
; Entry point OUT-NUM-2 is used from OUT-LINE to output the line number
7590
; addressed by HL with leading spaces if necessary.
7591
 
7592
;; OUT-NUM-1
7593
L1A1B:  PUSH    DE              ; save the
7594
        PUSH    HL              ; registers.
7595
        XOR     A               ; set A to zero.
7596
        BIT     7,B             ; is the line number minus two ?
7597
        JR      NZ,L1A42        ; forward to OUT-NUM-4 if so to print zero 
7598
                                ; for a direct command.
7599
 
7600
        LD      H,B             ; transfer the
7601
        LD      L,C             ; number to HL.
7602
        LD      E,$FF           ; signal 'no leading zeros'.
7603
        JR      L1A30           ; forward to continue at OUT-NUM-3
7604
 
7605
; ---
7606
 
7607
; from OUT-LINE - HL addresses line number.
7608
 
7609
;; OUT-NUM-2
7610
L1A28:  PUSH    DE              ; save flags
7611
        LD      D,(HL)          ; high byte to D
7612
        INC     HL              ; address next
7613
        LD      E,(HL)          ; low byte to E
7614
        PUSH    HL              ; save pointer
7615
        EX      DE,HL           ; transfer number to HL
7616
        LD      E,$20           ; signal 'output leading spaces'
7617
 
7618
;; OUT-NUM-3
7619
L1A30:  LD      BC,$FC18        ; value -1000
7620
        CALL    L192A           ; routine OUT-SP-NO outputs space or number
7621
        LD      BC,$FF9C        ; value -100
7622
        CALL    L192A           ; routine OUT-SP-NO
7623
        LD      C,$F6           ; value -10 ( B is still $FF )
7624
        CALL    L192A           ; routine OUT-SP-NO
7625
        LD      A,L             ; remainder to A.
7626
 
7627
;; OUT-NUM-4
7628
L1A42:  CALL    L15EF           ; routine OUT-CODE for final digit.
7629
                                ; else report code zero wouldn't get
7630
                                ; printed.
7631
        POP     HL              ; restore the
7632
        POP     DE              ; registers and
7633
        RET                     ; return.
7634
 
7635
 
7636
;***************************************************
7637
;** Part 7. BASIC LINE AND COMMAND INTERPRETATION **
7638
;***************************************************
7639
 
7640
; ----------------
7641
; The offset table
7642
; ----------------
7643
; The BASIC interpreter has found a command code $CE - $FF
7644
; which is then reduced to range $00 - $31 and added to the base address
7645
; of this table to give the address of an offset which, when added to
7646
; the offset therein, gives the location in the following parameter table
7647
; where a list of class codes, separators and addresses relevant to the
7648
; command exists.
7649
 
7650
;; offst-tbl
7651
L1A48:  DB    L1AF9 - $       ; B1 offset to Address: P-DEF-FN
7652
        DB    L1B14 - $       ; CB offset to Address: P-CAT
7653
        DB    L1B06 - $       ; BC offset to Address: P-FORMAT
7654
        DB    L1B0A - $       ; BF offset to Address: P-MOVE
7655
        DB    L1B10 - $       ; C4 offset to Address: P-ERASE
7656
        DB    L1AFC - $       ; AF offset to Address: P-OPEN
7657
        DB    L1B02 - $       ; B4 offset to Address: P-CLOSE
7658
        DB    L1AE2 - $       ; 93 offset to Address: P-MERGE
7659
        DB    L1AE1 - $       ; 91 offset to Address: P-VERIFY
7660
        DB    L1AE3 - $       ; 92 offset to Address: P-BEEP
7661
        DB    L1AE7 - $       ; 95 offset to Address: P-CIRCLE
7662
        DB    L1AEB - $       ; 98 offset to Address: P-INK
7663
        DB    L1AEC - $       ; 98 offset to Address: P-PAPER
7664
        DB    L1AED - $       ; 98 offset to Address: P-FLASH
7665
        DB    L1AEE - $       ; 98 offset to Address: P-BRIGHT
7666
        DB    L1AEF - $       ; 98 offset to Address: P-INVERSE
7667
        DB    L1AF0 - $       ; 98 offset to Address: P-OVER
7668
        DB    L1AF1 - $       ; 98 offset to Address: P-OUT
7669
        DB    L1AD9 - $       ; 7F offset to Address: P-LPRINT
7670
        DB    L1ADC - $       ; 81 offset to Address: P-LLIST
7671
        DB    L1A8A - $       ; 2E offset to Address: P-STOP
7672
        DB    L1AC9 - $       ; 6C offset to Address: P-READ
7673
        DB    L1ACC - $       ; 6E offset to Address: P-DATA
7674
        DB    L1ACF - $       ; 70 offset to Address: P-RESTORE
7675
        DB    L1AA8 - $       ; 48 offset to Address: P-NEW
7676
        DB    L1AF5 - $       ; 94 offset to Address: P-BORDER
7677
        DB    L1AB8 - $       ; 56 offset to Address: P-CONT
7678
        DB    L1AA2 - $       ; 3F offset to Address: P-DIM
7679
        DB    L1AA5 - $       ; 41 offset to Address: P-REM
7680
        DB    L1A90 - $       ; 2B offset to Address: P-FOR
7681
        DB    L1A7D - $       ; 17 offset to Address: P-GO-TO
7682
        DB    L1A86 - $       ; 1F offset to Address: P-GO-SUB
7683
        DB    L1A9F - $       ; 37 offset to Address: P-INPUT
7684
        DB    L1AE0 - $       ; 77 offset to Address: P-LOAD
7685
        DB    L1AAE - $       ; 44 offset to Address: P-LIST
7686
        DB    L1A7A - $       ; 0F offset to Address: P-LET
7687
        DB    L1AC5 - $       ; 59 offset to Address: P-PAUSE
7688
        DB    L1A98 - $       ; 2B offset to Address: P-NEXT
7689
        DB    L1AB1 - $       ; 43 offset to Address: P-POKE
7690
        DB    L1A9C - $       ; 2D offset to Address: P-PRINT
7691
        DB    L1AC1 - $       ; 51 offset to Address: P-PLOT
7692
        DB    L1AAB - $       ; 3A offset to Address: P-RUN
7693
        DB    L1ADF - $       ; 6D offset to Address: P-SAVE
7694
        DB    L1AB5 - $       ; 42 offset to Address: P-RANDOM
7695
        DB    L1A81 - $       ; 0D offset to Address: P-IF
7696
        DB    L1ABE - $       ; 49 offset to Address: P-CLS
7697
        DB    L1AD2 - $       ; 5C offset to Address: P-DRAW
7698
        DB    L1ABB - $       ; 44 offset to Address: P-CLEAR
7699
        DB    L1A8D - $       ; 15 offset to Address: P-RETURN
7700
        DB    L1AD6 - $       ; 5D offset to Address: P-COPY
7701
 
7702
 
7703
; -------------------------------
7704
; The parameter or "Syntax" table
7705
; -------------------------------
7706
; For each command there exists a variable list of parameters.
7707
; If the character is greater than a space it is a required separator.
7708
; If less, then it is a command class in the range 00 - 0B.
7709
; Note that classes 00, 03 and 05 will fetch the addresses from this table.
7710
; Some classes e.g. 07 and 0B have the same address in all invocations
7711
; and the command is re-computed from the low-byte of the parameter address.
7712
; Some e.g. 02 are only called once so a call to the command is made from
7713
; within the class routine rather than holding the address within the table.
7714
; Some class routines check syntax entirely and some leave this task for the
7715
; command itself.
7716
; Others for example CIRCLE (x,y,z) check the first part (x,y) using the
7717
; class routine and the final part (,z) within the command.
7718
; The last few commands appear to have been added in a rush but their syntax
7719
; is rather simple e.g. MOVE "M1","M2"
7720
 
7721
;; P-LET
7722
L1A7A:  DB    $01             ; Class-01 - A variable is required.
7723
        DB    $3D             ; Separator:  '='
7724
        DB    $02             ; Class-02 - An expression, numeric or string,
7725
                                ; must follow.
7726
 
7727
;; P-GO-TO
7728
L1A7D:  DB    $06             ; Class-06 - A numeric expression must follow.
7729
        DB    $00             ; Class-00 - No further operands.
7730
        DEFW    L1E67           ; Address: $1E67; Address: GO-TO
7731
 
7732
;; P-IF
7733
L1A81:  DB    $06             ; Class-06 - A numeric expression must follow.
7734
        DB    $CB             ; Separator:  'THEN'
7735
        DB    $05             ; Class-05 - Variable syntax checked
7736
                                ; by routine.
7737
        DEFW    L1CF0           ; Address: $1CF0; Address: IF
7738
 
7739
;; P-GO-SUB
7740
L1A86:  DB    $06             ; Class-06 - A numeric expression must follow.
7741
        DB    $00             ; Class-00 - No further operands.
7742
        DEFW    L1EED           ; Address: $1EED; Address: GO-SUB
7743
 
7744
;; P-STOP
7745
L1A8A:  DB    $00             ; Class-00 - No further operands.
7746
        DEFW    L1CEE           ; Address: $1CEE; Address: STOP
7747
 
7748
;; P-RETURN
7749
L1A8D:  DB    $00             ; Class-00 - No further operands.
7750
        DEFW    L1F23           ; Address: $1F23; Address: RETURN
7751
 
7752
;; P-FOR
7753
L1A90:  DB    $04             ; Class-04 - A single character variable must
7754
                                ; follow.
7755
        DB    $3D             ; Separator:  '='
7756
        DB    $06             ; Class-06 - A numeric expression must follow.
7757
        DB    $CC             ; Separator:  'TO'
7758
        DB    $06             ; Class-06 - A numeric expression must follow.
7759
        DB    $05             ; Class-05 - Variable syntax checked
7760
                                ; by routine.
7761
        DEFW    L1D03           ; Address: $1D03; Address: FOR
7762
 
7763
;; P-NEXT
7764
L1A98:  DB    $04             ; Class-04 - A single character variable must
7765
                                ; follow.
7766
        DB    $00             ; Class-00 - No further operands.
7767
        DEFW    L1DAB           ; Address: $1DAB; Address: NEXT
7768
 
7769
;; P-PRINT
7770
L1A9C:  DB    $05             ; Class-05 - Variable syntax checked entirely
7771
                                ; by routine.
7772
        DEFW    L1FCD           ; Address: $1FCD; Address: PRINT
7773
 
7774
;; P-INPUT
7775
L1A9F:  DB    $05             ; Class-05 - Variable syntax checked entirely
7776
                                ; by routine.
7777
        DEFW    L2089           ; Address: $2089; Address: INPUT
7778
 
7779
;; P-DIM
7780
L1AA2:  DB    $05             ; Class-05 - Variable syntax checked entirely
7781
                                ; by routine.
7782
        DEFW    L2C02           ; Address: $2C02; Address: DIM
7783
 
7784
;; P-REM
7785
L1AA5:  DB    $05             ; Class-05 - Variable syntax checked entirely
7786
                                ; by routine.
7787
        DEFW    L1BB2           ; Address: $1BB2; Address: REM
7788
 
7789
;; P-NEW
7790
L1AA8:  DB    $00             ; Class-00 - No further operands.
7791
        DEFW    L11B7           ; Address: $11B7; Address: NEW
7792
 
7793
;; P-RUN
7794
L1AAB:  DB    $03             ; Class-03 - A numeric expression may follow
7795
                                ; else default to zero.
7796
        DEFW    L1EA1           ; Address: $1EA1; Address: RUN
7797
 
7798
;; P-LIST
7799
L1AAE:  DB    $05             ; Class-05 - Variable syntax checked entirely
7800
                                ; by routine.
7801
        DEFW    L17F9           ; Address: $17F9; Address: LIST
7802
 
7803
;; P-POKE
7804
L1AB1:  DB    $08             ; Class-08 - Two comma-separated numeric
7805
                                ; expressions required.
7806
        DB    $00             ; Class-00 - No further operands.
7807
        DEFW    L1E80           ; Address: $1E80; Address: POKE
7808
 
7809
;; P-RANDOM
7810
L1AB5:  DB    $03             ; Class-03 - A numeric expression may follow
7811
                                ; else default to zero.
7812
        DEFW    L1E4F           ; Address: $1E4F; Address: RANDOMIZE
7813
 
7814
;; P-CONT
7815
L1AB8:  DB    $00             ; Class-00 - No further operands.
7816
        DEFW    L1E5F           ; Address: $1E5F; Address: CONTINUE
7817
 
7818
;; P-CLEAR
7819
L1ABB:  DB    $03             ; Class-03 - A numeric expression may follow
7820
                                ; else default to zero.
7821
        DEFW    L1EAC           ; Address: $1EAC; Address: CLEAR
7822
 
7823
;; P-CLS
7824
L1ABE:  DB    $00             ; Class-00 - No further operands.
7825
        DEFW    L0D6B           ; Address: $0D6B; Address: CLS
7826
 
7827
;; P-PLOT
7828
L1AC1:  DB    $09             ; Class-09 - Two comma-separated numeric
7829
                                ; expressions required with optional colour
7830
                                ; items.
7831
        DB    $00             ; Class-00 - No further operands.
7832
        DEFW    L22DC           ; Address: $22DC; Address: PLOT
7833
 
7834
;; P-PAUSE
7835
L1AC5:  DB    $06             ; Class-06 - A numeric expression must follow.
7836
        DB    $00             ; Class-00 - No further operands.
7837
        DEFW    L1F3A           ; Address: $1F3A; Address: PAUSE
7838
 
7839
;; P-READ
7840
L1AC9:  DB    $05             ; Class-05 - Variable syntax checked entirely
7841
                                ; by routine.
7842
        DEFW    L1DED           ; Address: $1DED; Address: READ
7843
 
7844
;; P-DATA
7845
L1ACC:  DB    $05             ; Class-05 - Variable syntax checked entirely
7846
                                ; by routine.
7847
        DEFW    L1E27           ; Address: $1E27; Address: DATA
7848
 
7849
;; P-RESTORE
7850
L1ACF:  DB    $03             ; Class-03 - A numeric expression may follow
7851
                                ; else default to zero.
7852
        DEFW    L1E42           ; Address: $1E42; Address: RESTORE
7853
 
7854
;; P-DRAW
7855
L1AD2:  DB    $09             ; Class-09 - Two comma-separated numeric
7856
                                ; expressions required with optional colour
7857
                                ; items.
7858
        DB    $05             ; Class-05 - Variable syntax checked
7859
                                ; by routine.
7860
        DEFW    L2382           ; Address: $2382; Address: DRAW
7861
 
7862
;; P-COPY
7863
L1AD6:  DB    $00             ; Class-00 - No further operands.
7864
        DEFW    L0EAC           ; Address: $0EAC; Address: COPY
7865
 
7866
;; P-LPRINT
7867
L1AD9:  DB    $05             ; Class-05 - Variable syntax checked entirely
7868
                                ; by routine.
7869
        DEFW    L1FC9           ; Address: $1FC9; Address: LPRINT
7870
 
7871
;; P-LLIST
7872
L1ADC:  DB    $05             ; Class-05 - Variable syntax checked entirely
7873
                                ; by routine.
7874
        DEFW    L17F5           ; Address: $17F5; Address: LLIST
7875
 
7876
;; P-SAVE
7877
L1ADF:  DB    $0B             ; Class-0B - Offset address converted to tape
7878
                                ; command.
7879
 
7880
;; P-LOAD
7881
L1AE0:  DB    $0B             ; Class-0B - Offset address converted to tape
7882
                                ; command.
7883
 
7884
;; P-VERIFY
7885
L1AE1:  DB    $0B             ; Class-0B - Offset address converted to tape
7886
                                ; command.
7887
 
7888
;; P-MERGE
7889
L1AE2:  DB    $0B             ; Class-0B - Offset address converted to tape
7890
                                ; command.
7891
 
7892
;; P-BEEP
7893
L1AE3:  DB    $08             ; Class-08 - Two comma-separated numeric
7894
                                ; expressions required.
7895
        DB    $00             ; Class-00 - No further operands.
7896
        DEFW    L03F8           ; Address: $03F8; Address: BEEP
7897
 
7898
;; P-CIRCLE
7899
L1AE7:  DB    $09             ; Class-09 - Two comma-separated numeric
7900
                                ; expressions required with optional colour
7901
                                ; items.
7902
        DB    $05             ; Class-05 - Variable syntax checked
7903
                                ; by routine.
7904
        DEFW    L2320           ; Address: $2320; Address: CIRCLE
7905
 
7906
;; P-INK
7907
L1AEB:  DB    $07             ; Class-07 - Offset address is converted to
7908
                                ; colour code.
7909
 
7910
;; P-PAPER
7911
L1AEC:  DB    $07             ; Class-07 - Offset address is converted to
7912
                                ; colour code.
7913
 
7914
;; P-FLASH
7915
L1AED:  DB    $07             ; Class-07 - Offset address is converted to
7916
                                ; colour code.
7917
 
7918
;; P-BRIGHT
7919
L1AEE:  DB    $07             ; Class-07 - Offset address is converted to
7920
                                ; colour code.
7921
 
7922
;; P-INVERSE
7923
L1AEF:  DB    $07             ; Class-07 - Offset address is converted to
7924
                                ; colour code.
7925
 
7926
;; P-OVER
7927
L1AF0:  DB    $07             ; Class-07 - Offset address is converted to
7928
                                ; colour code.
7929
 
7930
;; P-OUT
7931
L1AF1:  DB    $08             ; Class-08 - Two comma-separated numeric
7932
                                ; expressions required.
7933
        DB    $00             ; Class-00 - No further operands.
7934
        DEFW    L1E7A           ; Address: $1E7A; Address: OUT
7935
 
7936
;; P-BORDER
7937
L1AF5:  DB    $06             ; Class-06 - A numeric expression must follow.
7938
        DB    $00             ; Class-00 - No further operands.
7939
        DEFW    L2294           ; Address: $2294; Address: BORDER
7940
 
7941
;; P-DEF-FN
7942
L1AF9:  DB    $05             ; Class-05 - Variable syntax checked entirely
7943
                                ; by routine.
7944
        DEFW    L1F60           ; Address: $1F60; Address: DEF-FN
7945
 
7946
;; P-OPEN
7947
L1AFC:  DB    $06             ; Class-06 - A numeric expression must follow.
7948
        DB    $2C             ; Separator:  ','          see Footnote *
7949
        DB    $0A             ; Class-0A - A string expression must follow.
7950
        DB    $00             ; Class-00 - No further operands.
7951
        DEFW    L1736           ; Address: $1736; Address: OPEN
7952
 
7953
;; P-CLOSE
7954
L1B02:  DB    $06             ; Class-06 - A numeric expression must follow.
7955
        DB    $00             ; Class-00 - No further operands.
7956
        DEFW    L16E5           ; Address: $16E5; Address: CLOSE
7957
 
7958
;; P-FORMAT
7959
L1B06:  DB    $0A             ; Class-0A - A string expression must follow.
7960
        DB    $00             ; Class-00 - No further operands.
7961
        DEFW    L1793           ; Address: $1793; Address: CAT-ETC
7962
 
7963
;; P-MOVE
7964
L1B0A:  DB    $0A             ; Class-0A - A string expression must follow.
7965
        DB    $2C             ; Separator:  ','
7966
        DB    $0A             ; Class-0A - A string expression must follow.
7967
        DB    $00             ; Class-00 - No further operands.
7968
        DEFW    L1793           ; Address: $1793; Address: CAT-ETC
7969
 
7970
;; P-ERASE
7971
L1B10:  DB    $0A             ; Class-0A - A string expression must follow.
7972
        DB    $00             ; Class-00 - No further operands.
7973
        DEFW    L1793           ; Address: $1793; Address: CAT-ETC
7974
 
7975
;; P-CAT
7976
L1B14:  DB    $00             ; Class-00 - No further operands.
7977
        DEFW    L1793           ; Address: $1793; Address: CAT-ETC
7978
 
7979
; * Note that a comma is required as a separator with the OPEN command
7980
; but the Interface 1 programmers relaxed this allowing ';' as an
7981
; alternative for their channels creating a confusing mixture of
7982
; allowable syntax as it is this ROM which opens or re-opens the
7983
; normal channels.
7984
 
7985
; -------------------------------
7986
; Main parser (BASIC interpreter)
7987
; -------------------------------
7988
; This routine is called once from MAIN-2 when the BASIC line is to
7989
; be entered or re-entered into the Program area and the syntax
7990
; requires checking.
7991
 
7992
;; LINE-SCAN
7993
L1B17:  RES     7,(IY+$01)      ; update FLAGS - signal checking syntax
7994
        CALL    L19FB           ; routine E-LINE-NO              >>
7995
                                ; fetches the line number if in range.
7996
 
7997
        XOR     A               ; clear the accumulator.
7998
        LD      ($5C47),A       ; set statement number SUBPPC to zero.
7999
        DEC     A               ; set accumulator to $FF.
8000
        LD      ($5C3A),A       ; set ERR_NR to 'OK' - 1.
8001
        JR      L1B29           ; forward to continue at STMT-L-1.
8002
 
8003
; --------------
8004
; Statement loop
8005
; --------------
8006
;
8007
;
8008
 
8009
;; STMT-LOOP
8010
L1B28:  RST     20H             ; NEXT-CHAR
8011
 
8012
; -> the entry point from above or LINE-RUN
8013
;; STMT-L-1
8014
L1B29:  CALL    L16BF           ; routine SET-WORK clears workspace etc.
8015
 
8016
        INC     (IY+$0D)        ; increment statement number SUBPPC
8017
        JP      M,L1C8A         ; to REPORT-C to raise
8018
                                ; 'Nonsense in BASIC' if over 127.
8019
 
8020
        RST     18H             ; GET-CHAR
8021
 
8022
        LD      B,$00           ; set B to zero for later indexing.
8023
                                ; early so any other reason ???
8024
 
8025
        CP      $0D             ; is character carriage return ?
8026
                                ; i.e. an empty statement.
8027
        JR      Z,L1BB3         ; forward to LINE-END if so.
8028
 
8029
        CP      $3A             ; is it statement end marker ':' ?
8030
                                ; i.e. another type of empty statement.
8031
        JR      Z,L1B28         ; back to STMT-LOOP if so.
8032
 
8033
        LD      HL,L1B76        ; address: STMT-RET
8034
        PUSH    HL              ; is now pushed as a return address
8035
        LD      C,A             ; transfer the current character to C.
8036
 
8037
; advance CH_ADD to a position after command and test if it is a command.
8038
 
8039
        RST     20H             ; NEXT-CHAR to advance pointer
8040
        LD      A,C             ; restore current character
8041
        SUB     $CE             ; subtract 'DEF FN' - first command
8042
        JP      C,L1C8A         ; jump to REPORT-C if less than a command
8043
                                ; raising 
8044
                                ; 'Nonsense in BASIC'
8045
 
8046
        LD      C,A             ; put the valid command code back in C.
8047
                                ; register B is zero.
8048
        LD      HL,L1A48        ; address: offst-tbl
8049
        ADD     HL,BC           ; index into table with one of 50 commands.
8050
        LD      C,(HL)          ; pick up displacement to syntax table entry.
8051
        ADD     HL,BC           ; add to address the relevant entry.
8052
        JR      L1B55           ; forward to continue at GET-PARAM
8053
 
8054
; ----------------------
8055
; The main scanning loop
8056
; ----------------------
8057
; not documented properly
8058
;
8059
 
8060
;; SCAN-LOOP
8061
L1B52:  LD      HL,($5C74)      ; fetch temporary address from T_ADDR
8062
                                ; during subsequent loops.
8063
 
8064
; -> the initial entry point with HL addressing start of syntax table entry.
8065
 
8066
;; GET-PARAM
8067
L1B55:  LD      A,(HL)          ; pick up the parameter.
8068
        INC     HL              ; address next one.
8069
        LD      ($5C74),HL      ; save pointer in system variable T_ADDR
8070
 
8071
        LD      BC,L1B52        ; address: SCAN-LOOP
8072
        PUSH    BC              ; is now pushed on stack as looping address.
8073
        LD      C,A             ; store parameter in C.
8074
        CP      $20             ; is it greater than ' '  ?
8075
        JR      NC,L1B6F        ; forward to SEPARATOR to check that correct
8076
                                ; separator appears in statement if so.
8077
 
8078
        LD      HL,L1C01        ; address: class-tbl.
8079
        LD      B,$00           ; prepare to index into the class table.
8080
        ADD     HL,BC           ; index to find displacement to routine.
8081
        LD      C,(HL)          ; displacement to BC
8082
        ADD     HL,BC           ; add to address the CLASS routine.
8083
        PUSH    HL              ; push the address on the stack.
8084
 
8085
        RST     18H             ; GET-CHAR - HL points to place in statement.
8086
 
8087
        DEC     B               ; reset the zero flag - the initial state
8088
                                ; for all class routines.
8089
 
8090
        RET                     ; and make an indirect jump to routine
8091
                                ; and then SCAN-LOOP (also on stack).
8092
 
8093
; Note. one of the class routines will eventually drop the return address
8094
; off the stack breaking out of the above seemingly endless loop.
8095
 
8096
; ----------------
8097
; Verify separator
8098
; ----------------
8099
; This routine is called once to verify that the mandatory separator
8100
; present in the parameter table is also present in the correct
8101
; location following the command. For example, the 'THEN' token after
8102
; the 'IF' token and expression.
8103
 
8104
;; SEPARATOR
8105
L1B6F:  RST     18H             ; GET-CHAR
8106
        CP      C               ; does it match the character in C ?
8107
        JP      NZ,L1C8A        ; jump forward to REPORT-C if not
8108
                                ; 'Nonsense in BASIC'.
8109
 
8110
        RST     20H             ; NEXT-CHAR advance to next character
8111
        RET                     ; return.
8112
 
8113
; ------------------------------
8114
; Come here after interpretation
8115
; ------------------------------
8116
;
8117
;
8118
 
8119
;; STMT-RET
8120
L1B76:  CALL    L1F54           ; routine BREAK-KEY is tested after every
8121
                                ; statement.
8122
        JR      C,L1B7D         ; step forward to STMT-R-1 if not pressed.
8123
 
8124
;; REPORT-L
8125
L1B7B:  RST     08H             ; ERROR-1
8126
        DB    $14             ; Error Report: BREAK into program
8127
 
8128
;; STMT-R-1
678 savelij 8129
L1B7D           CALL L3B4D              ; Spectrum 128 patch
384 savelij 8130
                NOP
8131
 
8132
L1B81:  JR      NZ,L1BF4        ; forward to STMT-NEXT if a program line.
8133
 
8134
        LD      HL,($5C42)      ; fetch line number from NEWPPC
8135
        BIT     7,H             ; will be set if minus two - direct command(s)
8136
        JR      Z,L1B9E         ; forward to LINE-NEW if a jump is to be
8137
                                ; made to a new program line/statement.
8138
 
8139
; --------------------
8140
; Run a direct command
8141
; --------------------
8142
; A direct command is to be run or, if continuing from above,
8143
; the next statement of a direct command is to be considered.
8144
 
8145
;; LINE-RUN
8146
L1B8A:  LD      HL,$FFFE        ; The dummy value minus two
8147
        LD      ($5C45),HL      ; is set/reset as line number in PPC.
8148
        LD      HL,($5C61)      ; point to end of line + 1 - WORKSP.
8149
        DEC     HL              ; now point to $80 end-marker.
8150
        LD      DE,($5C59)      ; address the start of line E_LINE.
8151
        DEC     DE              ; now location before - for GET-CHAR.
8152
        LD      A,($5C44)       ; load statement to A from NSPPC.
8153
        JR      L1BD1           ; forward to NEXT-LINE.
8154
 
8155
; ------------------------------
8156
; Find start address of new line
8157
; ------------------------------
8158
; The branch was to here if a jump is to made to a new line number
8159
; and statement.
8160
; That is the previous statement was a GO TO, GO SUB, RUN, RETURN, NEXT etc..
8161
 
8162
;; LINE-NEW
8163
L1B9E:  CALL    L196E           ; routine LINE-ADDR gets address of line
8164
                                ; returning zero flag set if line found.
8165
        LD      A,($5C44)       ; fetch new statement from NSPPC
8166
        JR      Z,L1BBF         ; forward to LINE-USE if line matched.
8167
 
8168
; continue as must be a direct command.
8169
 
8170
        AND     A               ; test statement which should be zero
8171
        JR      NZ,L1BEC        ; forward to REPORT-N if not.
8172
                                ; 'Statement lost'
8173
 
8174
; 
8175
 
8176
        LD      B,A             ; save statement in B. ?
8177
        LD      A,(HL)          ; fetch high byte of line number.
8178
        AND     $C0             ; test if using direct command
8179
                                ; a program line is less than $3F
8180
        LD      A,B             ; retrieve statement.
8181
                                ; (we can assume it is zero).
8182
        JR      Z,L1BBF         ; forward to LINE-USE if was a program line
8183
 
8184
; Alternatively a direct statement has finished correctly.
8185
 
8186
;; REPORT-0
8187
L1BB0:  RST     08H             ; ERROR-1
8188
        DB    $FF             ; Error Report: OK
8189
 
8190
; ------------------
8191
; Handle REM command
8192
; ------------------
8193
; The REM command routine.
8194
; The return address STMT-RET is dropped and the rest of line ignored.
8195
 
8196
;; REM
8197
L1BB2:  POP     BC              ; drop return address STMT-RET and
8198
                                ; continue ignoring rest of line.
8199
 
8200
; ------------
8201
; End of line?
8202
; ------------
8203
;
8204
;
8205
 
8206
;; LINE-END
8207
L1BB3:  CALL    L2530           ; routine SYNTAX-Z  (UNSTACK-Z?)
8208
        RET     Z               ; return if checking syntax.
8209
 
8210
        LD      HL,($5C55)      ; fetch NXTLIN to HL.
8211
        LD      A,$C0           ; test against the
8212
        AND     (HL)            ; system limit $3F.
8213
        RET     NZ              ; return if more as must be
8214
                                ; end of program.
8215
                                ; (or direct command)
8216
 
8217
        XOR     A               ; set statement to zero.
8218
 
8219
; and continue to set up the next following line and then consider this new one.
8220
 
8221
; ---------------------
8222
; General line checking
8223
; ---------------------
8224
; The branch was here from LINE-NEW if BASIC is branching.
8225
; or a continuation from above if dealing with a new sequential line.
8226
; First make statement zero number one leaving others unaffected.
8227
 
8228
;; LINE-USE
8229
L1BBF:  CP      $01             ; will set carry if zero.
8230
        ADC     A,$00           ; add in any carry.
8231
 
8232
        LD      D,(HL)          ; high byte of line number to D.
8233
        INC     HL              ; advance pointer.
8234
        LD      E,(HL)          ; low byte of line number to E.
8235
        LD      ($5C45),DE      ; set system variable PPC.
8236
 
8237
        INC     HL              ; advance pointer.
8238
        LD      E,(HL)          ; low byte of line length to E.
8239
        INC     HL              ; advance pointer.
8240
        LD      D,(HL)          ; high byte of line length to D.
8241
 
8242
        EX      DE,HL           ; swap pointer to DE before
8243
        ADD     HL,DE           ; adding to address the end of line.
8244
        INC     HL              ; advance to start of next line.
8245
 
8246
; -----------------------------
8247
; Update NEXT LINE but consider
8248
; previous line or edit line.
8249
; -----------------------------
8250
; The pointer will be the next line if continuing from above or to
8251
; edit line end-marker ($80) if from LINE-RUN.
8252
 
8253
;; NEXT-LINE
8254
L1BD1:  LD      ($5C55),HL      ; store pointer in system variable NXTLIN
8255
 
8256
        EX      DE,HL           ; bring back pointer to previous or edit line
8257
        LD      ($5C5D),HL      ; and update CH_ADD with character address.
8258
 
8259
        LD      D,A             ; store statement in D.
8260
        LD      E,$00           ; set E to zero to suppress token searching
8261
                                ; if EACH-STMT is to be called.
8262
        LD      (IY+$0A),$FF    ; set statement NSPPC to $FF signalling
8263
                                ; no jump to be made.
8264
        DEC     D               ; decrement and test statement
8265
        LD      (IY+$0D),D      ; set SUBPPC to decremented statement number.
8266
        JP      Z,L1B28         ; to STMT-LOOP if result zero as statement is
8267
                                ; at start of line and address is known.
8268
 
8269
        INC     D               ; else restore statement.
8270
        CALL    L198B           ; routine EACH-STMT finds the D'th statement
8271
                                ; address as E does not contain a token.
8272
        JR      Z,L1BF4         ; forward to STMT-NEXT if address found.
8273
 
8274
;; REPORT-N
8275
L1BEC:  RST     08H             ; ERROR-1
8276
        DB    $16             ; Error Report: Statement lost
8277
 
8278
; -----------------
8279
; End of statement?
8280
; -----------------
8281
; This combination of routines is called from 20 places when
8282
; the end of a statement should have been reached and all preceding
8283
; syntax is in order.
8284
 
8285
;; CHECK-END
8286
L1BEE:  CALL    L2530           ; routine SYNTAX-Z
8287
        RET     NZ              ; return immediately in runtime
8288
 
8289
        POP     BC              ; drop address of calling routine.
8290
        POP     BC              ; drop address STMT-RET.
8291
                                ; and continue to find next statement.
8292
 
8293
; --------------------
8294
; Go to next statement
8295
; --------------------
8296
; Acceptable characters at this point are carriage return and ':'.
8297
; If so go to next statement which in the first case will be on next line.
8298
 
8299
;; STMT-NEXT
678 savelij 8300
L1BF4           CALL L3B5D              ; Spectrum 128 patch
384 savelij 8301
L1BF7:  JR      Z,L1BB3         ; back to LINE-END if so.
8302
 
8303
        CP      $3A             ; is it ':' ?
8304
        JP      Z,L1B28         ; jump back to STMT-LOOP to consider
8305
                                ; further statements
8306
 
8307
        JP      L1C8A           ; jump to REPORT-C with any other character
8308
                                ; 'Nonsense in BASIC'.
8309
 
8310
; Note. the two-byte sequence 'rst 08; DB $0b' could replace the above jp.
8311
 
8312
; -------------------
8313
; Command class table
8314
; -------------------
8315
;
8316
 
8317
;; class-tbl
8318
L1C01:  DB    L1C10 - $       ; 0F offset to Address: CLASS-00
8319
        DB    L1C1F - $       ; 1D offset to Address: CLASS-01
8320
        DB    L1C4E - $       ; 4B offset to Address: CLASS-02
8321
        DB    L1C0D - $       ; 09 offset to Address: CLASS-03
8322
        DB    L1C6C - $       ; 67 offset to Address: CLASS-04
8323
        DB    L1C11 - $       ; 0B offset to Address: CLASS-05
8324
        DB    L1C82 - $       ; 7B offset to Address: CLASS-06
8325
        DB    L1C96 - $       ; 8E offset to Address: CLASS-07
8326
        DB    L1C7A - $       ; 71 offset to Address: CLASS-08
8327
        DB    L1CBE - $       ; B4 offset to Address: CLASS-09
8328
        DB    L1C8C - $       ; 81 offset to Address: CLASS-0A
8329
        DB    L1CDB - $       ; CF offset to Address: CLASS-0B
8330
 
8331
 
8332
; --------------------------------
8333
; Command classes---00, 03, and 05
8334
; --------------------------------
8335
; class-03 e.g RUN or RUN 200   ;  optional operand
8336
; class-00 e.g CONTINUE         ;  no operand
8337
; class-05 e.g PRINT            ;  variable syntax checked by routine
8338
 
8339
;; CLASS-03
8340
L1C0D:  CALL    L1CDE           ; routine FETCH-NUM
8341
 
8342
;; CLASS-00
8343
 
8344
L1C10:  CP      A               ; reset zero flag.
8345
 
8346
; if entering here then all class routines are entered with zero reset.
8347
 
8348
;; CLASS-05
8349
L1C11:  POP     BC              ; drop address SCAN-LOOP.
8350
        CALL    Z,L1BEE         ; if zero set then call routine CHECK-END >>>
8351
                                ; as should be no further characters.
8352
 
8353
        EX      DE,HL           ; save HL to DE.
8354
        LD      HL,($5C74)      ; fetch T_ADDR
8355
        LD      C,(HL)          ; fetch low byte of routine
8356
        INC     HL              ; address next.
8357
        LD      B,(HL)          ; fetch high byte of routine.
8358
        EX      DE,HL           ; restore HL from DE
8359
        PUSH    BC              ; push the address
8360
        RET                     ; and make an indirect jump to the command.
8361
 
8362
; --------------------------------
8363
; Command classes---01, 02, and 04
8364
; --------------------------------
8365
; class-01  e.g LET A = 2*3     ; a variable is reqd
8366
 
8367
; This class routine is also called from INPUT and READ to find the
8368
; destination variable for an assignment.
8369
 
8370
;; CLASS-01
8371
L1C1F:  CALL    L28B2           ; routine LOOK-VARS returns carry set if not
8372
                                ; found in runtime.
8373
 
8374
; ----------------------
8375
; Variable in assignment
8376
; ----------------------
8377
;
8378
;
8379
 
8380
;; VAR-A-1
8381
L1C22:  LD      (IY+$37),$00    ; set FLAGX to zero
8382
        JR      NC,L1C30        ; forward to VAR-A-2 if found or checking
8383
                                ; syntax.
8384
 
8385
        SET     1,(IY+$37)      ; FLAGX  - Signal a new variable
8386
        JR      NZ,L1C46        ; to VAR-A-3 if not assigning to an array
8387
                                ; e.g. LET a$(3,3) = "X"
8388
 
8389
;; REPORT-2
8390
L1C2E:  RST     08H             ; ERROR-1
8391
        DB    $01             ; Error Report: Variable not found
8392
 
8393
;; VAR-A-2
8394
L1C30:  CALL    Z,L2996         ; routine STK-VAR considers a subscript/slice
8395
        BIT     6,(IY+$01)      ; test FLAGS  - Numeric or string result ?
8396
        JR      NZ,L1C46        ; to VAR-A-3 if numeric
8397
 
8398
        XOR     A               ; default to array/slice - to be retained.
8399
        CALL    L2530           ; routine SYNTAX-Z
8400
        CALL    NZ,L2BF1        ; routine STK-FETCH is called in runtime
8401
                                ; may overwrite A with 1.
8402
        LD      HL,$5C71        ; address system variable FLAGX
8403
        OR      (HL)            ; set bit 0 if simple variable to be reclaimed
8404
        LD      (HL),A          ; update FLAGX
8405
        EX      DE,HL           ; start of string/subscript to DE
8406
 
8407
;; VAR-A-3
8408
L1C46:  LD      ($5C72),BC      ; update STRLEN
8409
        LD      ($5C4D),HL      ; and DEST of assigned string.
8410
        RET                     ; return.
8411
 
8412
; -------------------------------------------------
8413
; class-02 e.g. LET a = 1 + 1   ; an expression must follow
8414
 
8415
;; CLASS-02
8416
L1C4E:  POP     BC              ; drop return address SCAN-LOOP
8417
        CALL    L1C56           ; routine VAL-FET-1 is called to check
8418
                                ; expression and assign result in runtime
8419
        CALL    L1BEE           ; routine CHECK-END checks nothing else
8420
                                ; is present in statement.
8421
        RET                     ; Return
8422
 
8423
; -------------
8424
; Fetch a value
8425
; -------------
8426
;
8427
;
8428
 
8429
;; VAL-FET-1
8430
L1C56:  LD      A,($5C3B)       ; initial FLAGS to A
8431
 
8432
;; VAL-FET-2
8433
L1C59:  PUSH    AF              ; save A briefly
8434
        CALL    L24FB           ; routine SCANNING evaluates expression.
8435
        POP     AF              ; restore A
8436
        LD      D,(IY+$01)      ; post-SCANNING FLAGS to D
8437
        XOR     D               ; xor the two sets of flags
8438
        AND     $40             ; pick up bit 6 of xored FLAGS should be zero
8439
        JR      NZ,L1C8A        ; forward to REPORT-C if not zero
8440
                                ; 'Nonsense in BASIC' - results don't agree.
8441
 
8442
        BIT     7,D             ; test FLAGS - is syntax being checked ?
8443
        JP      NZ,L2AFF        ; jump forward to LET to make the assignment
8444
                                ; in runtime.
8445
 
8446
        RET                     ; but return from here if checking syntax.
8447
 
8448
; ------------------
8449
; Command class---04
8450
; ------------------
8451
; class-04 e.g. FOR i            ; a single character variable must follow
8452
 
8453
;; CLASS-04
8454
L1C6C:  CALL    L28B2           ; routine LOOK-VARS
8455
        PUSH    AF              ; preserve flags.
8456
        LD      A,C             ; fetch type - should be 011xxxxx
8457
        OR      $9F             ; combine with 10011111.
8458
        INC     A               ; test if now $FF by incrementing.
8459
        JR      NZ,L1C8A        ; forward to REPORT-C if result not zero.
8460
 
8461
        POP     AF              ; else restore flags.
8462
        JR      L1C22           ; back to VAR-A-1
8463
 
8464
 
8465
; --------------------------------
8466
; Expect numeric/string expression
8467
; --------------------------------
8468
; This routine is used to get the two coordinates of STRING$, ATTR and POINT.
8469
; It is also called from PRINT-ITEM to get the two numeric expressions that
8470
; follow the AT ( in PRINT AT, INPUT AT).
8471
 
8472
;; NEXT-2NUM
8473
L1C79:  RST     20H             ; NEXT-CHAR advance past 'AT' or '('.
8474
 
8475
; --------
8476
; class-08 e.g POKE 65535,2     ; two numeric expressions separated by comma
8477
;; CLASS-08
8478
;; EXPT-2NUM
8479
L1C7A:  CALL    L1C82           ; routine EXPT-1NUM is called for first
8480
                                ; numeric expression
8481
        CP      $2C             ; is character ',' ?
8482
        JR      NZ,L1C8A        ; to REPORT-C if not required separator.
8483
                                ; 'Nonsense in BASIC'.
8484
 
8485
        RST     20H             ; NEXT-CHAR
8486
 
8487
; ->
8488
;  class-06  e.g. GOTO a*1000   ; a numeric expression must follow
8489
;; CLASS-06
8490
;; EXPT-1NUM
8491
L1C82:  CALL    L24FB           ; routine SCANNING
8492
        BIT     6,(IY+$01)      ; test FLAGS  - Numeric or string result ?
8493
        RET     NZ              ; return if result is numeric.
8494
 
8495
;; REPORT-C
8496
L1C8A:  RST     08H             ; ERROR-1
8497
        DB    $0B             ; Error Report: Nonsense in BASIC
8498
 
8499
; ---------------------------------------------------------------
8500
; class-0A e.g. ERASE "????"    ; a string expression must follow.
8501
;                               ; these only occur in unimplemented commands
8502
;                               ; although the routine expt-exp is called
8503
;                               ; from SAVE-ETC
8504
 
8505
;; CLASS-0A
8506
;; EXPT-EXP
8507
L1C8C:  CALL    L24FB           ; routine SCANNING
8508
        BIT     6,(IY+$01)      ; test FLAGS  - Numeric or string result ?
8509
        RET     Z               ; return if string result.
8510
 
8511
        JR      L1C8A           ; back to REPORT-C if numeric.
8512
 
8513
; ---------------------
8514
; Set permanent colours
8515
; class 07
8516
; ---------------------
8517
; class-07 e.g PAPER 6          ; a single class for a collection of
8518
;                               ; similar commands. Clever.
8519
;
8520
; Note. these commands should ensure that current channel is 'S'
8521
 
8522
;; CLASS-07
8523
L1C96:  BIT     7,(IY+$01)      ; test FLAGS - checking syntax only ?
8524
        RES     0,(IY+$02)      ; update TV_FLAG - signal main screen in use
8525
        CALL    NZ,L0D4D        ; routine TEMPS is called in runtime.
8526
        POP     AF              ; drop return address SCAN-LOOP
8527
        LD      A,($5C74)       ; T_ADDR_lo to accumulator.
8528
                                ; points to '$07' entry + 1
8529
                                ; e.g. for INK points to $EC now
8530
 
8531
; Note if you move alter the syntax table next line may have to be altered.
8532
 
8533
; Note. For ZASM assembler replace following expression with SUB $13.
8534
 
8535
L1CA5           SUB LOW (L1AEB)-$D8     ; % 256 ; convert $EB to $D8 ('INK') etc.
8536
                                ; ( is SUB $13 in standard ROM )
8537
 
8538
        CALL    L21FC           ; routine CO-TEMP-4
8539
        CALL    L1BEE           ; routine CHECK-END check that nothing else
8540
                                ; in statement.
8541
 
8542
; return here in runtime.
8543
 
8544
        LD      HL,($5C8F)      ; pick up ATTR_T and MASK_T
8545
        LD      ($5C8D),HL      ; and store in ATTR_P and MASK_P
8546
        LD      HL,$5C91        ; point to P_FLAG.
8547
        LD      A,(HL)          ; pick up in A
8548
        RLCA                    ; rotate to left
8549
        XOR     (HL)            ; combine with HL
8550
        AND     $AA             ; 10101010
8551
        XOR     (HL)            ; only permanent bits affected
8552
        LD      (HL),A          ; reload into P_FLAG.
8553
        RET                     ; return.
8554
 
8555
; ------------------
8556
; Command class---09
8557
; ------------------
8558
; e.g. PLOT PAPER 0; 128,88     ; two coordinates preceded by optional
8559
;                               ; embedded colour items.
8560
;
8561
; Note. this command should ensure that current channel is actually 'S'.
8562
 
8563
;; CLASS-09
8564
L1CBE:  CALL    L2530           ; routine SYNTAX-Z
8565
        JR      Z,L1CD6         ; forward to CL-09-1 if checking syntax.
8566
 
8567
        RES     0,(IY+$02)      ; update TV_FLAG - signal main screen in use
8568
        CALL    L0D4D           ; routine TEMPS is called.
8569
        LD      HL,$5C90        ; point to MASK_T
8570
        LD      A,(HL)          ; fetch mask to accumulator.
8571
        OR      $F8             ; or with 11111000 paper/bright/flash 8
8572
        LD      (HL),A          ; mask back to MASK_T system variable.
8573
        RES     6,(IY+$57)      ; reset P_FLAG  - signal NOT PAPER 9 ?
8574
 
8575
        RST     18H             ; GET-CHAR
8576
 
8577
;; CL-09-1
8578
L1CD6:  CALL    L21E2           ; routine CO-TEMP-2 deals with any embedded
8579
                                ; colour items.
8580
        JR      L1C7A           ; exit via EXPT-2NUM to check for x,y.
8581
 
8582
; Note. if either of the numeric expressions contain STR$ then the flag setting 
8583
; above will be undone when the channel flags are reset during STR$.
8584
; e.g. 
8585
; 10 BORDER 3 : PLOT VAL STR$ 128, VAL STR$ 100
8586
; credit John Elliott.
8587
 
8588
; ------------------
8589
; Command class---0B
8590
; ------------------
8591
; Again a single class for four commands.
8592
; This command just jumps back to SAVE-ETC to handle the four tape commands.
8593
; The routine itself works out which command has called it by examining the
8594
; address in T_ADDR_lo. Note therefore that the syntax table has to be
8595
; located where these and other sequential command addresses are not split
8596
; over a page boundary.
8597
 
8598
;; CLASS-0B
8599
L1CDB:  JP      L0605           ; jump way back to SAVE-ETC
8600
 
8601
; --------------
8602
; Fetch a number
8603
; --------------
8604
; This routine is called from CLASS-03 when a command may be followed by
8605
; an optional numeric expression e.g. RUN. If the end of statement has
8606
; been reached then zero is used as the default.
8607
; Also called from LIST-4.
8608
 
8609
;; FETCH-NUM
8610
L1CDE:  CP      $0D             ; is character a carriage return ?
8611
        JR      Z,L1CE6         ; forward to USE-ZERO if so
8612
 
8613
        CP      $3A             ; is it ':' ?
8614
        JR      NZ,L1C82        ; forward to EXPT-1NUM if not.
8615
                                ; else continue and use zero.
8616
 
8617
; ----------------
8618
; Use zero routine
8619
; ----------------
8620
; This routine is called four times to place the value zero on the
8621
; calculator stack as a default value in runtime.
8622
 
8623
;; USE-ZERO
8624
L1CE6:  CALL    L2530           ; routine SYNTAX-Z  (UNSTACK-Z?)
8625
        RET     Z               ;
8626
 
8627
        RST     28H             ;; FP-CALC
8628
        DB    $A0             ;;stk-zero       ;0.
8629
        DB    $38             ;;end-calc
8630
 
8631
        RET                     ; return.
8632
 
8633
; -------------------
8634
; Handle STOP command
8635
; -------------------
8636
; Command Syntax: STOP
8637
; One of the shortest and least used commands. As with 'OK' not an error.
8638
 
8639
;; REPORT-9
8640
;; STOP
8641
L1CEE:  RST     08H             ; ERROR-1
8642
        DB    $08             ; Error Report: STOP statement
8643
 
8644
; -----------------
8645
; Handle IF command
8646
; -----------------
8647
; e.g. IF score>100 THEN PRINT "You Win"
8648
; The parser has already checked the expression the result of which is on
8649
; the calculator stack. The presence of the 'THEN' separator has also been
8650
; checked and CH-ADD points to the command after THEN.
8651
;
8652
 
8653
;; IF
8654
L1CF0:  POP     BC              ; drop return address - STMT-RET
8655
        CALL    L2530           ; routine SYNTAX-Z
8656
        JR      Z,L1D00         ; forward to IF-1 if checking syntax
8657
                                ; to check syntax of PRINT "You Win"
8658
 
8659
 
8660
        RST     28H             ;; FP-CALC    score>100 (1=TRUE 0=FALSE)
8661
        DB    $02             ;;delete      .
8662
        DB    $38             ;;end-calc
8663
 
8664
        EX      DE,HL           ; make HL point to deleted value
8665
        CALL    L34E9           ; routine TEST-ZERO
8666
        JP      C,L1BB3         ; jump to LINE-END if FALSE (0)
8667
 
8668
;; IF-1
8669
L1D00:  JP      L1B29           ; to STMT-L-1, if true (1) to execute command
8670
                                ; after 'THEN' token.
8671
 
8672
; ------------------
8673
; Handle FOR command
8674
; ------------------
8675
; e.g. FOR i = 0 TO 1 STEP 0.1
8676
; Using the syntax tables, the parser has already checked for a start and
8677
; limit value and also for the intervening separator.
8678
; the two values v,l are on the calculator stack.
8679
; CLASS-04 has also checked the variable and the name is in STRLEN_lo.
8680
; The routine begins by checking for an optional STEP.
8681
 
8682
;; FOR
8683
L1D03:  CP      $CD             ; is there a 'STEP' ?
8684
        JR      NZ,L1D10        ; to F-USE-1 if not to use 1 as default.
8685
 
8686
        RST     20H             ; NEXT-CHAR
8687
        CALL    L1C82           ; routine EXPT-1NUM
8688
        CALL    L1BEE           ; routine CHECK-END
8689
        JR      L1D16           ; to F-REORDER
8690
 
8691
; ---
8692
 
8693
;; F-USE-1
8694
L1D10:  CALL    L1BEE           ; routine CHECK-END
8695
 
8696
        RST     28H             ;; FP-CALC      v,l.
8697
        DB    $A1             ;;stk-one       v,l,1=s.
8698
        DB    $38             ;;end-calc
8699
 
8700
 
8701
;; F-REORDER
8702
L1D16:  RST     28H             ;; FP-CALC       v,l,s.
8703
        DB    $C0             ;;st-mem-0       v,l,s.
8704
        DB    $02             ;;delete         v,l.
8705
        DB    $01             ;;exchange       l,v.
8706
        DB    $E0             ;;get-mem-0      l,v,s.
8707
        DB    $01             ;;exchange       l,s,v.
8708
        DB    $38             ;;end-calc
8709
 
8710
        CALL    L2AFF           ; routine LET assigns the initial value v to
8711
                                ; the variable altering type if necessary.
8712
        LD      ($5C68),HL      ; The system variable MEM is made to point to
8713
                                ; the variable instead of its normal
8714
                                ; location MEMBOT
8715
        DEC     HL              ; point to single-character name
8716
        LD      A,(HL)          ; fetch name
8717
        SET     7,(HL)          ; set bit 7 at location
8718
        LD      BC,$0006        ; add six to HL
8719
        ADD     HL,BC           ; to address where limit should be.
8720
        RLCA                    ; test bit 7 of original name.
8721
        JR      C,L1D34         ; forward to F-L-S if already a FOR/NEXT
8722
                                ; variable
8723
 
8724
        LD      C,$0D           ; otherwise an additional 13 bytes are needed.
8725
                                ; 5 for each value, two for line number and
8726
                                ; 1 byte for looping statement.
8727
        CALL    L1655           ; routine MAKE-ROOM creates them.
8728
        INC     HL              ; make HL address limit.
8729
 
8730
;; F-L-S
8731
L1D34:  PUSH    HL              ; save position.
8732
 
8733
        RST     28H             ;; FP-CALC         l,s.
8734
        DB    $02             ;;delete           l.
8735
        DB    $02             ;;delete           .
8736
        DB    $38             ;;end-calc
8737
                                ; DE points to STKEND, l.
8738
 
8739
        POP     HL              ; restore variable position
8740
        EX      DE,HL           ; swap pointers
8741
        LD      C,$0A           ; ten bytes to move
8742
        LDIR                    ; Copy 'deleted' values to variable.
8743
        LD      HL,($5C45)      ; Load with current line number from PPC
8744
        EX      DE,HL           ; exchange pointers.
8745
        LD      (HL),E          ; save the looping line
8746
        INC     HL              ; in the next
8747
        LD      (HL),D          ; two locations.
8748
        LD      D,(IY+$0D)      ; fetch statement from SUBPPC system variable.
8749
        INC     D               ; increment statement.
8750
        INC     HL              ; and pointer
8751
        LD      (HL),D          ; and store the looping statement.
8752
                                ;
8753
        CALL    L1DDA           ; routine NEXT-LOOP considers an initial
8754
        RET     NC              ; iteration. Return to STMT-RET if a loop is
8755
                                ; possible to execute next statement.
8756
 
8757
; no loop is possible so execution continues after the matching 'NEXT'
8758
 
8759
        LD      B,(IY+$38)      ; get single-character name from STRLEN_lo
8760
        LD      HL,($5C45)      ; get the current line from PPC
8761
        LD      ($5C42),HL      ; and store it in NEWPPC
8762
        LD      A,($5C47)       ; fetch current statement from SUBPPC
8763
        NEG                     ; Negate as counter decrements from zero
8764
                                ; initially and we are in the middle of a
8765
                                ; line.
8766
        LD      D,A             ; Store result in D.
8767
        LD      HL,($5C5D)      ; get current address from CH_ADD
8768
        LD      E,$F3           ; search will be for token 'NEXT'
8769
 
8770
;; F-LOOP
8771
L1D64:  PUSH    BC              ; save variable name.
8772
        LD      BC,($5C55)      ; fetch NXTLIN
8773
        CALL    L1D86           ; routine LOOK-PROG searches for 'NEXT' token.
8774
        LD      ($5C55),BC      ; update NXTLIN
8775
        POP     BC              ; and fetch the letter
8776
        JR      C,L1D84         ; forward to REPORT-I if the end of program
8777
                                ; was reached by LOOK-PROG.
8778
                                ; 'FOR without NEXT'
8779
 
8780
        RST     20H             ; NEXT-CHAR fetches character after NEXT
8781
        OR      $20             ; ensure it is upper-case.
8782
        CP      B               ; compare with FOR variable name
8783
        JR      Z,L1D7C         ; forward to F-FOUND if it matches.
8784
 
8785
; but if no match i.e. nested FOR/NEXT loops then continue search.
8786
 
8787
        RST     20H             ; NEXT-CHAR
8788
        JR      L1D64           ; back to F-LOOP
8789
 
8790
; ---
8791
 
8792
 
8793
;; F-FOUND
8794
L1D7C:  RST     20H             ; NEXT-CHAR
8795
        LD      A,$01           ; subtract the negated counter from 1
8796
        SUB     D               ; to give the statement after the NEXT
8797
        LD      ($5C44),A       ; set system variable NSPPC
8798
        RET                     ; return to STMT-RET to branch to new
8799
                                ; line and statement. ->
8800
; ---
8801
 
8802
;; REPORT-I
8803
L1D84:  RST     08H             ; ERROR-1
8804
        DB    $11             ; Error Report: FOR without NEXT
8805
 
8806
; ---------
8807
; LOOK-PROG
8808
; ---------
8809
; Find DATA, DEF FN or NEXT.
8810
; This routine searches the program area for one of the above three keywords.
8811
; On entry, HL points to start of search area.
8812
; The token is in E, and D holds a statement count, decremented from zero.
8813
 
8814
;; LOOK-PROG
8815
L1D86:  LD      A,(HL)          ; fetch current character
8816
        CP      $3A             ; is it ':' a statement separator ?
8817
        JR      Z,L1DA3         ; forward to LOOK-P-2 if so.
8818
 
8819
; The starting point was PROG - 1 or the end of a line.
8820
 
8821
;; LOOK-P-1
8822
L1D8B:  INC     HL              ; increment pointer to address
8823
        LD      A,(HL)          ; the high byte of line number
8824
        AND     $C0             ; test for program end marker $80 or a
8825
                                ; variable
8826
        SCF                     ; Set Carry Flag
8827
        RET     NZ              ; return with carry set if at end
8828
                                ; of program.           ->
8829
 
8830
        LD      B,(HL)          ; high byte of line number to B
8831
        INC     HL              ;
8832
        LD      C,(HL)          ; low byte to C.
8833
        LD      ($5C42),BC      ; set system variable NEWPPC.
8834
        INC     HL              ;
8835
        LD      C,(HL)          ; low byte of line length to C.
8836
        INC     HL              ;
8837
        LD      B,(HL)          ; high byte to B.
8838
        PUSH    HL              ; save address
8839
        ADD     HL,BC           ; add length to position.
8840
        LD      B,H             ; and save result
8841
        LD      C,L             ; in BC.
8842
        POP     HL              ; restore address.
8843
        LD      D,$00           ; initialize statement counter to zero.
8844
 
8845
;; LOOK-P-2
8846
L1DA3:  PUSH    BC              ; save address of next line
8847
        CALL    L198B           ; routine EACH-STMT searches current line.
8848
        POP     BC              ; restore address.
8849
        RET     NC              ; return if match was found. ->
8850
 
8851
        JR      L1D8B           ; back to LOOK-P-1 for next line.
8852
 
8853
; -------------------
8854
; Handle NEXT command
8855
; -------------------
8856
; e.g. NEXT i
8857
; The parameter tables have already evaluated the presence of a variable
8858
 
8859
;; NEXT
8860
L1DAB:  BIT     1,(IY+$37)      ; test FLAGX - handling a new variable ?
8861
        JP      NZ,L1C2E        ; jump back to REPORT-2 if so
8862
                                ; 'Variable not found'
8863
 
8864
; now test if found variable is a simple variable uninitialized by a FOR.
8865
 
8866
        LD      HL,($5C4D)      ; load address of variable from DEST
8867
        BIT     7,(HL)          ; is it correct type ?
8868
        JR      Z,L1DD8         ; forward to REPORT-1 if not
8869
                                ; 'NEXT without FOR'
8870
 
8871
        INC     HL              ; step past variable name
8872
        LD      ($5C68),HL      ; and set MEM to point to three 5-byte values
8873
                                ; value, limit, step.
8874
 
8875
        RST     28H             ;; FP-CALC     add step and re-store
8876
        DB    $E0             ;;get-mem-0    v.
8877
        DB    $E2             ;;get-mem-2    v,s.
8878
        DB    $0F             ;;addition     v+s.
8879
        DB    $C0             ;;st-mem-0     v+s.
8880
        DB    $02             ;;delete       .
8881
        DB    $38             ;;end-calc
8882
 
8883
        CALL    L1DDA           ; routine NEXT-LOOP tests against limit.
8884
        RET     C               ; return if no more iterations possible.
8885
 
8886
        LD      HL,($5C68)      ; find start of variable contents from MEM.
8887
        LD      DE,$000F        ; add 3*5 to
8888
        ADD     HL,DE           ; address the looping line number
8889
        LD      E,(HL)          ; low byte to E
8890
        INC     HL              ;
8891
        LD      D,(HL)          ; high byte to D
8892
        INC     HL              ; address looping statement
8893
        LD      H,(HL)          ; and store in H
8894
        EX      DE,HL           ; swap registers
8895
        JP      L1E73           ; exit via GO-TO-2 to execute another loop.
8896
 
8897
; ---
8898
 
8899
;; REPORT-1
8900
L1DD8:  RST     08H             ; ERROR-1
8901
        DB    $00             ; Error Report: NEXT without FOR
8902
 
8903
 
8904
; -----------------
8905
; Perform NEXT loop
8906
; -----------------
8907
; This routine is called from the FOR command to test for an initial
8908
; iteration and from the NEXT command to test for all subsequent iterations.
8909
; the system variable MEM addresses the variable's contents which, in the
8910
; latter case, have had the step, possibly negative, added to the value.
8911
 
8912
;; NEXT-LOOP
8913
L1DDA:  RST     28H             ;; FP-CALC
8914
        DB    $E1             ;;get-mem-1        l.
8915
        DB    $E0             ;;get-mem-0        l,v.
8916
        DB    $E2             ;;get-mem-2        l,v,s.
8917
        DB    $36             ;;less-0           l,v,(1/0) negative step ?
8918
        DB    $00             ;;jump-true        l,v.(1/0)
8919
 
8920
        DB    $02             ;;to L1DE2, NEXT-1 if step negative
8921
 
8922
        DB    $01             ;;exchange         v,l.
8923
 
8924
;; NEXT-1
8925
L1DE2:  DB    $03             ;;subtract         l-v OR v-l.
8926
        DB    $37             ;;greater-0        (1/0)
8927
        DB    $00             ;;jump-true        .
8928
 
8929
        DB    $04             ;;to L1DE9, NEXT-2 if no more iterations.
8930
 
8931
        DB    $38             ;;end-calc         .
8932
 
8933
        AND     A               ; clear carry flag signalling another loop.
8934
        RET                     ; return
8935
 
8936
; ---
8937
 
8938
;; NEXT-2
8939
L1DE9:  DB    $38             ;;end-calc         .
8940
 
8941
        SCF                     ; set carry flag signalling looping exhausted.
8942
        RET                     ; return
8943
 
8944
 
8945
; -------------------
8946
; Handle READ command
8947
; -------------------
8948
; e.g. READ a, b$, c$(1000 TO 3000)
8949
; A list of comma-separated variables is assigned from a list of
8950
; comma-separated expressions.
8951
; As it moves along the first list, the character address CH_ADD is stored
8952
; in X_PTR while CH_ADD is used to read the second list.
8953
 
8954
;; READ-3
8955
L1DEC:  RST     20H             ; NEXT-CHAR
8956
 
8957
; -> Entry point.
8958
;; READ
8959
L1DED:  CALL    L1C1F           ; routine CLASS-01 checks variable.
8960
        CALL    L2530           ; routine SYNTAX-Z
8961
        JR      Z,L1E1E         ; forward to READ-2 if checking syntax
8962
 
8963
 
8964
        RST     18H             ; GET-CHAR
8965
        LD      ($5C5F),HL      ; save character position in X_PTR.
8966
        LD      HL,($5C57)      ; load HL with Data Address DATADD, which is
8967
                                ; the start of the program or the address
8968
                                ; after the last expression that was read or
8969
                                ; the address of the line number of the 
8970
                                ; last RESTORE command.
8971
        LD      A,(HL)          ; fetch character
8972
        CP      $2C             ; is it a comma ?
8973
        JR      Z,L1E0A         ; forward to READ-1 if so.
8974
 
8975
; else all data in this statement has been read so look for next DATA token
8976
 
8977
        LD      E,$E4           ; token 'DATA'
8978
        CALL    L1D86           ; routine LOOK-PROG
8979
        JR      NC,L1E0A        ; forward to READ-1 if DATA found
8980
 
8981
; else report the error.
8982
 
8983
;; REPORT-E
8984
L1E08:  RST     08H             ; ERROR-1
8985
        DB    $0D             ; Error Report: Out of DATA
8986
 
8987
;; READ-1
8988
L1E0A:  CALL    L0077           ; routine TEMP-PTR1 advances updating CH_ADD
8989
                                ; with new DATADD position.
8990
        CALL    L1C56           ; routine VAL-FET-1 assigns value to variable
8991
                                ; checking type match and adjusting CH_ADD.
8992
 
8993
        RST     18H             ; GET-CHAR fetches adjusted character position
8994
        LD      ($5C57),HL      ; store back in DATADD
8995
        LD      HL,($5C5F)      ; fetch X_PTR  the original READ CH_ADD
8996
        LD      (IY+$26),$00    ; now nullify X_PTR_hi
8997
        CALL    L0078           ; routine TEMP-PTR2 restores READ CH_ADD
8998
 
8999
;; READ-2
9000
L1E1E:  RST     18H             ; GET-CHAR
9001
        CP      $2C             ; is it ',' indicating more variables to read ?
9002
        JR      Z,L1DEC         ; back to READ-3 if so
9003
 
9004
        CALL    L1BEE           ; routine CHECK-END
9005
        RET                     ; return from here in runtime to STMT-RET.
9006
 
9007
; -------------------
9008
; Handle DATA command
9009
; -------------------
9010
; In runtime this 'command' is passed by but the syntax is checked when such
9011
; a statement is found while parsing a line.
9012
; e.g. DATA 1, 2, "text", score-1, a$(location, room, object), FN r(49),
9013
;         wages - tax, TRUE, The meaning of life
9014
 
9015
;; DATA
9016
L1E27:  CALL    L2530           ; routine SYNTAX-Z to check status
9017
        JR      NZ,L1E37        ; forward to DATA-2 if in runtime
9018
 
9019
;; DATA-1
9020
L1E2C:  CALL    L24FB           ; routine SCANNING to check syntax of
9021
                                ; expression
9022
        CP      $2C             ; is it a comma ?
9023
        CALL    NZ,L1BEE        ; routine CHECK-END checks that statement
9024
                                ; is complete. Will make an early exit if
9025
                                ; so. >>>
9026
        RST     20H             ; NEXT-CHAR
9027
        JR      L1E2C           ; back to DATA-1
9028
 
9029
; ---
9030
 
9031
;; DATA-2
9032
L1E37:  LD      A,$E4           ; set token to 'DATA' and continue into
9033
                                ; the the PASS-BY routine.
9034
 
9035
 
9036
; ----------------------------------
9037
; Check statement for DATA or DEF FN
9038
; ----------------------------------
9039
; This routine is used to backtrack to a command token and then
9040
; forward to the next statement in runtime.
9041
 
9042
;; PASS-BY
9043
L1E39:  LD      B,A             ; Give BC enough space to find token.
9044
        CPDR                    ; Compare decrement and repeat. (Only use).
9045
                                ; Work backwards till keyword is found which
9046
                                ; is start of statement before any quotes.
9047
                                ; HL points to location before keyword.
9048
        LD      DE,$0200        ; count 1+1 statements, dummy value in E to
9049
                                ; inhibit searching for a token.
9050
        JP      L198B           ; to EACH-STMT to find next statement
9051
 
9052
; -----------------------------------------------------------------------
9053
; A General Note on Invalid Line Numbers.
9054
; =======================================
9055
; One of the revolutionary concepts of Sinclair BASIC was that it supported
9056
; virtual line numbers. That is the destination of a GO TO, RESTORE etc. need
9057
; not exist. It could be a point before or after an actual line number.
9058
; Zero suffices for a before but the after should logically be infinity.
9059
; Since the maximum actual line limit is 9999 then the system limit, 16383
9060
; when variables kick in, would serve fine as a virtual end point.
9061
; However, ironically, only the LOAD command gets it right. It will not
9062
; autostart a program that has been saved with a line higher than 16383.
9063
; All the other commands deal with the limit unsatisfactorily.
9064
; LIST, RUN, GO TO, GO SUB and RESTORE have problems and the latter may
9065
; crash the machine when supplied with an inappropriate virtual line number.
9066
; This is puzzling as very careful consideration must have been given to
9067
; this point when the new variable types were allocated their masks and also
9068
; when the routine NEXT-ONE was successfully re-written to reflect this.
9069
; An enigma.
9070
; -------------------------------------------------------------------------
9071
 
9072
; ----------------------
9073
; Handle RESTORE command
9074
; ----------------------
9075
; The restore command sets the system variable for the data address to
9076
; point to the location before the supplied line number or first line
9077
; thereafter.
9078
; This alters the position where subsequent READ commands look for data.
9079
; Note. If supplied with inappropriate high numbers the system may crash
9080
; in the LINE-ADDR routine as it will pass the program/variables end-marker
9081
; and then lose control of what it is looking for - variable or line number.
9082
; - observation, Steven Vickers, 1984, Pitman.
9083
 
9084
;; RESTORE
9085
L1E42:  CALL    L1E99           ; routine FIND-INT2 puts integer in BC.
9086
                                ; Note. B should be checked against limit $3F
9087
                                ; and an error generated if higher.
9088
 
9089
; this entry point is used from RUN command with BC holding zero
9090
 
9091
;; REST-RUN
9092
L1E45:  LD      H,B             ; transfer the line
9093
        LD      L,C             ; number to the HL register.
9094
        CALL    L196E           ; routine LINE-ADDR to fetch the address.
9095
        DEC     HL              ; point to the location before the line.
9096
        LD      ($5C57),HL      ; update system variable DATADD.
9097
        RET                     ; return to STMT-RET (or RUN)
9098
 
9099
; ------------------------
9100
; Handle RANDOMIZE command
9101
; ------------------------
9102
; This command sets the SEED for the RND function to a fixed value.
9103
; With the parameter zero, a random start point is used depending on
9104
; how long the computer has been switched on.
9105
 
9106
;; RANDOMIZE
9107
L1E4F:  CALL    L1E99           ; routine FIND-INT2 puts parameter in BC.
9108
        LD      A,B             ; test this
9109
        OR      C               ; for zero.
9110
        JR      NZ,L1E5A        ; forward to RAND-1 if not zero.
9111
 
9112
        LD      BC,($5C78)      ; use the lower two bytes at FRAMES1.
9113
 
9114
;; RAND-1
9115
L1E5A:  LD      ($5C76),BC      ; place in SEED system variable.
9116
        RET                     ; return to STMT-RET
9117
 
9118
; -----------------------
9119
; Handle CONTINUE command
9120
; -----------------------
9121
; The CONTINUE command transfers the OLD (but incremented) values of
9122
; line number and statement to the equivalent "NEW VALUE" system variables
9123
; by using the last part of GO TO and exits indirectly to STMT-RET.
9124
 
9125
;; CONTINUE
9126
L1E5F:  LD      HL,($5C6E)      ; fetch OLDPPC line number.
9127
        LD      D,(IY+$36)      ; fetch OSPPC statement.
9128
        JR      L1E73           ; forward to GO-TO-2
9129
 
9130
; --------------------
9131
; Handle GO TO command
9132
; --------------------
9133
; The GO TO command routine is also called by GO SUB and RUN routines
9134
; to evaluate the parameters of both commands.
9135
; It updates the system variables used to fetch the next line/statement.
9136
; It is at STMT-RET that the actual change in control takes place.
9137
; Unlike some BASICs the line number need not exist.
9138
; Note. the high byte of the line number is incorrectly compared with $F0
9139
; instead of $3F. This leads to commands with operands greater than 32767
9140
; being considered as having been run from the editing area and the
9141
; error report 'Statement Lost' is given instead of 'OK'.
9142
; - Steven Vickers, 1984.
9143
 
9144
;; GO-TO
9145
L1E67:  CALL    L1E99           ; routine FIND-INT2 puts operand in BC
9146
        LD      H,B             ; transfer line
9147
        LD      L,C             ; number to HL.
9148
        LD      D,$00           ; set statement to 0 - first.
9149
        LD      A,H             ; compare high byte only
9150
        CP      $F0             ; to $F0 i.e. 61439 in full.
9151
        JR      NC,L1E9F        ; forward to REPORT-B if above.
9152
 
9153
; This call entry point is used to update the system variables e.g. by RETURN.
9154
 
9155
;; GO-TO-2
9156
L1E73:  LD      ($5C42),HL      ; save line number in NEWPPC
9157
        LD      (IY+$0A),D      ; and statement in NSPPC
9158
        RET                     ; to STMT-RET (or GO-SUB command)
9159
 
9160
; ------------------
9161
; Handle OUT command
9162
; ------------------
9163
; Syntax has been checked and the two comma-separated values are on the
9164
; calculator stack.
9165
 
9166
;; OUT
9167
L1E7A:  CALL    L1E85           ; routine TWO-PARAM fetches values
9168
                                ; to BC and A.
9169
        OUT     (C),A           ; perform the operation.
9170
        RET                     ; return to STMT-RET.
9171
 
9172
; -------------------
9173
; Handle POKE command
9174
; -------------------
9175
; This routine alters a single byte in the 64K address space.
9176
; Happily no check is made as to whether ROM or RAM is addressed.
9177
; Sinclair BASIC requires no poking of system variables.
9178
 
9179
;; POKE
9180
L1E80:  CALL    L1E85           ; routine TWO-PARAM fetches values
9181
                                ; to BC and A.
9182
        LD      (BC),A          ; load memory location with A.
9183
        RET                     ; return to STMT-RET.
9184
 
9185
; ------------------------------------
9186
; Fetch two  parameters from calculator stack
9187
; ------------------------------------
9188
; This routine fetches a byte and word from the calculator stack
9189
; producing an error if either is out of range.
9190
 
9191
;; TWO-PARAM
9192
L1E85:  CALL    L2DD5           ; routine FP-TO-A
9193
        JR      C,L1E9F         ; forward to REPORT-B if overflow occurred
9194
 
9195
        JR      Z,L1E8E         ; forward to TWO-P-1 if positive
9196
 
9197
        NEG                     ; negative numbers are made positive
9198
 
9199
;; TWO-P-1
9200
L1E8E:  PUSH    AF              ; save the value
9201
        CALL    L1E99           ; routine FIND-INT2 gets integer to BC
9202
        POP     AF              ; restore the value
9203
        RET                     ; return
9204
 
9205
; -------------
9206
; Find integers
9207
; -------------
9208
; The first of these routines fetches a 8-bit integer (range 0-255) from the
9209
; calculator stack to the accumulator and is used for colours, streams,
9210
; durations and coordinates.
9211
; The second routine fetches 16-bit integers to the BC register pair 
9212
; and is used to fetch command and function arguments involving line numbers
9213
; or memory addresses and also array subscripts and tab arguments.
9214
; ->
9215
 
9216
;; FIND-INT1
9217
L1E94:  CALL    L2DD5           ; routine FP-TO-A
9218
        JR      L1E9C           ; forward to FIND-I-1 for common exit routine.
9219
 
9220
; ---
9221
 
9222
; ->
9223
 
9224
;; FIND-INT2
9225
L1E99:  CALL    L2DA2           ; routine FP-TO-BC
9226
 
9227
;; FIND-I-1
9228
L1E9C:  JR      C,L1E9F         ; to REPORT-Bb with overflow.
9229
 
9230
        RET     Z               ; return if positive.
9231
 
9232
 
9233
;; REPORT-Bb
9234
L1E9F:  RST     08H             ; ERROR-1
9235
        DB    $0A             ; Error Report: Integer out of range
9236
 
9237
; ------------------
9238
; Handle RUN command
9239
; ------------------
9240
; This command runs a program starting at an optional line.
9241
; It performs a 'RESTORE 0' then CLEAR
9242
 
9243
;; RUN
9244
L1EA1:  CALL    L1E67           ; routine GO-TO puts line number in
9245
                                ; system variables.
9246
        LD      BC,$0000        ; prepare to set DATADD to first line.
9247
        CALL    L1E45           ; routine REST-RUN does the 'restore'.
9248
                                ; Note BC still holds zero.
9249
        JR      L1EAF           ; forward to CLEAR-RUN to clear variables
9250
                                ; without disturbing RAMTOP and
9251
                                ; exit indirectly to STMT-RET
9252
 
9253
; --------------------
9254
; Handle CLEAR command
9255
; --------------------
9256
; This command reclaims the space used by the variables.
9257
; It also clears the screen and the GO SUB stack.
9258
; With an integer expression, it sets the uppermost memory
9259
; address within the BASIC system.
9260
; "Contrary to the manual, CLEAR doesn't execute a RESTORE" -
9261
; Steven Vickers, Pitman Pocket Guide to the Spectrum, 1984.
9262
 
9263
;; CLEAR
9264
L1EAC:  CALL    L1E99           ; routine FIND-INT2 fetches to BC.
9265
 
9266
;; CLEAR-RUN
9267
L1EAF:  LD      A,B             ; test for
9268
        OR      C               ; zero.
9269
        JR      NZ,L1EB7        ; skip to CLEAR-1 if not zero.
9270
 
9271
        LD      BC,($5CB2)      ; use the existing value of RAMTOP if zero.
9272
 
9273
;; CLEAR-1
9274
L1EB7:  PUSH    BC              ; save ramtop value.
9275
 
9276
        LD      DE,($5C4B)      ; fetch VARS
9277
        LD      HL,($5C59)      ; fetch E_LINE
9278
        DEC     HL              ; adjust to point at variables end-marker.
9279
        CALL    L19E5           ; routine RECLAIM-1 reclaims the space used by
9280
                                ; the variables.
9281
        CALL    L0D6B           ; routine CLS to clear screen.
9282
        LD      HL,($5C65)      ; fetch STKEND the start of free memory.
9283
        LD      DE,$0032        ; allow for another 50 bytes.
9284
        ADD     HL,DE           ; add the overhead to HL.
9285
 
9286
        POP     DE              ; restore the ramtop value.
9287
        SBC     HL,DE           ; if HL is greater than the value then jump
9288
        JR      NC,L1EDA        ; forward to REPORT-M
9289
                                ; 'RAMTOP no good'
9290
 
9291
        LD      HL,($5CB4)      ; now P-RAMT ($7FFF on 16K RAM machine)
9292
        AND     A               ; exact this time.
9293
        SBC     HL,DE           ; new ramtop must be lower or the same.
9294
        JR      NC,L1EDC        ; skip to CLEAR-2 if in actual RAM.
9295
 
9296
;; REPORT-M
9297
L1EDA:  RST     08H             ; ERROR-1
9298
        DB    $15             ; Error Report: RAMTOP no good
9299
 
9300
;; CLEAR-2
9301
L1EDC:  EX      DE,HL           ; transfer ramtop value to HL.
9302
        LD      ($5CB2),HL      ; update system variable RAMTOP.
9303
        POP     DE              ; pop the return address STMT-RET.
9304
        POP     BC              ; pop the Error Address.
9305
        LD      (HL),$3E        ; now put the GO SUB end-marker at RAMTOP.
9306
        DEC     HL              ; leave a location beneath it.
9307
        LD      SP,HL           ; initialize the machine stack pointer.
9308
        PUSH    BC              ; push the error address.
9309
        LD      ($5C3D),SP      ; make ERR_SP point to location.
9310
        EX      DE,HL           ; put STMT-RET in HL.
9311
        JP      (HL)            ; and go there directly.
9312
 
9313
; ---------------------
9314
; Handle GO SUB command
9315
; ---------------------
9316
; The GO SUB command diverts BASIC control to a new line number
9317
; in a very similar manner to GO TO but
9318
; the current line number and current statement + 1
9319
; are placed on the GO SUB stack as a RETURN point.
9320
 
9321
;; GO-SUB
9322
L1EED:  POP     DE              ; drop the address STMT-RET
9323
        LD      H,(IY+$0D)      ; fetch statement from SUBPPC and
9324
        INC     H               ; increment it
9325
        EX      (SP),HL         ; swap - error address to HL,
9326
                                ; H (statement) at top of stack,
9327
                                ; L (unimportant) beneath.
9328
        INC     SP              ; adjust to overwrite unimportant byte
9329
        LD      BC,($5C45)      ; fetch the current line number from PPC
9330
        PUSH    BC              ; and PUSH onto GO SUB stack.
9331
                                ; the empty machine-stack can be rebuilt
9332
        PUSH    HL              ; push the error address.
9333
        LD      ($5C3D),SP      ; make system variable ERR_SP point to it.
9334
        PUSH    DE              ; push the address STMT-RET.
9335
        CALL    L1E67           ; call routine GO-TO to update the system
9336
                                ; variables NEWPPC and NSPPC.
9337
                                ; then make an indirect exit to STMT-RET via
9338
        LD      BC,$0014        ; a 20-byte overhead memory check.
9339
 
9340
; ----------------------
9341
; Check available memory
9342
; ----------------------
9343
; This routine is used on many occasions when extending a dynamic area
9344
; upwards or the GO SUB stack downwards.
9345
 
9346
;; TEST-ROOM
9347
L1F05:  LD      HL,($5C65)      ; fetch STKEND
9348
        ADD     HL,BC           ; add the supplied test value
9349
        JR      C,L1F15         ; forward to REPORT-4 if over $FFFF
9350
 
9351
        EX      DE,HL           ; was less so transfer to DE
9352
        LD      HL,$0050        ; test against another 80 bytes
9353
        ADD     HL,DE           ; anyway
9354
        JR      C,L1F15         ; forward to REPORT-4 if this passes $FFFF
9355
 
9356
        SBC     HL,SP           ; if less than the machine stack pointer
9357
        RET     C               ; then return - OK.
9358
 
9359
;; REPORT-4
9360
L1F15:  LD      L,$03           ; prepare 'Out of Memory' 
9361
        JP      L0055           ; jump back to ERROR-3 at $0055
9362
                                ; Note. this error can't be trapped at $0008
9363
 
9364
; ------------------------------
9365
; THE 'FREE MEMORY' USER ROUTINE
9366
; ------------------------------
9367
; This routine is not used by the ROM but allows users to evaluate
9368
; approximate free memory with PRINT 65536 - USR 7962.
9369
 
9370
;; free-mem
9371
L1F1A:  LD      BC,$0000        ; allow no overhead.
9372
 
9373
        CALL    L1F05           ; routine TEST-ROOM.
9374
 
9375
        LD      B,H             ; transfer the result
9376
        LD      C,L             ; to the BC register.
9377
        RET                     ; the USR function returns value of BC.
9378
 
9379
; --------------------
9380
; THE 'RETURN' COMMAND
9381
; --------------------
9382
; As with any command, there are two values on the machine stack at the time 
9383
; it is invoked.  The machine stack is below the GOSUB stack.  Both grow 
9384
; downwards, the machine stack by two bytes, the GOSUB stack by 3 bytes. 
9385
; The highest location is a statement byte followed by a two-byte line number.
9386
 
9387
;; RETURN
9388
L1F23:  POP     BC              ; drop the address STMT-RET.
9389
        POP     HL              ; now the error address.
9390
        POP     DE              ; now a possible BASIC return line.
9391
        LD      A,D             ; the high byte $00 - $27 is 
9392
        CP      $3E             ; compared with the traditional end-marker $3E.
9393
        JR      Z,L1F36         ; forward to REPORT-7 with a match.
9394
                                ; 'RETURN without GOSUB'
9395
 
9396
; It was not the end-marker so a single statement byte remains at the base of 
9397
; the calculator stack. It can't be popped off.
9398
 
9399
        DEC     SP              ; adjust stack pointer to create room for two 
9400
                                ; bytes.
9401
        EX      (SP),HL         ; statement to H, error address to base of
9402
                                ; new machine stack.
9403
        EX      DE,HL           ; statement to D,  BASIC line number to HL.
9404
        LD      ($5C3D),SP      ; adjust ERR_SP to point to new stack pointer
9405
        PUSH    BC              ; now re-stack the address STMT-RET
9406
        JP      L1E73           ; to GO-TO-2 to update statement and line
9407
                                ; system variables and exit indirectly to the
9408
                                ; address just pushed on stack.
9409
 
9410
; ---
9411
 
9412
;; REPORT-7
9413
L1F36:  PUSH    DE              ; replace the end-marker.
9414
        PUSH    HL              ; now restore the error address
9415
                                ; as will be required in a few clock cycles.
9416
 
9417
        RST     08H             ; ERROR-1
9418
        DB    $06             ; Error Report: RETURN without GOSUB
9419
 
9420
; --------------------
9421
; Handle PAUSE command
9422
; --------------------
9423
; The pause command takes as its parameter the number of interrupts
9424
; for which to wait. PAUSE 50 pauses for about a second.
9425
; PAUSE 0 pauses indefinitely.
9426
; Both forms can be finished by pressing a key.
9427
 
9428
;; PAUSE
9429
L1F3A:  CALL    L1E99           ; routine FIND-INT2 puts value in BC
9430
 
9431
;; PAUSE-1
9432
L1F3D:  HALT                    ; wait for interrupt.
9433
        DEC     BC              ; decrease counter.
9434
        LD      A,B             ; test if
9435
        OR      C               ; result is zero.
9436
        JR      Z,L1F4F         ; forward to PAUSE-END if so.
9437
 
9438
        LD      A,B             ; test if
9439
        AND     C               ; now $FFFF
9440
        INC     A               ; that is, initially zero.
9441
        JR      NZ,L1F49        ; skip forward to PAUSE-2 if not.
9442
 
9443
        INC     BC              ; restore counter to zero.
9444
 
9445
;; PAUSE-2
9446
L1F49:  BIT     5,(IY+$01)      ; test FLAGS - has a new key been pressed ?
9447
        JR      Z,L1F3D         ; back to PAUSE-1 if not.
9448
 
9449
;; PAUSE-END
9450
L1F4F:  RES     5,(IY+$01)      ; update FLAGS - signal no new key
9451
        RET                     ; and return.
9452
 
9453
; -------------------
9454
; Check for BREAK key
9455
; -------------------
9456
; This routine is called from COPY-LINE, when interrupts are disabled,
9457
; to test if BREAK (SHIFT - SPACE) is being pressed.
9458
; It is also called at STMT-RET after every statement.
9459
 
9460
;; BREAK-KEY
9461
L1F54:  LD      A,$7F           ; Input address: $7FFE
9462
        IN      A,($FE)         ; read lower right keys
9463
        RRA                     ; rotate bit 0 - SPACE
9464
        RET     C               ; return if not reset
9465
 
9466
        LD      A,$FE           ; Input address: $FEFE
9467
        IN      A,($FE)         ; read lower left keys
9468
        RRA                     ; rotate bit 0 - SHIFT
9469
        RET                     ; carry will be set if not pressed.
9470
                                ; return with no carry if both keys
9471
                                ; pressed.
9472
 
9473
; ---------------------
9474
; Handle DEF FN command
9475
; ---------------------
9476
; e.g DEF FN r$(a$,a) = a$(a TO )
9477
; this 'command' is ignored in runtime but has its syntax checked
9478
; during line-entry.
9479
 
9480
;; DEF-FN
9481
L1F60:  CALL    L2530           ; routine SYNTAX-Z
9482
        JR      Z,L1F6A         ; forward to DEF-FN-1 if parsing
9483
 
9484
        LD      A,$CE           ; else load A with 'DEF FN' and
9485
        JP      L1E39           ; jump back to PASS-BY
9486
 
9487
; ---
9488
 
9489
; continue here if checking syntax.
9490
 
9491
;; DEF-FN-1
9492
L1F6A:  SET      6,(IY+$01)     ; set FLAGS  - Assume numeric result
9493
        CALL    L2C8D           ; call routine ALPHA
9494
        JR      NC,L1F89        ; if not then to DEF-FN-4 to jump to
9495
                                ; 'Nonsense in BASIC'
9496
 
9497
 
9498
        RST     20H             ; NEXT-CHAR
9499
        CP      $24             ; is it '$' ?
9500
        JR      NZ,L1F7D        ; to DEF-FN-2 if not as numeric.
9501
 
9502
        RES     6,(IY+$01)      ; set FLAGS  - Signal string result
9503
 
9504
        RST     20H             ; get NEXT-CHAR
9505
 
9506
;; DEF-FN-2
9507
L1F7D:  CP      $28             ; is it '(' ?
9508
        JR      NZ,L1FBD        ; to DEF-FN-7 'Nonsense in BASIC'
9509
 
9510
 
9511
        RST     20H             ; NEXT-CHAR
9512
        CP      $29             ; is it ')' ?
9513
        JR      Z,L1FA6         ; to DEF-FN-6 if null argument
9514
 
9515
;; DEF-FN-3
9516
L1F86:  CALL    L2C8D           ; routine ALPHA checks that it is the expected
9517
                                ; alphabetic character.
9518
 
9519
;; DEF-FN-4
9520
L1F89:  JP      NC,L1C8A        ; to REPORT-C  if not
9521
                                ; 'Nonsense in BASIC'.
9522
 
9523
        EX      DE,HL           ; save pointer in DE
9524
 
9525
        RST     20H             ; NEXT-CHAR re-initializes HL from CH_ADD
9526
                                ; and advances.
9527
        CP      $24             ; '$' ? is it a string argument.
9528
        JR      NZ,L1F94        ; forward to DEF-FN-5 if not.
9529
 
9530
        EX      DE,HL           ; save pointer to '$' in DE
9531
 
9532
        RST     20H             ; NEXT-CHAR re-initializes HL and advances
9533
 
9534
;; DEF-FN-5
9535
L1F94:  EX      DE,HL           ; bring back pointer.
9536
        LD      BC,$0006        ; the function requires six hidden bytes for
9537
                                ; each parameter passed.
9538
                                ; The first byte will be $0E
9539
                                ; then 5-byte numeric value
9540
                                ; or 5-byte string pointer.
9541
 
9542
        CALL    L1655           ; routine MAKE-ROOM creates space in program
9543
                                ; area.
9544
 
9545
        INC     HL              ; adjust HL (set by LDDR)
9546
        INC     HL              ; to point to first location.
9547
        LD      (HL),$0E        ; insert the 'hidden' marker.
9548
 
9549
; Note. these invisible storage locations hold nothing meaningful for the
9550
; moment. They will be used every time the corresponding function is
9551
; evaluated in runtime.
9552
; Now consider the following character fetched earlier.
9553
 
9554
        CP      $2C             ; is it ',' ? (more than one parameter)
9555
        JR      NZ,L1FA6        ; to DEF-FN-6 if not
9556
 
9557
 
9558
        RST     20H             ; else NEXT-CHAR
9559
        JR      L1F86           ; and back to DEF-FN-3
9560
 
9561
; ---
9562
 
9563
;; DEF-FN-6
9564
L1FA6:  CP      $29             ; should close with a ')'
9565
        JR      NZ,L1FBD        ; to DEF-FN-7 if not
9566
                                ; 'Nonsense in BASIC'
9567
 
9568
 
9569
        RST     20H             ; get NEXT-CHAR
9570
        CP      $3D             ; is it '=' ?
9571
        JR      NZ,L1FBD        ; to DEF-FN-7 if not 'Nonsense...'
9572
 
9573
 
9574
        RST     20H             ; address NEXT-CHAR
9575
        LD      A,($5C3B)       ; get FLAGS which has been set above
9576
        PUSH    AF              ; and preserve
9577
 
9578
        CALL    L24FB           ; routine SCANNING checks syntax of expression
9579
                                ; and also sets flags.
9580
 
9581
        POP     AF              ; restore previous flags
9582
        XOR     (IY+$01)        ; xor with FLAGS - bit 6 should be same 
9583
                                ; therefore will be reset.
9584
        AND     $40             ; isolate bit 6.
9585
 
9586
;; DEF-FN-7
9587
L1FBD:  JP      NZ,L1C8A        ; jump back to REPORT-C if the expected result 
9588
                                ; is not the same type.
9589
                                ; 'Nonsense in BASIC'
9590
 
9591
        CALL    L1BEE           ; routine CHECK-END will return early if
9592
                                ; at end of statement and move onto next
9593
                                ; else produce error report. >>>
9594
 
9595
                                ; There will be no return to here.
9596
 
9597
; -------------------------------
9598
; Returning early from subroutine
9599
; -------------------------------
9600
; All routines are capable of being run in two modes - syntax checking mode
9601
; and runtime mode.  This routine is called often to allow a routine to return 
9602
; early if checking syntax.
9603
 
9604
;; UNSTACK-Z
9605
L1FC3:  CALL    L2530           ; routine SYNTAX-Z sets zero flag if syntax
9606
                                ; is being checked.
9607
 
9608
        POP     HL              ; drop the return address.
9609
        RET      Z              ; return to previous call in chain if checking
9610
                                ; syntax.
9611
 
9612
        JP      (HL)            ; jump to return address as BASIC program is
9613
                                ; actually running.
9614
 
9615
; ---------------------
9616
; Handle LPRINT command
9617
; ---------------------
9618
; A simple form of 'PRINT #3' although it can output to 16 streams.
9619
; Probably for compatibility with other BASICs particularly ZX81 BASIC.
9620
; An extra UDG might have been better.
9621
 
9622
;; LPRINT
9623
L1FC9:  LD      A,$03           ; the printer channel
9624
        JR      L1FCF           ; forward to PRINT-1
9625
 
9626
; ---------------------
9627
; Handle PRINT commands
9628
; ---------------------
9629
; The Spectrum's main stream output command.
9630
; The default stream is stream 2 which is normally the upper screen
9631
; of the computer. However the stream can be altered in range 0 - 15.
9632
 
9633
;; PRINT
9634
L1FCD:  LD      A,$02           ; the stream for the upper screen.
9635
 
9636
; The LPRINT command joins here.
9637
 
9638
;; PRINT-1
9639
L1FCF:  CALL    L2530           ; routine SYNTAX-Z checks if program running
9640
        CALL    NZ,L1601        ; routine CHAN-OPEN if so
9641
        CALL    L0D4D           ; routine TEMPS sets temporary colours.
9642
        CALL    L1FDF           ; routine PRINT-2 - the actual item
9643
        CALL    L1BEE           ; routine CHECK-END gives error if not at end
9644
                                ; of statement
9645
        RET                     ; and return >>>
9646
 
9647
; ------------------------------------
9648
; this subroutine is called from above
9649
; and also from INPUT.
9650
 
9651
;; PRINT-2
9652
L1FDF:  RST     18H             ; GET-CHAR gets printable character
9653
        CALL    L2045           ; routine PR-END-Z checks if more printing
9654
        JR      Z,L1FF2         ; to PRINT-4 if not     e.g. just 'PRINT :'
9655
 
9656
; This tight loop deals with combinations of positional controls and
9657
; print items. An early return can be made from within the loop
9658
; if the end of a print sequence is reached.
9659
 
9660
;; PRINT-3
9661
L1FE5:  CALL    L204E           ; routine PR-POSN-1 returns zero if more
9662
                                ; but returns early at this point if
9663
                                ; at end of statement!
9664
                                ; 
9665
        JR      Z,L1FE5         ; to PRINT-3 if consecutive positioners
9666
 
9667
        CALL    L1FFC           ; routine PR-ITEM-1 deals with strings etc.
9668
        CALL    L204E           ; routine PR-POSN-1 for more position codes
9669
        JR      Z,L1FE5         ; loop back to PRINT-3 if so
9670
 
9671
;; PRINT-4
9672
L1FF2:  CP      $29             ; return now if this is ')' from input-item.
9673
                                ; (see INPUT.)
9674
        RET     Z               ; or continue and print carriage return in
9675
                                ; runtime
9676
 
9677
; ---------------------
9678
; Print carriage return
9679
; ---------------------
9680
; This routine which continues from above prints a carriage return
9681
; in run-time. It is also called once from PRINT-POSN.
9682
 
9683
;; PRINT-CR
9684
L1FF5:  CALL    L1FC3           ; routine UNSTACK-Z
9685
 
9686
        LD      A,$0D           ; prepare a carriage return
9687
 
9688
        RST     10H             ; PRINT-A
9689
        RET                     ; return
9690
 
9691
 
9692
; -----------
9693
; Print items
9694
; -----------
9695
; This routine deals with print items as in
9696
; PRINT AT 10,0;"The value of A is ";a
9697
; It returns once a single item has been dealt with as it is part
9698
; of a tight loop that considers sequences of positional and print items
9699
 
9700
;; PR-ITEM-1
9701
L1FFC:  RST     18H             ; GET-CHAR
9702
        CP      $AC             ; is character 'AT' ?
9703
        JR      NZ,L200E        ; forward to PR-ITEM-2 if not.
9704
 
9705
        CALL    L1C79           ; routine NEXT-2NUM  check for two comma 
9706
                                ; separated numbers placing them on the 
9707
                                ; calculator stack in runtime. 
9708
        CALL    L1FC3           ; routine UNSTACK-Z quits if checking syntax.
9709
 
9710
        CALL    L2307           ; routine STK-TO-BC get the numbers in B and C.
9711
        LD      A,$16           ; prepare the 'at' control.
9712
        JR      L201E           ; forward to PR-AT-TAB to print the sequence.
9713
 
9714
; ---
9715
 
9716
;; PR-ITEM-2
9717
L200E:  CP      $AD             ; is character 'TAB' ?
9718
        JR      NZ,L2024        ; to PR-ITEM-3 if not
9719
 
9720
 
9721
        RST     20H             ; NEXT-CHAR to address next character
9722
        CALL    L1C82           ; routine EXPT-1NUM
9723
        CALL    L1FC3           ; routine UNSTACK-Z quits if checking syntax.
9724
 
9725
        CALL    L1E99           ; routine FIND-INT2 puts integer in BC.
9726
        LD      A,$17           ; prepare the 'tab' control.
9727
 
9728
;; PR-AT-TAB
9729
L201E:  RST     10H             ; PRINT-A outputs the control
9730
 
9731
        LD      A,C             ; first value to A
9732
        RST     10H             ; PRINT-A outputs it.
9733
 
9734
        LD      A,B             ; second value
9735
        RST     10H             ; PRINT-A
9736
 
9737
        RET                     ; return - item finished >>>
9738
 
9739
; ---
9740
 
9741
; Now consider paper 2; #2; a$
9742
 
9743
;; PR-ITEM-3
9744
L2024:  CALL    L21F2           ; routine CO-TEMP-3 will print any colour
9745
        RET     NC              ; items - return if success.
9746
 
9747
        CALL    L2070           ; routine STR-ALTER considers new stream
9748
        RET     NC              ; return if altered.
9749
 
9750
        CALL    L24FB           ; routine SCANNING now to evaluate expression
9751
        CALL    L1FC3           ; routine UNSTACK-Z if not runtime.
9752
 
9753
        BIT     6,(IY+$01)      ; test FLAGS  - Numeric or string result ?
9754
        CALL    Z,L2BF1         ; routine STK-FETCH if string.
9755
                                ; note no flags affected.
9756
        JP      NZ,L2DE3        ; to PRINT-FP to print if numeric >>>
9757
 
9758
; It was a string expression - start in DE, length in BC
9759
; Now enter a loop to print it
9760
 
9761
;; PR-STRING
9762
L203C:  LD      A,B             ; this tests if the
9763
        OR      C               ; length is zero and sets flag accordingly.
9764
        DEC     BC              ; this doesn't but decrements counter.
9765
        RET     Z               ; return if zero.
9766
 
9767
        LD      A,(DE)          ; fetch character.
9768
        INC     DE              ; address next location.
9769
 
9770
        RST     10H             ; PRINT-A.
9771
 
9772
        JR      L203C           ; loop back to PR-STRING.
9773
 
9774
; ---------------
9775
; End of printing
9776
; ---------------
9777
; This subroutine returns zero if no further printing is required
9778
; in the current statement.
9779
; The first terminator is found in  escaped input items only,
9780
; the others in print_items.
9781
 
9782
;; PR-END-Z
9783
L2045:  CP      $29             ; is character a ')' ?
9784
        RET     Z               ; return if so -        e.g. INPUT (p$); a$
9785
 
9786
;; PR-ST-END
9787
L2048:  CP      $0D             ; is it a carriage return ?
9788
        RET     Z               ; return also -         e.g. PRINT a
9789
 
9790
        CP      $3A             ; is character a ':' ?
9791
        RET                     ; return - zero flag will be set if so.
9792
                                ;                       e.g. PRINT a :
9793
 
9794
; --------------
9795
; Print position
9796
; --------------
9797
; This routine considers a single positional character ';', ',', '''
9798
 
9799
;; PR-POSN-1
9800
L204E:  RST     18H             ; GET-CHAR
9801
        CP      $3B             ; is it ';' ?             
9802
                                ; i.e. print from last position.
9803
        JR      Z,L2067         ; forward to PR-POSN-3 if so.
9804
                                ; i.e. do nothing.
9805
 
9806
        CP      $2C             ; is it ',' ?
9807
                                ; i.e. print at next tabstop.
9808
        JR      NZ,L2061        ; forward to PR-POSN-2 if anything else.
9809
 
9810
        CALL    L2530           ; routine SYNTAX-Z
9811
        JR      Z,L2067         ; forward to PR-POSN-3 if checking syntax.
9812
 
9813
        LD      A,$06           ; prepare the 'comma' control character.
9814
 
9815
        RST     10H             ; PRINT-A  outputs to current channel in
9816
                                ; run-time.
9817
 
9818
        JR      L2067           ; skip to PR-POSN-3.
9819
 
9820
; ---
9821
 
9822
; check for newline.
9823
 
9824
;; PR-POSN-2
9825
L2061:  CP      $27             ; is character a "'" ? (newline)
9826
        RET     NZ              ; return if no match              >>>
9827
 
9828
        CALL    L1FF5           ; routine PRINT-CR outputs a carriage return
9829
                                ; in runtime only.
9830
 
9831
;; PR-POSN-3
9832
L2067:  RST     20H             ; NEXT-CHAR to A.
9833
        CALL    L2045           ; routine PR-END-Z checks if at end.
9834
        JR      NZ,L206E        ; to PR-POSN-4 if not.
9835
 
9836
        POP     BC              ; drop return address if at end.
9837
 
9838
;; PR-POSN-4
9839
L206E:  CP      A               ; reset the zero flag.
9840
        RET                     ; and return to loop or quit.
9841
 
9842
; ------------
9843
; Alter stream
9844
; ------------
9845
; This routine is called from PRINT ITEMS above, and also LIST as in
9846
; LIST #15
9847
 
9848
;; STR-ALTER
9849
L2070:  CP      $23             ; is character '#' ?
9850
        SCF                     ; set carry flag.
9851
        RET     NZ              ; return if no match.
9852
 
9853
 
9854
        RST      20H            ; NEXT-CHAR
9855
        CALL    L1C82           ; routine EXPT-1NUM gets stream number
9856
        AND     A               ; prepare to exit early with carry reset
9857
        CALL    L1FC3           ; routine UNSTACK-Z exits early if parsing
9858
        CALL    L1E94           ; routine FIND-INT1 gets number off stack
9859
        CP      $10             ; must be range 0 - 15 decimal.
9860
        JP      NC,L160E        ; jump back to REPORT-Oa if not
9861
                                ; 'Invalid stream'.
9862
 
9863
        CALL    L1601           ; routine CHAN-OPEN
9864
        AND     A               ; clear carry - signal item dealt with.
9865
        RET                     ; return
9866
 
9867
; --------------------
9868
; Handle INPUT command
9869
; --------------------
9870
; This command
9871
;
9872
 
9873
;; INPUT
9874
L2089:  CALL    L2530           ; routine SYNTAX-Z to check if in runtime.
9875
        JR      Z,L2096         ; forward to INPUT-1 if checking syntax.
9876
 
9877
        LD      A,$01           ; select channel 'K' the keyboard for input.
9878
        CALL    L1601           ; routine CHAN-OPEN opens it.
9879
        CALL    L0D6E           ; routine CLS-LOWER clears the lower screen
9880
                                ; and sets DF_SZ to two.
9881
 
9882
;; INPUT-1
9883
L2096:  LD      (IY+$02),$01    ; update TV_FLAG - signal lower screen in use
9884
                                ; ensuring that the correct set of system 
9885
                                ; variables are updated and that the border 
9886
                                ; colour is used.
9887
 
9888
        CALL    L20C1           ; routine IN-ITEM-1 to handle the input.
9889
 
9890
        CALL    L1BEE           ; routine CHECK-END will make an early exit
9891
                                ; if checking syntax. >>>
9892
 
9893
; keyboard input has been made and it remains to adjust the upper
9894
; screen in case the lower two lines have been extended upwards.
9895
 
9896
        LD      BC,($5C88)      ; fetch S_POSN current line/column of
9897
                                ; the upper screen.
9898
        LD      A,($5C6B)       ; fetch DF_SZ the display file size of
9899
                                ; the lower screen.
9900
        CP      B               ; test that lower screen does not overlap
9901
        JR      C,L20AD         ; forward to INPUT-2 if not.
9902
 
9903
; the two screens overlap so adjust upper screen.
9904
 
9905
        LD      C,$21           ; set column of upper screen to leftmost.
9906
        LD      B,A             ; and line to one above lower screen.
9907
                                ; continue forward to update upper screen
9908
                                ; print position.
9909
 
9910
;; INPUT-2
9911
L20AD:  LD      ($5C88),BC      ; set S_POSN update upper screen line/column.
9912
        LD      A,$19           ; subtract from twenty five
9913
        SUB     B               ; the new line number.
9914
        LD      ($5C8C),A       ; and place result in SCR_CT - scroll count.
9915
        RES     0,(IY+$02)      ; update TV_FLAG - signal main screen in use.
9916
        CALL    L0DD9           ; routine CL-SET sets the print position
9917
                                ; system variables for the upper screen.
9918
        JP      L0D6E           ; jump back to CLS-LOWER and make
9919
                                ; an indirect exit >>.
9920
 
9921
; ---------------------
9922
; INPUT ITEM subroutine
9923
; ---------------------
9924
; This subroutine deals with the input items and print items.
9925
; from  the current input channel.
9926
; It is only called from the above INPUT routine but was obviously
9927
; once called from somewhere else in another context.
9928
 
9929
;; IN-ITEM-1
9930
L20C1:  CALL    L204E           ; routine PR-POSN-1 deals with a single
9931
                                ; position item at each call.
9932
        JR      Z,L20C1         ; back to IN-ITEM-1 until no more in a
9933
                                ; sequence.
9934
 
9935
        CP      $28             ; is character '(' ?
9936
        JR      NZ,L20D8        ; forward to IN-ITEM-2 if not.
9937
 
9938
; any variables within braces will be treated as part, or all, of the prompt
9939
; instead of being used as destination variables.
9940
 
9941
        RST     20H             ; NEXT-CHAR
9942
        CALL    L1FDF           ; routine PRINT-2 to output the dynamic
9943
                                ; prompt.
9944
 
9945
        RST     18H             ; GET-CHAR
9946
        CP      $29             ; is character a matching ')' ?
9947
        JP      NZ,L1C8A        ; jump back to REPORT-C if not.
9948
                                ; 'Nonsense in BASIC'.
9949
 
9950
        RST     20H             ; NEXT-CHAR
9951
        JP      L21B2           ; forward to IN-NEXT-2
9952
 
9953
; ---
9954
 
9955
;; IN-ITEM-2
9956
L20D8:  CP      $CA             ; is the character the token 'LINE' ?
9957
        JR      NZ,L20ED        ; forward to IN-ITEM-3 if not.
9958
 
9959
        RST     20H             ; NEXT-CHAR - variable must come next.
9960
        CALL    L1C1F           ; routine CLASS-01 returns destination
9961
                                ; address of variable to be assigned.
9962
                                ; or generates an error if no variable
9963
                                ; at this position.
9964
 
9965
        SET     7,(IY+$37)      ; update FLAGX  - signal handling INPUT LINE
9966
        BIT     6,(IY+$01)      ; test FLAGS  - numeric or string result ?
9967
        JP      NZ,L1C8A        ; jump back to REPORT-C if not string
9968
                                ; 'Nonsense in BASIC'.
9969
 
9970
        JR      L20FA           ; forward to IN-PROMPT to set up workspace.
9971
 
9972
; ---
9973
 
9974
; the jump was here for other variables.
9975
 
9976
;; IN-ITEM-3
9977
L20ED:  CALL     L2C8D          ; routine ALPHA checks if character is
9978
                                ; a suitable variable name.
9979
        JP      NC,L21AF        ; forward to IN-NEXT-1 if not
9980
 
9981
        CALL    L1C1F           ; routine CLASS-01 returns destination
9982
                                ; address of variable to be assigned.
9983
        RES     7,(IY+$37)      ; update FLAGX  - signal not INPUT LINE.
9984
 
9985
;; IN-PROMPT
9986
L20FA:  CALL    L2530           ; routine SYNTAX-Z
9987
        JP      Z,L21B2         ; forward to IN-NEXT-2 if checking syntax.
9988
 
9989
        CALL    L16BF           ; routine SET-WORK clears workspace.
9990
        LD      HL,$5C71        ; point to system variable FLAGX
9991
        RES     6,(HL)          ; signal string result.
9992
        SET     5,(HL)          ; signal in Input Mode for editor.
9993
        LD      BC,$0001        ; initialize space required to one for
9994
                                ; the carriage return.
9995
        BIT     7,(HL)          ; test FLAGX - INPUT LINE in use ?
9996
        JR      NZ,L211C        ; forward to IN-PR-2 if so as that is
9997
                                ; all the space that is required.
9998
 
9999
        LD      A,($5C3B)       ; load accumulator from FLAGS
10000
        AND     $40             ; mask to test BIT 6 of FLAGS and clear
10001
                                ; the other bits in A.
10002
                                ; numeric result expected ?
10003
        JR      NZ,L211A        ; forward to IN-PR-1 if so
10004
 
10005
        LD      C,$03           ; increase space to three bytes for the
10006
                                ; pair of surrounding quotes.
10007
 
10008
;; IN-PR-1
10009
L211A:  OR      (HL)            ; if numeric result, set bit 6 of FLAGX.
10010
        LD      (HL),A          ; and update system variable
10011
 
10012
;; IN-PR-2
10013
L211C:  RST     30H             ; BC-SPACES opens 1 or 3 bytes in workspace
10014
        LD      (HL),$0D        ; insert carriage return at last new location.
10015
        LD      A,C             ; fetch the length, one or three.
10016
        RRCA                    ; lose bit 0.
10017
        RRCA                    ; test if quotes required.
10018
        JR      NC,L2129        ; forward to IN-PR-3 if not.
10019
 
10020
        LD      A,$22           ; load the '"' character
10021
        LD      (DE),A          ; place quote in first new location at DE.
10022
        DEC     HL              ; decrease HL - from carriage return.
10023
        LD      (HL),A          ; and place a quote in second location.
10024
 
10025
;; IN-PR-3
10026
L2129:  LD      ($5C5B),HL      ; set keyboard cursor K_CUR to HL
10027
        BIT     7,(IY+$37)      ; test FLAGX  - is this INPUT LINE ??
10028
        JR      NZ,L215E        ; forward to IN-VAR-3 if so as input will
10029
                                ; be accepted without checking its syntax.
10030
 
10031
        LD      HL,($5C5D)      ; fetch CH_ADD
10032
        PUSH    HL              ; and save on stack.
10033
        LD      HL,($5C3D)      ; fetch ERR_SP
10034
        PUSH    HL              ; and save on stack
10035
 
10036
;; IN-VAR-1
10037
L213A:  LD      HL,L213A        ; address: IN-VAR-1 - this address
10038
        PUSH    HL              ; is saved on stack to handle errors.
10039
        BIT     4,(IY+$30)      ; test FLAGS2  - is K channel in use ?
10040
        JR      Z,L2148         ; forward to IN-VAR-2 if not using the
10041
                                ; keyboard for input. (??)
10042
 
10043
        LD      ($5C3D),SP      ; set ERR_SP to point to IN-VAR-1 on stack.
10044
 
10045
;; IN-VAR-2
10046
L2148:  LD      HL,($5C61)      ; set HL to WORKSP - start of workspace.
10047
        CALL    L11A7           ; routine REMOVE-FP removes floating point
10048
                                ; forms when looping in error condition.
10049
        LD      (IY+$00),$FF    ; set ERR_NR to 'OK' cancelling the error.
10050
                                ; but X_PTR causes flashing error marker
10051
                                ; to be displayed at each call to the editor.
10052
        CALL    L0F2C           ; routine EDITOR allows input to be entered
10053
                                ; or corrected if this is second time around.
10054
 
10055
; if we pass to next then there are no system errors
10056
 
10057
        RES     7,(IY+$01)      ; update FLAGS  - signal checking syntax
10058
        CALL    L21B9           ; routine IN-ASSIGN checks syntax using
10059
                                ; the VAL-FET-2 and powerful SCANNING routines.
10060
                                ; any syntax error and its back to IN-VAR-1.
10061
                                ; but with the flashing error marker showing
10062
                                ; where the error is.
10063
                                ; Note. the syntax of string input has to be
10064
                                ; checked as the user may have removed the
10065
                                ; bounding quotes or escaped them as with
10066
                                ; "hat" + "stand" for example.
10067
; proceed if syntax passed.
10068
 
10069
        JR      L2161           ; jump forward to IN-VAR-4
10070
 
10071
; ---
10072
 
10073
; the jump was to here when using INPUT LINE.
10074
 
10075
;; IN-VAR-3
10076
L215E:  CALL    L0F2C           ; routine EDITOR is called for input
10077
 
10078
; when ENTER received rejoin other route but with no syntax check.
10079
 
10080
; INPUT and INPUT LINE converge here.
10081
 
10082
;; IN-VAR-4
10083
L2161:  LD      (IY+$22),$00    ; set K_CUR_hi to a low value so that the cursor
10084
                                ; no longer appears in the input line.
10085
 
10086
        CALL    L21D6           ; routine IN-CHAN-K tests if the keyboard
10087
                                ; is being used for input.
10088
        JR      NZ,L2174        ; forward to IN-VAR-5 if using another input 
10089
                                ; channel.
10090
 
10091
; continue here if using the keyboard.
10092
 
10093
        CALL    L111D           ; routine ED-COPY overprints the edit line
10094
                                ; to the lower screen. The only visible
10095
                                ; affect is that the cursor disappears.
10096
                                ; if you're inputting more than one item in
10097
                                ; a statement then that becomes apparent.
10098
 
10099
        LD      BC,($5C82)      ; fetch line and column from ECHO_E
10100
        CALL    L0DD9           ; routine CL-SET sets S-POSNL to those
10101
                                ; values.
10102
 
10103
; if using another input channel rejoin here.
10104
 
10105
;; IN-VAR-5
10106
L2174:  LD      HL,$5C71        ; point HL to FLAGX
10107
        RES     5,(HL)          ; signal not in input mode
10108
        BIT     7,(HL)          ; is this INPUT LINE ?
10109
        RES     7,(HL)          ; cancel the bit anyway.
10110
        JR      NZ,L219B        ; forward to IN-VAR-6 if INPUT LINE.
10111
 
10112
        POP     HL              ; drop the looping address
10113
        POP     HL              ; drop the the address of previous
10114
                                ; error handler.
10115
        LD      ($5C3D),HL      ; set ERR_SP to point to it.
10116
        POP     HL              ; drop original CH_ADD which points to
10117
                                ; INPUT command in BASIC line.
10118
        LD      ($5C5F),HL      ; save in X_PTR while input is assigned.
10119
        SET     7,(IY+$01)      ; update FLAGS - Signal running program
10120
        CALL    L21B9           ; routine IN-ASSIGN is called again
10121
                                ; this time the variable will be assigned
10122
                                ; the input value without error.
10123
                                ; Note. the previous example now
10124
                                ; becomes "hatstand"
10125
 
10126
        LD      HL,($5C5F)      ; fetch stored CH_ADD value from X_PTR.
10127
        LD      (IY+$26),$00    ; set X_PTR_hi so that iy is no longer relevant.
10128
        LD      ($5C5D),HL      ; put restored value back in CH_ADD
10129
        JR      L21B2           ; forward to IN-NEXT-2 to see if anything
10130
                                ; more in the INPUT list.
10131
 
10132
; ---
10133
 
10134
; the jump was to here with INPUT LINE only
10135
 
10136
;; IN-VAR-6
10137
L219B:  LD      HL,($5C63)      ; STKBOT points to the end of the input.
10138
        LD      DE,($5C61)      ; WORKSP points to the beginning.
10139
        SCF                     ; prepare for true subtraction.
10140
        SBC     HL,DE           ; subtract to get length
10141
        LD      B,H             ; transfer it to
10142
        LD      C,L             ; the BC register pair.
10143
        CALL    L2AB2           ; routine STK-STO-$ stores parameters on
10144
                                ; the calculator stack.
10145
        CALL    L2AFF           ; routine LET assigns it to destination.
10146
        JR      L21B2           ; forward to IN-NEXT-2 as print items
10147
                                ; not allowed with INPUT LINE.
10148
                                ; Note. that "hat" + "stand" will, for
10149
                                ; example, be unchanged as also would
10150
                                ; 'PRINT "Iris was here"'.
10151
 
10152
; ---
10153
 
10154
; the jump was to here when ALPHA found more items while looking for
10155
; a variable name.
10156
 
10157
;; IN-NEXT-1
10158
L21AF:  CALL    L1FFC           ; routine PR-ITEM-1 considers further items.
10159
 
10160
;; IN-NEXT-2
10161
L21B2:  CALL    L204E           ; routine PR-POSN-1 handles a position item.
10162
        JP      Z,L20C1         ; jump back to IN-ITEM-1 if the zero flag
10163
                                ; indicates more items are present.
10164
 
10165
        RET                     ; return.
10166
 
10167
; ---------------------------
10168
; INPUT ASSIGNMENT Subroutine
10169
; ---------------------------
10170
; This subroutine is called twice from the INPUT command when normal
10171
; keyboard input is assigned. On the first occasion syntax is checked
10172
; using SCANNING. The final call with the syntax flag reset is to make
10173
; the assignment.
10174
 
10175
;; IN-ASSIGN
10176
L21B9:  LD      HL,($5C61)      ; fetch WORKSP start of input
10177
        LD      ($5C5D),HL      ; set CH_ADD to first character
10178
 
10179
        RST     18H             ; GET-CHAR ignoring leading white-space.
10180
        CP      $E2             ; is it 'STOP'
10181
        JR      Z,L21D0         ; forward to IN-STOP if so.
10182
 
10183
        LD      A,($5C71)       ; load accumulator from FLAGX
10184
        CALL    L1C59           ; routine VAL-FET-2 makes assignment
10185
                                ; or goes through the motions if checking
10186
                                ; syntax. SCANNING is used.
10187
 
10188
        RST     18H             ; GET-CHAR
10189
        CP      $0D             ; is it carriage return ?
10190
        RET     Z               ; return if so
10191
                                ; either syntax is OK
10192
                                ; or assignment has been made.
10193
 
10194
; if another character was found then raise an error.
10195
; User doesn't see report but the flashing error marker
10196
; appears in the lower screen.
10197
 
10198
;; REPORT-Cb
10199
L21CE:  RST     08H             ; ERROR-1
10200
        DB    $0B             ; Error Report: Nonsense in BASIC
10201
 
10202
;; IN-STOP
10203
L21D0:  CALL    L2530           ; routine SYNTAX-Z (UNSTACK-Z?)
10204
        RET     Z               ; return if checking syntax
10205
                                ; as user wouldn't see error report.
10206
                                ; but generate visible error report
10207
                                ; on second invocation.
10208
 
10209
;; REPORT-H
10210
L21D4:  RST     08H             ; ERROR-1
10211
        DB    $10             ; Error Report: STOP in INPUT
10212
 
10213
; ------------------
10214
; Test for channel K
10215
; ------------------
10216
; This subroutine is called once from the keyboard
10217
; INPUT command to check if the input routine in
10218
; use is the one for the keyboard.
10219
 
10220
;; IN-CHAN-K
10221
L21D6:  LD      HL,($5C51)      ; fetch address of current channel CURCHL
10222
        INC     HL              ;
10223
        INC     HL              ; advance past
10224
        INC     HL              ; input and
10225
        INC     HL              ; output streams
10226
        LD      A,(HL)          ; fetch the channel identifier.
10227
        CP      $4B             ; test for 'K'
10228
        RET                     ; return with zero set if keyboard is use.
10229
 
10230
; --------------------
10231
; Colour Item Routines
10232
; --------------------
10233
;
10234
; These routines have 3 entry points -
10235
; 1) CO-TEMP-2 to handle a series of embedded Graphic colour items.
10236
; 2) CO-TEMP-3 to handle a single embedded print colour item.
10237
; 3) CO TEMP-4 to handle a colour command such as FLASH 1
10238
;
10239
; "Due to a bug, if you bring in a peripheral channel and later use a colour
10240
;  statement, colour controls will be sent to it by mistake." - Steven Vickers
10241
;  Pitman Pocket Guide, 1984.
10242
;
10243
; To be fair, this only applies if the last channel was other than 'K', 'S'
10244
; or 'P', which are all that are supported by this ROM, but if that last
10245
; channel was a microdrive file, network channel etc. then
10246
; PAPER 6; CLS will not turn the screen yellow and
10247
; CIRCLE INK 2; 128,88,50 will not draw a red circle.
10248
;
10249
; This bug does not apply to embedded PRINT items as it is quite permissible
10250
; to mix stream altering commands and colour items.
10251
; The fix therefore would be to ensure that CLASS-07 and CLASS-09 make
10252
; channel 'S' the current channel when not checking syntax.
10253
; -----------------------------------------------------------------
10254
 
10255
;; CO-TEMP-1
10256
L21E1:  RST     20H             ; NEXT-CHAR
10257
 
10258
; -> Entry point from CLASS-09. Embedded Graphic colour items.
10259
; e.g. PLOT INK 2; PAPER 8; 128,88
10260
; Loops till all colour items output, finally addressing the coordinates.
10261
 
10262
;; CO-TEMP-2
10263
L21E2:  CALL    L21F2           ; routine CO-TEMP-3 to output colour control.
10264
        RET     C               ; return if nothing more to output. ->
10265
 
10266
 
10267
        RST     18H             ; GET-CHAR
10268
        CP      $2C             ; is it ',' separator ?
10269
        JR      Z,L21E1         ; back if so to CO-TEMP-1
10270
 
10271
        CP      $3B             ; is it ';' separator ?
10272
        JR      Z,L21E1         ; back to CO-TEMP-1 for more.
10273
 
10274
        JP      L1C8A           ; to REPORT-C (REPORT-Cb is within range)
10275
                                ; 'Nonsense in BASIC'
10276
 
10277
; -------------------
10278
; CO-TEMP-3
10279
; -------------------
10280
; -> this routine evaluates and outputs a colour control and parameter.
10281
; It is called from above and also from PR-ITEM-3 to handle a single embedded
10282
; print item e.g. PRINT PAPER 6; "Hi". In the latter case, the looping for
10283
; multiple items is within the PR-ITEM routine.
10284
; It is quite permissible to send these to any stream.
10285
 
10286
;; CO-TEMP-3
10287
L21F2:  CP      $D9             ; is it 'INK' ?
10288
        RET     C               ; return if less.
10289
 
10290
        CP      $DF             ; compare with 'OUT'
10291
        CCF                     ; Complement Carry Flag
10292
        RET     C               ; return if greater than 'OVER', $DE.
10293
 
10294
        PUSH    AF              ; save the colour token.
10295
 
10296
        RST     20H             ; address NEXT-CHAR
10297
        POP     AF              ; restore token and continue.
10298
 
10299
; -> this entry point used by CLASS-07. e.g. the command PAPER 6.
10300
 
10301
;; CO-TEMP-4
10302
L21FC:  SUB     $C9             ; reduce to control character $10 (INK)
10303
                                ; thru $15 (OVER).
10304
        PUSH    AF              ; save control.
10305
        CALL    L1C82           ; routine EXPT-1NUM stacks addressed
10306
                                ; parameter on calculator stack.
10307
        POP     AF              ; restore control.
10308
        AND     A               ; clear carry
10309
 
10310
        CALL    L1FC3           ; routine UNSTACK-Z returns if checking syntax.
10311
 
10312
        PUSH    AF              ; save again
10313
        CALL    L1E94           ; routine FIND-INT1 fetches parameter to A.
10314
        LD      D,A             ; transfer now to D
10315
        POP     AF              ; restore control.
10316
 
10317
        RST     10H             ; PRINT-A outputs the control to current
10318
                                ; channel.
10319
        LD      A,D             ; transfer parameter to A.
10320
 
10321
        RST     10H             ; PRINT-A outputs parameter.
10322
        RET                     ; return. ->
10323
 
10324
; -------------------------------------------------------------------------
10325
;
10326
;         {fl}{br}{   paper   }{  ink    }    The temporary colour attributes
10327
;          ___ ___ ___ ___ ___ ___ ___ ___    system variable.
10328
; ATTR_T  |   |   |   |   |   |   |   |   |
10329
;         |   |   |   |   |   |   |   |   |
10330
; 23695   |___|___|___|___|___|___|___|___|
10331
;           7   6   5   4   3   2   1   0
10332
;
10333
;
10334
;         {fl}{br}{   paper   }{  ink    }    The temporary mask used for
10335
;          ___ ___ ___ ___ ___ ___ ___ ___    transparent colours. Any bit
10336
; MASK_T  |   |   |   |   |   |   |   |   |   that is 1 shows that the
10337
;         |   |   |   |   |   |   |   |   |   corresponding attribute is
10338
; 23696   |___|___|___|___|___|___|___|___|   taken not from ATTR-T but from
10339
;           7   6   5   4   3   2   1   0     what is already on the screen.
10340
;
10341
;
10342
;         {paper9 }{ ink9 }{ inv1 }{ over1}   The print flags. Even bits are
10343
;          ___ ___ ___ ___ ___ ___ ___ ___    temporary flags. The odd bits
10344
; P_FLAG  |   |   |   |   |   |   |   |   |   are the permanent flags.
10345
;         | p | t | p | t | p | t | p | t |
10346
; 23697   |___|___|___|___|___|___|___|___|
10347
;           7   6   5   4   3   2   1   0
10348
;
10349
; -----------------------------------------------------------------------
10350
 
10351
; ------------------------------------
10352
;  The colour system variable handler.
10353
; ------------------------------------
10354
; This is an exit branch from PO-1-OPER, PO-2-OPER
10355
; A holds control $10 (INK) to $15 (OVER)
10356
; D holds parameter 0-9 for ink/paper 0,1 or 8 for bright/flash,
10357
; 0 or 1 for over/inverse.
10358
 
10359
;; CO-TEMP-5
10360
L2211:  SUB     $11             ; reduce range $FF-$04
10361
        ADC     A,$00           ; add in carry if INK
10362
        JR      Z,L2234         ; forward to CO-TEMP-7 with INK and PAPER.
10363
 
10364
        SUB     $02             ; reduce range $FF-$02
10365
        ADC     A,$00           ; add carry if FLASH
10366
        JR      Z,L2273         ; forward to CO-TEMP-C with FLASH and BRIGHT.
10367
 
10368
        CP      $01             ; is it 'INVERSE' ?
10369
        LD      A,D             ; fetch parameter for INVERSE/OVER
10370
        LD      B,$01           ; prepare OVER mask setting bit 0.
10371
        JR      NZ,L2228        ; forward to CO-TEMP-6 if OVER
10372
 
10373
        RLCA                    ; shift bit 0
10374
        RLCA                    ; to bit 2
10375
        LD      B,$04           ; set bit 2 of mask for inverse.
10376
 
10377
;; CO-TEMP-6
10378
L2228:  LD      C,A             ; save the A
10379
        LD      A,D             ; re-fetch parameter
10380
        CP      $02             ; is it less than 2
10381
        JR      NC,L2244        ; to REPORT-K if not 0 or 1.
10382
                                ; 'Invalid colour'.
10383
 
10384
        LD      A,C             ; restore A
10385
        LD      HL,$5C91        ; address system variable P_FLAG
10386
        JR      L226C           ; forward to exit via routine CO-CHANGE
10387
 
10388
; ---
10389
 
10390
; the branch was here with INK/PAPER and carry set for INK.
10391
 
10392
;; CO-TEMP-7
10393
L2234:  LD      A,D             ; fetch parameter
10394
        LD      B,$07           ; set ink mask 00000111
10395
        JR      C,L223E         ; forward to CO-TEMP-8 with INK
10396
 
10397
        RLCA                    ; shift bits 0-2
10398
        RLCA                    ; to
10399
        RLCA                    ; bits 3-5
10400
        LD      B,$38           ; set paper mask 00111000
10401
 
10402
; both paper and ink rejoin here
10403
 
10404
;; CO-TEMP-8
10405
L223E:  LD      C,A             ; value to C
10406
        LD      A,D             ; fetch parameter
10407
        CP      $0A             ; is it less than 10d ?
10408
        JR      C,L2246         ; forward to CO-TEMP-9 if so.
10409
 
10410
; ink 10 etc. is not allowed.
10411
 
10412
;; REPORT-K
10413
L2244:  RST     08H             ; ERROR-1
10414
        DB    $13             ; Error Report: Invalid colour
10415
 
10416
;; CO-TEMP-9
10417
L2246:  LD      HL,$5C8F        ; address system variable ATTR_T initially.
10418
        CP      $08             ; compare with 8
10419
        JR      C,L2258         ; forward to CO-TEMP-B with 0-7.
10420
 
10421
        LD      A,(HL)          ; fetch temporary attribute as no change.
10422
        JR      Z,L2257         ; forward to CO-TEMP-A with INK/PAPER 8
10423
 
10424
; it is either ink 9 or paper 9 (contrasting)
10425
 
10426
        OR      B               ; or with mask to make white
10427
        CPL                     ; make black and change other to dark
10428
        AND     $24             ; 00100100
10429
        JR      Z,L2257         ; forward to CO-TEMP-A if black and
10430
                                ; originally light.
10431
 
10432
        LD      A,B             ; else just use the mask (white)
10433
 
10434
;; CO-TEMP-A
10435
L2257:  LD      C,A             ; save A in C
10436
 
10437
;; CO-TEMP-B
10438
L2258:  LD      A,C             ; load colour to A
10439
        CALL    L226C           ; routine CO-CHANGE addressing ATTR-T
10440
 
10441
        LD      A,$07           ; put 7 in accumulator
10442
        CP      D               ; compare with parameter
10443
        SBC     A,A             ; $00 if 0-7, $FF if 8
10444
        CALL    L226C           ; routine CO-CHANGE addressing MASK-T
10445
                                ; mask returned in A.
10446
 
10447
; now consider P-FLAG.
10448
 
10449
        RLCA                    ; 01110000 or 00001110
10450
        RLCA                    ; 11100000 or 00011100
10451
        AND     $50             ; 01000000 or 00010000  (AND 01010000)
10452
        LD      B,A             ; transfer to mask
10453
        LD      A,$08           ; load A with 8
10454
        CP      D               ; compare with parameter
10455
        SBC     A,A             ; $FF if was 9,  $00 if 0-8
10456
                                ; continue while addressing P-FLAG
10457
                                ; setting bit 4 if ink 9
10458
                                ; setting bit 6 if paper 9
10459
 
10460
; -----------------------
10461
; Handle change of colour
10462
; -----------------------
10463
; This routine addresses a system variable ATTR_T, MASK_T or P-FLAG in HL.
10464
; colour value in A, mask in B.
10465
 
10466
;; CO-CHANGE
10467
L226C:  XOR     (HL)            ; impress bits specified
10468
        AND     B               ; by mask
10469
        XOR     (HL)            ; on system variable.
10470
        LD      (HL),A          ; update system variable.
10471
        INC     HL              ; address next location.
10472
        LD      A,B             ; put current value of mask in A
10473
        RET                     ; return.
10474
 
10475
; ---
10476
 
10477
; the branch was here with flash and bright
10478
 
10479
;; CO-TEMP-C
10480
L2273:  SBC     A,A             ; set zero flag for bright.
10481
        LD      A,D             ; fetch original parameter 0,1 or 8
10482
        RRCA                    ; rotate bit 0 to bit 7
10483
        LD      B,$80           ; mask for flash 10000000
10484
        JR      NZ,L227D        ; forward to CO-TEMP-D if flash
10485
 
10486
        RRCA                    ; rotate bit 7 to bit 6
10487
        LD      B,$40           ; mask for bright 01000000
10488
 
10489
;; CO-TEMP-D
10490
L227D:  LD      C,A             ; store value in C
10491
        LD      A,D             ; fetch parameter
10492
        CP      $08             ; compare with 8
10493
        JR      Z,L2287         ; forward to CO-TEMP-E if 8
10494
 
10495
        CP      $02             ; test if 0 or 1
10496
        JR      NC,L2244        ; back to REPORT-K if not
10497
                                ; 'Invalid colour'
10498
 
10499
;; CO-TEMP-E
10500
L2287:  LD      A,C             ; value to A
10501
        LD      HL,$5C8F        ; address ATTR_T
10502
        CALL    L226C           ; routine CO-CHANGE addressing ATTR_T
10503
        LD      A,C             ; fetch value
10504
        RRCA                    ; for flash8/bright8 complete
10505
        RRCA                    ; rotations to put set bit in
10506
        RRCA                    ; bit 7 (flash) bit 6 (bright)
10507
        JR      L226C           ; back to CO-CHANGE addressing MASK_T
10508
                                ; and indirect return.
10509
 
10510
; ---------------------
10511
; Handle BORDER command
10512
; ---------------------
10513
; Command syntax example: BORDER 7
10514
; This command routine sets the border to one of the eight colours.
10515
; The colours used for the lower screen are based on this.
10516
 
10517
;; BORDER
10518
L2294:  CALL    L1E94           ; routine FIND-INT1
10519
        CP      $08             ; must be in range 0 (black) to 7 (white)
10520
        JR      NC,L2244        ; back to REPORT-K if not
10521
                                ; 'Invalid colour'.
10522
 
10523
        OUT     ($FE),A         ; outputting to port effects an immediate
10524
                                ; change.
10525
        RLCA                    ; shift the colour to
10526
        RLCA                    ; the paper bits setting the
10527
        RLCA                    ; ink colour black.
10528
        BIT     5,A             ; is the number light coloured ?
10529
                                ; i.e. in the range green to white.
10530
        JR      NZ,L22A6        ; skip to BORDER-1 if so
10531
 
10532
        XOR     $07             ; make the ink white.
10533
 
10534
;; BORDER-1
10535
L22A6:  LD      ($5C48),A       ; update BORDCR with new paper/ink
10536
        RET                     ; return.
10537
 
10538
; -----------------
10539
; Get pixel address
10540
; -----------------
10541
;
10542
;
10543
 
10544
;; PIXEL-ADD
10545
L22AA:  LD      A,$AF           ; load with 175 decimal.
10546
        SUB     B               ; subtract the y value.
10547
        JP      C,L24F9         ; jump forward to REPORT-Bc if greater.
10548
                                ; 'Integer out of range'
10549
 
10550
; the high byte is derived from Y only.
10551
; the first 3 bits are always 010
10552
; the next 2 bits denote in which third of the screen the byte is.
10553
; the last 3 bits denote in which of the 8 scan lines within a third
10554
; the byte is located. There are 24 discrete values.
10555
 
10556
 
10557
        LD      B,A             ; the line number from top of screen to B.
10558
        AND     A               ; clear carry (already clear)
10559
        RRA                     ;                     0xxxxxxx
10560
        SCF                     ; set carry flag
10561
        RRA                     ;                     10xxxxxx
10562
        AND     A               ; clear carry flag
10563
        RRA                     ;                     010xxxxx
10564
 
10565
        XOR     B               ;
10566
        AND     $F8             ; keep the top 5 bits 11111000
10567
        XOR     B               ;                     010xxbbb
10568
        LD      H,A             ; transfer high byte to H.
10569
 
10570
; the low byte is derived from both X and Y.
10571
 
10572
        LD      A,C             ; the x value 0-255.
10573
        RLCA                    ;
10574
        RLCA                    ;
10575
        RLCA                    ;
10576
        XOR     B               ; the y value
10577
        AND     $C7             ; apply mask             11000111
10578
        XOR     B               ; restore unmasked bits  xxyyyxxx
10579
        RLCA                    ; rotate to              xyyyxxxx
10580
        RLCA                    ; required position.     yyyxxxxx
10581
        LD      L,A             ; low byte to L.
10582
 
10583
; finally form the pixel position in A.
10584
 
10585
        LD      A,C             ; x value to A
10586
        AND     $07             ; mod 8
10587
        RET                     ; return
10588
 
10589
; ----------------
10590
; Point Subroutine
10591
; ----------------
10592
; The point subroutine is called from s-point via the scanning functions
10593
; table.
10594
 
10595
;; POINT-SUB
10596
L22CB:  CALL    L2307           ; routine STK-TO-BC
10597
        CALL    L22AA           ; routine PIXEL-ADD finds address of pixel.
10598
        LD      B,A             ; pixel position to B, 0-7.
10599
        INC     B               ; increment to give rotation count 1-8.
10600
        LD      A,(HL)          ; fetch byte from screen.
10601
 
10602
;; POINT-LP
10603
L22D4:  RLCA                    ; rotate and loop back
10604
        DJNZ    L22D4           ; to POINT-LP until pixel at right.
10605
 
10606
        AND      $01            ; test to give zero or one.
10607
        JP      L2D28           ; jump forward to STACK-A to save result.
10608
 
10609
; -------------------
10610
; Handle PLOT command
10611
; -------------------
10612
; Command Syntax example: PLOT 128,88
10613
;
10614
 
10615
;; PLOT
10616
L22DC:  CALL    L2307           ; routine STK-TO-BC
10617
        CALL    L22E5           ; routine PLOT-SUB
10618
        JP      L0D4D           ; to TEMPS
10619
 
10620
; -------------------
10621
; The Plot subroutine
10622
; -------------------
10623
; A screen byte holds 8 pixels so it is necessary to rotate a mask
10624
; into the correct position to leave the other 7 pixels unaffected.
10625
; However all 64 pixels in the character cell take any embedded colour
10626
; items.
10627
; A pixel can be reset (inverse 1), toggled (over 1), or set ( with inverse
10628
; and over switches off). With both switches on, the byte is simply put
10629
; back on the screen though the colours may change.
10630
 
10631
;; PLOT-SUB
10632
L22E5:  LD      ($5C7D),BC      ; store new x/y values in COORDS
10633
        CALL    L22AA           ; routine PIXEL-ADD gets address in HL,
10634
                                ; count from left 0-7 in B.
10635
        LD      B,A             ; transfer count to B.
10636
        INC     B               ; increase 1-8.
10637
        LD      A,$FE           ; 11111110 in A.
10638
 
10639
;; PLOT-LOOP
10640
L22F0:  RRCA                    ; rotate mask.
10641
        DJNZ    L22F0           ; to PLOT-LOOP until B circular rotations.
10642
 
10643
        LD      B,A             ; load mask to B
10644
        LD      A,(HL)          ; fetch screen byte to A
10645
 
10646
        LD      C,(IY+$57)      ; P_FLAG to C
10647
        BIT     0,C             ; is it to be OVER 1 ?
10648
        JR      NZ,L22FD        ; forward to PL-TST-IN if so.
10649
 
10650
; was over 0
10651
 
10652
        AND     B               ; combine with mask to blank pixel.
10653
 
10654
;; PL-TST-IN
10655
L22FD:  BIT     2,C             ; is it inverse 1 ?
10656
        JR      NZ,L2303        ; to PLOT-END if so.
10657
 
10658
        XOR     B               ; switch the pixel
10659
        CPL                     ; restore other 7 bits
10660
 
10661
;; PLOT-END
10662
L2303:  LD      (HL),A          ; load byte to the screen.
10663
        JP      L0BDB           ; exit to PO-ATTR to set colours for cell.
10664
 
10665
; ------------------------------
10666
; Put two numbers in BC register
10667
; ------------------------------
10668
;
10669
;
10670
 
10671
;; STK-TO-BC
10672
L2307:  CALL    L2314           ; routine STK-TO-A
10673
        LD      B,A             ;
10674
        PUSH    BC              ;
10675
        CALL    L2314           ; routine STK-TO-A
10676
        LD      E,C             ;
10677
        POP     BC              ;
10678
        LD      D,C             ;
10679
        LD      C,A             ;
10680
        RET                     ;
10681
 
10682
; -----------------------
10683
; Put stack in A register
10684
; -----------------------
10685
; This routine puts the last value on the calculator stack into the accumulator
10686
; deleting the last value.
10687
 
10688
;; STK-TO-A
10689
L2314:  CALL    L2DD5           ; routine FP-TO-A compresses last value into
10690
                                ; accumulator. e.g. PI would become 3. 
10691
                                ; zero flag set if positive.
10692
        JP      C,L24F9         ; jump forward to REPORT-Bc if >= 255.5.
10693
 
10694
        LD      C,$01           ; prepare a positive sign byte.
10695
        RET     Z               ; return if FP-TO-BC indicated positive.
10696
 
10697
        LD      C,$FF           ; prepare negative sign byte and
10698
        RET                     ; return.
10699
 
10700
 
10701
; ---------------------
10702
; Handle CIRCLE command
10703
; ---------------------
10704
;
10705
; syntax has been partly checked using the class for draw command.
10706
 
10707
;; CIRCLE
10708
L2320:  RST     18H             ; GET-CHAR
10709
        CP      $2C             ; is it required comma ?
10710
        JP      NZ,L1C8A        ; jump to REPORT-C if not
10711
 
10712
 
10713
        RST     20H             ; NEXT-CHAR
10714
        CALL    L1C82           ; routine EXPT-1NUM fetches radius
10715
        CALL    L1BEE           ; routine CHECK-END will return here if
10716
                                ; nothing follows command.
10717
 
10718
        RST     28H             ;; FP-CALC
10719
        DB    $2A             ;;abs           ; make radius positive
10720
        DB    $3D             ;;re-stack      ; in full floating point form
10721
        DB    $38             ;;end-calc
10722
 
10723
        LD      A,(HL)          ; fetch first floating point byte
10724
        CP      $81             ; compare to one
10725
        JR      NC,L233B        ; forward to C-R-GRE-1 if circle radius
10726
                                ; is greater than one.
10727
 
10728
 
10729
        RST     28H             ;; FP-CALC
10730
        DB    $02             ;;delete        ; delete the radius from stack.
10731
        DB    $38             ;;end-calc
10732
 
10733
        JR      L22DC           ; to PLOT to just plot x,y.
10734
 
10735
; ---
10736
 
10737
 
10738
;; C-R-GRE-1
10739
L233B:  RST     28H             ;; FP-CALC      ; x, y, r
10740
        DB    $A3             ;;stk-pi/2      ; x, y, r, pi/2.
10741
        DB    $38             ;;end-calc
10742
 
10743
        LD      (HL),$83        ;               ; x, y, r, 2*PI
10744
 
10745
        RST     28H             ;; FP-CALC
10746
        DB    $C5             ;;st-mem-5      ; store 2*PI in mem-5
10747
        DB    $02             ;;delete        ; x, y, z.
10748
        DB    $38             ;;end-calc
10749
 
10750
        CALL    L247D           ; routine CD-PRMS1
10751
        PUSH    BC              ;
10752
 
10753
        RST     28H             ;; FP-CALC
10754
        DB    $31             ;;duplicate
10755
        DB    $E1             ;;get-mem-1
10756
        DB    $04             ;;multiply
10757
        DB    $38             ;;end-calc
10758
 
10759
        LD      A,(HL)          ;
10760
        CP      $80             ;
10761
        JR      NC,L235A        ; to C-ARC-GE1
10762
 
10763
 
10764
        RST     28H             ;; FP-CALC
10765
        DB    $02             ;;delete
10766
        DB    $02             ;;delete
10767
        DB    $38             ;;end-calc
10768
 
10769
        POP     BC              ;
10770
        JP      L22DC           ; JUMP to PLOT
10771
 
10772
; ---
10773
 
10774
 
10775
;; C-ARC-GE1
10776
L235A:  RST     28H             ;; FP-CALC
10777
        DB    $C2             ;;st-mem-2
10778
        DB    $01             ;;exchange
10779
        DB    $C0             ;;st-mem-0
10780
        DB    $02             ;;delete
10781
        DB    $03             ;;subtract
10782
        DB    $01             ;;exchange
10783
        DB    $E0             ;;get-mem-0
10784
        DB    $0F             ;;addition
10785
        DB    $C0             ;;st-mem-0
10786
        DB    $01             ;;exchange
10787
        DB    $31             ;;duplicate
10788
        DB    $E0             ;;get-mem-0
10789
        DB    $01             ;;exchange
10790
        DB    $31             ;;duplicate
10791
        DB    $E0             ;;get-mem-0
10792
        DB    $A0             ;;stk-zero
10793
        DB    $C1             ;;st-mem-1
10794
        DB    $02             ;;delete
10795
        DB    $38             ;;end-calc
10796
 
10797
        INC     (IY+$62)        ; MEM-2-1st
10798
        CALL     L1E94          ; routine FIND-INT1
10799
        LD      L,A             ;
10800
        PUSH    HL              ;
10801
        CALL    L1E94           ; routine FIND-INT1
10802
        POP     HL              ;
10803
        LD      H,A             ;
10804
        LD      ($5C7D),HL      ; COORDS
10805
        POP     BC              ;
10806
        JP      L2420           ; to DRW-STEPS
10807
 
10808
 
10809
; -------------------
10810
; Handle DRAW command
10811
; -------------------
10812
;
10813
;
10814
 
10815
;; DRAW
10816
L2382:  RST     18H             ; GET-CHAR
10817
        CP      $2C             ;
10818
        JR      Z,L238D         ; to DR-3-PRMS
10819
 
10820
        CALL    L1BEE           ; routine CHECK-END
10821
        JP      L2477           ; to LINE-DRAW
10822
 
10823
; ---
10824
 
10825
;; DR-3-PRMS
10826
L238D:  RST     20H             ; NEXT-CHAR
10827
        CALL    L1C82           ; routine EXPT-1NUM
10828
        CALL    L1BEE           ; routine CHECK-END
10829
 
10830
        RST     28H             ;; FP-CALC
10831
        DB    $C5             ;;st-mem-5
10832
        DB    $A2             ;;stk-half
10833
        DB    $04             ;;multiply
10834
        DB    $1F             ;;sin
10835
        DB    $31             ;;duplicate
10836
        DB    $30             ;;not
10837
        DB    $30             ;;not
10838
        DB    $00             ;;jump-true
10839
 
10840
        DB    $06             ;;to L23A3, DR-SIN-NZ
10841
 
10842
        DB    $02             ;;delete
10843
        DB    $38             ;;end-calc
10844
 
10845
        JP      L2477           ; to LINE-DRAW
10846
 
10847
; ---
10848
 
10849
;; DR-SIN-NZ
10850
L23A3:  DB    $C0             ;;st-mem-0
10851
        DB    $02             ;;delete
10852
        DB    $C1             ;;st-mem-1
10853
        DB    $02             ;;delete
10854
        DB    $31             ;;duplicate
10855
        DB    $2A             ;;abs
10856
        DB    $E1             ;;get-mem-1
10857
        DB    $01             ;;exchange
10858
        DB    $E1             ;;get-mem-1
10859
        DB    $2A             ;;abs
10860
        DB    $0F             ;;addition
10861
        DB    $E0             ;;get-mem-0
10862
        DB    $05             ;;division
10863
        DB    $2A             ;;abs
10864
        DB    $E0             ;;get-mem-0
10865
        DB    $01             ;;exchange
10866
        DB    $3D             ;;re-stack
10867
        DB    $38             ;;end-calc
10868
 
10869
        LD      A,(HL)          ;
10870
        CP      $81             ;
10871
        JR      NC,L23C1        ; to DR-PRMS
10872
 
10873
 
10874
        RST     28H             ;; FP-CALC
10875
        DB    $02             ;;delete
10876
        DB    $02             ;;delete
10877
        DB    $38             ;;end-calc
10878
 
10879
        JP      L2477           ; to LINE-DRAW
10880
 
10881
; ---
10882
 
10883
;; DR-PRMS
10884
L23C1:  CALL    L247D           ; routine CD-PRMS1
10885
        PUSH    BC              ;
10886
 
10887
        RST     28H             ;; FP-CALC
10888
        DB    $02             ;;delete
10889
        DB    $E1             ;;get-mem-1
10890
        DB    $01             ;;exchange
10891
        DB    $05             ;;division
10892
        DB    $C1             ;;st-mem-1
10893
        DB    $02             ;;delete
10894
        DB    $01             ;;exchange
10895
        DB    $31             ;;duplicate
10896
        DB    $E1             ;;get-mem-1
10897
        DB    $04             ;;multiply
10898
        DB    $C2             ;;st-mem-2
10899
        DB    $02             ;;delete
10900
        DB    $01             ;;exchange
10901
        DB    $31             ;;duplicate
10902
        DB    $E1             ;;get-mem-1
10903
        DB    $04             ;;multiply
10904
        DB    $E2             ;;get-mem-2
10905
        DB    $E5             ;;get-mem-5
10906
        DB    $E0             ;;get-mem-0
10907
        DB    $03             ;;subtract
10908
        DB    $A2             ;;stk-half
10909
        DB    $04             ;;multiply
10910
        DB    $31             ;;duplicate
10911
        DB    $1F             ;;sin
10912
        DB    $C5             ;;st-mem-5
10913
        DB    $02             ;;delete
10914
        DB    $20             ;;cos
10915
        DB    $C0             ;;st-mem-0
10916
        DB    $02             ;;delete
10917
        DB    $C2             ;;st-mem-2
10918
        DB    $02             ;;delete
10919
        DB    $C1             ;;st-mem-1
10920
        DB    $E5             ;;get-mem-5
10921
        DB    $04             ;;multiply
10922
        DB    $E0             ;;get-mem-0
10923
        DB    $E2             ;;get-mem-2
10924
        DB    $04             ;;multiply
10925
        DB    $0F             ;;addition
10926
        DB    $E1             ;;get-mem-1
10927
        DB    $01             ;;exchange
10928
        DB    $C1             ;;st-mem-1
10929
        DB    $02             ;;delete
10930
        DB    $E0             ;;get-mem-0
10931
        DB    $04             ;;multiply
10932
        DB    $E2             ;;get-mem-2
10933
        DB    $E5             ;;get-mem-5
10934
        DB    $04             ;;multiply
10935
        DB    $03             ;;subtract
10936
        DB    $C2             ;;st-mem-2
10937
        DB    $2A             ;;abs
10938
        DB    $E1             ;;get-mem-1
10939
        DB    $2A             ;;abs
10940
        DB    $0F             ;;addition
10941
        DB    $02             ;;delete
10942
        DB    $38             ;;end-calc
10943
 
10944
        LD      A,(DE)          ;
10945
        CP       $81            ;
10946
        POP     BC              ;
10947
        JP      C,L2477         ; to LINE-DRAW
10948
 
10949
        PUSH    BC              ;
10950
 
10951
        RST     28H             ;; FP-CALC
10952
        DB    $01             ;;exchange
10953
        DB    $38             ;;end-calc
10954
 
10955
        LD      A,($5C7D)       ; COORDS-x
10956
        CALL    L2D28           ; routine STACK-A
10957
 
10958
        RST     28H             ;; FP-CALC
10959
        DB    $C0             ;;st-mem-0
10960
        DB    $0F             ;;addition
10961
        DB    $01             ;;exchange
10962
        DB    $38             ;;end-calc
10963
 
10964
        LD      A,($5C7E)       ; COORDS-y
10965
        CALL    L2D28           ; routine STACK-A
10966
 
10967
        RST     28H             ;; FP-CALC
10968
        DB    $C5             ;;st-mem-5
10969
        DB    $0F             ;;addition
10970
        DB    $E0             ;;get-mem-0
10971
        DB    $E5             ;;get-mem-5
10972
        DB    $38             ;;end-calc
10973
 
10974
        POP     BC              ;
10975
 
10976
;; DRW-STEPS
10977
L2420:  DEC     B               ;
10978
        JR      Z,L245F         ; to ARC-END
10979
 
10980
        JR      L2439           ; to ARC-START
10981
 
10982
; ---
10983
 
10984
 
10985
;; ARC-LOOP
10986
L2425:  RST     28H             ;; FP-CALC
10987
        DB    $E1             ;;get-mem-1
10988
        DB    $31             ;;duplicate
10989
        DB    $E3             ;;get-mem-3
10990
        DB    $04             ;;multiply
10991
        DB    $E2             ;;get-mem-2
10992
        DB    $E4             ;;get-mem-4
10993
        DB    $04             ;;multiply
10994
        DB    $03             ;;subtract
10995
        DB    $C1             ;;st-mem-1
10996
        DB    $02             ;;delete
10997
        DB    $E4             ;;get-mem-4
10998
        DB    $04             ;;multiply
10999
        DB    $E2             ;;get-mem-2
11000
        DB    $E3             ;;get-mem-3
11001
        DB    $04             ;;multiply
11002
        DB    $0F             ;;addition
11003
        DB    $C2             ;;st-mem-2
11004
        DB    $02             ;;delete
11005
        DB    $38             ;;end-calc
11006
 
11007
;; ARC-START
11008
L2439:  PUSH    BC              ;
11009
 
11010
        RST     28H             ;; FP-CALC
11011
        DB    $C0             ;;st-mem-0
11012
        DB    $02             ;;delete
11013
        DB    $E1             ;;get-mem-1
11014
        DB    $0F             ;;addition
11015
        DB    $31             ;;duplicate
11016
        DB    $38             ;;end-calc
11017
 
11018
        LD      A,($5C7D)       ; COORDS-x
11019
        CALL    L2D28           ; routine STACK-A
11020
 
11021
        RST     28H             ;; FP-CALC
11022
        DB    $03             ;;subtract
11023
        DB    $E0             ;;get-mem-0
11024
        DB    $E2             ;;get-mem-2
11025
        DB    $0F             ;;addition
11026
        DB    $C0             ;;st-mem-0
11027
        DB    $01             ;;exchange
11028
        DB    $E0             ;;get-mem-0
11029
        DB    $38             ;;end-calc
11030
 
11031
        LD      A,($5C7E)       ; COORDS-y
11032
        CALL    L2D28           ; routine STACK-A
11033
 
11034
        RST     28H             ;; FP-CALC
11035
        DB    $03             ;;subtract
11036
        DB    $38             ;;end-calc
11037
 
11038
        CALL    L24B7           ; routine DRAW-LINE
11039
        POP     BC              ;
11040
        DJNZ    L2425           ; to ARC-LOOP
11041
 
11042
 
11043
;; ARC-END
11044
L245F:  RST     28H             ;; FP-CALC
11045
        DB    $02             ;;delete
11046
        DB    $02             ;;delete
11047
        DB    $01             ;;exchange
11048
        DB    $38             ;;end-calc
11049
 
11050
        LD      A,($5C7D)       ; COORDS-x
11051
        CALL    L2D28           ; routine STACK-A
11052
 
11053
        RST     28H             ;; FP-CALC
11054
        DB    $03             ;;subtract
11055
        DB    $01             ;;exchange
11056
        DB    $38             ;;end-calc
11057
 
11058
        LD      A,($5C7E)       ; COORDS-y
11059
        CALL    L2D28           ; routine STACK-A
11060
 
11061
        RST     28H             ;; FP-CALC
11062
        DB    $03             ;;subtract
11063
        DB    $38             ;;end-calc
11064
 
11065
;; LINE-DRAW
11066
L2477:  CALL    L24B7           ; routine DRAW-LINE
11067
        JP      L0D4D           ; to TEMPS
11068
 
11069
 
11070
; ------------------
11071
; Initial parameters
11072
; ------------------
11073
;
11074
;
11075
 
11076
;; CD-PRMS1
11077
L247D:  RST     28H             ;; FP-CALC
11078
        DB    $31             ;;duplicate
11079
        DB    $28             ;;sqr
11080
        DB    $34             ;;stk-data
11081
        DB    $32             ;;Exponent: $82, Bytes: 1
11082
        DB    $00             ;;(+00,+00,+00)
11083
        DB    $01             ;;exchange
11084
        DB    $05             ;;division
11085
        DB    $E5             ;;get-mem-5
11086
        DB    $01             ;;exchange
11087
        DB    $05             ;;division
11088
        DB    $2A             ;;abs
11089
        DB    $38             ;;end-calc
11090
 
11091
        CALL    L2DD5           ; routine FP-TO-A
11092
        JR      C,L2495         ; to USE-252
11093
 
11094
        AND     $FC             ;
11095
        ADD     A,$04           ;
11096
        JR      NC,L2497        ; to DRAW-SAVE
11097
 
11098
;; USE-252
11099
L2495:  LD      A,$FC           ;
11100
 
11101
;; DRAW-SAVE
11102
L2497:  PUSH    AF              ;
11103
        CALL    L2D28           ; routine STACK-A
11104
 
11105
        RST     28H             ;; FP-CALC
11106
        DB    $E5             ;;get-mem-5
11107
        DB    $01             ;;exchange
11108
        DB    $05             ;;division
11109
        DB    $31             ;;duplicate
11110
        DB    $1F             ;;sin
11111
        DB    $C4             ;;st-mem-4
11112
        DB    $02             ;;delete
11113
        DB    $31             ;;duplicate
11114
        DB    $A2             ;;stk-half
11115
        DB    $04             ;;multiply
11116
        DB    $1F             ;;sin
11117
        DB    $C1             ;;st-mem-1
11118
        DB    $01             ;;exchange
11119
        DB    $C0             ;;st-mem-0
11120
        DB    $02             ;;delete
11121
        DB    $31             ;;duplicate
11122
        DB    $04             ;;multiply
11123
        DB    $31             ;;duplicate
11124
        DB    $0F             ;;addition
11125
        DB    $A1             ;;stk-one
11126
        DB    $03             ;;subtract
11127
        DB    $1B             ;;negate
11128
        DB    $C3             ;;st-mem-3
11129
        DB    $02             ;;delete
11130
        DB    $38             ;;end-calc
11131
 
11132
        POP     BC              ;
11133
        RET                     ;
11134
 
11135
; ------------
11136
; Line drawing
11137
; ------------
11138
;
11139
;
11140
 
11141
;; DRAW-LINE
11142
L24B7:  CALL    L2307           ; routine STK-TO-BC
11143
        LD      A,C             ;
11144
        CP      B               ;
11145
        JR      NC,L24C4        ; to DL-X-GE-Y
11146
 
11147
        LD      L,C             ;
11148
        PUSH    DE              ;
11149
        XOR     A               ;
11150
        LD      E,A             ;
11151
        JR      L24CB           ; to DL-LARGER
11152
 
11153
; ---
11154
 
11155
;; DL-X-GE-Y
11156
L24C4:  OR      C               ;
11157
        RET     Z               ;
11158
 
11159
        LD      L,B             ;
11160
        LD      B,C             ;
11161
        PUSH    DE              ;
11162
        LD      D,$00           ;
11163
 
11164
;; DL-LARGER
11165
L24CB:  LD      H,B             ;
11166
        LD      A,B             ;
11167
        RRA                     ;
11168
 
11169
;; D-L-LOOP
11170
L24CE:  ADD     A,L             ;
11171
        JR      C,L24D4         ; to D-L-DIAG
11172
 
11173
        CP      H               ;
11174
        JR      C,L24DB         ; to D-L-HR-VT
11175
 
11176
;; D-L-DIAG
11177
L24D4:  SUB     H               ;
11178
        LD      C,A             ;
11179
        EXX                     ;
11180
        POP     BC              ;
11181
        PUSH    BC              ;
11182
        JR      L24DF           ; to D-L-STEP
11183
 
11184
; ---
11185
 
11186
;; D-L-HR-VT
11187
L24DB:  LD      C,A             ;
11188
        PUSH    DE              ;
11189
        EXX                     ;
11190
        POP     BC              ;
11191
 
11192
;; D-L-STEP
11193
L24DF:  LD      HL,($5C7D)      ; COORDS
11194
        LD      A,B             ;
11195
        ADD     A,H             ;
11196
        LD      B,A             ;
11197
        LD      A,C             ;
11198
        INC     A               ;
11199
        ADD     A,L             ;
11200
        JR      C,L24F7         ; to D-L-RANGE
11201
 
11202
        JR      Z,L24F9         ; to REPORT-Bc
11203
 
11204
;; D-L-PLOT
11205
L24EC:  DEC     A               ;
11206
        LD      C,A             ;
11207
        CALL    L22E5           ; routine PLOT-SUB
11208
        EXX                     ;
11209
        LD      A,C             ;
11210
        DJNZ    L24CE           ; to D-L-LOOP
11211
 
11212
        POP     DE              ;
11213
        RET                     ;
11214
 
11215
; ---
11216
 
11217
;; D-L-RANGE
11218
L24F7:  JR      Z,L24EC         ; to D-L-PLOT
11219
 
11220
 
11221
;; REPORT-Bc
11222
L24F9:  RST     08H             ; ERROR-1
11223
        DB    $0A             ; Error Report: Integer out of range
11224
 
11225
 
11226
 
11227
;***********************************
11228
;** Part 8. EXPRESSION EVALUATION **
11229
;***********************************
11230
;
11231
; It is a this stage of the ROM that the Spectrum ceases altogether to be
11232
; just a colourful novelty. One remarkable feature is that in all previous
11233
; commands when the Spectrum is expecting a number or a string then an
11234
; expression of the same type can be substituted ad infinitum.
11235
; This is the routine that evaluates that expression.
11236
; This is what causes 2 + 2 to give the answer 4.
11237
; That is quite easy to understand. However you don't have to make it much
11238
; more complex to start a remarkable juggling act.
11239
; e.g. PRINT 2 * (VAL "2+2" + TAN 3)
11240
; In fact, provided there is enough free RAM, the Spectrum can evaluate
11241
; an expression of unlimited complexity.
11242
; Apart from a couple of minor glitches, which you can now correct, the
11243
; system is remarkably robust.
11244
 
11245
 
11246
; ---------------------------------
11247
; Scan expression or sub-expression
11248
; ---------------------------------
11249
;
11250
;
11251
 
11252
;; SCANNING
11253
L24FB:  RST     18H             ; GET-CHAR
11254
        LD      B,$00           ; priority marker zero is pushed on stack
11255
                                ; to signify end of expression when it is
11256
                                ; popped off again.
11257
        PUSH    BC              ; put in on stack.
11258
                                ; and proceed to consider the first character
11259
                                ; of the expression.
11260
 
11261
;; S-LOOP-1
11262
L24FF:  LD      C,A             ; store the character while a look up is done.
11263
        LD      HL,L2596        ; Address: scan-func
11264
        CALL    L16DC           ; routine INDEXER is called to see if it is
11265
                                ; part of a limited range '+', '(', 'ATTR' etc.
11266
 
11267
        LD      A,C             ; fetch the character back
11268
        JP      NC,L2684        ; jump forward to S-ALPHNUM if not in primary
11269
                                ; operators and functions to consider in the
11270
                                ; first instance a digit or a variable and
11271
                                ; then anything else.                >>>
11272
 
11273
        LD      B,$00           ; but here if it was found in table so
11274
        LD      C,(HL)          ; fetch offset from table and make B zero.
11275
        ADD     HL,BC           ; add the offset to position found
11276
        JP      (HL)            ; and jump to the routine e.g. S-BIN
11277
                                ; making an indirect exit from there.
11278
 
11279
; -------------------------------------------------------------------------
11280
; The four service subroutines for routines in the scannings function table
11281
; -------------------------------------------------------------------------
11282
 
11283
; PRINT """Hooray!"" he cried."
11284
 
11285
;; S-QUOTE-S
11286
L250F:  CALL    L0074           ; routine CH-ADD+1 points to next character
11287
                                ; and fetches that character.
11288
        INC     BC              ; increase length counter.
11289
        CP      $0D             ; is it carriage return ?
11290
                                ; inside a quote.
11291
        JP      Z,L1C8A         ; jump back to REPORT-C if so.
11292
                                ; 'Nonsense in BASIC'.
11293
 
11294
        CP      $22             ; is it a quote '"' ?
11295
        JR      NZ,L250F        ; back to S-QUOTE-S if not for more.
11296
 
11297
        CALL    L0074           ; routine CH-ADD+1
11298
        CP      $22             ; compare with possible adjacent quote
11299
        RET                     ; return. with zero set if two together.
11300
 
11301
; ---
11302
 
11303
; This subroutine is used to get two coordinate expressions for the three
11304
; functions SCREEN$, ATTR and POINT that have two fixed parameters and
11305
; therefore require surrounding braces.
11306
 
11307
;; S-2-COORD
11308
L2522:  RST     20H             ; NEXT-CHAR
11309
        CP      $28             ; is it the opening '(' ?
11310
        JR      NZ,L252D        ; forward to S-RPORT-C if not
11311
                                ; 'Nonsense in BASIC'.
11312
 
11313
        CALL    L1C79           ; routine NEXT-2NUM gets two comma-separated
11314
                                ; numeric expressions. Note. this could cause
11315
                                ; many more recursive calls to SCANNING but
11316
                                ; the parent function will be evaluated fully
11317
                                ; before rejoining the main juggling act.
11318
 
11319
        RST     18H             ; GET-CHAR
11320
        CP      $29             ; is it the closing ')' ?
11321
 
11322
;; S-RPORT-C
11323
L252D:  JP      NZ,L1C8A        ; jump back to REPORT-C if not.
11324
                                ; 'Nonsense in BASIC'.
11325
 
11326
; ------------
11327
; Check syntax
11328
; ------------
11329
; This routine is called on a number of occasions to check if syntax is being
11330
; checked or if the program is being run. To test the flag inline would use
11331
; four bytes of code, but a call instruction only uses 3 bytes of code.
11332
 
11333
;; SYNTAX-Z
11334
L2530:  BIT     7,(IY+$01)      ; test FLAGS  - checking syntax only ?
11335
        RET                     ; return.
11336
 
11337
; ----------------
11338
; Scanning SCREEN$
11339
; ----------------
11340
; This function returns the code of a bit-mapped character at screen
11341
; position at line C, column B. It is unable to detect the mosaic characters
11342
; which are not bit-mapped but detects the ASCII 32 - 127 range.
11343
; The bit-mapped UDGs are ignored which is curious as it requires only a
11344
; few extra bytes of code. As usual, anything to do with CHARS is weird.
11345
; If no match is found a null string is returned.
11346
; No actual check on ranges is performed - that's up to the BASIC programmer.
11347
; No real harm can come from SCREEN$(255,255) although the BASIC manual
11348
; says that invalid values will be trapped.
11349
; Interestingly, in the Pitman pocket guide, 1984, Vickers says that the
11350
; range checking will be performed. 
11351
 
11352
;; S-SCRN$-S
11353
L2535:  CALL    L2307           ; routine STK-TO-BC.
11354
        LD      HL,($5C36)      ; fetch address of CHARS.
11355
        LD      DE,$0100        ; fetch offset to chr$ 32
11356
        ADD     HL,DE           ; and find start of bitmaps.
11357
                                ; Note. not inc h. ??
11358
        LD      A,C             ; transfer line to A.
11359
        RRCA                    ; multiply
11360
        RRCA                    ; by
11361
        RRCA                    ; thirty-two.
11362
        AND     $E0             ; and with 11100000
11363
        XOR     B               ; combine with column $00 - $1F
11364
        LD      E,A             ; to give the low byte of top line
11365
        LD      A,C             ; column to A range 00000000 to 00011111
11366
        AND     $18             ; and with 00011000
11367
        XOR     $40             ; xor with 01000000 (high byte screen start)
11368
        LD      D,A             ; register DE now holds start address of cell.
11369
        LD      B,$60           ; there are 96 characters in ASCII set.
11370
 
11371
;; S-SCRN-LP
11372
L254F:  PUSH    BC              ; save count
11373
        PUSH    DE              ; save screen start address
11374
        PUSH    HL              ; save bitmap start
11375
        LD      A,(DE)          ; first byte of screen to A
11376
        XOR     (HL)            ; xor with corresponding character byte
11377
        JR      Z,L255A         ; forward to S-SC-MTCH if they match
11378
                                ; if inverse result would be $FF
11379
                                ; if any other then mismatch
11380
 
11381
        INC     A               ; set to $00 if inverse
11382
        JR      NZ,L2573        ; forward to S-SCR-NXT if a mismatch
11383
 
11384
        DEC     A               ; restore $FF
11385
 
11386
; a match has been found so seven more to test.
11387
 
11388
;; S-SC-MTCH
11389
L255A:  LD      C,A             ; load C with inverse mask $00 or $FF
11390
        LD      B,$07           ; count seven more bytes
11391
 
11392
;; S-SC-ROWS
11393
L255D:  INC     D               ; increment screen address.
11394
        INC     HL              ; increment bitmap address.
11395
        LD      A,(DE)          ; byte to A
11396
        XOR     (HL)            ; will give $00 or $FF (inverse)
11397
        XOR     C               ; xor with inverse mask
11398
        JR      NZ,L2573        ; forward to S-SCR-NXT if no match.
11399
 
11400
        DJNZ    L255D           ; back to S-SC-ROWS until all eight matched.
11401
 
11402
; continue if a match of all eight bytes was found
11403
 
11404
        POP     BC              ; discard the
11405
        POP     BC              ; saved
11406
        POP     BC              ; pointers
11407
        LD      A,$80           ; the endpoint of character set
11408
        SUB     B               ; subtract the counter
11409
                                ; to give the code 32-127
11410
        LD      BC,$0001        ; make one space in workspace.
11411
 
11412
        RST     30H             ; BC-SPACES creates the space sliding
11413
                                ; the calculator stack upwards.
11414
        LD      (DE),A          ; start is addressed by DE, so insert code
11415
        JR      L257D           ; forward to S-SCR-STO
11416
 
11417
; ---
11418
 
11419
; the jump was here if no match and more bitmaps to test.
11420
 
11421
;; S-SCR-NXT
11422
L2573:  POP     HL              ; restore the last bitmap start
11423
        LD      DE,$0008        ; and prepare to add 8.
11424
        ADD     HL,DE           ; now addresses next character bitmap.
11425
        POP     DE              ; restore screen address
11426
        POP     BC              ; and character counter in B
11427
        DJNZ    L254F           ; back to S-SCRN-LP if more characters.
11428
 
11429
        LD      C,B             ; B is now zero, so BC now zero.
11430
 
11431
;; S-SCR-STO
11432
L257D:  JP      L2AB2           ; to STK-STO-$ to store the string in
11433
                                ; workspace or a string with zero length.
11434
                                ; (value of DE doesn't matter in last case)
11435
 
11436
; Note. this exit seems correct but the general-purpose routine S-STRING
11437
; that calls this one will also stack any of its string results so this
11438
; leads to a double storing of the result in this case.
11439
; The instruction at L257D should just be a RET.
11440
; credit Stephen Kelly and others, 1982.
11441
 
11442
; -------------
11443
; Scanning ATTR
11444
; -------------
11445
; This function subroutine returns the attributes of a screen location -
11446
; a numeric result.
11447
; Again it's up to the BASIC programmer to supply valid values of line/column.
11448
 
11449
;; S-ATTR-S
11450
L2580:  CALL    L2307           ; routine STK-TO-BC fetches line to C,
11451
                                ; and column to B.
11452
        LD      A,C             ; line to A $00 - $17   (max 00010111)
11453
        RRCA                    ; rotate
11454
        RRCA                    ; bits
11455
        RRCA                    ; left.
11456
        LD      C,A             ; store in C as an intermediate value.
11457
 
11458
        AND     $E0             ; pick up bits 11100000 ( was 00011100 )
11459
        XOR     B               ; combine with column $00 - $1F
11460
        LD      L,A             ; low byte now correct.
11461
 
11462
        LD      A,C             ; bring back intermediate result from C
11463
        AND     $03             ; mask to give correct third of
11464
                                ; screen $00 - $02
11465
        XOR     $58             ; combine with base address.
11466
        LD      H,A             ; high byte correct.
11467
        LD      A,(HL)          ; pick up the colour attribute.
11468
        JP      L2D28           ; forward to STACK-A to store result
11469
                                ; and make an indirect exit.
11470
 
11471
; -----------------------
11472
; Scanning function table
11473
; -----------------------
11474
; This table is used by INDEXER routine to find the offsets to
11475
; four operators and eight functions. e.g. $A8 is the token 'FN'.
11476
; This table is used in the first instance for the first character of an
11477
; expression or by a recursive call to SCANNING for the first character of
11478
; any sub-expression. It eliminates functions that have no argument or
11479
; functions that can have more than one argument and therefore require
11480
; braces. By eliminating and dealing with these now it can later take a
11481
; simplistic approach to all other functions and assume that they have
11482
; one argument.
11483
; Similarly by eliminating BIN and '.' now it is later able to assume that
11484
; all numbers begin with a digit and that the presence of a number or
11485
; variable can be detected by a call to ALPHANUM.
11486
; By default all expressions are positive and the spurious '+' is eliminated
11487
; now as in print +2. This should not be confused with the operator '+'.
11488
; Note. this does allow a degree of nonsense to be accepted as in
11489
; PRINT +"3 is the greatest.".
11490
; An acquired programming skill is the ability to include brackets where
11491
; they are not necessary.
11492
; A bracket at the start of a sub-expression may be spurious or necessary
11493
; to denote that the contained expression is to be evaluated as an entity.
11494
; In either case this is dealt with by recursive calls to SCANNING.
11495
; An expression that begins with a quote requires special treatment.
11496
 
11497
;; scan-func
11498
L2596:  DB    $22, L25B3-$-1  ; $1C offset to S-QUOTE
11499
        DB    '(', L25E8-$-1  ; $4F offset to S-BRACKET
11500
        DB    '.', L268D-$-1  ; $F2 offset to S-DECIMAL
11501
        DB    '+', L25AF-$-1  ; $12 offset to S-U-PLUS
11502
 
11503
        DB    $A8, L25F5-$-1  ; $56 offset to S-FN
11504
        DB    $A5, L25F8-$-1  ; $57 offset to S-RND
11505
        DB    $A7, L2627-$-1  ; $84 offset to S-PI
11506
        DB    $A6, L2634-$-1  ; $8F offset to S-INKEY$
11507
        DB    $C4, L268D-$-1  ; $E6 offset to S-BIN
11508
        DB    $AA, L2668-$-1  ; $BF offset to S-SCREEN$
11509
        DB    $AB, L2672-$-1  ; $C7 offset to S-ATTR
11510
        DB    $A9, L267B-$-1  ; $CE offset to S-POINT
11511
 
11512
        DB    $00             ; zero end marker
11513
 
11514
; --------------------------
11515
; Scanning function routines
11516
; --------------------------
11517
; These are the 11 subroutines accessed by the above table.
11518
; S-BIN and S-DECIMAL are the same
11519
; The 1-byte offset limits their location to within 255 bytes of their
11520
; entry in the table.
11521
 
11522
; ->
11523
;; S-U-PLUS
11524
L25AF:  RST     20H             ; NEXT-CHAR just ignore
11525
        JP      L24FF           ; to S-LOOP-1
11526
 
11527
; ---
11528
 
11529
; ->
11530
;; S-QUOTE
11531
L25B3:  RST     18H             ; GET-CHAR
11532
        INC     HL              ; address next character (first in quotes)
11533
        PUSH    HL              ; save start of quoted text.
11534
        LD      BC,$0000        ; initialize length of string to zero.
11535
        CALL    L250F           ; routine S-QUOTE-S
11536
        JR      NZ,L25D9        ; forward to S-Q-PRMS if
11537
 
11538
;; S-Q-AGAIN
11539
L25BE:  CALL    L250F           ; routine S-QUOTE-S copies string until a
11540
                                ; quote is encountered
11541
        JR      Z,L25BE         ; back to S-Q-AGAIN if two quotes WERE
11542
                                ; together.
11543
 
11544
; but if just an isolated quote then that terminates the string.
11545
 
11546
        CALL    L2530           ; routine SYNTAX-Z
11547
        JR      Z,L25D9         ; forward to S-Q-PRMS if checking syntax.
11548
 
11549
 
11550
        RST     30H             ; BC-SPACES creates the space for true
11551
                                ; copy of string in workspace.
11552
        POP     HL              ; re-fetch start of quoted text.
11553
        PUSH    DE              ; save start in workspace.
11554
 
11555
;; S-Q-COPY
11556
L25CB:  LD      A,(HL)          ; fetch a character from source.
11557
        INC     HL              ; advance source address.
11558
        LD      (DE),A          ; place in destination.
11559
        INC     DE              ; advance destination address.
11560
        CP      $22             ; was it a '"' just copied ?
11561
        JR      NZ,L25CB        ; back to S-Q-COPY to copy more if not
11562
 
11563
        LD      A,(HL)          ; fetch adjacent character from source.
11564
        INC     HL              ; advance source address.
11565
        CP      $22             ; is this '"' ? - i.e. two quotes together ?
11566
        JR      Z,L25CB         ; to S-Q-COPY if so including just one of the
11567
                                ; pair of quotes.
11568
 
11569
; proceed when terminating quote encountered.
11570
 
11571
;; S-Q-PRMS
11572
L25D9:  DEC     BC              ; decrease count by 1.
11573
        POP     DE              ; restore start of string in workspace.
11574
 
11575
;; S-STRING
11576
L25DB:  LD      HL,$5C3B        ; Address FLAGS system variable.
11577
        RES     6,(HL)          ; signal string result.
11578
        BIT     7,(HL)          ; is syntax being checked.
11579
        CALL    NZ,L2AB2        ; routine STK-STO-$ is called in runtime.
11580
        JP      L2712           ; jump forward to S-CONT-2          ===>
11581
 
11582
; ---
11583
 
11584
; ->
11585
;; S-BRACKET
11586
L25E8:  RST     20H             ; NEXT-CHAR
11587
        CALL    L24FB           ; routine SCANNING is called recursively.
11588
        CP      $29             ; is it the closing ')' ?
11589
        JP      NZ,L1C8A        ; jump back to REPORT-C if not
11590
                                ; 'Nonsense in BASIC'
11591
 
11592
        RST     20H             ; NEXT-CHAR
11593
        JP      L2712           ; jump forward to S-CONT-2          ===>
11594
 
11595
; ---
11596
 
11597
; ->
11598
;; S-FN
11599
L25F5:  JP      L27BD           ; jump forward to S-FN-SBRN.
11600
 
11601
; ---
11602
 
11603
; ->
11604
;; S-RND
11605
L25F8:  CALL    L2530           ; routine SYNTAX-Z
11606
        JR      Z,L2625         ; forward to S-RND-END if checking syntax.
11607
 
11608
        LD      BC,($5C76)      ; fetch system variable SEED
11609
        CALL    L2D2B           ; routine STACK-BC places on calculator stack
11610
 
11611
        RST     28H             ;; FP-CALC           ;s.
11612
        DB    $A1             ;;stk-one            ;s,1.
11613
        DB    $0F             ;;addition           ;s+1.
11614
        DB    $34             ;;stk-data           ;
11615
        DB    $37             ;;Exponent: $87,
11616
                                ;;Bytes: 1
11617
        DB    $16             ;;(+00,+00,+00)      ;s+1,75.
11618
        DB    $04             ;;multiply           ;(s+1)*75 = v
11619
        DB    $34             ;;stk-data           ;v.
11620
        DB    $80             ;;Bytes: 3
11621
        DB    $41             ;;Exponent $91
11622
        DB    $00,$00,$80     ;;(+00)              ;v,65537.
11623
        DB    $32             ;;n-mod-m            ;remainder, result.
11624
        DB    $02             ;;delete             ;remainder.
11625
        DB    $A1             ;;stk-one            ;remainder, 1.
11626
        DB    $03             ;;subtract           ;remainder - 1. = rnd
11627
        DB    $31             ;;duplicate          ;rnd,rnd.
11628
        DB    $38             ;;end-calc
11629
 
11630
        CALL    L2DA2           ; routine FP-TO-BC
11631
        LD      ($5C76),BC      ; store in SEED for next starting point.
11632
        LD      A,(HL)          ; fetch exponent
11633
        AND     A               ; is it zero ?
11634
        JR      Z,L2625         ; forward if so to S-RND-END
11635
 
11636
        SUB     $10             ; reduce exponent by 2^16
11637
        LD      (HL),A          ; place back
11638
 
11639
;; S-RND-END
11640
L2625:  JR      L2630           ; forward to S-PI-END
11641
 
11642
; ---
11643
 
11644
; the number PI 3.14159...
11645
 
11646
; ->
11647
;; S-PI
11648
L2627:  CALL    L2530           ; routine SYNTAX-Z
11649
        JR      Z,L2630         ; to S-PI-END if checking syntax.
11650
 
11651
        RST     28H             ;; FP-CALC
11652
        DB    $A3             ;;stk-pi/2                          pi/2.
11653
        DB    $38             ;;end-calc
11654
 
11655
        INC     (HL)            ; increment the exponent leaving pi
11656
                                ; on the calculator stack.
11657
 
11658
;; S-PI-END
11659
L2630:  RST     20H             ; NEXT-CHAR
11660
        JP      L26C3           ; jump forward to S-NUMERIC
11661
 
11662
; ---
11663
 
11664
; ->
11665
;; S-INKEY$
11666
L2634:  LD      BC,$105A        ; priority $10, operation code $1A ('read-in')
11667
                                ; +$40 for string result, numeric operand.
11668
                                ; set this up now in case we need to use the
11669
                                ; calculator.
11670
        RST     20H             ; NEXT-CHAR
11671
        CP      $23             ; '#' ?
11672
        JP      Z,L270D         ; to S-PUSH-PO if so to use the calculator
11673
                                ; single operation
11674
                                ; to read from network/RS232 etc. .
11675
 
11676
; else read a key from the keyboard.
11677
 
11678
        LD      HL,$5C3B        ; fetch FLAGS
11679
        RES     6,(HL)          ; signal string result.
11680
        BIT     7,(HL)          ; checking syntax ?
11681
        JR      Z,L2665         ; forward to S-INK$-EN if so
11682
 
11683
;===============================
11684
                JP L3B6C                ; Spectrum 128 patch
11685
;===============================
11686
 
11687
L2649:  LD      C,$00           ; the length of an empty string
11688
        JR      NZ,L2660        ; to S-IK$-STK to store empty string if
11689
                                ; no key returned.
11690
 
11691
        CALL    L031E           ; routine K-TEST get main code in A
11692
        JR      NC,L2660        ; to S-IK$-STK to stack null string if
11693
                                ; invalid
11694
 
11695
        DEC     D               ; D is expected to be FLAGS so set bit 3 $FF
11696
                                ; 'L' Mode so no keywords.
11697
        LD      E,A             ; main key to A
11698
                                ; C is MODE 0 'KLC' from above still.
11699
        CALL    L0333           ; routine K-DECODE
11700
L2657:  PUSH    AF              ; save the code
11701
        LD      BC,$0001        ; make room for one character
11702
 
11703
        RST     30H             ; BC-SPACES
11704
        POP     AF              ; bring the code back
11705
        LD      (DE),A          ; put the key in workspace
11706
        LD      C,$01           ; set C length to one
11707
 
11708
;; S-IK$-STK
11709
L2660:  LD      B,$00           ; set high byte of length to zero
11710
        CALL    L2AB2           ; routine STK-STO-$
11711
 
11712
;; S-INK$-EN
11713
L2665:  JP      L2712           ; to S-CONT-2            ===>
11714
 
11715
; ---
11716
 
11717
; ->
11718
;; S-SCREEN$
11719
L2668:  CALL    L2522           ; routine S-2-COORD
11720
        CALL    NZ,L2535        ; routine S-SCRN$-S
11721
 
11722
        RST     20H             ; NEXT-CHAR
11723
        JP      L25DB           ; forward to S-STRING to stack result
11724
 
11725
; ---
11726
 
11727
; ->
11728
;; S-ATTR
11729
L2672:  CALL    L2522           ; routine S-2-COORD
11730
        CALL    NZ,L2580        ; routine S-ATTR-S
11731
 
11732
        RST     20H             ; NEXT-CHAR
11733
        JR      L26C3           ; forward to S-NUMERIC
11734
 
11735
; ---
11736
 
11737
; ->
11738
;; S-POINT
11739
L267B:  CALL    L2522           ; routine S-2-COORD
11740
        CALL    NZ,L22CB        ; routine POINT-SUB
11741
 
11742
        RST     20H             ; NEXT-CHAR
11743
        JR      L26C3           ; forward to S-NUMERIC
11744
 
11745
; -----------------------------
11746
 
11747
; ==> The branch was here if not in table.
11748
 
11749
;; S-ALPHNUM
11750
L2684:  CALL    L2C88           ; routine ALPHANUM checks if variable or
11751
                                ; a digit.
11752
        JR      NC,L26DF        ; forward to S-NEGATE if not to consider
11753
                                ; a '-' character then functions.
11754
 
11755
        CP      $41             ; compare 'A'
11756
        JR      NC,L26C9        ; forward to S-LETTER if alpha       ->
11757
                                ; else must have been numeric so continue
11758
                                ; into that routine.
11759
 
11760
; This important routine is called during runtime and from LINE-SCAN
11761
; when a BASIC line is checked for syntax. It is this routine that
11762
; inserts, during syntax checking, the invisible floating point numbers
11763
; after the numeric expression. During runtime it just picks these
11764
; numbers up. It also handles BIN format numbers.
11765
 
11766
; ->
11767
;; S-BIN
11768
;; S-DECIMAL
11769
L268D:  CALL    L2530           ; routine SYNTAX-Z
11770
        JR      NZ,L26B5        ; to S-STK-DEC in runtime
11771
 
11772
; this route is taken when checking syntax.
11773
 
11774
        CALL    L2C9B           ; routine DEC-TO-FP to evaluate number
11775
 
11776
        RST     18H             ; GET-CHAR to fetch HL
11777
        LD      BC,$0006        ; six locations required
11778
        CALL    L1655           ; routine MAKE-ROOM
11779
        INC     HL              ; to first new location
11780
        LD      (HL),$0E        ; insert number marker
11781
        INC     HL              ; address next
11782
        EX      DE,HL           ; make DE destination.
11783
        LD      HL,($5C65)      ; STKEND points to end of stack.
11784
        LD      C,$05           ; result is five locations lower
11785
        AND     A               ; prepare for true subtraction
11786
        SBC     HL,BC           ; point to start of value.
11787
        LD      ($5C65),HL      ; update STKEND as we are taking number.
11788
        LDIR                    ; Copy five bytes to program location
11789
        EX      DE,HL           ; transfer pointer to HL
11790
        DEC     HL              ; adjust
11791
        CALL    L0077           ; routine TEMP-PTR1 sets CH-ADD
11792
        JR      L26C3           ; to S-NUMERIC to record nature of result
11793
 
11794
; ---
11795
 
11796
; branch here in runtime.
11797
 
11798
;; S-STK-DEC
11799
L26B5:  RST     18H             ; GET-CHAR positions HL at digit.
11800
 
11801
;; S-SD-SKIP
11802
L26B6:  INC     HL              ; advance pointer
11803
        LD      A,(HL)          ; until we find
11804
        CP      $0E             ; chr 14d - the number indicator
11805
        JR      NZ,L26B6        ; to S-SD-SKIP until a match
11806
                                ; it has to be here.
11807
 
11808
        INC     HL              ; point to first byte of number
11809
        CALL    L33B4           ; routine STACK-NUM stacks it
11810
        LD      ($5C5D),HL      ; update system variable CH_ADD
11811
 
11812
;; S-NUMERIC
11813
L26C3:  SET     6,(IY+$01)      ; update FLAGS  - Signal numeric result
11814
        JR      L26DD           ; forward to S-CONT-1               ===>
11815
                                ; actually S-CONT-2 is destination but why
11816
                                ; waste a byte on a jump when a JR will do.
11817
                                ; actually a JR L2712 can be used. Rats.
11818
 
11819
; end of functions accessed from scanning functions table.
11820
 
11821
; --------------------------
11822
; Scanning variable routines
11823
; --------------------------
11824
;
11825
;
11826
 
11827
;; S-LETTER
11828
L26C9:  CALL    L28B2           ; routine LOOK-VARS
11829
        JP      C,L1C2E         ; jump back to REPORT-2 if not found
11830
                                ; 'Variable not found'
11831
                                ; but a variable is always 'found' if syntax
11832
                                ; is being checked.
11833
 
11834
        CALL    Z,L2996         ; routine STK-VAR considers a subscript/slice
11835
        LD      A,($5C3B)       ; fetch FLAGS value
11836
        CP      $C0             ; compare 11000000
11837
        JR      C,L26DD         ; step forward to S-CONT-1 if string  ===>
11838
 
11839
        INC     HL              ; advance pointer
11840
        CALL    L33B4           ; routine STACK-NUM
11841
 
11842
;; S-CONT-1
11843
L26DD:  JR      L2712           ; forward to S-CONT-2                 ===>
11844
 
11845
; ----------------------------------------
11846
; -> the scanning branch was here if not alphanumeric.
11847
; All the remaining functions will be evaluated by a single call to the
11848
; calculator. The correct priority for the operation has to be placed in
11849
; the B register and the operation code, calculator literal in the C register.
11850
; the operation code has bit 7 set if result is numeric and bit 6 is
11851
; set if operand is numeric. so
11852
; $C0 = numeric result, numeric operand.            e.g. 'sin'
11853
; $80 = numeric result, string operand.             e.g. 'code'
11854
; $40 = string result, numeric operand.             e.g. 'str$'
11855
; $00 = string result, string operand.              e.g. 'val$'
11856
 
11857
;; S-NEGATE
11858
L26DF:  LD      BC,$09DB        ; prepare priority 09, operation code $C0 + 
11859
                                ; 'negate' ($1B) - bits 6 and 7 set for numeric
11860
                                ; result and numeric operand.
11861
 
11862
        CP      $2D             ; is it '-' ?
11863
        JR      Z,L270D         ; forward if so to S-PUSH-PO
11864
 
11865
        LD      BC,$1018        ; prepare priority $10, operation code 'val$' -
11866
                                ; bits 6 and 7 reset for string result and
11867
                                ; string operand.
11868
 
11869
        CP      $AE             ; is it 'VAL$' ?
11870
        JR      Z,L270D         ; forward if so to S-PUSH-PO
11871
 
11872
        SUB     $AF             ; subtract token 'CODE' value to reduce
11873
                                ; functions 'CODE' to 'NOT' although the
11874
                                ; upper range is, as yet, unchecked.
11875
                                ; valid range would be $00 - $14.
11876
 
11877
        JP      C,L1C8A         ; jump back to REPORT-C with anything else
11878
                                ; 'Nonsense in BASIC'
11879
 
11880
        LD      BC,$04F0        ; prepare priority $04, operation $C0 + 
11881
                                ; 'not' ($30)
11882
 
11883
        CP      $14             ; is it 'NOT'
11884
        JR      Z,L270D         ; forward to S-PUSH-PO if so
11885
 
11886
        JP      NC,L1C8A        ; to REPORT-C if higher
11887
                                ; 'Nonsense in BASIC'
11888
 
11889
        LD      B,$10           ; priority $10 for all the rest
11890
        ADD     A,$DC           ; make range $DC - $EF
11891
                                ; $C0 + 'code'($1C) thru 'chr$' ($2F)
11892
 
11893
        LD      C,A             ; transfer 'function' to C
11894
        CP      $DF             ; is it 'sin' ?
11895
        JR      NC,L2707        ; forward to S-NO-TO-$  with 'sin' through
11896
                                ; 'chr$' as operand is numeric.
11897
 
11898
; all the rest 'cos' through 'chr$' give a numeric result except 'str$'
11899
; and 'chr$'.
11900
 
11901
        RES     6,C             ; signal string operand for 'code', 'val' and
11902
                                ; 'len'.
11903
 
11904
;; S-NO-TO-$
11905
L2707:  CP      $EE             ; compare 'str$'
11906
        JR      C,L270D         ; forward to S-PUSH-PO if lower as result
11907
                                ; is numeric.
11908
 
11909
        RES     7,C             ; reset bit 7 of op code for 'str$', 'chr$'
11910
                                ; as result is string.
11911
 
11912
; >> This is where they were all headed for.
11913
 
11914
;; S-PUSH-PO
11915
L270D:  PUSH    BC              ; push the priority and calculator operation
11916
                                ; code.
11917
 
11918
        RST     20H             ; NEXT-CHAR
11919
        JP      L24FF           ; jump back to S-LOOP-1 to go round the loop
11920
                                ; again with the next character.
11921
 
11922
; --------------------------------
11923
 
11924
; ===>  there were many branches forward to here
11925
 
11926
;; S-CONT-2
11927
L2712:  RST     18H             ; GET-CHAR
11928
 
11929
;; S-CONT-3
11930
L2713:  CP      $28             ; is it '(' ?
11931
        JR      NZ,L2723        ; forward to S-OPERTR if not    >
11932
 
11933
        BIT     6,(IY+$01)      ; test FLAGS - numeric or string result ?
11934
        JR      NZ,L2734        ; forward to S-LOOP if numeric to evaluate  >
11935
 
11936
; if a string preceded '(' then slice it.
11937
 
11938
        CALL    L2A52           ; routine SLICING
11939
 
11940
        RST     20H             ; NEXT-CHAR
11941
        JR      L2713           ; back to S-CONT-3
11942
 
11943
; ---------------------------
11944
 
11945
; the branch was here when possibility of an operator '(' has been excluded.
11946
 
11947
;; S-OPERTR
11948
L2723:  LD      B,$00           ; prepare to add
11949
        LD      C,A             ; possible operator to C
11950
        LD      HL,L2795        ; Address: $2795 - tbl-of-ops
11951
        CALL    L16DC           ; routine INDEXER
11952
        JR      NC,L2734        ; forward to S-LOOP if not in table
11953
 
11954
; but if found in table the priority has to be looked up.
11955
 
11956
        LD      C,(HL)          ; operation code to C ( B is still zero )
11957
        LD      HL,L27B0 - $C3  ; $26ED is base of table
11958
        ADD     HL,BC           ; index into table.
11959
        LD      B,(HL)          ; priority to B.
11960
 
11961
; ------------------
11962
; Scanning main loop
11963
; ------------------
11964
; the juggling act
11965
 
11966
;; S-LOOP
11967
L2734:  POP     DE              ; fetch last priority and operation
11968
        LD      A,D             ; priority to A
11969
        CP      B               ; compare with this one
11970
        JR      C,L2773         ; forward to S-TIGHTER to execute the
11971
                                ; last operation before this one as it has
11972
                                ; higher priority.
11973
 
11974
; the last priority was greater or equal this one.
11975
 
11976
        AND     A               ; if it is zero then so is this
11977
        JP      Z,L0018         ; jump to exit via get-char pointing at
11978
                                ; next character.
11979
                                ; This may be the character after the
11980
                                ; expression or, if exiting a recursive call,
11981
                                ; the next part of the expression to be
11982
                                ; evaluated.
11983
 
11984
        PUSH    BC              ; save current priority/operation
11985
                                ; as it has lower precedence than the one
11986
                                ; now in DE.
11987
 
11988
; the 'USR' function is special in that it is overloaded to give two types
11989
; of result.
11990
 
11991
        LD      HL,$5C3B        ; address FLAGS
11992
        LD      A,E             ; new operation to A register
11993
        CP      $ED             ; is it $C0 + 'usr-no' ($2D)  ?
11994
        JR      NZ,L274C        ; forward to S-STK-LST if not
11995
 
11996
        BIT     6,(HL)          ; string result expected ?
11997
                                ; (from the lower priority operand we've
11998
                                ; just pushed on stack )
11999
        JR      NZ,L274C        ; forward to S-STK-LST if numeric
12000
                                ; as operand bits match.
12001
 
12002
        LD      E,$99           ; reset bit 6 and substitute $19 'usr-$'
12003
                                ; for string operand.
12004
 
12005
;; S-STK-LST
12006
L274C:  PUSH    DE              ; now stack this priority/operation
12007
        CALL    L2530           ; routine SYNTAX-Z
12008
        JR      Z,L275B         ; forward to S-SYNTEST if checking syntax.
12009
 
12010
        LD      A,E             ; fetch the operation code
12011
        AND     $3F             ; mask off the result/operand bits to leave
12012
                                ; a calculator literal.
12013
        LD      B,A             ; transfer to B register
12014
 
12015
; now use the calculator to perform the single operation - operand is on
12016
; the calculator stack.
12017
; Note. although the calculator is performing a single operation most
12018
; functions e.g. TAN are written using other functions and literals and
12019
; these in turn are written using further strings of calculator literals so
12020
; another level of magical recursion joins the juggling act for a while
12021
; as the calculator too is calling itself.
12022
 
12023
        RST     28H             ;; FP-CALC
12024
        DB    $3B             ;;fp-calc-2
12025
L2758:  DB    $38             ;;end-calc
12026
 
12027
        JR      L2764           ; forward to S-RUNTEST
12028
 
12029
; ---
12030
 
12031
; the branch was here if checking syntax only. 
12032
 
12033
;; S-SYNTEST
12034
L275B:  LD      A,E             ; fetch the operation code to accumulator
12035
        XOR     (IY+$01)        ; compare with bits of FLAGS
12036
        AND     $40             ; bit 6 will be zero now if operand
12037
                                ; matched expected result.
12038
 
12039
;; S-RPORT-C2
12040
L2761:  JP      NZ,L1C8A        ; to REPORT-C if mismatch
12041
                                ; 'Nonsense in BASIC'
12042
                                ; else continue to set flags for next
12043
 
12044
; the branch is to here in runtime after a successful operation.
12045
 
12046
;; S-RUNTEST
12047
L2764:  POP     DE              ; fetch the last operation from stack
12048
        LD      HL,$5C3B        ; address FLAGS
12049
        SET     6,(HL)          ; set default to numeric result in FLAGS
12050
        BIT     7,E             ; test the operational result
12051
        JR      NZ,L2770        ; forward to S-LOOPEND if numeric
12052
 
12053
        RES     6,(HL)          ; reset bit 6 of FLAGS to show string result.
12054
 
12055
;; S-LOOPEND
12056
L2770:  POP     BC              ; fetch the previous priority/operation
12057
        JR      L2734           ; back to S-LOOP to perform these
12058
 
12059
; ---
12060
 
12061
; the branch was here when a stacked priority/operator had higher priority
12062
; than the current one.
12063
 
12064
;; S-TIGHTER
12065
L2773:  PUSH    DE              ; save high priority op on stack again
12066
        LD      A,C             ; fetch lower priority operation code
12067
        BIT     6,(IY+$01)      ; test FLAGS - Numeric or string result ?
12068
        JR      NZ,L2790        ; forward to S-NEXT if numeric result
12069
 
12070
; if this is lower priority yet has string then must be a comparison.
12071
; Since these can only be evaluated in context and were defaulted to
12072
; numeric in operator look up they must be changed to string equivalents.
12073
 
12074
        AND     $3F             ; mask to give true calculator literal
12075
        ADD     A,$08           ; augment numeric literals to string
12076
                                ; equivalents.
12077
                                ; 'no-&-no'  => 'str-&-no'
12078
                                ; 'no-l-eql' => 'str-l-eql'
12079
                                ; 'no-gr-eq' => 'str-gr-eq'
12080
                                ; 'nos-neql' => 'strs-neql'
12081
                                ; 'no-grtr'  => 'str-grtr'
12082
                                ; 'no-less'  => 'str-less'
12083
                                ; 'nos-eql'  => 'strs-eql'
12084
                                ; 'addition' => 'strs-add'
12085
        LD      C,A             ; put modified comparison operator back
12086
        CP      $10             ; is it now 'str-&-no' ?
12087
        JR      NZ,L2788        ; forward to S-NOT-AND  if not.
12088
 
12089
        SET     6,C             ; set numeric operand bit
12090
        JR      L2790           ; forward to S-NEXT
12091
 
12092
; ---
12093
 
12094
;; S-NOT-AND
12095
L2788:  JR      C,L2761         ; back to S-RPORT-C2 if less
12096
                                ; 'Nonsense in BASIC'.
12097
                                ; e.g. a$ * b$
12098
 
12099
        CP      $17             ; is it 'strs-add' ?
12100
        JR      Z,L2790         ; forward to to S-NEXT if so
12101
                                ; (bit 6 and 7 are reset)
12102
 
12103
        SET     7,C             ; set numeric (Boolean) result for all others
12104
 
12105
;; S-NEXT
12106
L2790:  PUSH    BC              ; now save this priority/operation on stack
12107
 
12108
        RST     20H             ; NEXT-CHAR
12109
        JP      L24FF           ; jump back to S-LOOP-1
12110
 
12111
; ------------------
12112
; Table of operators
12113
; ------------------
12114
; This table is used to look up the calculator literals associated with
12115
; the operator character. The thirteen calculator operations $03 - $0F
12116
; have bits 6 and 7 set to signify a numeric result.
12117
; Some of these codes and bits may be altered later if the context suggests
12118
; a string comparison or operation.
12119
; that is '+', '=', '>', '<', '<=', '>=' or '<>'.
12120
 
12121
;; tbl-of-ops
12122
L2795:  DB    '+', $CF        ;        $C0 + 'addition'
12123
        DB    '-', $C3        ;        $C0 + 'subtract'
12124
        DB    '*', $C4        ;        $C0 + 'multiply'
12125
        DB    '/', $C5        ;        $C0 + 'division'
12126
        DB    '^', $C6        ;        $C0 + 'to-power'
12127
        DB    '=', $CE        ;        $C0 + 'nos-eql'
12128
        DB    '>', $CC        ;        $C0 + 'no-grtr'
12129
        DB    '<', $CD        ;        $C0 + 'no-less'
12130
 
12131
        DB    $C7, $C9        ; '<='   $C0 + 'no-l-eql'
12132
        DB    $C8, $CA        ; '>='   $C0 + 'no-gr-eql'
12133
        DB    $C9, $CB        ; '<>'   $C0 + 'nos-neql'
12134
        DB    $C5, $C7        ; 'OR'   $C0 + 'or'
12135
        DB    $C6, $C8        ; 'AND'  $C0 + 'no-&-no'
12136
 
12137
        DB    $00             ; zero end-marker.
12138
 
12139
 
12140
; -------------------
12141
; Table of priorities
12142
; -------------------
12143
; This table is indexed with the operation code obtained from the above
12144
; table $C3 - $CF to obtain the priority for the respective operation.
12145
 
12146
;; tbl-priors
12147
L27B0:  DB    $06             ; '-'   opcode $C3
12148
        DB    $08             ; '*'   opcode $C4
12149
        DB    $08             ; '/'   opcode $C5
12150
        DB    $0A             ; '^'   opcode $C6
12151
        DB    $02             ; 'OR'  opcode $C7
12152
        DB    $03             ; 'AND' opcode $C8
12153
        DB    $05             ; '<='  opcode $C9
12154
        DB    $05             ; '>='  opcode $CA
12155
        DB    $05             ; '<>'  opcode $CB
12156
        DB    $05             ; '>'   opcode $CC
12157
        DB    $05             ; '<'   opcode $CD
12158
        DB    $05             ; '='   opcode $CE
12159
        DB    $06             ; '+'   opcode $CF
12160
 
12161
; ----------------------
12162
; Scanning function (FN)
12163
; ----------------------
12164
; This routine deals with user-defined functions.
12165
; The definition can be anywhere in the program area but these are best
12166
; placed near the start of the program as we shall see.
12167
; The evaluation process is quite complex as the Spectrum has to parse two
12168
; statements at the same time. Syntax of both has been checked previously
12169
; and hidden locations have been created immediately after each argument
12170
; of the DEF FN statement. Each of the arguments of the FN function is
12171
; evaluated by SCANNING and placed in the hidden locations. Then the
12172
; expression to the right of the DEF FN '=' is evaluated by SCANNING and for
12173
; any variables encountered, a search is made in the DEF FN variable list
12174
; in the program area before searching in the normal variables area.
12175
;
12176
; Recursion is not allowed: i.e. the definition of a function should not use
12177
; the same function, either directly or indirectly ( through another function).
12178
; You'll normally get error 4, ('Out of memory'), although sometimes the sytem
12179
; will crash. - Vickers, Pitman 1984.
12180
;
12181
; As the definition is just an expression, there would seem to be no means
12182
; of breaking out of such recursion.
12183
; However, by the clever use of string expressions and VAL, such recursion is
12184
; possible.
12185
; e.g. DEF FN a(n) = VAL "n+FN a(n-1)+0" ((n<1) * 10 + 1 TO )
12186
; will evaluate the full 11-character expression for all values where n is
12187
; greater than zero but just the 11th character, "0", when n drops to zero
12188
; thereby ending the recursion producing the correct result.
12189
; Recursive string functions are possible using VAL$ instead of VAL and the
12190
; null string as the final addend.
12191
; - from a turn of the century newsgroup discussion initiated by Mike Wynne.
12192
 
12193
;; S-FN-SBRN
12194
L27BD:  CALL    L2530           ; routine SYNTAX-Z
12195
        JR      NZ,L27F7        ; forward to SF-RUN in runtime
12196
 
12197
 
12198
        RST     20H             ; NEXT-CHAR
12199
        CALL    L2C8D           ; routine ALPHA check for letters A-Z a-z
12200
        JP      NC,L1C8A        ; jump back to REPORT-C if not
12201
                                ; 'Nonsense in BASIC'
12202
 
12203
 
12204
        RST     20H             ; NEXT-CHAR
12205
        CP      $24             ; is it '$' ?
12206
        PUSH    AF              ; save character and flags
12207
        JR      NZ,L27D0        ; forward to SF-BRKT-1 with numeric function
12208
 
12209
 
12210
        RST     20H             ; NEXT-CHAR
12211
 
12212
;; SF-BRKT-1
12213
L27D0:  CP      $28             ; is '(' ?
12214
        JR      NZ,L27E6        ; forward to SF-RPRT-C if not
12215
                                ; 'Nonsense in BASIC'
12216
 
12217
 
12218
        RST     20H             ; NEXT-CHAR
12219
        CP      $29             ; is it ')' ?
12220
        JR      Z,L27E9         ; forward to SF-FLAG-6 if no arguments.
12221
 
12222
;; SF-ARGMTS
12223
L27D9:  CALL    L24FB           ; routine SCANNING checks each argument
12224
                                ; which may be an expression.
12225
 
12226
        RST     18H             ; GET-CHAR
12227
        CP      $2C             ; is it a ',' ?
12228
        JR      NZ,L27E4        ; forward if not to SF-BRKT-2 to test bracket
12229
 
12230
 
12231
        RST     20H             ; NEXT-CHAR if a comma was found
12232
        JR      L27D9           ; back to SF-ARGMTS to parse all arguments.
12233
 
12234
; ---
12235
 
12236
;; SF-BRKT-2
12237
L27E4:  CP      $29             ; is character the closing ')' ?
12238
 
12239
;; SF-RPRT-C
12240
L27E6:  JP      NZ,L1C8A        ; jump to REPORT-C
12241
                                ; 'Nonsense in BASIC'
12242
 
12243
; at this point any optional arguments have had their syntax checked.
12244
 
12245
;; SF-FLAG-6
12246
L27E9:  RST     20H             ; NEXT-CHAR
12247
        LD      HL,$5C3B        ; address system variable FLAGS
12248
        RES     6,(HL)          ; signal string result
12249
        POP     AF              ; restore test against '$'.
12250
        JR      Z,L27F4         ; forward to SF-SYN-EN if string function.
12251
 
12252
        SET     6,(HL)          ; signal numeric result
12253
 
12254
;; SF-SYN-EN
12255
L27F4:  JP      L2712           ; jump back to S-CONT-2 to continue scanning.
12256
 
12257
; ---
12258
 
12259
; the branch was here in runtime.
12260
 
12261
;; SF-RUN
12262
L27F7:  RST     20H             ; NEXT-CHAR fetches name
12263
        AND     $DF             ; AND 11101111 - reset bit 5 - upper-case.
12264
        LD      B,A             ; save in B
12265
 
12266
        RST     20H             ; NEXT-CHAR
12267
        SUB     $24             ; subtract '$'
12268
        LD      C,A             ; save result in C
12269
        JR      NZ,L2802        ; forward if not '$' to SF-ARGMT1
12270
 
12271
        RST     20H             ; NEXT-CHAR advances to bracket
12272
 
12273
;; SF-ARGMT1
12274
L2802:  RST     20H             ; NEXT-CHAR advances to start of argument
12275
        PUSH    HL              ; save address
12276
        LD      HL,($5C53)      ; fetch start of program area from PROG
12277
        DEC     HL              ; the search starting point is the previous
12278
                                ; location.
12279
 
12280
;; SF-FND-DF
12281
L2808:  LD      DE,$00CE        ; search is for token 'DEF FN' in E,
12282
                                ; statement count in D.
12283
        PUSH    BC              ; save C the string test, and B the letter.
12284
        CALL    L1D86           ; routine LOOK-PROG will search for token.
12285
        POP     BC              ; restore BC.
12286
        JR      NC,L2814        ; forward to SF-CP-DEF if a match was found.
12287
 
12288
 
12289
;; REPORT-P
12290
L2812:  RST     08H             ; ERROR-1
12291
        DB    $18             ; Error Report: FN without DEF
12292
 
12293
;; SF-CP-DEF
12294
L2814:  PUSH    HL              ; save address of DEF FN
12295
        CALL    L28AB           ; routine FN-SKPOVR skips over white-space etc.
12296
                                ; without disturbing CH-ADD.
12297
        AND     $DF             ; make fetched character upper-case.
12298
        CP      B               ; compare with FN name
12299
        JR      NZ,L2825        ; forward to SF-NOT-FD if no match.
12300
 
12301
; the letters match so test the type.
12302
 
12303
        CALL    L28AB           ; routine FN-SKPOVR skips white-space
12304
        SUB     $24             ; subtract '$' from fetched character
12305
        CP      C               ; compare with saved result of same operation
12306
                                ; on FN name.
12307
        JR      Z,L2831         ; forward to SF-VALUES with a match.
12308
 
12309
; the letters matched but one was string and the other numeric.
12310
 
12311
;; SF-NOT-FD
12312
L2825:  POP     HL              ; restore search point.
12313
        DEC     HL              ; make location before
12314
        LD      DE,$0200        ; the search is to be for the end of the
12315
                                ; current definition - 2 statements forward.
12316
        PUSH    BC              ; save the letter/type
12317
        CALL    L198B           ; routine EACH-STMT steps past rejected
12318
                                ; definition.
12319
        POP     BC              ; restore letter/type
12320
        JR      L2808           ; back to SF-FND-DF to continue search
12321
 
12322
; ---
12323
 
12324
; Success!
12325
; the branch was here with matching letter and numeric/string type.
12326
 
12327
;; SF-VALUES
12328
L2831:  AND     A               ; test A ( will be zero if string '$' - '$' )
12329
 
12330
        CALL    Z,L28AB         ; routine FN-SKPOVR advances HL past '$'.
12331
 
12332
        POP     DE              ; discard pointer to 'DEF FN'.
12333
        POP     DE              ; restore pointer to first FN argument.
12334
        LD      ($5C5D),DE      ; save in CH_ADD
12335
 
12336
        CALL    L28AB           ; routine FN-SKPOVR advances HL past '('
12337
        PUSH    HL              ; save start address in DEF FN  ***
12338
        CP      $29             ; is character a ')' ?
12339
        JR      Z,L2885         ; forward to SF-R-BR-2 if no arguments.
12340
 
12341
;; SF-ARG-LP
12342
L2843:  INC     HL              ; point to next character.
12343
        LD      A,(HL)          ; fetch it.
12344
        CP      $0E             ; is it the number marker
12345
        LD      D,$40           ; signal numeric in D.
12346
        JR      Z,L2852         ; forward to SF-ARG-VL if numeric.
12347
 
12348
        DEC     HL              ; back to letter
12349
        CALL    L28AB           ; routine FN-SKPOVR skips any white-space
12350
        INC     HL              ; advance past the expected '$' to 
12351
                                ; the 'hidden' marker.
12352
        LD      D,$00           ; signal string.
12353
 
12354
;; SF-ARG-VL
12355
L2852:  INC     HL              ; now address first of 5-byte location.
12356
        PUSH    HL              ; save address in DEF FN statement
12357
        PUSH    DE              ; save D - result type
12358
 
12359
        CALL    L24FB           ; routine SCANNING evaluates expression in
12360
                                ; the FN statement setting FLAGS and leaving
12361
                                ; result as last value on calculator stack.
12362
 
12363
        POP     AF              ; restore saved result type to A
12364
 
12365
        XOR     (IY+$01)        ; xor with FLAGS
12366
        AND     $40             ; and with 01000000 to test bit 6
12367
        JR      NZ,L288B        ; forward to REPORT-Q if type mismatch.
12368
                                ; 'Parameter error'
12369
 
12370
        POP     HL              ; pop the start address in DEF FN statement
12371
        EX      DE,HL           ; transfer to DE ?? pop straight into de ?
12372
 
12373
        LD      HL,($5C65)      ; set HL to STKEND location after value
12374
        LD      BC,$0005        ; five bytes to move
12375
        SBC     HL,BC           ; decrease HL by 5 to point to start.
12376
        LD      ($5C65),HL      ; set STKEND 'removing' value from stack.
12377
 
12378
        LDIR                    ; copy value into DEF FN statement
12379
        EX      DE,HL           ; set HL to location after value in DEF FN
12380
        DEC     HL              ; step back one
12381
        CALL    L28AB           ; routine FN-SKPOVR gets next valid character
12382
        CP      $29             ; is it ')' end of arguments ?
12383
        JR      Z,L2885         ; forward to SF-R-BR-2 if so.
12384
 
12385
; a comma separator has been encountered in the DEF FN argument list.
12386
 
12387
        PUSH    HL              ; save position in DEF FN statement
12388
 
12389
        RST     18H             ; GET-CHAR from FN statement
12390
        CP      $2C             ; is it ',' ?
12391
        JR      NZ,L288B        ; forward to REPORT-Q if not
12392
                                ; 'Parameter error'
12393
 
12394
        RST     20H             ; NEXT-CHAR in FN statement advances to next
12395
                                ; argument.
12396
 
12397
        POP     HL              ; restore DEF FN pointer
12398
        CALL    L28AB           ; routine FN-SKPOVR advances to corresponding
12399
                                ; argument.
12400
 
12401
        JR      L2843           ; back to SF-ARG-LP looping until all
12402
                                ; arguments are passed into the DEF FN
12403
                                ; hidden locations.
12404
 
12405
; ---
12406
 
12407
; the branch was here when all arguments passed.
12408
 
12409
;; SF-R-BR-2
12410
L2885:  PUSH    HL              ; save location of ')' in DEF FN
12411
 
12412
        RST     18H             ; GET-CHAR gets next character in FN
12413
        CP      $29             ; is it a ')' also ?
12414
        JR      Z,L288D         ; forward to SF-VALUE if so.
12415
 
12416
 
12417
;; REPORT-Q
12418
L288B:  RST     08H             ; ERROR-1
12419
        DB    $19             ; Error Report: Parameter error
12420
 
12421
;; SF-VALUE
12422
L288D:  POP     DE              ; location of ')' in DEF FN to DE.
12423
        EX      DE,HL           ; now to HL, FN ')' pointer to DE.
12424
        LD      ($5C5D),HL      ; initialize CH_ADD to this value.
12425
 
12426
; At this point the start of the DEF FN argument list is on the machine stack.
12427
; We also have to consider that this defined function may form part of the
12428
; definition of another defined function (though not itself).
12429
; As this defined function may be part of a hierarchy of defined functions
12430
; currently being evaluated by recursive calls to SCANNING, then we have to
12431
; preserve the original value of DEFADD and not assume that it is zero.
12432
 
12433
        LD      HL,($5C0B)      ; get original DEFADD address
12434
        EX      (SP),HL         ; swap with DEF FN address on stack ***
12435
        LD      ($5C0B),HL      ; set DEFADD to point to this argument list
12436
                                ; during scanning.
12437
 
12438
        PUSH    DE              ; save FN ')' pointer.
12439
 
12440
        RST     20H             ; NEXT-CHAR advances past ')' in define
12441
 
12442
        RST     20H             ; NEXT-CHAR advances past '=' to expression
12443
 
12444
        CALL    L24FB           ; routine SCANNING evaluates but searches
12445
                                ; initially for variables at DEFADD
12446
 
12447
        POP     HL              ; pop the FN ')' pointer
12448
        LD      ($5C5D),HL      ; set CH_ADD to this
12449
        POP     HL              ; pop the original DEFADD value
12450
        LD      ($5C0B),HL      ; and re-insert into DEFADD system variable.
12451
 
12452
        RST     20H             ; NEXT-CHAR advances to character after ')'
12453
        JP      L2712           ; to S-CONT-2 - to continue current
12454
                                ; invocation of scanning
12455
 
12456
; --------------------
12457
; Used to parse DEF FN
12458
; --------------------
12459
; e.g. DEF FN     s $ ( x )     =  b     $ (  TO  x  ) : REM exaggerated
12460
;
12461
; This routine is used 10 times to advance along a DEF FN statement
12462
; skipping spaces and colour control codes. It is similar to NEXT-CHAR
12463
; which is, at the same time, used to skip along the corresponding FN function
12464
; except the latter has to deal with AT and TAB characters in string
12465
; expressions. These cannot occur in a program area so this routine is
12466
; simpler as both colour controls and their parameters are less than space.
12467
 
12468
;; FN-SKPOVR
12469
L28AB:  INC     HL              ; increase pointer
12470
        LD      A,(HL)          ; fetch addressed character
12471
        CP      $21             ; compare with space + 1
12472
        JR      C,L28AB         ; back to FN-SKPOVR if less
12473
 
12474
        RET                     ; return pointing to a valid character.
12475
 
12476
; ---------
12477
; LOOK-VARS
12478
; ---------
12479
;
12480
;
12481
 
12482
;; LOOK-VARS
12483
L28B2:  SET     6,(IY+$01)      ; update FLAGS - presume numeric result
12484
 
12485
        RST     18H             ; GET-CHAR
12486
        CALL    L2C8D           ; routine ALPHA tests for A-Za-z
12487
        JP      NC,L1C8A        ; jump to REPORT-C if not.
12488
                                ; 'Nonsense in BASIC'
12489
 
12490
        PUSH    HL              ; save pointer to first letter       ^1
12491
        AND     $1F             ; mask lower bits, 1 - 26 decimal     000xxxxx
12492
        LD      C,A             ; store in C.
12493
 
12494
        RST     20H             ; NEXT-CHAR
12495
        PUSH    HL              ; save pointer to second character   ^2
12496
        CP      $28             ; is it '(' - an array ?
12497
        JR      Z,L28EF         ; forward to V-RUN/SYN if so.
12498
 
12499
        SET     6,C             ; set 6 signaling string if solitary  010
12500
        CP      $24             ; is character a '$' ?
12501
        JR      Z,L28DE         ; forward to V-STR-VAR
12502
 
12503
        SET     5,C             ; signal numeric                       011
12504
        CALL    L2C88           ; routine ALPHANUM sets carry if second
12505
                                ; character is alphanumeric.
12506
        JR      NC,L28E3        ; forward to V-TEST-FN if just one character
12507
 
12508
; it is more than one character but re-test current character so that 6 reset
12509
; Note. this is a rare lack of elegance. Bit 6 could be reset once before
12510
; entering the loop. Another puzzle is that this loop renders the similar
12511
; loop at V-PASS redundant.
12512
 
12513
;; V-CHAR
12514
L28D4:  CALL    L2C88           ; routine ALPHANUM
12515
        JR      NC,L28EF        ; to V-RUN/SYN when no more
12516
 
12517
        RES     6,C             ; make long named type                 001
12518
 
12519
        RST     20H             ; NEXT-CHAR
12520
        JR      L28D4           ; loop back to V-CHAR
12521
 
12522
; ---
12523
 
12524
 
12525
;; V-STR-VAR
12526
L28DE:  RST     20H             ; NEXT-CHAR advances past '$'
12527
        RES     6,(IY+$01)      ; update FLAGS - signal string result.
12528
 
12529
;; V-TEST-FN
12530
L28E3:  LD      A,($5C0C)       ; load A with DEFADD_hi
12531
        AND     A               ; and test for zero.
12532
        JR      Z,L28EF         ; forward to V-RUN/SYN if a defined function
12533
                                ; is not being evaluated.
12534
 
12535
; Note.
12536
 
12537
        CALL    L2530           ; routine SYNTAX-Z
12538
        JP      NZ,L2951        ; JUMP to STK-F-ARG in runtime and then
12539
                                ; back to this point if no variable found.
12540
 
12541
;; V-RUN/SYN
12542
L28EF:  LD      B,C             ; save flags in B
12543
        CALL    L2530           ; routine SYNTAX-Z
12544
        JR      NZ,L28FD        ; to V-RUN to look for the variable in runtime
12545
 
12546
; if checking syntax the letter is not returned
12547
 
12548
        LD      A,C             ; copy letter/flags to A
12549
        AND     $E0             ; and with 11100000 to get rid of the letter
12550
        SET     7,A             ; use spare bit to signal checking syntax.
12551
        LD      C,A             ; and transfer to C.
12552
        JR      L2934           ; forward to V-SYNTAX
12553
 
12554
; ---
12555
 
12556
; but in runtime search for the variable.
12557
 
12558
;; V-RUN
12559
L28FD:  LD      HL,($5C4B)      ; set HL to start of variables from VARS
12560
 
12561
;; V-EACH
12562
L2900:  LD      A,(HL)          ; get first character
12563
        AND     $7F             ; and with 01111111
12564
                                ; ignoring bit 7 which distinguishes
12565
                                ; arrays or for/next variables.
12566
 
12567
        JR      Z,L2932         ; to V-80-BYTE if zero as must be 10000000
12568
                                ; the variables end-marker.
12569
 
12570
        CP      C               ; compare with supplied value.
12571
        JR      NZ,L292A        ; forward to V-NEXT if no match.
12572
 
12573
        RLA                     ; destructively test
12574
        ADD     A,A             ; bits 5 and 6 of A
12575
                                ; jumping if bit 5 reset or 6 set
12576
 
12577
        JP      P,L293F         ; to V-FOUND-2  strings and arrays
12578
 
12579
        JR      C,L293F         ; to V-FOUND-2  simple and for next
12580
 
12581
; leaving long name variables.
12582
 
12583
        POP     DE              ; pop pointer to 2nd. char
12584
        PUSH    DE              ; save it again
12585
        PUSH    HL              ; save variable first character pointer
12586
 
12587
;; V-MATCHES
12588
L2912:  INC     HL              ; address next character in vars area
12589
 
12590
;; V-SPACES
12591
L2913:  LD      A,(DE)          ; pick up letter from prog area
12592
        INC     DE              ; and advance address
12593
        CP      $20             ; is it a space
12594
        JR      Z,L2913         ; back to V-SPACES until non-space
12595
 
12596
        OR      $20             ; convert to range 1 - 26.
12597
        CP      (HL)            ; compare with addressed variables character
12598
        JR      Z,L2912         ; loop back to V-MATCHES if a match on an
12599
                                ; intermediate letter.
12600
 
12601
        OR      $80             ; now set bit 7 as last character of long
12602
                                ; names are inverted.
12603
        CP      (HL)            ; compare again
12604
        JR      NZ,L2929        ; forward to V-GET-PTR if no match
12605
 
12606
; but if they match check that this is also last letter in prog area
12607
 
12608
        LD      A,(DE)          ; fetch next character
12609
        CALL    L2C88           ; routine ALPHANUM sets carry if not alphanum
12610
        JR      NC,L293E        ; forward to V-FOUND-1 with a full match.
12611
 
12612
;; V-GET-PTR
12613
L2929:  POP     HL              ; pop saved pointer to char 1
12614
 
12615
;; V-NEXT
12616
L292A:  PUSH    BC              ; save flags
12617
        CALL    L19B8           ; routine NEXT-ONE gets next variable in DE
12618
        EX      DE,HL           ; transfer to HL.
12619
        POP     BC              ; restore the flags
12620
        JR      L2900           ; loop back to V-EACH
12621
                                ; to compare each variable
12622
 
12623
; ---
12624
 
12625
;; V-80-BYTE
12626
L2932:  SET     7,B             ; will signal not found
12627
 
12628
; the branch was here when checking syntax
12629
 
12630
;; V-SYNTAX
12631
L2934:  POP     DE              ; discard the pointer to 2nd. character  v2
12632
                                ; in BASIC line/workspace.
12633
 
12634
        RST     18H             ; GET-CHAR gets character after variable name.
12635
        CP      $28             ; is it '(' ?
12636
        JR      Z,L2943         ; forward to V-PASS
12637
                                ; Note. could go straight to V-END ?
12638
 
12639
        SET     5,B             ; signal not an array
12640
        JR      L294B           ; forward to V-END
12641
 
12642
; ---------------------------
12643
 
12644
; the jump was here when a long name matched and HL pointing to last character
12645
; in variables area.
12646
 
12647
;; V-FOUND-1
12648
L293E:  POP     DE              ; discard pointer to first var letter
12649
 
12650
; the jump was here with all other matches HL points to first var char.
12651
 
12652
;; V-FOUND-2
12653
L293F:  POP     DE              ; discard pointer to 2nd prog char       v2
12654
        POP     DE              ; drop pointer to 1st prog char          v1
12655
        PUSH    HL              ; save pointer to last char in vars
12656
 
12657
        RST     18H             ; GET-CHAR
12658
 
12659
;; V-PASS
12660
L2943:  CALL    L2C88           ; routine ALPHANUM
12661
        JR      NC,L294B        ; forward to V-END if not
12662
 
12663
; but it never will be as we advanced past long-named variables earlier.
12664
 
12665
        RST     20H             ; NEXT-CHAR
12666
        JR      L2943           ; back to V-PASS
12667
 
12668
; ---
12669
 
12670
;; V-END
12671
L294B:  POP     HL              ; pop the pointer to first character in
12672
                                ; BASIC line/workspace.
12673
        RL      B               ; rotate the B register left
12674
                                ; bit 7 to carry
12675
        BIT     6,B             ; test the array indicator bit.
12676
        RET                     ; return
12677
 
12678
; -----------------------
12679
; Stack function argument
12680
; -----------------------
12681
; This branch is taken from LOOK-VARS when a defined function is currently
12682
; being evaluated.
12683
; Scanning is evaluating the expression after the '=' and the variable
12684
; found could be in the argument list to the left of the '=' or in the
12685
; normal place after the program. Preference will be given to the former.
12686
; The variable name to be matched is in C.
12687
 
12688
;; STK-F-ARG
12689
L2951:  LD      HL,($5C0B)      ; set HL to DEFADD
12690
        LD      A,(HL)          ; load the first character
12691
        CP      $29             ; is it ')' ?
12692
        JP      Z,L28EF         ; JUMP back to V-RUN/SYN, if so, as there are
12693
                                ; no arguments.
12694
 
12695
; but proceed to search argument list of defined function first if not empty.
12696
 
12697
;; SFA-LOOP
12698
L295A:  LD      A,(HL)          ; fetch character again.
12699
        OR      $60             ; or with 01100000 presume a simple variable.
12700
        LD      B,A             ; save result in B.
12701
        INC     HL              ; address next location.
12702
        LD      A,(HL)          ; pick up byte.
12703
        CP      $0E             ; is it the number marker ?
12704
        JR      Z,L296B         ; forward to SFA-CP-VR if so.
12705
 
12706
; it was a string. White-space may be present but syntax has been checked.
12707
 
12708
        DEC     HL              ; point back to letter.
12709
        CALL    L28AB           ; routine FN-SKPOVR skips to the '$'
12710
        INC     HL              ; now address the hidden marker.
12711
        RES     5,B             ; signal a string variable.
12712
 
12713
;; SFA-CP-VR
12714
L296B:  LD      A,B             ; transfer found variable letter to A.
12715
        CP      C               ; compare with expected.
12716
        JR      Z,L2981         ; forward to SFA-MATCH with a match.
12717
 
12718
        INC     HL              ; step
12719
        INC     HL              ; past
12720
        INC     HL              ; the
12721
        INC     HL              ; five
12722
        INC     HL              ; bytes.
12723
 
12724
        CALL    L28AB           ; routine FN-SKPOVR skips to next character
12725
        CP      $29             ; is it ')' ?
12726
        JP      Z,L28EF         ; jump back if so to V-RUN/SYN to look in
12727
                                ; normal variables area.
12728
 
12729
        CALL    L28AB           ; routine FN-SKPOVR skips past the ','
12730
                                ; all syntax has been checked and these
12731
                                ; things can be taken as read.
12732
        JR      L295A           ; back to SFA-LOOP while there are more
12733
                                ; arguments.
12734
 
12735
; ---
12736
 
12737
;; SFA-MATCH
12738
L2981:  BIT     5,C             ; test if numeric
12739
        JR      NZ,L2991        ; to SFA-END if so as will be stacked
12740
                                ; by scanning
12741
 
12742
        INC     HL              ; point to start of string descriptor
12743
        LD      DE,($5C65)      ; set DE to STKEND
12744
        CALL    L33C0           ; routine MOVE-FP puts parameters on stack.
12745
        EX      DE,HL           ; new free location to HL.
12746
        LD      ($5C65),HL      ; use it to set STKEND system variable.
12747
 
12748
;; SFA-END
12749
L2991:  POP     DE              ; discard
12750
        POP     DE              ; pointers.
12751
        XOR     A               ; clear carry flag.
12752
        INC     A               ; and zero flag.
12753
        RET                     ; return.
12754
 
12755
; ------------------------
12756
; Stack variable component
12757
; ------------------------
12758
; This is called to evaluate a complex structure that has been found, in
12759
; runtime, by LOOK-VARS in the variables area.
12760
; In this case HL points to the initial letter, bits 7-5
12761
; of which indicate the type of variable.
12762
; 010 - simple string, 110 - string array, 100 - array of numbers.
12763
;
12764
; It is called from CLASS-01 when assigning to a string or array including
12765
; a slice.
12766
; It is called from SCANNING to isolate the required part of the structure.
12767
;
12768
; An important part of the runtime process is to check that the number of
12769
; dimensions of the variable match the number of subscripts supplied in the
12770
; BASIC line.
12771
;
12772
; If checking syntax,
12773
; the B register, which counts dimensions is set to zero (256) to allow
12774
; the loop to continue till all subscripts are checked. While doing this it
12775
; is reading dimension sizes from some arbitrary area of memory. Although
12776
; these are meaningless it is of no concern as the limit is never checked by
12777
; int-exp during syntax checking.
12778
;
12779
; The routine is also called from the syntax path of DIM command to check the
12780
; syntax of both string and numeric arrays definitions except that bit 6 of C
12781
; is reset so both are checked as numeric arrays. This ruse avoids a terminal
12782
; slice being accepted as part of the DIM command.
12783
; All that is being checked is that there are a valid set of comma-separated
12784
; expressions before a terminal ')', although, as above, it will still go
12785
; through the motions of checking dummy dimension sizes.
12786
 
12787
;; STK-VAR
12788
L2996:  XOR     A               ; clear A
12789
        LD      B,A             ; and B, the syntax dimension counter (256)
12790
        BIT     7,C             ; checking syntax ?
12791
        JR      NZ,L29E7        ; forward to SV-COUNT if so.
12792
 
12793
; runtime evaluation.
12794
 
12795
        BIT     7,(HL)          ; will be reset if a simple string.
12796
        JR      NZ,L29AE        ; forward to SV-ARRAYS otherwise
12797
 
12798
        INC     A               ; set A to 1, simple string.
12799
 
12800
;; SV-SIMPLE$
12801
L29A1:  INC     HL              ; address length low
12802
        LD      C,(HL)          ; place in C
12803
        INC     HL              ; address length high
12804
        LD      B,(HL)          ; place in B
12805
        INC     HL              ; address start of string
12806
        EX      DE,HL           ; DE = start now.
12807
        CALL    L2AB2           ; routine STK-STO-$ stacks string parameters
12808
                                ; DE start in variables area,
12809
                                ; BC length, A=1 simple string
12810
 
12811
; the only thing now is to consider if a slice is required.
12812
 
12813
        RST     18H             ; GET-CHAR puts character at CH_ADD in A
12814
        JP      L2A49           ; jump forward to SV-SLICE? to test for '('
12815
 
12816
; --------------------------------------------------------
12817
 
12818
; the branch was here with string and numeric arrays in runtime.
12819
 
12820
;; SV-ARRAYS
12821
L29AE:  INC     HL              ; step past
12822
        INC     HL              ; the total length
12823
        INC     HL              ; to address Number of dimensions.
12824
        LD      B,(HL)          ; transfer to B overwriting zero.
12825
        BIT     6,C             ; a numeric array ?
12826
        JR      Z,L29C0         ; forward to SV-PTR with numeric arrays
12827
 
12828
        DEC     B               ; ignore the final element of a string array
12829
                                ; the fixed string size.
12830
 
12831
        JR      Z,L29A1         ; back to SV-SIMPLE$ if result is zero as has
12832
                                ; been created with DIM a$(10) for instance
12833
                                ; and can be treated as a simple string.
12834
 
12835
; proceed with multi-dimensioned string arrays in runtime.
12836
 
12837
        EX      DE,HL           ; save pointer to dimensions in DE
12838
 
12839
        RST     18H             ; GET-CHAR looks at the BASIC line
12840
        CP      $28             ; is character '(' ?
12841
        JR      NZ,L2A20        ; to REPORT-3 if not
12842
                                ; 'Subscript wrong'
12843
 
12844
        EX      DE,HL           ; dimensions pointer to HL to synchronize
12845
                                ; with next instruction.
12846
 
12847
; runtime numeric arrays path rejoins here.
12848
 
12849
;; SV-PTR
12850
L29C0:  EX      DE,HL           ; save dimension pointer in DE
12851
        JR      L29E7           ; forward to SV-COUNT with true no of dims 
12852
                                ; in B. As there is no initial comma the 
12853
                                ; loop is entered at the midpoint.
12854
 
12855
; ----------------------------------------------------------
12856
; the dimension counting loop which is entered at mid-point.
12857
 
12858
;; SV-COMMA
12859
L29C3:  PUSH    HL              ; save counter
12860
 
12861
        RST     18H             ; GET-CHAR
12862
 
12863
        POP     HL              ; pop counter
12864
        CP      $2C             ; is character ',' ?
12865
        JR      Z,L29EA         ; forward to SV-LOOP if so
12866
 
12867
; in runtime the variable definition indicates a comma should appear here
12868
 
12869
        BIT     7,C             ; checking syntax ?
12870
        JR      Z,L2A20         ; forward to REPORT-3 if not
12871
                                ; 'Subscript error'
12872
 
12873
; proceed if checking syntax of an array?
12874
 
12875
        BIT     6,C             ; array of strings
12876
        JR      NZ,L29D8        ; forward to SV-CLOSE if so
12877
 
12878
; an array of numbers.
12879
 
12880
        CP      $29             ; is character ')' ?
12881
        JR      NZ,L2A12        ; forward to SV-RPT-C if not
12882
                                ; 'Nonsense in BASIC'
12883
 
12884
        RST     20H             ; NEXT-CHAR moves CH-ADD past the statement
12885
        RET                     ; return ->
12886
 
12887
; ---
12888
 
12889
; the branch was here with an array of strings.
12890
 
12891
;; SV-CLOSE
12892
L29D8:  CP      $29             ; as above ')' could follow the expression
12893
        JR      Z,L2A48         ; forward to SV-DIM if so
12894
 
12895
        CP      $CC             ; is it 'TO' ?
12896
        JR      NZ,L2A12        ; to SV-RPT-C with anything else
12897
                                ; 'Nonsense in BASIC'
12898
 
12899
; now backtrack CH_ADD to set up for slicing routine.
12900
; Note. in a BASIC line we can safely backtrack to a colour parameter.
12901
 
12902
;; SV-CH-ADD
12903
L29E0:  RST     18H             ; GET-CHAR
12904
        DEC     HL              ; backtrack HL
12905
        LD      ($5C5D),HL      ; to set CH_ADD up for slicing routine
12906
        JR      L2A45           ; forward to SV-SLICE and make a return
12907
                                ; when all slicing complete.
12908
 
12909
; ----------------------------------------
12910
; -> the mid-point entry point of the loop
12911
 
12912
;; SV-COUNT
12913
L29E7:  LD      HL,$0000        ; initialize data pointer to zero.
12914
 
12915
;; SV-LOOP
12916
L29EA:  PUSH    HL              ; save the data pointer.
12917
 
12918
        RST     20H             ; NEXT-CHAR in BASIC area points to an
12919
                                ; expression.
12920
 
12921
        POP     HL              ; restore the data pointer.
12922
        LD      A,C             ; transfer name/type to A.
12923
        CP      $C0             ; is it 11000000 ?
12924
                                ; Note. the letter component is absent if
12925
                                ; syntax checking.
12926
        JR      NZ,L29FB        ; forward to SV-MULT if not an array of
12927
                                ; strings.
12928
 
12929
; proceed to check string arrays during syntax.
12930
 
12931
        RST     18H             ; GET-CHAR
12932
        CP      $29             ; ')'  end of subscripts ?
12933
        JR      Z,L2A48         ; forward to SV-DIM to consider further slice
12934
 
12935
        CP      $CC             ; is it 'TO' ?
12936
        JR      Z,L29E0         ; back to SV-CH-ADD to consider a slice.
12937
                                ; (no need to repeat get-char at L29E0)
12938
 
12939
; if neither, then an expression is required so rejoin runtime loop ??
12940
; registers HL and DE only point to somewhere meaningful in runtime so 
12941
; comments apply to that situation.
12942
 
12943
;; SV-MULT
12944
L29FB:  PUSH    BC              ; save dimension number.
12945
        PUSH    HL              ; push data pointer/rubbish.
12946
                                ; DE points to current dimension.
12947
        CALL    L2AEE           ; routine DE,(DE+1) gets next dimension in DE
12948
                                ; and HL points to it.
12949
        EX      (SP),HL         ; dim pointer to stack, data pointer to HL (*)
12950
        EX      DE,HL           ; data pointer to DE, dim size to HL.
12951
 
12952
        CALL    L2ACC           ; routine INT-EXP1 checks integer expression
12953
                                ; and gets result in BC in runtime.
12954
        JR      C,L2A20         ; to REPORT-3 if > HL
12955
                                ; 'Subscript out of range'
12956
 
12957
        DEC     BC              ; adjust returned result from 1-x to 0-x
12958
        CALL    L2AF4           ; routine GET-HL*DE multiplies data pointer by
12959
                                ; dimension size.
12960
        ADD     HL,BC           ; add the integer returned by expression.
12961
        POP     DE              ; pop the dimension pointer.                              ***
12962
        POP     BC              ; pop dimension counter.
12963
        DJNZ    L29C3           ; back to SV-COMMA if more dimensions
12964
                                ; Note. during syntax checking, unless there
12965
                                ; are more than 256 subscripts, the branch
12966
                                ; back to SV-COMMA is always taken.
12967
 
12968
        BIT     7,C             ; are we checking syntax ?
12969
                                ; then we've got a joker here.
12970
 
12971
;; SV-RPT-C
12972
L2A12:  JR      NZ,L2A7A        ; forward to SL-RPT-C if so
12973
                                ; 'Nonsense in BASIC'
12974
                                ; more than 256 subscripts in BASIC line.
12975
 
12976
; but in runtime the number of subscripts are at least the same as dims
12977
 
12978
        PUSH    HL              ; save data pointer.
12979
        BIT     6,C             ; is it a string array ?
12980
        JR      NZ,L2A2C        ; forward to SV-ELEM$ if so.
12981
 
12982
; a runtime numeric array subscript.
12983
 
12984
        LD      B,D             ; register DE has advanced past all dimensions
12985
        LD      C,E             ; and points to start of data in variable.
12986
                                ; transfer it to BC.
12987
 
12988
        RST     18H             ; GET-CHAR checks BASIC line
12989
        CP      $29             ; must be a ')' ?
12990
        JR      Z,L2A22         ; skip to SV-NUMBER if so
12991
 
12992
; else more subscripts in BASIC line than the variable definition.
12993
 
12994
;; REPORT-3
12995
L2A20:  RST     08H             ; ERROR-1
12996
        DB    $02             ; Error Report: Subscript wrong
12997
 
12998
; continue if subscripts matched the numeric array.
12999
 
13000
;; SV-NUMBER
13001
L2A22:  RST     20H             ; NEXT-CHAR moves CH_ADD to next statement
13002
                                ; - finished parsing.
13003
 
13004
        POP     HL              ; pop the data pointer.
13005
        LD      DE,$0005        ; each numeric element is 5 bytes.
13006
        CALL    L2AF4           ; routine GET-HL*DE multiplies.
13007
        ADD     HL,BC           ; now add to start of data in the variable.
13008
 
13009
        RET                     ; return with HL pointing at the numeric
13010
                                ; array subscript.                       ->
13011
 
13012
; ---------------------------------------------------------------
13013
 
13014
; the branch was here for string subscripts when the number of subscripts
13015
; in the BASIC line was one less than in variable definition.
13016
 
13017
;; SV-ELEM$
13018
L2A2C:  CALL    L2AEE           ; routine DE,(DE+1) gets final dimension
13019
                                ; the length of strings in this array.
13020
        EX      (SP),HL         ; start pointer to stack, data pointer to HL.
13021
        CALL    L2AF4           ; routine GET-HL*DE multiplies by element
13022
                                ; size.
13023
        POP     BC              ; the start of data pointer is added
13024
        ADD     HL,BC           ; in - now points to location before.
13025
        INC     HL              ; point to start of required string.
13026
        LD      B,D             ; transfer the length (final dimension size)
13027
        LD      C,E             ; from DE to BC.
13028
        EX      DE,HL           ; put start in DE.
13029
        CALL    L2AB1           ; routine STK-ST-0 stores the string parameters
13030
                                ; with A=0 - a slice or subscript.
13031
 
13032
; now check that there were no more subscripts in the BASIC line.
13033
 
13034
        RST     18H             ; GET-CHAR
13035
        CP      $29             ; is it ')' ?
13036
        JR      Z,L2A48         ; forward to SV-DIM to consider a separate
13037
                                ; subscript or/and a slice.
13038
 
13039
        CP      $2C             ; a comma is allowed if the final subscript
13040
                                ; is to be sliced e.g a$(2,3,4 TO 6).
13041
        JR      NZ,L2A20        ; to REPORT-3 with anything else
13042
                                ; 'Subscript error'
13043
 
13044
;; SV-SLICE
13045
L2A45:  CALL    L2A52           ; routine SLICING slices the string.
13046
 
13047
; but a slice of a simple string can itself be sliced.
13048
 
13049
;; SV-DIM
13050
L2A48:  RST     20H             ; NEXT-CHAR
13051
 
13052
;; SV-SLICE?
13053
L2A49:  CP      $28             ; is character '(' ?
13054
        JR      Z,L2A45         ; loop back if so to SV-SLICE
13055
 
13056
        RES     6,(IY+$01)      ; update FLAGS  - Signal string result
13057
        RET                     ; and return.
13058
 
13059
; ---
13060
 
13061
; The above section deals with the flexible syntax allowed.
13062
; DIM a$(3,3,10) can be considered as two dimensional array of ten-character
13063
; strings or a 3-dimensional array of characters.
13064
; a$(1,1) will return a 10-character string as will a$(1,1,1 TO 10)
13065
; a$(1,1,1) will return a single character.
13066
; a$(1,1) (1 TO 6) is the same as a$(1,1,1 TO 6)
13067
; A slice can itself be sliced ad infinitum
13068
; b$ () () () () () () (2 TO 10) (2 TO 9) (3) is the same as b$(5)
13069
 
13070
 
13071
 
13072
; -------------------------
13073
; Handle slicing of strings
13074
; -------------------------
13075
; The syntax of string slicing is very natural and it is as well to reflect
13076
; on the permutations possible.
13077
; a$() and a$( TO ) indicate the entire string although just a$ would do
13078
; and would avoid coming here.
13079
; h$(16) indicates the single character at position 16.
13080
; a$( TO 32) indicates the first 32 characters.
13081
; a$(257 TO) indicates all except the first 256 characters.
13082
; a$(19000 TO 19999) indicates the thousand characters at position 19000.
13083
; Also a$(9 TO 5) returns a null string not an error.
13084
; This enables a$(2 TO) to return a null string if the passed string is
13085
; of length zero or 1.
13086
; A string expression in brackets can be sliced. e.g. (STR$ PI) (3 TO )
13087
; We arrived here from SCANNING with CH-ADD pointing to the initial '('
13088
; or from above.
13089
 
13090
;; SLICING
13091
L2A52:  CALL    L2530           ; routine SYNTAX-Z
13092
        CALL    NZ,L2BF1        ; routine STK-FETCH fetches parameters of
13093
                                ; string at runtime, start in DE, length 
13094
                                ; in BC. This could be an array subscript.
13095
 
13096
        RST     20H             ; NEXT-CHAR
13097
        CP      $29             ; is it ')' ?     e.g. a$()
13098
        JR      Z,L2AAD         ; forward to SL-STORE to store entire string.
13099
 
13100
        PUSH    DE              ; else save start address of string
13101
 
13102
        XOR     A               ; clear accumulator to use as a running flag.
13103
        PUSH    AF              ; and save on stack before any branching.
13104
 
13105
        PUSH    BC              ; save length of string to be sliced.
13106
        LD      DE,$0001        ; default the start point to position 1.
13107
 
13108
        RST     18H             ; GET-CHAR
13109
 
13110
        POP     HL              ; pop length to HL as default end point
13111
                                ; and limit.
13112
 
13113
        CP      $CC             ; is it 'TO' ?    e.g. a$( TO 10000)
13114
        JR      Z,L2A81         ; to SL-SECOND to evaluate second parameter.
13115
 
13116
        POP     AF              ; pop the running flag.
13117
 
13118
        CALL    L2ACD           ; routine INT-EXP2 fetches first parameter.
13119
 
13120
        PUSH    AF              ; save flag (will be $FF if parameter>limit)
13121
 
13122
        LD      D,B             ; transfer the start
13123
        LD      E,C             ; to DE overwriting 0001.
13124
        PUSH    HL              ; save original length.
13125
 
13126
        RST     18H             ; GET-CHAR
13127
        POP     HL              ; pop the limit length.
13128
        CP      $CC             ; is it 'TO' after a start ?
13129
        JR      Z,L2A81         ; to SL-SECOND to evaluate second parameter
13130
 
13131
        CP      $29             ; is it ')' ?       e.g. a$(365)
13132
 
13133
;; SL-RPT-C
13134
L2A7A:  JP      NZ,L1C8A        ; jump to REPORT-C with anything else
13135
                                ; 'Nonsense in BASIC'
13136
 
13137
        LD      H,D             ; copy start
13138
        LD      L,E             ; to end - just a one character slice.
13139
        JR      L2A94           ; forward to SL-DEFINE.
13140
 
13141
; ---------------------
13142
 
13143
;; SL-SECOND
13144
L2A81:  PUSH    HL              ; save limit length.
13145
 
13146
        RST     20H             ; NEXT-CHAR
13147
 
13148
        POP     HL              ; pop the length.
13149
 
13150
        CP      $29             ; is character ')' ?        e.g a$(7 TO )
13151
        JR      Z,L2A94         ; to SL-DEFINE using length as end point.
13152
 
13153
        POP     AF              ; else restore flag.
13154
        CALL    L2ACD           ; routine INT-EXP2 gets second expression.
13155
 
13156
        PUSH    AF              ; save the running flag.
13157
 
13158
        RST     18H             ; GET-CHAR
13159
 
13160
        LD      H,B             ; transfer second parameter
13161
        LD      L,C             ; to HL.              e.g. a$(42 to 99)
13162
        CP      $29             ; is character a ')' ?
13163
        JR      NZ,L2A7A        ; to SL-RPT-C if not
13164
                                ; 'Nonsense in BASIC'
13165
 
13166
; we now have start in DE and an end in HL.
13167
 
13168
;; SL-DEFINE
13169
L2A94:  POP     AF              ; pop the running flag.
13170
        EX      (SP),HL         ; put end point on stack, start address to HL
13171
        ADD     HL,DE           ; add address of string to the start point.
13172
        DEC     HL              ; point to first character of slice.
13173
        EX      (SP),HL         ; start address to stack, end point to HL (*)
13174
        AND     A               ; prepare to subtract.
13175
        SBC     HL,DE           ; subtract start point from end point.
13176
        LD      BC,$0000        ; default the length result to zero.
13177
        JR      C,L2AA8         ; forward to SL-OVER if start > end.
13178
 
13179
        INC     HL              ; increment the length for inclusive byte.
13180
 
13181
        AND     A               ; now test the running flag.
13182
        JP      M,L2A20         ; jump back to REPORT-3 if $FF.
13183
                                ; 'Subscript out of range'
13184
 
13185
        LD      B,H             ; transfer the length
13186
        LD      C,L             ; to BC.
13187
 
13188
;; SL-OVER
13189
L2AA8:  POP     DE              ; restore start address from machine stack ***
13190
        RES     6,(IY+$01)      ; update FLAGS - signal string result for
13191
                                ; syntax.
13192
 
13193
;; SL-STORE
13194
L2AAD:  CALL    L2530           ; routine SYNTAX-Z  (UNSTACK-Z?)
13195
        RET     Z               ; return if checking syntax.
13196
                                ; but continue to store the string in runtime.
13197
 
13198
; ------------------------------------
13199
; other than from above, this routine is called from STK-VAR to stack
13200
; a known string array element.
13201
; ------------------------------------
13202
 
13203
;; STK-ST-0
13204
L2AB1:  XOR     A               ; clear to signal a sliced string or element.
13205
 
13206
; -------------------------
13207
; this routine is called from chr$, scrn$ etc. to store a simple string result.
13208
; --------------------------
13209
 
13210
;; STK-STO-$
13211
L2AB2:  RES     6,(IY+$01)      ; update FLAGS - signal string result.
13212
                                ; and continue to store parameters of string.
13213
 
13214
; ---------------------------------------
13215
; Pass five registers to calculator stack
13216
; ---------------------------------------
13217
; This subroutine puts five registers on the calculator stack.
13218
 
13219
;; STK-STORE
13220
L2AB6:  PUSH    BC              ; save two registers
13221
        CALL    L33A9           ; routine TEST-5-SP checks room and puts 5 
13222
                                ; in BC.
13223
        POP     BC              ; fetch the saved registers.
13224
        LD      HL,($5C65)      ; make HL point to first empty location STKEND
13225
        LD      (HL),A          ; place the 5 registers.
13226
        INC     HL              ;
13227
        LD      (HL),E          ;
13228
        INC     HL              ;
13229
        LD      (HL),D          ;
13230
        INC     HL              ;
13231
        LD      (HL),C          ;
13232
        INC     HL              ;
13233
        LD      (HL),B          ;
13234
        INC     HL              ;
13235
        LD      ($5C65),HL      ; update system variable STKEND.
13236
        RET                     ; and return.
13237
 
13238
; -------------------------------------------
13239
; Return result of evaluating next expression
13240
; -------------------------------------------
13241
; This clever routine is used to check and evaluate an integer expression
13242
; which is returned in BC, setting A to $FF, if greater than a limit supplied
13243
; in HL. It is used to check array subscripts, parameters of a string slice
13244
; and the arguments of the DIM command. In the latter case, the limit check
13245
; is not required and H is set to $FF. When checking optional string slice
13246
; parameters, it is entered at the second entry point so as not to disturb
13247
; the running flag A, which may be $00 or $FF from a previous invocation.
13248
 
13249
;; INT-EXP1
13250
L2ACC:  XOR     A               ; set result flag to zero.
13251
 
13252
; -> The entry point is here if A is used as a running flag.
13253
 
13254
;; INT-EXP2
13255
L2ACD:  PUSH    DE              ; preserve DE register throughout.
13256
        PUSH    HL              ; save the supplied limit.
13257
        PUSH    AF              ; save the flag.
13258
 
13259
        CALL    L1C82           ; routine EXPT-1NUM evaluates expression
13260
                                ; at CH_ADD returning if numeric result,
13261
                                ; with value on calculator stack.
13262
 
13263
        POP     AF              ; pop the flag.
13264
        CALL    L2530           ; routine SYNTAX-Z
13265
        JR      Z,L2AEB         ; forward to I-RESTORE if checking syntax so
13266
                                ; avoiding a comparison with supplied limit.
13267
 
13268
        PUSH    AF              ; save the flag.
13269
 
13270
        CALL    L1E99           ; routine FIND-INT2 fetches value from
13271
                                ; calculator stack to BC producing an error
13272
                                ; if too high.
13273
 
13274
        POP     DE              ; pop the flag to D.
13275
        LD      A,B             ; test value for zero and reject
13276
        OR      C               ; as arrays and strings begin at 1.
13277
        SCF                     ; set carry flag.
13278
        JR      Z,L2AE8         ; forward to I-CARRY if zero.
13279
 
13280
        POP     HL              ; restore the limit.
13281
        PUSH    HL              ; and save.
13282
        AND     A               ; prepare to subtract.
13283
        SBC     HL,BC           ; subtract value from limit.
13284
 
13285
;; I-CARRY
13286
L2AE8:  LD      A,D             ; move flag to accumulator $00 or $FF.
13287
        SBC     A,$00           ; will set to $FF if carry set.
13288
 
13289
;; I-RESTORE
13290
L2AEB:  POP     HL              ; restore the limit.
13291
        POP     DE              ; and DE register.
13292
        RET                     ; return.
13293
 
13294
 
13295
; -----------------------
13296
; LD DE,(DE+1) Subroutine
13297
; -----------------------
13298
; This routine just loads the DE register with the contents of the two
13299
; locations following the location addressed by DE.
13300
; It is used to step along the 16-bit dimension sizes in array definitions.
13301
; Note. Such code is made into subroutines to make programs easier to
13302
; write and it would use less space to include the five instructions in-line.
13303
; However, there are so many exchanges going on at the places this is invoked
13304
; that to implement it in-line would make the code hard to follow.
13305
; It probably had a zippier label though as the intention is to simplify the
13306
; program.
13307
 
13308
;; DE,(DE+1)
13309
L2AEE:  EX      DE,HL           ;
13310
        INC     HL              ;
13311
        LD      E,(HL)          ;
13312
        INC     HL              ;
13313
        LD      D,(HL)          ;
13314
        RET                     ;
13315
 
13316
; -------------------
13317
; HL=HL*DE Subroutine
13318
; -------------------
13319
; This routine calls the mathematical routine to multiply HL by DE in runtime.
13320
; It is called from STK-VAR and from DIM. In the latter case syntax is not
13321
; being checked so the entry point could have been at the second CALL
13322
; instruction to save a few clock-cycles.
13323
 
13324
;; GET-HL*DE
13325
L2AF4:  CALL    L2530           ; routine SYNTAX-Z.
13326
        RET     Z               ; return if checking syntax.
13327
 
13328
        CALL    L30A9           ; routine HL-HL*DE.
13329
        JP      C,L1F15         ; jump back to REPORT-4 if over 65535.
13330
 
13331
        RET                     ; else return with 16-bit result in HL.
13332
 
13333
; -----------------
13334
; THE 'LET' COMMAND
13335
; -----------------
13336
; Sinclair BASIC adheres to the ANSI-78 standard and a LET is required in
13337
; assignments e.g. LET a = 1  :   LET h$ = "hat".
13338
;
13339
; Long names may contain spaces but not colour controls (when assigned).
13340
; a substring can appear to the left of the equals sign.
13341
 
13342
; An earlier mathematician Lewis Carroll may have been pleased that
13343
; 10 LET Babies cannot manage crocodiles = Babies are illogical AND
13344
;    Nobody is despised who can manage a crocodile AND Illogical persons
13345
;    are despised
13346
; does not give the 'Nonsense..' error if the three variables exist.
13347
; I digress.
13348
 
13349
;; LET
13350
L2AFF:  LD      HL,($5C4D)      ; fetch system variable DEST to HL.
13351
        BIT     1,(IY+$37)      ; test FLAGX - handling a new variable ?
13352
        JR      Z,L2B66         ; forward to L-EXISTS if not.
13353
 
13354
; continue for a new variable. DEST points to start in BASIC line.
13355
; from the CLASS routines.
13356
 
13357
        LD      BC,$0005        ; assume numeric and assign an initial 5 bytes
13358
 
13359
;; L-EACH-CH
13360
L2B0B:  INC     BC              ; increase byte count for each relevant
13361
                                ; character
13362
 
13363
;; L-NO-SP
13364
L2B0C:  INC     HL              ; increase pointer.
13365
        LD      A,(HL)          ; fetch character.
13366
        CP      $20             ; is it a space ?
13367
        JR      Z,L2B0C         ; back to L-NO-SP is so.
13368
 
13369
        JR      NC,L2B1F        ; forward to L-TEST-CH if higher.
13370
 
13371
        CP      $10             ; is it $00 - $0F ?
13372
        JR      C,L2B29         ; forward to L-SPACES if so.
13373
 
13374
        CP      $16             ; is it $16 - $1F ?
13375
        JR      NC,L2B29        ; forward to L-SPACES if so.
13376
 
13377
; it was $10 - $15  so step over a colour code.
13378
 
13379
        INC     HL              ; increase pointer.
13380
        JR      L2B0C           ; loop back to L-NO-SP.
13381
 
13382
; ---
13383
 
13384
; the branch was to here if higher than space.
13385
 
13386
;; L-TEST-CH
13387
L2B1F:  CALL    L2C88           ; routine ALPHANUM sets carry if alphanumeric
13388
        JR      C,L2B0B         ; loop back to L-EACH-CH for more if so.
13389
 
13390
        CP      $24             ; is it '$' ?
13391
        JP      Z,L2BC0         ; jump forward if so, to L-NEW$
13392
                                ; with a new string.
13393
 
13394
;; L-SPACES
13395
L2B29:  LD      A,C             ; save length lo in A.
13396
        LD      HL,($5C59)      ; fetch E_LINE to HL.
13397
        DEC     HL              ; point to location before, the variables
13398
                                ; end-marker.
13399
        CALL    L1655           ; routine MAKE-ROOM creates BC spaces
13400
                                ; for name and numeric value.
13401
        INC     HL              ; advance to first new location.
13402
        INC     HL              ; then to second.
13403
        EX      DE,HL           ; set DE to second location.
13404
        PUSH    DE              ; save this pointer.
13405
        LD      HL,($5C4D)      ; reload HL with DEST.
13406
        DEC     DE              ; point to first.
13407
        SUB     $06             ; subtract six from length_lo.
13408
        LD      B,A             ; save count in B.
13409
        JR      Z,L2B4F         ; forward to L-SINGLE if it was just
13410
                                ; one character.
13411
 
13412
; HL points to start of variable name after 'LET' in BASIC line.
13413
 
13414
;; L-CHAR
13415
L2B3E:  INC     HL              ; increase pointer.
13416
        LD      A,(HL)          ; pick up character.
13417
        CP      $21             ; is it space or higher ?
13418
        JR      C,L2B3E         ; back to L-CHAR with space and less.
13419
 
13420
        OR      $20             ; make variable lower-case.
13421
        INC     DE              ; increase destination pointer.
13422
        LD      (DE),A          ; and load to edit line.
13423
        DJNZ    L2B3E           ; loop back to L-CHAR until B is zero.
13424
 
13425
        OR      $80             ; invert the last character.
13426
        LD      (DE),A          ; and overwrite that in edit line.
13427
 
13428
; now consider first character which has bit 6 set
13429
 
13430
        LD      A,$C0           ; set A 11000000 is xor mask for a long name.
13431
                                ; %101      is xor/or  result
13432
 
13433
; single character numerics rejoin here with %00000000 in mask.
13434
;                                            %011      will be xor/or result
13435
 
13436
;; L-SINGLE
13437
L2B4F:  LD      HL,($5C4D)      ; fetch DEST - HL addresses first character.
13438
        XOR     (HL)            ; apply variable type indicator mask (above).
13439
        OR      $20             ; make lowercase - set bit 5.
13440
        POP     HL              ; restore pointer to 2nd character.
13441
        CALL    L2BEA           ; routine L-FIRST puts A in first character.
13442
                                ; and returns with HL holding
13443
                                ; new E_LINE-1  the $80 vars end-marker.
13444
 
13445
;; L-NUMERIC
13446
L2B59:  PUSH    HL              ; save the pointer.
13447
 
13448
; the value of variable is deleted but remains after calculator stack.
13449
 
13450
        RST     28H             ;; FP-CALC
13451
        DB    $02             ;;delete      ; delete variable value
13452
        DB    $38             ;;end-calc
13453
 
13454
; DE (STKEND) points to start of value.
13455
 
13456
        POP     HL              ; restore the pointer.
13457
        LD      BC,$0005        ; start of number is five bytes before.
13458
        AND     A               ; prepare for true subtraction.
13459
        SBC     HL,BC           ; HL points to start of value.
13460
        JR      L2BA6           ; forward to L-ENTER  ==>
13461
 
13462
; ---
13463
 
13464
 
13465
; the jump was to here if the variable already existed.
13466
 
13467
;; L-EXISTS
13468
L2B66:  BIT     6,(IY+$01)      ; test FLAGS - numeric or string result ?
13469
        JR      Z,L2B72         ; skip forward to L-DELETE$   -*->
13470
                                ; if string result.
13471
 
13472
; A numeric variable could be simple or an array element.
13473
; They are treated the same and the old value is overwritten.
13474
 
13475
        LD      DE,$0006        ; six bytes forward points to loc past value.
13476
        ADD     HL,DE           ; add to start of number.
13477
        JR      L2B59           ; back to L-NUMERIC to overwrite value.
13478
 
13479
; ---
13480
 
13481
; -*-> the branch was here if a string existed.
13482
 
13483
;; L-DELETE$
13484
L2B72:  LD      HL,($5C4D)      ; fetch DEST to HL.
13485
                                ; (still set from first instruction)
13486
        LD      BC,($5C72)      ; fetch STRLEN to BC.
13487
        BIT     0,(IY+$37)      ; test FLAGX - handling a complete simple
13488
                                ; string ?
13489
        JR      NZ,L2BAF        ; forward to L-ADD$ if so.
13490
 
13491
; must be a string array or a slice in workspace.
13492
; Note. LET a$(3 TO 6) = h$   will assign "hat " if h$ = "hat"
13493
;                                  and    "hats" if h$ = "hatstand".
13494
;
13495
; This is known as Procrustian lengthening and shortening after a
13496
; character Procrustes in Greek legend who made travellers sleep in his bed,
13497
; cutting off their feet or stretching them so they fitted the bed perfectly.
13498
; The bloke was hatstand and slain by Theseus.
13499
 
13500
        LD      A,B             ; test if length
13501
        OR      C               ; is zero and
13502
        RET     Z               ; return if so.
13503
 
13504
        PUSH    HL              ; save pointer to start.
13505
 
13506
        RST     30H             ; BC-SPACES creates room.
13507
        PUSH    DE              ; save pointer to first new location.
13508
        PUSH    BC              ; and length            (*)
13509
        LD      D,H             ; set DE to point to last location.
13510
        LD      E,L             ;
13511
        INC     HL              ; set HL to next location.
13512
        LD      (HL),$20        ; place a space there.
13513
        LDDR                    ; copy bytes filling with spaces.
13514
 
13515
        PUSH    HL              ; save pointer to start.
13516
        CALL    L2BF1           ; routine STK-FETCH start to DE,
13517
                                ; length to BC.
13518
        POP     HL              ; restore the pointer.
13519
        EX      (SP),HL         ; (*) length to HL, pointer to stack.
13520
        AND     A               ; prepare for true subtraction.
13521
        SBC     HL,BC           ; subtract old length from new.
13522
        ADD     HL,BC           ; and add back.
13523
        JR      NC,L2B9B        ; forward if it fits to L-LENGTH.
13524
 
13525
        LD      B,H             ; otherwise set
13526
        LD      C,L             ; length to old length.
13527
                                ; "hatstand" becomes "hats"
13528
 
13529
;; L-LENGTH
13530
L2B9B:  EX      (SP),HL         ; (*) length to stack, pointer to HL.
13531
        EX      DE,HL           ; pointer to DE, start of string to HL.
13532
        LD      A,B             ; is the length zero ?
13533
        OR      C               ;
13534
        JR      Z,L2BA3         ; forward to L-IN-W/S if so
13535
                                ; leaving prepared spaces.
13536
 
13537
        LDIR                    ; else copy bytes overwriting some spaces.
13538
 
13539
;; L-IN-W/S
13540
L2BA3:  POP     BC              ; pop the new length.  (*)
13541
        POP     DE              ; pop pointer to new area.
13542
        POP     HL              ; pop pointer to variable in assignment.
13543
                                ; and continue copying from workspace
13544
                                ; to variables area.
13545
 
13546
; ==> branch here from  L-NUMERIC
13547
 
13548
;; L-ENTER
13549
L2BA6:  EX      DE,HL           ; exchange pointers HL=STKEND DE=end of vars.
13550
        LD      A,B             ; test the length
13551
        OR      C               ; and make a 
13552
        RET     Z               ; return if zero (strings only).
13553
 
13554
        PUSH    DE              ; save start of destination.
13555
        LDIR                    ; copy bytes.
13556
        POP     HL              ; address the start.
13557
        RET                     ; and return.
13558
 
13559
; ---
13560
 
13561
; the branch was here from L-DELETE$ if an existing simple string.
13562
; register HL addresses start of string in variables area.
13563
 
13564
;; L-ADD$
13565
L2BAF:  DEC     HL              ; point to high byte of length.
13566
        DEC     HL              ; to low byte.
13567
        DEC     HL              ; to letter.
13568
        LD      A,(HL)          ; fetch masked letter to A.
13569
        PUSH    HL              ; save the pointer on stack.
13570
        PUSH    BC              ; save new length.
13571
        CALL    L2BC6           ; routine L-STRING adds new string at end
13572
                                ; of variables area.
13573
                                ; if no room we still have old one.
13574
        POP     BC              ; restore length.
13575
        POP     HL              ; restore start.
13576
        INC     BC              ; increase
13577
        INC     BC              ; length by three
13578
        INC     BC              ; to include character and length bytes.
13579
        JP      L19E8           ; jump to indirect exit via RECLAIM-2
13580
                                ; deleting old version and adjusting pointers.
13581
 
13582
; ---
13583
 
13584
; the jump was here with a new string variable.
13585
 
13586
;; L-NEW$
13587
L2BC0:  LD      A,$DF           ; indicator mask %11011111 for
13588
                                ;                %010xxxxx will be result
13589
        LD      HL,($5C4D)      ; address DEST first character.
13590
        AND     (HL)            ; combine mask with character.
13591
 
13592
;; L-STRING
13593
L2BC6:  PUSH    AF              ; save first character and mask.
13594
        CALL    L2BF1           ; routine STK-FETCH fetches parameters of
13595
                                ; the string.
13596
        EX      DE,HL           ; transfer start to HL.
13597
        ADD     HL,BC           ; add to length.
13598
        PUSH    BC              ; save the length.
13599
        DEC     HL              ; point to end of string.
13600
        LD      ($5C4D),HL      ; save pointer in DEST.
13601
                                ; (updated by POINTERS if in workspace)
13602
        INC     BC              ; extra byte for letter.
13603
        INC     BC              ; two bytes
13604
        INC     BC              ; for the length of string.
13605
        LD      HL,($5C59)      ; address E_LINE.
13606
        DEC     HL              ; now end of VARS area.
13607
        CALL    L1655           ; routine MAKE-ROOM makes room for string.
13608
                                ; updating pointers including DEST.
13609
        LD      HL,($5C4D)      ; pick up pointer to end of string from DEST.
13610
        POP     BC              ; restore length from stack.
13611
        PUSH    BC              ; and save again on stack.
13612
        INC     BC              ; add a byte.
13613
        LDDR                    ; copy bytes from end to start.
13614
        EX      DE,HL           ; HL addresses length low
13615
        INC     HL              ; increase to address high byte
13616
        POP     BC              ; restore length to BC
13617
        LD      (HL),B          ; insert high byte
13618
        DEC     HL              ; address low byte location
13619
        LD      (HL),C          ; insert that byte
13620
        POP     AF              ; restore character and mask
13621
 
13622
;; L-FIRST
13623
L2BEA:  DEC     HL              ; address variable name
13624
        LD      (HL),A          ; and insert character.
13625
        LD      HL,($5C59)      ; load HL with E_LINE.
13626
        DEC     HL              ; now end of VARS area.
13627
        RET                     ; return
13628
 
13629
; ------------------------------------
13630
; Get last value from calculator stack
13631
; ------------------------------------
13632
;
13633
;
13634
 
13635
;; STK-FETCH
13636
L2BF1:  LD      HL,($5C65)      ; STKEND
13637
        DEC     HL              ;
13638
        LD      B,(HL)          ;
13639
        DEC     HL              ;
13640
        LD      C,(HL)          ;
13641
        DEC     HL              ;
13642
        LD      D,(HL)          ;
13643
        DEC     HL              ;
13644
        LD      E,(HL)          ;
13645
        DEC     HL              ;
13646
        LD      A,(HL)          ;
13647
        LD      ($5C65),HL      ; STKEND
13648
        RET                     ;
13649
 
13650
; ------------------
13651
; Handle DIM command
13652
; ------------------
13653
; e.g. DIM a(2,3,4,7): DIM a$(32) : DIM b$(300,2,768) : DIM c$(20000)
13654
; the only limit to dimensions is memory so, for example,
13655
; DIM a(2,2,2,2,2,2,2,2,2,2,2,2,2) is possible and creates a multi-
13656
; dimensional array of zeros. String arrays are initialized to spaces.
13657
; It is not possible to erase an array, but it can be re-dimensioned to
13658
; a minimal size of 1, after use, to free up memory.
13659
 
13660
;; DIM
13661
L2C02:  CALL    L28B2           ; routine LOOK-VARS
13662
 
13663
;; D-RPORT-C
13664
L2C05:  JP      NZ,L1C8A        ; jump to REPORT-C if a long-name variable.
13665
                                ; DIM lottery numbers(49) doesn't work.
13666
 
13667
        CALL    L2530           ; routine SYNTAX-Z
13668
        JR      NZ,L2C15        ; forward to D-RUN in runtime.
13669
 
13670
        RES     6,C             ; signal 'numeric' array even if string as
13671
                                ; this simplifies the syntax checking.
13672
 
13673
        CALL    L2996           ; routine STK-VAR checks syntax.
13674
        CALL    L1BEE           ; routine CHECK-END performs early exit ->
13675
 
13676
; the branch was here in runtime.
13677
 
13678
;; D-RUN
13679
L2C15:  JR      C,L2C1F         ; skip to D-LETTER if variable did not exist.
13680
                                ; else reclaim the old one.
13681
 
13682
        PUSH    BC              ; save type in C.
13683
        CALL    L19B8           ; routine NEXT-ONE find following variable
13684
                                ; or position of $80 end-marker.
13685
        CALL    L19E8           ; routine RECLAIM-2 reclaims the 
13686
                                ; space between.
13687
        POP     BC              ; pop the type.
13688
 
13689
;; D-LETTER
13690
L2C1F:  SET     7,C             ; signal array.
13691
        LD      B,$00           ; initialize dimensions to zero and
13692
        PUSH    BC              ; save with the type.
13693
        LD      HL,$0001        ; make elements one character presuming string
13694
        BIT     6,C             ; is it a string ?
13695
        JR      NZ,L2C2D        ; forward to D-SIZE if so.
13696
 
13697
        LD      L,$05           ; make elements 5 bytes as is numeric.
13698
 
13699
;; D-SIZE
13700
L2C2D:  EX      DE,HL           ; save the element size in DE.
13701
 
13702
; now enter a loop to parse each of the integers in the list.
13703
 
13704
;; D-NO-LOOP
13705
L2C2E:  RST     20H             ; NEXT-CHAR
13706
        LD      H,$FF           ; disable limit check by setting HL high
13707
        CALL    L2ACC           ; routine INT-EXP1
13708
        JP      C,L2A20         ; to REPORT-3 if > 65280 and then some
13709
                                ; 'Subscript out of range'
13710
 
13711
        POP     HL              ; pop dimension counter, array type
13712
        PUSH    BC              ; save dimension size                     ***
13713
        INC     H               ; increment the dimension counter
13714
        PUSH    HL              ; save the dimension counter
13715
        LD      H,B             ; transfer size
13716
        LD      L,C             ; to HL
13717
        CALL    L2AF4           ; routine GET-HL*DE multiplies dimension by
13718
                                ; running total of size required initially
13719
                                ; 1 or 5.
13720
        EX      DE,HL           ; save running total in DE
13721
 
13722
        RST     18H             ; GET-CHAR
13723
        CP      $2C             ; is it ',' ?
13724
        JR      Z,L2C2E         ; loop back to D-NO-LOOP until all dimensions
13725
                                ; have been considered
13726
 
13727
; when loop complete continue.
13728
 
13729
        CP      $29             ; is it ')' ?
13730
        JR      NZ,L2C05        ; to D-RPORT-C with anything else
13731
                                ; 'Nonsense in BASIC'
13732
 
13733
 
13734
        RST     20H             ; NEXT-CHAR advances to next statement/CR
13735
 
13736
        POP     BC              ; pop dimension counter/type
13737
        LD      A,C             ; type to A
13738
 
13739
; now calculate space required for array variable
13740
 
13741
        LD      L,B             ; dimensions to L since these require 16 bits
13742
                                ; then this value will be doubled
13743
        LD      H,$00           ; set high byte to zero
13744
 
13745
; another four bytes are required for letter(1), total length(2), number of
13746
; dimensions(1) but since we have yet to double allow for two
13747
 
13748
        INC     HL              ; increment
13749
        INC     HL              ; increment
13750
 
13751
        ADD     HL,HL           ; now double giving 4 + dimensions * 2
13752
 
13753
        ADD     HL,DE           ; add to space required for array contents
13754
 
13755
        JP      C,L1F15         ; to REPORT-4 if > 65535
13756
                                ; 'Out of memory'
13757
 
13758
        PUSH    DE              ; save data space
13759
        PUSH    BC              ; save dimensions/type
13760
        PUSH    HL              ; save total space
13761
        LD      B,H             ; total space
13762
        LD      C,L             ; to BC
13763
        LD      HL,($5C59)      ; address E_LINE - first location after
13764
                                ; variables area
13765
        DEC     HL              ; point to location before - the $80 end-marker
13766
        CALL    L1655           ; routine MAKE-ROOM creates the space if
13767
                                ; memory is available.
13768
 
13769
        INC     HL              ; point to first new location and
13770
        LD      (HL),A          ; store letter/type
13771
 
13772
        POP     BC              ; pop total space
13773
        DEC     BC              ; exclude name
13774
        DEC     BC              ; exclude the 16-bit
13775
        DEC     BC              ; counter itself
13776
        INC     HL              ; point to next location the 16-bit counter
13777
        LD      (HL),C          ; insert low byte
13778
        INC     HL              ; address next
13779
        LD      (HL),B          ; insert high byte
13780
 
13781
        POP     BC              ; pop the number of dimensions.
13782
        LD      A,B             ; dimensions to A
13783
        INC     HL              ; address next
13784
        LD      (HL),A          ; and insert "No. of dims"
13785
 
13786
        LD      H,D             ; transfer DE space + 1 from make-room
13787
        LD      L,E             ; to HL
13788
        DEC     DE              ; set DE to next location down.
13789
        LD      (HL),$00        ; presume numeric and insert a zero
13790
        BIT     6,C             ; test bit 6 of C. numeric or string ?
13791
        JR      Z,L2C7C         ; skip to DIM-CLEAR if numeric
13792
 
13793
        LD      (HL),$20        ; place a space character in HL
13794
 
13795
;; DIM-CLEAR
13796
L2C7C:  POP     BC              ; pop the data length
13797
 
13798
        LDDR                    ; LDDR sets to zeros or spaces
13799
 
13800
; The number of dimensions is still in A.
13801
; A loop is now entered to insert the size of each dimension that was pushed
13802
; during the D-NO-LOOP working downwards from position before start of data.
13803
 
13804
;; DIM-SIZES
13805
L2C7F:  POP     BC              ; pop a dimension size                    ***
13806
        LD      (HL),B          ; insert high byte at position
13807
        DEC     HL              ; next location down
13808
        LD      (HL),C          ; insert low byte
13809
        DEC     HL              ; next location down
13810
        DEC     A               ; decrement dimension counter
13811
        JR      NZ,L2C7F        ; back to DIM-SIZES until all done.
13812
 
13813
        RET                     ; return.
13814
 
13815
; -----------------------------
13816
; Check whether digit or letter
13817
; -----------------------------
13818
; This routine checks that the character in A is alphanumeric
13819
; returning with carry set if so.
13820
 
13821
;; ALPHANUM
13822
L2C88:  CALL    L2D1B           ; routine NUMERIC will reset carry if so.
13823
        CCF                     ; Complement Carry Flag
13824
        RET     C               ; Return if numeric else continue into
13825
                                ; next routine.
13826
 
13827
; This routine checks that the character in A is alphabetic
13828
 
13829
;; ALPHA
13830
L2C8D:  CP      $41             ; less than 'A' ?
13831
        CCF                     ; Complement Carry Flag
13832
        RET     NC              ; return if so
13833
 
13834
        CP      $5B             ; less than 'Z'+1 ?
13835
        RET     C               ; is within first range
13836
 
13837
        CP      $61             ; less than 'a' ?
13838
        CCF                     ; Complement Carry Flag
13839
        RET     NC              ; return if so.
13840
 
13841
        CP      $7B             ; less than 'z'+1 ?
13842
        RET                     ; carry set if within a-z.
13843
 
13844
; -------------------------
13845
; Decimal to floating point
13846
; -------------------------
13847
; This routine finds the floating point number represented by an expression
13848
; beginning with BIN, '.' or a digit.
13849
; Note that BIN need not have any '0's or '1's after it.
13850
; BIN is really just a notational symbol and not a function.
13851
 
13852
;; DEC-TO-FP
13853
L2C9B:  CP      $C4             ; 'BIN' token ?
13854
        JR      NZ,L2CB8        ; to NOT-BIN if not
13855
 
13856
        LD      DE,$0000        ; initialize 16 bit buffer register.
13857
 
13858
;; BIN-DIGIT
13859
L2CA2:  RST     20H             ; NEXT-CHAR
13860
        SUB     $31             ; '1'
13861
        ADC     A,$00           ; will be zero if '1' or '0'
13862
                                ; carry will be set if was '0'
13863
        JR      NZ,L2CB3        ; forward to BIN-END if result not zero
13864
 
13865
        EX      DE,HL           ; buffer to HL
13866
        CCF                     ; Carry now set if originally '1'
13867
        ADC     HL,HL           ; shift the carry into HL
13868
        JP      C,L31AD         ; to REPORT-6 if overflow - too many digits
13869
                                ; after first '1'. There can be an unlimited
13870
                                ; number of leading zeros.
13871
                                ; 'Number too big' - raise an error
13872
 
13873
        EX      DE,HL           ; save the buffer
13874
        JR      L2CA2           ; back to BIN-DIGIT for more digits
13875
 
13876
; ---
13877
 
13878
;; BIN-END
13879
L2CB3:  LD      B,D             ; transfer 16 bit buffer
13880
        LD      C,E             ; to BC register pair.
13881
        JP      L2D2B           ; JUMP to STACK-BC to put on calculator stack
13882
 
13883
; ---
13884
 
13885
; continue here with .1,  42, 3.14, 5., 2.3 E -4
13886
 
13887
;; NOT-BIN
13888
L2CB8:  CP      $2E             ; '.' - leading decimal point ?
13889
        JR      Z,L2CCB         ; skip to DECIMAL if so.
13890
 
13891
        CALL    L2D3B           ; routine INT-TO-FP to evaluate all digits
13892
                                ; This number 'x' is placed on stack.
13893
        CP      $2E             ; '.' - mid decimal point ?
13894
 
13895
        JR      NZ,L2CEB        ; to E-FORMAT if not to consider that format
13896
 
13897
        RST     20H             ; NEXT-CHAR
13898
        CALL    L2D1B           ; routine NUMERIC returns carry reset if 0-9
13899
 
13900
        JR      C,L2CEB         ; to E-FORMAT if not a digit e.g. '1.'
13901
 
13902
        JR      L2CD5           ; to DEC-STO-1 to add the decimal part to 'x'
13903
 
13904
; ---
13905
 
13906
; a leading decimal point has been found in a number.
13907
 
13908
;; DECIMAL
13909
L2CCB:  RST     20H             ; NEXT-CHAR
13910
        CALL    L2D1B           ; routine NUMERIC will reset carry if digit
13911
 
13912
;; DEC-RPT-C
13913
L2CCF:  JP      C,L1C8A         ; to REPORT-C if just a '.'
13914
                                ; raise 'Nonsense in BASIC'
13915
 
13916
; since there is no leading zero put one on the calculator stack.
13917
 
13918
        RST     28H             ;; FP-CALC
13919
        DB    $A0             ;;stk-zero  ; 0.
13920
        DB    $38             ;;end-calc
13921
 
13922
; If rejoining from earlier there will be a value 'x' on stack.
13923
; If continuing from above the value zero.
13924
; Now store 1 in mem-0.
13925
; Note. At each pass of the digit loop this will be divided by ten.
13926
 
13927
;; DEC-STO-1
13928
L2CD5:  RST     28H             ;; FP-CALC
13929
        DB    $A1             ;;stk-one   ;x or 0,1.
13930
        DB    $C0             ;;st-mem-0  ;x or 0,1.
13931
        DB    $02             ;;delete    ;x or 0.
13932
        DB    $38             ;;end-calc
13933
 
13934
 
13935
;; NXT-DGT-1
13936
L2CDA:  RST     18H             ; GET-CHAR
13937
        CALL    L2D22           ; routine STK-DIGIT stacks single digit 'd'
13938
        JR      C,L2CEB         ; exit to E-FORMAT when digits exhausted  >
13939
 
13940
 
13941
        RST     28H             ;; FP-CALC   ;x or 0,d.           first pass.
13942
        DB    $E0             ;;get-mem-0  ;x or 0,d,1.
13943
        DB    $A4             ;;stk-ten    ;x or 0,d,1,10.
13944
        DB    $05             ;;division   ;x or 0,d,1/10.
13945
        DB    $C0             ;;st-mem-0   ;x or 0,d,1/10.
13946
        DB    $04             ;;multiply   ;x or 0,d/10.
13947
        DB    $0F             ;;addition   ;x or 0 + d/10.
13948
        DB    $38             ;;end-calc   last value.
13949
 
13950
        RST     20H             ; NEXT-CHAR  moves to next character
13951
        JR      L2CDA           ; back to NXT-DGT-1
13952
 
13953
; ---
13954
 
13955
; although only the first pass is shown it can be seen that at each pass
13956
; the new less significant digit is multiplied by an increasingly smaller
13957
; factor (1/100, 1/1000, 1/10000 ... ) before being added to the previous
13958
; last value to form a new last value.
13959
 
13960
; Finally see if an exponent has been input.
13961
 
13962
;; E-FORMAT
13963
L2CEB:  CP      $45             ; is character 'E' ?
13964
        JR      Z,L2CF2         ; to SIGN-FLAG if so
13965
 
13966
        CP      $65             ; 'e' is acceptable as well.
13967
        RET     NZ              ; return as no exponent.
13968
 
13969
;; SIGN-FLAG
13970
L2CF2:  LD      B,$FF           ; initialize temporary sign byte to $FF
13971
 
13972
        RST     20H             ; NEXT-CHAR
13973
        CP      $2B             ; is character '+' ?
13974
        JR      Z,L2CFE         ; to SIGN-DONE
13975
 
13976
        CP      $2D             ; is character '-' ?
13977
        JR      NZ,L2CFF        ; to ST-E-PART as no sign
13978
 
13979
        INC     B               ; set sign to zero
13980
 
13981
; now consider digits of exponent.
13982
; Note. incidentally this is the only occasion in Spectrum BASIC when an
13983
; expression may not be used when a number is expected.
13984
 
13985
;; SIGN-DONE
13986
L2CFE:  RST     20H             ; NEXT-CHAR
13987
 
13988
;; ST-E-PART
13989
L2CFF:  CALL    L2D1B           ; routine NUMERIC
13990
        JR      C,L2CCF         ; to DEC-RPT-C if not
13991
                                ; raise 'Nonsense in BASIC'.
13992
 
13993
        PUSH    BC              ; save sign (in B)
13994
        CALL    L2D3B           ; routine INT-TO-FP places exponent on stack
13995
        CALL    L2DD5           ; routine FP-TO-A  transfers it to A
13996
        POP     BC              ; restore sign
13997
        JP      C,L31AD         ; to REPORT-6 if overflow (over 255)
13998
                                ; raise 'Number too big'.
13999
 
14000
        AND     A               ; set flags
14001
        JP      M,L31AD         ; to REPORT-6 if over '127'.
14002
                                ; raise 'Number too big'.
14003
                                ; 127 is still way too high and it is
14004
                                ; impossible to enter an exponent greater
14005
                                ; than 39 from the keyboard. The error gets
14006
                                ; raised later in E-TO-FP so two different
14007
                                ; error messages depending how high A is.
14008
 
14009
        INC     B               ; $FF to $00 or $00 to $01 - expendable now.
14010
        JR      Z,L2D18         ; forward to E-FP-JUMP if exponent positive
14011
 
14012
        NEG                     ; Negate the exponent.
14013
 
14014
;; E-FP-JUMP
14015
L2D18:  JP      L2D4F           ; JUMP forward to E-TO-FP to assign to
14016
                                ; last value x on stack x * 10 to power A
14017
                                ; a relative jump would have done.
14018
 
14019
; ---------------------
14020
; Check for valid digit
14021
; ---------------------
14022
; This routine checks that the ASCII character in A is numeric
14023
; returning with carry reset if so.
14024
 
14025
;; NUMERIC
14026
L2D1B:  CP      $30             ; '0'
14027
        RET     C               ; return if less than zero character.
14028
 
14029
        CP      $3A             ; The upper test is '9'
14030
        CCF                     ; Complement Carry Flag
14031
        RET                     ; Return - carry clear if character '0' - '9'
14032
 
14033
; -----------
14034
; Stack Digit
14035
; -----------
14036
; This subroutine is called from INT-TO-FP and DEC-TO-FP to stack a digit
14037
; on the calculator stack.
14038
 
14039
;; STK-DIGIT
14040
L2D22:  CALL    L2D1B           ; routine NUMERIC
14041
        RET     C               ; return if not numeric character
14042
 
14043
        SUB     $30             ; convert from ASCII to digit
14044
 
14045
; -----------------
14046
; Stack accumulator
14047
; -----------------
14048
;
14049
;
14050
 
14051
;; STACK-A
14052
L2D28:  LD      C,A             ; transfer to C
14053
        LD      B,$00           ; and make B zero
14054
 
14055
; ----------------------
14056
; Stack BC register pair
14057
; ----------------------
14058
;
14059
 
14060
;; STACK-BC
14061
L2D2B:  LD      IY,$5C3A        ; re-initialize ERR_NR
14062
 
14063
        XOR     A               ; clear to signal small integer
14064
        LD      E,A             ; place in E for sign
14065
        LD      D,C             ; LSB to D
14066
        LD      C,B             ; MSB to C
14067
        LD      B,A             ; last byte not used
14068
        CALL    L2AB6           ; routine STK-STORE
14069
 
14070
        RST     28H             ;; FP-CALC
14071
        DB    $38             ;;end-calc  make HL = STKEND-5
14072
 
14073
        AND     A               ; clear carry
14074
        RET                     ; before returning
14075
 
14076
; -------------------------
14077
; Integer to floating point
14078
; -------------------------
14079
; This routine places one or more digits found in a BASIC line
14080
; on the calculator stack multiplying the previous value by ten each time
14081
; before adding in the new digit to form a last value on calculator stack.
14082
 
14083
;; INT-TO-FP
14084
L2D3B:  PUSH    AF              ; save first character
14085
 
14086
        RST     28H             ;; FP-CALC
14087
        DB    $A0             ;;stk-zero    ; v=0. initial value
14088
        DB    $38             ;;end-calc
14089
 
14090
        POP     AF              ; fetch first character back.
14091
 
14092
;; NXT-DGT-2
14093
L2D40:  CALL    L2D22           ; routine STK-DIGIT puts 0-9 on stack
14094
        RET     C               ; will return when character is not numeric >
14095
 
14096
        RST     28H             ;; FP-CALC    ; v, d.
14097
        DB    $01             ;;exchange    ; d, v.
14098
        DB    $A4             ;;stk-ten     ; d, v, 10.
14099
        DB    $04             ;;multiply    ; d, v*10.
14100
        DB    $0F             ;;addition    ; d + v*10 = newvalue
14101
        DB    $38             ;;end-calc    ; v.
14102
 
14103
        CALL    L0074           ; routine CH-ADD+1 get next character
14104
        JR      L2D40           ; back to NXT-DGT-2 to process as a digit
14105
 
14106
 
14107
;*********************************
14108
;** Part 9. ARITHMETIC ROUTINES **
14109
;*********************************
14110
 
14111
; --------------------------
14112
; E-format to floating point
14113
; --------------------------
14114
; This subroutine is used by the PRINT-FP routine and the decimal to FP
14115
; routines to stack a number expressed in exponent format.
14116
; Note. Though not used by the ROM as such, it has also been set up as
14117
; a unary calculator literal but this will not work as the accumulator
14118
; is not available from within the calculator.
14119
 
14120
; on entry there is a value x on the calculator stack and an exponent of ten
14121
; in A.    The required value is x + 10 ^ A
14122
 
14123
;; e-to-fp
14124
;; E-TO-FP
14125
L2D4F:  RLCA                    ; this will set the          x.
14126
        RRCA                    ; carry if bit 7 is set
14127
 
14128
        JR      NC,L2D55        ; to E-SAVE  if positive.
14129
 
14130
        CPL                     ; make negative positive
14131
        INC     A               ; without altering carry.
14132
 
14133
;; E-SAVE
14134
L2D55:  PUSH    AF              ; save positive exp and sign in carry
14135
 
14136
        LD      HL,$5C92        ; address MEM-0
14137
 
14138
        CALL    L350B           ; routine FP-0/1
14139
                                ; places an integer zero, if no carry,
14140
                                ; else a one in mem-0 as a sign flag
14141
 
14142
        RST     28H             ;; FP-CALC
14143
        DB    $A4             ;;stk-ten                    x, 10.
14144
        DB    $38             ;;end-calc
14145
 
14146
        POP     AF              ; pop the exponent.
14147
 
14148
; now enter a loop
14149
 
14150
;; E-LOOP
14151
L2D60:  SRL     A               ; 0>76543210>C
14152
 
14153
        JR      NC,L2D71        ; forward to E-TST-END if no bit
14154
 
14155
        PUSH    AF              ; save shifted exponent.
14156
 
14157
        RST     28H             ;; FP-CALC
14158
        DB    $C1             ;;st-mem-1                   x, 10.
14159
        DB    $E0             ;;get-mem-0                  x, 10, (0/1).
14160
        DB    $00             ;;jump-true
14161
 
14162
        DB    $04             ;;to L2D6D, E-DIVSN
14163
 
14164
        DB    $04             ;;multiply                   x*10.
14165
        DB    $33             ;;jump
14166
 
14167
        DB    $02             ;;to L2D6E, E-FETCH
14168
 
14169
;; E-DIVSN
14170
L2D6D:  DB    $05             ;;division                   x/10.
14171
 
14172
;; E-FETCH
14173
L2D6E:  DB    $E1             ;;get-mem-1                  x/10 or x*10, 10.
14174
        DB    $38             ;;end-calc                   new x, 10.
14175
 
14176
        POP     AF              ; restore shifted exponent
14177
 
14178
; the loop branched to here with no carry
14179
 
14180
;; E-TST-END
14181
L2D71:  JR      Z,L2D7B         ; forward to E-END  if A emptied of bits
14182
 
14183
        PUSH    AF              ; re-save shifted exponent
14184
 
14185
        RST     28H             ;; FP-CALC
14186
        DB    $31             ;;duplicate                  new x, 10, 10.
14187
        DB    $04             ;;multiply                   new x, 100.
14188
        DB    $38             ;;end-calc
14189
 
14190
        POP     AF              ; restore shifted exponent
14191
        JR      L2D60           ; back to E-LOOP  until all bits done.
14192
 
14193
; ---
14194
 
14195
; although only the first pass is shown it can be seen that for each set bit
14196
; representing a power of two, x is multiplied or divided by the
14197
; corresponding power of ten.
14198
 
14199
;; E-END
14200
L2D7B:  RST     28H             ;; FP-CALC                   final x, factor.
14201
        DB    $02             ;;delete                     final x.
14202
        DB    $38             ;;end-calc                   x.
14203
 
14204
        RET                     ; return
14205
 
14206
 
14207
 
14208
 
14209
; -------------
14210
; Fetch integer
14211
; -------------
14212
; This routine is called by the mathematical routines - FP-TO-BC, PRINT-FP,
14213
; mult, re-stack and negate to fetch an integer from address HL.
14214
; HL points to the stack or a location in MEM and no deletion occurs.
14215
; If the number is negative then a similar process to that used in INT-STORE
14216
; is used to restore the twos complement number to normal in DE and a sign
14217
; in C.
14218
 
14219
;; INT-FETCH
14220
L2D7F:  INC     HL              ; skip zero indicator.
14221
        LD      C,(HL)          ; fetch sign to C
14222
        INC     HL              ; address low byte
14223
        LD      A,(HL)          ; fetch to A
14224
        XOR     C               ; two's complement
14225
        SUB     C               ;
14226
        LD      E,A             ; place in E
14227
        INC     HL              ; address high byte
14228
        LD      A,(HL)          ; fetch to A
14229
        ADC     A,C             ; two's complement
14230
        XOR     C               ;
14231
        LD      D,A             ; place in D
14232
        RET                     ; return
14233
 
14234
; ------------------------
14235
; Store a positive integer
14236
; ------------------------
14237
; This entry point is not used in this ROM but would
14238
; store any integer as positive.
14239
 
14240
;; p-int-sto
14241
L2D8C:  LD      C,$00           ; make sign byte positive and continue
14242
 
14243
; -------------
14244
; Store integer
14245
; -------------
14246
; this routine stores an integer in DE at address HL.
14247
; It is called from mult, truncate, negate and sgn.
14248
; The sign byte $00 +ve or $FF -ve is in C.
14249
; If negative, the number is stored in 2's complement form so that it is
14250
; ready to be added.
14251
 
14252
;; INT-STORE
14253
L2D8E:  PUSH    HL              ; preserve HL
14254
 
14255
        LD      (HL),$00        ; first byte zero shows integer not exponent
14256
        INC     HL              ;
14257
        LD      (HL),C          ; then store the sign byte
14258
        INC     HL              ;
14259
                                ; e.g.             +1             -1
14260
        LD      A,E             ; fetch low byte   00000001       00000001
14261
        XOR     C               ; xor sign         00000000   or  11111111
14262
                                ; gives            00000001   or  11111110
14263
        SUB     C               ; sub sign         00000000   or  11111111
14264
                                ; gives            00000001>0 or  11111111>C
14265
        LD      (HL),A          ; store 2's complement.
14266
        INC     HL              ;
14267
        LD      A,D             ; high byte        00000000       00000000
14268
        ADC     A,C             ; sign             00000000<0     11111111<C
14269
                                ; gives            00000000   or  00000000
14270
        XOR     C               ; xor sign         00000000       11111111
14271
        LD      (HL),A          ; store 2's complement.
14272
        INC     HL              ;
14273
        LD      (HL),$00        ; last byte always zero for integers.
14274
                                ; is not used and need not be looked at when
14275
                                ; testing for zero but comes into play should
14276
                                ; an integer be converted to fp.
14277
        POP     HL              ; restore HL
14278
        RET                     ; return.
14279
 
14280
 
14281
; -----------------------------
14282
; Floating point to BC register
14283
; -----------------------------
14284
; This routine gets a floating point number e.g. 127.4 from the calculator
14285
; stack to the BC register.
14286
 
14287
;; FP-TO-BC
14288
L2DA2:  RST     28H             ;; FP-CALC            set HL to
14289
        DB    $38             ;;end-calc            point to last value.
14290
 
14291
        LD      A,(HL)          ; get first of 5 bytes
14292
        AND     A               ; and test
14293
        JR      Z,L2DAD         ; forward to FP-DELETE if an integer
14294
 
14295
; The value is first rounded up and then converted to integer.
14296
 
14297
        RST     28H             ;; FP-CALC           x.
14298
        DB    $A2             ;;stk-half           x. 1/2.
14299
        DB    $0F             ;;addition           x + 1/2.
14300
        DB    $27             ;;int                int(x + .5)
14301
        DB    $38             ;;end-calc
14302
 
14303
; now delete but leave HL pointing at integer
14304
 
14305
;; FP-DELETE
14306
L2DAD:  RST     28H             ;; FP-CALC
14307
        DB    $02             ;;delete
14308
        DB    $38             ;;end-calc
14309
 
14310
        PUSH    HL              ; save pointer.
14311
        PUSH    DE              ; and STKEND.
14312
        EX      DE,HL           ; make HL point to exponent/zero indicator
14313
        LD      B,(HL)          ; indicator to B
14314
        CALL    L2D7F           ; routine INT-FETCH
14315
                                ; gets int in DE sign byte to C
14316
                                ; but meaningless values if a large integer
14317
 
14318
        XOR     A               ; clear A
14319
        SUB     B               ; subtract indicator byte setting carry
14320
                                ; if not a small integer.
14321
 
14322
        BIT     7,C             ; test a bit of the sign byte setting zero
14323
                                ; if positive.
14324
 
14325
        LD      B,D             ; transfer int
14326
        LD      C,E             ; to BC
14327
        LD      A,E             ; low byte to A as a useful return value.
14328
 
14329
        POP     DE              ; pop STKEND
14330
        POP     HL              ; and pointer to last value
14331
        RET                     ; return
14332
                                ; if carry is set then the number was too big.
14333
 
14334
; ------------
14335
; LOG(2^A)
14336
; ------------
14337
; This routine is used when printing floating point numbers to calculate
14338
; the number of digits before the decimal point.
14339
 
14340
; first convert a one-byte signed integer to its five byte form.
14341
 
14342
;; LOG(2^A)
14343
L2DC1:  LD      D,A             ; store a copy of A in D.
14344
        RLA                     ; test sign bit of A.
14345
        SBC     A,A             ; now $FF if negative or $00
14346
        LD      E,A             ; sign byte to E.
14347
        LD      C,A             ; and to C
14348
        XOR     A               ; clear A
14349
        LD      B,A             ; and B.
14350
        CALL    L2AB6           ; routine STK-STORE stacks number AEDCB
14351
 
14352
;  so 00 00 XX 00 00 (positive) or 00 FF XX FF 00 (negative).
14353
;  i.e. integer indicator, sign byte, low, high, unused.
14354
 
14355
; now multiply exponent by log to the base 10 of two.
14356
 
14357
        RST      28H            ;; FP-CALC
14358
 
14359
        DB    $34             ;;stk-data                      .30103 (log 2)
14360
        DB    $EF             ;;Exponent: $7F, Bytes: 4
14361
        DB    $1A,$20,$9A,$85 ;;
14362
        DB    $04             ;;multiply
14363
 
14364
        DB    $27             ;;int
14365
 
14366
        DB    $38             ;;end-calc
14367
 
14368
; -------------------
14369
; Floating point to A
14370
; -------------------
14371
; this routine collects a floating point number from the stack into the
14372
; accumulator returning carry set if not in range 0 - 255.
14373
; Not all the calling routines raise an error with overflow so no attempt
14374
; is made to produce an error report here.
14375
 
14376
;; FP-TO-A
14377
L2DD5:  CALL    L2DA2           ; routine FP-TO-BC returns with C in A also.
14378
        RET     C               ; return with carry set if > 65535, overflow
14379
 
14380
        PUSH    AF              ; save the value and flags
14381
        DEC     B               ; and test that
14382
        INC     B               ; the high byte is zero.
14383
        JR      Z,L2DE1         ; forward  FP-A-END if zero
14384
 
14385
; else there has been 8-bit overflow
14386
 
14387
        POP     AF              ; retrieve the value
14388
        SCF                     ; set carry flag to show overflow
14389
        RET                     ; and return.
14390
 
14391
; ---
14392
 
14393
;; FP-A-END
14394
L2DE1:  POP     AF              ; restore value and success flag and
14395
        RET                     ; return.
14396
 
14397
 
14398
; -----------------------------
14399
; Print a floating point number
14400
; -----------------------------
14401
; Not a trivial task.
14402
; Begin by considering whether to print a leading sign for negative numbers.
14403
 
14404
;; PRINT-FP
14405
L2DE3:  RST     28H             ;; FP-CALC
14406
        DB    $31             ;;duplicate
14407
        DB    $36             ;;less-0
14408
        DB    $00             ;;jump-true
14409
 
14410
        DB    $0B             ;;to L2DF2, PF-NEGTVE
14411
 
14412
        DB    $31             ;;duplicate
14413
        DB    $37             ;;greater-0
14414
        DB    $00             ;;jump-true
14415
 
14416
        DB    $0D             ;;to L2DF8, PF-POSTVE
14417
 
14418
; must be zero itself
14419
 
14420
        DB    $02             ;;delete
14421
        DB    $38             ;;end-calc
14422
 
14423
        LD      A,$30           ; prepare the character '0'
14424
 
14425
        RST     10H             ; PRINT-A
14426
        RET                     ; return.                 ->
14427
; ---
14428
 
14429
;; PF-NEGTVE
14430
L2DF2:  DB    $2A             ;;abs
14431
        DB    $38             ;;end-calc
14432
 
14433
        LD      A,$2D           ; the character '-'
14434
 
14435
        RST     10H             ; PRINT-A
14436
 
14437
; and continue to print the now positive number.
14438
 
14439
        RST     28H             ;; FP-CALC
14440
 
14441
;; PF-POSTVE
14442
L2DF8:  DB    $A0             ;;stk-zero     x,0.     begin by
14443
        DB    $C3             ;;st-mem-3     x,0.     clearing a temporary
14444
        DB    $C4             ;;st-mem-4     x,0.     output buffer to
14445
        DB    $C5             ;;st-mem-5     x,0.     fifteen zeros.
14446
        DB    $02             ;;delete       x.
14447
        DB    $38             ;;end-calc     x.
14448
 
14449
        EXX                     ; in case called from 'str$' then save the
14450
        PUSH    HL              ; pointer to whatever comes after
14451
        EXX                     ; str$ as H'L' will be used.
14452
 
14453
; now enter a loop?
14454
 
14455
;; PF-LOOP
14456
L2E01:  RST     28H             ;; FP-CALC
14457
        DB    $31             ;;duplicate    x,x.
14458
        DB    $27             ;;int          x,int x.
14459
        DB    $C2             ;;st-mem-2     x,int x.
14460
        DB    $03             ;;subtract     x-int x.     fractional part.
14461
        DB    $E2             ;;get-mem-2    x-int x, int x.
14462
        DB    $01             ;;exchange     int x, x-int x.
14463
        DB    $C2             ;;st-mem-2     int x, x-int x.
14464
        DB    $02             ;;delete       int x.
14465
        DB    $38             ;;end-calc     int x.
14466
                                ;
14467
                                ; mem-2 holds the fractional part.
14468
 
14469
; HL points to last value int x
14470
 
14471
        LD      A,(HL)          ; fetch exponent of int x.
14472
        AND     A               ; test
14473
        JR      NZ,L2E56        ; forward to PF-LARGE if a large integer
14474
                                ; > 65535
14475
 
14476
; continue with small positive integer components in range 0 - 65535 
14477
; if original number was say .999 then this integer component is zero. 
14478
 
14479
        CALL    L2D7F           ; routine INT-FETCH gets x in DE
14480
                                ; (but x is not deleted)
14481
 
14482
        LD      B,$10           ; set B, bit counter, to 16d
14483
 
14484
        LD      A,D             ; test if
14485
        AND     A               ; high byte is zero
14486
        JR      NZ,L2E1E        ; forward to PF-SAVE if 16-bit integer.
14487
 
14488
; and continue with integer in range 0 - 255.
14489
 
14490
        OR      E               ; test the low byte for zero
14491
                                ; i.e. originally just point something or other.
14492
        JR      Z,L2E24         ; forward if so to PF-SMALL 
14493
 
14494
; 
14495
 
14496
        LD      D,E             ; transfer E to D
14497
        LD      B,$08           ; and reduce the bit counter to 8.
14498
 
14499
;; PF-SAVE
14500
L2E1E:  PUSH    DE              ; save the part before decimal point.
14501
        EXX                     ;
14502
        POP     DE              ; and pop in into D'E'
14503
        EXX                     ;
14504
        JR      L2E7B           ; forward to PF-BITS
14505
 
14506
; ---------------------
14507
 
14508
; the branch was here when 'int x' was found to be zero as in say 0.5.
14509
; The zero has been fetched from the calculator stack but not deleted and
14510
; this should occur now. This omission leaves the stack unbalanced and while
14511
; that causes no problems with a simple PRINT statement, it will if str$ is
14512
; being used in an expression e.g. "2" + STR$ 0.5 gives the result "0.5"
14513
; instead of the expected result "20.5".
14514
; credit Tony Stratton, 1982.
14515
; A DB 02 delete is required immediately on using the calculator.
14516
 
14517
;; PF-SMALL
14518
L2E24:  RST     28H             ;; FP-CALC       int x = 0.
14519
L2E25:  DB    $E2             ;;get-mem-2      int x = 0, x-int x.
14520
        DB    $38             ;;end-calc
14521
 
14522
        LD      A,(HL)          ; fetch exponent of positive fractional number
14523
        SUB     $7E             ; subtract 
14524
 
14525
        CALL    L2DC1           ; routine LOG(2^A) calculates leading digits.
14526
 
14527
        LD      D,A             ; transfer count to D
14528
        LD      A,($5CAC)       ; fetch total MEM-5-1
14529
        SUB     D               ;
14530
        LD      ($5CAC),A       ; MEM-5-1
14531
        LD      A,D             ; 
14532
        CALL    L2D4F           ; routine E-TO-FP
14533
 
14534
        RST     28H             ;; FP-CALC
14535
        DB    $31             ;;duplicate
14536
        DB    $27             ;;int
14537
        DB    $C1             ;;st-mem-1
14538
        DB    $03             ;;subtract
14539
        DB    $E1             ;;get-mem-1
14540
        DB    $38             ;;end-calc
14541
 
14542
        CALL    L2DD5           ; routine FP-TO-A
14543
 
14544
        PUSH    HL              ; save HL
14545
        LD      ($5CA1),A       ; MEM-3-1
14546
        DEC     A               ;
14547
        RLA                     ;
14548
        SBC     A,A             ;
14549
        INC     A               ;
14550
 
14551
        LD      HL,$5CAB        ; address MEM-5-1 leading digit counter
14552
        LD      (HL),A          ; store counter
14553
        INC     HL              ; address MEM-5-2 total digits
14554
        ADD     A,(HL)          ; add counter to contents
14555
        LD      (HL),A          ; and store updated value
14556
        POP     HL              ; restore HL
14557
 
14558
        JP      L2ECF           ; JUMP forward to PF-FRACTN
14559
 
14560
; ---
14561
 
14562
; Note. while it would be pedantic to comment on every occasion a JP
14563
; instruction could be replaced with a JR instruction, this applies to the
14564
; above, which is useful if you wish to correct the unbalanced stack error
14565
; by inserting a 'DB 02 delete' at L2E25, and maintain main addresses.
14566
 
14567
; the branch was here with a large positive integer > 65535 e.g. 123456789
14568
; the accumulator holds the exponent.
14569
 
14570
;; PF-LARGE
14571
L2E56:  SUB     $80             ; make exponent positive
14572
        CP      $1C             ; compare to 28
14573
        JR      C,L2E6F         ; to PF-MEDIUM if integer <= 2^27
14574
 
14575
        CALL    L2DC1           ; routine LOG(2^A)
14576
        SUB     $07             ;
14577
        LD      B,A             ;
14578
        LD      HL,$5CAC        ; address MEM-5-1 the leading digits counter.
14579
        ADD     A,(HL)          ; add A to contents
14580
        LD      (HL),A          ; store updated value.
14581
        LD      A,B             ; 
14582
        NEG                     ; negate
14583
        CALL    L2D4F           ; routine E-TO-FP
14584
        JR      L2E01           ; back to PF-LOOP
14585
 
14586
; ----------------------------
14587
 
14588
;; PF-MEDIUM
14589
L2E6F:  EX      DE,HL           ;
14590
        CALL    L2FBA           ; routine FETCH-TWO
14591
        EXX                     ;
14592
        SET     7,D             ;
14593
        LD      A,L             ;
14594
        EXX                     ;
14595
        SUB     $80             ;
14596
        LD      B,A             ;
14597
 
14598
; the branch was here to handle bits in DE with 8 or 16 in B  if small int
14599
; and integer in D'E', 6 nibbles will accommodate 065535 but routine does
14600
; 32-bit numbers as well from above
14601
 
14602
;; PF-BITS
14603
L2E7B:  SLA     E               ;  C<xxxxxxxx<0
14604
        RL      D               ;  C<xxxxxxxx<C
14605
        EXX                     ;
14606
        RL      E               ;  C<xxxxxxxx<C
14607
        RL      D               ;  C<xxxxxxxx<C
14608
        EXX                     ;
14609
 
14610
        LD      HL,$5CAA        ; set HL to mem-4-5th last byte of buffer
14611
        LD      C,$05           ; set byte count to 5 -  10 nibbles
14612
 
14613
;; PF-BYTES
14614
L2E8A:  LD      A,(HL)          ; fetch 0 or prev value
14615
        ADC     A,A             ; shift left add in carry    C<xxxxxxxx<C
14616
 
14617
        DAA                     ; Decimal Adjust Accumulator.
14618
                                ; if greater than 9 then the left hand
14619
                                ; nibble is incremented. If greater than
14620
                                ; 99 then adjusted and carry set.
14621
                                ; so if we'd built up 7 and a carry came in
14622
                                ;      0000 0111 < C
14623
                                ;      0000 1111
14624
                                ; daa     1 0101  which is 15 in BCD
14625
 
14626
        LD      (HL),A          ; put back
14627
        DEC     HL              ; work down thru mem 4
14628
        DEC     C               ; decrease the 5 counter.
14629
        JR      NZ,L2E8A        ; back to PF-BYTES until the ten nibbles rolled
14630
 
14631
        DJNZ    L2E7B           ; back to PF-BITS until 8 or 16 (or 32) done
14632
 
14633
; at most 9 digits for 32-bit number will have been loaded with digits
14634
; each of the 9 nibbles in mem 4 is placed into ten bytes in mem-3 and mem 4
14635
; unless the nibble is zero as the buffer is already zero.
14636
; ( or in the case of mem-5 will become zero as a result of RLD instruction )
14637
 
14638
        XOR     A               ; clear to accept
14639
        LD      HL,$5CA6        ; address MEM-4-0 byte destination.
14640
        LD      DE,$5CA1        ; address MEM-3-0 nibble source.
14641
        LD      B,$09           ; the count is 9 (not ten) as the first 
14642
                                ; nibble is known to be blank.
14643
 
14644
        RLD                     ; shift RH nibble to left in (HL)
14645
                                ;    A           (HL)
14646
                                ; 0000 0000 < 0000 3210
14647
                                ; 0000 0000   3210 0000
14648
                                ; A picks up the blank nibble
14649
 
14650
 
14651
        LD      C,$FF           ; set a flag to indicate when a significant
14652
                                ; digit has been encountered.
14653
 
14654
;; PF-DIGITS
14655
L2EA1:  RLD                     ; pick up leftmost nibble from (HL)
14656
                                ;    A           (HL)
14657
                                ; 0000 0000 < 7654 3210
14658
                                ; 0000 7654   3210 0000
14659
 
14660
 
14661
        JR      NZ,L2EA9        ; to PF-INSERT if non-zero value picked up.
14662
 
14663
        DEC     C               ; test
14664
        INC     C               ; flag
14665
        JR      NZ,L2EB3        ; skip forward to PF-TEST-2 if flag still $FF
14666
                                ; indicating this is a leading zero.
14667
 
14668
; but if the zero is a significant digit e.g. 10 then include in digit totals.
14669
; the path for non-zero digits rejoins here.
14670
 
14671
;; PF-INSERT
14672
L2EA9:  LD      (DE),A          ; insert digit at destination
14673
        INC     DE              ; increase the destination pointer
14674
        INC     (IY+$71)        ; increment MEM-5-1st  digit counter
14675
        INC     (IY+$72)        ; increment MEM-5-2nd  leading digit counter
14676
        LD      C,$00           ; set flag to zero indicating that any 
14677
                                ; subsequent zeros are significant and not 
14678
                                ; leading.
14679
 
14680
;; PF-TEST-2
14681
L2EB3:  BIT     0,B             ; test if the nibble count is even
14682
        JR      Z,L2EB8         ; skip to PF-ALL-9 if so to deal with the
14683
                                ; other nibble in the same byte
14684
 
14685
        INC     HL              ; point to next source byte if not
14686
 
14687
;; PF-ALL-9
14688
L2EB8:  DJNZ    L2EA1           ; decrement the nibble count, back to PF-DIGITS
14689
                                ; if all nine not done.
14690
 
14691
; For 8-bit integers there will be at most 3 digits.
14692
; For 16-bit integers there will be at most 5 digits. 
14693
; but for larger integers there could be nine leading digits.
14694
; if nine digits complete then the last one is rounded up as the number will
14695
; be printed using E-format notation
14696
 
14697
        LD      A,($5CAB)       ; fetch digit count from MEM-5-1st
14698
        SUB     $09             ; subtract 9 - max possible
14699
        JR      C,L2ECB         ; forward if less to PF-MORE
14700
 
14701
        DEC     (IY+$71)        ; decrement digit counter MEM-5-1st to 8
14702
        LD      A,$04           ; load A with the value 4.
14703
        CP      (IY+$6F)        ; compare with MEM-4-4th - the ninth digit
14704
        JR      L2F0C           ; forward to PF-ROUND
14705
                                ; to consider rounding.
14706
 
14707
; ---------------------------------------
14708
 
14709
; now delete int x from calculator stack and fetch fractional part.
14710
 
14711
;; PF-MORE
14712
L2ECB:  RST     28H             ;; FP-CALC        int x.
14713
        DB    $02             ;;delete          .
14714
        DB    $E2             ;;get-mem-2       x - int x = f.
14715
        DB    $38             ;;end-calca       f.
14716
 
14717
;; PF-FRACTN
14718
L2ECF:  EX      DE,HL           ;
14719
        CALL    L2FBA           ; routine FETCH-TWO
14720
        EXX                     ;
14721
        LD      A,$80           ;
14722
        SUB     L               ;
14723
        LD      L,$00           ;
14724
        SET     7,D             ;
14725
        EXX                     ;
14726
        CALL    L2FDD           ; routine SHIFT-FP
14727
 
14728
;; PF-FRN-LP
14729
L2EDF:  LD      A,(IY+$71)      ; MEM-5-1st
14730
        CP      $08             ;
14731
        JR      C,L2EEC         ; to PF-FR-DGT
14732
 
14733
        EXX                     ;
14734
        RL      D               ;
14735
        EXX                     ;
14736
        JR      L2F0C           ; to PF-ROUND
14737
 
14738
; ---
14739
 
14740
;; PF-FR-DGT
14741
L2EEC:  LD      BC,$0200        ;
14742
 
14743
;; PF-FR-EXX
14744
L2EEF:  LD      A,E             ;
14745
        CALL    L2F8B           ; routine CA-10*A+C
14746
        LD      E,A             ;
14747
        LD      A,D             ;
14748
        CALL    L2F8B           ; routine CA-10*A+C
14749
        LD      D,A             ;
14750
        PUSH    BC              ;
14751
        EXX                     ;
14752
        POP     BC              ;
14753
        DJNZ    L2EEF           ; to PF-FR-EXX
14754
 
14755
        LD      HL,$5CA1        ; MEM-3
14756
        LD      A,C             ;
14757
        LD      C,(IY+$71)      ; MEM-5-1st
14758
        ADD     HL,BC           ;
14759
        LD      (HL),A          ;
14760
        INC     (IY+$71)        ; MEM-5-1st
14761
        JR      L2EDF           ; to PF-FRN-LP
14762
 
14763
; ----------------
14764
 
14765
; 1) with 9 digits but 8 in mem-5-1 and A holding 4, carry set if rounding up.
14766
; e.g. 
14767
;      999999999 is printed as 1E+9
14768
;      100000001 is printed as 1E+8
14769
;      100000009 is printed as 1.0000001E+8
14770
 
14771
;; PF-ROUND
14772
L2F0C:  PUSH    AF              ; save A and flags
14773
        LD      HL,$5CA1        ; address MEM-3 start of digits
14774
        LD      C,(IY+$71)      ; MEM-5-1st No. of digits to C
14775
        LD      B,$00           ; prepare to add
14776
        ADD     HL,BC           ; address last digit + 1
14777
        LD      B,C             ; No. of digits to B counter
14778
        POP     AF              ; restore A and carry flag from comparison.
14779
 
14780
;; PF-RND-LP
14781
L2F18:  DEC     HL              ; address digit at rounding position.
14782
        LD      A,(HL)          ; fetch it
14783
        ADC     A,$00           ; add carry from the comparison
14784
        LD      (HL),A          ; put back result even if $0A.
14785
        AND     A               ; test A
14786
        JR      Z,L2F25         ; skip to PF-R-BACK if ZERO?
14787
 
14788
        CP      $0A             ; compare to 'ten' - overflow
14789
        CCF                     ; complement carry flag so that set if ten.
14790
        JR      NC,L2F2D        ; forward to PF-COUNT with 1 - 9.
14791
 
14792
;; PF-R-BACK
14793
L2F25:  DJNZ    L2F18           ; loop back to PF-RND-LP
14794
 
14795
; if B counts down to zero then we've rounded right back as in 999999995.
14796
; and the first 8 locations all hold $0A.
14797
 
14798
 
14799
        LD      (HL),$01        ; load first location with digit 1.
14800
        INC     B               ; make B hold 1 also.
14801
                                ; could save an instruction byte here.
14802
        INC     (IY+$72)        ; make MEM-5-2nd hold 1.
14803
                                ; and proceed to initialize total digits to 1.
14804
 
14805
;; PF-COUNT
14806
L2F2D:  LD      (IY+$71),B      ; MEM-5-1st
14807
 
14808
; now balance the calculator stack by deleting  it
14809
 
14810
        RST     28H             ;; FP-CALC
14811
        DB    $02             ;;delete
14812
        DB    $38             ;;end-calc
14813
 
14814
; note if used from str$ then other values may be on the calculator stack.
14815
; we can also restore the next literal pointer from its position on the
14816
; machine stack.
14817
 
14818
        EXX                     ;
14819
        POP     HL              ; restore next literal pointer.
14820
        EXX                     ;
14821
 
14822
        LD      BC,($5CAB)      ; set C to MEM-5-1st digit counter.
14823
                                ; set B to MEM-5-2nd leading digit counter.
14824
        LD      HL,$5CA1        ; set HL to start of digits at MEM-3-1
14825
        LD      A,B             ;
14826
        CP      $09             ;
14827
        JR      C,L2F46         ; to PF-NOT-E
14828
 
14829
        CP      $FC             ;
14830
        JR      C,L2F6C         ; to PF-E-FRMT
14831
 
14832
;; PF-NOT-E
14833
L2F46:  AND     A               ; test for zero leading digits as in .123
14834
 
14835
        CALL    Z,L15EF         ; routine OUT-CODE prints a zero e.g. 0.123
14836
 
14837
;; PF-E-SBRN
14838
L2F4A:  XOR     A               ;
14839
        SUB     B               ;
14840
        JP      M,L2F52         ; skip forward to PF-OUT-LP if originally +ve
14841
 
14842
        LD      B,A             ; else negative count now +ve
14843
        JR      L2F5E           ; forward to PF-DC-OUT       ->
14844
 
14845
; ---
14846
 
14847
;; PF-OUT-LP
14848
L2F52:  LD      A,C             ; fetch total digit count
14849
        AND     A               ; test for zero
14850
        JR      Z,L2F59         ; forward to PF-OUT-DT if so
14851
 
14852
        LD      A,(HL)          ; fetch digit
14853
        INC     HL              ; address next digit
14854
        DEC     C               ; decrease total digit counter
14855
 
14856
;; PF-OUT-DT
14857
L2F59:  CALL    L15EF           ; routine OUT-CODE outputs it.
14858
        DJNZ    L2F52           ; loop back to PF-OUT-LP until B leading 
14859
                                ; digits output.
14860
 
14861
;; PF-DC-OUT
14862
L2F5E:  LD      A,C             ; fetch total digits and
14863
        AND     A               ; test if also zero
14864
        RET     Z               ; return if so              -->
14865
 
14866
; 
14867
 
14868
        INC     B               ; increment B
14869
        LD      A,$2E           ; prepare the character '.'
14870
 
14871
;; PF-DEC-0$
14872
L2F64:  RST     10H             ; PRINT-A outputs the character '.' or '0'
14873
 
14874
        LD      A,$30           ; prepare the character '0'
14875
                                ; (for cases like .000012345678)
14876
        DJNZ    L2F64           ; loop back to PF-DEC-0$ for B times.
14877
 
14878
        LD      B,C             ; load B with now trailing digit counter.
14879
        JR      L2F52           ; back to PF-OUT-LP
14880
 
14881
; ---------------------------------
14882
 
14883
; the branch was here for E-format printing e.g 123456789 => 1.2345679e+8
14884
 
14885
;; PF-E-FRMT
14886
L2F6C:  LD      D,B             ; counter to D
14887
        DEC     D               ; decrement
14888
        LD      B,$01           ; load B with 1.
14889
 
14890
        CALL    L2F4A           ; routine PF-E-SBRN above
14891
 
14892
        LD      A,$45           ; prepare character 'e'
14893
        RST     10H             ; PRINT-A
14894
 
14895
        LD      C,D             ; exponent to C
14896
        LD      A,C             ; and to A
14897
        AND     A               ; test exponent
14898
        JP      P,L2F83         ; to PF-E-POS if positive
14899
 
14900
        NEG                     ; negate
14901
        LD      C,A             ; positive exponent to C
14902
        LD      A,$2D           ; prepare character '-'
14903
        JR      L2F85           ; skip to PF-E-SIGN
14904
 
14905
; ---
14906
 
14907
;; PF-E-POS
14908
L2F83:  LD      A,$2B           ; prepare character '+'
14909
 
14910
;; PF-E-SIGN
14911
L2F85:  RST     10H             ; PRINT-A outputs the sign
14912
 
14913
        LD      B,$00           ; make the high byte zero.
14914
        JP      L1A1B           ; exit via OUT-NUM-1 to print exponent in BC
14915
 
14916
; ------------------------------
14917
; Handle printing floating point
14918
; ------------------------------
14919
; This subroutine is called twice from above when printing floating-point
14920
; numbers. It returns 10*A +C in registers C and A
14921
 
14922
;; CA-10*A+C
14923
L2F8B:  PUSH    DE              ; preserve DE.
14924
        LD      L,A             ; transfer A to L
14925
        LD      H,$00           ; zero high byte.
14926
        LD      E,L             ; copy HL
14927
        LD      D,H             ; to DE.
14928
        ADD     HL,HL           ; double (*2)
14929
        ADD     HL,HL           ; double (*4)
14930
        ADD     HL,DE           ; add DE (*5)
14931
        ADD     HL,HL           ; double (*10)
14932
        LD      E,C             ; copy C to E    (D is 0)
14933
        ADD     HL,DE           ; and add to give required result.
14934
        LD      C,H             ; transfer to
14935
        LD      A,L             ; destination registers.
14936
        POP     DE              ; restore DE
14937
        RET                     ; return with result.
14938
 
14939
; --------------
14940
; Prepare to add
14941
; --------------
14942
; This routine is called twice by addition to prepare the two numbers. The
14943
; exponent is picked up in A and the location made zero. Then the sign bit
14944
; is tested before being set to the implied state. Negative numbers are twos
14945
; complemented.
14946
 
14947
;; PREP-ADD
14948
L2F9B:  LD      A,(HL)          ; pick up exponent
14949
        LD      (HL),$00        ; make location zero
14950
        AND     A               ; test if number is zero
14951
        RET     Z               ; return if so
14952
 
14953
        INC     HL              ; address mantissa
14954
        BIT     7,(HL)          ; test the sign bit
14955
        SET     7,(HL)          ; set it to implied state
14956
        DEC     HL              ; point to exponent
14957
        RET     Z               ; return if positive number.
14958
 
14959
        PUSH    BC              ; preserve BC
14960
        LD      BC,$0005        ; length of number
14961
        ADD     HL,BC           ; point HL past end
14962
        LD      B,C             ; set B to 5 counter
14963
        LD      C,A             ; store exponent in C
14964
        SCF                     ; set carry flag
14965
 
14966
;; NEG-BYTE
14967
L2FAF:  DEC     HL              ; work from LSB to MSB
14968
        LD      A,(HL)          ; fetch byte
14969
        CPL                     ; complement
14970
        ADC     A,$00           ; add in initial carry or from prev operation
14971
        LD      (HL),A          ; put back
14972
        DJNZ    L2FAF           ; loop to NEG-BYTE till all 5 done
14973
 
14974
        LD      A,C             ; stored exponent to A
14975
        POP     BC              ; restore original BC
14976
        RET                     ; return
14977
 
14978
; -----------------
14979
; Fetch two numbers
14980
; -----------------
14981
; This routine is called twice when printing floating point numbers and also
14982
; to fetch two numbers by the addition, multiply and division routines.
14983
; HL addresses the first number, DE addresses the second number.
14984
; For arithmetic only, A holds the sign of the result which is stored in
14985
; the second location. 
14986
 
14987
;; FETCH-TWO
14988
L2FBA:  PUSH    HL              ; save pointer to first number, result if math.
14989
        PUSH    AF              ; save result sign.
14990
 
14991
        LD      C,(HL)          ;
14992
        INC     HL              ;
14993
 
14994
        LD      B,(HL)          ;
14995
        LD      (HL),A          ; store the sign at correct location in 
14996
                                ; destination 5 bytes for arithmetic only.
14997
        INC     HL              ;
14998
 
14999
        LD      A,C             ;
15000
        LD      C,(HL)          ;
15001
        PUSH    BC              ;
15002
        INC     HL              ;
15003
        LD      C,(HL)          ;
15004
        INC     HL              ;
15005
        LD      B,(HL)          ;
15006
        EX      DE,HL           ;
15007
        LD      D,A             ;
15008
        LD      E,(HL)          ;
15009
        PUSH    DE              ;
15010
        INC     HL              ;
15011
        LD      D,(HL)          ;
15012
        INC     HL              ;
15013
        LD      E,(HL)          ;
15014
        PUSH    DE              ;
15015
        EXX                     ;
15016
        POP     DE              ;
15017
        POP     HL              ;
15018
        POP     BC              ;
15019
        EXX                     ;
15020
        INC     HL              ;
15021
        LD      D,(HL)          ;
15022
        INC     HL              ;
15023
        LD      E,(HL)          ;
15024
 
15025
        POP     AF              ; restore possible result sign.
15026
        POP     HL              ; and pointer to possible result.
15027
        RET                     ; return.
15028
 
15029
; ---------------------------------
15030
; Shift floating point number right
15031
; ---------------------------------
15032
;
15033
;
15034
 
15035
;; SHIFT-FP
15036
L2FDD:  AND     A               ;
15037
        RET     Z               ;
15038
 
15039
        CP      $21             ;
15040
        JR      NC,L2FF9        ; to ADDEND-0
15041
 
15042
        PUSH    BC              ;
15043
        LD      B,A             ;
15044
 
15045
;; ONE-SHIFT
15046
L2FE5:  EXX                     ;
15047
        SRA     L               ;
15048
        RR      D               ;
15049
        RR      E               ;
15050
        EXX                     ;
15051
        RR      D               ;
15052
        RR      E               ;
15053
        DJNZ    L2FE5           ; to ONE-SHIFT
15054
 
15055
        POP     BC              ;
15056
        RET     NC              ;
15057
 
15058
        CALL    L3004           ; routine ADD-BACK
15059
        RET     NZ              ;
15060
 
15061
;; ADDEND-0
15062
L2FF9:  EXX                     ;
15063
        XOR     A               ;
15064
 
15065
;; ZEROS-4/5
15066
L2FFB:  LD      L,$00           ;
15067
        LD      D,A             ;
15068
        LD      E,L             ;
15069
        EXX                     ;
15070
        LD      DE,$0000        ;
15071
        RET                     ;
15072
 
15073
; ------------------
15074
; Add back any carry
15075
; ------------------
15076
;
15077
;
15078
 
15079
;; ADD-BACK
15080
L3004:  INC     E               ;
15081
        RET     NZ              ;
15082
 
15083
        INC      D              ;
15084
        RET     NZ              ;
15085
 
15086
        EXX                     ;
15087
        INC     E               ;
15088
        JR      NZ,L300D        ; to ALL-ADDED
15089
 
15090
        INC     D               ;
15091
 
15092
;; ALL-ADDED
15093
L300D:  EXX                     ;
15094
        RET                     ;
15095
 
15096
; -----------------------
15097
; Handle subtraction (03)
15098
; -----------------------
15099
; Subtraction is done by switching the sign byte/bit of the second number
15100
; which may be integer of floating point and continuing into addition.
15101
 
15102
;; subtract
15103
L300F:  EX      DE,HL           ; address second number with HL
15104
 
15105
        CALL    L346E           ; routine NEGATE switches sign
15106
 
15107
        EX      DE,HL           ; address first number again
15108
                                ; and continue.
15109
 
15110
; --------------------
15111
; Handle addition (0F)
15112
; --------------------
15113
; HL points to first number, DE to second.
15114
; If they are both integers, then go for the easy route.
15115
 
15116
;; addition
15117
L3014:  LD      A,(DE)          ; fetch first byte of second
15118
        OR      (HL)            ; combine with first byte of first
15119
        JR      NZ,L303E        ; forward to FULL-ADDN if at least one was
15120
                                ; in floating point form.
15121
 
15122
; continue if both were small integers.
15123
 
15124
        PUSH    DE              ; save pointer to lowest number for result.
15125
 
15126
        INC     HL              ; address sign byte and
15127
        PUSH    HL              ; push the pointer.
15128
 
15129
        INC     HL              ; address low byte
15130
        LD      E,(HL)          ; to E
15131
        INC     HL              ; address high byte
15132
        LD      D,(HL)          ; to D
15133
        INC     HL              ; address unused byte
15134
 
15135
        INC     HL              ; address known zero indicator of 1st number
15136
        INC     HL              ; address sign byte
15137
 
15138
        LD      A,(HL)          ; sign to A, $00 or $FF
15139
 
15140
        INC     HL              ; address low byte
15141
        LD      C,(HL)          ; to C
15142
        INC     HL              ; address high byte
15143
        LD      B,(HL)          ; to B
15144
 
15145
        POP     HL              ; pop result sign pointer
15146
        EX      DE,HL           ; integer to HL
15147
 
15148
        ADD     HL,BC           ; add to the other one in BC
15149
                                ; setting carry if overflow.
15150
 
15151
        EX      DE,HL           ; save result in DE bringing back sign pointer
15152
 
15153
        ADC     A,(HL)          ; if pos/pos A=01 with overflow else 00
15154
                                ; if neg/neg A=FF with overflow else FE
15155
                                ; if mixture A=00 with overflow else FF
15156
 
15157
        RRCA                    ; bit 0 to (C)
15158
 
15159
        ADC     A,$00           ; both acceptable signs now zero
15160
 
15161
        JR      NZ,L303C        ; forward to ADDN-OFLW if not
15162
 
15163
        SBC     A,A             ; restore a negative result sign
15164
 
15165
        LD      (HL),A          ;
15166
        INC     HL              ;
15167
        LD      (HL),E          ;
15168
        INC     HL              ;
15169
        LD      (HL),D          ;
15170
        DEC     HL              ;
15171
        DEC     HL              ;
15172
        DEC     HL              ;
15173
 
15174
        POP     DE              ; STKEND
15175
        RET                     ;
15176
 
15177
; ---
15178
 
15179
;; ADDN-OFLW
15180
L303C:  DEC     HL              ;
15181
        POP     DE              ;
15182
 
15183
;; FULL-ADDN
15184
L303E:  CALL    L3293           ; routine RE-ST-TWO
15185
        EXX                     ;
15186
        PUSH    HL              ;
15187
        EXX                     ;
15188
        PUSH    DE              ;
15189
        PUSH    HL              ;
15190
        CALL    L2F9B           ; routine PREP-ADD
15191
        LD      B,A             ;
15192
        EX      DE,HL           ;
15193
        CALL    L2F9B           ; routine PREP-ADD
15194
        LD       C,A            ;
15195
        CP      B               ;
15196
        JR      NC,L3055        ; to SHIFT-LEN
15197
 
15198
        LD      A,B             ;
15199
        LD      B,C             ;
15200
        EX      DE,HL           ;
15201
 
15202
;; SHIFT-LEN
15203
L3055:  PUSH    AF              ;
15204
        SUB     B               ;
15205
        CALL    L2FBA           ; routine FETCH-TWO
15206
        CALL    L2FDD           ; routine SHIFT-FP
15207
        POP     AF              ;
15208
        POP     HL              ;
15209
        LD      (HL),A          ;
15210
        PUSH    HL              ;
15211
        LD      L,B             ;
15212
        LD      H,C             ;
15213
        ADD     HL,DE           ;
15214
        EXX                     ;
15215
        EX      DE,HL           ;
15216
        ADC     HL,BC           ;
15217
        EX      DE,HL           ;
15218
        LD      A,H             ;
15219
        ADC     A,L             ;
15220
        LD      L,A             ;
15221
        RRA                     ;
15222
        XOR     L               ;
15223
        EXX                     ;
15224
        EX      DE,HL           ;
15225
        POP     HL              ;
15226
        RRA                     ;
15227
        JR      NC,L307C        ; to TEST-NEG
15228
 
15229
        LD      A,$01           ;
15230
        CALL    L2FDD           ; routine SHIFT-FP
15231
        INC     (HL)            ;
15232
        JR      Z,L309F         ; to ADD-REP-6
15233
 
15234
;; TEST-NEG
15235
L307C:  EXX                     ;
15236
        LD      A,L             ;
15237
        AND     $80             ;
15238
        EXX                     ;
15239
        INC     HL              ;
15240
        LD      (HL),A          ;
15241
        DEC     HL              ;
15242
        JR      Z,L30A5         ; to GO-NC-MLT
15243
 
15244
        LD      A,E             ;
15245
        NEG                     ; Negate
15246
        CCF                     ; Complement Carry Flag
15247
        LD      E,A             ;
15248
        LD      A,D             ;
15249
        CPL                     ;
15250
        ADC     A,$00           ;
15251
        LD      D,A             ;
15252
        EXX                     ;
15253
        LD      A,E             ;
15254
        CPL                     ;
15255
        ADC     A,$00           ;
15256
        LD      E,A             ;
15257
        LD      A,D             ;
15258
        CPL                     ;
15259
        ADC     A,$00           ;
15260
        JR      NC,L30A3        ; to END-COMPL
15261
 
15262
        RRA                     ;
15263
        EXX                     ;
15264
        INC     (HL)            ;
15265
 
15266
;; ADD-REP-6
15267
L309F:  JP      Z,L31AD         ; to REPORT-6
15268
 
15269
        EXX                     ;
15270
 
15271
;; END-COMPL
15272
L30A3:  LD      D,A             ;
15273
        EXX                     ;
15274
 
15275
;; GO-NC-MLT
15276
L30A5:  XOR     A               ;
15277
        JP      L3155           ; to TEST-NORM
15278
 
15279
; -----------------------------
15280
; Used in 16 bit multiplication
15281
; -----------------------------
15282
; This routine is used, in the first instance, by the multiply calculator
15283
; literal to perform an integer multiplication in preference to
15284
; 32-bit multiplication to which it will resort if this overflows.
15285
;
15286
; It is also used by STK-VAR to calculate array subscripts and by DIM to
15287
; calculate the space required for multi-dimensional arrays.
15288
 
15289
;; HL-HL*DE
15290
L30A9:  PUSH    BC              ; preserve BC throughout
15291
        LD      B,$10           ; set B to 16
15292
        LD      A,H             ; save H in A high byte
15293
        LD      C,L             ; save L in C low byte
15294
        LD      HL,$0000        ; initialize result to zero
15295
 
15296
; now enter a loop.
15297
 
15298
;; HL-LOOP
15299
L30B1:  ADD     HL,HL           ; double result
15300
        JR      C,L30BE         ; to HL-END if overflow
15301
 
15302
        RL      C               ; shift AC left into carry
15303
        RLA                     ;
15304
        JR      NC,L30BC        ; to HL-AGAIN to skip addition if no carry
15305
 
15306
        ADD     HL,DE           ; add in DE
15307
        JR      C,L30BE         ; to HL-END if overflow
15308
 
15309
;; HL-AGAIN
15310
L30BC:  DJNZ    L30B1           ; back to HL-LOOP for all 16 bits
15311
 
15312
;; HL-END
15313
L30BE:  POP     BC              ; restore preserved BC
15314
        RET                     ; return with carry reset if successful
15315
                                ; and result in HL.
15316
 
15317
; -----------------------------
15318
; Prepare to multiply or divide
15319
; -----------------------------
15320
; This routine is called in succession from multiply and divide to prepare
15321
; two mantissas by setting the leftmost bit that is used for the sign.
15322
; On the first call A holds zero and picks up the sign bit. On the second
15323
; call the two bits are XORed to form the result sign - minus * minus giving
15324
; plus etc. If either number is zero then this is flagged.
15325
; HL addresses the exponent.
15326
 
15327
;; PREP-M/D
15328
L30C0:  CALL    L34E9           ; routine TEST-ZERO  preserves accumulator.
15329
        RET     C               ; return carry set if zero
15330
 
15331
        INC     HL              ; address first byte of mantissa
15332
        XOR     (HL)            ; pick up the first or xor with first.
15333
        SET     7,(HL)          ; now set to give true 32-bit mantissa
15334
        DEC     HL              ; point to exponent
15335
        RET                     ; return with carry reset
15336
 
15337
; --------------------------
15338
; Handle multiplication (04)
15339
; --------------------------
15340
;
15341
;
15342
 
15343
;; multiply
15344
L30CA:  LD      A,(DE)          ;
15345
        OR      (HL)            ;
15346
        JR      NZ,L30F0        ; to MULT-LONG
15347
 
15348
        PUSH    DE              ;
15349
        PUSH    HL              ;
15350
        PUSH    DE              ;
15351
        CALL    L2D7F           ; routine INT-FETCH
15352
        EX      DE,HL           ;
15353
        EX      (SP),HL         ;
15354
        LD      B,C             ;
15355
        CALL    L2D7F           ; routine INT-FETCH
15356
        LD      A,B             ;
15357
        XOR     C               ;
15358
        LD      C,A             ;
15359
        POP     HL              ;
15360
        CALL    L30A9           ; routine HL-HL*DE
15361
        EX      DE,HL           ;
15362
        POP     HL              ;
15363
        JR      C,L30EF         ; to MULT-OFLW
15364
 
15365
        LD      A,D             ;
15366
        OR      E               ;
15367
        JR      NZ,L30EA        ; to MULT-RSLT
15368
 
15369
        LD      C,A             ;
15370
 
15371
;; MULT-RSLT
15372
L30EA:  CALL    L2D8E           ; routine INT-STORE
15373
        POP      DE             ;
15374
        RET                     ;
15375
 
15376
; ---
15377
 
15378
;; MULT-OFLW
15379
L30EF:  POP     DE              ;
15380
 
15381
;; MULT-LONG
15382
L30F0:  CALL    L3293           ; routine RE-ST-TWO
15383
        XOR     A               ;
15384
        CALL    L30C0           ; routine PREP-M/D
15385
        RET     C               ;
15386
 
15387
        EXX                     ;
15388
        PUSH    HL              ;
15389
        EXX                     ;
15390
        PUSH    DE              ;
15391
        EX      DE,HL           ;
15392
        CALL    L30C0           ; routine PREP-M/D
15393
        EX      DE,HL           ;
15394
        JR      C,L315D         ; to ZERO-RSLT
15395
 
15396
        PUSH    HL              ;
15397
        CALL    L2FBA           ; routine FETCH-TWO
15398
        LD      A,B             ;
15399
        AND     A               ;
15400
        SBC     HL,HL           ;
15401
        EXX                     ;
15402
        PUSH    HL              ;
15403
        SBC     HL,HL           ;
15404
        EXX                     ;
15405
        LD      B,$21           ;
15406
        JR      L3125           ; to STRT-MLT
15407
 
15408
; ---
15409
 
15410
;; MLT-LOOP
15411
L3114:  JR      NC,L311B        ; to NO-ADD
15412
 
15413
        ADD     HL,DE           ;
15414
        EXX                     ;
15415
        ADC     HL,DE           ;
15416
        EXX                     ;
15417
 
15418
;; NO-ADD
15419
L311B:  EXX                     ;
15420
        RR      H               ;
15421
        RR      L               ;
15422
        EXX                     ;
15423
        RR      H               ;
15424
        RR      L               ;
15425
 
15426
;; STRT-MLT
15427
L3125:  EXX                     ;
15428
        RR      B               ;
15429
        RR      C               ;
15430
        EXX                     ;
15431
        RR      C               ;
15432
        RRA                     ;
15433
        DJNZ    L3114           ; to MLT-LOOP
15434
 
15435
        EX      DE,HL           ;
15436
        EXX                     ;
15437
        EX      DE,HL           ;
15438
        EXX                     ;
15439
        POP     BC              ;
15440
        POP     HL              ;
15441
        LD      A,B             ;
15442
        ADD     A,C             ;
15443
        JR      NZ,L313B        ; to MAKE-EXPT
15444
 
15445
        AND     A               ;
15446
 
15447
;; MAKE-EXPT
15448
L313B:  DEC     A               ;
15449
        CCF                     ; Complement Carry Flag
15450
 
15451
;; DIVN-EXPT
15452
L313D:  RLA                     ;
15453
        CCF                     ; Complement Carry Flag
15454
        RRA                     ;
15455
        JP      P,L3146         ; to OFLW1-CLR
15456
 
15457
        JR      NC,L31AD        ; to REPORT-6
15458
 
15459
        AND     A               ;
15460
 
15461
;; OFLW1-CLR
15462
L3146:  INC     A               ;
15463
        JR      NZ,L3151        ; to OFLW2-CLR
15464
 
15465
        JR      C,L3151         ; to OFLW2-CLR
15466
 
15467
        EXX                     ;
15468
        BIT     7,D             ;
15469
        EXX                     ;
15470
        JR      NZ,L31AD        ; to REPORT-6
15471
 
15472
;; OFLW2-CLR
15473
L3151:  LD      (HL),A          ;
15474
        EXX                     ;
15475
        LD      A,B             ;
15476
        EXX                     ;
15477
 
15478
;; TEST-NORM
15479
L3155:  JR      NC,L316C        ; to NORMALISE
15480
 
15481
        LD      A,(HL)          ;
15482
        AND     A               ;
15483
 
15484
;; NEAR-ZERO
15485
L3159:  LD      A,$80           ;
15486
        JR      Z,L315E         ; to SKIP-ZERO
15487
 
15488
;; ZERO-RSLT
15489
L315D:  XOR     A               ;
15490
 
15491
;; SKIP-ZERO
15492
L315E:  EXX                     ;
15493
        AND     D               ;
15494
        CALL    L2FFB           ; routine ZEROS-4/5
15495
        RLCA                    ;
15496
        LD      (HL),A          ;
15497
        JR      C,L3195         ; to OFLOW-CLR
15498
 
15499
        INC     HL              ;
15500
        LD      (HL),A          ;
15501
        DEC     HL              ;
15502
        JR      L3195           ; to OFLOW-CLR
15503
 
15504
; ---
15505
 
15506
;; NORMALISE
15507
L316C:  LD      B,$20           ;
15508
 
15509
;; SHIFT-ONE
15510
L316E:  EXX                     ;
15511
        BIT     7,D             ;
15512
        EXX                     ;
15513
        JR      NZ,L3186        ; to NORML-NOW
15514
 
15515
        RLCA                    ;
15516
        RL      E               ;
15517
        RL      D               ;
15518
        EXX                     ;
15519
        RL      E               ;
15520
        RL      D               ;
15521
        EXX                     ;
15522
        DEC     (HL)            ;
15523
        JR      Z,L3159         ; to NEAR-ZERO
15524
 
15525
        DJNZ    L316E           ; to SHIFT-ONE
15526
 
15527
        JR      L315D           ; to ZERO-RSLT
15528
 
15529
; ---
15530
 
15531
;; NORML-NOW
15532
L3186:  RLA                     ;
15533
        JR      NC,L3195        ; to OFLOW-CLR
15534
 
15535
        CALL    L3004           ; routine ADD-BACK
15536
        JR      NZ,L3195        ; to OFLOW-CLR
15537
 
15538
        EXX                     ;
15539
        LD       D,$80          ;
15540
        EXX                     ;
15541
        INC     (HL)            ;
15542
        JR      Z,L31AD         ; to REPORT-6
15543
 
15544
;; OFLOW-CLR
15545
L3195:  PUSH    HL              ;
15546
        INC     HL              ;
15547
        EXX                     ;
15548
        PUSH    DE              ;
15549
        EXX                     ;
15550
        POP     BC              ;
15551
        LD      A,B             ;
15552
        RLA                     ;
15553
        RL      (HL)            ;
15554
        RRA                     ;
15555
        LD      (HL),A          ;
15556
        INC     HL              ;
15557
        LD      (HL),C          ;
15558
        INC     HL              ;
15559
        LD      (HL),D          ;
15560
        INC     HL              ;
15561
        LD      (HL),E          ;
15562
        POP     HL              ;
15563
        POP     DE              ;
15564
        EXX                     ;
15565
        POP     HL              ;
15566
        EXX                     ;
15567
        RET                     ;
15568
 
15569
; ---
15570
 
15571
;; REPORT-6
15572
L31AD:  RST     08H             ; ERROR-1
15573
        DB    $05             ; Error Report: Number too big
15574
 
15575
; --------------------
15576
; Handle division (05)
15577
; --------------------
15578
;
15579
;
15580
 
15581
;; division
15582
L31AF:  CALL    L3293           ; routine RE-ST-TWO
15583
        EX      DE,HL           ;
15584
        XOR     A               ;
15585
        CALL    L30C0           ; routine PREP-M/D
15586
        JR      C,L31AD         ; to REPORT-6
15587
 
15588
        EX      DE,HL           ;
15589
        CALL    L30C0           ; routine PREP-M/D
15590
        RET     C               ;
15591
 
15592
        EXX                     ;
15593
        PUSH    HL              ;
15594
        EXX                     ;
15595
        PUSH    DE              ;
15596
        PUSH    HL              ;
15597
        CALL    L2FBA           ; routine FETCH-TWO
15598
        EXX                     ;
15599
        PUSH    HL              ;
15600
        LD      H,B             ;
15601
        LD      L,C             ;
15602
        EXX                     ;
15603
        LD      H,C             ;
15604
        LD      L,B             ;
15605
        XOR     A               ;
15606
        LD      B,$DF           ;
15607
        JR      L31E2           ; to DIV-START
15608
 
15609
; ---
15610
 
15611
;; DIV-LOOP
15612
L31D2:  RLA                     ;
15613
        RL      C               ;
15614
        EXX                     ;
15615
        RL      C               ;
15616
        RL      B               ;
15617
        EXX                     ;
15618
 
15619
;; div-34th
15620
L31DB:  ADD     HL,HL           ;
15621
        EXX                     ;
15622
        ADC     HL,HL           ;
15623
        EXX                     ;
15624
        JR      C,L31F2         ; to SUBN-ONLY
15625
 
15626
;; DIV-START
15627
L31E2:  SBC     HL,DE           ;
15628
        EXX                     ;
15629
        SBC     HL,DE           ;
15630
        EXX                     ;
15631
        JR      NC,L31F9        ; to NO-RSTORE
15632
 
15633
        ADD     HL,DE           ;
15634
        EXX                     ;
15635
        ADC     HL,DE           ;
15636
        EXX                     ;
15637
        AND     A               ;
15638
        JR      L31FA           ; to COUNT-ONE
15639
 
15640
; ---
15641
 
15642
;; SUBN-ONLY
15643
L31F2:  AND     A               ;
15644
        SBC     HL,DE           ;
15645
        EXX                     ;
15646
        SBC     HL,DE           ;
15647
        EXX                     ;
15648
 
15649
;; NO-RSTORE
15650
L31F9:  SCF                     ; Set Carry Flag
15651
 
15652
;; COUNT-ONE
15653
L31FA:  INC     B               ;
15654
        JP      M,L31D2         ; to DIV-LOOP
15655
 
15656
        PUSH    AF              ;
15657
        JR      Z,L31E2         ; to DIV-START
15658
 
15659
;
15660
;
15661
;
15662
;
15663
 
15664
        LD      E,A             ;
15665
        LD      D,C             ;
15666
        EXX                     ;
15667
        LD      E,C             ;
15668
        LD      D,B             ;
15669
        POP     AF              ;
15670
        RR      B               ;
15671
        POP     AF              ;
15672
        RR      B               ;
15673
        EXX                     ;
15674
        POP     BC              ;
15675
        POP     HL              ;
15676
        LD      A,B             ;
15677
        SUB     C               ;
15678
        JP      L313D           ; jump back to DIVN-EXPT
15679
 
15680
; ------------------------------------
15681
; Integer truncation towards zero ($3A)
15682
; ------------------------------------
15683
;
15684
;
15685
 
15686
;; truncate
15687
L3214:  LD      A,(HL)          ;
15688
        AND     A               ;
15689
        RET     Z               ;
15690
 
15691
        CP      $81             ;
15692
        JR      NC,L3221        ; to T-GR-ZERO
15693
 
15694
        LD      (HL),$00        ;
15695
        LD      A,$20           ;
15696
        JR      L3272           ; to NIL-BYTES
15697
 
15698
; ---
15699
 
15700
;; T-GR-ZERO
15701
L3221:  CP      $91             ;
15702
        JR      NZ,L323F        ; to T-SMALL
15703
 
15704
        INC     HL              ;
15705
        INC     HL              ;
15706
        INC     HL              ;
15707
        LD      A,$80           ;
15708
        AND     (HL)            ;
15709
        DEC      HL             ;
15710
        OR      (HL)            ;
15711
        DEC     HL              ;
15712
        JR      NZ,L3233        ; to T-FIRST
15713
 
15714
        LD      A,$80           ;
15715
        XOR     (HL)            ;
15716
 
15717
;; T-FIRST
15718
L3233:  DEC     HL              ;
15719
        JR      NZ,L326C        ; to T-EXPNENT
15720
 
15721
        LD      (HL),A          ;
15722
        INC     HL              ;
15723
        LD      (HL),$FF        ;
15724
        DEC     HL              ;
15725
        LD      A,$18           ;
15726
        JR      L3272           ; to NIL-BYTES
15727
 
15728
; ---
15729
 
15730
;; T-SMALL
15731
L323F:  JR      NC,L326D        ; to X-LARGE
15732
 
15733
        PUSH    DE              ;
15734
        CPL                     ;
15735
        ADD     A,$91           ;
15736
        INC     HL              ;
15737
        LD      D,(HL)          ;
15738
        INC     HL              ;
15739
        LD      E,(HL)          ;
15740
        DEC     HL              ;
15741
        DEC     HL              ;
15742
        LD      C,$00           ;
15743
        BIT     7,D             ;
15744
        JR      Z,L3252         ; to T-NUMERIC
15745
 
15746
        DEC     C               ;
15747
 
15748
;; T-NUMERIC
15749
L3252:  SET     7,D             ;
15750
        LD      B,$08           ;
15751
        SUB     B               ;
15752
        ADD     A,B             ;
15753
        JR      C,L325E         ; to T-TEST
15754
 
15755
        LD      E,D             ;
15756
        LD      D,$00           ;
15757
        SUB     B               ;
15758
 
15759
;; T-TEST
15760
L325E:  JR      Z,L3267         ; to T-STORE
15761
 
15762
        LD      B,A             ;
15763
 
15764
;; T-SHIFT
15765
L3261:  SRL     D               ;
15766
        RR      E               ;
15767
        DJNZ    L3261           ; to T-SHIFT
15768
 
15769
;; T-STORE
15770
L3267:  CALL    L2D8E           ; routine INT-STORE
15771
        POP     DE              ;
15772
        RET                     ;
15773
 
15774
; ---
15775
 
15776
;; T-EXPNENT
15777
L326C:  LD      A,(HL)          ;
15778
 
15779
;; X-LARGE
15780
L326D:  SUB     $A0             ;
15781
        RET     P               ;
15782
 
15783
        NEG                     ; Negate
15784
 
15785
;; NIL-BYTES
15786
L3272:  PUSH    DE              ;
15787
        EX      DE,HL           ;
15788
        DEC     HL              ;
15789
        LD      B,A             ;
15790
        SRL     B               ;
15791
        SRL     B               ;
15792
        SRL     B               ;
15793
        JR      Z,L3283         ; to BITS-ZERO
15794
 
15795
;; BYTE-ZERO
15796
L327E:  LD      (HL),$00        ;
15797
        DEC     HL              ;
15798
        DJNZ    L327E           ; to BYTE-ZERO
15799
 
15800
;; BITS-ZERO
15801
L3283:  AND     $07             ;
15802
        JR      Z,L3290         ; to IX-END
15803
 
15804
        LD      B,A             ;
15805
        LD      A,$FF           ;
15806
 
15807
;; LESS-MASK
15808
L328A:  SLA     A               ;
15809
        DJNZ    L328A           ; to LESS-MASK
15810
 
15811
        AND     (HL)            ;
15812
        LD      (HL),A          ;
15813
 
15814
;; IX-END
15815
L3290:  EX      DE,HL           ;
15816
        POP     DE              ;
15817
        RET                     ;
15818
 
15819
; ----------------------------------
15820
; Storage of numbers in 5 byte form.
15821
; ==================================
15822
; Both integers and floating-point numbers can be stored in five bytes.
15823
; Zero is a special case stored as 5 zeros.
15824
; For integers the form is
15825
; Byte 1 - zero,
15826
; Byte 2 - sign byte, $00 +ve, $FF -ve.
15827
; Byte 3 - Low byte of integer.
15828
; Byte 4 - High byte
15829
; Byte 5 - unused but always zero.
15830
;
15831
; it seems unusual to store the low byte first but it is just as easy either
15832
; way. Statistically it just increases the chances of trailing zeros which
15833
; is an advantage elsewhere in saving ROM code.
15834
;
15835
;             zero     sign     low      high    unused
15836
; So +1 is  00000000 00000000 00000001 00000000 00000000
15837
;
15838
; and -1 is 00000000 11111111 11111111 11111111 00000000
15839
;
15840
; much of the arithmetic found in BASIC lines can be done using numbers
15841
; in this form using the Z80's 16 bit register operation ADD.
15842
; (multiplication is done by a sequence of additions).
15843
;
15844
; Storing -ve integers in two's complement form, means that they are ready for
15845
; addition and you might like to add the numbers above to prove that the
15846
; answer is zero. If, as in this case, the carry is set then that denotes that
15847
; the result is positive. This only applies when the signs don't match.
15848
; With positive numbers a carry denotes the result is out of integer range.
15849
; With negative numbers a carry denotes the result is within range.
15850
; The exception to the last rule is when the result is -65536
15851
;
15852
; Floating point form is an alternative method of storing numbers which can
15853
; be used for integers and larger (or fractional) numbers.
15854
;
15855
; In this form 1 is stored as
15856
;           10000001 00000000 00000000 00000000 00000000
15857
;
15858
; When a small integer is converted to a floating point number the last two
15859
; bytes are always blank so they are omitted in the following steps
15860
;
15861
; first make exponent +1 +16d  (bit 7 of the exponent is set if positive)
15862
 
15863
; 10010001 00000000 00000001
15864
; 10010000 00000000 00000010 <-  now shift left and decrement exponent
15865
; ...
15866
; 10000010 01000000 00000000 <-  until a 1 abuts the imaginary point
15867
; 10000001 10000000 00000000     to the left of the mantissa.
15868
;
15869
; however since the leftmost bit of the mantissa is always set then it can
15870
; be used to denote the sign of the mantissa and put back when needed by the
15871
; PREP routines which gives
15872
;
15873
; 10000001 00000000 00000000
15874
 
15875
; -----------------------------
15876
; Re-stack two `small' integers
15877
; -----------------------------
15878
; This routine is called to re-stack two numbers in full floating point form
15879
; e.g. from mult when integer multiplication has overflowed.
15880
 
15881
;; RE-ST-TWO
15882
L3293:  CALL    L3296           ; routine RESTK-SUB  below and continue
15883
                                ; into the routine to do the other one.
15884
 
15885
;; RESTK-SUB
15886
L3296:  EX      DE,HL           ; swap pointers
15887
 
15888
; --------------------------------
15889
; Re-stack one number in full form
15890
; --------------------------------
15891
; This routine re-stacks an integer usually on the calculator stack
15892
; in full floating point form.
15893
; HL points to first byte.
15894
 
15895
;; re-stack
15896
L3297:  LD      A,(HL)          ; Fetch Exponent byte to A
15897
        AND     A               ; test it
15898
        RET     NZ              ; return if not zero as already in full
15899
                                ; floating-point form.
15900
 
15901
        PUSH    DE              ; preserve DE.
15902
        CALL    L2D7F           ; routine INT-FETCH
15903
                                ; integer to DE, sign to C.
15904
 
15905
; HL points to 4th byte.
15906
 
15907
        XOR     A               ; clear accumulator.
15908
        INC     HL              ; point to 5th.
15909
        LD      (HL),A          ; and blank.
15910
        DEC     HL              ; point to 4th.
15911
        LD      (HL),A          ; and blank.
15912
 
15913
        LD      B,$91           ; set exponent byte +ve $81
15914
                                ; and imaginary dec point 16 bits to right
15915
                                ; of first bit.
15916
 
15917
; we could skip to normalize now but it's quicker to avoid
15918
; normalizing through an empty D.
15919
 
15920
        LD      A,D             ; fetch the high byte D
15921
        AND     A               ; is it zero ?
15922
        JR      NZ,L32B1        ; skip to RS-NRMLSE if not.
15923
 
15924
        OR      E               ; low byte E to A and test for zero
15925
        LD      B,D             ; set B exponent to 0
15926
        JR      Z,L32BD         ; forward to RS-STORE if value is zero.
15927
 
15928
        LD      D,E             ; transfer E to D
15929
        LD      E,B             ; set E to 0
15930
        LD      B,$89           ; reduce the initial exponent by eight.
15931
 
15932
 
15933
;; RS-NRMLSE
15934
L32B1:  EX      DE,HL           ; integer to HL, addr of 4th byte to DE.
15935
 
15936
;; RSTK-LOOP
15937
L32B2:  DEC     B               ; decrease exponent
15938
        ADD     HL,HL           ; shift DE left
15939
        JR      NC,L32B2        ; loop back to RSTK-LOOP
15940
                                ; until a set bit pops into carry
15941
 
15942
        RRC     C               ; now rotate the sign byte $00 or $FF
15943
                                ; into carry to give a sign bit
15944
 
15945
        RR      H               ; rotate the sign bit to left of H
15946
        RR      L               ; rotate any carry into L
15947
 
15948
        EX      DE,HL           ; address 4th byte, normalized int to DE
15949
 
15950
;; RS-STORE
15951
L32BD:  DEC     HL              ; address 3rd byte
15952
        LD      (HL),E          ; place E
15953
        DEC     HL              ; address 2nd byte
15954
        LD      (HL),D          ; place D
15955
        DEC     HL              ; address 1st byte
15956
        LD      (HL),B          ; store the exponent
15957
 
15958
        POP     DE              ; restore initial DE.
15959
        RET                     ; return.
15960
 
15961
;****************************************
15962
;** Part 10. FLOATING-POINT CALCULATOR **
15963
;****************************************
15964
 
15965
; As a general rule the calculator avoids using the IY register.
15966
; exceptions are val, val$ and str$.
15967
; So an assembly language programmer who has disabled interrupts to use
15968
; IY for other purposes can still use the calculator for mathematical
15969
; purposes.
15970
 
15971
 
15972
; ------------------
15973
; Table of constants
15974
; ------------------
15975
;
15976
;
15977
 
15978
; used 11 times
15979
;; stk-zero                                                 00 00 00 00 00
15980
L32C5:  DB    $00             ;;Bytes: 1
15981
        DB    $B0             ;;Exponent $00
15982
        DB    $00             ;;(+00,+00,+00)
15983
 
15984
; used 19 times
15985
;; stk-one                                                  00 00 01 00 00
15986
L32C8:  DB    $40             ;;Bytes: 2
15987
        DB    $B0             ;;Exponent $00
15988
        DB    $00,$01         ;;(+00,+00)
15989
 
15990
; used 9 times
15991
;; stk-half                                                 80 00 00 00 00
15992
L32CC:  DB    $30             ;;Exponent: $80, Bytes: 1
15993
        DB    $00             ;;(+00,+00,+00)
15994
 
15995
; used 4 times.
15996
;; stk-pi/2                                                 81 49 0F DA A2
15997
L32CE:  DB    $F1             ;;Exponent: $81, Bytes: 4
15998
        DB    $49,$0F,$DA,$A2 ;;
15999
 
16000
; used 3 times.
16001
;; stk-ten                                                  00 00 0A 00 00
16002
L32D3:  DB    $40             ;;Bytes: 2
16003
        DB    $B0             ;;Exponent $00
16004
        DB    $00,$0A         ;;(+00,+00)
16005
 
16006
 
16007
; ------------------
16008
; Table of addresses
16009
; ------------------
16010
;
16011
; starts with binary operations which have two operands and one result.
16012
; three pseudo binary operations first.
16013
 
16014
;; tbl-addrs
16015
L32D7:  DEFW    L368F           ; $00 Address: $368F - jump-true
16016
        DEFW    L343C           ; $01 Address: $343C - exchange
16017
        DEFW    L33A1           ; $02 Address: $33A1 - delete
16018
 
16019
; true binary operations.
16020
 
16021
        DEFW    L300F           ; $03 Address: $300F - subtract
16022
        DEFW    L30CA           ; $04 Address: $30CA - multiply
16023
        DEFW    L31AF           ; $05 Address: $31AF - division
16024
        DEFW    L3851           ; $06 Address: $3851 - to-power
16025
        DEFW    L351B           ; $07 Address: $351B - or
16026
 
16027
        DEFW    L3524           ; $08 Address: $3524 - no-&-no
16028
        DEFW    L353B           ; $09 Address: $353B - no-l-eql
16029
        DEFW    L353B           ; $0A Address: $353B - no-gr-eql
16030
        DEFW    L353B           ; $0B Address: $353B - nos-neql
16031
        DEFW    L353B           ; $0C Address: $353B - no-grtr
16032
        DEFW    L353B           ; $0D Address: $353B - no-less
16033
        DEFW    L353B           ; $0E Address: $353B - nos-eql
16034
        DEFW    L3014           ; $0F Address: $3014 - addition
16035
 
16036
        DEFW    L352D           ; $10 Address: $352D - str-&-no
16037
        DEFW    L353B           ; $11 Address: $353B - str-l-eql
16038
        DEFW    L353B           ; $12 Address: $353B - str-gr-eql
16039
        DEFW    L353B           ; $13 Address: $353B - strs-neql
16040
        DEFW    L353B           ; $14 Address: $353B - str-grtr
16041
        DEFW    L353B           ; $15 Address: $353B - str-less
16042
        DEFW    L353B           ; $16 Address: $353B - strs-eql
16043
        DEFW    L359C           ; $17 Address: $359C - strs-add
16044
 
16045
; unary follow
16046
 
16047
        DEFW    L35DE           ; $18 Address: $35DE - val$
16048
        DEFW    L34BC           ; $19 Address: $34BC - usr-$
16049
        DEFW    L3645           ; $1A Address: $3645 - read-in
16050
        DEFW    L346E           ; $1B Address: $346E - negate
16051
 
16052
        DEFW    L3669           ; $1C Address: $3669 - code
16053
        DEFW    L35DE           ; $1D Address: $35DE - val
16054
        DEFW    L3674           ; $1E Address: $3674 - len
16055
        DEFW    L37B5           ; $1F Address: $37B5 - sin
16056
        DEFW    L37AA           ; $20 Address: $37AA - cos
16057
        DEFW    L37DA           ; $21 Address: $37DA - tan
16058
        DEFW    L3833           ; $22 Address: $3833 - asn
16059
        DEFW    L3843           ; $23 Address: $3843 - acs
16060
        DEFW    L37E2           ; $24 Address: $37E2 - atn
16061
        DEFW    L3713           ; $25 Address: $3713 - ln
16062
        DEFW    L36C4           ; $26 Address: $36C4 - exp
16063
        DEFW    L36AF           ; $27 Address: $36AF - int
16064
        DEFW    L384A           ; $28 Address: $384A - sqr
16065
        DEFW    L3492           ; $29 Address: $3492 - sgn
16066
        DEFW    L346A           ; $2A Address: $346A - abs
16067
        DEFW    L34AC           ; $2B Address: $34AC - peek
16068
        DEFW    L34A5           ; $2C Address: $34A5 - in
16069
        DEFW    L34B3           ; $2D Address: $34B3 - usr-no
16070
        DEFW    L361F           ; $2E Address: $361F - str$
16071
        DEFW    L35C9           ; $2F Address: $35C9 - chrs
16072
        DEFW    L3501           ; $30 Address: $3501 - not
16073
 
16074
; end of true unary
16075
 
16076
        DEFW    L33C0           ; $31 Address: $33C0 - duplicate
16077
        DEFW    L36A0           ; $32 Address: $36A0 - n-mod-m
16078
        DEFW    L3686           ; $33 Address: $3686 - jump
16079
        DEFW    L33C6           ; $34 Address: $33C6 - stk-data
16080
        DEFW    L367A           ; $35 Address: $367A - dec-jr-nz
16081
        DEFW    L3506           ; $36 Address: $3506 - less-0
16082
        DEFW    L34F9           ; $37 Address: $34F9 - greater-0
16083
        DEFW    L369B           ; $38 Address: $369B - end-calc
16084
        DEFW    L3783           ; $39 Address: $3783 - get-argt
16085
        DEFW    L3214           ; $3A Address: $3214 - truncate
16086
        DEFW    L33A2           ; $3B Address: $33A2 - fp-calc-2
16087
        DEFW    L2D4F           ; $3C Address: $2D4F - e-to-fp
16088
        DEFW    L3297           ; $3D Address: $3297 - re-stack
16089
 
16090
; the following are just the next available slots for the 128 compound literals
16091
; which are in range $80 - $FF.
16092
 
16093
        DEFW    L3449           ; $3E Address: $3449 - series-xx    $80 - $9F.
16094
        DEFW    L341B           ; $3F Address: $341B - stk-const-xx $A0 - $BF.
16095
        DEFW    L342D           ; $40 Address: $342D - st-mem-xx    $C0 - $DF.
16096
        DEFW    L340F           ; $41 Address: $340F - get-mem-xx   $E0 - $FF.
16097
 
16098
; Aside: 3E - 7F are therefore unused calculator literals.
16099
;        3E - 7B would be available for expansion.
16100
 
16101
; --------------
16102
; The Calculator
16103
; --------------
16104
;
16105
;
16106
 
16107
;; CALCULATE
16108
L335B:  CALL    L35BF           ; routine STK-PNTRS is called to set up the
16109
                                ; calculator stack pointers for a default
16110
                                ; unary operation. HL = last value on stack.
16111
                                ; DE = STKEND first location after stack.
16112
 
16113
; the calculate routine is called at this point by the series generator...
16114
 
16115
;; GEN-ENT-1
16116
L335E:  LD      A,B             ; fetch the Z80 B register to A
16117
        LD      ($5C67),A       ; and store value in system variable BREG.
16118
                                ; this will be the counter for dec-jr-nz
16119
                                ; or if used from fp-calc2 the calculator
16120
                                ; instruction.
16121
 
16122
; ... and again later at this point
16123
 
16124
;; GEN-ENT-2
16125
L3362:  EXX                     ; switch sets
16126
        EX      (SP),HL         ; and store the address of next instruction,
16127
                                ; the return address, in H'L'.
16128
                                ; If this is a recursive call the the H'L'
16129
                                ; of the previous invocation goes on stack.
16130
                                ; c.f. end-calc.
16131
        EXX                     ; switch back to main set
16132
 
16133
; this is the re-entry looping point when handling a string of literals.
16134
 
16135
;; RE-ENTRY
16136
L3365:  LD      ($5C65),DE      ; save end of stack in system variable STKEND
16137
        EXX                     ; switch to alt
16138
        LD      A,(HL)          ; get next literal
16139
        INC     HL              ; increase pointer'
16140
 
16141
; single operation jumps back to here
16142
 
16143
;; SCAN-ENT
16144
L336C:  PUSH    HL              ; save pointer on stack
16145
        AND     A               ; now test the literal
16146
        JP      P,L3380         ; forward to FIRST-3D if in range $00 - $3D
16147
                                ; anything with bit 7 set will be one of
16148
                                ; 128 compound literals.
16149
 
16150
; compound literals have the following format.
16151
; bit 7 set indicates compound.
16152
; bits 6-5 the subgroup 0-3.
16153
; bits 4-0 the embedded parameter $00 - $1F.
16154
; The subgroup 0-3 needs to be manipulated to form the next available four
16155
; address places after the simple literals in the address table.
16156
 
16157
        LD      D,A             ; save literal in D
16158
        AND     $60             ; and with 01100000 to isolate subgroup
16159
        RRCA                    ; rotate bits
16160
        RRCA                    ; 4 places to right
16161
        RRCA                    ; not five as we need offset * 2
16162
        RRCA                    ; 00000xx0
16163
        ADD     A,$7C           ; add ($3E * 2) to give correct offset.
16164
                                ; alter above if you add more literals.
16165
        LD      L,A             ; store in L for later indexing.
16166
        LD      A,D             ; bring back compound literal
16167
        AND     $1F             ; use mask to isolate parameter bits
16168
        JR      L338E           ; forward to ENT-TABLE
16169
 
16170
; ---
16171
 
16172
; the branch was here with simple literals.
16173
 
16174
;; FIRST-3D
16175
L3380:  CP      $18             ; compare with first unary operations.
16176
        JR      NC,L338C        ; to DOUBLE-A with unary operations
16177
 
16178
; it is binary so adjust pointers.
16179
 
16180
        EXX                     ;
16181
        LD      BC,$FFFB        ; the value -5
16182
        LD      D,H             ; transfer HL, the last value, to DE.
16183
        LD      E,L             ;
16184
        ADD     HL,BC           ; subtract 5 making HL point to second
16185
                                ; value.
16186
        EXX                     ;
16187
 
16188
;; DOUBLE-A
16189
L338C:  RLCA                    ; double the literal
16190
        LD      L,A             ; and store in L for indexing
16191
 
16192
;; ENT-TABLE
16193
L338E:  LD      DE,L32D7        ; Address: tbl-addrs
16194
        LD      H,$00           ; prepare to index
16195
        ADD     HL,DE           ; add to get address of routine
16196
        LD      E,(HL)          ; low byte to E
16197
        INC     HL              ;
16198
        LD      D,(HL)          ; high byte to D
16199
        LD      HL,L3365        ; Address: RE-ENTRY
16200
        EX      (SP),HL         ; goes to stack
16201
        PUSH    DE              ; now address of routine
16202
        EXX                     ; main set
16203
                                ; avoid using IY register.
16204
        LD      BC,($5C66)      ; STKEND_hi
16205
                                ; nothing much goes to C but BREG to B
16206
                                ; and continue into next ret instruction
16207
                                ; which has a dual identity
16208
 
16209
 
16210
; ------------------
16211
; Handle delete (02)
16212
; ------------------
16213
; A simple return but when used as a calculator literal this
16214
; deletes the last value from the calculator stack.
16215
; On entry, as always with binary operations,
16216
; HL=first number, DE=second number
16217
; On exit, HL=result, DE=stkend.
16218
; So nothing to do
16219
 
16220
;; delete
16221
L33A1:  RET                     ; return - indirect jump if from above.
16222
 
16223
; ---------------------
16224
; Single operation (3B)
16225
; ---------------------
16226
; this single operation is used, in the first instance, to evaluate most
16227
; of the mathematical and string functions found in BASIC expressions.
16228
 
16229
;; fp-calc-2
16230
L33A2:  POP     AF              ; drop return address.
16231
        LD      A,($5C67)       ; load accumulator from system variable BREG
16232
                                ; value will be literal eg. 'tan'
16233
        EXX                     ; switch to alt
16234
        JR      L336C           ; back to SCAN-ENT
16235
                                ; next literal will be end-calc at L2758
16236
 
16237
; ----------------
16238
; Test five-spaces
16239
; ----------------
16240
; This routine is called from MOVE-FP, STK-CONST and STK-STORE to
16241
; test that there is enough space between the calculator stack and the
16242
; machine stack for another five-byte value. It returns with BC holding
16243
; the value 5 ready for any subsequent LDIR.
16244
 
16245
;; TEST-5-SP
16246
L33A9:  PUSH    DE              ; save
16247
        PUSH    HL              ; registers
16248
        LD      BC,$0005        ; an overhead of five bytes
16249
        CALL    L1F05           ; routine TEST-ROOM tests free RAM raising
16250
                                ; an error if not.
16251
        POP     HL              ; else restore
16252
        POP     DE              ; registers.
16253
        RET                     ; return with BC set at 5.
16254
 
16255
; ------------
16256
; Stack number
16257
; ------------
16258
; This routine is called to stack a hidden floating point number found in
16259
; a BASIC line. It is also called to stack a numeric variable value, and
16260
; from BEEP, to stack an entry in the semi-tone table. It is not part of the
16261
; calculator suite of routines.
16262
; On entry HL points to the number to be stacked.
16263
 
16264
;; STACK-NUM
16265
L33B4:  LD      DE,($5C65)      ; load destination from STKEND system variable.
16266
        CALL    L33C0           ; routine MOVE-FP puts on calculator stack 
16267
                                ; with a memory check.
16268
        LD      ($5C65),DE      ; set STKEND to next free location.
16269
        RET                     ; return.
16270
 
16271
; ---------------------------------
16272
; Move a floating point number (31)
16273
; ---------------------------------
16274
; This simple routine is a 5-byte LDIR instruction
16275
; that incorporates a memory check.
16276
; When used as a calculator literal it duplicates the last value on the
16277
; calculator stack.
16278
; Unary so on entry HL points to last value, DE to stkend
16279
 
16280
;; duplicate
16281
;; MOVE-FP
16282
L33C0:  CALL    L33A9           ; routine TEST-5-SP test free memory
16283
                                ; and sets BC to 5.
16284
        LDIR                    ; copy the five bytes.
16285
        RET                     ; return with DE addressing new STKEND
16286
                                ; and HL addressing new last value.
16287
 
16288
; -------------------
16289
; Stack literals ($34)
16290
; -------------------
16291
; When a calculator subroutine needs to put a value on the calculator
16292
; stack that is not a regular constant this routine is called with a
16293
; variable number of following data bytes that convey to the routine
16294
; the integer or floating point form as succinctly as is possible.
16295
 
16296
;; stk-data
16297
L33C6:  LD      H,D             ; transfer STKEND
16298
        LD      L,E             ; to HL for result.
16299
 
16300
;; STK-CONST
16301
L33C8:  CALL    L33A9           ; routine TEST-5-SP tests that room exists
16302
                                ; and sets BC to $05.
16303
 
16304
        EXX                     ; switch to alternate set
16305
        PUSH    HL              ; save the pointer to next literal on stack
16306
        EXX                     ; switch back to main set
16307
 
16308
        EX      (SP),HL         ; pointer to HL, destination to stack.
16309
 
16310
        PUSH    BC              ; save BC - value 5 from test room ??.
16311
 
16312
        LD      A,(HL)          ; fetch the byte following 'stk-data'
16313
        AND     $C0             ; isolate bits 7 and 6
16314
        RLCA                    ; rotate
16315
        RLCA                    ; to bits 1 and 0  range $00 - $03.
16316
        LD      C,A             ; transfer to C
16317
        INC     C               ; and increment to give number of bytes
16318
                                ; to read. $01 - $04
16319
        LD      A,(HL)          ; reload the first byte
16320
        AND     $3F             ; mask off to give possible exponent.
16321
        JR      NZ,L33DE        ; forward to FORM-EXP if it was possible to
16322
                                ; include the exponent.
16323
 
16324
; else byte is just a byte count and exponent comes next.
16325
 
16326
        INC     HL              ; address next byte and
16327
        LD      A,(HL)          ; pick up the exponent ( - $50).
16328
 
16329
;; FORM-EXP
16330
L33DE:  ADD     A,$50           ; now add $50 to form actual exponent
16331
        LD      (DE),A          ; and load into first destination byte.
16332
        LD      A,$05           ; load accumulator with $05 and
16333
        SUB     C               ; subtract C to give count of trailing
16334
                                ; zeros plus one.
16335
        INC     HL              ; increment source
16336
        INC     DE              ; increment destination
16337
        LD      B,$00           ; prepare to copy
16338
        LDIR                    ; copy C bytes
16339
 
16340
        POP     BC              ; restore 5 counter to BC ??.
16341
 
16342
        EX      (SP),HL         ; put HL on stack as next literal pointer
16343
                                ; and the stack value - result pointer -
16344
                                ; to HL.
16345
 
16346
        EXX                     ; switch to alternate set.
16347
        POP     HL              ; restore next literal pointer from stack
16348
                                ; to H'L'.
16349
        EXX                     ; switch back to main set.
16350
 
16351
        LD      B,A             ; zero count to B
16352
        XOR     A               ; clear accumulator
16353
 
16354
;; STK-ZEROS
16355
L33F1:  DEC     B               ; decrement B counter
16356
        RET     Z               ; return if zero.          >>
16357
                                ; DE points to new STKEND
16358
                                ; HL to new number.
16359
 
16360
        LD      (DE),A          ; else load zero to destination
16361
        INC     DE              ; increase destination
16362
        JR      L33F1           ; loop back to STK-ZEROS until done.
16363
 
16364
; -------------------------------
16365
; THE 'SKIP CONSTANTS' SUBROUTINE
16366
; -------------------------------
16367
; This routine traverses variable-length entries in the table of constants,
16368
; stacking intermediate, unwanted constants onto a dummy calculator stack,
16369
; in the first five bytes of ROM. The destination DE normally points to the
16370
; end of the calculator stack which might be in the normal place or in the
16371
; system variables area during E-LINE-NO; INT-TO-FP; stk-ten. In any case,
16372
; it would be simpler all round if the routine just shoved unwanted values 
16373
; where it is going to stick the wanted value. 
16374
; The instruction LD DE, $0000 can be removed.
16375
 
16376
;; SKIP-CONS
16377
L33F7:  AND     A               ; test if initially zero.
16378
 
16379
;; SKIP-NEXT
16380
L33F8:  RET     Z               ; return if zero.          >>
16381
 
16382
        PUSH    AF              ; save count.
16383
        PUSH    DE              ; and normal STKEND
16384
 
16385
        LD      DE,$0000        ; dummy value for STKEND at start of ROM
16386
                                ; Note. not a fault but this has to be
16387
                                ; moved elsewhere when running in RAM.
16388
                                ; e.g. with Expandor Systems 'Soft ROM'.
16389
                                ; Better still, write to the normal place.
16390
        CALL    L33C8           ; routine STK-CONST works through variable
16391
                                ; length records.
16392
 
16393
        POP     DE              ; restore real STKEND
16394
        POP     AF              ; restore count
16395
        DEC     A               ; decrease
16396
        JR      L33F8           ; loop back to SKIP-NEXT
16397
 
16398
; ---------------
16399
; Memory location
16400
; ---------------
16401
; This routine, when supplied with a base address in HL and an index in A
16402
; will calculate the address of the A'th entry, where each entry occupies
16403
; five bytes. It is used for reading the semi-tone table and addressing
16404
; floating-point numbers in the calculator's memory area.
16405
 
16406
;; LOC-MEM
16407
L3406:  LD      C,A             ; store the original number $00-$1F.
16408
        RLCA                    ; double.
16409
        RLCA                    ; quadruple.
16410
        ADD     A,C             ; now add original to multiply by five.
16411
 
16412
        LD      C,A             ; place the result in C.
16413
        LD      B,$00           ; set B to 0.
16414
        ADD     HL,BC           ; add to form address of start of number in HL.
16415
        RET                     ; return.
16416
 
16417
; ------------------------------
16418
; Get from memory area ($E0 etc.)
16419
; ------------------------------
16420
; Literals $E0 to $FF
16421
; A holds $00-$1F offset.
16422
; The calculator stack increases by 5 bytes.
16423
 
16424
;; get-mem-xx
16425
L340F:  PUSH    DE              ; save STKEND
16426
        LD      HL,($5C68)      ; MEM is base address of the memory cells.
16427
        CALL    L3406           ; routine LOC-MEM so that HL = first byte
16428
        CALL    L33C0           ; routine MOVE-FP moves 5 bytes with memory
16429
                                ; check.
16430
                                ; DE now points to new STKEND.
16431
        POP     HL              ; original STKEND is now RESULT pointer.
16432
        RET                     ; return.
16433
 
16434
; --------------------------
16435
; Stack a constant (A0 etc.)
16436
; --------------------------
16437
; This routine allows a one-byte instruction to stack up to 32 constants
16438
; held in short form in a table of constants. In fact only 5 constants are
16439
; required. On entry the A register holds the literal ANDed with 1F.
16440
; It isn't very efficient and it would have been better to hold the
16441
; numbers in full, five byte form and stack them in a similar manner
16442
; to that used for semi-tone table values.
16443
 
16444
;; stk-const-xx
16445
L341B:  LD      H,D             ; save STKEND - required for result
16446
        LD      L,E             ;
16447
        EXX                     ; swap
16448
        PUSH    HL              ; save pointer to next literal
16449
        LD      HL,L32C5        ; Address: stk-zero - start of table of
16450
                                ; constants
16451
        EXX                     ;
16452
        CALL    L33F7           ; routine SKIP-CONS
16453
        CALL    L33C8           ; routine STK-CONST
16454
        EXX                     ;
16455
        POP     HL              ; restore pointer to next literal.
16456
        EXX                     ;
16457
        RET                     ; return.
16458
 
16459
; --------------------------------
16460
; Store in a memory area ($C0 etc.)
16461
; --------------------------------
16462
; Offsets $C0 to $DF
16463
; Although 32 memory storage locations can be addressed, only six
16464
; $C0 to $C5 are required by the ROM and only the thirty bytes (6*5)
16465
; required for these are allocated. Spectrum programmers who wish to
16466
; use the floating point routines from assembly language may wish to
16467
; alter the system variable MEM to point to 160 bytes of RAM to have 
16468
; use the full range available.
16469
; A holds the derived offset $00-$1F.
16470
; This is a unary operation, so on entry HL points to the last value and DE 
16471
; points to STKEND.
16472
 
16473
;; st-mem-xx
16474
L342D:  PUSH    HL              ; save the result pointer.
16475
        EX      DE,HL           ; transfer to DE.
16476
        LD      HL,($5C68)      ; fetch MEM the base of memory area.
16477
        CALL    L3406           ; routine LOC-MEM sets HL to the destination.
16478
        EX      DE,HL           ; swap - HL is start, DE is destination.
16479
        CALL    L33C0           ; routine MOVE-FP.
16480
                                ; note. a short ld bc,5; ldir
16481
                                ; the embedded memory check is not required
16482
                                ; so these instructions would be faster.
16483
        EX      DE,HL           ; DE = STKEND
16484
        POP     HL              ; restore original result pointer
16485
        RET                     ; return.
16486
 
16487
; ------------------------------------
16488
; Swap first number with second number
16489
; ------------------------------------
16490
; This routine exchanges the last two values on the calculator stack
16491
; On entry, as always with binary operations,
16492
; HL=first number, DE=second number
16493
; On exit, HL=result, DE=stkend.
16494
 
16495
;; exchange
16496
L343C:  LD      B,$05           ; there are five bytes to be swapped
16497
 
16498
; start of loop.
16499
 
16500
;; SWAP-BYTE
16501
L343E:  LD      A,(DE)          ; each byte of second
16502
        LD      C,(HL)          ; each byte of first
16503
        EX      DE,HL           ; swap pointers
16504
        LD      (DE),A          ; store each byte of first
16505
        LD      (HL),C          ; store each byte of second
16506
        INC     HL              ; advance both
16507
        INC     DE              ; pointers.
16508
        DJNZ    L343E           ; loop back to SWAP-BYTE until all 5 done.
16509
 
16510
        EX      DE,HL           ; even up the exchanges
16511
                                ; so that DE addresses STKEND.
16512
        RET                     ; return.
16513
 
16514
; --------------------------
16515
; Series generator (86 etc.)
16516
; --------------------------
16517
; The Spectrum uses Chebyshev polynomials to generate approximations for
16518
; SIN, ATN, LN and EXP. These are named after the Russian mathematician
16519
; Pafnuty Chebyshev, born in 1821, who did much pioneering work on numerical
16520
; series. As far as calculators are concerned, Chebyshev polynomials have an
16521
; advantage over other series, for example the Taylor series, as they can
16522
; reach an approximation in just six iterations for SIN, eight for EXP and
16523
; twelve for LN and ATN. The mechanics of the routine are interesting but
16524
; for full treatment of how these are generated with demonstrations in
16525
; Sinclair BASIC see "The Complete Spectrum ROM Disassembly" by Dr Ian Logan
16526
; and Dr Frank O'Hara, published 1983 by Melbourne House.
16527
 
16528
;; series-xx
16529
L3449:  LD      B,A             ; parameter $00 - $1F to B counter
16530
        CALL    L335E           ; routine GEN-ENT-1 is called.
16531
                                ; A recursive call to a special entry point
16532
                                ; in the calculator that puts the B register
16533
                                ; in the system variable BREG. The return
16534
                                ; address is the next location and where
16535
                                ; the calculator will expect its first
16536
                                ; instruction - now pointed to by HL'.
16537
                                ; The previous pointer to the series of
16538
                                ; five-byte numbers goes on the machine stack.
16539
 
16540
; The initialization phase.
16541
 
16542
        DB    $31             ;;duplicate       x,x
16543
        DB    $0F             ;;addition        x+x
16544
        DB    $C0             ;;st-mem-0        x+x
16545
        DB    $02             ;;delete          .
16546
        DB    $A0             ;;stk-zero        0
16547
        DB    $C2             ;;st-mem-2        0
16548
 
16549
; a loop is now entered to perform the algebraic calculation for each of
16550
; the numbers in the series
16551
 
16552
;; G-LOOP
16553
L3453:  DB    $31             ;;duplicate       v,v.
16554
        DB    $E0             ;;get-mem-0       v,v,x+2
16555
        DB    $04             ;;multiply        v,v*x+2
16556
        DB    $E2             ;;get-mem-2       v,v*x+2,v
16557
        DB    $C1             ;;st-mem-1
16558
        DB    $03             ;;subtract
16559
        DB    $38             ;;end-calc
16560
 
16561
; the previous pointer is fetched from the machine stack to H'L' where it
16562
; addresses one of the numbers of the series following the series literal.
16563
 
16564
        CALL    L33C6           ; routine STK-DATA is called directly to
16565
                                ; push a value and advance H'L'.
16566
        CALL    L3362           ; routine GEN-ENT-2 recursively re-enters
16567
                                ; the calculator without disturbing
16568
                                ; system variable BREG
16569
                                ; H'L' value goes on the machine stack and is
16570
                                ; then loaded as usual with the next address.
16571
 
16572
        DB    $0F             ;;addition
16573
        DB    $01             ;;exchange
16574
        DB    $C2             ;;st-mem-2
16575
        DB    $02             ;;delete
16576
 
16577
        DB    $35             ;;dec-jr-nz
16578
        DB    $EE             ;;back to L3453, G-LOOP
16579
 
16580
; when the counted loop is complete the final subtraction yields the result
16581
; for example SIN X.
16582
 
16583
        DB    $E1             ;;get-mem-1
16584
        DB    $03             ;;subtract
16585
        DB    $38             ;;end-calc
16586
 
16587
        RET                     ; return with H'L' pointing to location
16588
                                ; after last number in series.
16589
 
16590
; -----------------------
16591
; Absolute magnitude (2A)
16592
; -----------------------
16593
; This calculator literal finds the absolute value of the last value,
16594
; integer or floating point, on calculator stack.
16595
 
16596
;; abs
16597
L346A:  LD      B,$FF           ; signal abs
16598
        JR      L3474           ; forward to NEG-TEST
16599
 
16600
; -----------------------
16601
; Handle unary minus (1B)
16602
; -----------------------
16603
; Unary so on entry HL points to last value, DE to STKEND.
16604
 
16605
;; NEGATE
16606
;; negate
16607
L346E:  CALL    L34E9           ; call routine TEST-ZERO and
16608
        RET     C               ; return if so leaving zero unchanged.
16609
 
16610
        LD      B,$00           ; signal negate required before joining
16611
                                ; common code.
16612
 
16613
;; NEG-TEST
16614
L3474:  LD      A,(HL)          ; load first byte and 
16615
        AND     A               ; test for zero
16616
        JR      Z,L3483         ; forward to INT-CASE if a small integer
16617
 
16618
; for floating point numbers a single bit denotes the sign.
16619
 
16620
        INC     HL              ; address the first byte of mantissa.
16621
        LD      A,B             ; action flag $FF=abs, $00=neg.
16622
        AND     $80             ; now         $80      $00
16623
        OR      (HL)            ; sets bit 7 for abs
16624
        RLA                     ; sets carry for abs and if number negative
16625
        CCF                     ; complement carry flag
16626
        RRA                     ; and rotate back in altering sign
16627
        LD      (HL),A          ; put the altered adjusted number back
16628
        DEC     HL              ; HL points to result
16629
        RET                     ; return with DE unchanged
16630
 
16631
; ---
16632
 
16633
; for integer numbers an entire byte denotes the sign.
16634
 
16635
;; INT-CASE
16636
L3483:  PUSH    DE              ; save STKEND.
16637
 
16638
        PUSH    HL              ; save pointer to the last value/result.
16639
 
16640
        CALL    L2D7F           ; routine INT-FETCH puts integer in DE
16641
                                ; and the sign in C.
16642
 
16643
        POP     HL              ; restore the result pointer.
16644
 
16645
        LD      A,B             ; $FF=abs, $00=neg
16646
        OR      C               ; $FF for abs, no change neg
16647
        CPL                     ; $00 for abs, switched for neg
16648
        LD      C,A             ; transfer result to sign byte.
16649
 
16650
        CALL    L2D8E           ; routine INT-STORE to re-write the integer.
16651
 
16652
        POP     DE              ; restore STKEND.
16653
        RET                     ; return.
16654
 
16655
; -----------
16656
; Signum (29)
16657
; -----------
16658
; This routine replaces the last value on the calculator stack,
16659
; which may be in floating point or integer form, with the integer values
16660
; zero if zero, with one if positive and  with -minus one if negative.
16661
 
16662
;; sgn
16663
L3492:  CALL    L34E9           ; call routine TEST-ZERO and
16664
        RET     C               ; exit if so as no change is required.
16665
 
16666
        PUSH    DE              ; save pointer to STKEND.
16667
 
16668
        LD      DE,$0001        ; the result will be 1.
16669
        INC     HL              ; skip over the exponent.
16670
        RL      (HL)            ; rotate the sign bit into the carry flag.
16671
        DEC     HL              ; step back to point to the result.
16672
        SBC     A,A             ; byte will be $FF if negative, $00 if positive.
16673
        LD      C,A             ; store the sign byte in the C register.
16674
        CALL    L2D8E           ; routine INT-STORE to overwrite the last
16675
                                ; value with 0001 and sign.
16676
 
16677
        POP     DE              ; restore STKEND.
16678
        RET                     ; return.
16679
 
16680
; -----------------------
16681
; Handle IN function (2C)
16682
; -----------------------
16683
; This function reads a byte from an input port.
16684
 
16685
;; in
16686
L34A5:  CALL    L1E99           ; routine FIND-INT2 puts port address in BC.
16687
                                ; all 16 bits are put on the address line.
16688
        IN      A,(C)           ; read the port.
16689
 
16690
        JR      L34B0           ; exit to STACK-A (via IN-PK-STK to save a byte 
16691
                                ; of instruction code).
16692
 
16693
; -------------------------
16694
; Handle PEEK function (2B)
16695
; -------------------------
16696
; This function returns the contents of a memory address.
16697
; The entire address space can be peeked including the ROM.
16698
 
16699
;; peek
16700
L34AC:  CALL    L1E99           ; routine FIND-INT2 puts address in BC.
16701
        LD      A,(BC)          ; load contents into A register.
16702
 
16703
;; IN-PK-STK
16704
L34B0:  JP      L2D28           ; exit via STACK-A to put value on the 
16705
                                ; calculator stack.
16706
 
16707
; ---------------
16708
; USR number (2D)
16709
; ---------------
16710
; The USR function followed by a number 0-65535 is the method by which
16711
; the Spectrum invokes machine code programs. This function returns the
16712
; contents of the BC register pair.
16713
; Note. that STACK-BC re-initializes the IY register if a user-written
16714
; program has altered it.
16715
 
16716
;; usr-no
16717
L34B3:  CALL    L1E99           ; routine FIND-INT2 to fetch the
16718
                                ; supplied address into BC.
16719
 
16720
        LD      HL,L2D2B        ; address: STACK-BC is
16721
        PUSH    HL              ; pushed onto the machine stack.
16722
        PUSH    BC              ; then the address of the machine code
16723
                                ; routine.
16724
 
16725
        RET                     ; make an indirect jump to the routine
16726
                                ; and, hopefully, to STACK-BC also.
16727
 
16728
; ---------------
16729
; USR string (19)
16730
; ---------------
16731
; The user function with a one-character string argument, calculates the
16732
; address of the User Defined Graphic character that is in the string.
16733
; As an alternative, the ASCII equivalent, upper or lower case,
16734
; may be supplied. This provides a user-friendly method of redefining
16735
; the 21 User Definable Graphics e.g.
16736
; POKE USR "a", BIN 10000000 will put a dot in the top left corner of the
16737
; character 144.
16738
; Note. the curious double check on the range. With 26 UDGs the first check
16739
; only is necessary. With anything less the second check only is required.
16740
; It is highly likely that the first check was written by Steven Vickers.
16741
 
16742
;; usr-$
16743
L34BC:  CALL    L2BF1           ; routine STK-FETCH fetches the string
16744
                                ; parameters.
16745
        DEC     BC              ; decrease BC by
16746
        LD      A,B             ; one to test
16747
        OR      C               ; the length.
16748
        JR      NZ,L34E7        ; to REPORT-A if not a single character.
16749
 
16750
        LD      A,(DE)          ; fetch the character
16751
        CALL    L2C8D           ; routine ALPHA sets carry if 'A-Z' or 'a-z'.
16752
        JR      C,L34D3         ; forward to USR-RANGE if ASCII.
16753
 
16754
        SUB     $90             ; make udgs range 0-20d
16755
        JR      C,L34E7         ; to REPORT-A if too low. e.g. usr " ".
16756
 
16757
        CP      $15             ; Note. this test is not necessary.
16758
        JR      NC,L34E7        ; to REPORT-A if higher than 20.
16759
 
16760
        INC     A               ; make range 1-21d to match LSBs of ASCII
16761
 
16762
;; USR-RANGE
16763
L34D3:  DEC     A               ; make range of bits 0-4 start at zero
16764
        ADD     A,A             ; multiply by eight
16765
        ADD     A,A             ; and lose any set bits
16766
        ADD     A,A             ; range now 0 - 25*8
16767
        CP      $A8             ; compare to 21*8
16768
        JR      NC,L34E7        ; to REPORT-A if originally higher 
16769
                                ; than 'U','u' or graphics U.
16770
 
16771
        LD      BC,($5C7B)      ; fetch the UDG system variable value.
16772
        ADD     A,C             ; add the offset to character
16773
        LD      C,A             ; and store back in register C.
16774
        JR      NC,L34E4        ; forward to USR-STACK if no overflow.
16775
 
16776
        INC     B               ; increment high byte.
16777
 
16778
;; USR-STACK
16779
L34E4:  JP      L2D2B           ; jump back and exit via STACK-BC to store
16780
 
16781
; ---
16782
 
16783
;; REPORT-A
16784
L34E7:  RST     08H             ; ERROR-1
16785
        DB    $09             ; Error Report: Invalid argument
16786
 
16787
; -------------
16788
; Test for zero
16789
; -------------
16790
; Test if top value on calculator stack is zero.
16791
; The carry flag is set if the last value is zero but no registers are altered.
16792
; All five bytes will be zero but first four only need be tested.
16793
; On entry HL points to the exponent the first byte of the value.
16794
 
16795
;; TEST-ZERO
16796
L34E9:  PUSH    HL              ; preserve HL which is used to address.
16797
        PUSH    BC              ; preserve BC which is used as a store.
16798
        LD      B,A             ; preserve A in B.
16799
 
16800
        LD      A,(HL)          ; load first byte to accumulator
16801
        INC     HL              ; advance.
16802
        OR      (HL)            ; OR with second byte and clear carry.
16803
        INC     HL              ; advance.
16804
        OR      (HL)            ; OR with third byte.
16805
        INC     HL              ; advance.
16806
        OR      (HL)            ; OR with fourth byte.
16807
 
16808
        LD      A,B             ; restore A without affecting flags.
16809
        POP     BC              ; restore the saved
16810
        POP     HL              ; registers.
16811
 
16812
        RET     NZ              ; return if not zero and with carry reset.
16813
 
16814
        SCF                     ; set the carry flag.
16815
        RET                     ; return with carry set if zero.
16816
 
16817
; -----------------------
16818
; Greater than zero ($37)
16819
; -----------------------
16820
; Test if the last value on the calculator stack is greater than zero.
16821
; This routine is also called directly from the end-tests of the comparison 
16822
; routine.
16823
 
16824
;; GREATER-0
16825
;; greater-0
16826
L34F9:  CALL    L34E9           ; routine TEST-ZERO
16827
        RET     C               ; return if was zero as this
16828
                                ; is also the Boolean 'false' value.
16829
 
16830
        LD      A,$FF           ; prepare XOR mask for sign bit
16831
        JR      L3507           ; forward to SIGN-TO-C
16832
                                ; to put sign in carry
16833
                                ; (carry will become set if sign is positive)
16834
                                ; and then overwrite location with 1 or 0 
16835
                                ; as appropriate.
16836
 
16837
; ------------------------
16838
; Handle NOT operator ($30)
16839
; ------------------------
16840
; This overwrites the last value with 1 if it was zero else with zero
16841
; if it was any other value.
16842
;
16843
; e.g NOT 0 returns 1, NOT 1 returns 0, NOT -3 returns 0.
16844
;
16845
; The subroutine is also called directly from the end-tests of the comparison
16846
; operator.
16847
 
16848
;; NOT
16849
;; not
16850
L3501:  CALL    L34E9           ; routine TEST-ZERO sets carry if zero
16851
 
16852
        JR      L350B           ; to FP-0/1 to overwrite operand with
16853
                                ; 1 if carry is set else to overwrite with zero.
16854
 
16855
; -------------------
16856
; Less than zero (36)
16857
; -------------------
16858
; Destructively test if last value on calculator stack is less than zero.
16859
; Bit 7 of second byte will be set if so.
16860
 
16861
;; less-0
16862
L3506:  XOR     A               ; set xor mask to zero
16863
                                ; (carry will become set if sign is negative).
16864
 
16865
; transfer sign of mantissa to Carry Flag.
16866
 
16867
;; SIGN-TO-C
16868
L3507:  INC     HL              ; address 2nd byte.
16869
        XOR     (HL)            ; bit 7 of HL will be set if number is negative.
16870
        DEC     HL              ; address 1st byte again.
16871
        RLCA                    ; rotate bit 7 of A to carry.
16872
 
16873
; -----------
16874
; Zero or one
16875
; -----------
16876
; This routine places an integer value of zero or one at the addressed location
16877
; of the calculator stack or MEM area.  The value one is written if carry is 
16878
; set on entry else zero.
16879
 
16880
;; FP-0/1
16881
L350B:  PUSH    HL              ; save pointer to the first byte
16882
        LD      A,$00           ; load accumulator with zero - without
16883
                                ; disturbing flags.
16884
        LD      (HL),A          ; zero to first byte
16885
        INC     HL              ; address next
16886
        LD      (HL),A          ; zero to 2nd byte
16887
        INC     HL              ; address low byte of integer
16888
        RLA                     ; carry to bit 0 of A
16889
        LD      (HL),A          ; load one or zero to low byte.
16890
        RRA                     ; restore zero to accumulator.
16891
        INC     HL              ; address high byte of integer.
16892
        LD      (HL),A          ; put a zero there.
16893
        INC     HL              ; address fifth byte.
16894
        LD      (HL),A          ; put a zero there.
16895
        POP     HL              ; restore pointer to the first byte.
16896
        RET                     ; return.
16897
 
16898
; -----------------------
16899
; Handle OR operator (07)
16900
; -----------------------
16901
; The Boolean OR operator. eg. X OR Y
16902
; The result is zero if both values are zero else a non-zero value.
16903
;
16904
; e.g.    0 OR 0  returns 0.
16905
;        -3 OR 0  returns -3.
16906
;         0 OR -3 returns 1.
16907
;        -3 OR 2  returns 1.
16908
;
16909
; A binary operation.
16910
; On entry HL points to first operand (X) and DE to second operand (Y).
16911
 
16912
;; or
16913
L351B:  EX      DE,HL           ; make HL point to second number
16914
        CALL    L34E9           ; routine TEST-ZERO
16915
        EX      DE,HL           ; restore pointers
16916
        RET     C               ; return if result was zero - first operand, 
16917
                                ; now the last value, is the result.
16918
 
16919
        SCF                     ; set carry flag
16920
        JR      L350B           ; back to FP-0/1 to overwrite the first operand
16921
                                ; with the value 1.
16922
 
16923
 
16924
; -----------------------------
16925
; Handle number AND number (08)
16926
; -----------------------------
16927
; The Boolean AND operator.
16928
;
16929
; e.g.    -3 AND 2  returns -3.
16930
;         -3 AND 0  returns 0.
16931
;          0 and -2 returns 0.
16932
;          0 and 0  returns 0.
16933
;
16934
; Compare with OR routine above.
16935
 
16936
;; no-&-no
16937
L3524:  EX      DE,HL           ; make HL address second operand.
16938
 
16939
        CALL    L34E9           ; routine TEST-ZERO sets carry if zero.
16940
 
16941
        EX      DE,HL           ; restore pointers.
16942
        RET     NC              ; return if second non-zero, first is result.
16943
 
16944
;
16945
 
16946
        AND     A               ; else clear carry.
16947
        JR      L350B           ; back to FP-0/1 to overwrite first operand
16948
                                ; with zero for return value.
16949
 
16950
; -----------------------------
16951
; Handle string AND number (10)
16952
; -----------------------------
16953
; e.g. "You Win" AND score>99 will return the string if condition is true
16954
; or the null string if false.
16955
 
16956
;; str-&-no
16957
L352D:  EX      DE,HL           ; make HL point to the number.
16958
        CALL    L34E9           ; routine TEST-ZERO.
16959
        EX      DE,HL           ; restore pointers. 
16960
        RET     NC              ; return if number was not zero - the string 
16961
                                ; is the result.
16962
 
16963
; if the number was zero (false) then the null string must be returned by
16964
; altering the length of the string on the calculator stack to zero.
16965
 
16966
        PUSH    DE              ; save pointer to the now obsolete number 
16967
                                ; (which will become the new STKEND)
16968
 
16969
        DEC     DE              ; point to the 5th byte of string descriptor.
16970
        XOR     A               ; clear the accumulator.
16971
        LD      (DE),A          ; place zero in high byte of length.
16972
        DEC     DE              ; address low byte of length.
16973
        LD      (DE),A          ; place zero there - now the null string.
16974
 
16975
        POP     DE              ; restore pointer - new STKEND.
16976
        RET                     ; return.
16977
 
16978
; -----------------------------------
16979
; Perform comparison ($09-$0E, $11-$16)
16980
; -----------------------------------
16981
; True binary operations.
16982
;
16983
; A single entry point is used to evaluate six numeric and six string
16984
; comparisons. On entry, the calculator literal is in the B register and
16985
; the two numeric values, or the two string parameters, are on the 
16986
; calculator stack.
16987
; The individual bits of the literal are manipulated to group similar
16988
; operations although the SUB 8 instruction does nothing useful and merely
16989
; alters the string test bit.
16990
; Numbers are compared by subtracting one from the other, strings are 
16991
; compared by comparing every character until a mismatch, or the end of one
16992
; or both, is reached.
16993
;
16994
; Numeric Comparisons.
16995
; --------------------
16996
; The 'x>y' example is the easiest as it employs straight-thru logic.
16997
; Number y is subtracted from x and the result tested for greater-0 yielding
16998
; a final value 1 (true) or 0 (false). 
16999
; For 'x<y' the same logic is used but the two values are first swapped on the
17000
; calculator stack. 
17001
; For 'x=y' NOT is applied to the subtraction result yielding true if the
17002
; difference was zero and false with anything else. 
17003
; The first three numeric comparisons are just the opposite of the last three
17004
; so the same processing steps are used and then a final NOT is applied.
17005
;
17006
; literal    Test   No  sub 8       ExOrNot  1st RRCA  exch sub  ?   End-Tests
17007
; =========  ====   == ======== === ======== ========  ==== ===  =  === === ===
17008
; no-l-eql   x<=y   09 00000001 dec 00000000 00000000  ---- x-y  ?  --- >0? NOT
17009
; no-gr-eql  x>=y   0A 00000010 dec 00000001 10000000c swap y-x  ?  --- >0? NOT
17010
; nos-neql   x<>y   0B 00000011 dec 00000010 00000001  ---- x-y  ?  NOT --- NOT
17011
; no-grtr    x>y    0C 00000100  -  00000100 00000010  ---- x-y  ?  --- >0? ---
17012
; no-less    x<y    0D 00000101  -  00000101 10000010c swap y-x  ?  --- >0? ---
17013
; nos-eql    x=y    0E 00000110  -  00000110 00000011  ---- x-y  ?  NOT --- ---
17014
;
17015
;                                                           comp -> C/F
17016
;                                                           ====    ===
17017
; str-l-eql  x$<=y$ 11 00001001 dec 00001000 00000100  ---- x$y$ 0  !or >0? NOT
17018
; str-gr-eql x$>=y$ 12 00001010 dec 00001001 10000100c swap y$x$ 0  !or >0? NOT
17019
; strs-neql  x$<>y$ 13 00001011 dec 00001010 00000101  ---- x$y$ 0  !or >0? NOT
17020
; str-grtr   x$>y$  14 00001100  -  00001100 00000110  ---- x$y$ 0  !or >0? ---
17021
; str-less   x$<y$  15 00001101  -  00001101 10000110c swap y$x$ 0  !or >0? ---
17022
; strs-eql   x$=y$  16 00001110  -  00001110 00000111  ---- x$y$ 0  !or >0? ---
17023
;
17024
; String comparisons are a little different in that the eql/neql carry flag
17025
; from the 2nd RRCA is, as before, fed into the first of the end tests but
17026
; along the way it gets modified by the comparison process. The result on the
17027
; stack always starts off as zero and the carry fed in determines if NOT is 
17028
; applied to it. So the only time the greater-0 test is applied is if the
17029
; stack holds zero which is not very efficient as the test will always yield
17030
; zero. The most likely explanation is that there were once separate end tests
17031
; for numbers and strings.
17032
 
17033
;; no-l-eql, etc.
17034
L353B:  LD      A,B             ; transfer literal to accumulator.
17035
        SUB     $08             ; subtract eight - which is not useful. 
17036
 
17037
        BIT     2,A             ; isolate '>', '<', '='.
17038
 
17039
        JR      NZ,L3543        ; skip to EX-OR-NOT with these.
17040
 
17041
        DEC     A               ; else make $00-$02, $08-$0A to match bits 0-2.
17042
 
17043
;; EX-OR-NOT
17044
L3543:  RRCA                    ; the first RRCA sets carry for a swap. 
17045
        JR      NC,L354E        ; forward to NU-OR-STR with other 8 cases
17046
 
17047
; for the other 4 cases the two values on the calculator stack are exchanged.
17048
 
17049
        PUSH    AF              ; save A and carry.
17050
        PUSH    HL              ; save HL - pointer to first operand.
17051
                                ; (DE points to second operand).
17052
 
17053
        CALL    L343C           ; routine exchange swaps the two values.
17054
                                ; (HL = second operand, DE = STKEND)
17055
 
17056
        POP     DE              ; DE = first operand
17057
        EX      DE,HL           ; as we were.
17058
        POP     AF              ; restore A and carry.
17059
 
17060
; Note. it would be better if the 2nd RRCA preceded the string test.
17061
; It would save two duplicate bytes and if we also got rid of that sub 8 
17062
; at the beginning we wouldn't have to alter which bit we test.
17063
 
17064
;; NU-OR-STR
17065
L354E:  BIT     2,A             ; test if a string comparison.
17066
        JR      NZ,L3559        ; forward to STRINGS if so.
17067
 
17068
; continue with numeric comparisons.
17069
 
17070
        RRCA                    ; 2nd RRCA causes eql/neql to set carry.
17071
        PUSH    AF              ; save A and carry
17072
 
17073
        CALL    L300F           ; routine subtract leaves result on stack.
17074
        JR      L358C           ; forward to END-TESTS
17075
 
17076
; ---
17077
 
17078
;; STRINGS
17079
L3559:  RRCA                    ; 2nd RRCA causes eql/neql to set carry.
17080
        PUSH    AF              ; save A and carry.
17081
 
17082
        CALL    L2BF1           ; routine STK-FETCH gets 2nd string params
17083
        PUSH    DE              ; save start2 *.
17084
        PUSH    BC              ; and the length.
17085
 
17086
        CALL    L2BF1           ; routine STK-FETCH gets 1st string 
17087
                                ; parameters - start in DE, length in BC.
17088
        POP     HL              ; restore length of second to HL.
17089
 
17090
; A loop is now entered to compare, by subtraction, each corresponding character
17091
; of the strings. For each successful match, the pointers are incremented and 
17092
; the lengths decreased and the branch taken back to here. If both string 
17093
; remainders become null at the same time, then an exact match exists.
17094
 
17095
;; BYTE-COMP
17096
L3564:  LD      A,H             ; test if the second string
17097
        OR      L               ; is the null string and hold flags.
17098
 
17099
        EX      (SP),HL         ; put length2 on stack, bring start2 to HL *.
17100
        LD      A,B             ; hi byte of length1 to A
17101
 
17102
        JR      NZ,L3575        ; forward to SEC-PLUS if second not null.
17103
 
17104
        OR      C               ; test length of first string.
17105
 
17106
;; SECND-LOW
17107
L356B:  POP     BC              ; pop the second length off stack.
17108
        JR      Z,L3572         ; forward to BOTH-NULL if first string is also
17109
                                ; of zero length.
17110
 
17111
; the true condition - first is longer than second (SECND-LESS)
17112
 
17113
        POP     AF              ; restore carry (set if eql/neql)
17114
        CCF                     ; complement carry flag.
17115
                                ; Note. equality becomes false.
17116
                                ; Inequality is true. By swapping or applying
17117
                                ; a terminal 'not', all comparisons have been
17118
                                ; manipulated so that this is success path. 
17119
        JR      L3588           ; forward to leave via STR-TEST
17120
 
17121
; ---
17122
; the branch was here with a match
17123
 
17124
;; BOTH-NULL
17125
L3572:  POP     AF              ; restore carry - set for eql/neql
17126
        JR      L3588           ; forward to STR-TEST
17127
 
17128
; ---  
17129
; the branch was here when 2nd string not null and low byte of first is yet
17130
; to be tested.
17131
 
17132
 
17133
;; SEC-PLUS
17134
L3575:  OR      C               ; test the length of first string.
17135
        JR      Z,L3585         ; forward to FRST-LESS if length is zero.
17136
 
17137
; both strings have at least one character left.
17138
 
17139
        LD      A,(DE)          ; fetch character of first string. 
17140
        SUB     (HL)            ; subtract with that of 2nd string.
17141
        JR      C,L3585         ; forward to FRST-LESS if carry set
17142
 
17143
        JR      NZ,L356B        ; back to SECND-LOW and then STR-TEST
17144
                                ; if not exact match.
17145
 
17146
        DEC     BC              ; decrease length of 1st string.
17147
        INC     DE              ; increment 1st string pointer.
17148
 
17149
        INC     HL              ; increment 2nd string pointer.
17150
        EX      (SP),HL         ; swap with length on stack
17151
        DEC     HL              ; decrement 2nd string length
17152
        JR      L3564           ; back to BYTE-COMP
17153
 
17154
; ---
17155
; the false condition.
17156
 
17157
;; FRST-LESS
17158
L3585:  POP     BC              ; discard length
17159
        POP     AF              ; pop A
17160
        AND     A               ; clear the carry for false result.
17161
 
17162
; ---
17163
; exact match and x$>y$ rejoin here
17164
 
17165
;; STR-TEST
17166
L3588:  PUSH    AF              ; save A and carry
17167
 
17168
        RST     28H             ;; FP-CALC
17169
        DB    $A0             ;;stk-zero      an initial false value.
17170
        DB    $38             ;;end-calc
17171
 
17172
; both numeric and string paths converge here.
17173
 
17174
;; END-TESTS
17175
L358C:  POP     AF              ; pop carry  - will be set if eql/neql
17176
        PUSH    AF              ; save it again.
17177
 
17178
        CALL    C,L3501         ; routine NOT sets true(1) if equal(0)
17179
                                ; or, for strings, applies true result.
17180
 
17181
        POP     AF              ; pop carry and
17182
        PUSH    AF              ; save A
17183
 
17184
        CALL    NC,L34F9        ; routine GREATER-0 tests numeric subtraction 
17185
                                ; result but also needlessly tests the string 
17186
                                ; value for zero - it must be.
17187
 
17188
        POP     AF              ; pop A 
17189
        RRCA                    ; the third RRCA - test for '<=', '>=' or '<>'.
17190
        CALL    NC,L3501        ; apply a terminal NOT if so.
17191
        RET                     ; return.
17192
 
17193
; -------------------------
17194
; String concatenation ($17)
17195
; -------------------------
17196
; This literal combines two strings into one e.g. LET a$ = b$ + c$
17197
; The two parameters of the two strings to be combined are on the stack.
17198
 
17199
;; strs-add
17200
L359C:  CALL    L2BF1           ; routine STK-FETCH fetches string parameters
17201
                                ; and deletes calculator stack entry.
17202
        PUSH    DE              ; save start address.
17203
        PUSH    BC              ; and length.
17204
 
17205
        CALL    L2BF1           ; routine STK-FETCH for first string
17206
        POP     HL              ; re-fetch first length
17207
        PUSH    HL              ; and save again
17208
        PUSH    DE              ; save start of second string
17209
        PUSH    BC              ; and its length.
17210
 
17211
        ADD     HL,BC           ; add the two lengths.
17212
        LD      B,H             ; transfer to BC
17213
        LD      C,L             ; and create
17214
        RST     30H             ; BC-SPACES in workspace.
17215
                                ; DE points to start of space.
17216
 
17217
        CALL    L2AB2           ; routine STK-STO-$ stores parameters
17218
                                ; of new string updating STKEND.
17219
 
17220
        POP     BC              ; length of first
17221
        POP     HL              ; address of start
17222
        LD      A,B             ; test for
17223
        OR      C               ; zero length.
17224
        JR      Z,L35B7         ; to OTHER-STR if null string
17225
 
17226
        LDIR                    ; copy string to workspace.
17227
 
17228
;; OTHER-STR
17229
L35B7:  POP     BC              ; now second length
17230
        POP     HL              ; and start of string
17231
        LD      A,B             ; test this one
17232
        OR      C               ; for zero length
17233
        JR      Z,L35BF         ; skip forward to STK-PNTRS if so as complete.
17234
 
17235
        LDIR                    ; else copy the bytes.
17236
                                ; and continue into next routine which
17237
                                ; sets the calculator stack pointers.
17238
 
17239
; --------------------
17240
; Check stack pointers
17241
; --------------------
17242
; Register DE is set to STKEND and HL, the result pointer, is set to five 
17243
; locations below this.
17244
; This routine is used when it is inconvenient to save these values at the
17245
; time the calculator stack is manipulated due to other activity on the 
17246
; machine stack.
17247
; This routine is also used to terminate the VAL and READ-IN  routines for
17248
; the same reason and to initialize the calculator stack at the start of
17249
; the CALCULATE routine.
17250
 
17251
;; STK-PNTRS
17252
L35BF:  LD      HL,($5C65)      ; fetch STKEND value from system variable.
17253
        LD      DE,$FFFB        ; the value -5
17254
        PUSH    HL              ; push STKEND value.
17255
 
17256
        ADD     HL,DE           ; subtract 5 from HL.
17257
 
17258
        POP     DE              ; pop STKEND to DE.
17259
        RET                     ; return.
17260
 
17261
; ----------------
17262
; Handle CHR$ (2F)
17263
; ----------------
17264
; This function returns a single character string that is a result of 
17265
; converting a number in the range 0-255 to a string e.g. CHR$ 65 = "A".
17266
 
17267
;; chrs
17268
L35C9:  CALL    L2DD5           ; routine FP-TO-A puts the number in A.
17269
 
17270
        JR      C,L35DC         ; forward to REPORT-Bd if overflow
17271
        JR      NZ,L35DC        ; forward to REPORT-Bd if negative
17272
 
17273
        PUSH    AF              ; save the argument.
17274
 
17275
        LD      BC,$0001        ; one space required.
17276
        RST     30H             ; BC-SPACES makes DE point to start
17277
 
17278
        POP     AF              ; restore the number.
17279
 
17280
        LD      (DE),A          ; and store in workspace
17281
 
17282
        CALL    L2AB2           ; routine STK-STO-$ stacks descriptor.
17283
 
17284
        EX      DE,HL           ; make HL point to result and DE to STKEND.
17285
        RET                     ; return.
17286
 
17287
; ---
17288
 
17289
;; REPORT-Bd
17290
L35DC:  RST     08H             ; ERROR-1
17291
        DB    $0A             ; Error Report: Integer out of range
17292
 
17293
; ----------------------------
17294
; Handle VAL and VAL$ ($1D, $18)
17295
; ----------------------------
17296
; VAL treats the characters in a string as a numeric expression.
17297
;     e.g. VAL "2.3" = 2.3, VAL "2+4" = 6, VAL ("2" + "4") = 24.
17298
; VAL$ treats the characters in a string as a string expression.
17299
;     e.g. VAL$ (z$+"(2)") = a$(2) if z$ happens to be "a$".
17300
 
17301
;; val
17302
;; val$
17303
L35DE:  LD      HL,($5C5D)      ; fetch value of system variable CH_ADD
17304
        PUSH    HL              ; and save on the machine stack.
17305
        LD      A,B             ; fetch the literal (either $1D or $18).
17306
        ADD     A,$E3           ; add $E3 to form $00 (setting carry) or $FB.
17307
        SBC     A,A             ; now form $FF bit 6 = numeric result
17308
                                ; or $00 bit 6 = string result.
17309
        PUSH    AF              ; save this mask on the stack
17310
 
17311
        CALL    L2BF1           ; routine STK-FETCH fetches the string operand
17312
                                ; from calculator stack.
17313
 
17314
        PUSH    DE              ; save the address of the start of the string.
17315
        INC     BC              ; increment the length for a carriage return.
17316
 
17317
        RST     30H             ; BC-SPACES creates the space in workspace.
17318
        POP     HL              ; restore start of string to HL.
17319
        LD      ($5C5D),DE      ; load CH_ADD with start DE in workspace.
17320
 
17321
        PUSH    DE              ; save the start in workspace
17322
        LDIR                    ; copy string from program or variables or
17323
                                ; workspace to the workspace area.
17324
        EX      DE,HL           ; end of string + 1 to HL
17325
        DEC     HL              ; decrement HL to point to end of new area.
17326
        LD      (HL),$0D        ; insert a carriage return at end.
17327
        RES     7,(IY+$01)      ; update FLAGS  - signal checking syntax.
17328
        CALL    L24FB           ; routine SCANNING evaluates string
17329
                                ; expression and result.
17330
 
17331
        RST     18H             ; GET-CHAR fetches next character.
17332
        CP      $0D             ; is it the expected carriage return ?
17333
        JR      NZ,L360C        ; forward to V-RPORT-C if not
17334
                                ; 'Nonsense in BASIC'.
17335
 
17336
        POP     HL              ; restore start of string in workspace.
17337
        POP     AF              ; restore expected result flag (bit 6).
17338
        XOR     (IY+$01)        ; xor with FLAGS now updated by SCANNING.
17339
        AND     $40             ; test bit 6 - should be zero if result types
17340
                                ; match.
17341
 
17342
;; V-RPORT-C
17343
L360C:  JP      NZ,L1C8A        ; jump back to REPORT-C with a result mismatch.
17344
 
17345
        LD      ($5C5D),HL      ; set CH_ADD to the start of the string again.
17346
        SET     7,(IY+$01)      ; update FLAGS  - signal running program.
17347
        CALL    L24FB           ; routine SCANNING evaluates the string
17348
                                ; in full leaving result on calculator stack.
17349
 
17350
        POP     HL              ; restore saved character address in program.
17351
        LD      ($5C5D),HL      ; and reset the system variable CH_ADD.
17352
 
17353
        JR      L35BF           ; back to exit via STK-PNTRS.
17354
                                ; resetting the calculator stack pointers
17355
                                ; HL and DE from STKEND as it wasn't possible 
17356
                                ; to preserve them during this routine.
17357
 
17358
; ----------------
17359
; Handle STR$ (2E)
17360
; ----------------
17361
;
17362
;
17363
 
17364
;; str$
17365
L361F:  LD      BC,$0001        ; create an initial byte in workspace
17366
        RST     30H             ; using BC-SPACES restart.
17367
 
17368
        LD      ($5C5B),HL      ; set system variable K_CUR to new location.
17369
        PUSH    HL              ; and save start on machine stack also.
17370
 
17371
        LD      HL,($5C51)      ; fetch value of system variable CURCHL
17372
        PUSH    HL              ; and save that too.
17373
 
17374
        LD      A,$FF           ; select system channel 'R'.
17375
        CALL    L1601           ; routine CHAN-OPEN opens it.
17376
        CALL    L2DE3           ; routine PRINT-FP outputs the number to
17377
                                ; workspace updating K-CUR.
17378
 
17379
        POP     HL              ; restore current channel.
17380
        CALL    L1615           ; routine CHAN-FLAG resets flags.
17381
 
17382
        POP     DE              ; fetch saved start of string to DE.
17383
        LD      HL,($5C5B)      ; load HL with end of string from K_CUR.
17384
 
17385
        AND     A               ; prepare for true subtraction.
17386
        SBC     HL,DE           ; subtract start from end to give length.
17387
        LD      B,H             ; transfer the length to
17388
        LD      C,L             ; the BC register pair.
17389
 
17390
        CALL    L2AB2           ; routine STK-STO-$ stores string parameters
17391
                                ; on the calculator stack.
17392
 
17393
        EX      DE,HL           ; HL = last value, DE = STKEND.
17394
        RET                     ; return.
17395
 
17396
; ------------
17397
; Read-in (1A)
17398
; ------------
17399
; This is the calculator literal used by the INKEY$ function when a '#'
17400
; is encountered after the keyword.
17401
; INKEY$ # does not interact correctly with the keyboard, #0 or #1, and
17402
; its uses are for other channels.
17403
 
17404
;; read-in
17405
L3645:  CALL    L1E94           ; routine FIND-INT1 fetches stream to A
17406
        CP      $10             ; compare with 16 decimal.
17407
        JP      NC,L1E9F        ; jump to REPORT-Bb if not in range 0 - 15.
17408
                                ; 'Integer out of range'
17409
                                ; (REPORT-Bd is within range)
17410
 
17411
        LD      HL,($5C51)      ; fetch current channel CURCHL
17412
        PUSH    HL              ; save it
17413
        CALL    L1601           ; routine CHAN-OPEN opens channel
17414
 
17415
        CALL    L15E6           ; routine INPUT-AD - the channel must have an
17416
                                ; input stream or else error here from stream
17417
                                ; stub.
17418
        LD      BC,$0000        ; initialize length of string to zero
17419
        JR      NC,L365F        ; forward to R-I-STORE if no key detected.
17420
 
17421
        INC     C               ; increase length to one.
17422
 
17423
        RST     30H             ; BC-SPACES creates space for one character
17424
                                ; in workspace.
17425
        LD      (DE),A          ; the character is inserted.
17426
 
17427
;; R-I-STORE
17428
L365F:  CALL    L2AB2           ; routine STK-STO-$ stacks the string
17429
                                ; parameters.
17430
        POP     HL              ; restore current channel address
17431
        CALL    L1615           ; routine CHAN-FLAG resets current channel
17432
                                ; system variable and flags.
17433
        JP      L35BF           ; jump back to STK-PNTRS
17434
 
17435
; ----------------
17436
; Handle CODE (1C)
17437
; ----------------
17438
; Returns the ASCII code of a character or first character of a string
17439
; e.g. CODE "Aardvark" = 65, CODE "" = 0.
17440
 
17441
;; code
17442
L3669:  CALL    L2BF1           ; routine STK-FETCH to fetch and delete the
17443
                                ; string parameters.
17444
                                ; DE points to the start, BC holds the length.
17445
        LD      A,B             ; test length
17446
        OR      C               ; of the string.
17447
        JR      Z,L3671         ; skip to STK-CODE with zero if the null string.
17448
 
17449
        LD      A,(DE)          ; else fetch the first character.
17450
 
17451
;; STK-CODE
17452
L3671:  JP      L2D28           ; jump back to STACK-A (with memory check)
17453
 
17454
; ---------------
17455
; Handle LEN (1E)
17456
; ---------------
17457
; Returns the length of a string.
17458
; In Sinclair BASIC strings can be more than twenty thousand characters long
17459
; so a sixteen-bit register is required to store the length
17460
 
17461
;; len
17462
L3674:  CALL    L2BF1           ; routine STK-FETCH to fetch and delete the
17463
                                ; string parameters from the calculator stack.
17464
                                ; register BC now holds the length of string.
17465
 
17466
        JP      L2D2B           ; jump back to STACK-BC to save result on the
17467
                                ; calculator stack (with memory check).
17468
 
17469
; -------------------------
17470
; Decrease the counter (35)
17471
; -------------------------
17472
; The calculator has an instruction that decrements a single-byte
17473
; pseudo-register and makes consequential relative jumps just like
17474
; the Z80's DJNZ instruction.
17475
 
17476
;; dec-jr-nz
17477
L367A:  EXX                     ; switch in set that addresses code
17478
 
17479
        PUSH    HL              ; save pointer to offset byte
17480
        LD      HL,$5C67        ; address BREG in system variables
17481
        DEC     (HL)            ; decrement it
17482
        POP     HL              ; restore pointer
17483
 
17484
        JR      NZ,L3687        ; to JUMP-2 if not zero
17485
 
17486
        INC     HL              ; step past the jump length.
17487
        EXX                     ; switch in the main set.
17488
        RET                     ; return.
17489
 
17490
; Note. as a general rule the calculator avoids using the IY register
17491
; otherwise the cumbersome 4 instructions in the middle could be replaced by
17492
; dec (iy+$2d) - three bytes instead of six.
17493
 
17494
 
17495
; ---------
17496
; Jump (33)
17497
; ---------
17498
; This enables the calculator to perform relative jumps just like
17499
; the Z80 chip's JR instruction
17500
 
17501
;; jump
17502
;; JUMP
17503
L3686:  EXX                     ;switch in pointer set
17504
 
17505
;; JUMP-2
17506
L3687:  LD      E,(HL)          ; the jump byte 0-127 forward, 128-255 back.
17507
        LD      A,E             ; transfer to accumulator.
17508
        RLA                     ; if backward jump, carry is set.
17509
        SBC     A,A             ; will be $FF if backward or $00 if forward.
17510
        LD      D,A             ; transfer to high byte.
17511
        ADD     HL,DE           ; advance calculator pointer forward or back.
17512
        EXX                     ; switch back.
17513
        RET                     ; return.
17514
 
17515
; -----------------
17516
; Jump on true (00)
17517
; -----------------
17518
; This enables the calculator to perform conditional relative jumps
17519
; dependent on whether the last test gave a true result
17520
 
17521
;; jump-true
17522
L368F:  INC     DE              ; collect the 
17523
        INC     DE              ; third byte
17524
        LD      A,(DE)          ; of the test
17525
        DEC     DE              ; result and
17526
        DEC     DE              ; backtrack.
17527
 
17528
        AND     A               ; is result 0 or 1 ? 
17529
        JR      NZ,L3686        ; back to JUMP if true (1).
17530
 
17531
        EXX                     ; else switch in the pointer set.
17532
        INC     HL              ; step past the jump length.
17533
        EXX                     ; switch in the main set.
17534
        RET                     ; return.
17535
 
17536
; -----------------------
17537
; End of calculation (38)
17538
; -----------------------
17539
; The end-calc literal terminates a mini-program written in the Spectrum's
17540
; internal language.
17541
 
17542
;; end-calc
17543
L369B:  POP     AF              ; drop the calculator return address RE-ENTRY
17544
        EXX                     ; switch to the other set.
17545
 
17546
        EX      (SP),HL         ; transfer H'L' to machine stack for the
17547
                                ; return address.
17548
                                ; when exiting recursion then the previous
17549
                                ; pointer is transferred to H'L'.
17550
 
17551
        EXX                     ; back to main set.
17552
        RET                     ; return.
17553
 
17554
 
17555
; ------------------------
17556
; THE 'MODULUS' SUBROUTINE 
17557
; ------------------------
17558
; (offset: $32 'n-mod-m')
17559
;
17560
;
17561
 
17562
;; n-mod-m
17563
L36A0:  RST     28H             ;; FP-CALC          17, 3.
17564
        DB    $C0             ;;st-mem-0          17, 3.
17565
        DB    $02             ;;delete            17.
17566
        DB    $31             ;;duplicate         17, 17.
17567
        DB    $E0             ;;get-mem-0         17, 17, 3.
17568
        DB    $05             ;;division          17, 17/3.
17569
        DB    $27             ;;int               17, 5.
17570
        DB    $E0             ;;get-mem-0         17, 5, 3.
17571
        DB    $01             ;;exchange          17, 3, 5.
17572
        DB    $C0             ;;st-mem-0          17, 3, 5.
17573
        DB    $04             ;;multiply          17, 15.
17574
        DB    $03             ;;subtract          2.
17575
        DB    $E0             ;;get-mem-0         2, 5.
17576
        DB    $38             ;;end-calc          2, 5.
17577
 
17578
        RET                     ; return.
17579
 
17580
 
17581
; ------------------
17582
; THE 'INT' FUNCTION
17583
; ------------------
17584
; (offset $27: 'int' )
17585
;
17586
; This function returns the integer of x, which is just the same as truncate
17587
; for positive numbers. The truncate literal truncates negative numbers
17588
; upwards so that -3.4 gives -3 whereas the BASIC INT function has to
17589
; truncate negative numbers down so that INT -3.4 is -4.
17590
; It is best to work through using, say, +-3.4 as examples.
17591
 
17592
;; int
17593
L36AF:  RST     28H             ;; FP-CALC              x.    (= 3.4 or -3.4).
17594
        DB    $31             ;;duplicate             x, x.
17595
        DB    $36             ;;less-0                x, (1/0)
17596
        DB    $00             ;;jump-true             x, (1/0)
17597
        DB    $04             ;;to L36B7, X-NEG
17598
 
17599
        DB    $3A             ;;truncate              trunc 3.4 = 3.
17600
        DB    $38             ;;end-calc              3.
17601
 
17602
        RET                     ; return with + int x on stack.
17603
 
17604
; ---
17605
 
17606
 
17607
;; X-NEG
17608
L36B7:  DB    $31             ;;duplicate             -3.4, -3.4.
17609
        DB    $3A             ;;truncate              -3.4, -3.
17610
        DB    $C0             ;;st-mem-0              -3.4, -3.
17611
        DB    $03             ;;subtract              -.4
17612
        DB    $E0             ;;get-mem-0             -.4, -3.
17613
        DB    $01             ;;exchange              -3, -.4.
17614
        DB    $30             ;;not                   -3, (0).
17615
        DB    $00             ;;jump-true             -3.
17616
        DB    $03             ;;to L36C2, EXIT        -3.
17617
 
17618
        DB    $A1             ;;stk-one               -3, 1.
17619
        DB    $03             ;;subtract              -4.
17620
 
17621
;; EXIT
17622
L36C2:  DB    $38             ;;end-calc              -4.
17623
 
17624
        RET                     ; return.
17625
 
17626
 
17627
; ----------------
17628
; Exponential (26)
17629
; ----------------
17630
;
17631
;
17632
 
17633
;; EXP
17634
;; exp
17635
L36C4:  RST     28H             ;; FP-CALC
17636
        DB    $3D             ;;re-stack
17637
        DB    $34             ;;stk-data
17638
        DB    $F1             ;;Exponent: $81, Bytes: 4
17639
        DB    $38,$AA,$3B,$29 ;;
17640
        DB    $04             ;;multiply
17641
        DB    $31             ;;duplicate
17642
        DB    $27             ;;int
17643
        DB    $C3             ;;st-mem-3
17644
        DB    $03             ;;subtract
17645
        DB    $31             ;;duplicate
17646
        DB    $0F             ;;addition
17647
        DB    $A1             ;;stk-one
17648
        DB    $03             ;;subtract
17649
        DB    $88             ;;series-08
17650
        DB    $13             ;;Exponent: $63, Bytes: 1
17651
        DB    $36             ;;(+00,+00,+00)
17652
        DB    $58             ;;Exponent: $68, Bytes: 2
17653
        DB    $65,$66         ;;(+00,+00)
17654
        DB    $9D             ;;Exponent: $6D, Bytes: 3
17655
        DB    $78,$65,$40     ;;(+00)
17656
        DB    $A2             ;;Exponent: $72, Bytes: 3
17657
        DB    $60,$32,$C9     ;;(+00)
17658
        DB    $E7             ;;Exponent: $77, Bytes: 4
17659
        DB    $21,$F7,$AF,$24 ;;
17660
        DB    $EB             ;;Exponent: $7B, Bytes: 4
17661
        DB    $2F,$B0,$B0,$14 ;;
17662
        DB    $EE             ;;Exponent: $7E, Bytes: 4
17663
        DB    $7E,$BB,$94,$58 ;;
17664
        DB    $F1             ;;Exponent: $81, Bytes: 4
17665
        DB    $3A,$7E,$F8,$CF ;;
17666
        DB    $E3             ;;get-mem-3
17667
        DB    $38             ;;end-calc
17668
 
17669
        CALL    L2DD5           ; routine FP-TO-A
17670
        JR      NZ,L3705        ; to N-NEGTV
17671
 
17672
        JR      C,L3703         ; to REPORT-6b
17673
 
17674
        ADD     A,(HL)          ;
17675
        JR      NC,L370C        ; to RESULT-OK
17676
 
17677
 
17678
;; REPORT-6b
17679
L3703:  RST     08H             ; ERROR-1
17680
        DB    $05             ; Error Report: Number too big
17681
 
17682
;; N-NEGTV
17683
L3705:  JR      C,L370E         ; to RSLT-ZERO
17684
 
17685
        SUB     (HL)            ;
17686
        JR      NC,L370E        ; to RSLT-ZERO
17687
 
17688
        NEG                     ; Negate
17689
 
17690
;; RESULT-OK
17691
L370C:  LD      (HL),A          ;
17692
        RET                     ; return.
17693
 
17694
; ---
17695
 
17696
 
17697
;; RSLT-ZERO
17698
L370E:  RST     28H             ;; FP-CALC
17699
        DB    $02             ;;delete
17700
        DB    $A0             ;;stk-zero
17701
        DB    $38             ;;end-calc
17702
 
17703
        RET                     ; return.
17704
 
17705
 
17706
; ----------------------
17707
; Natural logarithm (25)
17708
; ----------------------
17709
;
17710
;
17711
 
17712
;; ln
17713
L3713:  RST     28H             ;; FP-CALC
17714
        DB    $3D             ;;re-stack
17715
        DB    $31             ;;duplicate
17716
        DB    $37             ;;greater-0
17717
        DB    $00             ;;jump-true
17718
        DB    $04             ;;to L371C, VALID
17719
 
17720
        DB    $38             ;;end-calc
17721
 
17722
 
17723
;; REPORT-Ab
17724
L371A:  RST     08H             ; ERROR-1
17725
        DB    $09             ; Error Report: Invalid argument
17726
 
17727
;; VALID
17728
L371C:  DB    $A0             ;;stk-zero
17729
        DB    $02             ;;delete
17730
        DB    $38             ;;end-calc
17731
        LD      A,(HL)          ;
17732
 
17733
        LD      (HL),$80        ;
17734
        CALL    L2D28           ; routine STACK-A
17735
 
17736
        RST     28H             ;; FP-CALC
17737
        DB    $34             ;;stk-data
17738
        DB    $38             ;;Exponent: $88, Bytes: 1
17739
        DB    $00             ;;(+00,+00,+00)
17740
        DB    $03             ;;subtract
17741
        DB    $01             ;;exchange
17742
        DB    $31             ;;duplicate
17743
        DB    $34             ;;stk-data
17744
        DB    $F0             ;;Exponent: $80, Bytes: 4
17745
        DB    $4C,$CC,$CC,$CD ;;
17746
        DB    $03             ;;subtract
17747
        DB    $37             ;;greater-0
17748
        DB    $00             ;;jump-true
17749
        DB    $08             ;;to L373D, GRE.8
17750
 
17751
        DB    $01             ;;exchange
17752
        DB    $A1             ;;stk-one
17753
        DB    $03             ;;subtract
17754
        DB    $01             ;;exchange
17755
        DB    $38             ;;end-calc
17756
 
17757
        INC     (HL)            ;
17758
 
17759
        RST     28H             ;; FP-CALC
17760
 
17761
;; GRE.8
17762
L373D:  DB    $01             ;;exchange
17763
        DB    $34             ;;stk-data
17764
        DB    $F0             ;;Exponent: $80, Bytes: 4
17765
        DB    $31,$72,$17,$F8 ;;
17766
        DB    $04             ;;multiply
17767
        DB    $01             ;;exchange
17768
        DB    $A2             ;;stk-half
17769
        DB    $03             ;;subtract
17770
        DB    $A2             ;;stk-half
17771
        DB    $03             ;;subtract
17772
        DB    $31             ;;duplicate
17773
        DB    $34             ;;stk-data
17774
        DB    $32             ;;Exponent: $82, Bytes: 1
17775
        DB    $20             ;;(+00,+00,+00)
17776
        DB    $04             ;;multiply
17777
        DB    $A2             ;;stk-half
17778
        DB    $03             ;;subtract
17779
        DB    $8C             ;;series-0C
17780
        DB    $11             ;;Exponent: $61, Bytes: 1
17781
        DB    $AC             ;;(+00,+00,+00)
17782
        DB    $14             ;;Exponent: $64, Bytes: 1
17783
        DB    $09             ;;(+00,+00,+00)
17784
        DB    $56             ;;Exponent: $66, Bytes: 2
17785
        DB    $DA,$A5         ;;(+00,+00)
17786
        DB    $59             ;;Exponent: $69, Bytes: 2
17787
        DB    $30,$C5         ;;(+00,+00)
17788
        DB    $5C             ;;Exponent: $6C, Bytes: 2
17789
        DB    $90,$AA         ;;(+00,+00)
17790
        DB    $9E             ;;Exponent: $6E, Bytes: 3
17791
        DB    $70,$6F,$61     ;;(+00)
17792
        DB    $A1             ;;Exponent: $71, Bytes: 3
17793
        DB    $CB,$DA,$96     ;;(+00)
17794
        DB    $A4             ;;Exponent: $74, Bytes: 3
17795
        DB    $31,$9F,$B4     ;;(+00)
17796
        DB    $E7             ;;Exponent: $77, Bytes: 4
17797
        DB    $A0,$FE,$5C,$FC ;;
17798
        DB    $EA             ;;Exponent: $7A, Bytes: 4
17799
        DB    $1B,$43,$CA,$36 ;;
17800
        DB    $ED             ;;Exponent: $7D, Bytes: 4
17801
        DB    $A7,$9C,$7E,$5E ;;
17802
        DB    $F0             ;;Exponent: $80, Bytes: 4
17803
        DB    $6E,$23,$80,$93 ;;
17804
        DB    $04             ;;multiply
17805
        DB    $0F             ;;addition
17806
        DB    $38             ;;end-calc
17807
 
17808
        RET                     ; return.
17809
 
17810
 
17811
; -----------------------------
17812
; THE 'TRIGONOMETRIC' FUNCTIONS
17813
; -----------------------------
17814
; Trigonometry is rocket science. It is also used by carpenters and pyramid
17815
; builders. 
17816
; Some uses can be quite abstract but the principles can be seen in simple
17817
; right-angled triangles. Triangles have some special properties -
17818
;
17819
; 1) The sum of the three angles is always PI radians (180 degrees).
17820
;    Very helpful if you know two angles and wish to find the third.
17821
; 2) In any right-angled triangle the sum of the squares of the two shorter
17822
;    sides is equal to the square of the longest side opposite the right-angle.
17823
;    Very useful if you know the length of two sides and wish to know the
17824
;    length of the third side.
17825
; 3) Functions sine, cosine and tangent enable one to calculate the length 
17826
;    of an unknown side when the length of one other side and an angle is 
17827
;    known.
17828
; 4) Functions arcsin, arccosine and arctan enable one to calculate an unknown
17829
;    angle when the length of two of the sides is known.
17830
 
17831
;---------------------------------
17832
; THE 'REDUCE ARGUMENT' SUBROUTINE
17833
;---------------------------------
17834
; (offset $39: 'get-argt')
17835
;
17836
; This routine performs two functions on the angle, in radians, that forms
17837
; the argument to the sine and cosine functions.
17838
; First it ensures that the angle 'wraps round'. That if a ship turns through 
17839
; an angle of, say, 3*PI radians (540 degrees) then the net effect is to turn 
17840
; through an angle of PI radians (180 degrees).
17841
; Secondly it converts the angle in radians to a fraction of a right angle,
17842
; depending within which quadrant the angle lies, with the periodicity 
17843
; resembling that of the desired sine value.
17844
; The result lies in the range -1 to +1.              
17845
;
17846
;                     90 deg.
17847
; 
17848
;                     (pi/2)
17849
;              II       +1        I
17850
;                       |
17851
;        sin+      |\   |   /|    sin+
17852
;        cos-      | \  |  / |    cos+
17853
;        tan-      |  \ | /  |    tan+
17854
;                  |   \|/)  |           
17855
; 180 deg. (pi) 0 -|----+----|-- 0  (0)   0 degrees
17856
;                  |   /|\   |
17857
;        sin-      |  / | \  |    sin-
17858
;        cos-      | /  |  \ |    cos+
17859
;        tan+      |/   |   \|    tan-
17860
;                       |
17861
;              III      -1       IV
17862
;                     (3pi/2)
17863
;
17864
;                     270 deg.
17865
;
17866
 
17867
;; get-argt
17868
L3783:  RST     28H             ;; FP-CALC      X.
17869
        DB    $3D             ;;re-stack
17870
        DB    $34             ;;stk-data
17871
        DB    $EE             ;;Exponent: $7E, 
17872
                                ;;Bytes: 4
17873
        DB    $22,$F9,$83,$6E ;;              X, 1/(2*PI)
17874
        DB    $04             ;;multiply      X/(2*PI) = fraction
17875
        DB    $31             ;;duplicate
17876
        DB    $A2             ;;stk-half
17877
        DB    $0F             ;;addition
17878
        DB    $27             ;;int
17879
 
17880
        DB    $03             ;;subtract      now range -.5 to .5
17881
 
17882
        DB    $31             ;;duplicate
17883
        DB    $0F             ;;addition      now range -1 to 1.
17884
        DB    $31             ;;duplicate
17885
        DB    $0F             ;;addition      now range -2 to +2.
17886
 
17887
; quadrant I (0 to +1) and quadrant IV (-1 to 0) are now correct.
17888
; quadrant II ranges +1 to +2.
17889
; quadrant III ranges -2 to -1.
17890
 
17891
        DB    $31             ;;duplicate     Y, Y.
17892
        DB    $2A             ;;abs           Y, abs(Y).    range 1 to 2
17893
        DB    $A1             ;;stk-one       Y, abs(Y), 1.
17894
        DB    $03             ;;subtract      Y, abs(Y)-1.  range 0 to 1
17895
        DB    $31             ;;duplicate     Y, Z, Z.
17896
        DB    $37             ;;greater-0     Y, Z, (1/0).
17897
 
17898
        DB    $C0             ;;st-mem-0         store as possible sign 
17899
                                ;;                 for cosine function.
17900
 
17901
        DB    $00             ;;jump-true
17902
        DB    $04             ;;to L37A1, ZPLUS  with quadrants II and III.
17903
 
17904
; else the angle lies in quadrant I or IV and value Y is already correct.
17905
 
17906
        DB    $02             ;;delete        Y.   delete the test value.
17907
        DB    $38             ;;end-calc      Y.
17908
 
17909
        RET                     ; return.       with Q1 and Q4           >>>
17910
 
17911
; ---
17912
 
17913
; the branch was here with quadrants II (0 to 1) and III (1 to 0).
17914
; Y will hold -2 to -1 if this is quadrant III.
17915
 
17916
;; ZPLUS
17917
L37A1:  DB    $A1             ;;stk-one         Y, Z, 1.
17918
        DB    $03             ;;subtract        Y, Z-1.       Q3 = 0 to -1
17919
        DB    $01             ;;exchange        Z-1, Y.
17920
        DB    $36             ;;less-0          Z-1, (1/0).
17921
        DB    $00             ;;jump-true       Z-1.
17922
        DB    $02             ;;to L37A8, YNEG
17923
                                ;;if angle in quadrant III
17924
 
17925
; else angle is within quadrant II (-1 to 0)
17926
 
17927
        DB    $1B             ;;negate          range +1 to 0.
17928
 
17929
;; YNEG
17930
L37A8:  DB    $38             ;;end-calc        quadrants II and III correct.
17931
 
17932
        RET                     ; return.
17933
 
17934
 
17935
;----------------------
17936
; THE 'COSINE' FUNCTION
17937
;----------------------
17938
; (offset $20: 'cos')
17939
; Cosines are calculated as the sine of the opposite angle rectifying the 
17940
; sign depending on the quadrant rules. 
17941
;
17942
;
17943
;           /|
17944
;        h /y|
17945
;         /  |o
17946
;        /x  |
17947
;       /----|    
17948
;         a
17949
;
17950
; The cosine of angle x is the adjacent side (a) divided by the hypotenuse 1.
17951
; However if we examine angle y then a/h is the sine of that angle.
17952
; Since angle x plus angle y equals a right-angle, we can find angle y by 
17953
; subtracting angle x from pi/2.
17954
; However it's just as easy to reduce the argument first and subtract the
17955
; reduced argument from the value 1 (a reduced right-angle).
17956
; It's even easier to subtract 1 from the angle and rectify the sign.
17957
; In fact, after reducing the argument, the absolute value of the argument
17958
; is used and rectified using the test result stored in mem-0 by 'get-argt'
17959
; for that purpose.
17960
;
17961
 
17962
;; cos
17963
L37AA:  RST     28H             ;; FP-CALC              angle in radians.
17964
        DB    $39             ;;get-argt              X     reduce -1 to +1 
17965
 
17966
        DB    $2A             ;;abs                   ABS X.   0 to 1
17967
        DB    $A1             ;;stk-one               ABS X, 1.
17968
        DB    $03             ;;subtract              now opposite angle
17969
                                ;;                      although sign is -ve.
17970
 
17971
        DB    $E0             ;;get-mem-0             fetch the sign indicator
17972
        DB    $00             ;;jump-true
17973
        DB    $06             ;;fwd to L37B7, C-ENT
17974
                                ;;forward to common code if in QII or QIII.
17975
 
17976
        DB    $1B             ;;negate                else make sign +ve.
17977
        DB    $33             ;;jump
17978
        DB    $03             ;;fwd to L37B7, C-ENT
17979
                                ;; with quadrants I and IV.
17980
 
17981
;--------------------
17982
; THE 'SINE' FUNCTION
17983
;--------------------
17984
; (offset $1F: 'sin')
17985
; This is a fundamental transcendental function from which others such as cos
17986
; and tan are directly, or indirectly, derived.
17987
; It uses the series generator to produce Chebyshev polynomials.
17988
;
17989
;
17990
;           /|
17991
;        1 / |
17992
;         /  |x
17993
;        /a  |
17994
;       /----|    
17995
;         y
17996
;
17997
; The 'get-argt' function is designed to modify the angle and its sign 
17998
; in line with the desired sine value and afterwards it can launch straight
17999
; into common code.
18000
 
18001
;; sin
18002
L37B5:  RST     28H             ;; FP-CALC      angle in radians
18003
        DB    $39             ;;get-argt      reduce - sign now correct.
18004
 
18005
;; C-ENT
18006
L37B7:  DB    $31             ;;duplicate
18007
        DB    $31             ;;duplicate
18008
        DB    $04             ;;multiply
18009
        DB    $31             ;;duplicate
18010
        DB    $0F             ;;addition
18011
        DB    $A1             ;;stk-one
18012
        DB    $03             ;;subtract
18013
 
18014
        DB    $86             ;;series-06
18015
        DB    $14             ;;Exponent: $64, Bytes: 1
18016
        DB    $E6             ;;(+00,+00,+00)
18017
        DB    $5C             ;;Exponent: $6C, Bytes: 2
18018
        DB    $1F,$0B         ;;(+00,+00)
18019
        DB    $A3             ;;Exponent: $73, Bytes: 3
18020
        DB    $8F,$38,$EE     ;;(+00)
18021
        DB    $E9             ;;Exponent: $79, Bytes: 4
18022
        DB    $15,$63,$BB,$23 ;;
18023
        DB    $EE             ;;Exponent: $7E, Bytes: 4
18024
        DB    $92,$0D,$CD,$ED ;;
18025
        DB    $F1             ;;Exponent: $81, Bytes: 4
18026
        DB    $23,$5D,$1B,$EA ;;
18027
        DB    $04             ;;multiply
18028
        DB    $38             ;;end-calc
18029
 
18030
        RET                     ; return.
18031
 
18032
;-----------------------
18033
; THE 'TANGENT' FUNCTION
18034
;-----------------------
18035
; (offset $21: 'tan')
18036
;
18037
; Evaluates tangent x as    sin(x) / cos(x).
18038
;
18039
;
18040
;           /|
18041
;        h / |
18042
;         /  |o
18043
;        /x  |
18044
;       /----|    
18045
;         a
18046
;
18047
; the tangent of angle x is the ratio of the length of the opposite side 
18048
; divided by the length of the adjacent side. As the opposite length can 
18049
; be calculates using sin(x) and the adjacent length using cos(x) then 
18050
; the tangent can be defined in terms of the previous two functions.
18051
 
18052
; Error 6 if the argument, in radians, is too close to one like pi/2
18053
; which has an infinite tangent. e.g. PRINT TAN (PI/2)  evaluates as 1/0.
18054
; Similarly PRINT TAN (3*PI/2), TAN (5*PI/2) etc.
18055
 
18056
;; tan
18057
L37DA:  RST     28H             ;; FP-CALC          x.
18058
        DB    $31             ;;duplicate         x, x.
18059
        DB    $1F             ;;sin               x, sin x.
18060
        DB    $01             ;;exchange          sin x, x.
18061
        DB    $20             ;;cos               sin x, cos x.
18062
        DB    $05             ;;division          sin x/cos x (= tan x).
18063
        DB    $38             ;;end-calc          tan x.
18064
 
18065
        RET                     ; return.
18066
 
18067
;----------------------
18068
; THE 'ARCTAN' FUNCTION
18069
;----------------------
18070
; (Offset $24: 'atn')
18071
; the inverse tangent function with the result in radians.
18072
; This is a fundamental transcendental function from which others such as asn
18073
; and acs are directly, or indirectly, derived.
18074
; It uses the series generator to produce Chebyshev polynomials.
18075
 
18076
;; atn
18077
L37E2:  CALL    L3297           ; routine re-stack
18078
        LD      A,(HL)          ; fetch exponent byte.
18079
        CP      $81             ; compare to that for 'one'
18080
        JR      C,L37F8         ; forward, if less, to SMALL
18081
 
18082
        RST     28H             ;; FP-CALC
18083
        DB    $A1             ;;stk-one
18084
        DB    $1B             ;;negate
18085
        DB    $01             ;;exchange
18086
        DB    $05             ;;division
18087
        DB    $31             ;;duplicate
18088
        DB    $36             ;;less-0
18089
        DB    $A3             ;;stk-pi/2
18090
        DB    $01             ;;exchange
18091
        DB    $00             ;;jump-true
18092
        DB    $06             ;;to L37FA, CASES
18093
 
18094
        DB    $1B             ;;negate
18095
        DB    $33             ;;jump
18096
        DB    $03             ;;to L37FA, CASES
18097
 
18098
;; SMALL
18099
L37F8:  RST     28H             ;; FP-CALC
18100
        DB    $A0             ;;stk-zero
18101
 
18102
;; CASES
18103
L37FA:  DB    $01             ;;exchange
18104
        DB    $31             ;;duplicate
18105
        DB    $31             ;;duplicate
18106
        DB    $04             ;;multiply
18107
        DB    $31             ;;duplicate
18108
        DB    $0F             ;;addition
18109
        DB    $A1             ;;stk-one
18110
        DB    $03             ;;subtract
18111
        DB    $8C             ;;series-0C
18112
        DB    $10             ;;Exponent: $60, Bytes: 1
18113
        DB    $B2             ;;(+00,+00,+00)
18114
        DB    $13             ;;Exponent: $63, Bytes: 1
18115
        DB    $0E             ;;(+00,+00,+00)
18116
        DB    $55             ;;Exponent: $65, Bytes: 2
18117
        DB    $E4,$8D         ;;(+00,+00)
18118
        DB    $58             ;;Exponent: $68, Bytes: 2
18119
        DB    $39,$BC         ;;(+00,+00)
18120
        DB    $5B             ;;Exponent: $6B, Bytes: 2
18121
        DB    $98,$FD         ;;(+00,+00)
18122
        DB    $9E             ;;Exponent: $6E, Bytes: 3
18123
        DB    $00,$36,$75     ;;(+00)
18124
        DB    $A0             ;;Exponent: $70, Bytes: 3
18125
        DB    $DB,$E8,$B4     ;;(+00)
18126
        DB    $63             ;;Exponent: $73, Bytes: 2
18127
        DB    $42,$C4         ;;(+00,+00)
18128
        DB    $E6             ;;Exponent: $76, Bytes: 4
18129
        DB    $B5,$09,$36,$BE ;;
18130
        DB    $E9             ;;Exponent: $79, Bytes: 4
18131
        DB    $36,$73,$1B,$5D ;;
18132
        DB    $EC             ;;Exponent: $7C, Bytes: 4
18133
        DB    $D8,$DE,$63,$BE ;;
18134
        DB    $F0             ;;Exponent: $80, Bytes: 4
18135
        DB    $61,$A1,$B3,$0C ;;
18136
        DB    $04             ;;multiply
18137
        DB    $0F             ;;addition
18138
        DB    $38             ;;end-calc
18139
 
18140
        RET                     ; return.
18141
 
18142
 
18143
;----------------------
18144
; THE 'ARCSIN' FUNCTION
18145
;----------------------
18146
; (Offset $22: 'asn')
18147
; the inverse sine function with result in radians.
18148
; derived from arctan function above.
18149
; Error A unless the argument is between -1 and +1 inclusive.
18150
; uses an adaptation of the formula asn(x) = atn(x/sqr(1-x*x))
18151
;
18152
;
18153
;           /|
18154
;        1 / |
18155
;         /  |x
18156
;        /a  |
18157
;       /----|    
18158
;         y
18159
;
18160
; e.g. we know the opposite side (x) and hypotenuse (1) 
18161
; and we wish to find angle a in radians.
18162
; we can derive length y by Pythagorus and then use ATN instead. 
18163
; since y*y + x*x = 1*1 (Pythagorus Theorem) then
18164
; y=sqr(1-x*x)                         - no need to multiply 1 by itself.
18165
; so, asn(a) = atn(x/y)
18166
; or more fully,
18167
; asn(a) = atn(x/sqr(1-x*x))
18168
 
18169
; Close but no cigar.
18170
 
18171
; While PRINT ATN (x/SQR (1-x*x)) gives the same results as PRINT ASN x,
18172
; it leads to division by zero when x is 1 or -1.
18173
; To overcome this, 1 is added to y giving half the required angle and the 
18174
; result is then doubled. 
18175
; That is PRINT ATN (x/(SQR (1-x*x) +1)) *2
18176
; A value higher than 1 gives the required error as attempting to find  the
18177
; square root of a negative number generates an error in Sinclair BASIC.
18178
 
18179
;; asn
18180
L3833:  RST     28H             ;; FP-CALC      x.
18181
        DB    $31             ;;duplicate     x, x.
18182
        DB    $31             ;;duplicate     x, x, x.
18183
        DB    $04             ;;multiply      x, x*x.
18184
        DB    $A1             ;;stk-one       x, x*x, 1.
18185
        DB    $03             ;;subtract      x, x*x-1.
18186
        DB    $1B             ;;negate        x, 1-x*x.
18187
        DB    $28             ;;sqr           x, sqr(1-x*x) = y
18188
        DB    $A1             ;;stk-one       x, y, 1.
18189
        DB    $0F             ;;addition      x, y+1.
18190
        DB    $05             ;;division      x/y+1.
18191
        DB    $24             ;;atn           a/2       (half the angle)
18192
        DB    $31             ;;duplicate     a/2, a/2.
18193
        DB    $0F             ;;addition      a.
18194
        DB    $38             ;;end-calc      a.
18195
 
18196
        RET                     ; return.
18197
 
18198
 
18199
;-------------------------
18200
; THE 'ARCCOS' FUNCTION
18201
;-------------------------
18202
; (Offset $23: 'acs')
18203
; the inverse cosine function with the result in radians.
18204
; Error A unless the argument is between -1 and +1.
18205
; Result in range 0 to pi.
18206
; Derived from asn above which is in turn derived from the preceding atn.
18207
; It could have been derived directly from atn using acs(x) = atn(sqr(1-x*x)/x).
18208
; However, as sine and cosine are horizontal translations of each other,
18209
; uses acs(x) = pi/2 - asn(x)
18210
 
18211
; e.g. the arccosine of a known x value will give the required angle b in 
18212
; radians.
18213
; We know, from above, how to calculate the angle a using asn(x). 
18214
; Since the three angles of any triangle add up to 180 degrees, or pi radians,
18215
; and the largest angle in this case is a right-angle (pi/2 radians), then
18216
; we can calculate angle b as pi/2 (both angles) minus asn(x) (angle a).
18217
; 
18218
;
18219
;           /|
18220
;        1 /b|
18221
;         /  |x
18222
;        /a  |
18223
;       /----|    
18224
;         y
18225
;
18226
 
18227
;; acs
18228
L3843:  RST     28H             ;; FP-CALC      x.
18229
        DB    $22             ;;asn           asn(x).
18230
        DB    $A3             ;;stk-pi/2      asn(x), pi/2.
18231
        DB    $03             ;;subtract      asn(x) - pi/2.
18232
        DB    $1B             ;;negate        pi/2 -asn(x)  =  acs(x).
18233
        DB    $38             ;;end-calc      acs(x).
18234
 
18235
        RET                     ; return.
18236
 
18237
 
18238
; --------------------------
18239
; THE 'SQUARE ROOT' FUNCTION
18240
; --------------------------
18241
; (Offset $28: 'sqr')
18242
; This routine is remarkable only in its brevity - 7 bytes.
18243
; It wasn't written here but in the ZX81 where the programmers had to squeeze
18244
; a bulky operating sytem into an 8K ROM. It simply calculates 
18245
; the square root by stacking the value .5 and continuing into the 'to-power'
18246
; routine. With more space available the much faster Newton-Raphson method
18247
; should have been used as on the Jupiter Ace.
18248
 
18249
;; sqr
18250
L384A:  RST     28H             ;; FP-CALC
18251
        DB    $31             ;;duplicate
18252
        DB    $30             ;;not
18253
        DB    $00             ;;jump-true
18254
        DB    $1E             ;;to L386C, LAST
18255
 
18256
        DB    $A2             ;;stk-half
18257
        DB    $38             ;;end-calc
18258
 
18259
 
18260
; ------------------------------
18261
; THE 'EXPONENTIATION' OPERATION
18262
; ------------------------------
18263
; (Offset $06: 'to-power')
18264
; This raises the first number X to the power of the second number Y.
18265
; As with the ZX80,
18266
; 0 ^ 0 = 1.
18267
; 0 ^ +n = 0.
18268
; 0 ^ -n = arithmetic overflow.
18269
;
18270
 
18271
;; to-power
18272
L3851:  RST     28H             ;; FP-CALC              X, Y.
18273
        DB    $01             ;;exchange              Y, X.
18274
        DB    $31             ;;duplicate             Y, X, X.
18275
        DB    $30             ;;not                   Y, X, (1/0).
18276
        DB    $00             ;;jump-true
18277
        DB    $07             ;;to L385D, XISO   if X is zero.
18278
 
18279
; else X is non-zero. Function 'ln' will catch a negative value of X.
18280
 
18281
        DB    $25             ;;ln                    Y, LN X.
18282
        DB    $04             ;;multiply              Y * LN X.
18283
        DB    $38             ;;end-calc
18284
 
18285
        JP      L36C4           ; jump back to EXP routine   ->
18286
 
18287
; ---
18288
 
18289
; these routines form the three simple results when the number is zero.
18290
; begin by deleting the known zero to leave Y the power factor.
18291
 
18292
;; XISO
18293
L385D:  DB    $02             ;;delete                Y.
18294
        DB    $31             ;;duplicate             Y, Y.
18295
        DB    $30             ;;not                   Y, (1/0).
18296
        DB    $00             ;;jump-true
18297
        DB    $09             ;;to L386A, ONE         if Y is zero.
18298
 
18299
        DB    $A0             ;;stk-zero              Y, 0.
18300
        DB    $01             ;;exchange              0, Y.
18301
        DB    $37             ;;greater-0             0, (1/0).
18302
        DB    $00             ;;jump-true             0.
18303
        DB    $06             ;;to L386C, LAST        if Y was any positive 
18304
                                ;;                      number.
18305
 
18306
; else force division by zero thereby raising an Arithmetic overflow error.
18307
; There are some one and two-byte alternatives but perhaps the most formal
18308
; might have been to use end-calc; rst 08; DB 05.
18309
 
18310
        DB    $A1             ;;stk-one               0, 1.
18311
        DB    $01             ;;exchange              1, 0.
18312
        DB    $05             ;;division              1/0        ouch!
18313
 
18314
; ---
18315
 
18316
;; ONE
18317
L386A:  DB    $02             ;;delete                .
18318
        DB    $A1             ;;stk-one               1.
18319
 
18320
;; LAST
18321
L386C:  DB    $38             ;;end-calc              last value is 1 or 0.
18322
 
18323
        RET                     ; return.               Whew!
18324
 
18325
;*********************************
18326
;** Spectrum 128 Patch Routines **
18327
;*********************************
18328
 
18329
; The new code added to the standard 48K Spectrum ROM is mainly devoted to the scanning and decoding of the keypad.
18330
; These routines occupy addresses 386E through to 3B3A. Addresses 3B3B through to 3C96 contain a variety of routines for the following purposes: displaying the new tokens 'PLAY' and 'SPECTRUM',
18331
; dealing with the keypad when using INKEY$, handling new 128 BASIC error messages, and producing the TV tuner display. Addresses 3BE1 to 3BFE and addresses 3C97 to 3CFF are unused and all contain 00.
18332
; Documented by Paul Farrow.
18333
 
18334
; --------------------------------
18335
; SCAN THE KEYPAD AND THE KEYBOARD
18336
; --------------------------------
18337
; This patch will attempt to scan the keypad if in 128K mode and will then scan the keyboard.
18338
 
18339
;; KEYS
18340
L386E:  PUSH    IX
18341
        BIT     4,(IY+$01)      ; [FLAGS] Test if in 128K mode
18342
        JR      Z,L3879         ; Z=in 48K mode
18343
 
18344
        CALL    L3A42           ; Attempt to scan the keypad
18345
 
18346
;; KEYS_CONT
18347
L3879:  CALL    L02BF           ; Scan the keyboard
18348
        POP     IX
18349
        RET
18350
 
18351
; ----------------------------------
18352
; READ THE STATE OF THE OUTPUT LINES
18353
; ----------------------------------
18354
; This routine returns the state of the four output lines (bits 0-3) in the lower four bits of L. The LSB of L corresponds to the output communication line to the keypad.
18355
; In this way the state of the other three outputs are maintained when the state of the LSB of L is changed and sent out to register 14 of the AY-3-8912.
18356
 
18357
;; READ_OUTPUTS
18358
L387F:  LD      C,$FD           ; FFFD = Address of the
18359
        LD      D,$FF           ; command register (register 7)
18360
        LD      E,$BF           ; BFFD = Address of the
18361
        LD      B,D             ; data register (register 14)
18362
        LD      A,$07
18363
        OUT     (C),A           ; Select command register
18364
        IN      H,(C)           ; Read its status
18365
        LD      A,$0E
18366
        OUT     (C),A           ; Select data register
18367
        IN      A,(C)           ; Read its status
18368
        OR      $F0             ; Mask off the input lines 
18369
        LD      L,A             ; L=state of output lines at the
18370
        RET                     ; keypad socket
18371
 
18372
; --------------------------
18373
; SET THE OUTPUT LINE, BIT 0
18374
; --------------------------
18375
; The output line to the keypad is set via the LSB of L.
18376
 
18377
;; SET_REG14
18378
L3896:  LD      B,D
18379
        LD      A,$0E
18380
        OUT     (C),A           ; Select the data register
18381
        LD      B,E
18382
        OUT     (C),L           ; Send L out to the data register
18383
        RET                     ; Set the output line
18384
 
18385
; ----------------------------------------
18386
; FETCH THE STATE OF THE INPUT LINE, BIT 5
18387
; ----------------------------------------
18388
; Return the state of the input line from the keypad in bit 5 of A.
18389
 
18390
;; GET_REG14
18391
L389F:  LD      B,D
18392
        LD      A,$0E
18393
        OUT     (C),A           ; Select the data register
18394
        IN      A,(C)           ; Read the input line
18395
        RET
18396
 
18397
; ------------------------------
18398
; SET THE OUTPUT LINE LOW, BIT 0
18399
; ------------------------------
18400
 
18401
;; RESET_LINE
18402
L38A7:  LD      A,L
18403
        AND     $FE             ; Reset bit 0 of L
18404
        LD      L,A
18405
        JR      L3896           ; Send out L to the data register
18406
 
18407
; -------------------------------
18408
; SET THE OUTPUT LINE HIGH, BIT 0
18409
; -------------------------------
18410
 
18411
;; SET_LINE
18412
L38AD:  LD      A,L
18413
        OR      $01             ; Set bit 0 of L
18414
        LD      L,A
18415
        JR      L3896           ; Send out L to the data register
18416
 
18417
; -------------------
18418
; MINOR DELAY ROUTINE
18419
; -------------------
18420
; Delay for (B*13)+5 T-States.
18421
 
18422
;; DELAY
18423
L38B3:  DJNZ    L38B3
18424
        RET
18425
 
18426
; -------------------
18427
; MAJOR DELAY ROUTINE
18428
; -------------------
18429
; Delay for (B*271)+5 T-states.
18430
 
18431
;; DELAY2
18432
L38B6:  PUSH    BC
18433
        LD      B,$10
18434
        CALL    L38B3           ; Inner delay of 135 T-States
18435
        POP     BC
18436
        DJNZ    L38B6
18437
        RET
18438
 
18439
; ------------------------------------
18440
; MONITOR FOR THE INPUT LINE TO GO LOW
18441
; ------------------------------------
18442
; Monitor the input line, bit 5, for up to (B*108)+5 T-states.
18443
 
18444
;; MON_B5_LO
18445
L38C0:  PUSH    BC
18446
        CALL    L389F           ; Read the state of the input line
18447
        POP     BC
18448
        AND     $20             ; Test bit 5, the input line
18449
        JR      Z,L38CB         ; Exit if input line found low
18450
        DJNZ    L38C0           ; Repeat until timeout expires
18451
 
18452
;; EXT_MON_LO
18453
L38CB:  RET
18454
 
18455
; -------------------------------------
18456
; MONITOR FOR THE INPUT LINE TO GO HIGH
18457
; -------------------------------------
18458
; Monitor the input line, bit 5, for up to (B*108)+5 T-states.
18459
 
18460
;; MON_B5_HI
18461
L38CC:  PUSH    BC
18462
        CALL    L389F           ; Read the state of the input line
18463
        POP     BC
18464
        AND     $20             ; Test bit 5, the input line
18465
        JR      NZ,L38D7        ; Exit if input line found low
18466
        DJNZ    L38CC           ; Repeat until timeout expires
18467
 
18468
;; EXT_MON_HI
18469
L38D7:  RET
18470
 
18471
; -------------------------
18472
; READ KEY PRESS STATUS BIT
18473
; -------------------------
18474
; This entry point is used to read in the status bit for a keypad row. If a key is being pressed in the current row then the bit read in will be a 1.
18475
 
18476
;; READ_STATUS
18477
L38D8:  CALL    L387F           ; Read the output lines
18478
        LD      B,$01           ; Read in one bit
18479
        JR      L38E4
18480
 
18481
; ----------------
18482
; READ IN A NIBBLE 
18483
; ----------------
18484
; This entry point is used to read in a nibble of data from the keypad. It is used for two functions. The first is to read in the poll nibble and the second is to read in a row of key press data.
18485
; For a nibble of key press data, a bit read in as 1 indicates that the corresponding key was pressed.
18486
 
18487
;; READ_NIBBLE
18488
L38DF:  CALL    L387F           ; Read the state of the output lines
18489
        LD      B,$04           ; Read in four bits
18490
 
18491
;; READ_BIT
18492
L38E4:  PUSH    BC
18493
        CALL    L389F           ; Read the input line from the keypad
18494
        POP     BC
18495
        AND     $20             ; This line should initially be high
18496
        JR      Z,L392D         ; Z=read in a 0, there must be an error
18497
 
18498
        XOR     A               ; The bits read in will be stored in register A
18499
 
18500
;; BIT_LOOP
18501
L38EE:  PUSH    BC              ; Preserve the loop count and any bits
18502
        PUSH    AF              ; read in so far
18503
        CALL    L38AD           ; Set the output line high
18504
 
18505
        LD      B,$A3           ; Monitor for 17609 T-states for the
18506
        CALL    L38C0           ; input line to go low
18507
        JR      NZ,L392B        ; NZ=the line did not go low
18508
 
18509
        CALL    L38A7           ; Set the output line low
18510
        JR      L3901           ; Insert a delay of 12 T-states
18511
 
18512
L38FF:  DB    $FF, $FF
18513
 
18514
;; BL_CONTINUE
18515
L3901:  LD      B,$2B           ; Delay for 564 T-states
18516
        CALL    L38B3
18517
        CALL    L389F           ; Read in the bit value 
18518
        BIT     5,A
18519
        JR      Z,L3911         ; Z=read in a 0
18520
 
18521
        POP     AF              ; Retrieve read in bits
18522
        SCF                     ; Set carry bit
18523
        JR      L3914
18524
 
18525
;; BL_READ_0
18526
L3911:  POP     AF              ; Retrieve read in bits
18527
        SCF
18528
        CCF                     ; Clear carry bit
18529
 
18530
;; BL_STORE
18531
L3914:  RRA                     ; Shift the carry bit into bit 0 of A
18532
        PUSH    AF              ; Save bits read in
18533
        CALL    L38AD           ; Set the output line high
18534
 
18535
        LD      B,$26           ; Delay for 499 T-states
18536
        CALL    L38B3
18537
 
18538
        CALL    L38A7           ; Set the output line low
18539
 
18540
        LD      B,$23           ; Delay for 460 T-states
18541
        CALL    L38B3
18542
 
18543
        POP     AF              ; Retrieve read in bits
18544
        POP     BC              ; Retrieve loop counter and repeat
18545
        DJNZ    L38EE           ; for all bits to read in
18546
        RET
18547
 
18548
; ----------
18549
; LINE ERROR
18550
; ----------
18551
; The input line was found at the wrong level. The output line is now set high which will eventually cause the keypad to abandon its transmissions.
18552
; The upper nibble of system variable FLAGS/ROW3 will be cleared to indicate that communications to the keypad is no longer in progress.
18553
 
18554
;; LINE_ERROR
18555
L392B:  POP     AF
18556
        POP     BC              ; Clear the stack
18557
 
18558
;; LINE_ERROR2
18559
L392D:  CALL    L38AD           ; Set the output line high
18560
 
18561
        XOR     A               ; Clear FLAGS nibble
18562
        LD      ($5B88),A       ; [FLAGS/ROW3]
18563
 
18564
        INC     A               ; Return zero flag reset
18565
        SCF
18566
        CCF                     ; Return carry flag reset
18567
        RET
18568
 
18569
; ---------------
18570
; POLL THE KEYPAD
18571
; ---------------
18572
; The Spectrum 128 polls the keypad by changing the state of the output line and monitoring for responses from the keypad on the input line.
18573
; Before a poll occurs, the poll counter must be decremented until it reaches zero. This counter causes a delay of three seconds before a communications attempt to the keypad is made.
18574
; The routine can exit at five different places and it is the state of the A register, the zero flag and the carry flag which indicates the cause of the exit. This is summarised below:
18575
;
18576
; A Register    Zero Flag       Carry Flag    Cause
18577
; 0             set             set           Communications already established
18578
; 0             set             reset         Nibble read in OK
18579
; 1             reset           reset         Nibble read in with an error or i/p line initially low
18580
; 1             reset           set           Poll counter has not yet reached zero
18581
;
18582
; The third bit of the nibble read in must be set for the poll to be subsequently accepted.
18583
 
18584
;; ATTEMPT_POLL
18585
L3938:  CALL    L387F           ; Read the output line states
18586
 
18587
        LD      A,($5B88)       ; [FLAGS/ROW3] Has communications already been
18588
        AND     $80             ; established with the keypad?
18589
        JR      NZ,L3999        ; NZ=yes, so skip the poll
18590
 
18591
        CALL    L389F           ; Read the input line
18592
        AND     $20             ; It should be high initially
18593
        JR      Z,L392D         ; Z=error, input line found low
18594
 
18595
        LD      A,($5B88)       ; [FLAGS/ROW3] Test if poll counter already zero thus
18596
        AND     A               ; indicating a previous comms error
18597
        JR      NZ,L395A        ; NZ=ready to poll the keypad
18598
 
18599
        INC     A               ; Indicate comms not established
18600
        LD      ($5B88),A       ; [FLAGS/ROW3]
18601
        LD      A,$4C           ; Reset the poll counter
18602
        LD      ($5B89),A       ; [ROW2/ROW1]
18603
        JR      L399C           ; Exit the routine
18604
 
18605
;;POLL_KEYPAD
18606
L395A:  LD      A,($5B89)       ; [ROW2/ROW1] Decrement the poll counter
18607
        DEC     A
18608
        LD      ($5B89),A       ; [ROW2/ROW1]
18609
        JR      NZ,L399C        ; Exit the routine if it is not yet zero
18610
 
18611
; The poll counter has reached zero so a poll of the keypad can now occur.
18612
 
18613
        XOR     A
18614
        LD      ($5B88),A       ; [FLAGS/ROW3] Indicate that a poll can occur
18615
        LD      ($5B89),A       ; [ROW2/ROW1]
18616
        LD      ($5B8A),A       ; [ROW4/ROW5] Clear all the row nibble stores
18617
 
18618
        CALL    L38A7           ; Set the output line low
18619
 
18620
        LD      B,$21           ; Wait up to 3569 T-States for the
18621
        CALL    L38C0           ; input line to go low
18622
        JR      NZ,L392D        ; NZ=line did not go low
18623
 
18624
        CALL    L38AD           ; Set the output line high
18625
 
18626
        LD      B,$24           ; Wait up to 3893 T-States for the
18627
        CALL    L38CC           ; input line to go high
18628
        JR      Z,L392D         ; NZ=line did not go high
18629
 
18630
        CALL    L38A7           ; Set the output line low
18631
 
18632
        LD      B,$0F
18633
        CALL    L38B6           ; Delay for 4070 T-States
18634
        CALL    L38DF           ; Read in a nibble of data
18635
        JR      NZ,L392D        ; NZ=error occurred when reading in nibble
18636
 
18637
        SET     7,A             ; Set bit 7
18638
        AND     $F0             ; Keep only the upper four bits
18639
                                ; (Bit 6 will be set if poll successful)
18640
        LD      ($5B88),A       ; [FLAGS/ROW3] Store the flags nibble
18641
        XOR     A
18642
        SRL     A               ; Exit: Zero flag set, Carry flag reset
18643
        RET
18644
 
18645
;; AP_SKIP_POLL
18646
L3999:  XOR     A               ; Communications already established
18647
        SCF                     ; Exit: Zero flag set, Carry flag set
18648
        RET
18649
 
18650
;; PK_EXIT
18651
L399C:  XOR     A               ; Poll counter not zero
18652
        INC     A
18653
        SCF                     ; Exit: Zero flag reset, Carry flag set
18654
        RET
18655
 
18656
; -----------------------
18657
; SCAN THE KEYPAD ROUTINE
18658
; -----------------------
18659
; If a successful poll of the keypad occurs then the five rows of keys are read in and a unique key code generated.
18660
 
18661
;; KEYPAD_SCAN
18662
L39A0:  CALL    L3938           ; Try to poll the keypad
18663
 
18664
        LD      A,($5B88)       ; [FLAGS/ROW3] Test the flags nibble
18665
        CPL
18666
        AND     $C0             ; Bits 6 and 7 must be set in FLAGS
18667
        RET     NZ              ; NZ=poll was not successful
18668
 
18669
; The poll was successful so now read in data for the five keypad rows.
18670
 
18671
        LD      IX,$5B8A        ; [ROW4/ROW5]
18672
        LD      B,$05           ; The five rows
18673
 
18674
;; KS_LOOP
18675
L39B0:  PUSH    BC              ; Save counter
18676
 
18677
        CALL    L38D8           ; Read the key press status bit
18678
        JP      NZ,L3A3A        ; NZ=error occurred
18679
 
18680
        BIT     7,A             ; Test the bit read in
18681
        JR      Z,L39DC         ; Z=no key pressed in this row
18682
 
18683
        CALL    L38DF           ; Read in the row's nibble of data
18684
        JR      NZ,L3A3A        ; NZ=error occurred
18685
 
18686
        POP     BC              ; Fetch the nibble loop counter
18687
        PUSH    BC
18688
        LD      C,A             ; Move the nibble read in to C
18689
        LD      A,(IX+$00)      ; Fetch the nibble store
18690
        BIT     0,B             ; Test if an upper or lower nibble
18691
        JR      Z,L39D6         ; Z=upper nibble
18692
 
18693
        SRL     C               ; Shift the nibble to the lower position
18694
        SRL     C
18695
        SRL     C
18696
        SRL     C
18697
        AND     $F0             ; Mask off the lower nibble of the
18698
        JR      L39D8           ; nibble store
18699
 
18700
;; KS_UPPER
18701
L39D6:  AND     $0F             ; Mask off the upper nibble of the nibble store
18702
 
18703
;; KS_STORE
18704
L39D8:  OR      C               ; Combine the existing and new
18705
        LD      (IX+$00),A      ; nibbles and store them
18706
 
18707
;; KS_NEXT
18708
L39DC:  POP     BC              ; Retrieve the row counter
18709
        BIT     0,B             ; Test if next nibble store is required
18710
        JR      NZ,L39E3        ; NZ=use same nibble store
18711
 
18712
        DEC     IX              ; Point to the next nibble store
18713
 
18714
;; KS_NEW
18715
L39E3:  DJNZ    L39B0           ; Repeat for the next keypad row
18716
 
18717
; All five rows have now been read so compose a unique code for the key pressed.
18718
 
18719
        LD      E,$80           ; Signal no key press found yet
18720
        LD      IX,$5B88        ; [FLAGS/ROW3]
18721
        LD      HL,$3A3F        ; Point to the key mask data
18722
        LD      B,$03           ; Scan three nibbles
18723
 
18724
;; GEN_LOOP
18725
L39F0:  LD      A,(IX+$00)      ; Fetch a pair of nibbles
18726
        AND     (HL)            ; This will mask off the FLAGS nibble and the SHIFT/0 key
18727
 
18728
        JR      Z,L3A17         ; Z=no key pressed in these nibbles
18729
 
18730
        BIT     7,E             ; Test if a key has already been found
18731
        JR      Z,L3A3C         ; Z=multiple keys pressed
18732
 
18733
        PUSH    BC              ; Save the loop counter
18734
        PUSH    AF              ; Save the byte of key bit data
18735
        LD      A,B             ; Move loop counter to A
18736
        JR      L3A01           ; A delay of 12 T-States
18737
 
18738
L39FF:  DB    $FF, $FF        ; Unused locations
18739
 
18740
;; GEN_CONT
18741
L3A01:  DEC     A               ; These lines of code generate base
18742
        SLA     A               ; values of 7, 15 and 23 for the three
18743
        SLA     A               ; nibble stores 5B88, 5B89 & 5B8A.
18744
        SLA     A
18745
        OR      $07
18746
        LD      B,A             ; B=(loop counter-1)*8+7
18747
        POP     AF              ; Fetch the byte of key press data
18748
 
18749
;; GEN_BIT
18750
L3A0C:  SLA     A               ; Shift until a set key bit drops into the
18751
        JP      C,L3A13         ; carry flag
18752
 
18753
        DJNZ    L3A0C           ; Decrement B for each 'unsuccessful' shift of the A register
18754
 
18755
;; GEN_FOUND
18756
L3A13:  LD      E,B             ; E=a unique number for the key pressed, between 1 - 19 except 2 & 3
18757
 
18758
        POP     BC              ; As a result shifting the set key bit
18759
                                ; into the carry flag, the A register will
18760
                                ; hold 00 if only one key was pressed
18761
        JR      NZ,L3A3C        ; NZ=multiple keys pressed
18762
 
18763
;; GEN_NEXT
18764
L3A17:  INC     IX              ; Point to the next nibble store
18765
        INC     HL              ; Point to the corresponding mask data
18766
        DJNZ    L39F0           ; Repeat for all three 'nibble' bytes
18767
 
18768
        BIT     7,E             ; Test if any keys were pressed
18769
        JR      NZ,L3A27        ; NZ=no keys were pressed
18770
 
18771
        LD      A,E             ; Copy the key code
18772
        AND     $FC             ; Test for the '.' key (E=1)
18773
        JR      Z,L3A27         ; Z='.' key pressed
18774
 
18775
        DEC     E
18776
        DEC     E               ; Key code in range 2 - 17
18777
 
18778
; The E register now holds a unique key code value between 1 and 17.
18779
 
18780
;; GEN_POINT
18781
L3A27:  LD      A,($5B8A)       ; [ROW4/ROW5] Test if the SHIFT key was pressed
18782
        AND     $08
18783
        JR      Z,L3A34         ; Z=the SHIFT key was not pressed
18784
 
18785
; The SHIFT key was pressed or no key was pressed.
18786
 
18787
        LD      A,E             ; Fetch the key code
18788
        AND     $7F             ; Mask off 'no key pressed' bit
18789
        ADD     A,$12           ; Add on a shift offset of 12
18790
        LD      E,A
18791
 
18792
; Add a base offset of 5A to all key codes. Note that no key press will result in a key code of DA. This is the only code with bit 7 set and so will be detected later.
18793
 
18794
;; GEN_NOSHIFT
18795
L3A34:  LD      A,E
18796
        ADD     A,$5A           ; Add a base offset of 5A
18797
        LD      E,A             ; Return key codes in range 5B - 7D
18798
        XOR     A
18799
        RET                     ; Exit: Zero flag set, key found OK
18800
 
18801
; These two lines belong with the loop above to read in the five keypad rows and are jumped to when an error occurs during reading in a nibble of data.
18802
 
18803
;; KS_ERROR
18804
L3A3A:  POP     BC              ; Clear the stack and exit
18805
        RET                     ; Exit: Zero flag reset
18806
 
18807
;; GEN_INVALID
18808
L3A3C:  XOR     A               ; Exit: Zero flag reset indicating an
18809
        INC     A               ; invalid key press
18810
        RET
18811
 
18812
; ----------------
18813
; KEYPAD MASK DATA
18814
; ----------------
18815
 
18816
;; KEY_MASKS
18817
L3A3F:  DB    $0F, $FF, $F2   ; Key mask data
18818
 
18819
; ---------------
18820
; READ THE KEYPAD
18821
; ---------------
18822
; This routine reads the keypad and handles key repeat and decoding. The bulk of the key repeat code is very similar to that used in the equivalent keyboard routine and works are follows.
18823
; A double system of KSTATE system variables (KSTATE0 - KSTATE3 and KSTATE4 - KSTATE7) is used to allow the detection of one key while in the repeat period of the previous key.
18824
; In this way, a 'spike' from another key will not stop the previous key from repeating. For a new key to be acknowledged, it must be held down for at least 1/5th of a second, i.e. ten calls to KEYPAD.
18825
; The KSTATE system variables store the following data:
18826
;
18827
;       KSTATE0/4       Un-decoded Key Value (00-27 for keyboard, 5B-7D for keypad, FF for no key)
18828
;       KSTATE1/5       10 Call Counter
18829
;       KSTATE2/6       Repeat Delay
18830
;       KSTATE3/7       Decoded Key Value
18831
;
18832
; The code returned is then stored in system variable LAST_K (5C08) and a new key signalled by setting bit 5 of FLAGS (5C3B).
18833
;
18834
; If the Spectrum 128 were to operate identically to the standard 48K Spectrum when in 48K mode, it would have to spend zero time in reading the keypad.
18835
; As this is not possible, the loading on the CPU is reduced by scanning the keypad upon every other interrupt. A '10 Call Counter' is then used to ensure that a key is held down for at least 1/5th of a second
18836
; before it is registered. Note that this is twice as long as for keyboard key presses and so the keypad key repeat delay is halved.
18837
;
18838
; At every other interrupt the keypad scanning routine is skipped. The net result of the routine is simply to decrement both '10 Call Counters', if appropriate. By loading the E register with 80 ensures that
18839
; the call to KP_TEST will reject the key code and cause a return. A test for keyboard key codes prevents the Call Counter decrements affecting a keyboard key press. It would have been more efficient to execute
18840
; a return upon every other call to KEYPAD and then to have used a '5 Call Counter' just as the keyboard routine does.
18841
;
18842
; A side effect of both the keyboard and keypad using the same KSTATE system variables is that if a key is held down on the keypad and then a key is held down on the keyboard, both keys will be monitored and
18843
; repeated alternatively, but with a reduced repeat delay. This delay is between the keypad key repeat delay and the keyboard key repeat delay. This occurs because both the keypad and keyboard routines will
18844
; decrement the KSTATE system variable Call Counters. The keypad routine 'knows' of the existence of keyboard key codes but the reverse is not true.
18845
 
18846
;; KEYPAD
18847
L3A42:  LD      E,$80           ; Signal no key pressed
18848
        LD      A,($5C78)       ; [FRAMES]
18849
        AND     $01             ; Scan the keypad every other
18850
        JR      NZ,L3A4F        ; interrupt
18851
 
18852
        CALL    L39A0
18853
        RET     NZ              ; NZ=no valid key pressed
18854
 
18855
;; KP_CHECK
18856
L3A4F:  LD HL,$5C00             ; [KSTATE0] Test the first KSTATE variable
18857
 
18858
;; KP_LOOP
18859
L3A52:  BIT     7,(HL)          ; Is the set free?
18860
        JR      NZ,L3A62        ; NZ=yes
18861
 
18862
        LD      A,(HL)          ; Fetch the un-decoded key value
18863
        CP      $5B             ; Is it a keyboard code?
18864
        JR      C,L3A62         ; C=yes, so do not decrement counter
18865
 
18866
        INC     HL
18867
        DEC     (HL)            ; Decrement the 10 Call Counter
18868
        DEC     HL
18869
        JR      NZ,L3A62        ; If the counter reaches zero, then
18870
                                ; signal the set is free
18871
        LD      (HL),$FF
18872
 
18873
;; KP_CH_SET
18874
L3A62:  LD      A,L             ; Jump back and test the second set if
18875
        LD      HL,$5C04        ; [KSTATE4] not yet considered
18876
        CP      L
18877
        JR      NZ,L3A52
18878
 
18879
        CALL    L3AAE           ; Test for valid key combinations and
18880
        RET     NZ              ; return if invalid
18881
 
18882
        LD      A,E             ; Test if the key in the first set is being
18883
        LD      HL,$5C00        ; [KSTATE0] repeated
18884
        CP      (HL)
18885
        JR      Z,L3A9E         ; Jump if being repeated
18886
 
18887
        EX      DE,HL           ; Save the address of KSTATE0
18888
        LD      HL,$5C04        ; [KSTATE4] Test if the key in the second set is
18889
        CP      (HL)            ; being repeated
18890
        JR      Z,L3A9E         ; Jump if being repeated
18891
 
18892
; A new key will not be accepted unless one of the KSTATE sets is free.
18893
 
18894
        BIT     7,(HL)          ; Test if the second set is free
18895
        JR      NZ,L3A83        ; Jump if set is free
18896
 
18897
        EX      DE,HL
18898
        BIT     7,(HL)          ; Test if the first set is free
18899
        RET     Z               ; Return if no set is free
18900
 
18901
;; KP_NEW
18902
L3A83:  LD      E,A             ; Pass the key code to the E register
18903
        LD      (HL),A          ; and to KSTATE0/4
18904
        INC     HL
18905
        LD      (HL),$0A        ; Set the '10 Call Counter' to 10
18906
        INC     HL
18907
 
18908
        LD      A,($5C09)       ; [REPDEL] Fetch the initial repeat delay
18909
        SRL     A               ; Divide delay by two
18910
        LD      (HL),A          ; Store the repeat delay
18911
        INC     HL
18912
 
18913
        CALL    L3AD7           ; Decode the keypad key code
18914
        LD      (HL),E          ; and store it in KSTATE3/7
18915
 
18916
; This section is common for both new keys and repeated keys.
18917
 
18918
;; KP_END
18919
L3A94:  LD      A,E
18920
        LD      ($5C08),A       ; [LAST_K] Store the key value in LAST_K
18921
        LD      HL,$5C3B        ; FLAGS
18922
        SET     5,(HL)          ; Signal a new key pressed
18923
        RET
18924
 
18925
; -------------------------
18926
; THE KEY REPEAT SUBROUTINE
18927
; -------------------------
18928
 
18929
;; KP_REPEAT
18930
L3A9E:  INC     HL
18931
        LD      (HL),$0A        ; Reset the '10 Call Counter' to 10
18932
        INC     HL
18933
        DEC     (HL)            ; Decrement the repeat delay
18934
        RET     NZ              ; Return if not zero
18935
 
18936
        LD      A,($5C0A)       ; [REPPER] The subsequent repeat delay is
18937
        SRL     A               ; divided by two and stored
18938
        LD      (HL),A
18939
        INC     HL
18940
        LD      E,(HL)          ; The key repeating is fetched
18941
        JR      L3A94           ; and then returned in LAST_K
18942
 
18943
; ----------------------------------------
18944
; THE TEST FOR A VALID KEY CODE SUBROUTINE
18945
; ----------------------------------------
18946
; The zero flag is returned set if the key code is valid. No key press, SHIFT only or invalid shifted key presses return the zero flag reset.
18947
 
18948
;; KP_TEST
18949
L3AAE:  LD      A,E
18950
        LD      HL,$5B66        ; FLAGS3 Test if in BASIC or EDIT mode
18951
        BIT     0,(HL)
18952
        JR      Z,L3ABC         ; Z=EDIT mode
18953
 
18954
; Test key codes when in BASIC/CALCULATOR mode
18955
 
18956
        CP      $6D             ; Test for shifted keys
18957
        JR      NC,L3AD4        ; and signal an error if found
18958
 
18959
;; KPT_OK
18960
L3ABA:  XOR     A               ; Signal valid key code
18961
        RET                     ; Exit: Zero flag set
18962
 
18963
; Test key codes when in EDIT/MENU mode.
18964
 
18965
;; KPT_EDIT
18966
L3ABC:  CP      $80             ; Test for no key press
18967
        JR      NC,L3AD4        ; NC=no key press
18968
 
18969
        CP      $6C             ; Test for SHIFT on its own
18970
        JR      NZ,L3ABA        ; NZ=valid key code
18971
 
18972
L3AC4:  DB    $00, $00, $00   ; Delay for 64 T-States
18973
        DB    $00, $00, $00
18974
        DB    $00, $00, $00
18975
        DB    $00, $00, $00
18976
        DB    $00, $00, $00
18977
        DB    $00
18978
 
18979
;; KPT_INVALID
18980
L3AD4:  XOR     A               ; Signal invalid key code
18981
        INC     A
18982
        RET                     ; Exit: Zero flag reset
18983
 
18984
; ---------------------------
18985
; THE KEY DECODING SUBROUTINE
18986
; ---------------------------
18987
 
18988
;; KP_DECODE
18989
L3AD7:  PUSH    HL              ; Save the KSTATE pointer
18990
        LD      A,E
18991
        SUB     $5B             ; Reduce the key code range to
18992
        LD      D,$00           ; 00 - 22 and transfer to DE
18993
        LD      E,A
18994
 
18995
        LD      HL,$5B66        ; FLAGS3 Test if in EDIT or BASIC mode
18996
        BIT     0,(HL)
18997
        JR      Z,L3AEA         ; Z=EDIT/MENU mode
18998
 
18999
; Use Table 1 when in CALCULATOR/BASIC mode.
19000
 
19001
        LD      HL,L3B13
19002
        JR      L3B0F           ; Look up the key value
19003
 
19004
; Deal with EDIT/MENU mode.
19005
 
19006
;; KPD_EDIT
19007
L3AEA:  LD      HL,L3B25        ; Use Table 4 for unshifted key
19008
        CP      $11             ; presses
19009
        JR      C,L3B0F
19010
 
19011
; Deal with shifted keys in EDIT/MENU mode.
19012
 
19013
; Use Table 3 with SHIFT 1 (delete to beginning of line), SHIFT 2 (delete to end of line), SHIFT 3 (SHIFT TOGGLE). Note that although SHIFT TOGGLE produces a unique valid code,
19014
; it actually performs no function when editing a BASIC program.
19015
 
19016
        LD      HL,L3B21
19017
        CP      $15             ; Test for SHIFT 1
19018
        JR      Z,L3B0F
19019
 
19020
        CP      $16             ; Test for SHIFT 2
19021
        JR      Z,L3B0F
19022
 
19023
        JR      L3B01           ; Delay for 12 T-States
19024
 
19025
L3AFE:  DB    $00, $FF, $FF   ; Unused locations
19026
 
19027
;; KPD_CONT
19028
L3B01:  CP      $17             ; Test for SHIFT 3
19029
        JR      Z,L3B0F
19030
 
19031
; Use Table 2 with SHIFT 4 (delete to beginning of word) and SHIFT 5 (delete to end of word).
19032
 
19033
        LD      HL,L3B18
19034
        CP      $21             ; Test for SHIFT 4 and above
19035
        JR      NC,L3B0F
19036
 
19037
;Use Table 1 for all other shifted key presses.
19038
 
19039
        LD      HL,L3B13
19040
 
19041
;; KPD_EXIT
19042
L3B0F:  ADD     HL,DE           ; Look up the key value
19043
        LD      E,(HL)
19044
        POP     HL              ; Retrieve the KSTATE address
19045
        RET
19046
 
19047
; --------------------------------
19048
; THE KEYPAD DECODE LOOK-UP TABLES
19049
; --------------------------------
19050
 
19051
;; KPD_TABLE1
19052
L3B13:  DB    $2E, $0D, $33   ; '.', ENTER, 3
19053
        DB    $32, $31        ; 2, 1
19054
 
19055
;; KPD_TABLE2
19056
L3B18:  DB    $29, $28, $2A   ; ), (, *
19057
        DB    $2F, $2D, $39   ; /, - , 9
19058
        DB    $38, $37, $2B   ; 8, 7, +
19059
 
19060
;; KPD_TABLE3
19061
L3B21:  DB    $36, $35, $34   ; 6, 5, 4
19062
        DB    $30             ; 0
19063
 
19064
;; KPD_TABLE4
19065
L3B25:  DB    $A5, $0D, $A6   ; Bottom, ENTER, Top
19066
        DB    $A7, $A8, $A9   ; End of line, Start of line, TOGGLE
19067
        DB    $AA, $0B, $0C   ; DEL right, Up, DEL
19068
        DB    $07, $09, $0A   ; CMND, Right, Down
19069
        DB    $08, $AC, $AD   ; Left, Down ten, Up ten
19070
        DB    $AE, $AF        ; End word, Beginning of word
19071
        DB    $B0, $B1, $B2   ; DEL to end of line, DEL to start of line, SHIFT TOGGLE
19072
        DB    $B3, $B4        ; DEL to end of word, DEL to beginning of word
19073
 
19074
; -----------------------------
19075
; PRINT NEW ERROR MESSAGE PATCH
19076
; -----------------------------
19077
 
19078
L3B3B:  BIT     4,(IY+$01)      ; FLAGS 3 - In 128K mode?
19079
        JR      NZ,L3B46        ; NZ=128K mode
19080
 
19081
; In 48K mode
19082
 
19083
        XOR     A               ; Replicate code from standard ROM that the patch over-wrote
19084
        LD      DE,$1536
19085
        RET    
19086
 
19087
; In 128K mode
19088
 
19089
L3B46:  LD      HL,$010F        ; Vector table entry in Editor ROM -> JP $03A2
19090
 
19091
; Return to Editor ROM at address in HL
19092
 
19093
L3B49:  EX      (SP),HL         ; Change the return address
19094
        JP      $5B00           ; Page Editor ROM and return to the address on the stack
19095
 
19096
; -------------------------------------
19097
; STATEMENT INTERPRETATION RETURN PATCH
19098
; -------------------------------------
19099
 
19100
L3B4D:  BIT     4,(IY+$01)      ; In 128K mode?
19101
        JR      NZ,L3B58        ; NZ=128K mode
19102
 
19103
; In 48K mode
19104
 
19105
        BIT     7,(IY+$0A)      ; replicate code from standard ROM that the patch over-wrote
19106
        RET    
19107
 
19108
; In 128K mode
19109
 
19110
L3B58:  LD      HL,$0112        ; Handle in Editor ROM by jumping to Vector table entry in Editor ROM -> JP #182A
19111
        JR      L3B49
19112
 
19113
; --------------------------
19114
; GO TO NEXT STATEMENT PATCH
19115
; --------------------------
19116
 
19117
L3B5D:  BIT     4,(IY+$01)      ; In 128K mode?
19118
        JR      NZ,L3B67        ; NZ=128K mode
19119
 
19120
; In 48K mode
19121
 
19122
        RST     18H             ; replicate code from standard ROM that the patch over-wrote
19123
        CP      $0D
19124
        RET    
19125
 
19126
; In 128K mode
19127
 
19128
L3B67:  LD      HL,$0115        ; Handle in Editor ROM by jumping to Vector table entry in Editor ROM -> JP #18A8
19129
        JR      L3B49
19130
 
19131
; --------------------------------------
19132
; INKEY$ ROUTINE TO DEAL WITH THE KEYPAD
19133
; --------------------------------------
19134
 
19135
;; KEYSCAN2
19136
L3B6C:  CALL    L028E           ; KEYSCAN Scan the keyboard
19137
        LD      C,$00
19138
        JR      NZ,L3B80        ; NZ=multiple keys
19139
 
19140
        CALL    L031E           ; K_TEST
19141
        JR      NC,L3B80        ; NC=shift only or no key
19142
 
19143
        DEC     D
19144
        LD      E,A
19145
        CALL    L0333           ; K_DECODE
19146
        JP      L2657           ; S_CONT Get string and continue scanning
19147
 
19148
;; KPI_SCAN
19149
L3B80:  BIT     4,(IY+$01)      ; 128K mode?
19150
        JP      Z,L2660         ; S_IK$_STK Z=no, stack keyboard code
19151
 
19152
        DI                      ; Disable interrupts whilst scanning
19153
        CALL    L39A0           ; the keypad
19154
        EI
19155
        JR      NZ,L3B9A        ; NZ=multiple keys
19156
 
19157
        CALL    L3AAE           ; Test the keypad
19158
        JR      NZ,L3B9A        ; NZ=no key, shift only or invalid combination
19159
 
19160
        CALL    L3AD7           ; Form the key code
19161
        LD      A,E
19162
        JP      L2657           ; S_CONT Get string and continue scanning
19163
 
19164
;; KPI_INVALID
19165
L3B9A:  LD      C,$00           ; Signal no key, i.e. length=0
19166
        JP      L2660           ; S_IK$_STK
19167
 
19168
; ---------------------
19169
; PRINT TOKEN/UDG PATCH
19170
; ---------------------
19171
 
19172
L3B9F:  CP      $A3             ; SPECTRUM (T)
19173
        JR      Z,L3BAF
19174
 
19175
        CP      $A4             ; PLAY (U)
19176
        JR      Z,L3BAF
19177
 
19178
; In 48K mode here
19179
 
19180
L3BA7:  SUB     $A5             ; Check as per original ROM
19181
        JP      NC,$0B5F
19182
 
19183
        JP      $0B56           ; Rejoin original ROM routine
19184
 
19185
L3BAF:  BIT     4,(IY+$01)      ; FLAGS3 - Bit 4=1 if in 128K mode
19186
        JR      Z,L3BA7         ; Rejoin code for when in 48K mode
19187
 
19188
; In 128K mode here
19189
 
19190
        LD      DE,L3BC9
19191
        PUSH    DE              ; Stack return address
19192
 
19193
        SUB     $A3             ; Check whether the SPECTRUM token
19194
 
19195
        LD      DE,L3BD2        ; SPECTRUM token
19196
        JR      Z,L3BC3
19197
 
19198
        LD      DE,L3BDA        ; PLAY token
19199
 
19200
L3BC3:  LD      A,$04           ; Signal not RND, INKEY$ or PI so that a trailing space is printed
19201
        PUSH    AF
19202
        JP      L0C17           ; Rejoin printing routine PO-TABLE+3
19203
 
19204
; Return address from above
19205
 
19206
L3BC9:  SCF                     ; Return as if no trailing space
19207
 
19208
        BIT     1,(IY+$01)      ; Test if printer is in use
19209
        RET     NZ              ; NZ=printer in use
19210
 
19211
        JP      $0B03           ; PO-FETCH - Return via Position Fetch routine
19212
 
19213
L3BD2           DC "SPECTRUM"   ;DEFM    "SPECTRU"       ; SPECTRUM token
19214
                                ;DB    'M'+$80
19215
 
19216
L3BDA           DC "PLAY"       ;DEFM    "PLA"           ; PLAY token
19217
                                ;DB    'Y'+$80
19218
 
19219
;; KP_SCAN2
19220
L3BDE:  JP      L3C01           ; This is not called from either ROM. It can be used to scan the keypad.
19221
 
19222
;===============================
585 savelij 19223
PRINTER_INITER  RST 8
19224
                DB _AY_PRN_INIT
550 savelij 19225
                RET
384 savelij 19226
 
585 savelij 19227
PRN_TOKEN       RST 8
19228
                DB _AY_PRN_TOKEN
403 savelij 19229
                RET
19230
 
384 savelij 19231
                DUPL 0X3BFF-$,0
19232
                DW 0XFFFF
19233
;===============================
19234
 
19235
;; KP_SCAN
19236
L3C01:  JP      L39A0           ; This was to be called via the vector table in the EDITOR ROM but due to a programming error it never gets called.
19237
 
19238
; -----------------------
19239
; TV TUNER VECTOR ENTRIES
19240
; -----------------------
19241
 
19242
L3C04:  JP      L3C10
19243
L3C07:  JP      L3C10
19244
L3C0A:  JP      L3C10
19245
L3C0D:  JP      L3C10
19246
 
19247
; ----------------
19248
; TV TUNER ROUTINE
19249
; ----------------
19250
; This routine generates a display showing all possible colours and emitting a continuous cycle of a 440Hz tone for 1 second followed by silence for 1 second.
19251
; Its purpose is to ease the tuning in of TV sets to the Spectrum 128's RF signal. The display consists of vertical stripes of width four character squares showing each of the eight colours
19252
; available at both their normal and bright intensities. The display begins with white on the left progressing up to black on the right. With in each colour stripe in the first eight rows is
19253
; shown the year '1986' in varying ink colours. This leads to a display that shows all possible ink colours on all possible paper colours.
19254
 
19255
;; TV_TUNER
19256
L3C10:  LD      A,$7F           ; Test for the BREAK key
19257
        IN      A,($FE)
19258
        RRA    
19259
        RET     C               ; C=SPACE not pressed
19260
 
19261
        LD      A,$FE
19262
        IN      A,($FE)
19263
        RRA    
19264
        RET     C               ; C=SPACE not pressed
19265
 
19266
        LD      A,$07
19267
        OUT     ($FE),A         ; Set the border to white
19268
 
19269
        LD      A,$02           ; Open channel 2 (main screen)
19270
        CALL    $1601
19271
 
19272
        XOR     A
19273
        LD      ($5C3C),A       ; [TV_FLAG] Signal using main screen
19274
 
19275
        LD      A,$16           ; Print character 'AT'
19276
        RST     10H
19277
 
19278
        XOR     A               ; Print character '0'
19279
        RST     10H
19280
 
19281
        XOR     A               ; Print character '0'
19282
        RST     10H
19283
 
19284
        LD      E,$08           ; Number of characters per colour
19285
        LD      B,E             ; Paper counter + 1
19286
        LD      D,B             ; Ink counter + 1
19287
 
19288
;; TVT_ROW
19289
L3C34:  LD      A,B             ; Calculate the paper colour
19290
        DEC     A               ; Bits 3-5 of each screen attribute
19291
                DB 0XCB
19292
                RLA               ; holds the paper colour; bits 0-2
19293
                DB 0XCB
19294
                RLA               ; the ink colour
19295
                DB 0XCB
19296
                RLA
19297
        ADD     A,D             ; Add the ink colour
19298
        DEC     A
19299
        LD      ($5C8F),A       ; [ATTR_T] Store as temporary attribute value
19300
 
19301
        LD      HL,L3C8F        ; TVT_DATA Point to the 'year' data
19302
        LD      C,E             ; Get number of characters to print
19303
 
19304
;; TVT_YEAR
19305
L3C45:  LD      A,(HL)          ; Fetch a character from the data
19306
        RST     10H             ; Print it
19307
        INC     HL
19308
        DEC     C
19309
        JR      NZ,L3C45        ; Repeat for the 8 characters
19310
 
19311
        DJNZ    L3C34           ; Repeat for all colours in this row
19312
 
19313
        LD      B,E             ; Reset paper colour
19314
        DEC     D               ; Next ink colour
19315
        JR      NZ,L3C34        ; Produce next row with new ink colour
19316
 
19317
        LD      HL,$4800        ; Point to 2nd third of display file
19318
        LD      D,H
19319
        LD      E,L
19320
        INC     DE              ; Point to the next display cell
19321
        XOR     A
19322
        LD      (HL),A          ; Clear first display cell
19323
        LD      BC,$0FFF
19324
        LDIR                    ; Clear lower 2 thirds of display file
19325
 
19326
        EX      DE,HL           ; HL points to start of attributes file
19327
        LD      DE,$5900        ; Point to 2nd third of attributes file
19328
        LD      BC,$0200
19329
        LDIR                    ; Copy screen attributes
19330
 
19331
; Now that the display has been constructed, produce a continuous cycle of a 440Hz tone for 1 second followed by a period of silence for 1 second (actually 962ms).
19332
 
19333
        DI                      ; Disable interrupts so that a pure tone can be generated
19334
 
19335
;; TVT_TONE
19336
L3C68:  LD      DE,$0370        ; DE=twice the tone frequency in Hz
19337
        LD      L,$07           ; Border colour of white
19338
 
19339
;; TVT_DURATION
19340
L3C6D:  LD      BC,$0099        ; Delay for 950.4us
19341
 
19342
;; TVT_PERIOD
19343
L3C70:  DEC     BC
19344
        LD      A,B
19345
        OR      C
19346
        JR      NZ,L3C70
19347
 
19348
        LD      A,L
19349
        XOR     $10              ; Toggle the speaker output whilst
19350
        LD      L,A              ; preserving the border colour
19351
        OUT     ($FE),A
19352
 
19353
        DEC     DE               ; Generate the tone for 1 second
19354
        LD      A,D
19355
        OR      E
19356
        JR      NZ,L3C6D
19357
 
19358
; At this point the speaker is turned off, so delay for 1 second.
19359
 
19360
        LD      BC,$0000         ; Delay for 480.4us
19361
 
19362
;; TVT_DELAY1
19363
L3C83:  DEC     BC
19364
        LD      A,B
19365
        OR      C
19366
        JR      NZ,L3C83
19367
 
19368
;; TVT_DELAY2
19369
L3C88:  DEC     BC               ; Delay for 480.4us
19370
        LD      A,B
19371
        OR      C
19372
        JR      NZ,L3C88
19373
 
19374
        JR      L3C68            ; Repeat the tone cycle
19375
 
19376
;; TVT_DATA
19377
L3C8F:  DB    $13, $00         ; Bright, off
19378
        DB    $31, $39         ; '1', '9'
19379
        DB    $13, $01         ; Bright, on
19380
        DB    $38, $36         ; '8', '6'
19381
 
678 savelij 19382
                include rst8.a80
384 savelij 19383
 
19384
; ------
19385
; UNUSED
19386
; ------
19387
 
678 savelij 19388
                DUPL 0X3D00-$,0XFF
384 savelij 19389
 
19390
; -------------------------------
19391
; THE 'ZX SPECTRUM CHARACTER SET'
19392
; -------------------------------
19393
 
19394
;; char-set
19395
 
19396
; $20 - Character: ' '          CHR$(32)
19397
 
19398
CHARS           binclude shr_3d00.bin