-
-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathmsx.spin2
6870 lines (6197 loc) · 319 KB
/
msx.spin2
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
{
MSX Emulator
Copyright (c) 2022-23 by Marco Maccaferri <[email protected]>
Based on ZiKore Z80 Emulator by Ada Gottensträter
with modifications by Marco Maccaferri
TMS9918 Emulator by Marco Maccaferri
AY-3-8912 Emulator by Johannes Ahlebrand, port to P2 by Ada Gottensträter
PS/2 Keyboard driver by Marco Maccaferri
USB host driver based on 1CogKbM code by Garry Jordan ("garryj")
with gamepad support and other modifications by Marco Maccaferri
and portions derived from code by Ada Gottensträter
}
CON
_CLKFREQ = 250_000_000
VIDEO = PAL ' video mode VGA, NTSC or PAL
VGA_PIN = 48
CVBS_PIN = 32 addpins 1 ' 32=CVBS/Y, 33=C
ACTIVITY_LED = 57
ERROR_LED = 56
USB_BASE_PIN = 40
PS2_DATA_PIN = 47
PS2_CLOCK_PIN = 46
AUDIO_LEFT_PIN = 38
AUDIO_RIGHT_PIN = 39
TAPE_MIC = 0 ' OUT
TAPE_EAR = 1 ' IN
TAPE_REM = 2 ' OUT (motor)
' Keyboard Map
'
' +-----+------+------+------+------+-------+-----+-----+-----+-----+-----+--------+--------+
' | ESC | F1-6 | F2-7 | F3-8 | F4-9 | F5-10 | F6 | F7 | F8 | F9 | F10 | SELECT | STOP |
' +-----+-----++----+-+---+--+--+---+-+-----+-----+-----+-----+-----+-----+-----+--+--------+
' | ` ~ | 1 ! | 2 @ | 3 # | 4 $ | 5 % | 6 ^ | 7 & | 8 * | 9 ( | 0 ) | - _ | = + | BS |
' +-----+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--------+
' | TAB | Q | W | E | R | T | Y | U | I | O | P | [ { | ] } | |
' +--------++----++----++----++----++----++----++----++----++----++----++----++----++ |
' | A | S | D | F | G | H | J | K | L | ; : | ' " | \ ¦ | ENTER |
' +-------+-++----++----++----++----++----++----++----++----++----++----++-------+--+-------+
' | SHIFT | | Z | X | C | V | B | N | M | , < | . > | / ? | SHIFT |
' +-------+--+-+---+---+-+-----+-----+-----+-----+-----+-----+-+---+--+--+----+------+------+
' | CTRL | | GRAPH | SPACE | CODE | | DEAD | CTRL |
' +-------+----+-------+---------------------------------------+------+-------+------+------+
'
' Other mapped keys:
'
' HOME/INS/DEL
' UP/DOWN/LEFT/RIGHT Arrows
' Numeric Keypad
'
' WIN+WIN = RESET
DAT ' Startup
org $000
asmclk
drvl #ACTIVITY_LED
drvl #ERROR_LED
rep @.lockmadness,#16 ' allocate all locks, to be safe...
locknew pb
nop
.lockmadness
setq #@psg_regs
coginit #4, ##@ay_emu ' start AY-3-8912 on cog #4
coginit #3, ##@tms_driver ' start video on cog #3
coginit #2, ##@ps2_driver ' start PS/2 driver on cog #2
coginit #1, ##@usb_host_start ' start USB2 driver on cog #1
waitx ##_CLKFREQ ' delay to allow monitor to settle
coginit #0, ##@zk_cogbase ' start Z80 on cog #0 (this)
orgh
' Shared variables
zk_irq_mem long $0_FF ' bit 8 = IRQ, 7..0 vector
msx_keys byte $FF[16]
' TMS9918 registers
tms_regs byte $00, $00, $00, $00, $00, $00, $00, $00
tms_status long $0_00 ' bit 8 = VDP_IRQ, 7..0 = VDP status
' AY-3-8910 registers
psg_regs byte $00[14]
byte $FF ' port A
byte $FF ' port B
msx_joystick0 byte $FF
msx_joystick1 byte $FF
DAT ' PS/2 keyboard driver
org $000
ps2_driver
fltl #PS2_DATA_PIN
fltl #PS2_CLOCK_PIN
mov ps2_data, #$F3 ' auto-repeat
call #ps2_transmit
and ps2_data, #%011_11111 ' slow 1s / 2cps
call #ps2_transmit
mov ps2_state, #%0_000 ' turn off all leds
jmp #ps2_locks
ps2_reset
wrlong ps2_ffffffff, #@msx_keys
wrlong ps2_ffffffff, #@msx_keys+4
wrlong ps2_ffffffff, #@msx_keys+8
wrlong ps2_ffffffff, #@msx_keys+12
ps2_loop and ps2_state, #$07 ' keep locks state
.l1 testp #PS2_DATA_PIN wz ' wait initial clock low
if_x1 jmp #$-1
call #ps2_rx
getbyte ps2_code, ps2_data, #0
cmp ps2_data, #$F0 wz ' release
if_z bith ps2_state, #7
if_z jmp #.l1
cmp ps2_data, #$E0 wz
if_z bith ps2_state, #28
if_z jmp #ps2_ext0
cmp ps2_data, #$E1 wz
if_z bith ps2_state, #29
if_z jmp #ps2_ext1
ps2_ext_ret testbn ps2_state, #29 wz
if_nz jmp #ps2_loop ' E1 codes are ignored
testb ps2_state, #7 wc ' release
cmp ps2_code, #$14 wz ' left control
if_z testbn ps2_state, #28 andz
if_z muxnc ps2_shift, #LEFT_CTRLF
cmp ps2_code, #$14 wz ' right control
if_z testb ps2_state, #28 andz
if_z muxnc ps2_shift, #RIGHT_CTRLF
cmp ps2_code, #$12 wz ' left shift
if_z testbn ps2_state, #28 andz
if_z muxnc ps2_shift, #LEFT_SHIFTF
cmp ps2_data, #$59 wz ' right shift
if_z testbn ps2_state, #28 andz
if_z muxnc ps2_shift, #RIGHT_SHIFTF
cmp ps2_code, #$11 wz ' left alt
if_z testbn ps2_state, #28 andz
if_z muxnc ps2_shift, #LEFT_ALTF
cmp ps2_code, #$11 wz ' right alt
if_z testb ps2_state, #28 andz
if_z muxnc ps2_shift, #RIGHT_ALTF
cmp ps2_code, #$1F wz ' left gui
if_z testb ps2_state, #28 andz
if_z muxnc ps2_shift, #LEFT_GUIF
cmp ps2_code, #$27 wz ' right gui
if_z testb ps2_state, #28 andz
if_z muxnc ps2_shift, #RIGHT_GUIF
cmp ps2_code, #$77 wz ' num. lock
if_z_and_c jmp #ps2_loop ' | ignore if released
if_z bitnot ps2_state, #0 ' | toggle state
if_z jmp #ps2_locks ' | update leds
cmp ps2_code, #$58 wz ' caps lock
if_z_and_c jmp #ps2_loop ' | ignore if released
if_z bitnot ps2_state, #1 ' | toggle state
if_z jmp #ps2_locks ' | update leds
cmp ps2_code, #$7E wz ' scroll lock
if_z_and_c jmp #ps2_loop ' | ignore if released
if_z bitnot ps2_state, #2 ' | toggle state
if_z jmp #ps2_locks ' | update leds
test ps2_shift, #LEFT_GUIF wz ' check reset key combination
if_nz test ps2_shift, #RIGHT_GUIF wz ' |
if_nz andn ps2_shift, #LEFT_GUIF|RIGHT_GUIF
if_nz call #\sys_reset ' jump to system reset
if_nz jmp #ps2_loop
cmp ps2_code, #$0B wz
if_nz cmp ps2_code, #$83 wz
if_nz cmp ps2_code, #$0A wz
if_nz cmp ps2_code, #$01 wz
if_nz cmp ps2_code, #$09 wz
if_z call #shift
testb ps2_state, #28 wc ' extended code
rcl ps2_code, #1 ' to bit 0
shl ps2_code, #1 ' to word offset
mov ps2_key, ##@ps2_table
add ps2_key, ps2_code
rdword ps2_key, ps2_key wz
if_z jmp #ps2_loop
testb ps2_state, #7 wc ' release
getnib ps2_data, ps2_key, #2
mov ptra, #@msx_keys
add ptra, ps2_data
rdbyte ps2_temp, ptra
muxc ps2_temp, ps2_key
wrbyte ps2_temp, ptra
jmp #ps2_loop
ps2_locks mov ps2_data, #$ED
call #ps2_transmit
mov ps2_data, #0
testb ps2_state, #0 wz ' num. lock
bitz ps2_data, #1
testb ps2_state, #1 wz ' caps lock
bitz ps2_data, #2
testb ps2_state, #2 wz ' scroll lock
bitz ps2_data, #0
call #ps2_transmit
jmp #ps2_loop
ps2_ext1 call #ps2_receive
setbyte ps2_code, ps2_data, #1
cmp ps2_data, #$F0 wz ' release
if_z bith ps2_state, #7
if_z jmp #ps2_ext1
' fall through
ps2_ext0 call #ps2_receive
setbyte ps2_code, ps2_data, #0
cmp ps2_data, #$F0 wz ' release
if_z bith ps2_state, #7
if_z jmp #ps2_ext0
jmp #ps2_ext_ret
ps2_transmit drvl #PS2_CLOCK_PIN ' pull clock low
getct ps2_tout ' hold clock for 128us (must be > 100us)
addct1 ps2_tout, ps2_us128 ' |
jnct1 #$
drvl #PS2_DATA_PIN ' pull data low
getct ps2_tout ' hold data for 4us
addct1 ps2_tout, ps2_us4 ' |
jnct1 #$
fltl #PS2_CLOCK_PIN ' release clock
getct ps2_tout ' allow pin to float
addct1 ps2_tout, ps2_us1 ' |
jnct1 #$
test ps2_data, #$FF wc ' append parity
muxnc ps2_data, #$100 ' |
bith ps2_data, #9 ' append stop bit
getct ps2_tout ' safety timeout
addct1 ps2_tout, ps2_us6000
mov ps2_bits, #10
.l1 testp #PS2_CLOCK_PIN wz ' wait until clock low
if_x1 jct1 #ps2_reset ' | check timeout
if_x1 jmp #.l1 ' |
shr ps2_data, #1 wc ' output data bit
drvc #PS2_DATA_PIN ' |
.l2 testp #PS2_CLOCK_PIN wz ' wait until clock high
if_x0 jct1 #ps2_reset ' | check timeout
if_x0 jmp #.l2 ' |
djnz ps2_bits, #.l1 ' another bit ?
fltl #PS2_DATA_PIN
.l3 testp #PS2_CLOCK_PIN wc ' wait until clock and data low
testp #PS2_DATA_PIN wz ' |
if_not_00 jct1 #ps2_reset ' | check timeout
if_not_00 jmp #.l3 ' |
.l4 testp #PS2_CLOCK_PIN wc ' wait until clock and data high
testp #PS2_DATA_PIN wz ' |
if_not_11 jct1 #ps2_reset ' | check timeout
if_not_11 jmp #.l4 ' |
' Fall through to receive ack
ps2_receive getct ps2_tout ' safety timeout
addct1 ps2_tout, ps2_us6000
.l0 testp #PS2_DATA_PIN wz ' wait until data low
if_x1 jct1 #ps2_reset ' | check timeout
if_x1 jmp #.l0 ' |
ps2_rx
getct ps2_tout ' safety timeout
addct1 ps2_tout, ps2_us3000
mov ps2_bits, #11
.l1 testp #PS2_CLOCK_PIN wz ' wait until clock low
if_x1 jct1 #ps2_reset ' | check timeout
if_x1 jmp #.l1 ' |
testp #PS2_DATA_PIN wc ' sample data
rcr ps2_data, #1 ' |
.l2 testp #PS2_CLOCK_PIN wz ' wait until clock high
if_x0 jct1 #ps2_reset ' | check timeout
if_x0 jmp #.l2 ' |
djnz ps2_bits, #.l1 ' another bit?
.l3 testp #PS2_CLOCK_PIN wc ' wait until clock and data high
testp #PS2_DATA_PIN wz ' |
if_not_11 jct1 #.l4 ' | check timeout
if_not_11 jmp #.l3 ' |
.l4
shr ps2_data, #22 ' align byte
test ps2_data, #$1FF wc ' test parity
if_nc jmp #ps2_reset ' |
_ret_ and ps2_data, #$FF ' ok
shift mov ptra, #@msx_keys
rdbyte ps2_temp, ptra[6]
and ps2_temp, #%11101000
muxc ps2_temp, #%00000001
_ret_ wrbyte ps2_temp, ptra[6]
ps2_us1 long _CLKFREQ / 1_000_000 * 1 ' 1 usec.
ps2_us4 long _CLKFREQ / 1_000_000 * 4 ' 4 usec.
ps2_us128 long _CLKFREQ / 1_000_000 * 128 ' 128 usec.
ps2_us3000 long _CLKFREQ / 1_000_000 * 3000 ' 3000 usec.
ps2_us6000 long _CLKFREQ / 1_000_000 * 6000 ' 6000 usec.
ps2_bits long 0
ps2_code long 0
ps2_state long 0
ps2_shift long 0
ps2_key long 0
ps2_data long 0
ps2_tout long 0
ps2_temp long 0
ps2_ffffffff long $FFFFFFFF
fit $1F0
DAT ' PS/2 Lookup table
orgh
' Keyboard Matrix
'
' bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
' row 0 7 & 6 ^ 5 % 4 $ 3 # 2 @ 1 ! 0 )
' row 1 ; : ] } [ { \ ¦ = + - _ 9 ( 8 *
' row 2 B A DEAD / ? . > , < ` ~ ' "
' row 3 J I H G F E D C
' row 4 R Q P O N M L K
' row 5 Z Y X W V U T S
' row 6 F3 F2 F1 CODE CAPS GRAPH CTRL SHIFT
' row 7 RET SELECT BS STOP TAB ESC F5 F4
' row 8 → ↓ ↑ ← DEL INS HOME SPACE
' row 9 NUM4 NUM3 NUM2 NUM1 NUM0 NUM/ NUM+ NUM*
' row 10 NUM. NUM, NUM- NUM9 NUM8 NUM7 NUM6 NUM5
' ROW B7....B0 ROW B7....B0
ps2_table word 0, 0 '00
word (7 << 8) | %00000001, 0 '01 F9
word 0, 0 '02
word (7 << 8) | %00000010, 0 '03 F5
word (6 << 8) | %10000000, 0 '04 F3
word (6 << 8) | %00100000, 0 '05 F1
word (6 << 8) | %01000000, 0 '06 F2
word (7 << 8) | %00010000, 0 '07 F12
word 0, 0 '08
word (7 << 8) | %00000010, 0 '09 F10
word (6 << 8) | %10000000, 0 '0A F8
word (6 << 8) | %00100000, 0 '0B F6
word (7 << 8) | %00000001, 0 '0C F4
word (7 << 8) | %00001000, 0 '0D Tab
word (2 << 8) | %00000010, 0 '0E `
word 0, 0 '0F
word 0, 0 '10
word (6 << 8) | %00000100, (6 << 8) | %00000100 '11 Alt-L Alt-R
word (6 << 8) | %00000001, 0 '12 Shift-L
word 0, 0 '13
word (6 << 8) | %00000010, (6 << 8) | %00000010 '14 Ctrl-L Ctrl-R
word (4 << 8) | %01000000, 0 '15 q
word (0 << 8) | %00000010, 0 '16 1
word 0, 0 '17
word 0, 0 '18
word 0, 0 '19
word (5 << 8) | %10000000, 0 '1A z
word (5 << 8) | %00000001, 0 '1B s
word (2 << 8) | %01000000, 0 '1C a
word (5 << 8) | %00010000, 0 '1D w
word (0 << 8) | %00000100, 0 '1E 2
word 0, 0 '1F Win-L
word 0, 0 '20
word (3 << 8) | %00000001, 0 '21 c
word (5 << 8) | %00100000, 0 '22 x
word (3 << 8) | %00000010, 0 '23 d
word (3 << 8) | %00000100, 0 '24 e
word (0 << 8) | %00010000, 0 '25 4
word (0 << 8) | %00001000, 0 '26 3
word 0, 0 '27 Win-R
word 0, 0 '28
word (8 << 8) | %00000001, 0 '29 Space
word (5 << 8) | %00001000, 0 '2A v
word (3 << 8) | %00001000, 0 '2B f
word (5 << 8) | %00000010, 0 '2C t
word (4 << 8) | %10000000, 0 '2D r
word (0 << 8) | %00100000, 0 '2E 5
word 0, (2 << 8) | %00100000 '2F Apps
word 0, 0 '30
word (4 << 8) | %00001000, 0 '31 n
word (2 << 8) | %10000000, 0 '32 b
word (3 << 8) | %00100000, 0 '33 h
word (3 << 8) | %00010000, 0 '34 g
word (5 << 8) | %01000000, 0 '35 y
word (0 << 8) | %01000000, 0 '36 6
word 0, 0 '37 Power
word 0, 0 '38
word 0, 0 '39
word (4 << 8) | %00000100, 0 '3A m
word (3 << 8) | %10000000, 0 '3B j
word (5 << 8) | %00000100, 0 '3C u
word (0 << 8) | %10000000, 0 '3D 7
word (1 << 8) | %00000001, 0 '3E 8
word 0, 0 '3F Sleep
word 0, 0 '40
word (2 << 8) | %00000100, 0 '41 ,
word (4 << 8) | %00000001, 0 '42 k
word (3 << 8) | %01000000, 0 '43 i
word (4 << 8) | %00010000, 0 '44 o
word (0 << 8) | %00000001, 0 '45 0
word (1 << 8) | %00000010, 0 '46 9
word 0, 0 '47
word 0, 0 '48
word (2 << 8) | %00001000, 0 '49 .
word (2 << 8) | %00010000, (9 << 8) | %00000100 '4A / (/)
word (4 << 8) | %00000010, 0 '4B l
word (1 << 8) | %10000000, 0 '4C ;
word (4 << 8) | %00100000, 0 '4D p
word (1 << 8) | %00000100, 0 '4E -
word 0, 0 '4F
word 0, 0 '50
word 0, 0 '51
word (2 << 8) | %00000001, 0 '52 '
word 0, 0 '53
word (1 << 8) | %00100000, 0 '54 [
word (1 << 8) | %00001000, 0 '55 =
word 0, 0 '56
word 0, 0 '57
word (6 << 8) | %00010000, 0 '58 CapsLock
word (6 << 8) | %00000001, 0 '59 Shift-R
word (7 << 8) | %10000000, (7 << 8) | %10000000 '5A Enter (Enter)
word (1 << 8) | %01000000, 0 '5B ]
word 0, 0 '5C
word (1 << 8) | %00010000, 0 '5D \
word 0, 0 '5E WakeUp
word 0, 0 '5F
word 0, 0 '60
word 0, 0 '61
word 0, 0 '62
word 0, 0 '63
word 0, 0 '64
word 0, 0 '65
word (7 << 8) | %00100000, 0 '66 BackSpace
word 0, 0 '67
word 0, 0 '68
word (9 << 8) | %00010000, 0 '69 (1) End
word 0, 0 '6A
word (9 << 8) | %10000000, (8 << 8) | %00010000 '6B (4) Left
word (10 << 8) | %00000100, (8 << 8) | %00000010 '6C (7) Home
word 0, 0 '6D
word 0, 0 '6E
word 0, 0 '6F
word (9 << 8) | %00001000, (8 << 8) | %00000100 '70 (0) Insert
word (10 << 8) | %10000000, (8 << 8) | %00001000 '71 (.) Delete
word (9 << 8) | %00100000, (8 << 8) | %01000000 '72 (2) Down
word (10 << 8) | %00000001, 0 '73 (5)
word (10 << 8) | %00000010, (8 << 8) | %10000000 '74 (6) Right
word (10 << 8) | %00001000, (8 << 8) | %00100000 '75 (8) Up
word (7 << 8) | %00000100, 0 '76 Esc
word 0, 0 '77 NumLock
word (7 << 8) | %01000000, 0 '78 F11
word (9 << 8) | %00000010, 0 '79 (+)
word (9 << 8) | %01000000, 0 '7A (3) PageDn
word (10 << 8) | %00100000, 0 '7B (-)
word (9 << 8) | %00000001, 0 '7C (*) PrScr
word (10 << 8) | %00010000, 0 '7D (9) PageUp
word 0, 0 '7E ScrLock
word 0, 0 '7F
word 0, 0 '80
word 0, 0 '81
word 0, 0 '82
word (6 << 8) | %01000000, 0 '83 F7
DAT ' USB Host driver
org $000
usb_host_start
mov hcog_base_addr, ptrb
mov htmp, ##@hlut_end - 4 - @hlut_start ' Dealing with hub addresses
shr htmp, #2 ' so byte->long for the lut cell count
loc pb, #@hlut_start - @usb_host_start
add pb, hcog_base_addr
setq2 htmp
rdlong 0, pb ' Do the hub->lut copy
loc pb, #@usb_host_init - @usb_host_start
add pb, hcog_base_addr
jmp pb ' Initialize host and enter main processing loop
'------------------------------------------------------------------------------
' SETUP transaction. The mechanics of SETUP are identical to OUT, but it's
' special because the receiving function must not respond with either STALL or
' NAK, and must accept the DATAx packet that follows the SETUP token. If a
' non-control endpoint receives a SETUP token, or the function receives a
' corrupt packet, it must ignore the transaction
'------------------------------------------------------------------------------
' On entry:
' PTRA - start address of the SETUP data struct.
' On exit:
' retval - PID_ACK on success, otherwise error code.
'------------------------------------------------------------------------------
txn_setup
setbyte ep_addr_pid, #PID_SETUP, #0
mov pkt_data, #SETUP_TXN_LEN ' SETUP is single fixed size DATAx packet
bitl hstatus, #DATAx_TGLB ' And always uses DATA0 packet
mov retry, #TXN_RETRIES ' Retries possible as function will ignore a corrupt packet
mov pa, ptra ' Save SETUP struct pointer in case of retry
.setup
call #txn_out ' SETUP/OUT are the same transaction type, just different PIDs
cmp retval, #PID_ACK wz
if_z ret
call #retry_wait
cmp retval, #ERR_TXN_RETRY wz
if_z ret
mov ptra, pa ' Restore SETUP's DATAx pointer
jmp #.setup
'------------------------------------------------------------------------------
' IN/INTERRUPT transaction.
' Possible function response: STALL or NAK handshake, or DATAx packet.
'------------------------------------------------------------------------------
' On entry:
' ep_addr_pid - PID_IN(b0..7), address(b8..b14), endpoint(b15..18) and
' CRC(b19..23).
' On exit:
'------------------------------------------------------------------------------
txn_in
call #wait_txn_ok ' ISR: ensure txn doesn't cross frame boundary
setbyte ep_addr_pid, #PID_IN, #0
call #utx_token ' Put IN request on the bus
' Fall through to urx_packet
'------------------------------------------------------------------------------
' Wait for a packet from a device/function. As host, the only two packet types
' received are handshakes and IN DATAx.
'------------------------------------------------------------------------------
' On entry:
' On exit:
' retval - the ID of the packet. If a PID fails validation, ERR_PACKET is
' returned.
'------------------------------------------------------------------------------
urx_packet
rqpin urx, dm ' Wait until start-of-packet signal appears on the USB.
testb urx, #SOPB wc
if_c jmp #urx_packet
getct hct2
addct2 hct2, tat_wait ' Start the response turn-around timer
bitl hstatus, #EOPB ' Make sure sticky EOP flag is clear
mov newb_flg, #0 ' Initialize for multi-byte read
.wait_sop
rdpin urx, dm
testb urx, #SOPB wc
if_c jmp #.get_pid
jnct2 #.wait_sop
_ret_ mov retval, #ERR_TAT
.get_pid
call #urx_next
testb urx, #BUS_ERRB wc
if_nc jmp #.chk_pid
_ret_ mov retval, #ERR_URX
.chk_pid
cmp retval, #PID_ACK wz
if_nz cmp retval, #PID_NAK wz
if_nz cmp retval, #PID_STALL wz
if_z jmp #.chk_eop ' Handshake, so check that packet is single byte
testb hstatus, #DATAx_TGLB wc ' Get low/full speed even/odd DATAx sequence to look for
cmp retval, #PID_DATA0 wz
if_z_and_nc jmp #urx_data ' DATA0 and sequence match
if_z_and_c jmp #.ack_resend ' Sequence error. Ignore data, resend the ACK that the device must have missed
cmp retval, #PID_DATA1 wz
if_z_and_c jmp #urx_data ' DATA1 and sequence match
if_z_and_nc jmp #.ack_resend
_ret_ mov retval, #ERR_PACKET ' Some other bus error...
.ack_resend
rqpin urx, dm
testb urx, #EOPB wc
if_nc jmp #.ack_resend
mov retval, #PID_ACK
call #utx_handshake ' Send handshake PID and return to caller
_ret_ mov retval, #ERR_DATAX_SYNC
.chk_eop
testb hstatus, #LOW_SPEEDB wc
if_nc jmp #.idle ' Full-speed doesn't need an additional read to get EOP status
call #urx_next ' Low-speed requires an additional read to get EOP status
testb hstatus, #EOPB wc
if_c jmp #.idle ' Low-speed EOP seen
testb urx, #BUS_ERRB wz
if_nc mov retval, #ERR_PACKET ' No EOP where one was expected
if_z mov retval, #ERR_URX ' Bit unstuff error, EOP SE0 > 3 bits or SE1, so we're hosed
ret
.idle
rqpin urx, dm
testb urx, #J_IDLEB wc
if_nc jmp #.idle ' Wait for bus IDLE before returning handshake result
ret
'------------------------------------------------------------------------------
' Send a token packet with CRC5 checksum of address and endpoint. It is the
' responsibility of the caller to append the appropriate inter-packet delay,
' if one is required.
'------------------------------------------------------------------------------
' On entry:
' ep_addr_pid - packed with the PID, address and endpoint.
' On exit:
'------------------------------------------------------------------------------
utx_token
rqpin urx, dm
testb urx, #J_IDLEB wc
if_nc jmp #utx_token
testb hstatus, #DWNSTRM_HUBB wc
if_c call #utx_pre
mov utx, #OUT_SOP
call #utx_byte ' Send sync byte
mov htmp, ep_addr_pid ' Preserve the PID and destination
mov pkt_cnt, #3
.next_byte
getbyte utx, htmp, #0 ' Bytes on the bus LSB->MSB
shr htmp, #8 ' Shift to next byte to send
.wait
testp dp wc
if_nc jmp #.wait
akpin dp
wypin utx, dm
_ret_ djnz pkt_cnt, #.next_byte
'------------------------------------------------------------------------------
' SETUP/OUT/INTERRUPT transaction.
' Possible function response in order of precedence: STALL, ACK, NAK.
'------------------------------------------------------------------------------
' On entry:
' ep_addr_pid - PID_OUT(b0..7), address(b8..b14), endpoint(b15..18) and
' CRC(b19..23).
' PTRA - start address of the data buff/struct that has the bytes to send.
' pkt_data - count of DATAx payload bytes to send.
' On exit:
'------------------------------------------------------------------------------
txn_out
call #wait_txn_ok ' ISR: ensure txn doesn't cross frame boundary
call #utx_token ' Put SETUP/OUT token on the bus
rdfast ##$80000000, ptra ' Use hub RAM FIFO interface to read the tx buffer
mov pkt_cnt, pkt_data
' Fall through to utx_data
'------------------------------------------------------------------------------
' Transmit a DATAx packet with USB-16 checksum of payload. The payload CRC is
' calculated while the data byte is being shifted out. Since data stage
' success/fail is not determined until the status stage of the transaction,
' this routine is only concerned about the current DATAx packet.
'------------------------------------------------------------------------------
' On entry:
' PTRA - hub start address of the data to read.
' pkt_cnt - data payload size.
' On exit:
'------------------------------------------------------------------------------
utx_data
rqpin urx, dm
testb urx, #SOPB wc
if_c jmp #utx_data
mov hctwait, ip_delay
call #poll_waitx ' SETUP/OUT token always precedes tx DATAx so insert IP delay
testb hstatus, #DWNSTRM_HUBB wc
if_c call #utx_pre
mov utx, #OUT_SOP
call #utx_byte ' Send sync
bmask crc, #15 ' Prime the CRC16 pump
testb hstatus, #DATAx_TGLB wc ' Set the requested DATAx PID
if_nc mov utx, #PID_DATA0
if_c mov utx, #PID_DATA1
call #utx_byte ' No CRC calc done on PID
cmp pkt_cnt, #0 wz ' Check if sending a zero length payload
if_z jmp #.send_crc ' If so, only the CRC goes out
.read_byte
rfbyte utx ' Fetch data byte
call #utx_byte
rev utx ' Calculate CRC while the data is shifting out
setq utx ' SETQ left-justifies the reflected data byte
crcnib crc, ##USB16_POLY ' Run CRC calc on the data nibs
crcnib crc, ##USB16_POLY
djnz pkt_cnt, #.read_byte
.send_crc
bitnot crc,#0 addbits 15 ' Final XOR, and send the calculated CRC16
getbyte utx, crc, #0
call #utx_byte
getbyte utx, crc, #1
call #utx_byte ' Last CRC byte out
jmp #urx_packet ' Handle function response/error and back to caller
'------------------------------------------------------------------------------
' Receive a DATAx_ payload with USB-16 checksum. The CRC is calculated as the
' payload bytes are received. The routine reads bytes until EOP is detected and
' expects that the packet includes at least the CRC word.
'
' In control transfers, it's possible to recieve fewer data bytes than what
' was requested, which makes it difficult to determine where the data stops
' and the CRC word begins. So the CRC calculation is done on every byte of the
' packet, including the CRC word. The CRC value should then be equal to the
' USB-16 expected residual value of 0xB001.
'
' The routine writes the IN packet data to a static max_packet_size buffer
' so the caller can verify IN success before writing the data to its final
' destination.
'------------------------------------------------------------------------------
' On entry:
' pkt_data - max byte count expected to be in the packet.
' newb_flg - signals new byte ready when toggled.
' On exit:
' pkt_cnt - actual number of bytes read.
'------------------------------------------------------------------------------
urx_data
mov htmp2, pb
mov pb, urx_buff_p
wrfast ##$80000000, pb ' Use hub RAM FIFO interface to buffer bytes received
mov pb, htmp2
bmask crc, #15 ' Prime the CRC16 pump
mov pkt_cnt, #0 ' Keep track of payload bytes received
mov pkt_tmp, pkt_data
add pkt_tmp, #2 ' Tweak payload byte count to include CRC word
.wait_byte
' In-line rx for max speed
rqpin urx, dm
mov utx, #BYTE_TGLF ' Reg utx free in this context
and utx, urx
cmp newb_flg, utx wz ' Fetch a byte whenever the flags differ
if_nz xor newb_flg, #BYTE_TGLF ' Synchronize flags
if_nz jmp #.get_byte ' New byte!
testb urx, #EOPB wc
if_c jmp #.chk_crc ' At end-of-packet
jmp #.wait_byte
.get_byte
getbyte retval, urx, #1 ' New byte from smart pins
wfbyte retval ' Add it to the data buffer
rev retval ' Calculate CRC while next byte is shifting in
setq retval ' SETQ left-justifies the reflected data byte
crcnib crc, ##USB16_POLY ' Run CRC calc on the data nibs
crcnib crc, ##USB16_POLY
.end_crc
add pkt_cnt, #1
cmp pkt_cnt, pkt_tmp wcz
if_a mov retval, #ERR_PACKET ' Error if payload > expected size
if_a ret
' For full-speed at 80MHz, the time it takes to do the final byte write and
' CRC verify has likely put us into the EOP zone. The P2 smart pins keep the
' EOP flag "sticky" for 7-bits of J, but at 80MHz, it still could be possible
' to miss it, so cheat a bit and look for SOP clear here.
rqpin urx, dm
testb urx, #EOPB wc ' FIXME: checking for EOP set should work when > 80MHz
if_nc jmp #.wait_byte ' Next read will catch EOP at low-speed
' CRC OK = Payload CRC calc ^ packet's CRC bytes = $B001 (the USB-16 expected residual)
.chk_crc
sub pkt_cnt, #2 ' Adjust payload count to exclude the CRC bytes read
xor crc, ##USB16_RESIDUAL wz ' CRC of (data + transmitted CRC) XOR residual should equal zero
if_nz jmp #urx_packet ' CRC fail; discard data and wait until data re-sent or transfer timeout
mov retval, #PID_ACK
mov hctwait, ip_delay
call #poll_waitx
' Fall through to utx_handshake
'------------------------------------------------------------------------------
' Transmit a handshake PID. The routine assumes that the bus is IDLE and
' the appropriate IP delay has been inserted.
'------------------------------------------------------------------------------
' On entry:
' retval - handshake PID to send.
' On exit:
' retval unchanged.
'------------------------------------------------------------------------------
utx_handshake
testb hstatus, #DWNSTRM_HUBB wc
if_c call #utx_pre
mov utx, #OUT_SOP
call #utx_byte ' Send sync
mov utx, retval
call #utx_byte ' Send handshake PID
.idle
rqpin urx, dm
testb urx, #J_IDLEB wc
if_nc jmp #.idle ' Wait for IDLE to ensure the PID tx is complete
mov hctwait, tat_wait ' Ensure one turn-around time before next transaction
jmp #poll_waitx
'------------------------------------------------------------------------------
' Wait for the USB tx buffer to empty and feed it a new byte.
'------------------------------------------------------------------------------
' On entry:
' utx - byte to transmit.
' On exit:
'------------------------------------------------------------------------------
utx_byte
testp dp wc
if_nc jmp #utx_byte
akpin dp
waitx utx_tweak ' Wait #0 '#3 if < 180MHz, wait #3 '#20 if 180MHz+
_ret_ wypin utx, dm
'------------------------------------------------------------------------------
' Fetch the next data byte of a packet. Always check receiver status for EOP.
'------------------------------------------------------------------------------
' On entry:
' On exit:
' retval - the byte read.
' urx - the receiver status. The caller must check the hstatus reg EOP flag
' on return. If EOP is set, the byte in reg retval remains as the last byte
' received.
'------------------------------------------------------------------------------
urx_next
rdpin urx, dm
mov utx, #BYTE_TGLF ' Reg utx free in this context
and utx, urx
cmp newb_flg, utx wz ' Fetch a byte whenever the flags differ
if_nz xor newb_flg, #BYTE_TGLF ' Synchronize flags
if_nz getbyte retval, urx, #1 ' Fetch the new byte
if_nz ret ' New byte is priority, so return now
testb urx, #SOPB wc
testb urx, #BUS_ERRB wz
if_c_and_nz jmp #urx_next ' If SOP still raised and !BUS_ERRB a new byte should be coming
if_nc bith hstatus, #EOPB ' If EOP make it sticky, otherwise it's a bus error
ret
'------------------------------------------------------------------------------
' Calculate USB-5 CRC. The upper word of the CRC pre-calc table in LUT contains
' the data used for the USB-5 CRC lookups. The token packet is three bytes in
' length, and the PID is not included in the CRC calculation:
' CRC5 FRAME_NUMBER SOF (full-speed)
' CRC5 ENDP ADDRESS PID
' %00000_1111_1111111_xxxxxxxx
'------------------------------------------------------------------------------
' On entry:
' ep_addr_pid - stuffed with the function endpoint, address and
' SETUP/IN/OUT/SOF PID according to the USB standard.
' On exit:
' ep_addr_pid - CRC value appended to the packet.
'------------------------------------------------------------------------------
calc_crc5
and ep_addr_pid, ##EP_ADDR_MASK ' Clear existing CRC, if any
mov htmp, ep_addr_pid
shr htmp, #8 ' PID not included in CRC calc
mov crc, #$1f ' Initial CRC5 value
rev htmp ' Input data reflected
setq htmp ' CRCNIB setup for data bits 0..7
crcnib crc, #USB5_POLY
crcnib crc, #USB5_POLY ' Data bits 0..7 calculated
shl htmp, #9 wc ' Shift out processed bits + 1 to set up CRC of remaining bits 8..10
crcbit crc, #USB5_POLY ' Inline instead of REP as we're in hubexec
shl htmp, #1 wc
crcbit crc, #USB5_POLY
shl htmp, #1 wc
crcbit crc, #USB5_POLY
xor crc, #$1f ' Final XOR value
shl crc, #8 + 11 ' CRC to bits 23..19 of the token packet
_ret_ or ep_addr_pid, crc ' Put the CRC in its new home
'------------------------------------------------------------------------------
' Full-speed/low-speed frame timing interrupt service routine.
'------------------------------------------------------------------------------
isr1_fsframe
getct iframe_ct_base
mov iframe_ct_new, iframe_ct_base
addct1 iframe_ct_new, _frame1ms_clks_
wxpin _usb_h_fs_nco_, dm ' Restore host mode and 12Mbs baud
.wait
testp dp wc
if_nc jmp #.wait
akpin dp
mov utx, #PID_SOF
wypin #OUT_SOP, dm ' Put start-of-packet SYNC field on the USB
call #utx_byte ' Send token PID byte
mov icrc, #$1f ' Prime the CRC5 pump
mov sof_pkt, frame ' CRC5 calculation done on the 11-bit frame number value
rev sof_pkt ' Input data reflected
setq sof_pkt ' CRCNIB setup for data bits 0..7
crcnib icrc, #USB5_POLY
crcnib icrc, #USB5_POLY ' Data bits 0..7 calculated
getbyte utx, frame, #0 ' Send the low byte of the frame number
call #utx_byte
shl sof_pkt, #8 ' Shift out processed bits to set up CRCBIT * 3
rep #2, #3 ' Three data bits left to process
shl sof_pkt, #1 wc
crcbit icrc, #USB5_POLY ' Data bits 8..10 calculated
xor icrc, #$1f ' Final XOR value
getbyte utx, frame, #1 ' Send remaining frame number bits
shl icrc, #3 ' Merge CRC to bits 7..3 of the final token byte
or utx, icrc
call #utx_byte ' Last start-of-frame byte is on the wire
mov isrtmp1, _ip_delay_fs_ ' Use normal inter-packet delay when full-speed
jmp #isr1_wait
isr1_lsframe
getct iframe_ct_base
mov iframe_ct_new, iframe_ct_base
addct1 iframe_ct_new, _frame1ms_clks_
.wait
testp dp wc
if_nc jmp #.wait
akpin dp
wypin #OUT_EOP, dm ' EOP is the low-speed keep-alive strobe
mov isrtmp1, _ip_delay_ls_ ' Normal inter-packet delay works when low-speed
isr1_wait
rqpin utx, dm
testb utx, #SOPB wc
if_c jmp #isr1_wait
add frame, #1 ' Next frame# and check for wrap around
zerox frame, #10
waitx isrtmp1 ' Make sure bus is idle
reti1
'------------------------------------------------------------------------------
' Wait for a window within the 1ms frame boundary that will ensure that a
' transaction will complete before the next frame is triggered.
'------------------------------------------------------------------------------
' On entry:
' On exit:
'------------------------------------------------------------------------------
wait_txn_ok
getct htmp2
sub htmp2, iframe_ct_base
testb hstatus, #LOW_SPEEDB wc
if_c cmp htmp2, _txn_ok_ls_ wcz
if_nc cmp htmp2, _txn_ok_fs_ wcz
if_a jmp #wait_txn_ok ' Not enough time, so wait until next frame
ret
'------------------------------------------------------------------------------
' A device connection was detected, or a bus reset was requested by the USB
' client. Set the appropriate smart pin FS/LS speed mode to match the device
' and perform a reset sequence prior to device enumeration.
'------------------------------------------------------------------------------
dev_reset
rqpin urx, dm
testb urx, #K_RESUMEB wc ' K differential "1" in FS mode signals low-speed
if_c call #set_speed_low ' The speed config subroutines must restore the caller C flag
if_nc call #set_speed_full ' state on return if it writes the C flag.
reset
setint1 #0 ' Don't want frame interrupt while in reset
wypin #OUT_SE0, dm ' Assert bus reset
waitx _reset_hold_ ' Spec is >= 10ms
wypin #OUT_IDLE, dm
mov frame, #0 ' Reset the frame timespan count
getct iframe_ct_base
mov iframe_ct_new, iframe_ct_base
addct1 iframe_ct_new, _frame1ms_clks_
mov htmp, frame ' Allow reset recovery time (Section 9.2.6.2)
add htmp, #36
setint1 #1 ' Set ISR event trigger to CT-passed-CT1
.framewait
cmp frame, htmp wcz
if_b jmp #.framewait
ret
'------------------------------------------------------------------------------
' Bulk hub<->hub byte copy. Does not check for src/dest buffer overlap.
'------------------------------------------------------------------------------
' On entry:
' PTRA - source address.
' PB - destination address.
' hr0 - length of copy, in bytes.
' On exit:
'------------------------------------------------------------------------------
hmemcpy
rdbyte htmp, ptra++
wrbyte htmp, pb
add pb, #1
_ret_ djnz hr0, #hmemcpy