OpenCores
URL https://opencores.org/ocsvn/a-z80/a-z80/trunk

Subversion Repositories a-z80

[/] [a-z80/] [trunk/] [host/] [zxspectrum_de1/] [rom/] [zxspectrum_rom.asm] - Blame information for rev 8

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 8 gdevic
;************************************************************************
2
;** An Assembly File Listing to generate a 16K ROM for the ZX Spectrum **
3
;************************************************************************
4
;
5
; 11-10-2014:
6
; This version has been updated to correctly handle the NMI jump.
7
;
8
; -------------------------
9
; Last updated: 13-DEC-2004
10
; -------------------------
11
 
12
; TASM cross-assembler directives.
13
; ( comment out, perhaps, for other assemblers - see Notes at end.)
14
 
15
#define DEFB .BYTE
16
#define DEFW .WORD
17
#define DEFM .TEXT
18
#define ORG  .ORG
19
#define EQU  .EQU
20
#define equ  .EQU
21
 
22
;   It is always a good idea to anchor, using ORGs, important sections such as
23
;   the character bitmaps so that they don't move as code is added and removed.
24
 
25
;   Generally most approaches try to maintain main entry points as they are
26
;   often used by third-party software.
27
 
28
ORG 0000
29
 
30
;*****************************************
31
;** Part 1. RESTART ROUTINES AND TABLES **
32
;*****************************************
33
 
34
; -----------
35
; THE 'START'
36
; -----------
37
;   At switch on, the Z80 chip is in Interrupt Mode 0.
38
;   The Spectrum uses Interrupt Mode 1.
39
;   This location can also be 'called' to reset the machine.
40
;   Typically with PRINT USR 0.
41
 
42
;; START
43
L0000:  DI                      ; Disable Interrupts.
44
        XOR     A               ; Signal coming from START.
45
        LD      DE,$FFFF        ; Set pointer to top of possible physical RAM.
46
        JP      L11CB           ; Jump forward to common code at START-NEW.
47
 
48
; -------------------
49
; THE 'ERROR' RESTART
50
; -------------------
51
;   The error pointer is made to point to the position of the error to enable
52
;   the editor to highlight the error position if it occurred during syntax
53
;   checking.  It is used at 37 places in the program.  An instruction fetch
54
;   on address $0008 may page in a peripheral ROM such as the Sinclair
55
;   Interface 1 or Disciple Disk Interface.  This was not an original design
56
;   concept and not all errors pass through here.
57
 
58
;; ERROR-1
59
L0008:  LD      HL,($5C5D)      ; Fetch the character address from CH_ADD.
60
        LD      ($5C5F),HL      ; Copy it to the error pointer X_PTR.
61
        JR      L0053           ; Forward to continue at ERROR-2.
62
 
63
; -----------------------------
64
; THE 'PRINT CHARACTER' RESTART
65
; -----------------------------
66
;   The A register holds the code of the character that is to be sent to
67
;   the output stream of the current channel.  The alternate register set is
68
;   used to output a character in the A register so there is no need to
69
;   preserve any of the current main registers (HL, DE, BC).
70
;   This restart is used 21 times.
71
 
72
;; PRINT-A
73
L0010:  JP      L15F2           ; Jump forward to continue at PRINT-A-2.
74
 
75
; ---
76
 
77
        DEFB    $FF, $FF, $FF   ; Five unused locations.
78
        DEFB    $FF, $FF        ;
79
 
80
; -------------------------------
81
; THE 'COLLECT CHARACTER' RESTART
82
; -------------------------------
83
;   The contents of the location currently addressed by CH_ADD are fetched.
84
;   A return is made if the value represents a character that has
85
;   relevance to the BASIC parser. Otherwise CH_ADD is incremented and the
86
;   tests repeated. CH_ADD will be addressing somewhere -
87
;   1) in the BASIC program area during line execution.
88
;   2) in workspace if evaluating, for example, a string expression.
89
;   3) in the edit buffer if parsing a direct command or a new BASIC line.
90
;   4) in workspace if accepting input but not that from INPUT LINE.
91
 
92
;; GET-CHAR
93
L0018:  LD      HL,($5C5D)      ; fetch the address from CH_ADD.
94
        LD      A,(HL)          ; use it to pick up current character.
95
 
96
;; TEST-CHAR
97
L001C:  CALL    L007D           ; routine SKIP-OVER tests if the character is
98
                                ; relevant.
99
        RET     NC              ; Return if it is significant.
100
 
101
; ------------------------------------
102
; THE 'COLLECT NEXT CHARACTER' RESTART
103
; ------------------------------------
104
;   As the BASIC commands and expressions are interpreted, this routine is
105
;   called repeatedly to step along the line.  It is used 83 times.
106
 
107
;; NEXT-CHAR
108
L0020:  CALL    L0074           ; routine CH-ADD+1 fetches the next immediate
109
                                ; character.
110
        JR      L001C           ; jump back to TEST-CHAR until a valid
111
                                ; character is found.
112
 
113
; ---
114
 
115
        DEFB    $FF, $FF, $FF   ; unused
116
 
117
; -----------------------
118
; THE 'CALCULATE' RESTART
119
; -----------------------
120
;   This restart enters the Spectrum's internal, floating-point, stack-based,
121
;   FORTH-like language.
122
;   It is further used recursively from within the calculator.
123
;   It is used on 77 occasions.
124
 
125
;; FP-CALC
126
L0028:  JP      L335B           ; jump forward to the CALCULATE routine.
127
 
128
; ---
129
 
130
        DEFB    $FF, $FF, $FF   ; spare - note that on the ZX81, space being a
131
        DEFB    $FF, $FF        ; little cramped, these same locations were
132
                                ; used for the five-byte end-calc literal.
133
 
134
; ------------------------------
135
; THE 'CREATE BC SPACES' RESTART
136
; ------------------------------
137
;   This restart is used on only 12 occasions to create BC spaces
138
;   between workspace and the calculator stack.
139
 
140
;; BC-SPACES
141
L0030:  PUSH    BC              ; Save number of spaces.
142
        LD      HL,($5C61)      ; Fetch WORKSP.
143
        PUSH    HL              ; Save address of workspace.
144
        JP      L169E           ; Jump forward to continuation code RESERVE.
145
 
146
; --------------------------------
147
; THE 'MASKABLE INTERRUPT' ROUTINE
148
; --------------------------------
149
;   This routine increments the Spectrum's three-byte FRAMES counter fifty
150
;   times a second (sixty times a second in the USA ).
151
;   Both this routine and the called KEYBOARD subroutine use the IY register
152
;   to access system variables and flags so a user-written program must
153
;   disable interrupts to make use of the IY register.
154
 
155
;; MASK-INT
156
L0038:  PUSH    AF              ; Save the registers that will be used but not
157
        PUSH    HL              ; the IY register unfortunately.
158
        LD      HL,($5C78)      ; Fetch the first two bytes at FRAMES1.
159
        INC     HL              ; Increment lowest two bytes of counter.
160
        LD      ($5C78),HL      ; Place back in FRAMES1.
161
        LD      A,H             ; Test if the result was zero.
162
        OR      L               ;
163
        JR      NZ,L0048        ; Forward, if not, to KEY-INT
164
 
165
        INC     (IY+$40)        ; otherwise increment FRAMES3 the third byte.
166
 
167
;   Now save the rest of the main registers and read and decode the keyboard.
168
 
169
;; KEY-INT
170
L0048:  PUSH    BC              ; Save the other main registers.
171
        PUSH    DE              ;
172
 
173
        CALL    L02BF           ; Routine KEYBOARD executes a stage in the
174
                                ; process of reading a key-press.
175
        POP     DE              ;
176
        POP     BC              ; Restore registers.
177
 
178
        POP     HL              ;
179
        POP     AF              ;
180
 
181
        EI                      ; Enable Interrupts.
182
        RET                     ; Return.
183
 
184
; ---------------------
185
; THE 'ERROR-2' ROUTINE
186
; ---------------------
187
;   A continuation of the code at 0008.
188
;   The error code is stored and after clearing down stacks, an indirect jump
189
;   is made to MAIN-4, etc. to handle the error.
190
 
191
;; ERROR-2
192
L0053:  POP     HL              ; drop the return address - the location
193
                                ; after the RST 08H instruction.
194
        LD      L,(HL)          ; fetch the error code that follows.
195
                                ; (nice to see this instruction used.)
196
 
197
;   Note. this entry point is used when out of memory at REPORT-4.
198
;   The L register has been loaded with the report code but X-PTR is not
199
;   updated.
200
 
201
;; ERROR-3
202
L0055:  LD      (IY+$00),L      ; Store it in the system variable ERR_NR.
203
        LD      SP,($5C3D)      ; ERR_SP points to an error handler on the
204
                                ; machine stack. There may be a hierarchy
205
                                ; of routines.
206
                                ; To MAIN-4 initially at base.
207
                                ; or REPORT-G on line entry.
208
                                ; or  ED-ERROR when editing.
209
                                ; or   ED-FULL during ed-enter.
210
                                ; or  IN-VAR-1 during runtime input etc.
211
 
212
        JP      L16C5           ; Jump to SET-STK to clear the calculator stack
213
                                ; and reset MEM to usual place in the systems
214
                                ; variables area and then indirectly to MAIN-4,
215
                                ; etc.
216
 
217
; ---
218
 
219
        DEFB    $FF, $FF, $FF   ; Unused locations
220
        DEFB    $FF, $FF, $FF   ; before the fixed-position
221
        DEFB    $FF             ; NMI routine.
222
 
223
; ------------------------------------
224
; THE 'NON-MASKABLE INTERRUPT' ROUTINE
225
; ------------------------------------
226
;
227
;   There is no NMI switch on the standard Spectrum or its peripherals.
228
;   When the NMI line is held low, then no matter what the Z80 was doing at
229
;   the time, it will now execute the code at 66 Hex.
230
;   This Interrupt Service Routine will jump to location zero if the contents
231
;   of the system variable NMIADD are zero or return if the location holds a
232
;   non-zero address.   So attaching a simple switch to the NMI as in the book
233
;   "Spectrum Hardware Manual" causes a reset.  The logic was obviously
234
;   intended to work the other way.  Sinclair Research said that, since they
235
;   had never advertised the NMI, they had no plans to fix the error "until
236
;   the opportunity arose".
237
;
238
;   Note. The location NMIADD was, in fact, later used by Sinclair Research
239
;   to enhance the text channel on the ZX Interface 1.
240
;   On later Amstrad-made Spectrums, and the Brazilian Spectrum, the logic of
241
;   this routine was indeed reversed but not as at first intended.
242
;
243
;   It can be deduced by looking elsewhere in this ROM that the NMIADD system
244
;   variable pointed to L121C and that this enabled a Warm Restart to be
245
;   performed at any time, even while playing machine code games, or while
246
;   another Spectrum has been allowed to gain control of this one.
247
;
248
;   Software houses would have been able to protect their games from attack by
249
;   placing two zeros in the NMIADD system variable.
250
 
251
;; RESET
252
L0066:  PUSH    AF              ; save the
253
        PUSH    HL              ; registers.
254
        LD      HL,($5CB0)      ; fetch the system variable NMIADD.
255
        LD      A,H             ; test address
256
        OR      L               ; for zero.
257
 
258
;        JR      NZ,L0070       ; skip to NO-RESET if NOT ZERO
259
        JR      Z,L0070         ; **FIXED**
260
 
261
        JP      (HL)            ; jump to routine ( i.e. L0000 )
262
 
263
;; NO-RESET
264
L0070:  POP     HL              ; restore the
265
        POP     AF              ; registers.
266
        RETN                    ; return to previous interrupt state.
267
 
268
; ---------------------------
269
; THE 'CH ADD + 1' SUBROUTINE
270
; ---------------------------
271
;   This subroutine is called from RST 20, and three times from elsewhere
272
;   to fetch the next immediate character following the current valid character
273
;   address and update the associated system variable.
274
;   The entry point TEMP-PTR1 is used from the SCANNING routine.
275
;   Both TEMP-PTR1 and TEMP-PTR2 are used by the READ command routine.
276
 
277
;; CH-ADD+1
278
L0074:  LD      HL,($5C5D)      ; fetch address from CH_ADD.
279
 
280
;; TEMP-PTR1
281
L0077:  INC     HL              ; increase the character address by one.
282
 
283
;; TEMP-PTR2
284
L0078:  LD      ($5C5D),HL      ; update CH_ADD with character address.
285
 
286
X007B:  LD      A,(HL)          ; load character to A from HL.
287
        RET                     ; and return.
288
 
289
; --------------------------
290
; THE 'SKIP OVER' SUBROUTINE
291
; --------------------------
292
;   This subroutine is called once from RST 18 to skip over white-space and
293
;   other characters irrelevant to the parsing of a BASIC line etc. .
294
;   Initially the A register holds the character to be considered
295
;   and HL holds its address which will not be within quoted text
296
;   when a BASIC line is parsed.
297
;   Although the 'tab' and 'at' characters will not appear in a BASIC line,
298
;   they could be present in a string expression, and in other situations.
299
;   Note. although white-space is usually placed in a program to indent loops
300
;   and make it more readable, it can also be used for the opposite effect and
301
;   spaces may appear in variable names although the parser never sees them.
302
;   It is this routine that helps make the variables 'Anum bEr5 3BUS' and
303
;   'a number 53 bus' appear the same to the parser.
304
 
305
;; SKIP-OVER
306
L007D:  CP      $21             ; test if higher than space.
307
        RET     NC              ; return with carry clear if so.
308
 
309
        CP      $0D             ; carriage return ?
310
        RET     Z               ; return also with carry clear if so.
311
 
312
                                ; all other characters have no relevance
313
                                ; to the parser and must be returned with
314
                                ; carry set.
315
 
316
        CP      $10             ; test if 0-15d
317
        RET     C               ; return, if so, with carry set.
318
 
319
        CP      $18             ; test if 24-32d
320
        CCF                     ; complement carry flag.
321
        RET     C               ; return with carry set if so.
322
 
323
                                ; now leaves 16d-23d
324
 
325
        INC     HL              ; all above have at least one extra character
326
                                ; to be stepped over.
327
 
328
        CP      $16             ; controls 22d ('at') and 23d ('tab') have two.
329
        JR      C,L0090         ; forward to SKIPS with ink, paper, flash,
330
                                ; bright, inverse or over controls.
331
                                ; Note. the high byte of tab is for RS232 only.
332
                                ; it has no relevance on this machine.
333
 
334
        INC     HL              ; step over the second character of 'at'/'tab'.
335
 
336
;; SKIPS
337
L0090:  SCF                     ; set the carry flag
338
        LD      ($5C5D),HL      ; update the CH_ADD system variable.
339
        RET                     ; return with carry set.
340
 
341
 
342
; ------------------
343
; THE 'TOKEN' TABLES
344
; ------------------
345
;   The tokenized characters 134d (RND) to 255d (COPY) are expanded using
346
;   this table. The last byte of a token is inverted to denote the end of
347
;   the word. The first is an inverted step-over byte.
348
 
349
;; TKN-TABLE
350
L0095:  DEFB    '?'+$80
351
        DEFM    "RN"
352
        DEFB    'D'+$80
353
        DEFM    "INKEY"
354
        DEFB    '$'+$80
355
        DEFB    'P','I'+$80
356
        DEFB    'F','N'+$80
357
        DEFM    "POIN"
358
        DEFB    'T'+$80
359
        DEFM    "SCREEN"
360
        DEFB    '$'+$80
361
        DEFM    "ATT"
362
        DEFB    'R'+$80
363
        DEFB    'A','T'+$80
364
        DEFM    "TA"
365
        DEFB    'B'+$80
366
        DEFM    "VAL"
367
        DEFB    '$'+$80
368
        DEFM    "COD"
369
        DEFB    'E'+$80
370
        DEFM    "VA"
371
        DEFB    'L'+$80
372
        DEFM    "LE"
373
        DEFB    'N'+$80
374
        DEFM    "SI"
375
        DEFB    'N'+$80
376
        DEFM    "CO"
377
        DEFB    'S'+$80
378
        DEFM    "TA"
379
        DEFB    'N'+$80
380
        DEFM    "AS"
381
        DEFB    'N'+$80
382
        DEFM    "AC"
383
        DEFB    'S'+$80
384
        DEFM    "AT"
385
        DEFB    'N'+$80
386
        DEFB    'L','N'+$80
387
        DEFM    "EX"
388
        DEFB    'P'+$80
389
        DEFM    "IN"
390
        DEFB    'T'+$80
391
        DEFM    "SQ"
392
        DEFB    'R'+$80
393
        DEFM    "SG"
394
        DEFB    'N'+$80
395
        DEFM    "AB"
396
        DEFB    'S'+$80
397
        DEFM    "PEE"
398
        DEFB    'K'+$80
399
        DEFB    'I','N'+$80
400
        DEFM    "US"
401
        DEFB    'R'+$80
402
        DEFM    "STR"
403
        DEFB    '$'+$80
404
        DEFM    "CHR"
405
        DEFB    '$'+$80
406
        DEFM    "NO"
407
        DEFB    'T'+$80
408
        DEFM    "BI"
409
        DEFB    'N'+$80
410
 
411
;   The previous 32 function-type words are printed without a leading space
412
;   The following have a leading space if they begin with a letter
413
 
414
        DEFB    'O','R'+$80
415
        DEFM    "AN"
416
        DEFB    'D'+$80
417
        DEFB    $3C,'='+$80             ; <=
418
        DEFB    $3E,'='+$80             ; >=
419
        DEFB    $3C,$3E+$80             ; <>
420
        DEFM    "LIN"
421
        DEFB    'E'+$80
422
        DEFM    "THE"
423
        DEFB    'N'+$80
424
        DEFB    'T','O'+$80
425
        DEFM    "STE"
426
        DEFB    'P'+$80
427
        DEFM    "DEF F"
428
        DEFB    'N'+$80
429
        DEFM    "CA"
430
        DEFB    'T'+$80
431
        DEFM    "FORMA"
432
        DEFB    'T'+$80
433
        DEFM    "MOV"
434
        DEFB    'E'+$80
435
        DEFM    "ERAS"
436
        DEFB    'E'+$80
437
        DEFM    "OPEN "
438
        DEFB    '#'+$80
439
        DEFM    "CLOSE "
440
        DEFB    '#'+$80
441
        DEFM    "MERG"
442
        DEFB    'E'+$80
443
        DEFM    "VERIF"
444
        DEFB    'Y'+$80
445
        DEFM    "BEE"
446
        DEFB    'P'+$80
447
        DEFM    "CIRCL"
448
        DEFB    'E'+$80
449
        DEFM    "IN"
450
        DEFB    'K'+$80
451
        DEFM    "PAPE"
452
        DEFB    'R'+$80
453
        DEFM    "FLAS"
454
        DEFB    'H'+$80
455
        DEFM    "BRIGH"
456
        DEFB    'T'+$80
457
        DEFM    "INVERS"
458
        DEFB    'E'+$80
459
        DEFM    "OVE"
460
        DEFB    'R'+$80
461
        DEFM    "OU"
462
        DEFB    'T'+$80
463
        DEFM    "LPRIN"
464
        DEFB    'T'+$80
465
        DEFM    "LLIS"
466
        DEFB    'T'+$80
467
        DEFM    "STO"
468
        DEFB    'P'+$80
469
        DEFM    "REA"
470
        DEFB    'D'+$80
471
        DEFM    "DAT"
472
        DEFB    'A'+$80
473
        DEFM    "RESTOR"
474
        DEFB    'E'+$80
475
        DEFM    "NE"
476
        DEFB    'W'+$80
477
        DEFM    "BORDE"
478
        DEFB    'R'+$80
479
        DEFM    "CONTINU"
480
        DEFB    'E'+$80
481
        DEFM    "DI"
482
        DEFB    'M'+$80
483
        DEFM    "RE"
484
        DEFB    'M'+$80
485
        DEFM    "FO"
486
        DEFB    'R'+$80
487
        DEFM    "GO T"
488
        DEFB    'O'+$80
489
        DEFM    "GO SU"
490
        DEFB    'B'+$80
491
        DEFM    "INPU"
492
        DEFB    'T'+$80
493
        DEFM    "LOA"
494
        DEFB    'D'+$80
495
        DEFM    "LIS"
496
        DEFB    'T'+$80
497
        DEFM    "LE"
498
        DEFB    'T'+$80
499
        DEFM    "PAUS"
500
        DEFB    'E'+$80
501
        DEFM    "NEX"
502
        DEFB    'T'+$80
503
        DEFM    "POK"
504
        DEFB    'E'+$80
505
        DEFM    "PRIN"
506
        DEFB    'T'+$80
507
        DEFM    "PLO"
508
        DEFB    'T'+$80
509
        DEFM    "RU"
510
        DEFB    'N'+$80
511
        DEFM    "SAV"
512
        DEFB    'E'+$80
513
        DEFM    "RANDOMIZ"
514
        DEFB    'E'+$80
515
        DEFB    'I','F'+$80
516
        DEFM    "CL"
517
        DEFB    'S'+$80
518
        DEFM    "DRA"
519
        DEFB    'W'+$80
520
        DEFM    "CLEA"
521
        DEFB    'R'+$80
522
        DEFM    "RETUR"
523
        DEFB    'N'+$80
524
        DEFM    "COP"
525
        DEFB    'Y'+$80
526
 
527
; ----------------
528
; THE 'KEY' TABLES
529
; ----------------
530
;   These six look-up tables are used by the keyboard reading routine
531
;   to decode the key values.
532
;
533
;   The first table contains the maps for the 39 keys of the standard
534
;   40-key Spectrum keyboard. The remaining key [SHIFT $27] is read directly.
535
;   The keys consist of the 26 upper-case alphabetic characters, the 10 digit
536
;   keys and the space, ENTER and symbol shift key.
537
;   Unshifted alphabetic keys have $20 added to the value.
538
;   The keywords for the main alphabetic keys are obtained by adding $A5 to
539
;   the values obtained from this table.
540
 
541
;; MAIN-KEYS
542
L0205:  DEFB    $42             ; B
543
        DEFB    $48             ; H
544
        DEFB    $59             ; Y
545
        DEFB    $36             ; 6
546
        DEFB    $35             ; 5
547
        DEFB    $54             ; T
548
        DEFB    $47             ; G
549
        DEFB    $56             ; V
550
        DEFB    $4E             ; N
551
        DEFB    $4A             ; J
552
        DEFB    $55             ; U
553
        DEFB    $37             ; 7
554
        DEFB    $34             ; 4
555
        DEFB    $52             ; R
556
        DEFB    $46             ; F
557
        DEFB    $43             ; C
558
        DEFB    $4D             ; M
559
        DEFB    $4B             ; K
560
        DEFB    $49             ; I
561
        DEFB    $38             ; 8
562
        DEFB    $33             ; 3
563
        DEFB    $45             ; E
564
        DEFB    $44             ; D
565
        DEFB    $58             ; X
566
        DEFB    $0E             ; SYMBOL SHIFT
567
        DEFB    $4C             ; L
568
        DEFB    $4F             ; O
569
        DEFB    $39             ; 9
570
        DEFB    $32             ; 2
571
        DEFB    $57             ; W
572
        DEFB    $53             ; S
573
        DEFB    $5A             ; Z
574
        DEFB    $20             ; SPACE
575
        DEFB    $0D             ; ENTER
576
        DEFB    $50             ; P
577
        DEFB    $30             ; 0
578
        DEFB    $31             ; 1
579
        DEFB    $51             ; Q
580
        DEFB    $41             ; A
581
 
582
 
583
;; E-UNSHIFT
584
;  The 26 unshifted extended mode keys for the alphabetic characters.
585
;  The green keywords on the original keyboard.
586
L022C:  DEFB    $E3             ; READ
587
        DEFB    $C4             ; BIN
588
        DEFB    $E0             ; LPRINT
589
        DEFB    $E4             ; DATA
590
        DEFB    $B4             ; TAN
591
        DEFB    $BC             ; SGN
592
        DEFB    $BD             ; ABS
593
        DEFB    $BB             ; SQR
594
        DEFB    $AF             ; CODE
595
        DEFB    $B0             ; VAL
596
        DEFB    $B1             ; LEN
597
        DEFB    $C0             ; USR
598
        DEFB    $A7             ; PI
599
        DEFB    $A6             ; INKEY$
600
        DEFB    $BE             ; PEEK
601
        DEFB    $AD             ; TAB
602
        DEFB    $B2             ; SIN
603
        DEFB    $BA             ; INT
604
        DEFB    $E5             ; RESTORE
605
        DEFB    $A5             ; RND
606
        DEFB    $C2             ; CHR$
607
        DEFB    $E1             ; LLIST
608
        DEFB    $B3             ; COS
609
        DEFB    $B9             ; EXP
610
        DEFB    $C1             ; STR$
611
        DEFB    $B8             ; LN
612
 
613
 
614
;; EXT-SHIFT
615
;  The 26 shifted extended mode keys for the alphabetic characters.
616
;  The red keywords below keys on the original keyboard.
617
L0246:  DEFB    $7E             ; ~
618
        DEFB    $DC             ; BRIGHT
