-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathzx81 version 2 rom source.txt
10556 lines (8288 loc) · 354 KB
/
zx81 version 2 rom source.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
; ===========================================================
; An Assembly Listing of the Operating System of the ZX81 ROM
; ===========================================================
; -------------------------
; Last updated: 13-DEC-2004
; -------------------------
;
; Work in progress.
; This file will cross-assemble an original version of the "Improved"
; ZX81 ROM. The file can be modified to change the behaviour of the ROM
; when used in emulators although there is no spare space available.
;
; The documentation is incomplete and if you can find a copy
; of "The Complete Spectrum ROM Disassembly" then many routines
; such as POINTERS and most of the mathematical routines are
; similar and often identical.
;
; I've used the labels from the above book in this file and also
; some from the more elusive Complete ZX81 ROM Disassembly
; by the same publishers, Melbourne House.
#define DEFB .BYTE ; TASM cross-assembler definitions
#define DEFW .WORD
#define EQU .EQU
;*****************************************
;** Part 1. RESTART ROUTINES AND TABLES **
;*****************************************
; -----------
; THE 'START'
; -----------
; All Z80 chips start at location zero.
; At start-up the Interrupt Mode is 0, ZX computers use Interrupt Mode 1.
; Interrupts are disabled .
;; START
L0000: OUT ($FD),A ; Turn off the NMI generator if this ROM is
; running in ZX81 hardware. This does nothing
; if this ROM is running within an upgraded
; ZX80.
LD BC,$7FFF ; Set BC to the top of possible RAM.
; The higher unpopulated addresses are used for
; video generation.
JP L03CB ; Jump forward to RAM-CHECK.
; -------------------
; THE 'ERROR' RESTART
; -------------------
; The error restart deals immediately with an error. ZX computers execute the
; same code in runtime as when checking syntax. If the error occurred while
; running a program then a brief report is produced. If the error occurred
; while entering a BASIC line or in input etc., then the error marker indicates
; the exact point at which the error lies.
;; ERROR-1
L0008: LD HL,($4016) ; fetch character address from CH_ADD.
LD ($4018),HL ; and set the error pointer X_PTR.
JR L0056 ; forward to continue at ERROR-2.
; -------------------------------
; THE 'PRINT A CHARACTER' RESTART
; -------------------------------
; This restart prints the character in the accumulator using the alternate
; register set so there is no requirement to save the main registers.
; There is sufficient room available to separate a space (zero) from other
; characters as leading spaces need not be considered with a space.
;; PRINT-A
L0010: AND A ; test for zero - space.
JP NZ,L07F1 ; jump forward if not to PRINT-CH.
JP L07F5 ; jump forward to PRINT-SP.
; ---
DEFB $FF ; unused location.
; ---------------------------------
; THE 'COLLECT A CHARACTER' RESTART
; ---------------------------------
; The character addressed by the system variable CH_ADD is fetched and if it
; is a non-space, non-cursor character it is returned else CH_ADD is
; incremented and the new addressed character tested until it is not a space.
;; GET-CHAR
L0018: LD HL,($4016) ; set HL to character address CH_ADD.
LD A,(HL) ; fetch addressed character to A.
;; TEST-SP
L001C: AND A ; test for space.
RET NZ ; return if not a space
NOP ; else trickle through
NOP ; to the next routine.
; ------------------------------------
; THE 'COLLECT NEXT CHARACTER' RESTART
; ------------------------------------
; The character address in incremented and the new addressed character is
; returned if not a space, or cursor, else the process is repeated.
;; NEXT-CHAR
L0020: CALL L0049 ; routine CH-ADD+1 gets next immediate
; character.
JR L001C ; back to TEST-SP.
; ---
DEFB $FF, $FF, $FF ; unused locations.
; ---------------------------------------
; THE 'FLOATING POINT CALCULATOR' RESTART
; ---------------------------------------
; this restart jumps to the recursive floating-point calculator.
; the ZX81's internal, FORTH-like, stack-based language.
;
; In the five remaining bytes there is, appropriately, enough room for the
; end-calc literal - the instruction which exits the calculator.
;; FP-CALC
L0028: JP L199D ; jump immediately to the CALCULATE routine.
; ---
;; end-calc
L002B: POP AF ; drop the calculator return address RE-ENTRY
EXX ; switch to the other set.
EX (SP),HL ; transfer H'L' to machine stack for the
; return address.
; when exiting recursion then the previous
; pointer is transferred to H'L'.
EXX ; back to main set.
RET ; return.
; -----------------------------
; THE 'MAKE BC SPACES' RESTART
; -----------------------------
; This restart is used eight times to create, in workspace, the number of
; spaces passed in the BC register.
;; BC-SPACES
L0030: PUSH BC ; push number of spaces on stack.
LD HL,($4014) ; fetch edit line location from E_LINE.
PUSH HL ; save this value on stack.
JP L1488 ; jump forward to continue at RESERVE.
; -----------------------
; THE 'INTERRUPT' RESTART
; -----------------------
; The Mode 1 Interrupt routine is concerned solely with generating the central
; television picture.
; On the ZX81 interrupts are enabled only during the interrupt routine,
; although the interrupt
; This Interrupt Service Routine automatically disables interrupts at the
; outset and the last interrupt in a cascade exits before the interrupts are
; enabled.
; There is no DI instruction in the ZX81 ROM.
; An maskable interrupt is triggered when bit 6 of the Z80's Refresh register
; changes from set to reset.
; The Z80 will always be executing a HALT (NEWLINE) when the interrupt occurs.
; A HALT instruction repeatedly executes NOPS but the seven lower bits
; of the Refresh register are incremented each time as they are when any
; simple instruction is executed. (The lower 7 bits are incremented twice for
; a prefixed instruction)
; This is controlled by the Sinclair Computer Logic Chip - manufactured from
; a Ferranti Uncommitted Logic Array.
;
; When a Mode 1 Interrupt occurs the Program Counter, which is the address in
; the upper echo display following the NEWLINE/HALT instruction, goes on the
; machine stack. 193 interrupts are required to generate the last part of
; the 56th border line and then the 192 lines of the central TV picture and,
; although each interrupt interrupts the previous one, there are no stack
; problems as the 'return address' is discarded each time.
;
; The scan line counter in C counts down from 8 to 1 within the generation of
; each text line. For the first interrupt in a cascade the initial value of
; C is set to 1 for the last border line.
; Timing is of the utmost importance as the RH border, horizontal retrace
; and LH border are mostly generated in the 58 clock cycles this routine
; takes .
;; INTERRUPT
L0038: DEC C ; (4) decrement C - the scan line counter.
JP NZ,L0045 ; (10/10) JUMP forward if not zero to SCAN-LINE
POP HL ; (10) point to start of next row in display
; file.
DEC B ; (4) decrement the row counter. (4)
RET Z ; (11/5) return when picture complete to L028B
; with interrupts disabled.
SET 3,C ; (8) Load the scan line counter with eight.
; Note. LD C,$08 is 7 clock cycles which
; is way too fast.
; ->
;; WAIT-INT
L0041: LD R,A ; (9) Load R with initial rising value $DD.
EI ; (4) Enable Interrupts. [ R is now $DE ].
JP (HL) ; (4) jump to the echo display file in upper
; memory and execute characters $00 - $3F
; as NOP instructions. The video hardware
; is able to read these characters and,
; with the I register is able to convert
; the character bitmaps in this ROM into a
; line of bytes. Eventually the NEWLINE/HALT
; will be encountered before R reaches $FF.
; It is however the transition from $FF to
; $80 that triggers the next interrupt.
; [ The Refresh register is now $DF ]
; ---
;; SCAN-LINE
L0045: POP DE ; (10) discard the address after NEWLINE as the
; same text line has to be done again
; eight times.
RET Z ; (5) Harmless Nonsensical Timing.
; (condition never met)
JR L0041 ; (12) back to WAIT-INT
; Note. that a computer with less than 4K or RAM will have a collapsed
; display file and the above mechanism deals with both types of display.
;
; With a full display, the 32 characters in the line are treated as NOPS
; and the Refresh register rises from $E0 to $FF and, at the next instruction
; - HALT, the interrupt occurs.
; With a collapsed display and an initial NEWLINE/HALT, it is the NOPs
; generated by the HALT that cause the Refresh value to rise from $E0 to $FF,
; triggering an Interrupt on the next transition.
; This works happily for all display lines between these extremes and the
; generation of the 32 character, 1 pixel high, line will always take 128
; clock cycles.
; ---------------------------------
; THE 'INCREMENT CH-ADD' SUBROUTINE
; ---------------------------------
; This is the subroutine that increments the character address system variable
; and returns if it is not the cursor character. The ZX81 has an actual
; character at the cursor position rather than a pointer system variable
; as is the case with prior and subsequent ZX computers.
;; CH-ADD+1
L0049: LD HL,($4016) ; fetch character address to CH_ADD.
;; TEMP-PTR1
L004C: INC HL ; address next immediate location.
;; TEMP-PTR2
L004D: LD ($4016),HL ; update system variable CH_ADD.
LD A,(HL) ; fetch the character.
CP $7F ; compare to cursor character.
RET NZ ; return if not the cursor.
JR L004C ; back for next character to TEMP-PTR1.
; --------------------
; THE 'ERROR-2' BRANCH
; --------------------
; This is a continuation of the error restart.
; If the error occurred in runtime then the error stack pointer will probably
; lead to an error report being printed unless it occurred during input.
; If the error occurred when checking syntax then the error stack pointer
; will be an editing routine and the position of the error will be shown
; when the lower screen is reprinted.
;; ERROR-2
L0056: POP HL ; pop the return address which points to the
; DEFB, error code, after the RST 08.
LD L,(HL) ; load L with the error code. HL is not needed
; anymore.
;; ERROR-3
L0058: LD (IY+$00),L ; place error code in system variable ERR_NR
LD SP,($4002) ; set the stack pointer from ERR_SP
CALL L0207 ; routine SLOW/FAST selects slow mode.
JP L14BC ; exit to address on stack via routine SET-MIN.
; ---
DEFB $FF ; unused.
; ------------------------------------
; THE 'NON MASKABLE INTERRUPT' ROUTINE
; ------------------------------------
; Jim Westwood's technical dodge using Non-Maskable Interrupts solved the
; flicker problem of the ZX80 and gave the ZX81 a multi-tasking SLOW mode
; with a steady display. Note that the AF' register is reserved for this
; function and its interaction with the display routines. When counting
; TV lines, the NMI makes no use of the main registers.
; The circuitry for the NMI generator is contained within the SCL (Sinclair
; Computer Logic) chip.
; ( It takes 32 clock cycles while incrementing towards zero ).
;; NMI
L0066: EX AF,AF' ; (4) switch in the NMI's copy of the
; accumulator.
INC A ; (4) increment.
JP M,L006D ; (10/10) jump, if minus, to NMI-RET as this is
; part of a test to see if the NMI
; generation is working or an intermediate
; value for the ascending negated blank
; line counter.
JR Z,L006F ; (12) forward to NMI-CONT
; when line count has incremented to zero.
; Note. the synchronizing NMI when A increments from zero to one takes this
; 7 clock cycle route making 39 clock cycles in all.
;; NMI-RET
L006D: EX AF,AF' ; (4) switch out the incremented line counter
; or test result $80
RET ; (10) return to User application for a while.
; ---
; This branch is taken when the 55 (or 31) lines have been drawn.
;; NMI-CONT
L006F: EX AF,AF' ; (4) restore the main accumulator.
PUSH AF ; (11) * Save Main Registers
PUSH BC ; (11) **
PUSH DE ; (11) ***
PUSH HL ; (11) ****
; the next set-up procedure is only really applicable when the top set of
; blank lines have been generated.
LD HL,($400C) ; (16) fetch start of Display File from D_FILE
; points to the HALT at beginning.
SET 7,H ; (8) point to upper 32K 'echo display file'
HALT ; (1) HALT synchronizes with NMI.
; Used with special hardware connected to the
; Z80 HALT and WAIT lines to take 1 clock cycle.
; ----------------------------------------------------------------------------
; the NMI has been generated - start counting. The cathode ray is at the RH
; side of the TV.
; First the NMI servicing, similar to CALL = 17 clock cycles.
; Then the time taken by the NMI for zero-to-one path = 39 cycles
; The HALT above = 01 cycles.
; The two instructions below = 19 cycles.
; The code at L0281 up to and including the CALL = 43 cycles.
; The Called routine at L02B5 = 24 cycles.
; -------------------------------------- ---
; Total Z80 instructions = 143 cycles.
;
; Meanwhile in TV world,
; Horizontal retrace = 15 cycles.
; Left blanking border 8 character positions = 32 cycles
; Generation of 75% scanline from the first NEWLINE = 96 cycles
; --------------------------------------- ---
; 143 cycles
;
; Since at the time the first JP (HL) is encountered to execute the echo
; display another 8 character positions have to be put out, then the
; Refresh register need to hold $F8. Working back and counteracting
; the fact that every instruction increments the Refresh register then
; the value that is loaded into R needs to be $F5. :-)
;
;
OUT ($FD),A ; (11) Stop the NMI generator.
JP (IX) ; (8) forward to L0281 (after top) or L028F
; ****************
; ** KEY TABLES **
; ****************
; -------------------------------
; THE 'UNSHIFTED' CHARACTER CODES
; -------------------------------
;; K-UNSHIFT
L007E: DEFB $3F ; Z
DEFB $3D ; X
DEFB $28 ; C
DEFB $3B ; V
DEFB $26 ; A
DEFB $38 ; S
DEFB $29 ; D
DEFB $2B ; F
DEFB $2C ; G
DEFB $36 ; Q
DEFB $3C ; W
DEFB $2A ; E
DEFB $37 ; R
DEFB $39 ; T
DEFB $1D ; 1
DEFB $1E ; 2
DEFB $1F ; 3
DEFB $20 ; 4
DEFB $21 ; 5
DEFB $1C ; 0
DEFB $25 ; 9
DEFB $24 ; 8
DEFB $23 ; 7
DEFB $22 ; 6
DEFB $35 ; P
DEFB $34 ; O
DEFB $2E ; I
DEFB $3A ; U
DEFB $3E ; Y
DEFB $76 ; NEWLINE
DEFB $31 ; L
DEFB $30 ; K
DEFB $2F ; J
DEFB $2D ; H
DEFB $00 ; SPACE
DEFB $1B ; .
DEFB $32 ; M
DEFB $33 ; N
DEFB $27 ; B
; -----------------------------
; THE 'SHIFTED' CHARACTER CODES
; -----------------------------
;; K-SHIFT
L00A5: DEFB $0E ; :
DEFB $19 ; ;
DEFB $0F ; ?
DEFB $18 ; /
DEFB $E3 ; STOP
DEFB $E1 ; LPRINT
DEFB $E4 ; SLOW
DEFB $E5 ; FAST
DEFB $E2 ; LLIST
DEFB $C0 ; ""
DEFB $D9 ; OR
DEFB $E0 ; STEP
DEFB $DB ; <=
DEFB $DD ; <>
DEFB $75 ; EDIT
DEFB $DA ; AND
DEFB $DE ; THEN
DEFB $DF ; TO
DEFB $72 ; cursor-left
DEFB $77 ; RUBOUT
DEFB $74 ; GRAPHICS
DEFB $73 ; cursor-right
DEFB $70 ; cursor-up
DEFB $71 ; cursor-down
DEFB $0B ; "
DEFB $11 ; )
DEFB $10 ; (
DEFB $0D ; $
DEFB $DC ; >=
DEFB $79 ; FUNCTION
DEFB $14 ; =
DEFB $15 ; +
DEFB $16 ; -
DEFB $D8 ; **
DEFB $0C ; ukp
DEFB $1A ; ,
DEFB $12 ; >
DEFB $13 ; <
DEFB $17 ; *
; ------------------------------
; THE 'FUNCTION' CHARACTER CODES
; ------------------------------
;; K-FUNCT
L00CC: DEFB $CD ; LN
DEFB $CE ; EXP
DEFB $C1 ; AT
DEFB $78 ; KL
DEFB $CA ; ASN
DEFB $CB ; ACS
DEFB $CC ; ATN
DEFB $D1 ; SGN
DEFB $D2 ; ABS
DEFB $C7 ; SIN
DEFB $C8 ; COS
DEFB $C9 ; TAN
DEFB $CF ; INT
DEFB $40 ; RND
DEFB $78 ; KL
DEFB $78 ; KL
DEFB $78 ; KL
DEFB $78 ; KL
DEFB $78 ; KL
DEFB $78 ; KL
DEFB $78 ; KL
DEFB $78 ; KL
DEFB $78 ; KL
DEFB $78 ; KL
DEFB $C2 ; TAB
DEFB $D3 ; PEEK
DEFB $C4 ; CODE
DEFB $D6 ; CHR$
DEFB $D5 ; STR$
DEFB $78 ; KL
DEFB $D4 ; USR
DEFB $C6 ; LEN
DEFB $C5 ; VAL
DEFB $D0 ; SQR
DEFB $78 ; KL
DEFB $78 ; KL
DEFB $42 ; PI
DEFB $D7 ; NOT
DEFB $41 ; INKEY$
; -----------------------------
; THE 'GRAPHIC' CHARACTER CODES
; -----------------------------
;; K-GRAPH
L00F3: DEFB $08 ; graphic
DEFB $0A ; graphic
DEFB $09 ; graphic
DEFB $8A ; graphic
DEFB $89 ; graphic
DEFB $81 ; graphic
DEFB $82 ; graphic
DEFB $07 ; graphic
DEFB $84 ; graphic
DEFB $06 ; graphic
DEFB $01 ; graphic
DEFB $02 ; graphic
DEFB $87 ; graphic
DEFB $04 ; graphic
DEFB $05 ; graphic
DEFB $77 ; RUBOUT
DEFB $78 ; KL
DEFB $85 ; graphic
DEFB $03 ; graphic
DEFB $83 ; graphic
DEFB $8B ; graphic
DEFB $91 ; inverse )
DEFB $90 ; inverse (
DEFB $8D ; inverse $
DEFB $86 ; graphic
DEFB $78 ; KL
DEFB $92 ; inverse >
DEFB $95 ; inverse +
DEFB $96 ; inverse -
DEFB $88 ; graphic
; ------------------
; THE 'TOKEN' TABLES
; ------------------
;; TOKENS
L0111: DEFB $0F+$80 ; '?'+$80
DEFB $0B,$0B+$80 ; ""
DEFB $26,$39+$80 ; AT
DEFB $39,$26,$27+$80 ; TAB
DEFB $0F+$80 ; '?'+$80
DEFB $28,$34,$29,$2A+$80 ; CODE
DEFB $3B,$26,$31+$80 ; VAL
DEFB $31,$2A,$33+$80 ; LEN
DEFB $38,$2E,$33+$80 ; SIN
DEFB $28,$34,$38+$80 ; COS
DEFB $39,$26,$33+$80 ; TAN
DEFB $26,$38,$33+$80 ; ASN
DEFB $26,$28,$38+$80 ; ACS
DEFB $26,$39,$33+$80 ; ATN
DEFB $31,$33+$80 ; LN
DEFB $2A,$3D,$35+$80 ; EXP
DEFB $2E,$33,$39+$80 ; INT
DEFB $38,$36,$37+$80 ; SQR
DEFB $38,$2C,$33+$80 ; SGN
DEFB $26,$27,$38+$80 ; ABS
DEFB $35,$2A,$2A,$30+$80 ; PEEK
DEFB $3A,$38,$37+$80 ; USR
DEFB $38,$39,$37,$0D+$80 ; STR$
DEFB $28,$2D,$37,$0D+$80 ; CHR$
DEFB $33,$34,$39+$80 ; NOT
DEFB $17,$17+$80 ; **
DEFB $34,$37+$80 ; OR
DEFB $26,$33,$29+$80 ; AND
DEFB $13,$14+$80 ; <=
DEFB $12,$14+$80 ; >=
DEFB $13,$12+$80 ; <>
DEFB $39,$2D,$2A,$33+$80 ; THEN
DEFB $39,$34+$80 ; TO
DEFB $38,$39,$2A,$35+$80 ; STEP
DEFB $31,$35,$37,$2E,$33,$39+$80 ; LPRINT
DEFB $31,$31,$2E,$38,$39+$80 ; LLIST
DEFB $38,$39,$34,$35+$80 ; STOP
DEFB $38,$31,$34,$3C+$80 ; SLOW
DEFB $2B,$26,$38,$39+$80 ; FAST
DEFB $33,$2A,$3C+$80 ; NEW
DEFB $38,$28,$37,$34,$31,$31+$80 ; SCROLL
DEFB $28,$34,$33,$39+$80 ; CONT
DEFB $29,$2E,$32+$80 ; DIM
DEFB $37,$2A,$32+$80 ; REM
DEFB $2B,$34,$37+$80 ; FOR
DEFB $2C,$34,$39,$34+$80 ; GOTO
DEFB $2C,$34,$38,$3A,$27+$80 ; GOSUB
DEFB $2E,$33,$35,$3A,$39+$80 ; INPUT
DEFB $31,$34,$26,$29+$80 ; LOAD
DEFB $31,$2E,$38,$39+$80 ; LIST
DEFB $31,$2A,$39+$80 ; LET
DEFB $35,$26,$3A,$38,$2A+$80 ; PAUSE
DEFB $33,$2A,$3D,$39+$80 ; NEXT
DEFB $35,$34,$30,$2A+$80 ; POKE
DEFB $35,$37,$2E,$33,$39+$80 ; PRINT
DEFB $35,$31,$34,$39+$80 ; PLOT
DEFB $37,$3A,$33+$80 ; RUN
DEFB $38,$26,$3B,$2A+$80 ; SAVE
DEFB $37,$26,$33,$29+$80 ; RAND
DEFB $2E,$2B+$80 ; IF
DEFB $28,$31,$38+$80 ; CLS
DEFB $3A,$33,$35,$31,$34,$39+$80 ; UNPLOT
DEFB $28,$31,$2A,$26,$37+$80 ; CLEAR
DEFB $37,$2A,$39,$3A,$37,$33+$80 ; RETURN
DEFB $28,$34,$35,$3E+$80 ; COPY
DEFB $37,$33,$29+$80 ; RND
DEFB $2E,$33,$30,$2A,$3E,$0D+$80 ; INKEY$
DEFB $35,$2E+$80 ; PI
; ------------------------------
; THE 'LOAD-SAVE UPDATE' ROUTINE
; ------------------------------
;
;
;; LOAD/SAVE
L01FC: INC HL ;
EX DE,HL ;
LD HL,($4014) ; system variable edit line E_LINE.
SCF ; set carry flag
SBC HL,DE ;
EX DE,HL ;
RET NC ; return if more bytes to load/save.
POP HL ; else drop return address
; ----------------------
; THE 'DISPLAY' ROUTINES
; ----------------------
;
;
;; SLOW/FAST
L0207: LD HL,$403B ; Address the system variable CDFLAG.
LD A,(HL) ; Load value to the accumulator.
RLA ; rotate bit 6 to position 7.
XOR (HL) ; exclusive or with original bit 7.
RLA ; rotate result out to carry.
RET NC ; return if both bits were the same.
; Now test if this really is a ZX81 or a ZX80 running the upgraded ROM.
; The standard ZX80 did not have an NMI generator.
LD A,$7F ; Load accumulator with %011111111
EX AF,AF' ; save in AF'
LD B,$11 ; A counter within which an NMI should occur
; if this is a ZX81.
OUT ($FE),A ; start the NMI generator.
; Note that if this is a ZX81 then the NMI will increment AF'.
;; LOOP-11
L0216: DJNZ L0216 ; self loop to give the NMI a chance to kick in.
; = 16*13 clock cycles + 8 = 216 clock cycles.
OUT ($FD),A ; Turn off the NMI generator.
EX AF,AF' ; bring back the AF' value.
RLA ; test bit 7.
JR NC,L0226 ; forward, if bit 7 is still reset, to NO-SLOW.
; If the AF' was incremented then the NMI generator works and SLOW mode can
; be set.
SET 7,(HL) ; Indicate SLOW mode - Compute and Display.
PUSH AF ; * Save Main Registers
PUSH BC ; **
PUSH DE ; ***
PUSH HL ; ****
JR L0229 ; skip forward - to DISPLAY-1.
; ---
;; NO-SLOW
L0226: RES 6,(HL) ; reset bit 6 of CDFLAG.
RET ; return.
; -----------------------
; THE 'MAIN DISPLAY' LOOP
; -----------------------
; This routine is executed once for every frame displayed.
;; DISPLAY-1
L0229: LD HL,($4034) ; fetch two-byte system variable FRAMES.
DEC HL ; decrement frames counter.
;; DISPLAY-P
L022D: LD A,$7F ; prepare a mask
AND H ; pick up bits 6-0 of H.
OR L ; and any bits of L.
LD A,H ; reload A with all bits of H for PAUSE test.
; Note both branches must take the same time.
JR NZ,L0237 ; (12/7) forward if bits 14-0 are not zero
; to ANOTHER
RLA ; (4) test bit 15 of FRAMES.
JR L0239 ; (12) forward with result to OVER-NC
; ---
;; ANOTHER
L0237: LD B,(HL) ; (7) Note. Harmless Nonsensical Timing weight.
SCF ; (4) Set Carry Flag.
; Note. the branch to here takes either (12)(7)(4) cyles or (7)(4)(12) cycles.
;; OVER-NC
L0239: LD H,A ; (4) set H to zero
LD ($4034),HL ; (16) update system variable FRAMES
RET NC ; (11/5) return if FRAMES is in use by PAUSE
; command.
;; DISPLAY-2
L023E: CALL L02BB ; routine KEYBOARD gets the key row in H and
; the column in L. Reading the ports also starts
; the TV frame synchronization pulse. (VSYNC)
LD BC,($4025) ; fetch the last key values read from LAST_K
LD ($4025),HL ; update LAST_K with new values.
LD A,B ; load A with previous column - will be $FF if
; there was no key.
ADD A,$02 ; adding two will set carry if no previous key.
SBC HL,BC ; subtract with the carry the two key values.
; If the same key value has been returned twice then HL will be zero.
LD A,($4027) ; fetch system variable DEBOUNCE
OR H ; and OR with both bytes of the difference
OR L ; setting the zero flag for the upcoming branch.
LD E,B ; transfer the column value to E
LD B,$0B ; and load B with eleven
LD HL,$403B ; address system variable CDFLAG
RES 0,(HL) ; reset the rightmost bit of CDFLAG
JR NZ,L0264 ; skip forward if debounce/diff >0 to NO-KEY
BIT 7,(HL) ; test compute and display bit of CDFLAG
SET 0,(HL) ; set the rightmost bit of CDFLAG.
RET Z ; return if bit 7 indicated fast mode.
DEC B ; (4) decrement the counter.
NOP ; (4) Timing - 4 clock cycles. ??
SCF ; (4) Set Carry Flag
;; NO-KEY
L0264: LD HL,$4027 ; sv DEBOUNCE
CCF ; Complement Carry Flag
RL B ; rotate left B picking up carry
; C<-76543210<-C
;; LOOP-B
L026A: DJNZ L026A ; self-loop while B>0 to LOOP-B
LD B,(HL) ; fetch value of DEBOUNCE to B
LD A,E ; transfer column value
CP $FE ;
SBC A,A ;
LD B,$1F ;
OR (HL) ;
AND B ;
RRA ;
LD (HL),A ;
OUT ($FF),A ; end the TV frame synchronization pulse.
LD HL,($400C) ; (12) set HL to the Display File from D_FILE
SET 7,H ; (8) set bit 15 to address the echo display.
CALL L0292 ; (17) routine DISPLAY-3 displays the top set
; of blank lines.
; ---------------------
; THE 'VIDEO-1' ROUTINE
; ---------------------
;; R-IX-1
L0281: LD A,R ; (9) Harmless Nonsensical Timing or something
; very clever?
LD BC,$1901 ; (10) 25 lines, 1 scanline in first.
LD A,$F5 ; (7) This value will be loaded into R and
; ensures that the cycle starts at the right
; part of the display - after 32nd character
; position.
CALL L02B5 ; (17) routine DISPLAY-5 completes the current
; blank line and then generates the display of
; the live picture using INT interrupts
; The final interrupt returns to the next
; address.
L028B: DEC HL ; point HL to the last NEWLINE/HALT.
CALL L0292 ; routine DISPLAY-3 displays the bottom set of
; blank lines.
; ---
;; R-IX-2
L028F: JP L0229 ; JUMP back to DISPLAY-1
; ---------------------------------
; THE 'DISPLAY BLANK LINES' ROUTINE
; ---------------------------------
; This subroutine is called twice (see above) to generate first the blank
; lines at the top of the television display and then the blank lines at the
; bottom of the display.
;; DISPLAY-3
L0292: POP IX ; pop the return address to IX register.
; will be either L0281 or L028F - see above.
LD C,(IY+$28) ; load C with value of system constant MARGIN.
BIT 7,(IY+$3B) ; test CDFLAG for compute and display.
JR Z,L02A9 ; forward, with FAST mode, to DISPLAY-4
LD A,C ; move MARGIN to A - 31d or 55d.
NEG ; Negate
INC A ;
EX AF,AF' ; place negative count of blank lines in A'
OUT ($FE),A ; enable the NMI generator.
POP HL ; ****
POP DE ; ***
POP BC ; **
POP AF ; * Restore Main Registers
RET ; return - end of interrupt. Return is to
; user's program - BASIC or machine code.
; which will be interrupted by every NMI.
; ------------------------
; THE 'FAST MODE' ROUTINES
; ------------------------
;; DISPLAY-4
L02A9: LD A,$FC ; (7) load A with first R delay value
LD B,$01 ; (7) one row only.
CALL L02B5 ; (17) routine DISPLAY-5
DEC HL ; (6) point back to the HALT.
EX (SP),HL ; (19) Harmless Nonsensical Timing if paired.
EX (SP),HL ; (19) Harmless Nonsensical Timing.
JP (IX) ; (8) to L0281 or L028F
; --------------------------
; THE 'DISPLAY-5' SUBROUTINE
; --------------------------
; This subroutine is called from SLOW mode and FAST mode to generate the
; central TV picture. With SLOW mode the R register is incremented, with
; each instruction, to $F7 by the time it completes. With fast mode, the
; final R value will be $FF and an interrupt will occur as soon as the
; Program Counter reaches the HALT. (24 clock cycles)
;; DISPLAY-5
L02B5: LD R,A ; (9) Load R from A. R = slow: $F5 fast: $FC
LD A,$DD ; (7) load future R value. $F6 $FD
EI ; (4) Enable Interrupts $F7 $FE
JP (HL) ; (4) jump to the echo display. $F8 $FF
; ----------------------------------
; THE 'KEYBOARD SCANNING' SUBROUTINE
; ----------------------------------
; The keyboard is read during the vertical sync interval while no video is
; being displayed. Reading a port with address bit 0 low i.e. $FE starts the
; vertical sync pulse.
;; KEYBOARD
L02BB: LD HL,$FFFF ; (16) prepare a buffer to take key.
LD BC,$FEFE ; (20) set BC to port $FEFE. The B register,
; with its single reset bit also acts as
; an 8-counter.
IN A,(C) ; (11) read the port - all 16 bits are put on
; the address bus. Start VSYNC pulse.
OR $01 ; (7) set the rightmost bit so as to ignore
; the SHIFT key.
;; EACH-LINE
L02C5: OR $E0 ; [7] OR %11100000
LD D,A ; [4] transfer to D.
CPL ; [4] complement - only bits 4-0 meaningful now.
CP $01 ; [7] sets carry if A is zero.
SBC A,A ; [4] $FF if $00 else zero.
OR B ; [7] $FF or port FE,FD,FB....
AND L ; [4] unless more than one key, L will still be
; $FF. if more than one key is pressed then A is
; now invalid.
LD L,A ; [4] transfer to L.
; now consider the column identifier.
LD A,H ; [4] will be $FF if no previous keys.
AND D ; [4] 111xxxxx
LD H,A ; [4] transfer A to H
; since only one key may be pressed, H will, if valid, be one of
; 11111110, 11111101, 11111011, 11110111, 11101111
; reading from the outer column, say Q, to the inner column, say T.
RLC B ; [8] rotate the 8-counter/port address.
; sets carry if more to do.
IN A,(C) ; [10] read another half-row.
; all five bits this time.
JR C,L02C5 ; [12](7) loop back, until done, to EACH-LINE
; The last row read is SHIFT,Z,X,C,V for the second time.
RRA ; (4) test the shift key - carry will be reset
; if the key is pressed.
RL H ; (8) rotate left H picking up the carry giving
; column values -
; $FD, $FB, $F7, $EF, $DF.
; or $FC, $FA, $F6, $EE, $DE if shifted.
; We now have H identifying the column and L identifying the row in the
; keyboard matrix.
; This is a good time to test if this is an American or British machine.
; The US machine has an extra diode that causes bit 6 of a byte read from
; a port to be reset.
RLA ; (4) compensate for the shift test.
RLA ; (4) rotate bit 7 out.
RLA ; (4) test bit 6.
SBC A,A ; (4) $FF or $00 {USA}
AND $18 ; (7) $18 or $00
ADD A,$1F ; (7) $37 or $1F
; result is either 31 (USA) or 55 (UK) blank lines above and below the TV
; picture.
LD ($4028),A ; (13) update system variable MARGIN
RET ; (10) return
; ------------------------------
; THE 'SET FAST MODE' SUBROUTINE
; ------------------------------
;
;
;; SET-FAST
L02E7: BIT 7,(IY+$3B) ; sv CDFLAG
RET Z ;
HALT ; Wait for Interrupt
OUT ($FD),A ;
RES 7,(IY+$3B) ; sv CDFLAG
RET ; return.
; --------------
; THE 'REPORT-F'
; --------------
;; REPORT-F
L02F4: RST 08H ; ERROR-1
DEFB $0E ; Error Report: No Program Name supplied.
; --------------------------
; THE 'SAVE COMMAND' ROUTINE
; --------------------------