619
        DEFB    $DA             ; PAPER
620
        DEFB    $5C             ; \
621
        DEFB    $B7             ; ATN
622
        DEFB    $7B             ; {
623
        DEFB    $7D             ; }
624
        DEFB    $D8             ; CIRCLE
625
        DEFB    $BF             ; IN
626
        DEFB    $AE             ; VAL$
627
        DEFB    $AA             ; SCREEN$
628
        DEFB    $AB             ; ATTR
629
        DEFB    $DD             ; INVERSE
630
        DEFB    $DE             ; OVER
631
        DEFB    $DF             ; OUT
632
        DEFB    $7F             ; (Copyright character)
633
        DEFB    $B5             ; ASN
634
        DEFB    $D6             ; VERIFY
635
        DEFB    $7C             ; |
636
        DEFB    $D5             ; MERGE
637
        DEFB    $5D             ; ]
638
        DEFB    $DB             ; FLASH
639
        DEFB    $B6             ; ACS
640
        DEFB    $D9             ; INK
641
        DEFB    $5B             ; [
642
        DEFB    $D7             ; BEEP
643
 
644
 
645
;; CTL-CODES
646
;  The ten control codes assigned to the top line of digits when the shift
647
;  key is pressed.
648
L0260:  DEFB    $0C             ; DELETE
649
        DEFB    $07             ; EDIT
650
        DEFB    $06             ; CAPS LOCK
651
        DEFB    $04             ; TRUE VIDEO
652
        DEFB    $05             ; INVERSE VIDEO
653
        DEFB    $08             ; CURSOR LEFT
654
        DEFB    $0A             ; CURSOR DOWN
655
        DEFB    $0B             ; CURSOR UP
656
        DEFB    $09             ; CURSOR RIGHT
657
        DEFB    $0F             ; GRAPHICS
658
 
659
 
660
;; SYM-CODES
661
;  The 26 red symbols assigned to the alphabetic characters of the keyboard.
662
;  The ten single-character digit symbols are converted without the aid of
663
;  a table using subtraction and minor manipulation.
664
L026A:  DEFB    $E2             ; STOP
665
        DEFB    $2A             ; *
666
        DEFB    $3F             ; ?
667
        DEFB    $CD             ; STEP
668
        DEFB    $C8             ; >=
669
        DEFB    $CC             ; TO
670
        DEFB    $CB             ; THEN
671
        DEFB    $5E             ; ^
672
        DEFB    $AC             ; AT
673
        DEFB    $2D             ; -
674
        DEFB    $2B             ; +
675
        DEFB    $3D             ; =
676
        DEFB    $2E             ; .
677
        DEFB    $2C             ; ,
678
        DEFB    $3B             ; ;
679
        DEFB    $22             ; "
680
        DEFB    $C7             ; <=
681
        DEFB    $3C             ; <
682
        DEFB    $C3             ; NOT
683
        DEFB    $3E             ; >
684
        DEFB    $C5             ; OR
685
        DEFB    $2F             ; /
686
        DEFB    $C9             ; <>
687
        DEFB    $60             ; pound
688
        DEFB    $C6             ; AND
689
        DEFB    $3A             ; :
690
 
691
;; E-DIGITS
692
;  The ten keywords assigned to the digits in extended mode.
693
;  The remaining red keywords below the keys.
694
L0284:  DEFB    $D0             ; FORMAT
695
        DEFB    $CE             ; DEF FN
696
        DEFB    $A8             ; FN
697
        DEFB    $CA             ; LINE
698
        DEFB    $D3             ; OPEN #
699
        DEFB    $D4             ; CLOSE #
700
        DEFB    $D1             ; MOVE
701
        DEFB    $D2             ; ERASE
702
        DEFB    $A9             ; POINT
703
        DEFB    $CF             ; CAT
704
 
705
 
706
;*******************************
707
;** Part 2. KEYBOARD ROUTINES **
708
;*******************************
709
 
710
;   Using shift keys and a combination of modes the Spectrum 40-key keyboard
711
;   can be mapped to 256 input characters
712
 
713
; ---------------------------------------------------------------------------
714
;
715
;         0     1     2     3     4 -Bits-  4     3     2     1     0
716
; PORT                                                                    PORT
717
;
718
; F7FE  [ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ]  |  [ 6 ] [ 7 ] [ 8 ] [ 9 ] [ 0 ]   EFFE
719
;  ^                                   |                                   v
720
; FBFE  [ Q ] [ W ] [ E ] [ R ] [ T ]  |  [ Y ] [ U ] [ I ] [ O ] [ P ]   DFFE
721
;  ^                                   |                                   v
722
; FDFE  [ A ] [ S ] [ D ] [ F ] [ G ]  |  [ H ] [ J ] [ K ] [ L ] [ ENT ] BFFE
723
;  ^                                   |                                   v
724
; FEFE  [SHI] [ Z ] [ X ] [ C ] [ V ]  |  [ B ] [ N ] [ M ] [sym] [ SPC ] 7FFE
725
;  ^     $27                                                 $18           v
726
; Start                                                                   End
727
;        00100111                                            00011000
728
;
729
; ---------------------------------------------------------------------------
730
;   The above map may help in reading.
731
;   The neat arrangement of ports means that the B register need only be
732
;   rotated left to work up the left hand side and then down the right
733
;   hand side of the keyboard. When the reset bit drops into the carry
734
;   then all 8 half-rows have been read. Shift is the first key to be
735
;   read. The lower six bits of the shifts are unambiguous.
736
 
737
; -------------------------------
738
; THE 'KEYBOARD SCANNING' ROUTINE
739
; -------------------------------
740
;   From keyboard and s-inkey$
741
;   Returns 1 or 2 keys in DE, most significant shift first if any
742
;   key values 0-39 else 255
743
 
744
;; KEY-SCAN
745
L028E:  LD      L,$2F           ; initial key value
746
                                ; valid values are obtained by subtracting
747
                                ; eight five times.
748
        LD      DE,$FFFF        ; a buffer to receive 2 keys.
749
 
750
        LD      BC,$FEFE        ; the commencing port address
751
                                ; B holds 11111110 initially and is also
752
                                ; used to count the 8 half-rows
753
;; KEY-LINE
754
L0296:  IN      A,(C)           ; read the port to A - bits will be reset
755
                                ; if a key is pressed else set.
756
        CPL                     ; complement - pressed key-bits are now set
757
        AND     $1F             ; apply 00011111 mask to pick up the
758
                                ; relevant set bits.
759
 
760
        JR      Z,L02AB         ; forward to KEY-DONE if zero and therefore
761
                                ; no keys pressed in row at all.
762
 
763
        LD      H,A             ; transfer row bits to H
764
        LD      A,L             ; load the initial key value to A
765
 
766
;; KEY-3KEYS
767
L029F:  INC     D               ; now test the key buffer
768
        RET     NZ              ; if we have collected 2 keys already
769
                                ; then too many so quit.
770
 
771
;; KEY-BITS
772
L02A1:  SUB     $08             ; subtract 8 from the key value
773
                                ; cycling through key values (top = $27)
774
                                ; e.g. 2F>   27>1F>17>0F>07
775
                                ;      2E>   26>1E>16>0E>06
776
        SRL     H               ; shift key bits right into carry.
777
        JR      NC,L02A1        ; back to KEY-BITS if not pressed
778
                                ; but if pressed we have a value (0-39d)
779
 
780
        LD      D,E             ; transfer a possible previous key to D
781
        LD      E,A             ; transfer the new key to E
782
        JR      NZ,L029F        ; back to KEY-3KEYS if there were more
783
                                ; set bits - H was not yet zero.
784
 
785
;; KEY-DONE
786
L02AB:  DEC     L               ; cycles 2F>2E>2D>2C>2B>2A>29>28 for
787
                                ; each half-row.
788
        RLC     B               ; form next port address e.g. FEFE > FDFE
789
        JR      C,L0296         ; back to KEY-LINE if still more rows to do.
790
 
791
        LD      A,D             ; now test if D is still FF ?
792
        INC     A               ; if it is zero we have at most 1 key
793
                                ; range now $01-$28  (1-40d)
794
        RET     Z               ; return if one key or no key.
795
 
796
        CP      $28             ; is it capsshift (was $27) ?
797
        RET     Z               ; return if so.
798
 
799
        CP      $19             ; is it symbol shift (was $18) ?
800
        RET     Z               ; return also
801
 
802
        LD      A,E             ; now test E
803
        LD      E,D             ; but first switch
804
        LD      D,A             ; the two keys.
805
        CP      $18             ; is it symbol shift ?
806
        RET                     ; return (with zero set if it was).
807
                                ; but with symbol shift now in D
808
 
809
; ----------------------
810
; THE 'KEYBOARD' ROUTINE
811
; ----------------------
812
;   Called from the interrupt 50 times a second.
813
;
814
 
815
;; KEYBOARD
816
L02BF:  CALL    L028E           ; routine KEY-SCAN
817
        RET     NZ              ; return if invalid combinations
818
 
819
;   then decrease the counters within the two key-state maps
820
;   as this could cause one to become free.
821
;   if the keyboard has not been pressed during the last five interrupts
822
;   then both sets will be free.
823
 
824
 
825
        LD      HL,$5C00        ; point to KSTATE-0
826
 
827
;; K-ST-LOOP
828
L02C6:  BIT     7,(HL)          ; is it free ?  (i.e. $FF)
829
        JR      NZ,L02D1        ; forward to K-CH-SET if so
830
 
831
        INC     HL              ; address the 5-counter
832
        DEC     (HL)            ; decrease the counter
833
        DEC     HL              ; step back
834
 
835
        JR      NZ,L02D1        ; forward to K-CH-SET if not at end of count
836
 
837
        LD      (HL),$FF        ; else mark this particular map free.
838
 
839
;; K-CH-SET
840
L02D1:  LD      A,L             ; make a copy of the low address byte.
841
        LD      HL,$5C04        ; point to KSTATE-4
842
                                ; (ld l,$04 would do)
843
        CP      L               ; have both sets been considered ?
844
        JR      NZ,L02C6        ; back to K-ST-LOOP to consider this 2nd set
845
 
846
;   now the raw key (0-38d) is converted to a main key (uppercase).
847
 
848
        CALL    L031E           ; routine K-TEST to get main key in A
849
 
850
        RET     NC              ; return if just a single shift
851
 
852
        LD      HL,$5C00        ; point to KSTATE-0
853
        CP      (HL)            ; does the main key code match ?
854
        JR      Z,L0310         ; forward to K-REPEAT if so
855
 
856
;   if not consider the second key map.
857
 
858
        EX      DE,HL           ; save kstate-0 in de
859
        LD      HL,$5C04        ; point to KSTATE-4
860
        CP      (HL)            ; does the main key code match ?
861
        JR      Z,L0310         ; forward to K-REPEAT if so
862
 
863
;   having excluded a repeating key we can now consider a new key.
864
;   the second set is always examined before the first.
865
 
866
        BIT     7,(HL)          ; is the key map free ?
867
        JR      NZ,L02F1        ; forward to K-NEW if so.
868
 
869
        EX      DE,HL           ; bring back KSTATE-0
870
        BIT     7,(HL)          ; is it free ?
871
        RET     Z               ; return if not.
872
                                ; as we have a key but nowhere to put it yet.
873
 
874
;   continue or jump to here if one of the buffers was free.
875
 
876
;; K-NEW
877
L02F1:  LD      E,A             ; store key in E
878
        LD      (HL),A          ; place in free location
879
        INC     HL              ; advance to the interrupt counter
880
        LD      (HL),$05        ; and initialize counter to 5
881
        INC     HL              ; advance to the delay
882
        LD      A,($5C09)       ; pick up the system variable REPDEL
883
        LD      (HL),A          ; and insert that for first repeat delay.
884
        INC     HL              ; advance to last location of state map.
885
 
886
        LD      C,(IY+$07)      ; pick up MODE  (3 bytes)
887
        LD      D,(IY+$01)      ; pick up FLAGS (3 bytes)
888
        PUSH    HL              ; save state map location
889
                                ; Note. could now have used, to avoid IY,
890
                                ; ld l,$41; ld c,(hl); ld l,$3B; ld d,(hl).
891
                                ; six and two threes of course.
892
 
893
        CALL    L0333           ; routine K-DECODE
894
 
895
        POP     HL              ; restore map pointer
896
        LD      (HL),A          ; put the 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 a new key.
901
        RET                     ; return to interrupt routine.
902
 
903
; -----------------------
904
; THE 'REPEAT KEY' BRANCH
905
; -----------------------
906
;   A possible repeat has been identified. HL addresses the raw key.
907
;   The last location of the key map holds the decoded key from the first
908
;   context.  This could be a keyword and, with the exception of NOT a repeat
909
;   is syntactically incorrect and not really desirable.
910
 
911
;; K-REPEAT
912
L0310:  INC     HL              ; increment the map pointer to second location.
913
        LD      (HL),$05        ; maintain interrupt counter at 5.
914
        INC     HL              ; now point to third location.
915
        DEC     (HL)            ; decrease the REPDEL value which is used to
916
                                ; time the delay of a repeat key.
917
 
918
        RET     NZ              ; return if not yet zero.
919
 
920
        LD      A,($5C0A)       ; fetch the system variable value REPPER.
921
        LD      (HL),A          ; for subsequent repeats REPPER will be used.
922
 
923
        INC     HL              ; advance
924
                                ;
925
        LD      A,(HL)          ; pick up the key decoded possibly in another
926
                                ; context.
927
                                ; Note. should compare with $A5 (RND) and make
928
                                ; a simple return if this is a keyword.
929
                                ; e.g. cp $a5; ret nc; (3 extra bytes)
930
        JR      L0308           ; back to K-END
931
 
932
; ----------------------
933
; THE 'KEY-TEST' ROUTINE
934
; ----------------------
935
;   also called from s-inkey$
936
;   begin by testing for a shift with no other.
937
 
938
;; K-TEST
939
L031E:  LD      B,D             ; load most significant key to B
940
                                ; will be $FF if not shift.
941
        LD      D,$00           ; and reset D to index into main table
942
        LD      A,E             ; load least significant key from E
943
        CP      $27             ; is it higher than 39d   i.e. FF
944
        RET     NC              ; return with just a shift (in B now)
945
 
946
        CP      $18             ; is it symbol shift ?
947
        JR      NZ,L032C        ; forward to K-MAIN if not
948
 
949
;   but we could have just symbol shift and no other
950
 
951
        BIT     7,B             ; is other key $FF (ie not shift)
952
        RET     NZ              ; return with solitary symbol shift
953
 
954
 
955
;; K-MAIN
956
L032C:  LD      HL,L0205        ; address: MAIN-KEYS
957
        ADD     HL,DE           ; add offset 0-38
958
        LD      A,(HL)          ; pick up main key value
959
        SCF                     ; set carry flag
960
        RET                     ; return    (B has other key still)
961
 
962
; ----------------------------------
963
; THE 'KEYBOARD DECODING' SUBROUTINE
964
; ----------------------------------
965
;   also called from s-inkey$
966
 
967
;; K-DECODE
968
L0333:  LD      A,E             ; pick up the stored main key
969
        CP      $3A             ; an arbitrary point between digits and letters
970
        JR      C,L0367         ; forward to K-DIGIT with digits, space, enter.
971
 
972
        DEC     C               ; decrease MODE ( 0='KLC', 1='E', 2='G')
973
 
974
        JP      M,L034F         ; to K-KLC-LET if was zero
975
 
976
        JR      Z,L0341         ; to K-E-LET if was 1 for extended letters.
977
 
978
;   proceed with graphic codes.
979
;   Note. should selectively drop return address if code > 'U' ($55).
980
;   i.e. abort the KEYBOARD call.
981
;   e.g. cp 'V'; jr c,addit; pop af ;pop af ;;addit etc. (6 extra bytes).
982
;   (s-inkey$ never gets into graphics mode.)
983
 
984
;; addit
985
        ADD     A,$4F           ; add offset to augment 'A' to graphics A say.
986
        RET                     ; return.
987
                                ; Note. ( but [GRAPH] V gives RND, etc ).
988
 
989
; ---
990
 
991
;   the jump was to here with extended mode with uppercase A-Z.
992
 
993
;; K-E-LET
994
L0341:  LD      HL,L022C-$41    ; base address of E-UNSHIFT L022c.
995
                                ; ( $01EB in standard ROM ).
996
        INC     B               ; test B is it empty i.e. not a shift.
997
        JR      Z,L034A         ; forward to K-LOOK-UP if neither shift.
998
 
999
        LD      HL,L0246-$41    ; Address: $0205 L0246-$41 EXT-SHIFT base
1000
 
1001
;; K-LOOK-UP
1002
L034A:  LD      D,$00           ; prepare to index.
1003
        ADD     HL,DE           ; add the main key value.
1004
        LD      A,(HL)          ; pick up other mode value.
1005
        RET                     ; return.
1006
 
1007
; ---
1008
 
1009
;   the jump was here with mode = 0
1010
 
1011
;; K-KLC-LET
1012
L034F:  LD      HL,L026A-$41    ; prepare base of sym-codes
1013
        BIT     0,B             ; shift=$27 sym-shift=$18
1014
        JR      Z,L034A         ; back to K-LOOK-UP with symbol-shift
1015
 
1016
        BIT     3,D             ; test FLAGS is it 'K' mode (from OUT-CURS)
1017
        JR      Z,L0364         ; skip to K-TOKENS if so
1018
 
1019
        BIT     3,(IY+$30)      ; test FLAGS2 - consider CAPS LOCK ?
1020
        RET     NZ              ; return if so with main code.
1021
 
1022
        INC     B               ; is shift being pressed ?
1023
                                ; result zero if not
1024
        RET     NZ              ; return if shift pressed.
1025
 
1026
        ADD     A,$20           ; else convert the code to lower case.
1027
        RET                     ; return.
1028
 
1029
; ---
1030
 
1031
;   the jump was here for tokens
1032
 
1033
;; K-TOKENS
1034
L0364:  ADD     A,$A5           ; add offset to main code so that 'A'
1035
                                ; becomes 'NEW' etc.
1036
 
1037
        RET                     ; return.
1038
 
1039
; ---
1040
 
1041
;   the jump was here with digits, space, enter and symbol shift (< $xx)
1042
 
1043
;; K-DIGIT
1044
L0367:  CP      $30             ; is it '0' or higher ?
1045
        RET     C               ; return with space, enter and symbol-shift
1046
 
1047
        DEC     C               ; test MODE (was 0='KLC', 1='E', 2='G')
1048
        JP      M,L039D         ; jump to K-KLC-DGT if was 0.
1049
 
1050
        JR      NZ,L0389        ; forward to K-GRA-DGT if mode was 2.
1051
 
1052
;   continue with extended digits 0-9.
1053
 
1054
        LD      HL,L0284-$30    ; $0254 - base of E-DIGITS
1055
        BIT     5,B             ; test - shift=$27 sym-shift=$18
1056
        JR      Z,L034A         ; to K-LOOK-UP if sym-shift
1057
 
1058
        CP      $38             ; is character '8' ?
1059
        JR      NC,L0382        ; to K-8-&-9 if greater than '7'
1060
 
1061
        SUB     $20             ; reduce to ink range $10-$17
1062
        INC     B               ; shift ?
1063
        RET     Z               ; return if not.
1064
 
1065
        ADD     A,$08           ; add 8 to give paper range $18 - $1F
1066
        RET                     ; return
1067
 
1068
; ---
1069
 
1070
;   89
1071
 
1072
;; K-8-&-9
1073
L0382:  SUB     $36             ; reduce to 02 and 03  bright codes
1074
        INC     B               ; test if shift pressed.
1075
        RET     Z               ; return if not.
1076
 
1077
        ADD     A,$FE           ; subtract 2 setting carry
1078
        RET                     ; to give 0 and 1    flash codes.
1079
 
1080
; ---
1081
 
1082
;   graphics mode with digits
1083
 
1084
;; K-GRA-DGT
1085
L0389:  LD      HL,L0260-$30    ; $0230 base address of CTL-CODES
1086
 
1087
        CP      $39             ; is key '9' ?
1088
        JR      Z,L034A         ; back to K-LOOK-UP - changed to $0F, GRAPHICS.
1089
 
1090
        CP      $30             ; is key '0' ?
1091
        JR      Z,L034A         ; back to K-LOOK-UP - changed to $0C, delete.
1092
 
1093
;   for keys '0' - '7' we assign a mosaic character depending on shift.
1094
 
1095
        AND     $07             ; convert character to number. 0 - 7.
1096
        ADD     A,$80           ; add offset - they start at $80
1097
 
1098
        INC     B               ; destructively test for shift
1099
        RET     Z               ; and return if not pressed.
1100
 
1101
        XOR     $0F             ; toggle bits becomes range $88-$8F
1102
        RET                     ; return.
1103
 
1104
; ---
1105
 
1106
;   now digits in 'KLC' mode
1107
 
1108
;; K-KLC-DGT
1109
L039D:  INC     B               ; return with digit codes if neither
1110
        RET     Z               ; shift key pressed.
1111
 
1112
        BIT     5,B             ; test for caps shift.
1113
 
1114
        LD      HL,L0260-$30    ; prepare base of table CTL-CODES.
1115
        JR      NZ,L034A        ; back to K-LOOK-UP if shift pressed.
1116
 
1117
;   must have been symbol shift
1118
 
1119
        SUB     $10             ; for ASCII most will now be correct
1120
                                ; on a standard typewriter.
1121
 
1122
        CP      $22             ; but '@' is not - see below.
1123
        JR      Z,L03B2         ; forward to K-@-CHAR if so
1124
 
1125
        CP      $20             ; '_' is the other one that fails
1126
        RET     NZ              ; return if not.
1127
 
1128
        LD      A,$5F           ; substitute ASCII '_'
1129
        RET                     ; return.
1130
 
1131
; ---
1132
 
1133
;; K-@-CHAR
1134
L03B2:  LD      A,$40           ; substitute ASCII '@'
1135
        RET                     ; return.
1136
 
1137
 
1138
; ------------------------------------------------------------------------
1139
;   The Spectrum Input character keys. One or two are abbreviated.
1140
;   From $00 Flash 0 to $FF COPY. The routine above has decoded all these.
1141
 
1142
;  | 00 Fl0| 01 Fl1| 02 Br0| 03 Br1| 04 In0| 05 In1| 06 CAP| 07 EDT|
1143
;  | 08 LFT| 09 RIG| 0A DWN| 0B UP | 0C DEL| 0D ENT| 0E SYM| 0F GRA|
1144
;  | 10 Ik0| 11 Ik1| 12 Ik2| 13 Ik3| 14 Ik4| 15 Ik5| 16 Ik6| 17 Ik7|
1145
;  | 18 Pa0| 19 Pa1| 1A Pa2| 1B Pa3| 1C Pa4| 1D Pa5| 1E Pa6| 1F Pa7|
1146
;  | 20 SP | 21  ! | 22  " | 23  # | 24  $ | 25  % | 26  & | 27  ' |
1147
;  | 28  ( | 29  ) | 2A  * | 2B  + | 2C  , | 2D  - | 2E  . | 2F  / |
1148
;  | 30  0 | 31  1 | 32  2 | 33  3 | 34  4 | 35  5 | 36  6 | 37  7 |
1149
;  | 38  8 | 39  9 | 3A  : | 3B  ; | 3C  < | 3D  = | 3E  > | 3F  ? |
1150
;  | 40  @ | 41  A | 42  B | 43  C | 44  D | 45  E | 46  F | 47  G |
1151
;  | 48  H | 49  I | 4A  J | 4B  K | 4C  L | 4D  M | 4E  N | 4F  O |
1152
;  | 50  P | 51  Q | 52  R | 53  S | 54  T | 55  U | 56  V | 57  W |
1153
;  | 58  X | 59  Y | 5A  Z | 5B  [ | 5C  \ | 5D  ] | 5E  ^ | 5F  _ |
1154
;  | 60  £ | 61  a | 62  b | 63  c | 64  d | 65  e | 66  f | 67  g |
1155
;  | 68  h | 69  i | 6A  j | 6B  k | 6C  l | 6D  m | 6E  n | 6F  o |
1156
;  | 70  p | 71  q | 72  r | 73  s | 74  t | 75  u | 76  v | 77  w |
1157
;  | 78  x | 79  y | 7A  z | 7B  { | 7C  | | 7D  } | 7E  ~ | 7F  © |
1158
;  | 80 128| 81 129| 82 130| 83 131| 84 132| 85 133| 86 134| 87 135|
1159
;  | 88 136| 89 137| 8A 138| 8B 139| 8C 140| 8D 141| 8E 142| 8F 143|
1160
;  | 90 [A]| 91 [B]| 92 [C]| 93 [D]| 94 [E]| 95 [F]| 96 [G]| 97 [H]|
1161
;  | 98 [I]| 99 [J]| 9A [K]| 9B [L]| 9C [M]| 9D [N]| 9E [O]| 9F [P]|
1162
;  | A0 [Q]| A1 [R]| A2 [S]| A3 [T]| A4 [U]| A5 RND| A6 IK$| A7 PI |
1163
;  | A8 FN | A9 PNT| AA SC$| AB ATT| AC AT | AD TAB| AE VL$| AF COD|
1164
;  | B0 VAL| B1 LEN| B2 SIN| B3 COS| B4 TAN| B5 ASN| B6 ACS| B7 ATN|
1165
;  | B8 LN | B9 EXP| BA INT| BB SQR| BC SGN| BD ABS| BE PEK| BF IN |
1166
;  | C0 USR| C1 ST$| C2 CH$| C3 NOT| C4 BIN| C5 OR | C6 AND| C7 <= |
1167
;  | C8 >= | C9 <> | CA LIN| CB THN| CC TO | CD STP| CE DEF| CF CAT|
1168
;  | D0 FMT| D1 MOV| D2 ERS| D3 OPN| D4 CLO| D5 MRG| D6 VFY| D7 BEP|
1169
;  | D8 CIR| D9 INK| DA PAP| DB FLA| DC BRI| DD INV| DE OVR| DF OUT|
1170
;  | E0 LPR| E1 LLI| E2 STP| E3 REA| E4 DAT| E5 RES| E6 NEW| E7 BDR|
1171
;  | E8 CON| E9 DIM| EA REM| EB FOR| EC GTO| ED GSB| EE INP| EF LOA|
1172
;  | F0 LIS| F1 LET| F2 PAU| F3 NXT| F4 POK| F5 PRI| F6 PLO| F7 RUN|
1173
;  | F8 SAV| F9 RAN| FA IF | FB CLS| FC DRW| FD CLR| FE RET| FF CPY|
1174
 
1175
;   Note that for simplicity, Sinclair have located all the control codes
1176
;   below the space character.
1177
;   ASCII DEL, $7F, has been made a copyright symbol.
1178
;   Also $60, '`', not used in BASIC but used in other languages, has been
1179
;   allocated the local currency symbol for the relevant country -
1180
;    £  in most Spectrums.
1181
 
1182
; ------------------------------------------------------------------------
1183
 
1184
 
1185
;**********************************
1186
;** Part 3. LOUDSPEAKER ROUTINES **
1187
;**********************************
1188
 
1189
; Documented by Alvin Albrecht.
1190
 
1191
; ------------------------------
1192
; Routine to control loudspeaker
1193
; ------------------------------
1194
; Outputs a square wave of given duration and frequency
1195
; to the loudspeaker.
1196
;   Enter with: DE = #cycles - 1
1197
;               HL = tone period as described next
1198
;
1199
; The tone period is measured in T states and consists of
1200
; three parts: a coarse part (H register), a medium part
1201
; (bits 7..2 of L) and a fine part (bits 1..0 of L) which
1202
; contribute to the waveform timing as follows:
1203
;
1204
;                          coarse    medium       fine
1205
; duration of low  = 118 + 1024*H + 16*(L>>2) + 4*(L&0x3)
1206
; duration of hi   = 118 + 1024*H + 16*(L>>2) + 4*(L&0x3)
1207
; Tp = tone period = 236 + 2048*H + 32*(L>>2) + 8*(L&0x3)
1208
;                  = 236 + 2048*H + 8*L = 236 + 8*HL
1209
;
1210
; As an example, to output five seconds of middle C (261.624 Hz):
1211
;   (a) Tone period = 1/261.624 = 3.822ms
1212
;   (b) Tone period in T-States = 3.822ms*fCPU = 13378
1213
;         where fCPU = clock frequency of the CPU = 3.5MHz
1214
;    ©  Find H and L for desired tone period:
1215
;         HL = (Tp - 236) / 8 = (13378 - 236) / 8 = 1643 = 0x066B
1216
;   (d) Tone duration in cycles = 5s/3.822ms = 1308 cycles
1217
;         DE = 1308 - 1 = 0x051B
1218
;
1219
; The resulting waveform has a duty ratio of exactly 50%.
1220
;
1221
;
1222
;; BEEPER
1223
L03B5:  DI                      ; Disable Interrupts so they don't disturb timing
1224
        LD      A,L             ;
1225
        SRL     L               ;
1226
        SRL     L               ; L = medium part of tone period
1227
        CPL                     ;
1228
        AND     $03             ; A = 3 - fine part of tone period
1229
        LD      C,A             ;
1230
        LD      B,$00           ;
1231
        LD      IX,L03D1        ; Address: BE-IX+3
1232
        ADD     IX,BC           ;   IX holds address of entry into the loop
1233
                                ;   the loop will contain 0-3 NOPs, implementing
1234
                                ;   the fine part of the tone period.
1235
        LD      A,($5C48)       ; BORDCR
1236
        AND     $38             ; bits 5..3 contain border colour
1237
        RRCA                    ; border colour bits moved to 2..0
1238
        RRCA                    ;   to match border bits on port #FE
1239
        RRCA                    ;
1240
        OR       $08            ; bit 3 set (tape output bit on port #FE)
1241
                                ;   for loud sound output
1242
;; BE-IX+3
1243
L03D1:  NOP              ;(4)   ; optionally executed NOPs for small
1244
                                ;   adjustments to tone period
1245
;; BE-IX+2
1246
L03D2:  NOP              ;(4)   ;
1247
 
1248
;; BE-IX+1
1249
L03D3:  NOP              ;(4)   ;
1250
 
1251
;; BE-IX+0
1252
L03D4:  INC     B        ;(4)   ;
1253
        INC     C        ;(4)   ;
1254
 
1255
;; BE-H&L-LP
1256
L03D6:  DEC     C        ;(4)   ; timing loop for duration of
1257
        JR      NZ,L03D6 ;(12/7);   high or low pulse of waveform
1258
 
1259
        LD      C,$3F    ;(7)   ;
1260
        DEC     B        ;(4)   ;
1261
        JP      NZ,L03D6 ;(10)  ; to BE-H&L-LP
1262
 
1263
        XOR     $10      ;(7)   ; toggle output beep bit
1264
        OUT     ($FE),A  ;(11)  ; output pulse
1265
        LD      B,H      ;(4)   ; B = coarse part of tone period
1266
        LD      C,A      ;(4)   ; save port #FE output byte
1267
        BIT     4,A      ;(8)   ; if new output bit is high, go
1268
        JR      NZ,L03F2 ;(12/7);   to BE-AGAIN
1269
 
1270
        LD      A,D      ;(4)   ; one cycle of waveform has completed
1271
        OR      E        ;(4)   ;   (low->low). if cycle countdown = 0
1272
        JR      Z,L03F6  ;(12/7);   go to BE-END
1273
 
1274
        LD      A,C      ;(4)   ; restore output byte for port #FE
1275
        LD      C,L      ;(4)   ; C = medium part of tone period
1276
        DEC     DE       ;(6)   ; decrement cycle count
1277
        JP      (IX)     ;(8)   ; do another cycle
1278
 
1279
;; BE-AGAIN                     ; halfway through cycle
1280
L03F2:  LD      C,L      ;(4)   ; C = medium part of tone period
1281
        INC     C        ;(4)   ; adds 16 cycles to make duration of high = duration of low
1282
        JP      (IX)     ;(8)   ; do high pulse of tone
1283
 
1284
;; BE-END
1285
L03F6:  EI                      ; Enable Interrupts
1286
        RET                     ;
1287
 
1288
 
1289
; ------------------
1290
; THE 'BEEP' COMMAND
1291
; ------------------
1292
; BASIC interface to BEEPER subroutine.
1293
; Invoked in BASIC with:
1294
;   BEEP dur, pitch
1295
;   where dur   = duration in seconds
1296
;         pitch = # of semitones above/below middle C
1297
;
1298
; Enter with: pitch on top of calculator stack
1299
;             duration next on calculator stack
1300
;
1301
;; beep
1302
L03F8:  RST     28H             ;; FP-CALC
1303
        DEFB    $31             ;;duplicate                  ; duplicate pitch
1304
        DEFB    $27             ;;int                        ; convert to integer
1305
        DEFB    $C0             ;;st-mem-0                   ; store integer pitch to memory 0
1306
        DEFB    $03             ;;subtract                   ; calculate fractional part of pitch = fp_pitch - int_pitch
1307
        DEFB    $34             ;;stk-data                   ; push constant
1308
        DEFB    $EC             ;;Exponent: $7C, Bytes: 4    ; constant = 0.05762265
1309
        DEFB    $6C,$98,$1F,$F5 ;;($6C,$98,$1F,$F5)
1310
        DEFB    $04             ;;multiply                   ; compute:
1311
        DEFB    $A1             ;;stk-one                    ; 1 + 0.05762265 * fraction_part(pitch)
1312
        DEFB    $0F             ;;addition
1313
        DEFB    $38             ;;end-calc                   ; leave on calc stack
1314
 
1315
        LD      HL,$5C92        ; MEM-0: number stored here is in 16 bit integer format (pitch)
1316
                                ;   0, 0/FF (pos/neg), LSB, MSB, 0
1317
                                ;   LSB/MSB is stored in two's complement
1318
                                ; In the following, the pitch is checked if it is in the range -128<=p<=127
1319
        LD      A,(HL)          ; First byte must be zero, otherwise
1320
        AND     A               ;   error in integer conversion
1321
        JR      NZ,L046C        ; to REPORT-B
1322
 
1323
        INC     HL              ;
1324
        LD      C,(HL)          ; C = pos/neg flag = 0/FF
1325
        INC     HL              ;
1326
        LD      B,(HL)          ; B = LSB, two's complement
1327
        LD      A,B             ;
1328
        RLA                     ;
1329
        SBC     A,A             ; A = 0/FF if B is pos/neg
1330
        CP      C               ; must be the same as C if the pitch is -128<=p<=127
1331
        JR      NZ,L046C        ; if no, error REPORT-B
1332
 
1333
        INC     HL              ; if -128<=p<=127, MSB will be 0/FF if B is pos/neg
1334
        CP      (HL)            ; verify this
1335
        JR      NZ,L046C        ; if no, error REPORT-B
1336
                                ; now we know -128<=p<=127
1337
        LD      A,B             ; A = pitch + 60
1338
        ADD     A,$3C           ; if -60<=pitch<=67,
1339
        JP      P,L0425         ;   goto BE-i-OK
1340
 
1341
        JP      PO,L046C        ; if pitch <= 67 goto REPORT-B
1342
                                ;   lower bound of pitch set at -60
1343
 
1344
;; BE-I-OK                      ; here, -60<=pitch<=127
1345
                                ; and A=pitch+60 -> 0<=A<=187
1346
 
1347
L0425:  LD      B,$FA           ; 6 octaves below middle C
1348
 
1349
;; BE-OCTAVE                    ; A=# semitones above 5 octaves below middle C
1350
L0427:  INC     B               ; increment octave
1351
        SUB     $0C             ; 12 semitones = one octave
1352
        JR      NC,L0427        ; to BE-OCTAVE
1353
 
1354
        ADD     A,$0C           ; A = # semitones above C (0-11)
1355
        PUSH    BC              ; B = octave displacement from middle C, 2's complement: -5<=B<=10
1356
        LD      HL,L046E        ; Address: semi-tone
1357
        CALL    L3406           ; routine LOC-MEM
1358
                                ;   HL = 5*A + $046E
1359
        CALL    L33B4           ; routine STACK-NUM
1360
                                ;   read FP value (freq) from semitone table (HL) and push onto calc stack
1361
 
1362
        RST     28H             ;; FP-CALC
1363
        DEFB    $04             ;;multiply   mult freq by 1 + 0.0576 * fraction_part(pitch) stacked earlier
1364
                                ;;             thus taking into account fractional part of pitch.
1365
                                ;;           the number 0.0576*frequency is the distance in Hz to the next
1366
                                ;;             note (verify with the frequencies recorded in the semitone
1367
                                ;;             table below) so that the fraction_part of the pitch does
1368
                                ;;             indeed represent a fractional distance to the next note.
1369
        DEFB    $38             ;;end-calc   HL points to first byte of fp num on stack = middle frequency to generate
1370
 
1371
        POP     AF              ; A = octave displacement from middle C, 2's complement: -5<=A<=10
1372
        ADD     A,(HL)          ; increase exponent by A (equivalent to multiplying by 2^A)
1373
        LD      (HL),A          ;
1374
 
1375
        RST     28H             ;; FP-CALC
1376
        DEFB    $C0             ;;st-mem-0          ; store frequency in memory 0
1377
        DEFB    $02             ;;delete            ; remove from calc stack
1378
        DEFB    $31             ;;duplicate         ; duplicate duration (seconds)
1379
        DEFB    $38             ;;end-calc
1380
 
1381
        CALL    L1E94           ; routine FIND-INT1 ; FP duration to A
1382
        CP      $0B             ; if dur > 10 seconds,
1383
        JR      NC,L046C        ;   goto REPORT-B
1384
 
1385
        ;;; The following calculation finds the tone period for HL and the cycle count
1386
        ;;; for DE expected in the BEEPER subroutine.  From the example in the BEEPER comments,
1387
        ;;;
1388
        ;;; HL = ((fCPU / f) - 236) / 8 = fCPU/8/f - 236/8 = 437500/f -29.5
1389
        ;;; DE = duration * frequency - 1
1390
        ;;;
1391
        ;;; Note the different constant (30.125) used in the calculation of HL
1392
        ;;; below.  This is probably an error.
1393
 
1394
        RST     28H             ;; FP-CALC
1395
        DEFB    $E0             ;;get-mem-0                 ; push frequency
1396
        DEFB    $04             ;;multiply                  ; result1: #cycles = duration * frequency
1397
        DEFB    $E0             ;;get-mem-0                 ; push frequency
1398
        DEFB    $34             ;;stk-data                  ; push constant
1399
        DEFB    $80             ;;Exponent $93, Bytes: 3    ; constant = 437500
1400
        DEFB    $43,$55,$9F,$80 ;;($55,$9F,$80,$00)
1401
        DEFB    $01             ;;exchange                  ; frequency on top
1402
        DEFB    $05             ;;division                  ; 437500 / frequency
1403
        DEFB    $34             ;;stk-data                  ; push constant
1404
        DEFB    $35             ;;Exponent: $85, Bytes: 1   ; constant = 30.125
1405
        DEFB    $71             ;;($71,$00,$00,$00)
1406
        DEFB    $03             ;;subtract                  ; result2: tone_period(HL) = 437500 / freq - 30.125
1407
        DEFB    $38             ;;end-calc
1408
 
1409
        CALL    L1E99           ; routine FIND-INT2
1410
        PUSH    BC              ;   BC = tone_period(HL)
1411
        CALL    L1E99           ; routine FIND-INT2, BC = #cycles to generate
1412
        POP     HL              ; HL = tone period
1413
        LD      D,B             ;
1414
        LD      E,C             ; DE = #cycles
1415
        LD      A,D             ;
1416
        OR      E               ;
1417
        RET     Z               ; if duration = 0, skip BEEP and avoid 65536 cycle
1418
                                ;   boondoggle that would occur next
1419
        DEC     DE              ; DE = #cycles - 1
1420
        JP      L03B5           ; to BEEPER
1421
 
1422
; ---
1423
 
1424
 
1425
;; REPORT-B
1426
L046C:  RST     08H             ; ERROR-1
1427
        DEFB    $0A             ; Error Report: Integer out of range
1428
 
1429
 
1430
 
1431
; ---------------------
1432
; THE 'SEMI-TONE' TABLE
1433
; ---------------------
1434
;
1435
;   Holds frequencies corresponding to semitones in middle octave.
1436
;   To move n octaves higher or lower, frequencies are multiplied by 2^n.
1437
 
1438
;; semi-tone         five byte fp         decimal freq     note (middle)
1439
L046E:  DEFB    $89, $02, $D0, $12, $86;  261.625565290         C
1440
        DEFB    $89, $0A, $97, $60, $75;  277.182631135         C#
1441
        DEFB    $89, $12, $D5, $17, $1F;  293.664768100         D
1442
        DEFB    $89, $1B, $90, $41, $02;  311.126983881         D#
1443
        DEFB    $89, $24, $D0, $53, $CA;  329.627557039         E
1444
        DEFB    $89, $2E, $9D, $36, $B1;  349.228231549         F
1445
        DEFB    $89, $38, $FF, $49, $3E;  369.994422674         F#
1446
        DEFB    $89, $43, $FF, $6A, $73;  391.995436072         G
1447
        DEFB    $89, $4F, $A7, $00, $54;  415.304697513         G#
1448
        DEFB    $89, $5C, $00, $00, $00;  440.000000000         A
1449
        DEFB    $89, $69, $14, $F6, $24;  466.163761616         A#
1450
        DEFB    $89, $76, $F1, $10, $05;  493.883301378         B
1451
 
1452
 
1453
;   "Music is the hidden mathematical endeavour of a soul unconscious it
1454
;    is calculating" - Gottfried Wilhelm Liebnitz 1646 - 1716
1455
 
1456
 
1457
;****************************************
1458
;** Part 4. CASSETTE HANDLING ROUTINES **
1459
;****************************************
1460
 
1461
;   These routines begin with the service routines followed by a single
1462
;   command entry point.
1463
;   The first of these service routines is a curiosity.
1464
 
1465
; -----------------------
1466
; THE 'ZX81 NAME' ROUTINE
1467
; -----------------------
1468
;   This routine fetches a filename in ZX81 format and is not used by the
1469
;   cassette handling routines in this ROM.
1470
 
1471
;; zx81-name
1472
L04AA:  CALL    L24FB           ; routine SCANNING to evaluate expression.
1473
        LD      A,($5C3B)       ; fetch system variable FLAGS.
1474
        ADD     A,A             ; test bit 7 - syntax, bit 6 - result type.
1475
        JP      M,L1C8A         ; to REPORT-C if not string result
1476
                                ; 'Nonsense in BASIC'.
1477
 
1478
        POP     HL              ; drop return address.
1479
        RET     NC              ; return early if checking syntax.
1480
 
1481
        PUSH    HL              ; re-save return address.
1482
        CALL    L2BF1           ; routine STK-FETCH fetches string parameters.
1483
        LD      H,D             ; transfer start of filename
1484
        LD      L,E             ; to the HL register.
1485
        DEC     C               ; adjust to point to last character and
1486
        RET     M               ; return if the null string.
1487
                                ; or multiple of 256!
1488
 
1489
        ADD     HL,BC           ; find last character of the filename.
1490
                                ; and also clear carry.
1491
        SET     7,(HL)          ; invert it.
1492
        RET                     ; return.
1493
 
1494
; =========================================
1495
;
1496
; PORT 254 ($FE)
1497
;
1498
;                      spk mic { border  }
1499
;          ___ ___ ___ ___ ___ ___ ___ ___
1500
; PORT    |   |   |   |   |   |   |   |   |
1501
; 254     |   |   |   |   |   |   |   |   |
1502
; $FE     |___|___|___|___|___|___|___|___|
1503
;           7   6   5   4   3   2   1   0
1504
;
1505
 
1506
; ----------------------------------
1507
; Save header and program/data bytes
1508
; ----------------------------------
1509
;   This routine saves a section of data. It is called from SA-CTRL to save the
1510
;   seventeen bytes of header data. It is also the exit route from that routine
1511
;   when it is set up to save the actual data.
1512
;   On entry -
1513
;   HL points to start of data.
1514
;   IX points to descriptor.
1515
;   The accumulator is set to  $00 for a header, $FF for data.
1516
 
1517
;; SA-BYTES
1518
L04C2:  LD      HL,L053F        ; address: SA/LD-RET
1519
        PUSH    HL              ; is pushed as common exit route.
1520
                                ; however there is only one non-terminal exit
1521
                                ; point.
1522
 
1523
        LD      HL,$1F80        ; a timing constant H=$1F, L=$80
1524
                                ; inner and outer loop counters
1525
                                ; a five second lead-in is used for a header.
1526
 
1527
        BIT     7,A             ; test one bit of accumulator.
1528
                                ; (AND A ?)
1529
        JR      Z,L04D0         ; skip to SA-FLAG if a header is being saved.
1530
 
1531
;   else is data bytes and a shorter lead-in is used.
1532
 
1533
        LD      HL,$0C98        ; another timing value H=$0C, L=$98.
1534
                                ; a two second lead-in is used for the data.
1535
 
1536
 
1537
;; SA-FLAG
1538
L04D0:  EX      AF,AF'          ; save flag
1539
        INC     DE              ; increase length by one.
1540
        DEC     IX              ; decrease start.
1541
 
1542
        DI                      ; disable interrupts
1543
 
1544
        LD      A,$02           ; select red for border, microphone bit on.
1545
        LD      B,A             ; also does as an initial slight counter value.
1546
 
1547
;; SA-LEADER
1548
L04D8:  DJNZ    L04D8           ; self loop to SA-LEADER for delay.
1549
                                ; after initial loop, count is $A4 (or $A3)
1550
 
1551
        OUT     ($FE),A         ; output byte $02/$0D to tape port.
1552
 
1553
        XOR     $0F             ; switch from RED (mic on) to CYAN (mic off).
1554
 
1555
        LD      B,$A4           ; hold count. also timed instruction.
1556
 
1557
        DEC     L               ; originally $80 or $98.
1558
                                ; but subsequently cycles 256 times.
1559
        JR      NZ,L04D8        ; back to SA-LEADER until L is zero.
1560
 
1561
;   the outer loop is counted by H
1562
 
1563
        DEC     B               ; decrement count
1564
        DEC     H               ; originally  twelve or thirty-one.
1565
        JP      P,L04D8         ; back to SA-LEADER until H becomes $FF
1566
 
1567
;   now send a sync pulse. At this stage mic is off and A holds value
1568
;   for mic on.
1569
;   A sync pulse is much shorter than the steady pulses of the lead-in.
1570
 
1571
        LD      B,$2F           ; another short timed delay.
1572
 
1573
;; SA-SYNC-1
1574
L04EA:  DJNZ    L04EA           ; self loop to SA-SYNC-1
1575
 
1576
        OUT     ($FE),A         ; switch to mic on and red.
1577
        LD      A,$0D           ; prepare mic off - cyan
1578
        LD      B,$37           ; another short timed delay.
1579
 
1580
;; SA-SYNC-2
1581
L04F2:  DJNZ    L04F2           ; self loop to SA-SYNC-2
1582
 
1583
        OUT     ($FE),A         ; output mic off, cyan border.
1584
        LD      BC,$3B0E        ; B=$3B time(*), C=$0E, YELLOW, MIC OFF.
1585
 
1586
;
1587
 
1588
        EX      AF,AF'          ; restore saved flag
1589
                                ; which is 1st byte to be saved.
1590
 
1591
        LD      L,A             ; and transfer to L.
1592
                                ; the initial parity is A, $FF or $00.
1593
        JP      L0507           ; JUMP forward to SA-START     ->
1594
                                ; the mid entry point of loop.
1595
 
1596
; -------------------------
1597
;   During the save loop a parity byte is maintained in H.
1598
;   the save loop begins by testing if reduced length is zero and if so
1599
;   the final parity byte is saved reducing count to $FFFF.
1600
 
1601
;; SA-LOOP
1602
L04FE:  LD      A,D             ; fetch high byte
1603
        OR      E               ; test against low byte.
1604
        JR      Z,L050E         ; forward to SA-PARITY if zero.
1605
 
1606
        LD      L,(IX+$00)      ; load currently addressed byte to L.
1607
 
1608
;; SA-LOOP-P
1609
L0505:  LD      A,H             ; fetch parity byte.
1610
        XOR     L               ; exclusive or with new byte.
1611
 
1612
; -> the mid entry point of loop.
1613
 
1614
;; SA-START
1615
L0507:  LD      H,A             ; put parity byte in H.
1616
        LD      A,$01           ; prepare blue, mic=on.
1617
        SCF                     ; set carry flag ready to rotate in.
1618
        JP      L0525           ; JUMP forward to SA-8-BITS            -8->
1619
 
1620
; ---
1621
 
1622
;; SA-PARITY
1623
L050E:  LD      L,H             ; transfer the running parity byte to L and
1624
        JR      L0505           ; back to SA-LOOP-P
1625
                                ; to output that byte before quitting normally.
1626
 
1627
; ---
1628
 
1629
;   The entry point to save yellow part of bit.
1630
;   A bit consists of a period with mic on and blue border followed by
1631
;   a period of mic off with yellow border.
1632
;   Note. since the DJNZ instruction does not affect flags, the zero flag is
1633
;   used to indicate which of the two passes is in effect and the carry
1634
;   maintains the state of the bit to be saved.
1635
 
1636
;; SA-BIT-2
1637
L0511:  LD      A,C             ; fetch 'mic on and yellow' which is
1638
                                ; held permanently in C.
1639
        BIT     7,B             ; set the zero flag. B holds $3E.
1640
 
1641
;   The entry point to save 1 entire bit. For first bit B holds $3B(*).
1642
;   Carry is set if saved bit is 1. zero is reset NZ on entry.
1643
 
1644
;; SA-BIT-1
1645
L0514:  DJNZ    L0514           ; self loop for delay to SA-BIT-1
1646
 
1647
        JR      NC,L051C        ; forward to SA-OUT if bit is 0.
1648
 
1649
;   but if bit is 1 then the mic state is held for longer.
1650
 
1651
        LD      B,$42           ; set timed delay. (66 decimal)
1652
 
1653
;; SA-SET
1654
L051A:  DJNZ    L051A           ; self loop to SA-SET
1655
                                ; (roughly an extra 66*13 clock cycles)
1656
 
1657
;; SA-OUT
1658
L051C:  OUT     ($FE),A         ; blue and mic on OR  yellow and mic off.
1659
 
1660
        LD      B,$3E           ; set up delay
1661
        JR      NZ,L0511        ; back to SA-BIT-2 if zero reset NZ (first pass)
1662
 
1663
;   proceed when the blue and yellow bands have been output.
1664
 
1665
        DEC     B               ; change value $3E to $3D.
1666
        XOR     A               ; clear carry flag (ready to rotate in).
1667
        INC     A               ; reset zero flag i.e. NZ.
1668
 
1669
; -8->
1670
 
1671
;; SA-8-BITS
1672
L0525:  RL      L               ; rotate left through carry
1673
                                ; C<76543210
1674
        JP      NZ,L0514        ; JUMP back to SA-BIT-1
1675
                                ; until all 8 bits done.
1676
 
1677
;   when the initial set carry is passed out again then a byte is complete.
1678
 
1679
        DEC     DE              ; decrease length
1680
        INC     IX              ; increase byte pointer
1681
        LD      B,$31           ; set up timing.
1682
 
1683
        LD      A,$7F           ; test the space key and
1684
        IN      A,($FE)         ; return to common exit (to restore border)
1685
        RRA                     ; if a space is pressed
1686
        RET     NC              ; return to SA/LD-RET.   - - >
1687
 
1688
;   now test if byte counter has reached $FFFF.
1689
 
1690
        LD      A,D             ; fetch high byte
1691
        INC     A               ; increment.
1692
        JP      NZ,L04FE        ; JUMP to SA-LOOP if more bytes.
1693
 
1694
        LD      B,$3B           ; a final delay.
1695
 
1696
;; SA-DELAY
1697
L053C:  DJNZ    L053C           ; self loop to SA-DELAY
1698
 
1699
        RET                     ; return - - >
1700
 
1701
; ------------------------------
1702
; THE 'SAVE/LOAD RETURN' ROUTINE
1703
; ------------------------------
1704
;   The address of this routine is pushed on the stack prior to any load/save
1705
;   operation and it handles normal completion with the restoration of the
1706
;   border and also abnormal termination when the break key, or to be more
1707
;   precise the space key is pressed during a tape operation.
1708
;
1709
; - - >
1710
 
1711
;; SA/LD-RET
1712
L053F:  PUSH    AF              ; preserve accumulator throughout.
1713
        LD      A,($5C48)       ; fetch border colour from BORDCR.
1714
        AND     $38             ; mask off paper bits.
1715
        RRCA                    ; rotate
1716
        RRCA                    ; to the
1717
        RRCA                    ; range 0-7.
1718
 
1719
        OUT     ($FE),A         ; change the border colour.
1720
 
1721
        LD      A,$7F           ; read from port address $7FFE the
1722
        IN      A,($FE)         ; row with the space key at outside.
1723
 
1724
        RRA                     ; test for space key pressed.
1725
        EI                      ; enable interrupts
1726
        JR      C,L0554         ; forward to SA/LD-END if not
1727
 
1728
 
1729
;; REPORT-Da
1730
L0552:  RST     08H             ; ERROR-1
1731
        DEFB    $0C             ; Error Report: BREAK - CONT repeats
1732
 
1733
; ---
1734
 
1735
;; SA/LD-END
1736
L0554:  POP     AF              ; restore the accumulator.
1737
        RET                     ; return.
1738
 
1739
; ------------------------------------
1740
; Load header or block of information
1741
; ------------------------------------
1742
;   This routine is used to load bytes and on entry A is set to $00 for a
1743
;   header or to $FF for data.  IX points to the start of receiving location
1744
;   and DE holds the length of bytes to be loaded. If, on entry the carry flag
1745
;   is set then data is loaded, if reset then it is verified.
1746
 
1747
;; LD-BYTES
1748
L0556:  INC     D               ; reset the zero flag without disturbing carry.
1749
        EX      AF,AF'          ; preserve entry flags.
1750
        DEC     D               ; restore high byte of length.
1751
 
1752
        DI                      ; disable interrupts
1753
 
1754
        LD      A,$0F           ; make the border white and mic off.
1755
        OUT     ($FE),A         ; output to port.
1756
 
1757
        LD      HL,L053F        ; Address: SA/LD-RET
1758
        PUSH    HL              ; is saved on stack as terminating routine.
1759
 
1760
;   the reading of the EAR bit (D6) will always be preceded by a test of the
1761
;   space key (D0), so store the initial post-test state.
1762
 
1763
        IN      A,($FE)         ; read the ear state - bit 6.
1764
        RRA                     ; rotate to bit 5.
1765
        AND     $20             ; isolate this bit.
1766
        OR      $02             ; combine with red border colour.
1767
        LD      C,A             ; and store initial state long-term in C.
1768
        CP      A               ; set the zero flag.
1769
 
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 sync 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 sync 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 sync 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 the 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     L1ADF + 1 % 256 ; 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
        DEFB    $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
        DEFB    $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
 
2248
;   if there are more characters after CODE expect start and possibly length.
2249
 
2250
;; SA-CODE-1
2251
L06E1:  CALL    L1C82           ; routine EXPT-1NUM checks for numeric
2252
                                ; expression and stacks it in run-time.
2253
 
2254
        RST     18H             ; GET-CHAR
2255
        CP      $2C             ; does a comma follow ?
2256
        JR      Z,L06F5         ; forward if so to SA-CODE-3
2257
 
2258
;   else allow saved code to be loaded to a specified address.
2259
 
2260
        LD      A,($5C74)       ; fetch command from T_ADDR.
2261
        AND     A               ; is the command SAVE which requires length ?
2262
        JP      Z,L1C8A         ; jump to REPORT-C if so.
2263
                                ; 'Nonsense in BASIC'
2264
 
2265
;   the command LOAD code may rejoin here with zero stacked as start.
2266
 
2267
;; SA-CODE-2
2268
L06F0:  CALL    L1CE6           ; routine USE-ZERO stacks zero for length.
2269
        JR      L06F9           ; forward to SA-CODE-4
2270
 
2271
; ---
2272
;   the branch was here with SAVE CODE start,
2273
 
2274
;; SA-CODE-3
2275
L06F5:  RST     20H             ; NEXT-CHAR advances character address.
2276
        CALL    L1C82           ; routine EXPT-1NUM checks for expression
2277
                                ; and stacks in run-time.
2278
 
2279
;   paths converge here and nothing must follow.
2280
 
2281
;; SA-CODE-4
2282
L06F9:  CALL    L1BEE           ; routine CHECK-END errors with extraneous
2283
                                ; characters and quits if checking syntax.
2284
 
2285
;   in run-time there are two 16-bit parameters on the calculator stack.
2286
 
2287
        CALL    L1E99           ; routine FIND-INT2 gets length.
2288
        LD      (IX+$0B),C      ; place length
2289
        LD      (IX+$0C),B      ; in descriptor.
2290
        CALL    L1E99           ; routine FIND-INT2 gets start.
2291
        LD      (IX+$0D),C      ; place start
2292
        LD      (IX+$0E),B      ; in descriptor.
2293
        LD      H,B             ; transfer the
2294
        LD      L,C             ; start to HL also.
2295
 
2296
;; SA-TYPE-3
2297
L0710:  LD      (IX+$00),$03    ; place type 3 - code in descriptor.
2298
        JR      L075A           ; forward to SA-ALL.
2299
 
2300
; ---
2301
;   the branch was here with BASIC to consider an optional auto-start line
2302
;   number.
2303
 
2304
;; SA-LINE
2305
L0716:  CP      $CA             ; is character the token 'LINE' ?
2306
        JR      Z,L0723         ; forward to SA-LINE-1 if so.
2307
 
2308
;   else all possibilities have been considered and nothing must follow.
2309
 
2310
        CALL    L1BEE           ; routine CHECK-END
2311
 
2312
;   continue in run-time to save BASIC without auto-start.
2313
 
2314
        LD      (IX+$0E),$80    ; place high line number in descriptor to
2315
                                ; disable auto-start.
2316
        JR      L073A           ; forward to SA-TYPE-0 to save program.
2317
 
2318
; ---
2319
;   the branch was here to consider auto-start.
2320
 
2321
;; SA-LINE-1
2322
L0723:  LD      A,($5C74)       ; fetch command from T_ADDR
2323
        AND     A               ; test for SAVE.
2324
        JP      NZ,L1C8A        ; jump forward to REPORT-C with anything else.
2325
                                ; 'Nonsense in BASIC'
2326
 
2327
;
2328
 
2329
        RST     20H             ; NEXT-CHAR
2330
        CALL    L1C82           ; routine EXPT-1NUM checks for numeric
2331
                                ; expression and stacks in run-time.
2332
        CALL    L1BEE           ; routine CHECK-END quits if syntax path.
2333
        CALL    L1E99           ; routine FIND-INT2 fetches the numeric
2334
                                ; expression.
2335
        LD      (IX+$0D),C      ; place the auto-start
2336
        LD      (IX+$0E),B      ; line number in the descriptor.
2337
 
2338
;   Note. this isn't checked, but is subsequently handled by the system.
2339
;   If the user typed 40000 instead of 4000 then it won't auto-start
2340
;   at line 4000, or indeed, at all.
2341
 
2342
;   continue to save program and any variables.
2343
 
2344
;; SA-TYPE-0
2345
L073A:  LD      (IX+$00),$00    ; place type zero - program in descriptor.
2346
        LD      HL,($5C59)      ; fetch E_LINE to HL.
2347
        LD      DE,($5C53)      ; fetch PROG to DE.
2348
        SCF                     ; set carry flag to calculate from end of
2349
                                ; variables E_LINE -1.
2350
        SBC     HL,DE           ; subtract to give total length.
2351
 
2352
        LD      (IX+$0B),L      ; place total length
2353
        LD      (IX+$0C),H      ; in descriptor.
2354
        LD      HL,($5C4B)      ; load HL from system variable VARS
2355
        SBC     HL,DE           ; subtract to give program length.
2356
        LD      (IX+$0F),L      ; place length of program
2357
        LD      (IX+$10),H      ; in the descriptor.
2358
        EX      DE,HL           ; start to HL, length to DE.
2359
 
2360
;; SA-ALL
2361
L075A:  LD      A,($5C74)       ; fetch command from T_ADDR
2362
        AND     A               ; test for zero - SAVE.
2363
        JP      Z,L0970         ; jump forward to SA-CONTRL with SAVE  ->
2364
 
2365
; ---
2366
;   continue with LOAD, MERGE and VERIFY.
2367
 
2368
        PUSH    HL              ; save start.
2369
        LD      BC,$0011        ; prepare to add seventeen
2370
        ADD     IX,BC           ; to point IX at second descriptor.
2371
 
2372
;; LD-LOOK-H
2373
L0767:  PUSH    IX              ; save IX
2374
        LD      DE,$0011        ; seventeen bytes
2375
        XOR     A               ; reset zero flag
2376
        SCF                     ; set carry flag
2377
        CALL    L0556           ; routine LD-BYTES loads a header from tape
2378
                                ; to second descriptor.
2379
        POP     IX              ; restore IX.
2380
        JR      NC,L0767        ; loop back to LD-LOOK-H until header found.
2381
 
2382
        LD      A,$FE           ; select system channel 'S'
2383
        CALL    L1601           ; routine CHAN-OPEN opens it.
2384
 
2385
        LD      (IY+$52),$03    ; set SCR_CT to 3 lines.
2386
 
2387
        LD      C,$80           ; C has bit 7 set to indicate type mismatch as
2388
                                ; a default startpoint.
2389
 
2390
        LD      A,(IX+$00)      ; fetch loaded header type to A
2391
        CP      (IX-$11)        ; compare with expected type.
2392
        JR      NZ,L078A        ; forward to LD-TYPE with mis-match.
2393
 
2394
        LD      C,$F6           ; set C to minus ten - will count characters
2395
                                ; up to zero.
2396
 
2397
;; LD-TYPE
2398
L078A:  CP      $04             ; check if type in acceptable range 0 - 3.
2399
        JR      NC,L0767        ; back to LD-LOOK-H with 4 and over.
2400
 
2401
;   else A indicates type 0-3.
2402
 
2403
        LD      DE,L09C0        ; address base of last 4 tape messages
2404
        PUSH    BC              ; save BC
2405
        CALL    L0C0A           ; routine PO-MSG outputs relevant message.
2406
                                ; Note. all messages have a leading newline.
2407
        POP     BC              ; restore BC
2408
 
2409
        PUSH    IX              ; transfer IX,
2410
        POP     DE              ; the 2nd descriptor, to DE.
2411
        LD      HL,$FFF0        ; prepare minus seventeen.
2412
        ADD     HL,DE           ; add to point HL to 1st descriptor.
2413
        LD      B,$0A           ; the count will be ten characters for the
2414
                                ; filename.
2415
 
2416
        LD      A,(HL)          ; fetch first character and test for
2417
        INC     A               ; value 255.
2418
        JR      NZ,L07A6        ; forward to LD-NAME if not the wildcard.
2419
 
2420
;   but if it is the wildcard, then add ten to C which is minus ten for a type
2421
;   match or -128 for a type mismatch. Although characters have to be counted
2422
;   bit 7 of C will not alter from state set here.
2423
 
2424
        LD      A,C             ; transfer $F6 or $80 to A
2425
        ADD     A,B             ; add $0A
2426
        LD      C,A             ; place result, zero or -118, in C.
2427
 
2428
;   At this point we have either a type mismatch, a wildcard match or ten
2429
;   characters to be counted. The characters must be shown on the screen.
2430
 
2431
;; LD-NAME
2432
L07A6:  INC     DE              ; address next input character
2433
        LD      A,(DE)          ; fetch character
2434
        CP      (HL)            ; compare to expected
2435
        INC     HL              ; address next expected character
2436
        JR      NZ,L07AD        ; forward to LD-CH-PR with mismatch
2437
 
2438
        INC     C               ; increment matched character count
2439
 
2440
;; LD-CH-PR
2441
L07AD:  RST     10H             ; PRINT-A prints character
2442
        DJNZ    L07A6           ; loop back to LD-NAME for ten characters.
2443
 
2444
;   if ten characters matched and the types previously matched then C will
2445
;   now hold zero.
2446
 
2447
        BIT     7,C             ; test if all matched
2448
        JR      NZ,L0767        ; back to LD-LOOK-H if not
2449
 
2450
;   else print a terminal carriage return.
2451
 
2452
        LD      A,$0D           ; prepare carriage return.
2453
        RST     10H             ; PRINT-A outputs it.
2454
 
2455
;   The various control routines for LOAD, VERIFY and MERGE are executed
2456
;   during the one-second gap following the header on tape.
2457
 
2458
        POP     HL              ; restore xx
2459
        LD      A,(IX+$00)      ; fetch incoming type
2460
        CP      $03             ; compare with CODE
2461
        JR      Z,L07CB         ; forward to VR-CONTRL if it is CODE.
2462
 
2463
;  type is a program or an array.
2464
 
2465
        LD      A,($5C74)       ; fetch command from T_ADDR
2466
        DEC     A               ; was it LOAD ?
2467
        JP      Z,L0808         ; JUMP forward to LD-CONTRL if so to
2468
                                ; load BASIC or variables.
2469
 
2470
        CP      $02             ; was command MERGE ?
2471
        JP      Z,L08B6         ; jump forward to ME-CONTRL if so.
2472
 
2473
;   else continue into VERIFY control routine to verify.
2474
 
2475
; ----------------------------
2476
; THE 'VERIFY CONTROL' ROUTINE
2477
; ----------------------------
2478
;   There are two branches to this routine.
2479
;   1) From above to verify a program or array
2480
;   2) from earlier with no carry to load or verify code.
2481
 
2482
;; VR-CONTRL
2483
L07CB:  PUSH    HL              ; save pointer to data.
2484
        LD      L,(IX-$06)      ; fetch length of old data
2485
        LD      H,(IX-$05)      ; to HL.
2486
        LD      E,(IX+$0B)      ; fetch length of new data
2487
        LD      D,(IX+$0C)      ; to DE.
2488
        LD      A,H             ; check length of old
2489
        OR      L               ; for zero.
2490
        JR      Z,L07E9         ; forward to VR-CONT-1 if length unspecified
2491
                                ; e.g. LOAD "x" CODE
2492
 
2493
;   as opposed to, say, LOAD 'x' CODE 32768,300.
2494
 
2495
        SBC     HL,DE           ; subtract the two lengths.
2496
        JR      C,L0806         ; forward to REPORT-R if the length on tape is
2497
                                ; larger than that specified in command.
2498
                                ; 'Tape loading error'
2499
 
2500
        JR      Z,L07E9         ; forward to VR-CONT-1 if lengths match.
2501
 
2502
;   a length on tape shorter than expected is not allowed for CODE
2503
 
2504
        LD      A,(IX+$00)      ; else fetch type from tape.
2505
        CP      $03             ; is it CODE ?
2506
        JR      NZ,L0806        ; forward to REPORT-R if so
2507
                                ; 'Tape loading error'
2508
 
2509
;; VR-CONT-1
2510
L07E9:  POP     HL              ; pop pointer to data
2511
        LD      A,H             ; test for zero
2512
        OR      L               ; e.g. LOAD 'x' CODE
2513
        JR      NZ,L07F4        ; forward to VR-CONT-2 if destination specified.
2514
 
2515
        LD      L,(IX+$0D)      ; else use the destination in the header
2516
        LD      H,(IX+$0E)      ; and load code at address saved from.
2517
 
2518
;; VR-CONT-2
2519
L07F4:  PUSH    HL              ; push pointer to start of data block.
2520
        POP     IX              ; transfer to IX.
2521
        LD      A,($5C74)       ; fetch reduced command from T_ADDR
2522
        CP      $02             ; is it VERIFY ?
2523
        SCF                     ; prepare a set carry flag
2524
        JR      NZ,L0800        ; skip to VR-CONT-3 if not
2525
 
2526
        AND     A               ; clear carry flag for VERIFY so that
2527
                                ; data is not loaded.
2528
 
2529
;; VR-CONT-3
2530
L0800:  LD      A,$FF           ; signal data block to be loaded
2531
 
2532
; -----------------
2533
; Load a data block
2534
; -----------------
2535
;   This routine is called from 3 places other than above to load a data block.
2536
;   In all cases the accumulator is first set to $FF so the routine could be
2537
;   called at the previous instruction.
2538
 
2539
;; LD-BLOCK
2540
L0802:  CALL    L0556           ; routine LD-BYTES
2541
        RET     C               ; return if successful.
2542
 
2543
 
2544
;; REPORT-R
2545
L0806:  RST     08H             ; ERROR-1
2546
        DEFB    $1A             ; Error Report: Tape loading error
2547
 
2548
; --------------------------
2549
; THE 'LOAD CONTROL' ROUTINE
2550
; --------------------------
2551
;   This branch is taken when the command is LOAD with type 0, 1 or 2.
2552
 
2553
;; LD-CONTRL
2554
L0808:  LD      E,(IX+$0B)      ; fetch length of found data block
2555
        LD      D,(IX+$0C)      ; from 2nd descriptor.
2556
        PUSH    HL              ; save destination
2557
        LD      A,H             ; test for zero
2558
        OR      L               ;
2559
        JR      NZ,L0819        ; forward if not to LD-CONT-1
2560
 
2561
        INC     DE              ; increase length
2562
        INC     DE              ; for letter name
2563
        INC     DE              ; and 16-bit length
2564
        EX      DE,HL           ; length to HL,
2565
        JR      L0825           ; forward to LD-CONT-2
2566
 
2567
; ---
2568
 
2569
;; LD-CONT-1
2570
L0819:  LD      L,(IX-$06)      ; fetch length from
2571
        LD      H,(IX-$05)      ; the first header.
2572
        EX      DE,HL           ;
2573
        SCF                     ; set carry flag
2574
        SBC     HL,DE           ;
2575
        JR      C,L082E         ; to LD-DATA
2576
 
2577
;; LD-CONT-2
2578
L0825:  LD      DE,$0005        ; allow overhead of five bytes.
2579
        ADD     HL,DE           ; add in the difference in data lengths.
2580
        LD      B,H             ; transfer to
2581
        LD      C,L             ; the BC register pair
2582
        CALL    L1F05           ; routine TEST-ROOM fails if not enough room.
2583
 
2584
;; LD-DATA
2585
L082E:  POP     HL              ; pop destination
2586
        LD      A,(IX+$00)      ; fetch type 0, 1 or 2.
2587
        AND     A               ; test for program and variables.
2588
        JR      Z,L0873         ; forward if so to LD-PROG
2589
 
2590
;   the type is a numeric or string array.
2591
 
2592
        LD      A,H             ; test the destination for zero
2593
        OR      L               ; indicating variable does not already exist.
2594
        JR      Z,L084C         ; forward if so to LD-DATA-1
2595
 
2596
;   else the destination is the first dimension within the array structure
2597
 
2598
        DEC     HL              ; address high byte of total length
2599
        LD      B,(HL)          ; transfer to B.
2600
        DEC     HL              ; address low byte of total length.
2601
        LD      C,(HL)          ; transfer to C.
2602
        DEC     HL              ; point to letter of variable.
2603
        INC     BC              ; adjust length to
2604
        INC     BC              ; include these
2605
        INC     BC              ; three bytes also.
2606
        LD      ($5C5F),IX      ; save header pointer in X_PTR.
2607
        CALL    L19E8           ; routine RECLAIM-2 reclaims the old variable
2608
                                ; sliding workspace including the two headers
2609
                                ; downwards.
2610
        LD      IX,($5C5F)      ; reload IX from X_PTR which will have been
2611
                                ; adjusted down by POINTERS routine.
2612
 
2613
;; LD-DATA-1
2614
L084C:  LD      HL,($5C59)      ; address E_LINE
2615
        DEC     HL              ; now point to the $80 variables end-marker.
2616
        LD      C,(IX+$0B)      ; fetch new data length
2617
        LD      B,(IX+$0C)      ; from 2nd header.
2618
        PUSH    BC              ; * save it.
2619
        INC     BC              ; adjust the
2620
        INC     BC              ; length to include
2621
        INC     BC              ; letter name and total length.
2622
        LD      A,(IX-$03)      ; fetch letter name from old header.
2623
        PUSH    AF              ; preserve accumulator though not corrupted.
2624
 
2625
        CALL    L1655           ; routine MAKE-ROOM creates space for variable
2626
                                ; sliding workspace up. IX no longer addresses
2627
                                ; anywhere meaningful.
2628
        INC     HL              ; point to first new location.
2629
 
2630
        POP     AF              ; fetch back the letter name.
2631
        LD      (HL),A          ; place in first new location.
2632
        POP     DE              ; * pop the data length.
2633
        INC     HL              ; address 2nd location
2634
        LD      (HL),E          ; store low byte of length.
2635
        INC     HL              ; address next.
2636
        LD      (HL),D          ; store high byte.
2637
        INC     HL              ; address start of data.
2638
        PUSH    HL              ; transfer address
2639
        POP     IX              ; to IX register pair.
2640
        SCF                     ; set carry flag indicating load not verify.
2641
        LD      A,$FF           ; signal data not header.
2642
        JP      L0802           ; JUMP back to LD-BLOCK
2643
 
2644
; -----------------
2645
;   the branch is here when a program as opposed to an array is to be loaded.
2646
 
2647
;; LD-PROG
2648
L0873:  EX      DE,HL           ; transfer dest to DE.
2649
        LD      HL,($5C59)      ; address E_LINE
2650
        DEC     HL              ; now variables end-marker.
2651
        LD      ($5C5F),IX      ; place the IX header pointer in X_PTR
2652
        LD      C,(IX+$0B)      ; get new length
2653
        LD      B,(IX+$0C)      ; from 2nd header
2654
        PUSH    BC              ; and save it.
2655
 
2656
        CALL    L19E5           ; routine RECLAIM-1 reclaims program and vars.
2657
                                ; adjusting X-PTR.
2658
 
2659
        POP     BC              ; restore new length.
2660
        PUSH    HL              ; * save start
2661
        PUSH    BC              ; ** and length.
2662
 
2663
        CALL    L1655           ; routine MAKE-ROOM creates the space.
2664
 
2665
        LD      IX,($5C5F)      ; reload IX from adjusted X_PTR
2666
        INC     HL              ; point to start of new area.
2667
        LD      C,(IX+$0F)      ; fetch length of BASIC on tape
2668
        LD      B,(IX+$10)      ; from 2nd descriptor
2669
        ADD     HL,BC           ; add to address the start of variables.
2670
        LD      ($5C4B),HL      ; set system variable VARS
2671
 
2672
        LD      H,(IX+$0E)      ; fetch high byte of autostart line number.
2673
        LD      A,H             ; transfer to A
2674
        AND     $C0             ; test if greater than $3F.
2675
        JR      NZ,L08AD        ; forward to LD-PROG-1 if so with no autostart.
2676
 
2677
        LD      L,(IX+$0D)      ; else fetch the low byte.
2678
        LD      ($5C42),HL      ; set system variable to line number NEWPPC
2679
        LD      (IY+$0A),$00    ; set statement NSPPC to zero.
2680
 
2681
;; LD-PROG-1
2682
L08AD:  POP     DE              ; ** pop the length
2683
        POP     IX              ; * and start.
2684
        SCF                     ; set carry flag
2685
        LD      A,$FF           ; signal data as opposed to a header.
2686
        JP      L0802           ; jump back to LD-BLOCK
2687
 
2688
; ---------------------------
2689
; THE 'MERGE CONTROL' ROUTINE
2690
; ---------------------------
2691
;   the branch was here to merge a program and its variables or an array.
2692
;
2693
 
2694
;; ME-CONTRL
2695
L08B6:  LD      C,(IX+$0B)      ; fetch length
2696
        LD      B,(IX+$0C)      ; of data block on tape.
2697
        PUSH    BC              ; save it.
2698
        INC     BC              ; one for the pot.
2699
 
2700
        RST     30H             ; BC-SPACES creates room in workspace.
2701
                                ; HL addresses last new location.
2702
        LD      (HL),$80        ; place end-marker at end.
2703
        EX      DE,HL           ; transfer first location to HL.
2704
        POP     DE              ; restore length to DE.
2705
        PUSH    HL              ; save start.
2706
 
2707
        PUSH    HL              ; and transfer it
2708
        POP     IX              ; to IX register.
2709
        SCF                     ; set carry flag to load data on tape.
2710
        LD      A,$FF           ; signal data not a header.
2711
        CALL    L0802           ; routine LD-BLOCK loads to workspace.
2712
        POP     HL              ; restore first location in workspace to HL.
2713
X08CE   LD      DE,($5C53)      ; set DE from system variable PROG.
2714
 
2715
;   now enter a loop to merge the data block in workspace with the program and
2716
;   variables.
2717
 
2718
;; ME-NEW-LP
2719
L08D2:  LD      A,(HL)          ; fetch next byte from workspace.
2720
        AND     $C0             ; compare with $3F.
2721
        JR      NZ,L08F0        ; forward to ME-VAR-LP if a variable or
2722
                                ; end-marker.
2723
 
2724
;   continue when HL addresses a BASIC line number.
2725
 
2726
;; ME-OLD-LP
2727
L08D7:  LD      A,(DE)          ; fetch high byte from program area.
2728
        INC     DE              ; bump prog address.
2729
        CP      (HL)            ; compare with that in workspace.
2730
        INC     HL              ; bump workspace address.
2731
        JR      NZ,L08DF        ; forward to ME-OLD-L1 if high bytes don't match
2732
 
2733
        LD      A,(DE)          ; fetch the low byte of program line number.
2734
        CP      (HL)            ; compare with that in workspace.
2735
 
2736
;; ME-OLD-L1
2737
L08DF:  DEC     DE              ; point to start of
2738
        DEC     HL              ; respective lines again.
2739
        JR      NC,L08EB        ; forward to ME-NEW-L2 if line number in
2740
                                ; workspace is less than or equal to current
2741
                                ; program line as has to be added to program.
2742
 
2743
        PUSH    HL              ; else save workspace pointer.
2744
        EX      DE,HL           ; transfer prog pointer to HL
2745
        CALL    L19B8           ; routine NEXT-ONE finds next line in DE.
2746
        POP     HL              ; restore workspace pointer
2747
        JR      L08D7           ; back to ME-OLD-LP until destination position
2748
                                ; in program area found.
2749
 
2750
; ---
2751
;   the branch was here with an insertion or replacement point.
2752
 
2753
;; ME-NEW-L2
2754
L08EB:  CALL    L092C           ; routine ME-ENTER enters the line
2755
        JR      L08D2           ; loop back to ME-NEW-LP.
2756
 
2757
; ---
2758
;   the branch was here when the location in workspace held a variable.
2759
 
2760
;; ME-VAR-LP
2761
L08F0:  LD      A,(HL)          ; fetch first byte of workspace variable.
2762
        LD      C,A             ; copy to C also.
2763
        CP      $80             ; is it the end-marker ?
2764
        RET     Z               ; return if so as complete.  >>>>>
2765
 
2766
        PUSH    HL              ; save workspace area pointer.
2767
        LD      HL,($5C4B)      ; load HL with VARS - start of variables area.
2768
 
2769
;; ME-OLD-VP
2770
L08F9:  LD      A,(HL)          ; fetch first byte.
2771
        CP      $80             ; is it the end-marker ?
2772
        JR      Z,L0923         ; forward if so to ME-VAR-L2 to add
2773
                                ; variable at end of variables area.
2774
 
2775
        CP      C               ; compare with variable in workspace area.
2776
        JR      Z,L0909         ; forward to ME-OLD-V2 if a match to replace.
2777
 
2778
;   else entire variables area has to be searched.
2779
 
2780
;; ME-OLD-V1
2781
L0901:  PUSH    BC              ; save character in C.
2782
        CALL    L19B8           ; routine NEXT-ONE gets following variable
2783
                                ; address in DE.
2784
        POP     BC              ; restore character in C
2785
        EX      DE,HL           ; transfer next address to HL.
2786
        JR      L08F9           ; loop back to ME-OLD-VP
2787
 
2788
; ---
2789
;   the branch was here when first characters of name matched.
2790
 
2791
;; ME-OLD-V2
2792
L0909:  AND     $E0             ; keep bits 11100000
2793
        CP      $A0             ; compare   10100000 - a long-named variable.
2794
 
2795
        JR      NZ,L0921        ; forward to ME-VAR-L1 if just one-character.
2796
 
2797
;   but long-named variables have to be matched character by character.
2798
 
2799
        POP     DE              ; fetch workspace 1st character pointer
2800
        PUSH    DE              ; and save it on the stack again.
2801
        PUSH    HL              ; save variables area pointer on stack.
2802
 
2803
;; ME-OLD-V3
2804
L0912:  INC     HL              ; address next character in vars area.
2805
        INC     DE              ; address next character in workspace area.
2806
        LD      A,(DE)          ; fetch workspace character.
2807
        CP      (HL)            ; compare to variables character.
2808
        JR      NZ,L091E        ; forward to ME-OLD-V4 with a mismatch.
2809
 
2810
        RLA                     ; test if the terminal inverted character.
2811
        JR      NC,L0912        ; loop back to ME-OLD-V3 if more to test.
2812
 
2813
;   otherwise the long name matches in its entirety.
2814
 
2815
        POP     HL              ; restore pointer to first character of variable
2816
        JR      L0921           ; forward to ME-VAR-L1
2817
 
2818
; ---
2819
;   the branch is here when two characters don't match
2820
 
2821
;; ME-OLD-V4
2822
L091E:  POP     HL              ; restore the prog/vars pointer.
2823
        JR      L0901           ; back to ME-OLD-V1 to resume search.
2824
 
2825
; ---
2826
;   branch here when variable is to replace an existing one
2827
 
2828
;; ME-VAR-L1
2829
L0921:  LD      A,$FF           ; indicate a replacement.
2830
 
2831
;   this entry point is when A holds $80 indicating a new variable.
2832
 
2833
;; ME-VAR-L2
2834
L0923:  POP     DE              ; pop workspace pointer.
2835
        EX      DE,HL           ; now make HL workspace pointer, DE vars pointer
2836
        INC     A               ; zero flag set if replacement.
2837
        SCF                     ; set carry flag indicating a variable not a
2838
                                ; program line.
2839
        CALL    L092C           ; routine ME-ENTER copies variable in.
2840
        JR      L08F0           ; loop back to ME-VAR-LP
2841
 
2842
; ------------------------
2843
; Merge a Line or Variable
2844
; ------------------------
2845
;   A BASIC line or variable is inserted at the current point. If the line
2846
;   number or variable names match (zero flag set) then a replacement takes
2847
;   place.
2848
 
2849
;; ME-ENTER
2850
L092C:  JR      NZ,L093E        ; forward to ME-ENT-1 for insertion only.
2851
 
2852
;   but the program line or variable matches so old one is reclaimed.
2853
 
2854
        EX      AF,AF'          ; save flag??
2855
        LD      ($5C5F),HL      ; preserve workspace pointer in dynamic X_PTR
2856
        EX      DE,HL           ; transfer program dest pointer to HL.
2857
        CALL    L19B8           ; routine NEXT-ONE finds following location
2858
                                ; in program or variables area.
2859
        CALL    L19E8           ; routine RECLAIM-2 reclaims the space between.
2860
        EX      DE,HL           ; transfer program dest pointer back to DE.
2861
        LD      HL,($5C5F)      ; fetch adjusted workspace pointer from X_PTR
2862
        EX      AF,AF'          ; restore flags.
2863
 
2864
;   now the new line or variable is entered.
2865
 
2866
;; ME-ENT-1
2867
L093E:  EX      AF,AF'          ; save or re-save flags.
2868
        PUSH    DE              ; save dest pointer in prog/vars area.
2869
        CALL    L19B8           ; routine NEXT-ONE finds next in workspace.
2870
                                ; gets next in DE, difference in BC.
2871
                                ; prev addr in HL
2872
        LD      ($5C5F),HL      ; store pointer in X_PTR
2873
        LD      HL,($5C53)      ; load HL from system variable PROG
2874
        EX      (SP),HL         ; swap with prog/vars pointer on stack.
2875
        PUSH    BC              ; ** save length of new program line/variable.
2876
        EX      AF,AF'          ; fetch flags back.
2877
        JR      C,L0955         ; skip to ME-ENT-2 if variable
2878
 
2879
        DEC     HL              ; address location before pointer
2880
        CALL    L1655           ; routine MAKE-ROOM creates room for BASIC line
2881
        INC     HL              ; address next.
2882
        JR      L0958           ; forward to ME-ENT-3
2883
 
2884
; ---
2885
 
2886
;; ME-ENT-2
2887
L0955:  CALL    L1655           ; routine MAKE-ROOM creates room for variable.
2888
 
2889
;; ME-ENT-3
2890
L0958:  INC     HL              ; address next?
2891
 
2892
        POP     BC              ; ** pop length
2893
        POP     DE              ; * pop value for PROG which may have been
2894
                                ; altered by POINTERS if first line.
2895
        LD      ($5C53),DE      ; set PROG to original value.
2896
        LD      DE,($5C5F)      ; fetch adjusted workspace pointer from X_PTR
2897
        PUSH    BC              ; save length
2898
        PUSH    DE              ; and workspace pointer
2899
        EX      DE,HL           ; make workspace pointer source, prog/vars
2900
                                ; pointer the destination
2901
        LDIR                    ; copy bytes of line or variable into new area.
2902
        POP     HL              ; restore workspace pointer.
2903
        POP     BC              ; restore length.
2904
        PUSH    DE              ; save new prog/vars pointer.
2905
        CALL    L19E8           ; routine RECLAIM-2 reclaims the space used
2906
                                ; by the line or variable in workspace block
2907
                                ; as no longer required and space could be
2908
                                ; useful for adding more lines.
2909
        POP     DE              ; restore the prog/vars pointer
2910
        RET                     ; return.
2911
 
2912
; --------------------------
2913
; THE 'SAVE CONTROL' ROUTINE
2914
; --------------------------
2915
;   A branch from the main SAVE-ETC routine at SAVE-ALL.
2916
;   First the header data is saved. Then after a wait of 1 second
2917
;   the data itself is saved.
2918
;   HL points to start of data.
2919
;   IX points to start of descriptor.
2920
 
2921
;; SA-CONTRL
2922
L0970:  PUSH    HL              ; save start of data
2923
 
2924
        LD      A,$FD           ; select system channel 'S'
2925
        CALL    L1601           ; routine CHAN-OPEN
2926
 
2927
        XOR     A               ; clear to address table directly
2928
        LD      DE,L09A1        ; address: tape-msgs
2929
        CALL    L0C0A           ; routine PO-MSG -
2930
                                ; 'Start tape then press any key.'
2931
 
2932
        SET     5,(IY+$02)      ; TV_FLAG  - Signal lower screen requires
2933
                                ; clearing
2934
        CALL    L15D4           ; routine WAIT-KEY
2935
 
2936
        PUSH    IX              ; save pointer to descriptor.
2937
        LD      DE,$0011        ; there are seventeen bytes.
2938
        XOR     A               ; signal a header.
2939
        CALL    L04C2           ; routine SA-BYTES
2940
 
2941
        POP     IX              ; restore descriptor pointer.
2942
 
2943
        LD      B,$32           ; wait for a second - 50 interrupts.
2944
 
2945
;; SA-1-SEC
2946
L0991:  HALT                    ; wait for interrupt
2947
        DJNZ    L0991           ; back to SA-1-SEC until pause complete.
2948
 
2949
        LD      E,(IX+$0B)      ; fetch length of bytes from the
2950
        LD      D,(IX+$0C)      ; descriptor.
2951
 
2952
        LD      A,$FF           ; signal data bytes.
2953
 
2954
        POP     IX              ; retrieve pointer to start
2955
        JP      L04C2           ; jump back to SA-BYTES
2956
 
2957
 
2958
;   Arrangement of two headers in workspace.
2959
;   Originally IX addresses first location and only one header is required
2960
;   when saving.
2961
;
2962
;   OLD     NEW         PROG   DATA  DATA  CODE
2963
;   HEADER  HEADER             num   chr          NOTES.
2964
;   ------  ------      ----   ----  ----  ----   -----------------------------
2965
;   IX-$11  IX+$00      0      1     2     3      Type.
2966
;   IX-$10  IX+$01      x      x     x     x      F  ($FF if filename is null).
2967
;   IX-$0F  IX+$02      x      x     x     x      i
2968
;   IX-$0E  IX+$03      x      x     x     x      l
2969
;   IX-$0D  IX+$04      x      x     x     x      e
2970
;   IX-$0C  IX+$05      x      x     x     x      n
2971
;   IX-$0B  IX+$06      x      x     x     x      a
2972
;   IX-$0A  IX+$07      x      x     x     x      m
2973
;   IX-$09  IX+$08      x      x     x     x      e
2974
;   IX-$08  IX+$09      x      x     x     x      .
2975
;   IX-$07  IX+$0A      x      x     x     x      (terminal spaces).
2976
;   IX-$06  IX+$0B      lo     lo    lo    lo     Total
2977
;   IX-$05  IX+$0C      hi     hi    hi    hi     Length of datablock.
2978
;   IX-$04  IX+$0D      Auto   -     -     Start  Various
2979
;   IX-$03  IX+$0E      Start  a-z   a-z   addr   ($80 if no autostart).
2980
;   IX-$02  IX+$0F      lo     -     -     -      Length of Program
2981
;   IX-$01  IX+$10      hi     -     -     -      only i.e. without variables.
2982
;
2983
 
2984
 
2985
; ------------------------
2986
; Canned cassette messages
2987
; ------------------------
2988
;   The last-character-inverted Cassette messages.
2989
;   Starts with normal initial step-over byte.
2990
 
2991
;; tape-msgs
2992
L09A1:  DEFB    $80
2993
        DEFM    "Start tape, then press any key"
2994
L09C0:  DEFB    '.'+$80
2995
        DEFB    $0D
2996
        DEFM    "Program:"
2997
        DEFB    ' '+$80
2998
        DEFB    $0D
2999
        DEFM    "Number array:"
3000
        DEFB    ' '+$80
3001
        DEFB    $0D
3002
        DEFM    "Character array:"
3003
        DEFB    ' '+$80
3004
        DEFB    $0D
3005
        DEFM    "Bytes:"
3006
        DEFB    ' '+$80
3007
 
3008
 
3009
;**************************************************
3010
;** Part 5. SCREEN AND PRINTER HANDLING ROUTINES **
3011
;**************************************************
3012
 
3013
; --------------------------
3014
; THE 'PRINT OUTPUT' ROUTINE
3015
; --------------------------
3016
;   This is the routine most often used by the RST 10 restart although the
3017
;   subroutine is on two occasions called directly when it is known that
3018
;   output will definitely be to the lower screen.
3019
 
3020
;; PRINT-OUT
3021
L09F4:  CALL    L0B03           ; routine PO-FETCH fetches print position
3022
                                ; to HL register pair.
3023
        CP      $20             ; is character a space or higher ?
3024
        JP      NC,L0AD9        ; jump forward to PO-ABLE if so.
3025
 
3026
        CP      $06             ; is character in range 00-05 ?
3027
        JR      C,L0A69         ; to PO-QUEST to print '?' if so.
3028
 
3029
        CP      $18             ; is character in range 24d - 31d ?
3030
        JR      NC,L0A69        ; to PO-QUEST to also print '?' if so.
3031
 
3032
        LD      HL,L0A11 - 6    ; address 0A0B - the base address of control
3033
                                ; character table - where zero would be.
3034
        LD      E,A             ; control character 06 - 23d
3035
        LD      D,$00           ; is transferred to DE.
3036
 
3037
        ADD     HL,DE           ; index into table.
3038
 
3039
        LD      E,(HL)          ; fetch the offset to routine.
3040
        ADD     HL,DE           ; add to make HL the address.
3041
        PUSH    HL              ; push the address.
3042
 
3043
        JP      L0B03           ; Jump forward to PO-FETCH,
3044
                                ; as the screen/printer position has been
3045
                                ; disturbed, and then indirectly to the PO-STORE
3046
                                ; routine on stack.
3047
 
3048
; -----------------------------
3049
; THE 'CONTROL CHARACTER' TABLE
3050
; -----------------------------
3051
;   For control characters in the range 6 - 23d the following table
3052
;   is indexed to provide an offset to the handling routine that
3053
;   follows the table.
3054
 
3055
;; ctlchrtab
3056
L0A11:  DEFB    L0A5F - $       ; 06d offset $4E to Address: PO-COMMA
3057
        DEFB    L0A69 - $       ; 07d offset $57 to Address: PO-QUEST
3058
        DEFB    L0A23 - $       ; 08d offset $10 to Address: PO-BACK-1
3059
        DEFB    L0A3D - $       ; 09d offset $29 to Address: PO-RIGHT
3060
        DEFB    L0A69 - $       ; 10d offset $54 to Address: PO-QUEST
3061
        DEFB    L0A69 - $       ; 11d offset $53 to Address: PO-QUEST
3062
        DEFB    L0A69 - $       ; 12d offset $52 to Address: PO-QUEST
3063
        DEFB    L0A4F - $       ; 13d offset $37 to Address: PO-ENTER
3064
        DEFB    L0A69 - $       ; 14d offset $50 to Address: PO-QUEST
3065
        DEFB    L0A69 - $       ; 15d offset $4F to Address: PO-QUEST
3066
        DEFB    L0A7A - $       ; 16d offset $5F to Address: PO-1-OPER
3067
        DEFB    L0A7A - $       ; 17d offset $5E to Address: PO-1-OPER
3068
        DEFB    L0A7A - $       ; 18d offset $5D to Address: PO-1-OPER
3069
        DEFB    L0A7A - $       ; 19d offset $5C to Address: PO-1-OPER
3070
        DEFB    L0A7A - $       ; 20d offset $5B to Address: PO-1-OPER
3071
        DEFB    L0A7A - $       ; 21d offset $5A to Address: PO-1-OPER
3072
        DEFB    L0A75 - $       ; 22d offset $54 to Address: PO-2-OPER
3073
        DEFB    L0A75 - $       ; 23d offset $53 to Address: PO-2-OPER
3074
 
3075
 
3076
; -------------------------
3077
; THE 'CURSOR LEFT' ROUTINE
3078
; -------------------------
3079
;   Backspace and up a line if that action is from the left of screen.
3080
;   For ZX printer backspace up to first column but not beyond.
3081
 
3082
;; PO-BACK-1
3083
L0A23:  INC     C               ; move left one column.
3084
        LD      A,$22           ; value $21 is leftmost column.
3085
        CP      C               ; have we passed ?
3086
        JR      NZ,L0A3A        ; to PO-BACK-3 if not and store new position.
3087
 
3088
        BIT     1,(IY+$01)      ; test FLAGS  - is printer in use ?
3089
        JR      NZ,L0A38        ; to PO-BACK-2 if so, as we are unable to
3090
                                ; backspace from the leftmost position.
3091
 
3092
 
3093
        INC     B               ; move up one screen line
3094
        LD      C,$02           ; the rightmost column position.
3095
        LD      A,$18           ; Note. This should be $19
3096
                                ; credit. Dr. Frank O'Hara, 1982
3097
 
3098
        CP      B               ; has position moved past top of screen ?
3099
        JR      NZ,L0A3A        ; to PO-BACK-3 if not and store new position.
3100
 
3101
        DEC     B               ; else back to $18.
3102
 
3103
;; PO-BACK-2
3104
L0A38:  LD      C,$21           ; the leftmost column position.
3105
 
3106
;; PO-BACK-3
3107
L0A3A:  JP      L0DD9           ; to CL-SET and PO-STORE to save new
3108
                                ; position in system variables.
3109
 
3110
; --------------------------
3111
; THE 'CURSOR RIGHT' ROUTINE
3112
; --------------------------
3113
;   This moves the print position to the right leaving a trail in the
3114
;   current background colour.
3115
;   "However the programmer has failed to store the new print position
3116
;   so CHR$ 9 will only work if the next print position is at a newly
3117
;   defined place.
3118
;   e.g. PRINT PAPER 2; CHR$ 9; AT 4,0;
3119
;   does work but is not very helpful"
3120
;   - Dr. Ian Logan, Understanding Your Spectrum, 1982.
3121
 
3122
;; PO-RIGHT
3123
L0A3D:  LD      A,($5C91)       ; fetch P_FLAG value
3124
        PUSH    AF              ; and save it on stack.
3125
 
3126
        LD      (IY+$57),$01    ; temporarily set P_FLAG 'OVER 1'.
3127
        LD      A,$20           ; prepare a space.
3128
        CALL    L0B65           ; routine PO-CHAR to print it.
3129
                                ; Note. could be PO-ABLE which would update
3130
                                ; the column position.
3131
 
3132
        POP     AF              ; restore the permanent flag.
3133
        LD      ($5C91),A       ; and restore system variable P_FLAG
3134
 
3135
        RET                     ; return without updating column position
3136
 
3137
; -----------------------
3138
; Perform carriage return
3139
; -----------------------
3140
; A carriage return is 'printed' to screen or printer buffer.
3141
 
3142
;; PO-ENTER
3143
L0A4F:  BIT     1,(IY+$01)      ; test FLAGS  - is printer in use ?
3144
        JP      NZ,L0ECD        ; to COPY-BUFF if so, to flush buffer and reset
3145
                                ; the print position.
3146
 
3147
        LD      C,$21           ; the leftmost column position.
3148
        CALL    L0C55           ; routine PO-SCR handles any scrolling required.
3149
        DEC     B               ; to next screen line.
3150
        JP      L0DD9           ; jump forward to CL-SET to store new position.
3151
 
3152
; -----------
3153
; Print comma
3154
; -----------
3155
; The comma control character. The 32 column screen has two 16 character
3156
; tabstops.  The routine is only reached via the control character table.
3157
 
3158
;; PO-COMMA
3159
L0A5F:  CALL    L0B03           ; routine PO-FETCH - seems unnecessary.
3160
 
3161
        LD      A,C             ; the column position. $21-$01
3162
        DEC     A               ; move right. $20-$00
3163
        DEC     A               ; and again   $1F-$00 or $FF if trailing
3164
        AND     $10             ; will be $00 or $10.
3165
        JR      L0AC3           ; forward to PO-FILL
3166
 
3167
; -------------------
3168
; Print question mark
3169
; -------------------
3170
; This routine prints a question mark which is commonly
3171
; used to print an unassigned control character in range 0-31d.
3172
; there are a surprising number yet to be assigned.
3173
 
3174
;; PO-QUEST
3175
L0A69:  LD      A,$3F           ; prepare the character '?'.
3176
        JR      L0AD9           ; forward to PO-ABLE.
3177
 
3178
; --------------------------------
3179
; Control characters with operands
3180
; --------------------------------
3181
; Certain control characters are followed by 1 or 2 operands.
3182
; The entry points from control character table are PO-2-OPER and PO-1-OPER.
3183
; The routines alter the output address of the current channel so that
3184
; subsequent RST $10 instructions take the appropriate action
3185
; before finally resetting the output address back to PRINT-OUT.
3186
 
3187
;; PO-TV-2
3188
L0A6D:  LD      DE,L0A87        ; address: PO-CONT will be next output routine
3189
        LD      ($5C0F),A       ; store first operand in TVDATA-hi
3190
        JR      L0A80           ; forward to PO-CHANGE >>
3191
 
3192
; ---
3193
 
3194
; -> This initial entry point deals with two operands - AT or TAB.
3195
 
3196
;; PO-2-OPER
3197
L0A75:  LD      DE,L0A6D        ; address: PO-TV-2 will be next output routine
3198
        JR      L0A7D           ; forward to PO-TV-1
3199
 
3200
; ---
3201
 
3202
; -> This initial entry point deals with one operand INK to OVER.
3203
 
3204
;; PO-1-OPER
3205
L0A7A:  LD      DE,L0A87        ; address: PO-CONT will be next output routine
3206
 
3207
;; PO-TV-1
3208
L0A7D:  LD      ($5C0E),A       ; store control code in TVDATA-lo
3209
 
3210
;; PO-CHANGE
3211
L0A80:  LD      HL,($5C51)      ; use CURCHL to find current output channel.
3212
        LD      (HL),E          ; make it
3213
        INC     HL              ; the supplied
3214
        LD      (HL),D          ; address from DE.
3215
        RET                     ; return.
3216
 
3217
; ---
3218
 
3219
;; PO-CONT
3220
L0A87:  LD      DE,L09F4        ; Address: PRINT-OUT
3221
        CALL    L0A80           ; routine PO-CHANGE to restore normal channel.
3222
        LD      HL,($5C0E)      ; TVDATA gives control code and possible
3223
                                ; subsequent character
3224
        LD      D,A             ; save current character
3225
        LD      A,L             ; the stored control code
3226
        CP      $16             ; was it INK to OVER (1 operand) ?
3227
        JP      C,L2211         ; to CO-TEMP-5
3228
 
3229
        JR      NZ,L0AC2        ; to PO-TAB if not 22d i.e. 23d TAB.
3230
 
3231
                                ; else must have been 22d AT.
3232
        LD      B,H             ; line to H   (0-23d)
3233
        LD      C,D             ; column to C (0-31d)
3234
        LD      A,$1F           ; the value 31d
3235
        SUB     C               ; reverse the column number.
3236
        JR      C,L0AAC         ; to PO-AT-ERR if C was greater than 31d.
3237
 
3238
        ADD     A,$02           ; transform to system range $02-$21
3239
        LD      C,A             ; and place in column register.
3240
 
3241
        BIT     1,(IY+$01)      ; test FLAGS  - is printer in use ?
3242
        JR      NZ,L0ABF        ; to PO-AT-SET as line can be ignored.
3243
 
3244
        LD      A,$16           ; 22 decimal
3245
        SUB     B               ; subtract line number to reverse
3246
                                ; 0 - 22 becomes 22 - 0.
3247
 
3248
;; PO-AT-ERR
3249
L0AAC:  JP      C,L1E9F         ; to REPORT-B if higher than 22 decimal
3250
                                ; Integer out of range.
3251
 
3252
        INC     A               ; adjust for system range $01-$17
3253
        LD      B,A             ; place in line register
3254
        INC     B               ; adjust to system range  $02-$18
3255
        BIT     0,(IY+$02)      ; TV_FLAG  - Lower screen in use ?
3256
        JP      NZ,L0C55        ; exit to PO-SCR to test for scrolling
3257
 
3258
        CP      (IY+$31)        ; Compare against DF_SZ
3259
        JP      C,L0C86         ; to REPORT-5 if too low
3260
                                ; Out of screen.
3261
 
3262
;; PO-AT-SET
3263
L0ABF:  JP      L0DD9           ; print position is valid so exit via CL-SET
3264
 
3265
; ---
3266
 
3267
; Continue here when dealing with TAB.
3268
; Note. In BASIC, TAB is followed by a 16-bit number and was initially
3269
; designed to work with any output device.
3270
 
3271
;; PO-TAB
3272
L0AC2:  LD      A,H             ; transfer parameter to A
3273
                                ; Losing current character -
3274
                                ; High byte of TAB parameter.
3275
 
3276
 
3277
;; PO-FILL
3278
L0AC3:  CALL    L0B03           ; routine PO-FETCH, HL-addr, BC=line/column.
3279
                                ; column 1 (right), $21 (left)
3280
        ADD     A,C             ; add operand to current column
3281
        DEC     A               ; range 0 - 31+
3282
        AND     $1F             ; make range 0 - 31d
3283
        RET     Z               ; return if result zero
3284
 
3285
        LD      D,A             ; Counter to D
3286
        SET     0,(IY+$01)      ; update FLAGS  - signal suppress leading space.
3287
 
3288
;; PO-SPACE
3289
L0AD0:  LD      A,$20           ; space character.
3290
 
3291
        CALL    L0C3B           ; routine PO-SAVE prints the character
3292
                                ; using alternate set (normal output routine)
3293
 
3294
        DEC     D               ; decrement counter.
3295
        JR      NZ,L0AD0        ; to PO-SPACE until done
3296
 
3297
        RET                     ; return
3298
 
3299
; ----------------------
3300
; Printable character(s)
3301
; ----------------------
3302
; This routine prints printable characters and continues into
3303
; the position store routine
3304
 
3305
;; PO-ABLE
3306
L0AD9:  CALL    L0B24           ; routine PO-ANY
3307
                                ; and continue into position store routine.
3308
 
3309
; ----------------------------
3310
; THE 'POSITION STORE' ROUTINE
3311
; ----------------------------
3312
;   This routine updates the system variables associated with the main screen,
3313
;   the lower screen/input buffer or the ZX printer.
3314
 
3315
;; PO-STORE
3316
L0ADC:  BIT     1,(IY+$01)      ; Test FLAGS - is printer in use ?
3317
        JR      NZ,L0AFC        ; Forward, if so, to PO-ST-PR
3318
 
3319
        BIT     0,(IY+$02)      ; Test TV_FLAG - is lower screen in use ?
3320
        JR      NZ,L0AF0        ; Forward, if so, to PO-ST-E
3321
 
3322
;   This section deals with the upper screen.
3323
 
3324
        LD      ($5C88),BC      ; Update S_POSN - line/column upper screen
3325
        LD      ($5C84),HL      ; Update DF_CC - upper display file address
3326
 
3327
        RET                     ; Return.
3328
 
3329
; ---
3330
 
3331
;   This section deals with the lower screen.
3332
 
3333
;; PO-ST-E
3334
L0AF0:  LD      ($5C8A),BC      ; Update SPOSNL line/column lower screen
3335
        LD      ($5C82),BC      ; Update ECHO_E line/column input buffer
3336
        LD      ($5C86),HL      ; Update DFCCL  lower screen memory address
3337
        RET                     ; Return.
3338
 
3339
; ---
3340
 
3341
;   This section deals with the ZX Printer.
3342
 
3343
;; PO-ST-PR
3344
L0AFC:  LD      (IY+$45),C      ; Update P_POSN column position printer
3345
        LD      ($5C80),HL      ; Update PR_CC - full printer buffer memory
3346
                                ; address
3347
        RET                     ; Return.
3348
 
3349
;   Note. that any values stored in location 23681 will be overwritten with
3350
;   the value 91 decimal.
3351
;   Credit April 1983, Dilwyn Jones. "Delving Deeper into your ZX Spectrum".
3352
 
3353
; ----------------------------
3354
; THE 'POSITION FETCH' ROUTINE
3355
; ----------------------------
3356
;   This routine fetches the line/column and display file address of the upper
3357
;   and lower screen or, if the printer is in use, the column position and
3358
;   absolute memory address.
3359
;   Note. that PR-CC-hi (23681) is used by this routine and if, in accordance
3360
;   with the manual (that says this is unused), the location has been used for
3361
;   other purposes, then subsequent output to the printer buffer could corrupt
3362
;   a 256-byte section of memory.
3363
 
3364
;; PO-FETCH
3365
L0B03:  BIT     1,(IY+$01)      ; Test FLAGS - is printer in use ?
3366
        JR      NZ,L0B1D        ; Forward, if so, to PO-F-PR
3367
 
3368
;   assume upper screen in use and thus optimize for path that requires speed.
3369
 
3370
        LD      BC,($5C88)      ; Fetch line/column from S_POSN
3371
        LD      HL,($5C84)      ; Fetch DF_CC display file address
3372
 
3373
        BIT     0,(IY+$02)      ; Test TV_FLAG - lower screen in use ?
3374
        RET     Z               ; Return if upper screen in use.
3375
 
3376
;   Overwrite registers with values for lower screen.
3377
 
3378
        LD      BC,($5C8A)      ; Fetch line/column from SPOSNL
3379
        LD      HL,($5C86)      ; Fetch display file address from DFCCL
3380
        RET                     ; Return.
3381
 
3382
; ---
3383
 
3384
;   This section deals with the ZX Printer.
3385
 
3386
;; PO-F-PR
3387
L0B1D:  LD      C,(IY+$45)      ; Fetch column from P_POSN.
3388
        LD      HL,($5C80)      ; Fetch printer buffer address from PR_CC.
3389
        RET                     ; Return.
3390
 
3391
; ---------------------------------
3392
; THE 'PRINT ANY CHARACTER' ROUTINE
3393
; ---------------------------------
3394
;   This routine is used to print any character in range 32d - 255d
3395
;   It is only called from PO-ABLE which continues into PO-STORE
3396
 
3397
;; PO-ANY
3398
L0B24:  CP      $80             ; ASCII ?
3399
        JR      C,L0B65         ; to PO-CHAR is so.
3400
 
3401
        CP      $90             ; test if a block graphic character.
3402
        JR      NC,L0B52        ; to PO-T&UDG to print tokens and UDGs
3403
 
3404
; The 16 2*2 mosaic characters 128-143 decimal are formed from
3405
; bits 0-3 of the character.
3406
 
3407
        LD      B,A             ; save character
3408
        CALL    L0B38           ; routine PO-GR-1 to construct top half
3409
                                ; then bottom half.
3410
        CALL    L0B03           ; routine PO-FETCH fetches print position.
3411
        LD      DE,$5C92        ; MEM-0 is location of 8 bytes of character
3412
        JR      L0B7F           ; to PR-ALL to print to screen or printer
3413
 
3414
; ---
3415
 
3416
;; PO-GR-1
3417
L0B38:  LD      HL,$5C92        ; address MEM-0 - a temporary buffer in
3418
                                ; systems variables which is normally used
3419
                                ; by the calculator.
3420
        CALL    L0B3E           ; routine PO-GR-2 to construct top half
3421
                                ; and continue into routine to construct
3422
                                ; bottom half.
3423
 
3424
;; PO-GR-2
3425
L0B3E:  RR      B               ; rotate bit 0/2 to carry
3426
        SBC     A,A             ; result $00 or $FF
3427
        AND     $0F             ; mask off right hand side
3428
        LD      C,A             ; store part in C
3429
        RR      B               ; rotate bit 1/3 of original chr to carry
3430
        SBC     A,A             ; result $00 or $FF
3431
        AND     $F0             ; mask off left hand side
3432
        OR      C               ; combine with stored pattern
3433
        LD      C,$04           ; four bytes for top/bottom half
3434
 
3435
;; PO-GR-3
3436
L0B4C:  LD      (HL),A          ; store bit patterns in temporary buffer
3437
        INC     HL              ; next address
3438
        DEC     C               ; jump back to
3439
        JR      NZ,L0B4C        ; to PO-GR-3 until byte is stored 4 times
3440
 
3441
        RET                     ; return
3442
 
3443
; ---
3444
 
3445
; Tokens and User defined graphics are now separated.
3446
 
3447
;; PO-T&UDG
3448
L0B52:  SUB     $A5             ; the 'RND' character
3449
        JR      NC,L0B5F        ; to PO-T to print tokens
3450
 
3451
        ADD     A,$15           ; add 21d to restore to 0 - 20
3452
        PUSH    BC              ; save current print position
3453
        LD      BC,($5C7B)      ; fetch UDG to address bit patterns
3454
        JR      L0B6A           ; to PO-CHAR-2 - common code to lay down
3455
                                ; a bit patterned character
3456
 
3457
; ---
3458
 
3459
;; PO-T
3460
L0B5F:  CALL    L0C10           ; routine PO-TOKENS prints tokens
3461
        JP      L0B03           ; exit via a JUMP to PO-FETCH as this routine
3462
                                ; must continue into PO-STORE.
3463
                                ; A JR instruction could be used.
3464
 
3465
; This point is used to print ASCII characters  32d - 127d.
3466
 
3467
;; PO-CHAR
3468
L0B65:  PUSH    BC              ; save print position
3469
        LD      BC,($5C36)      ; address CHARS
3470
 
3471
; This common code is used to transfer the character bytes to memory.
3472
 
3473
;; PO-CHAR-2
3474
L0B6A:  EX      DE,HL           ; transfer destination address to DE
3475
        LD      HL,$5C3B        ; point to FLAGS
3476
        RES     0,(HL)          ; allow for leading space
3477
        CP      $20             ; is it a space ?
3478
        JR      NZ,L0B76        ; to PO-CHAR-3 if not
3479
 
3480
        SET     0,(HL)          ; signal no leading space to FLAGS
3481
 
3482
;; PO-CHAR-3
3483
L0B76:  LD      H,$00           ; set high byte to 0
3484
        LD      L,A             ; character to A
3485
                                ; 0-21 UDG or 32-127 ASCII.
3486
        ADD     HL,HL           ; multiply
3487
        ADD     HL,HL           ; by
3488
        ADD     HL,HL           ; eight
3489
        ADD     HL,BC           ; HL now points to first byte of character
3490
        POP     BC              ; the source address CHARS or UDG
3491
        EX      DE,HL           ; character address to DE
3492
 
3493
; ----------------------------------
3494
; THE 'PRINT ALL CHARACTERS' ROUTINE
3495
; ----------------------------------
3496
;   This entry point entered from above to print ASCII and UDGs but also from
3497
;   earlier to print mosaic characters.
3498
;   HL=destination
3499
;   DE=character source
3500
;   BC=line/column
3501
 
3502
;; PR-ALL
3503
L0B7F:  LD      A,C             ; column to A
3504
        DEC     A               ; move right
3505
        LD      A,$21           ; pre-load with leftmost position
3506
        JR      NZ,L0B93        ; but if not zero to PR-ALL-1
3507
 
3508
        DEC     B               ; down one line
3509
        LD      C,A             ; load C with $21
3510
        BIT     1,(IY+$01)      ; test FLAGS  - Is printer in use
3511
        JR      Z,L0B93         ; to PR-ALL-1 if not
3512
 
3513
        PUSH    DE              ; save source address
3514
        CALL    L0ECD           ; routine COPY-BUFF outputs line to printer
3515
        POP     DE              ; restore character source address
3516
        LD      A,C             ; the new column number ($21) to C
3517
 
3518
;; PR-ALL-1
3519
L0B93:  CP      C               ; this test is really for screen - new line ?
3520
        PUSH    DE              ; save source
3521
 
3522
        CALL    Z,L0C55         ; routine PO-SCR considers scrolling
3523
 
3524
        POP     DE              ; restore source
3525
        PUSH    BC              ; save line/column
3526
        PUSH    HL              ; and destination
3527
        LD      A,($5C91)       ; fetch P_FLAG to accumulator
3528
        LD      B,$FF           ; prepare OVER mask in B.
3529
        RRA                     ; bit 0 set if OVER 1
3530
        JR      C,L0BA4         ; to PR-ALL-2
3531
 
3532
        INC     B               ; set OVER mask to 0
3533
 
3534
;; PR-ALL-2
3535
L0BA4:  RRA                     ; skip bit 1 of P_FLAG
3536
        RRA                     ; bit 2 is INVERSE
3537
        SBC     A,A             ; will be FF for INVERSE 1 else zero
3538
        LD      C,A             ; transfer INVERSE mask to C
3539
        LD      A,$08           ; prepare to count 8 bytes
3540
        AND     A               ; clear carry to signal screen
3541
        BIT     1,(IY+$01)      ; test FLAGS  - is printer in use ?
3542
        JR      Z,L0BB6         ; to PR-ALL-3 if screen
3543
 
3544
        SET     1,(IY+$30)      ; update FLAGS2  - signal printer buffer has
3545
                                ; been used.
3546
        SCF                     ; set carry flag to signal printer.
3547
 
3548
;; PR-ALL-3
3549
L0BB6:  EX      DE,HL           ; now HL=source, DE=destination
3550
 
3551
;; PR-ALL-4
3552
L0BB7:  EX      AF,AF'          ; save printer/screen flag
3553
        LD      A,(DE)          ; fetch existing destination byte
3554
        AND     B               ; consider OVER
3555
        XOR     (HL)            ; now XOR with source
3556
        XOR     C               ; now with INVERSE MASK
3557
        LD      (DE),A          ; update screen/printer
3558
        EX      AF,AF'          ; restore flag
3559
        JR      C,L0BD3         ; to PR-ALL-6 - printer address update
3560
 
3561
        INC     D               ; gives next pixel line down screen
3562
 
3563
;; PR-ALL-5
3564
L0BC1:  INC     HL              ; address next character byte
3565
        DEC     A               ; the byte count is decremented
3566
        JR      NZ,L0BB7        ; back to PR-ALL-4 for all 8 bytes
3567
 
3568
        EX      DE,HL           ; destination to HL
3569
        DEC     H               ; bring back to last updated screen position
3570
        BIT     1,(IY+$01)      ; test FLAGS  - is printer in use ?
3571
        CALL    Z,L0BDB         ; if not, call routine PO-ATTR to update
3572
                                ; corresponding colour attribute.
3573
        POP     HL              ; restore original screen/printer position
3574
        POP     BC              ; and line column
3575
        DEC     C               ; move column to right
3576
        INC     HL              ; increase screen/printer position
3577
        RET                     ; return and continue into PO-STORE
3578
                                ; within PO-ABLE
3579
 
3580
; ---
3581
 
3582
;   This branch is used to update the printer position by 32 places
3583
;   Note. The high byte of the address D remains constant (which it should).
3584
 
3585
;; PR-ALL-6
3586
L0BD3:  EX      AF,AF'          ; save the flag
3587
        LD      A,$20           ; load A with 32 decimal
3588
        ADD     A,E             ; add this to E
3589
        LD      E,A             ; and store result in E
3590
        EX      AF,AF'          ; fetch the flag
3591
        JR      L0BC1           ; back to PR-ALL-5
3592
 
3593
; -----------------------------------
3594
; THE 'GET ATTRIBUTE ADDRESS' ROUTINE
3595
; -----------------------------------
3596
;   This routine is entered with the HL register holding the last screen
3597
;   address to be updated by PRINT or PLOT.
3598
;   The Spectrum screen arrangement leads to the L register holding the correct
3599
;   value for the attribute file and it is only necessary to manipulate H to
3600
;   form the correct colour attribute address.
3601
 
3602
;; PO-ATTR
3603
L0BDB:  LD       A,H            ; fetch high byte $40 - $57
3604
        RRCA                    ; shift
3605
        RRCA                    ; bits 3 and 4
3606
        RRCA                    ; to right.
3607
        AND     $03             ; range is now 0 - 2
3608
        OR      $58             ; form correct high byte for third of screen
3609
        LD      H,A             ; HL is now correct
3610
        LD      DE,($5C8F)      ; make D hold ATTR_T, E hold MASK-T
3611
        LD      A,(HL)          ; fetch existing attribute
3612
        XOR     E               ; apply masks
3613
        AND     D               ;
3614
        XOR     E               ;
3615
        BIT     6,(IY+$57)      ; test P_FLAG  - is this PAPER 9 ??
3616
        JR      Z,L0BFA         ; skip to PO-ATTR-1 if not.
3617
 
3618
        AND     $C7             ; set paper
3619
        BIT     2,A             ; to contrast with ink
3620
        JR      NZ,L0BFA        ; skip to PO-ATTR-1
3621
 
3622
        XOR     $38             ;
3623
 
3624
;; PO-ATTR-1
3625
L0BFA:  BIT     4,(IY+$57)      ; test P_FLAG  - Is this INK 9 ??
3626
        JR      Z,L0C08         ; skip to PO-ATTR-2 if not
3627
 
3628
        AND     $F8             ; make ink
3629
        BIT     5,A             ; contrast with paper.
3630
        JR      NZ,L0C08        ; to PO-ATTR-2
3631
 
3632
        XOR     $07             ;
3633
 
3634
;; PO-ATTR-2
3635
L0C08:  LD      (HL),A          ; save the new attribute.
3636
        RET                     ; return.
3637
 
3638
; ---------------------------------
3639
; THE 'MESSAGE PRINTING' SUBROUTINE
3640
; ---------------------------------
3641
;   This entry point is used to print tape, boot-up, scroll? and error messages.
3642
;   On entry the DE register points to an initial step-over byte or the
3643
;   inverted end-marker of the previous entry in the table.
3644
;   Register A contains the message number, often zero to print first message.
3645
;   (HL has nothing important usually P_FLAG)
3646
 
3647
;; PO-MSG
3648
L0C0A:  PUSH    HL              ; put hi-byte zero on stack to suppress
3649
        LD      H,$00           ; trailing spaces
3650
        EX      (SP),HL         ; ld h,0; push hl would have done ?.
3651
        JR      L0C14           ; forward to PO-TABLE.
3652
 
3653
; ---
3654
 
3655
;   This entry point prints the BASIC keywords, '<>' etc. from alt set
3656
 
3657
;; PO-TOKENS
3658
L0C10:  LD      DE,L0095        ; address: TKN-TABLE
3659
        PUSH    AF              ; save the token number to control
3660
                                ; trailing spaces - see later *
3661
 
3662
; ->
3663
 
3664
;; PO-TABLE
3665
L0C14:  CALL    L0C41           ; routine PO-SEARCH will set carry for
3666
                                ; all messages and function words.
3667
 
3668
        JR      C,L0C22         ; forward to PO-EACH if not a command, '<>' etc.
3669
 
3670
        LD      A,$20           ; prepare leading space
3671
        BIT     0,(IY+$01)      ; test FLAGS  - leading space if not set
3672
 
3673
        CALL    Z,L0C3B         ; routine PO-SAVE to print a space without
3674
                                ; disturbing registers.
3675
 
3676
;; PO-EACH
3677
L0C22:  LD      A,(DE)          ; Fetch character from the table.
3678
        AND     $7F             ; Cancel any inverted bit.
3679
 
3680
        CALL    L0C3B           ; Routine PO-SAVE to print using the alternate
3681
                                ; set of registers.
3682
 
3683
        LD      A,(DE)          ; Re-fetch character from table.
3684
        INC     DE              ; Address next character in the table.
3685
 
3686
        ADD     A,A             ; Was character inverted ?
3687
                                ; (this also doubles character)
3688
        JR      NC,L0C22        ; back to PO-EACH if not.
3689
 
3690
        POP     DE              ; * re-fetch trailing space byte to D
3691
 
3692
        CP      $48             ; was the last character '$' ?
3693
        JR      Z,L0C35         ; forward to PO-TR-SP to consider trailing
3694
                                ; space if so.
3695
 
3696
        CP      $82             ; was it < 'A' i.e. '#','>','=' from tokens
3697
                                ; or ' ','.' (from tape) or '?' from scroll
3698
 
3699
        RET     C               ; Return if so as no trailing space required.
3700
 
3701
;; PO-TR-SP
3702
L0C35:  LD      A,D             ; The trailing space flag (zero if an error msg)
3703
 
3704
        CP      $03             ; Test against RND, INKEY$ and PI which have no
3705
                                ; parameters and therefore no trailing space.
3706
 
3707
        RET     C               ; Return if no trailing space.
3708
 
3709
        LD      A,$20           ; Prepare the space character and continue to
3710
                                ; print and make an indirect return.
3711
 
3712
; -----------------------------------
3713
; THE 'RECURSIVE PRINTING' SUBROUTINE
3714
; -----------------------------------
3715
;   This routine which is part of PRINT-OUT allows RST $10 to be used
3716
;   recursively to print tokens and the spaces associated with them.
3717
;   It is called on three occasions when the value of DE must be preserved.
3718
 
3719
;; PO-SAVE
3720
L0C3B:  PUSH    DE              ; Save DE value.
3721
        EXX                     ; Switch in main set
3722
 
3723
        RST     10H             ; PRINT-A prints using this alternate set.
3724
 
3725
        EXX                     ; Switch back to this alternate set.
3726
        POP     DE              ; Restore the initial DE value.
3727
 
3728
        RET                     ; Return.
3729
 
3730
; ------------
3731
; Table search
3732
; ------------
3733
; This subroutine searches a message or the token table for the
3734
; message number held in A. DE holds the address of the table.
3735
 
3736
;; PO-SEARCH
3737
L0C41:  PUSH    AF              ; save the message/token number
3738
        EX      DE,HL           ; transfer DE to HL
3739
        INC     A               ; adjust for initial step-over byte
3740
 
3741
;; PO-STEP
3742
L0C44:  BIT     7,(HL)          ; is character inverted ?
3743
        INC     HL              ; address next
3744
        JR      Z,L0C44         ; back to PO-STEP if not inverted.
3745
 
3746
        DEC     A               ; decrease counter
3747
        JR      NZ,L0C44        ; back to PO-STEP if not zero
3748
 
3749
        EX      DE,HL           ; transfer address to DE
3750
        POP     AF              ; restore message/token number
3751
        CP      $20             ; return with carry set
3752
        RET     C               ; for all messages and function tokens
3753
 
3754
        LD      A,(DE)          ; test first character of token
3755
        SUB     $41             ; and return with carry set
3756
        RET                     ; if it is less that 'A'
3757
                                ; i.e. '<>', '<=', '>='
3758
 
3759
; ---------------
3760
; Test for scroll
3761
; ---------------
3762
; This test routine is called when printing carriage return, when considering
3763
; PRINT AT and from the general PRINT ALL characters routine to test if
3764
; scrolling is required, prompting the user if necessary.
3765
; This is therefore using the alternate set.
3766
; The B register holds the current line.
3767
 
3768
;; PO-SCR
3769
L0C55:  BIT     1,(IY+$01)      ; test FLAGS  - is printer in use ?
3770
        RET     NZ              ; return immediately if so.
3771
 
3772
        LD      DE,L0DD9        ; set DE to address: CL-SET
3773
        PUSH    DE              ; and push for return address.
3774
 
3775
        LD      A,B             ; transfer the line to A.
3776
        BIT     0,(IY+$02)      ; test TV_FLAG - lower screen in use ?
3777
        JP      NZ,L0D02        ; jump forward to PO-SCR-4 if so.
3778
 
3779
        CP      (IY+$31)        ; greater than DF_SZ display file size ?
3780
        JR      C,L0C86         ; forward to REPORT-5 if less.
3781
                                ; 'Out of screen'
3782
 
3783
        RET     NZ              ; return (via CL-SET) if greater
3784
 
3785
        BIT     4,(IY+$02)      ; test TV_FLAG  - Automatic listing ?
3786
        JR      Z,L0C88         ; forward to PO-SCR-2 if not.
3787
 
3788
        LD      E,(IY+$2D)      ; fetch BREG - the count of scroll lines to E.
3789
        DEC     E               ; decrease and jump
3790
        JR      Z,L0CD2         ; to PO-SCR-3 if zero and scrolling required.
3791
 
3792
        LD      A,$00           ; explicit - select channel zero.
3793
        CALL    L1601           ; routine CHAN-OPEN opens it.
3794
 
3795
        LD      SP,($5C3F)      ; set stack pointer to LIST_SP
3796
 
3797
        RES     4,(IY+$02)      ; reset TV_FLAG  - signal auto listing finished.
3798
        RET                     ; return ignoring pushed value, CL-SET
3799
                                ; to MAIN or EDITOR without updating
3800
                                ; print position                         >>
3801
 
3802
; ---
3803
 
3804
 
3805
;; REPORT-5
3806
L0C86:  RST     08H             ; ERROR-1
3807
        DEFB    $04             ; Error Report: Out of screen
3808
 
3809
; continue here if not an automatic listing.
3810
 
3811
;; PO-SCR-2
3812
L0C88:  DEC     (IY+$52)        ; decrease SCR_CT
3813
        JR      NZ,L0CD2        ; forward to PO-SCR-3 to scroll display if
3814
                                ; result not zero.
3815
 
3816
; now produce prompt.
3817
 
3818
        LD      A,$18           ; reset
3819
        SUB     B               ; the
3820
        LD      ($5C8C),A       ; SCR_CT scroll count
3821
        LD      HL,($5C8F)      ; L=ATTR_T, H=MASK_T
3822
        PUSH    HL              ; save on stack
3823
        LD      A,($5C91)       ; P_FLAG
3824
        PUSH    AF              ; save on stack to prevent lower screen
3825
                                ; attributes (BORDCR etc.) being applied.
3826
        LD      A,$FD           ; select system channel 'K'
3827
        CALL    L1601           ; routine CHAN-OPEN opens it
3828
        XOR     A               ; clear to address message directly
3829
        LD      DE,L0CF8        ; make DE address: scrl-mssg
3830
        CALL    L0C0A           ; routine PO-MSG prints to lower screen
3831
        SET     5,(IY+$02)      ; set TV_FLAG  - signal lower screen requires
3832
                                ; clearing
3833
        LD      HL,$5C3B        ; make HL address FLAGS
3834
        SET     3,(HL)          ; signal 'L' mode.
3835
        RES     5,(HL)          ; signal 'no new key'.
3836
        EXX                     ; switch to main set.
3837
                                ; as calling chr input from alternative set.
3838
        CALL    L15D4           ; routine WAIT-KEY waits for new key
3839
                                ; Note. this is the right routine but the
3840
                                ; stream in use is unsatisfactory. From the
3841
                                ; choices available, it is however the best.
3842
 
3843
        EXX                     ; switch back to alternate set.
3844
        CP      $20             ; space is considered as BREAK
3845
        JR      Z,L0D00         ; forward to REPORT-D if so
3846
                                ; 'BREAK - CONT repeats'
3847
 
3848
        CP      $E2             ; is character 'STOP' ?
3849
        JR      Z,L0D00         ; forward to REPORT-D if so
3850
 
3851
        OR      $20             ; convert to lower-case
3852
        CP      $6E             ; is character 'n' ?
3853
        JR      Z,L0D00         ; forward to REPORT-D if so else scroll.
3854
 
3855
        LD      A,$FE           ; select system channel 'S'
3856
        CALL    L1601           ; routine CHAN-OPEN
3857
        POP     AF              ; restore original P_FLAG
3858
        LD      ($5C91),A       ; and save in P_FLAG.
3859
        POP     HL              ; restore original ATTR_T, MASK_T
3860
        LD      ($5C8F),HL      ; and reset ATTR_T, MASK-T as 'scroll?' has
3861
                                ; been printed.
3862
 
3863
;; PO-SCR-3
3864
L0CD2:  CALL    L0DFE           ; routine CL-SC-ALL to scroll whole display
3865
        LD      B,(IY+$31)      ; fetch DF_SZ to B
3866
        INC     B               ; increase to address last line of display
3867
        LD      C,$21           ; set C to $21 (was $21 from above routine)
3868
        PUSH    BC              ; save the line and column in BC.
3869
 
3870
        CALL    L0E9B           ; routine CL-ADDR finds display address.
3871
 
3872
        LD      A,H             ; now find the corresponding attribute byte
3873
        RRCA                    ; (this code sequence is used twice
3874
        RRCA                    ; elsewhere and is a candidate for
3875
        RRCA                    ; a subroutine.)
3876
        AND     $03             ;
3877
        OR      $58             ;
3878
        LD      H,A             ;
3879
 
3880
        LD      DE,$5AE0        ; start of last 'line' of attribute area
3881
        LD      A,(DE)          ; get attribute for last line
3882
        LD      C,(HL)          ; transfer to base line of upper part
3883
        LD      B,$20           ; there are thirty two bytes
3884
        EX      DE,HL           ; swap the pointers.
3885
 
3886
;; PO-SCR-3A
3887
L0CF0:  LD      (DE),A          ; transfer
3888
        LD      (HL),C          ; attributes.
3889
        INC     DE              ; address next.
3890
        INC     HL              ; address next.
3891
        DJNZ    L0CF0           ; loop back to PO-SCR-3A for all adjacent
3892
                                ; attribute lines.
3893
 
3894
        POP     BC              ; restore the line/column.
3895
        RET                     ; return via CL-SET (was pushed on stack).
3896
 
3897
; ---
3898
 
3899
; The message 'scroll?' appears here with last byte inverted.
3900
 
3901
;; scrl-mssg
3902
L0CF8:  DEFB    $80             ; initial step-over byte.
3903
        DEFM    "scroll"
3904
        DEFB    '?'+$80
3905
 
3906
;; REPORT-D
3907
L0D00:  RST     08H             ; ERROR-1
3908
        DEFB    $0C             ; Error Report: BREAK - CONT repeats
3909
 
3910
; continue here if using lower display - A holds line number.
3911
 
3912
;; PO-SCR-4
3913
L0D02:  CP      $02             ; is line number less than 2 ?
3914
        JR      C,L0C86         ; to REPORT-5 if so
3915
                                ; 'Out of Screen'.
3916
 
3917
        ADD     A,(IY+$31)      ; add DF_SZ
3918
        SUB     $19             ;
3919
        RET     NC              ; return if scrolling unnecessary
3920
 
3921
        NEG                     ; Negate to give number of scrolls required.
3922
        PUSH    BC              ; save line/column
3923
        LD      B,A             ; count to B
3924
        LD      HL,($5C8F)      ; fetch current ATTR_T, MASK_T to HL.
3925
        PUSH    HL              ; and save
3926
        LD      HL,($5C91)      ; fetch P_FLAG
3927
        PUSH    HL              ; and save.
3928
                                ; to prevent corruption by input AT
3929
 
3930
        CALL    L0D4D           ; routine TEMPS sets to BORDCR etc
3931
        LD      A,B             ; transfer scroll number to A.
3932
 
3933
;; PO-SCR-4A
3934
L0D1C:  PUSH    AF              ; save scroll number.
3935
        LD      HL,$5C6B        ; address DF_SZ
3936
        LD      B,(HL)          ; fetch old value
3937
        LD      A,B             ; transfer to A
3938
        INC     A               ; and increment
3939
        LD      (HL),A          ; then put back.
3940
        LD      HL,$5C89        ; address S_POSN_hi - line
3941
        CP      (HL)            ; compare
3942
        JR      C,L0D2D         ; forward to PO-SCR-4B if scrolling required
3943
 
3944
        INC     (HL)            ; else increment S_POSN_hi
3945
        LD      B,$18           ; set count to whole display ??
3946
                                ; Note. should be $17 and the top line will be
3947
                                ; scrolled into the ROM which is harmless on
3948
                                ; the standard set up.
3949
                                ; credit P.Giblin 1984.
3950
 
3951
;; PO-SCR-4B
3952
L0D2D:  CALL    L0E00           ; routine CL-SCROLL scrolls B lines
3953
        POP     AF              ; restore scroll counter.
3954
        DEC     A               ; decrease
3955
        JR      NZ,L0D1C        ; back to PO-SCR-4A until done
3956
 
3957
        POP     HL              ; restore original P_FLAG.
3958
        LD      (IY+$57),L      ; and overwrite system variable P_FLAG.
3959
 
3960
        POP     HL              ; restore original ATTR_T/MASK_T.
3961
        LD      ($5C8F),HL      ; and update system variables.
3962
 
3963
        LD      BC,($5C88)      ; fetch S_POSN to BC.
3964
        RES     0,(IY+$02)      ; signal to TV_FLAG  - main screen in use.
3965
        CALL    L0DD9           ; call routine CL-SET for upper display.
3966
 
3967
        SET     0,(IY+$02)      ; signal to TV_FLAG  - lower screen in use.
3968
        POP     BC              ; restore line/column
3969
        RET                     ; return via CL-SET for lower display.
3970
 
3971
; ----------------------
3972
; Temporary colour items
3973
; ----------------------
3974
; This subroutine is called 11 times to copy the permanent colour items
3975
; to the temporary ones.
3976
 
3977
;; TEMPS
3978
L0D4D:  XOR     A               ; clear the accumulator
3979
        LD      HL,($5C8D)      ; fetch L=ATTR_P and H=MASK_P
3980
        BIT     0,(IY+$02)      ; test TV_FLAG  - is lower screen in use ?
3981
        JR      Z,L0D5B         ; skip to TEMPS-1 if not
3982
 
3983
        LD      H,A             ; set H, MASK P, to 00000000.
3984
        LD      L,(IY+$0E)      ; fetch BORDCR to L which is used for lower
3985
                                ; screen.
3986
 
3987
;; TEMPS-1
3988
L0D5B:  LD      ($5C8F),HL      ; transfer values to ATTR_T and MASK_T
3989
 
3990
; for the print flag the permanent values are odd bits, temporary even bits.
3991
 
3992
        LD      HL,$5C91        ; address P_FLAG.
3993
        JR      NZ,L0D65        ; skip to TEMPS-2 if lower screen using A=0.
3994
 
3995
        LD      A,(HL)          ; else pick up flag bits.
3996
        RRCA                    ; rotate permanent bits to temporary bits.
3997
 
3998
;; TEMPS-2
3999
L0D65:  XOR     (HL)            ;
4000
        AND     $55             ; BIN 01010101
4001
        XOR     (HL)            ; permanent now as original
4002
        LD      (HL),A          ; apply permanent bits to temporary bits.
4003
        RET                     ; and return.
4004
 
4005
; -----------------
4006
; THE 'CLS' COMMAND
4007
; -----------------
4008
;    This command clears the display.
4009
;    The routine is also called during initialization and by the CLEAR command.
4010
;    If it's difficult to write it should be difficult to read.
4011
 
4012
;; CLS
4013
L0D6B:  CALL    L0DAF           ; Routine CL-ALL clears the entire display and
4014
                                ; sets the attributes to the permanent ones
4015
                                ; from ATTR-P.
4016
 
4017
;   Having cleared all 24 lines of the display area, continue into the
4018
;   subroutine that clears the lower display area.  Note that at the moment
4019
;   the attributes for the lower lines are the same as upper ones and have
4020
;   to be changed to match the BORDER colour.
4021
 
4022
; --------------------------
4023
; THE 'CLS-LOWER' SUBROUTINE
4024
; --------------------------
4025
;   This routine is called from INPUT, and from the MAIN execution loop.
4026
;   This is very much a housekeeping routine which clears between 2 and 23
4027
;   lines of the display, setting attributes and correcting situations where
4028
;   errors have occurred while the normal input and output routines have been
4029
;   temporarily diverted to deal with, say colour control codes.
4030
 
4031
;; CLS-LOWER
4032
L0D6E:  LD      HL,$5C3C        ; address System Variable TV_FLAG.
4033
        RES     5,(HL)          ; TV_FLAG - signal do not clear lower screen.
4034
        SET     0,(HL)          ; TV_FLAG - signal lower screen in use.
4035
 
4036
        CALL    L0D4D           ; routine TEMPS applies permanent attributes,
4037
                                ; in this case BORDCR to ATTR_T.
4038
                                ; Note. this seems unnecessary and is repeated
4039
                                ; within CL-LINE.
4040
 
4041
        LD      B,(IY+$31)      ; fetch lower screen display file size DF_SZ
4042
 
4043
        CALL    L0E44           ; routine CL-LINE clears lines to bottom of the
4044
                                ; display and sets attributes from BORDCR while
4045
                                ; preserving the B register.
4046
 
4047
        LD      HL,$5AC0        ; set initial attribute address to the leftmost
4048
                                ; cell of second line up.
4049
 
4050
        LD      A,($5C8D)       ; fetch permanent attribute from ATTR_P.
4051
 
4052
        DEC     B               ; decrement lower screen display file size.
4053
 
4054
        JR      L0D8E           ; forward to enter the backfill loop at CLS-3
4055
                                ; where B is decremented again.
4056
 
4057
; ---
4058
 
4059
;   The backfill loop is entered at midpoint and ensures, if more than 2
4060
;   lines have been cleared, that any other lines take the permanent screen
4061
;   attributes.
4062
 
4063
;; CLS-1
4064
L0D87:  LD      C,$20           ; set counter to 32 character cells per line
4065
 
4066
;; CLS-2
4067
L0D89:  DEC     HL              ; decrease attribute address.
4068
        LD      (HL),A          ; and place attributes in next line up.
4069
        DEC     C               ; decrease the 32 counter.
4070
        JR      NZ,L0D89        ; loop back to CLS-2 until all 32 cells done.
4071
 
4072
;; CLS-3
4073
L0D8E:  DJNZ    L0D87           ; decrease B counter and back to CLS-1
4074
                                ; if not zero.
4075
 
4076
        LD      (IY+$31),$02    ; now set DF_SZ lower screen to 2
4077
 
4078
; This entry point is also called from CL-ALL below to
4079
; reset the system channel input and output addresses to normal.
4080
 
4081
;; CL-CHAN
4082
L0D94:  LD      A,$FD           ; select system channel 'K'
4083
 
4084
        CALL    L1601           ; routine CHAN-OPEN opens it.
4085
 
4086
        LD      HL,($5C51)      ; fetch CURCHL to HL to address current channel
4087
        LD      DE,L09F4        ; set address to PRINT-OUT for first pass.
4088
        AND     A               ; clear carry for first pass.
4089
 
4090
;; CL-CHAN-A
4091
L0DA0:  LD      (HL),E          ; Insert the output address on the first pass
4092
        INC     HL              ; or the input address on the second pass.
4093
        LD      (HL),D          ;
4094
        INC     HL              ;
4095
 
4096
        LD      DE,L10A8        ; fetch address KEY-INPUT for second pass
4097
        CCF                     ; complement carry flag - will set on pass 1.
4098
 
4099
        JR      C,L0DA0         ; back to CL-CHAN-A if first pass else done.
4100
 
4101
        LD      BC,$1721        ; line 23 for lower screen
4102
        JR      L0DD9           ; exit via CL-SET to set column
4103
                                ; for lower display
4104
 
4105
; ---------------------------
4106
; Clearing whole display area
4107
; ---------------------------
4108
; This subroutine called from CLS, AUTO-LIST and MAIN-3
4109
; clears 24 lines of the display and resets the relevant system variables.
4110
; This routine also recovers from an error situation where, for instance, an
4111
; invalid colour or position control code has left the output routine addressing
4112
; PO-TV-2 or PO-CONT.
4113
 
4114
;; CL-ALL
4115
L0DAF:  LD      HL,$0000        ; Initialize plot coordinates.
4116
        LD      ($5C7D),HL      ; Set system variable COORDS to 0,0.
4117
 
4118
        RES     0,(IY+$30)      ; update FLAGS2  - signal main screen is clear.
4119
 
4120
        CALL    L0D94           ; routine CL-CHAN makes channel 'K' 'normal'.
4121
 
4122
        LD      A,$FE           ; select system channel 'S'
4123
        CALL    L1601           ; routine CHAN-OPEN opens it.
4124
 
4125
        CALL    L0D4D           ; routine TEMPS applies permanent attributes,
4126
                                ; in this case ATTR_P, to ATTR_T.
4127
                                ; Note. this seems unnecessary.
4128
 
4129
        LD      B,$18           ; There are 24 lines.
4130
 
4131
        CALL    L0E44           ; routine CL-LINE clears 24 text lines and sets
4132
                                ; attributes from ATTR-P.
4133
                                ; This routine preserves B and sets C to $21.
4134
 
4135
        LD      HL,($5C51)      ; fetch CURCHL make HL address output routine.
4136
 
4137
        LD      DE,L09F4        ; address: PRINT-OUT
4138
        LD      (HL),E          ; is made
4139
        INC     HL              ; the normal
4140
        LD      (HL),D          ; output address.
4141
 
4142
        LD      (IY+$52),$01    ; set SCR_CT - scroll count - to default.
4143
 
4144
;   Note. BC already contains $1821.
4145
 
4146
        LD      BC,$1821        ; reset column and line to 0,0
4147
                                ; and continue into CL-SET, below, exiting
4148
                                ; via PO-STORE (for the upper screen).
4149
 
4150
; --------------------
4151
; THE 'CL-SET' ROUTINE
4152
; --------------------
4153
; This important subroutine is used to calculate the character output
4154
; address for screens or printer based on the line/column for screens
4155
; or the column for printer.
4156
 
4157
;; CL-SET
4158
L0DD9:  LD      HL,$5B00        ; the base address of printer buffer
4159
        BIT     1,(IY+$01)      ; test FLAGS  - is printer in use ?
4160
        JR      NZ,L0DF4        ; forward to CL-SET-2 if so.
4161
 
4162
        LD      A,B             ; transfer line to A.
4163
        BIT     0,(IY+$02)      ; test TV_FLAG  - lower screen in use ?
4164
        JR      Z,L0DEE         ; skip to CL-SET-1 if handling upper part
4165
 
4166
        ADD     A,(IY+$31)      ; add DF_SZ for lower screen
4167
        SUB     $18             ; and adjust.
4168
 
4169
;; CL-SET-1
4170
L0DEE:  PUSH    BC              ; save the line/column.
4171
        LD      B,A             ; transfer line to B
4172
                                ; (adjusted if lower screen)
4173
 
4174
        CALL    L0E9B           ; routine CL-ADDR calculates address at left
4175
                                ; of screen.
4176
        POP     BC              ; restore the line/column.
4177
 
4178
;; CL-SET-2
4179
L0DF4:  LD      A,$21           ; the column $01-$21 is reversed
4180
        SUB     C               ; to range $00 - $20
4181
        LD      E,A             ; now transfer to DE
4182
        LD      D,$00           ; prepare for addition
4183
        ADD     HL,DE           ; and add to base address
4184
 
4185
        JP      L0ADC           ; exit via PO-STORE to update the relevant
4186
                                ; system variables.
4187
; ----------------
4188
; Handle scrolling
4189
; ----------------
4190
; The routine CL-SC-ALL is called once from PO to scroll all the display
4191
; and from the routine CL-SCROLL, once, to scroll part of the display.
4192
 
4193
;; CL-SC-ALL
4194
L0DFE:  LD      B,$17           ; scroll 23 lines, after 'scroll?'.
4195
 
4196
;; CL-SCROLL
4197
L0E00:  CALL    L0E9B           ; routine CL-ADDR gets screen address in HL.
4198
        LD      C,$08           ; there are 8 pixel lines to scroll.
4199
 
4200
;; CL-SCR-1
4201
L0E05:  PUSH    BC              ; save counters.
4202
        PUSH    HL              ; and initial address.
4203
        LD      A,B             ; get line count.
4204
        AND     $07             ; will set zero if all third to be scrolled.
4205
        LD      A,B             ; re-fetch the line count.
4206
        JR      NZ,L0E19        ; forward to CL-SCR-3 if partial scroll.
4207
 
4208
; HL points to top line of third and must be copied to bottom of previous 3rd.
4209
; ( so HL = $4800 or $5000 ) ( but also sometimes $4000 )
4210
 
4211
;; CL-SCR-2
4212
L0E0D:  EX      DE,HL           ; copy HL to DE.
4213
        LD      HL,$F8E0        ; subtract $08 from H and add $E0 to L -
4214
        ADD     HL,DE           ; to make destination bottom line of previous
4215
                                ; third.
4216
        EX      DE,HL           ; restore the source and destination.
4217
        LD      BC,$0020        ; thirty-two bytes are to be copied.
4218
        DEC     A               ; decrement the line count.
4219
        LDIR                    ; copy a pixel line to previous third.
4220
 
4221
;; CL-SCR-3
4222
L0E19:  EX      DE,HL           ; save source in DE.
4223
        LD      HL,$FFE0        ; load the value -32.
4224
        ADD     HL,DE           ; add to form destination in HL.
4225
        EX      DE,HL           ; switch source and destination
4226
        LD      B,A             ; save the count in B.
4227
        AND     $07             ; mask to find count applicable to current
4228
        RRCA                    ; third and
4229
        RRCA                    ; multiply by
4230
        RRCA                    ; thirty two (same as 5 RLCAs)
4231
 
4232
        LD      C,A             ; transfer byte count to C ($E0 at most)
4233
        LD      A,B             ; store line count to A
4234
        LD      B,$00           ; make B zero
4235
        LDIR                    ; copy bytes (BC=0, H incremented, L=0)
4236
        LD      B,$07           ; set B to 7, C is zero.
4237
        ADD     HL,BC           ; add 7 to H to address next third.
4238
        AND     $F8             ; has last third been done ?
4239
        JR      NZ,L0E0D        ; back to CL-SCR-2 if not.
4240
 
4241
        POP     HL              ; restore topmost address.
4242
        INC     H               ; next pixel line down.
4243
        POP     BC              ; restore counts.
4244
        DEC     C               ; reduce pixel line count.
4245
        JR      NZ,L0E05        ; back to CL-SCR-1 if all eight not done.
4246
 
4247
        CALL    L0E88           ; routine CL-ATTR gets address in attributes
4248
                                ; from current 'ninth line', count in BC.
4249
 
4250
        LD      HL,$FFE0        ; set HL to the 16-bit value -32.
4251
        ADD     HL,DE           ; and add to form destination address.
4252
        EX      DE,HL           ; swap source and destination addresses.
4253
        LDIR                    ; copy bytes scrolling the linear attributes.
4254
        LD      B,$01           ; continue to clear the bottom line.
4255
 
4256
; ------------------------------
4257
; THE 'CLEAR TEXT LINES' ROUTINE
4258
; ------------------------------
4259
; This subroutine, called from CL-ALL, CLS-LOWER and AUTO-LIST and above,
4260
; clears text lines at bottom of display.
4261
; The B register holds on entry the number of lines to be cleared 1-24.
4262
 
4263
;; CL-LINE
4264
L0E44:  PUSH    BC              ; save line count
4265
        CALL    L0E9B           ; routine CL-ADDR gets top address
4266
        LD      C,$08           ; there are eight screen lines to a text line.
4267
 
4268
;; CL-LINE-1
4269
L0E4A:  PUSH    BC              ; save pixel line count
4270
        PUSH    HL              ; and save the address
4271
        LD      A,B             ; transfer the line to A (1-24).
4272
 
4273
;; CL-LINE-2
4274
L0E4D:  AND     $07             ; mask 0-7 to consider thirds at a time
4275
        RRCA                    ; multiply
4276
        RRCA                    ; by 32  (same as five RLCA instructions)
4277
        RRCA                    ; now 32 - 256(0)
4278
        LD      C,A             ; store result in C
4279
        LD      A,B             ; save line in A (1-24)
4280
        LD      B,$00           ; set high byte to 0, prepare for ldir.
4281
        DEC     C               ; decrement count 31-255.
4282
        LD      D,H             ; copy HL
4283
        LD      E,L             ; to DE.
4284
        LD      (HL),$00        ; blank the first byte.
4285
        INC     DE              ; make DE point to next byte.
4286
        LDIR                    ; ldir will clear lines.
4287
        LD      DE,$0701        ; now address next third adjusting
4288
        ADD     HL,DE           ; register E to address left hand side
4289
        DEC     A               ; decrease the line count.
4290
        AND     $F8             ; will be 16, 8 or 0  (AND $18 will do).
4291
        LD      B,A             ; transfer count to B.
4292
        JR      NZ,L0E4D        ; back to CL-LINE-2 if 16 or 8 to do
4293
                                ; the next third.
4294
 
4295
        POP     HL              ; restore start address.
4296
        INC     H               ; address next line down.
4297
        POP     BC              ; fetch counts.
4298
        DEC     C               ; decrement pixel line count
4299
        JR      NZ,L0E4A        ; back to CL-LINE-1 till all done.
4300
 
4301
        CALL    L0E88           ; routine CL-ATTR gets attribute address
4302
                                ; in DE and B * 32 in BC.
4303
 
4304
        LD      H,D             ; transfer the address
4305
        LD      L,E             ; to HL.
4306
 
4307
        INC     DE              ; make DE point to next location.
4308
 
4309
        LD      A,($5C8D)       ; fetch ATTR_P - permanent attributes
4310
        BIT     0,(IY+$02)      ; test TV_FLAG  - lower screen in use ?
4311
        JR      Z,L0E80         ; skip to CL-LINE-3 if not.
4312
 
4313
        LD      A,($5C48)       ; else lower screen uses BORDCR as attribute.
4314
 
4315
;; CL-LINE-3
4316
L0E80:  LD      (HL),A          ; put attribute in first byte.
4317
        DEC     BC              ; decrement the counter.
4318
        LDIR                    ; copy bytes to set all attributes.
4319
        POP     BC              ; restore the line $01-$24.
4320
        LD      C,$21           ; make column $21. (No use is made of this)
4321
        RET                     ; return to the calling routine.
4322
 
4323
; ------------------
4324
; Attribute handling
4325
; ------------------
4326
; This subroutine is called from CL-LINE or CL-SCROLL with the HL register
4327
; pointing to the 'ninth' line and H needs to be decremented before or after
4328
; the division. Had it been done first then either present code or that used
4329
; at the start of PO-ATTR could have been used.
4330
; The Spectrum screen arrangement leads to the L register already holding
4331
; the correct value for the attribute file and it is only necessary
4332
; to manipulate H to form the correct colour attribute address.
4333
 
4334
;; CL-ATTR
4335
L0E88:  LD      A,H             ; fetch H to A - $48, $50, or $58.
4336
        RRCA                    ; divide by
4337
        RRCA                    ; eight.
4338
        RRCA                    ; $09, $0A or $0B.
4339
        DEC     A               ; $08, $09 or $0A.
4340
        OR      $50             ; $58, $59 or $5A.
4341
        LD      H,A             ; save high byte of attributes.
4342
 
4343
        EX      DE,HL           ; transfer attribute address to DE
4344
        LD      H,C             ; set H to zero - from last LDIR.
4345
        LD      L,B             ; load L with the line from B.
4346
        ADD     HL,HL           ; multiply
4347
        ADD     HL,HL           ; by
4348
        ADD     HL,HL           ; thirty two
4349
        ADD     HL,HL           ; to give count of attribute
4350
        ADD     HL,HL           ; cells to the end of display.
4351
 
4352
        LD      B,H             ; transfer the result
4353
        LD      C,L             ; to register BC.
4354
 
4355
        RET                     ; return.
4356
 
4357
; -------------------------------
4358
; Handle display with line number
4359
; -------------------------------
4360
; This subroutine is called from four places to calculate the address
4361
; of the start of a screen character line which is supplied in B.
4362
 
4363
;; CL-ADDR
4364
L0E9B:  LD      A,$18           ; reverse the line number
4365
        SUB     B               ; to range $00 - $17.
4366
        LD      D,A             ; save line in D for later.
4367
        RRCA                    ; multiply
4368
        RRCA                    ; by
4369
        RRCA                    ; thirty-two.
4370
 
4371
        AND     $E0             ; mask off low bits to make
4372
        LD      L,A             ; L a multiple of 32.
4373
 
4374
        LD      A,D             ; bring back the line to A.
4375
 
4376
        AND     $18             ; now $00, $08 or $10.
4377
 
4378
        OR      $40             ; add the base address of screen.
4379
 
4380
        LD      H,A             ; HL now has the correct address.
4381
        RET                     ; return.
4382
 
4383
; -------------------
4384
; Handle COPY command
4385
; -------------------
4386
; This command copies the top 176 lines to the ZX Printer
4387
; It is popular to call this from machine code at point
4388
; L0EAF with B holding 192 (and interrupts disabled) for a full-screen
4389
; copy. This particularly applies to 16K Spectrums as time-critical
4390
; machine code routines cannot be written in the first 16K of RAM as
4391
; it is shared with the ULA which has precedence over the Z80 chip.
4392
 
4393
;; COPY
4394
L0EAC:  DI                      ; disable interrupts as this is time-critical.
4395
 
4396
        LD      B,$B0           ; top 176 lines.
4397
L0EAF:  LD      HL,$4000        ; address start of the display file.
4398
 
4399
; now enter a loop to handle each pixel line.
4400
 
4401
;; COPY-1
4402
L0EB2:  PUSH    HL              ; save the screen address.
4403
        PUSH    BC              ; and the line counter.
4404
 
4405
        CALL    L0EF4           ; routine COPY-LINE outputs one line.
4406
 
4407
        POP     BC              ; restore the line counter.
4408
        POP     HL              ; and display address.
4409
        INC     H               ; next line down screen within 'thirds'.
4410
        LD      A,H             ; high byte to A.
4411
        AND     $07             ; result will be zero if we have left third.
4412
        JR      NZ,L0EC9        ; forward to COPY-2 if not to continue loop.
4413
 
4414
        LD      A,L             ; consider low byte first.
4415
        ADD     A,$20           ; increase by 32 - sets carry if back to zero.
4416
        LD      L,A             ; will be next group of 8.
4417
        CCF                     ; complement - carry set if more lines in
4418
                                ; the previous third.
4419
        SBC     A,A             ; will be FF, if more, else 00.
4420
        AND     $F8             ; will be F8 (-8) or 00.
4421
        ADD     A,H             ; that is subtract 8, if more to do in third.
4422
        LD      H,A             ; and reset address.
4423
 
4424
;; COPY-2
4425
L0EC9:  DJNZ    L0EB2           ; back to COPY-1 for all lines.
4426
 
4427
        JR      L0EDA           ; forward to COPY-END to switch off the printer
4428
                                ; motor and enable interrupts.
4429
                                ; Note. Nothing else is required.
4430
 
4431
; ------------------------------
4432
; Pass printer buffer to printer
4433
; ------------------------------
4434
; This routine is used to copy 8 text lines from the printer buffer
4435
; to the ZX Printer. These text lines are mapped linearly so HL does
4436
; not need to be adjusted at the end of each line.
4437
 
4438
;; COPY-BUFF
4439
L0ECD:  DI                      ; disable interrupts
4440
        LD      HL,$5B00        ; the base address of the Printer Buffer.
4441
        LD      B,$08           ; set count to 8 lines of 32 bytes.
4442
 
4443
;; COPY-3
4444
L0ED3:  PUSH    BC              ; save counter.
4445
 
4446
        CALL    L0EF4           ; routine COPY-LINE outputs 32 bytes
4447
 
4448
        POP     BC              ; restore counter.
4449
        DJNZ    L0ED3           ; loop back to COPY-3 for all 8 lines.
4450
                                ; then stop motor and clear buffer.
4451
 
4452
; Note. the COPY command rejoins here, essentially to execute the next
4453
; three instructions.
4454
 
4455
;; COPY-END
4456
L0EDA:  LD      A,$04           ; output value 4 to port
4457
        OUT     ($FB),A         ; to stop the slowed printer motor.
4458
        EI                      ; enable interrupts.
4459
 
4460
; --------------------
4461
; Clear Printer Buffer
4462
; --------------------
4463
; This routine clears an arbitrary 256 bytes of memory.
4464
; Note. The routine seems designed to clear a buffer that follows the
4465
; system variables.
4466
; The routine should check a flag or HL address and simply return if COPY
4467
; is in use.
4468
; As a consequence of this omission the buffer will needlessly
4469
; be cleared when COPY is used and the screen/printer position may be set to
4470
; the start of the buffer and the line number to 0 (B)
4471
; giving an 'Out of Screen' error.
4472
; There seems to have been an unsuccessful attempt to circumvent the use
4473
; of PR_CC_hi.
4474
 
4475
;; CLEAR-PRB
4476
L0EDF:  LD      HL,$5B00        ; the location of the buffer.
4477
        LD      (IY+$46),L      ; update PR_CC_lo - set to zero - superfluous.
4478
        XOR     A               ; clear the accumulator.
4479
        LD      B,A             ; set count to 256 bytes.
4480
 
4481
;; PRB-BYTES
4482
L0EE7:  LD      (HL),A          ; set addressed location to zero.
4483
        INC     HL              ; address next byte - Note. not INC L.
4484
        DJNZ    L0EE7           ; back to PRB-BYTES. repeat for 256 bytes.
4485
 
4486
        RES     1,(IY+$30)      ; set FLAGS2 - signal printer buffer is clear.
4487
        LD      C,$21           ; set the column position .
4488
        JP      L0DD9           ; exit via CL-SET and then PO-STORE.
4489
 
4490
; -----------------
4491
; Copy line routine
4492
; -----------------
4493
; This routine is called from COPY and COPY-BUFF to output a line of
4494
; 32 bytes to the ZX Printer.
4495
; Output to port $FB -
4496
; bit 7 set - activate stylus.
4497
; bit 7 low - deactivate stylus.
4498
; bit 2 set - stops printer.
4499
; bit 2 reset - starts printer
4500
; bit 1 set - slows printer.
4501
; bit 1 reset - normal speed.
4502
 
4503
;; COPY-LINE
4504
L0EF4:  LD      A,B             ; fetch the counter 1-8 or 1-176
4505
        CP      $03             ; is it 01 or 02 ?.
4506
        SBC     A,A             ; result is $FF if so else $00.
4507
        AND     $02             ; result is 02 now else 00.
4508
                                ; bit 1 set slows the printer.
4509
        OUT     ($FB),A         ; slow the printer for the
4510
                                ; last two lines.
4511
        LD      D,A             ; save the mask to control the printer later.
4512
 
4513
;; COPY-L-1
4514
L0EFD:  CALL    L1F54           ; call BREAK-KEY to read keyboard immediately.
4515
        JR      C,L0F0C         ; forward to COPY-L-2 if 'break' not pressed.
4516
 
4517
        LD      A,$04           ; else stop the
4518
        OUT     ($FB),A         ; printer motor.
4519
        EI                      ; enable interrupts.
4520
        CALL    L0EDF           ; call routine CLEAR-PRB.
4521
                                ; Note. should not be cleared if COPY in use.
4522
 
4523
;; REPORT-Dc
4524
L0F0A:  RST     08H             ; ERROR-1
4525
        DEFB    $0C             ; Error Report: BREAK - CONT repeats
4526
 
4527
;; COPY-L-2
4528
L0F0C:  IN      A,($FB)         ; test now to see if
4529
        ADD     A,A             ; a printer is attached.
4530
        RET     M               ; return if not - but continue with parent
4531
                                ; command.
4532
 
4533
        JR      NC,L0EFD        ; back to COPY-L-1 if stylus of printer not
4534
                                ; in position.
4535
 
4536
        LD      C,$20           ; set count to 32 bytes.
4537
 
4538
;; COPY-L-3
4539
L0F14:  LD      E,(HL)          ; fetch a byte from line.
4540
        INC     HL              ; address next location. Note. not INC L.
4541
        LD      B,$08           ; count the bits.
4542
 
4543
;; COPY-L-4
4544
L0F18:  RL      D               ; prepare mask to receive bit.
4545
        RL      E               ; rotate leftmost print bit to carry
4546
        RR      D               ; and back to bit 7 of D restoring bit 1
4547
 
4548
;; COPY-L-5
4549
L0F1E:  IN      A,($FB)         ; read the port.
4550
        RRA                     ; bit 0 to carry.
4551
        JR      NC,L0F1E        ; back to COPY-L-5 if stylus not in position.
4552
 
4553
        LD      A,D             ; transfer command bits to A.
4554
        OUT     ($FB),A         ; and output to port.
4555
        DJNZ    L0F18           ; loop back to COPY-L-4 for all 8 bits.
4556
 
4557
        DEC     C               ; decrease the byte count.
4558
        JR      NZ,L0F14        ; back to COPY-L-3 until 256 bits done.
4559
 
4560
        RET                     ; return to calling routine COPY/COPY-BUFF.
4561
 
4562
 
4563
; ----------------------------------
4564
; Editor routine for BASIC and INPUT
4565
; ----------------------------------
4566
; The editor is called to prepare or edit a BASIC line.
4567
; It is also called from INPUT to input a numeric or string expression.
4568
; The behaviour and options are quite different in the various modes
4569
; and distinguished by bit 5 of FLAGX.
4570
;
4571
; This is a compact and highly versatile routine.
4572
 
4573
;; EDITOR
4574
L0F2C:  LD      HL,($5C3D)      ; fetch ERR_SP
4575
        PUSH    HL              ; save on stack
4576
 
4577
;; ED-AGAIN
4578
L0F30:  LD      HL,L107F        ; address: ED-ERROR
4579
        PUSH    HL              ; save address on stack and
4580
        LD      ($5C3D),SP      ; make ERR_SP point to it.
4581
 
4582
; Note. While in editing/input mode should an error occur then RST 08 will
4583
; update X_PTR to the location reached by CH_ADD and jump to ED-ERROR
4584
; where the error will be cancelled and the loop begin again from ED-AGAIN
4585
; above. The position of the error will be apparent when the lower screen is
4586
; reprinted. If no error then the re-iteration is to ED-LOOP below when
4587
; input is arriving from the keyboard.
4588
 
4589
;; ED-LOOP
4590
L0F38:  CALL    L15D4           ; routine WAIT-KEY gets key possibly
4591
                                ; changing the mode.
4592
        PUSH    AF              ; save key.
4593
        LD      D,$00           ; and give a short click based
4594
        LD      E,(IY-$01)      ; on PIP value for duration.
4595
        LD      HL,$00C8        ; and pitch.
4596
        CALL    L03B5           ; routine BEEPER gives click - effective
4597
                                ; with rubber keyboard.
4598
        POP     AF              ; get saved key value.
4599
        LD      HL,L0F38        ; address: ED-LOOP is loaded to HL.
4600
        PUSH    HL              ; and pushed onto stack.
4601
 
4602
; At this point there is a looping return address on the stack, an error
4603
; handler and an input stream set up to supply characters.
4604
; The character that has been received can now be processed.
4605
 
4606
        CP      $18             ; range 24 to 255 ?
4607
        JR      NC,L0F81        ; forward to ADD-CHAR if so.
4608
 
4609
        CP      $07             ; lower than 7 ?
4610
        JR      C,L0F81         ; forward to ADD-CHAR also.
4611
                                ; Note. This is a 'bug' and chr$ 6, the comma
4612
                                ; control character, should have had an
4613
                                ; entry in the ED-KEYS table.
4614
                                ; Steven Vickers, 1984, Pitman.
4615
 
4616
        CP      $10             ; less than 16 ?
4617
        JR      C,L0F92         ; forward to ED-KEYS if editing control
4618
                                ; range 7 to 15 dealt with by a table
4619
 
4620
        LD      BC,$0002        ; prepare for ink/paper etc.
4621
        LD      D,A             ; save character in D
4622
        CP      $16             ; is it ink/paper/bright etc. ?
4623
        JR      C,L0F6C         ; forward to ED-CONTR if so
4624
 
4625
                                ; leaves 22d AT and 23d TAB
4626
                                ; which can't be entered via KEY-INPUT.
4627
                                ; so this code is never normally executed
4628
                                ; when the keyboard is used for input.
4629
 
4630
        INC     BC              ; if it was AT/TAB - 3 locations required
4631
        BIT     7,(IY+$37)      ; test FLAGX  - Is this INPUT LINE ?
4632
        JP      Z,L101E         ; jump to ED-IGNORE if not, else
4633
 
4634
        CALL    L15D4           ; routine WAIT-KEY - input address is KEY-NEXT
4635
                                ; but is reset to KEY-INPUT
4636
        LD      E,A             ; save first in E
4637
 
4638
;; ED-CONTR
4639
L0F6C:  CALL    L15D4           ; routine WAIT-KEY for control.
4640
                                ; input address will be key-next.
4641
 
4642
        PUSH    DE              ; saved code/parameters
4643
        LD      HL,($5C5B)      ; fetch address of keyboard cursor from K_CUR
4644
        RES     0,(IY+$07)      ; set MODE to 'L'
4645
 
4646
        CALL    L1655           ; routine MAKE-ROOM makes 2/3 spaces at cursor
4647
 
4648
        POP     BC              ; restore code/parameters
4649
        INC     HL              ; address first location
4650
        LD      (HL),B          ; place code (ink etc.)
4651
        INC     HL              ; address next
4652
        LD      (HL),C          ; place possible parameter. If only one
4653
                                ; then DE points to this location also.
4654
        JR      L0F8B           ; forward to ADD-CH-1
4655
 
4656
; ------------------------
4657
; Add code to current line
4658
; ------------------------
4659
; this is the branch used to add normal non-control characters
4660
; with ED-LOOP as the stacked return address.
4661
; it is also the OUTPUT service routine for system channel 'R'.
4662
 
4663
;; ADD-CHAR
4664
L0F81:  RES     0,(IY+$07)      ; set MODE to 'L'
4665
 
4666
X0F85:  LD      HL,($5C5B)      ; fetch address of keyboard cursor from K_CUR
4667
 
4668
        CALL    L1652           ; routine ONE-SPACE creates one space.
4669
 
4670
; either a continuation of above or from ED-CONTR with ED-LOOP on stack.
4671
 
4672
;; ADD-CH-1
4673
L0F8B:  LD      (DE),A          ; load current character to last new location.
4674
        INC     DE              ; address next
4675
        LD      ($5C5B),DE      ; and update K_CUR system variable.
4676
        RET                     ; return - either a simple return
4677
                                ; from ADD-CHAR or to ED-LOOP on stack.
4678
 
4679
; ---
4680
 
4681
; a branch of the editing loop to deal with control characters
4682
; using a look-up table.
4683
 
4684
;; ED-KEYS
4685
L0F92:  LD      E,A             ; character to E.
4686
        LD      D,$00           ; prepare to add.
4687
        LD      HL,L0FA0 - 7    ; base address of editing keys table. $0F99
4688
        ADD     HL,DE           ; add E
4689
        LD      E,(HL)          ; fetch offset to E
4690
        ADD     HL,DE           ; add offset for address of handling routine.
4691
        PUSH    HL              ; push the address on machine stack.
4692
        LD      HL,($5C5B)      ; load address of cursor from K_CUR.
4693
        RET                     ; Make an indirect jump forward to routine.
4694
 
4695
; ------------------
4696
; Editing keys table
4697
; ------------------
4698
; For each code in the range $07 to $0F this table contains a
4699
; single offset byte to the routine that services that code.
4700
; Note. for what was intended there should also have been an
4701
; entry for chr$ 6 with offset to ed-symbol.
4702
 
4703
;; ed-keys-t
4704
L0FA0:  DEFB    L0FA9 - $  ; 07d offset $09 to Address: ED-EDIT
4705
        DEFB    L1007 - $  ; 08d offset $66 to Address: ED-LEFT
4706
        DEFB    L100C - $  ; 09d offset $6A to Address: ED-RIGHT
4707
        DEFB    L0FF3 - $  ; 10d offset $50 to Address: ED-DOWN
4708
        DEFB    L1059 - $  ; 11d offset $B5 to Address: ED-UP
4709
        DEFB    L1015 - $  ; 12d offset $70 to Address: ED-DELETE
4710
        DEFB    L1024 - $  ; 13d offset $7E to Address: ED-ENTER
4711
        DEFB    L1076 - $  ; 14d offset $CF to Address: ED-SYMBOL
4712
        DEFB    L107C - $  ; 15d offset $D4 to Address: ED-GRAPH
4713
 
4714
; ---------------
4715
; Handle EDIT key
4716
; ---------------
4717
; The user has pressed SHIFT 1 to bring edit line down to bottom of screen.
4718
; Alternatively the user wishes to clear the input buffer and start again.
4719
; Alternatively ...
4720
 
4721
;; ED-EDIT
4722
L0FA9:  LD      HL,($5C49)      ; fetch E_PPC the last line number entered.
4723
                                ; Note. may not exist and may follow program.
4724
        BIT     5,(IY+$37)      ; test FLAGX  - input mode ?
4725
        JP      NZ,L1097        ; jump forward to CLEAR-SP if not in editor.
4726
 
4727
        CALL    L196E           ; routine LINE-ADDR to find address of line
4728
                                ; or following line if it doesn't exist.
4729
        CALL    L1695           ; routine LINE-NO will get line number from
4730
                                ; address or previous line if at end-marker.
4731
        LD      A,D             ; if there is no program then DE will
4732
        OR      E               ; contain zero so test for this.
4733
        JP      Z,L1097         ; jump to CLEAR-SP if so.
4734
 
4735
; Note. at this point we have a validated line number, not just an
4736
; approximation and it would be best to update E_PPC with the true
4737
; cursor line value which would enable the line cursor to be suppressed
4738
; in all situations - see shortly.
4739
 
4740
        PUSH    HL              ; save address of line.
4741
        INC     HL              ; address low byte of length.
4742
        LD      C,(HL)          ; transfer to C
4743
        INC     HL              ; next to high byte
4744
        LD      B,(HL)          ; transfer to B.
4745
        LD      HL,$000A        ; an overhead of ten bytes
4746
        ADD     HL,BC           ; is added to length.
4747
        LD      B,H             ; transfer adjusted value
4748
        LD      C,L             ; to BC register.
4749
        CALL    L1F05           ; routine TEST-ROOM checks free memory.
4750
        CALL    L1097           ; routine CLEAR-SP clears editing area.
4751
        LD      HL,($5C51)      ; address CURCHL
4752
        EX      (SP),HL         ; swap with line address on stack
4753
        PUSH    HL              ; save line address underneath
4754
 
4755
        LD      A,$FF           ; select system channel 'R'
4756
        CALL    L1601           ; routine CHAN-OPEN opens it
4757
 
4758
        POP     HL              ; drop line address
4759
        DEC     HL              ; make it point to first byte of line num.
4760
        DEC     (IY+$0F)        ; decrease E_PPC_lo to suppress line cursor.
4761
                                ; Note. ineffective when E_PPC is one
4762
                                ; greater than last line of program perhaps
4763
                                ; as a result of a delete.
4764
                                ; credit. Paul Harrison 1982.
4765
 
4766
        CALL    L1855           ; routine OUT-LINE outputs the BASIC line
4767
                                ; to the editing area.
4768
        INC     (IY+$0F)        ; restore E_PPC_lo to the previous value.
4769
        LD      HL,($5C59)      ; address E_LINE in editing area.
4770
        INC     HL              ; advance
4771
        INC     HL              ; past space
4772
        INC     HL              ; and digit characters
4773
        INC     HL              ; of line number.
4774
 
4775
        LD      ($5C5B),HL      ; update K_CUR to address start of BASIC.
4776
        POP     HL              ; restore the address of CURCHL.
4777
        CALL    L1615           ; routine CHAN-FLAG sets flags for it.
4778
 
4779
        RET                     ; RETURN to ED-LOOP.
4780
 
4781
; -------------------
4782
; Cursor down editing
4783
; -------------------
4784
;   The BASIC lines are displayed at the top of the screen and the user
4785
;   wishes to move the cursor down one line in edit mode.
4786
;   With INPUT LINE, this key must be used instead of entering STOP.
4787
 
4788
;; ED-DOWN
4789
L0FF3:  BIT     5,(IY+$37)      ; test FLAGX  - Input Mode ?
4790
        JR      NZ,L1001        ; skip to ED-STOP if so
4791
 
4792
        LD      HL,$5C49        ; address E_PPC - 'current line'
4793
        CALL    L190F           ; routine LN-FETCH fetches number of next
4794
                                ; line or same if at end of program.