-
Notifications
You must be signed in to change notification settings - Fork 3
/
pcetech_edit.txt
1907 lines (1462 loc) · 78 KB
/
pcetech_edit.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
TurboGrafx-16 Hardware Notes
by Charles MacDonald
WWW: http://cgfm2.emuviews.com
Unpublished work Copyright 2001, 2002 Charles MacDonald
Revision history:
[09/22/01]
- Initial revision
[09/23/01]
- Added CD-ROM details to memory map
- Added CD sense bit to I/O port
- Fixed description of read latch loading
- Added display parameter list
[09/24/01]
- Formatted and cleaned up document
- Added BRAM details
- Added display layout information
- Re-tested the effect of illegal instructions on P and S
- Re-tested P startup value
- Re-tested MPR startup value
- Rewrote T-flag section
- Rewrote CD-ROM section
- Added details to VCE section
[09/25/01]
- Added more details to VCE section
- Added joypad connector pinout
- Added 2-button controller details
- Added Turbo Tap information
- Added information on block transfer instructions
[09/28/01]
- Added description of MWR register and CG mode processing
- Added description of VDC addresss and register access
- Added sprite information
- Added background information
- Confirmed use of status flag bits
- Added several registers to register reference
[10/01/01]
- Added read result for VCE address registers
- Added preliminary Super System Card information
- Added details of BRAM unlock sequence
[10/07/01]
- Added information on VDC register $05
- Added information on VDC/VCE accesses
- Added sprite section
- Added details on invalid pattern name values
[10/08/01]
- Added details on $0800-$17FF data buffer
- Added information on invalid TAM/TMA arguments
- Added information on store immediate instructions
- Added information on BRK and B flag
- Confirmed cycle count for illegal instructions
- Added information on cross-page/bank branches
- Added information on RMW instructions
- Added information on JMP,indirect
[10/10/01]
- Added information of P state within interrupts
- Added information on T flag and RTI
- Added better description of TMA #$00 results
[10/11/01]
- Added information on when CRT registers can be changed
- Added DMA section
[10/25/01]
- Added timer section
- Added interrupt section
- Fixed VCE dot clock information
- Made minor changes to a few sections
- Confirmed CPU speed at power-up
- Added timing section
[10/27/01]
- Added more details to the timer section
- Added a note about ADC
[12/02/01]
- Added information on how TAM and TMA work
- Rewrote description of RCR register
[12/07/01]
- Added observations about CD-ROM registers
- Added information on ADPCM hardware
- Added more information about block transfers
[12/08/01]
- Added more information on TMA #$00
- Added information on IRQ2
- Added information on initial timer and IRQ register values
- Added a note about SBC
[01/25/02]
- Added details about VCE palette flicker
- Added details on flag calculation for some instructions
- Rewrote display details section
- Fixed description of VDC's VD flag
- Added some notes about the PSG
- Added lots of information to the CD section
[02/28/02]
- Added a note about $180D
- Added a note about block transfer instructions
- Added details on CSL, CSH
Table of contents:
1.) Introduction
2.) HuC6280 - CPU
2.1) Interrupts
2.2) Timer
2.3) T flag
2.4) Timing
3.) Memory map
4.) I/O port
5.) HuC6260 - Video Display Controller
5.1) Register reference
5.2) Background
5.3) Sprites
5.4) DMA
6.) HuC6270 - Video Color Encoder
7.) Display details
8.) CD-ROM
8.1) Super System Card
9.) Display parameter settings
10.) Programmable Sound Generator
11.) Acknowledgements
12.) Contact
13.) Disclaimer
----------------------------------------------------------------------------
1.) Introduction
----------------------------------------------------------------------------
This document is in a very preliminary state and is subject to change.
Everything within has been tested and verified on a TurboGrafx-16 console,
but please be aware that my testing methods or interpretations of
results could be flawed. I can't guarantee that everything is 100% accurate.
At the moment, some parts of this document are simply a compilation of
notes and test results, while others are detailed descriptions of the
hardware. I'll try to get everything coordinated as time progresses.
----------------------------------------------------------------------------
2.) HuC6280 - CPU
----------------------------------------------------------------------------
- Block transfer instructions push Y, A, X to the stack in that order, and
then pop X, A, Y from the stack in that order when finished.
- For the alternating block transfer instructions (TAI and TIA), they
alternate the source or destination address by adding and then
subtracting one; not by inverting bit 0 of the address.
- The length parameter to a block transfer instruction specifies the
number of bytes to transfer. For example, $0010 will transfer 16 bytes,
and $0000 will transfer 64K bytes, not zero.
- Block transfer instructions cannot be interrupted. If an interrupt
is supposed to occur, it occurs once the instruction finishes.
- When using any block transfer instruction to read addresses $0800 through
$1400 in the I/O page, the value zero is always returned for every
address, regardless of the CPU speed. (So you can't read the joystick
port, timer, or IRQ registers) The I/O buffer is not changed either.
Writing to the same range of addresses using the block transfer
instructions will work, and the I/O buffer will be modified.
- Stack and zero page operations always use logical addresses $2000-$21FF.
For example, ROM data can be read by using instructions that access the
zero page or stack.
- On power-up, MPR 7 is set to zero, and the other MPRs are loaded
with random values.
- The TMA instruction transfers the contents of an MPR register to the
accumulator. Bits 7 to 0 in the operand specify which MPR register to
read from, bit 7 is MPR #7 and bit 0 is MPR #0.
If an operand of $00 is used, the accumulator is loaded with the last
value that was written with TAM (only if one or more of it's operand bits
were set), or the last value that was read with TMA. I think the CPU
treats zero as a 'no change' value and the MPR selecting logic isn't
updated from the last time it was set.
If multiple bits are set in the operand to TMA, the values from several
MPRs are combined together and returned. However, I have not figured out
exactly how this works.
- The TAM instruction transfers the contents of the accumulator to an MPR
register. Bits 7 to 0 in the operand specify which MPR register to write
to, bit 7 is MPR #7 and bit 0 is MPR #0.
If an operand of $00 is used, none of the MPR registers are written to.
This does not change the last MPR value that can be read by TMA #$00.
If multiple bits are set in the operand to TAM, each MPR register selected
is loaded with the accumulator. For example, an operand of $FF would load
all MPR registers.
- ST0, ST1, ST2 write immediate data directly to the VDC (at physical
addresses $1FE000-$1FE003), the address is not translated through the
CPUs memory mapping hardware.
- When an interrupt occurs (I've tested the timer and IRQ1), P is pushed
with the current state of D and T. Within the interrupt subroutine, the
CPU clears D, T and sets I, preventing further interrupts from occuring.
- The B flag is set at all times. The only exception is when an interrupt
occurs, (I've tested the timer and IRQ1) in this case the value of P
pushed to the stack has B cleared. (but B is set if P is read again
within the interrupt subroutine) The BRK instruction pushes P with B set.
- BRK pushes the return address plus one to the stack; the next byte after
the BRK instruction is always skipped.
- The CSL and CSH instructions change the CPU's clock speed. CSL selects
low speed mode which is 1.78 MHz, CSH selects high speed mode which is
7.16 MHz. On power-up the CPU is in low speed mode.
CSH and CSL take 3 cycles each, but that was tested with the CPU already
set to the respective clock speed. It currently isn't known if either
instruction takes more or less time when switching between different
speeds.
- On power-up, the timer count is set to zero and the IRQ disable mask is
set to zero.
6502/65C02 bugs and features compared against the HuC6280:
- On power-up, A, X, Y, and S hold random values. P always has T and D
cleared, I and B set, and N, Z, V, C are random.
- A branch instruction that crosses a 256-byte or 8192-byte boundary does
not take any additional cycles.
- An indirect JMP instruction with the low byte of the address set to $FF
will correctly read the high byte at the next address, instead of wrapping
to address 0 like the 6502 does. (so jmp [$FEFF] reads the MSB from
address $FF00, not $FE00)
- Illegal opcodes are treated as a NOP, and take 2 cycles each.
They do not change the state of the A, X, Y, S, or P registers, with the
exception that the T flag will be cleared if set prior to executing an
illegal opcode (check the section on the T flag for more information).
I tested the following opcodes:
E2, 63, 33, 0B, 2B, 4B, 6B, 8B, AB, CB, EB, 1B, 3B, 5B, 7B, 9B,
BB, DB, FB, 5C, DC, FC
- Read-modify-write instructions read the effective address once, then
write the modified value. There are no dummy writes (like the 6502) or
reads. (like the 65C02)
- The decimal mode versions of ADC and SBC do not change the state of the
overflow flag.
Flag calculation
I haven't tested all of the instructions, just those that I wasn't sure
about.
Logical Shift Right (LSR)
N = 0
C = Bit 0 of operand
Z = Set if result is zero
Pull X, Pull Y (PHX, PHY)
N = Bit 7 of pulled byte
Z = Set if pulled byte is zero
Rotate Left (ROL)
C = Bit 7 of operand
N = Bit 6 of operand
Z = Set if result is zero
Rotate right (ROR)
N = Set if carry is set
C = Bit 1 of operand
Z = Set if result is zero
Test and Set Bits (TSB)
Test and Reset Bits (TRB)
N = Bit 7 of result
V = Bit 6 of result
Z = If result is zero
Test (TST)
N = Bit 7 of operand
V = Bit 6 of operand
Z = Set if operand & immediate byte is equal to zero
Bit (BIT)
N = Bit 7 of operand
V = Bit 6 of operand
Z = Set if if operand & accumulator is equal to zero
For the immediate form of this instruction, the flags are still calculated
in the same way. So 'BIT #$C0' would set N and V, for example.
----------------------------------------------------------------------------
2.1) Interrupts
----------------------------------------------------------------------------
The HuC6280 has several interrupt sources:
NMI - Not used. It isn't connected to anything internally nor is it
available on any connector.
IRQ2 - Available on the HuCard and backplane connectors. It's used by the
CD-ROM's ADPCM hardware, and the BRK instruction also uses the IRQ2
vector.
IRQ1 - Connected to the VDC.
Timer - Generated by the HuC6280's internal timer.
The patents mention an external input used to test timer interrupts,
but I believe this isn't used in the TurboGrafx-16.
Interrupts can be disabled through the CPU, the I flag of the P register
disabled interrupts (except NMI, BRK) when set. In addition, there are
four registers, two of which are usable, that control interrupts:
$1400 : Writes do nothing, reads return the I/O buffer contents.
$1401 : Writes do nothing, reads return the I/O buffer contents.
$1402 : Bits 2-0 are interrupt enable/disable bits, which can be read as
well as written.
bit 2 - 1= Disable timer interrupt, 0= Enable timer interrupt
bit 1 - 1= Disable IRQ1 interrupt, 0= Enable IRQ1 interrupt
bit 0 - 1= Disable IRQ2 interrupt, 0= Enable IRQ2 interrupt
Bits 7-3 return the I/O buffer contents.
$1403 : Writing any value acknowledges the timer interrupt.
Reads return the interrupt state in bits 2-0:
bit 2 - 1= Timer interrupt pending, 0= No timer interrupt
bit 1 - 1= IRQ1 interrupt pending, 0= No IRQ1 interrupt
bit 0 - 1= IRQ2 interrupt pending, 0= No IRQ2 interrupt
Bits 7-3 return the I/O buffer contents.
The enable/disable register does not actually stop the interrupt from
occuring; for example if the VDC asserts the IRQ1 line, and bit 1 of $1402
is set, then an interrupt isn't generated. But you can still read the state
of the IRQ1 line through $1403, and bit 1 would be set in this case.
All interrupts need to be acknowledged. If not, the interrupt in question
occurs after every instruction that is executed (unless the I flag or
interrupt disable registers are used)
----------------------------------------------------------------------------
2.2) Timer
----------------------------------------------------------------------------
The HuC6280 has a 7-bit timer that is decremented once every 1024 clock
cycles. This is based on a 7.16 MHz clock, and is unrelated to the CPU
speed determined by CSL/CSH.
The timer is controlled by two registers:
$0C00 : Timer value (bits 6-0)
A value of $00 counts as 1, $7F counts as 128.
Bit 7 is unused.
$0C01 : Timer enable (bit 0)
Bits 7-1 are unused.
Data written to $0C00 is copied to a 7-bit latch. When the timer is
enabled, a 7-bit counter is loaded with the contents of the latch. The
counter is decremented once every 1024 clock cycles, and the timer
interrupt request line is asserted when the counter underflows from zero
to $7F. (not when the timer goes from one to zero) However, when this
occurs the counter is reloaded, so after reading zero from the timer
registers you will read the value that's been reloaded into the counter,
not $7F.
The interrupt can then be acknowledged by writing any value to $1403. If
this is not done an interrupt will occur after every instruction.
Reading $0C00 or $0C01 returns the current value in the counter, or if the
timer is disabled, then the last value the counter had prior to it being
disabled. If the timer is disabled and then enabled again, it is reloaded
with the last value written to $0C00.
When the timer expires, it is reloaded with the last value written to $0C00.
The timer begins to count down immediately, it does not wait for the
interrupt to be acknowledged. (so the timer is reloaded and counts down
within the timer interrupt routine)
Bit 7 of $0C00 and $0C01 return the contents of the I/O buffer.
----------------------------------------------------------------------------
2.3) T flag
----------------------------------------------------------------------------
The HuC6280 assigns the T flag to bit 5 of the processor status register.
It allows all forms of the ADC, AND, ORA and EOR instructions to be
processed differently, while the other instructions execute normally.
When the T flag is set, the accumulator is replaced with a zero page memory
location indexed by the X register. The operation defined by the
instruction is performed using the memory location as one operand, and the
effective address as the other. The result is stored in the memory
location, leaving the accumulator undisturbed.
It seems that the T flag is cleared each time the CPU fetches an
instruction. For example, both BRK and PHP (which save the status of P on
the stack) always push it with the T flag cleared when prefixed by SET.
T can be set by the SET instruction, or by pushing a byte with bit 5 set
and pulling it into P via PLP (in which case the instruction after PLP
will be affected as if SET came before it).
If you want to use this feature with ADC for processing BCD numbers,
you must execute SED before SET, otherwise the T flag will be cleared.
Like PLP, RTI keeps the T flag set. If an interrupt occurs after a SET
instruction which causes P to be pushed with T set, or if the stack
is manipulated to have T set, the instruction following the return address
used by RTI will be affected as if it was prefixed with SET.
The Mitsubishi 740 series of 65C02-derived CPUs also have a similar
feature, with some differences. The T flag remains set until cleared with
a CLT instruction or when P is modified. It also supports SBC, LDA and CMP
as valid instructions to use with the T flag.
---------------------------------------------------------------------------
2.4) Timing
----------------------------------------------------------------------------
The TurboGrafx-16 has a 21.47727 MHz master clock which is used to drive
several components:
- Divided by three in high speed mode (7.16 MHz) or twelve in low speed
mode (1.78 MHz) to provide the CPU clock. This is controlled by the
CSL and CSH instructions.
- Divided by three to run the timer. (7.16 MHz)
- Divided by six for the PSG clock. (3.58 MHz)
The PSG patents say the clock is 7.16 MHz, but all of the formulas for
determining frequency multiply the result by two, effectively making
the clock 3.58 MHz.
- The VCE divides the master clock by 2, 3, or 4, for 10.74 MHz, 7.16 MHz,
and 5.37 MHz dot clocks. The VDC presumably runs at whatever speed the
VCE does. It uses 1365 masterclocks per scanline, 455 clocks @7.16MHz.
---------------------------------------------------------------------------
3.) Memory map
----------------------------------------------------------------------------
The HuC6280 has a 21-bit address bus. All of the address decoding is
handled internally by the CPU.
The address space is divided into 256 8K pages, and the following memory
map refers to the page number only:
$00-$7F : HuCard ROM (1)
$80-$F7 : Unused (always returns $FF) (2)
$F8-$FB : Work RAM (pages $F9-$FB mirror page $F8)
$FC-$FE : Unused (always returns $FF)
$FF : Hardware page
1. Depending on the configuration of the HuCard, ROMs smaller
than 1MB may be mirrored within this range.
2. See the CD-ROM section for more details.
Hardware page ($FF)
$0000-$03FF : VDC (registers mirrored every 4 bytes) (3)
$0400-$07FF : VCE (registers mirrored every 8 bytes) (3)
$0800-$0BFF : PSG (1)
$0C00-$0FFF : Timer (registers mirrored every 2 bytes) (1)
$1000-$13FF : I/O port (mirrored every byte) (1)
$1400-$17FF : Interrupt control (registers mirrored every four bytes) (1)
$1800-$1BFF : Always returns $FF (2)
$1C00-$1FFF : Always returns $FF
1. The last value read from or written to $0800-$17FF is saved in an
internal 8-bit buffer. Reading $0800-$17FF will return this value,
though readable locations will modify certain bits in the buffer.
Here are some details:
$0800-$080F : All addresses return the buffer value in full.
$0C00-$0C01 : Both locations return the buffer value in bit 7.
$1000-$1000 : Always returns the I/O port value. (no bits are from
the buffer)
$1400-$1403 : $1400/$1401 return the buffer value in full, $1402/$1403
return the buffer value in the upper five bits.
Some example code to illustrate how the buffer works:
stz $0C01 ; Buffer = $00
stz $0C00 ; Buffer = $00
lda #$05
sta $1402 ; Buffer = $05
lda #$FF
sta $080F ; Buffer = $FF
lda $1400 ; Read $FF, Buffer = $FF
lda $1402 ; Read $FD, Buffer = $FD
sta $1000 ; Buffer = $FD
lda $0C00 ; Read = $80, Buffer = $80
2. See the CD-ROM/Arcade-card section for more details.
3. When accessing the VDC or VCE, an additional cycle is taken. This occurs
for reads and writes (regardless of addressing mode) and instruction
execution. (e.g. jsr $0002) I figured this had something to do with the
VDC and VCE being external to the CPU, however the CD-ROM registers are
not affected.
----------------------------------------------------------------------------
4.) I/O port
----------------------------------------------------------------------------
Here is the layout of the I/O port bits:
D7 : CD-ROM base unit sense bit (1= Not attached, 0= attached)
D6 : Country detection (1= PC-Engine, 0= TurboGrafx-16)
D5 : Always returns '1'
D4 : Always returns '1'
D3 : Joypad port pin 5 (read)
D2 : Joypad port pin 4 (read)
D1 : Joypad port pin 3 (read) / pin 7 CLR (write)
D0 : Joypad port pin 2 (read) / pin 6 SEL (write)
The TurboGrafx-16 uses a 9-pin connector for peripherals. I use the naming
conventions from the Develo Box schematics:
Pin 1 - Vcc
Pin 2 - D0
Pin 3 - D1
Pin 4 - D2
Pin 5 - D3
Pin 6 - SEL
Pin 7 - CLR
Pin 8 - Gnd
Pin 9 - Gnd
2-button controller details:
The 2-button controller has a four-way directional pad and four buttons:
Select, Run, II and I. A multiplexer is used to determine which values
(directions or buttons) are returned when D3-D0 are read. The SEL line of
the I/O port selects directions when high, and buttons when low. The state
of D3-D0 are inverted, so '0' means a switch is closed and '1' means a
switch is open.
SEL = 0 SEL = 1
D3 : Run Left
D2 : Select Right
D1 : Button II Down
D0 : Button I Up
Games use a small delay after changing the SEL line, before the new data is
read (a common sequence is PHA PLA NOP NOP). This ensures the multiplexer
has had enough time to change it's state and return the right data.
When the CLR line is low, the joypad can be read normally. When CLR is
high, input from the joypad is disabled and D3-D0 always return '0'.
Turbo Tap details:
The Turbo Tap is a 5-player adapter that plugs into the joypad port. It
allows five controllers to be read in a serial fashion, one after the other.
This is handled by an internal counter that is incremented each time there
is a zero-to-one transition of the SEL line while CLR is zero.
The counter can be reset by holding SEL high and doing a zero-to-one
transition on CLR. At this point, you can then strobe SEL five times
to read each controller. Once all five controllers have been read,
the Turbo Tap will return $00 in D3-D0 until the counter is reset again.
Unconnected controllers always return $0F in D3-D0.
There is also a quirk in how the data is returned during the reset
sequence. When $01 is written to $1000, D3-D0 are returned as zero,
even though the CLR line isn't high. When $03 is written to $1000, D3-D0
now return the direction pad data for controller #1, although now the CLR
line is high and should disable joypad input.
After resetting the Turbo Tap, reading continues normally. You can set
SEL high to read the directions, low to read the buttons, and the next high
transition will increment the counter and return data from controller #2,
and so on up to controller #5.
----------------------------------------------------------------------------
5.) HuC6260 - Video Display Controller
----------------------------------------------------------------------------
The Video Display Controller (VDC) manages a background layer, sprites,
and display generation. It has 20 internal 16-bit registers, and can
address up to 128K of video RAM (VRAM).
The TurboGrafx-16 only has 64K VRAM available, so the latter half of
the 128K area mirrors the former half. Sometimes reading mirrored VRAM
returns corrupted data, and writes to the mirrored half of VRAM are always
ignored (this includes VWR access or through VRAM to VRAM DMA).
The VDC is mapped to addresses $0000-0003 in the hardware page, and these
locations are repeatedly mirrored throughout the $0000-03FF area.
VDC addresses:
$0000 : VDC register latch
$0001 : Unused (writes do nothing, reads return $00)
$0002 : VDC data (LSB)
$0003 : VDC data (MSB)
The lower five bits of $0000 select which register will be accessed
at $0002 and $0003. Only registers $00-$02, $05-$13 are valid; selecting
registers $03-$04 or $14-1F and trying to access them has no effect.
Likewise, reading $0002 or $0003 when any other register but VRR is
selected will return the contents of the read buffer; but reading $0003
will not update MARR.
You can update registers in part by writing to the LSB or MSB; the new
data written will immediately have an effect. Some registers have special
properities when the MSB is written to. See the register reference section
for more details.
Reading $0000 returns a set of status flags. The letters in parenthesis
are the names of the flags from the Develo Book (I think):
D7 : Unused, always returns '0'
D6 : Set when the VDC is waiting for a CPU access slot during
the active display area. (BSY)
D5 : Set when the vertical blank interrupt occurs. (VD)
D4 : Set when the VRAM to VRAM DMA completion interrupt occurs. (DV)
D3 : Set when the VRAM to SAT DMA completion interrupt occurs. (DS)
D2 : Set when the raster compare interrupt occurs. (RR)
D1 : Set when the sprite overflow occurance interrupt occurs. (OR)
D0 : Set when the sprite #0 collision detection interrupt occurs. (CR)
Bit 6 is set when the VDC is waiting to read or write data requested by
the CPU when it accesses VRR/VWR. For more information, see register $09
in the register reference section.
Bits 5-0 are set when a condition occurs which would trigger an interrupt,
as dictated by the corresponding interrupt enable bits in register $05
and $0F. If the interrupt enable bits are not set, the matching status
bits will not be set, even if the condition occurs. Bits 5-0 are cleared
after the status port is read.
Bit 5 is set on the first line after the active display area ends, which
signifies the vertical blanking period has started. This will occur even
if the line after the active display area is off-screen, such as within
the first 14 lines of the frame (top blanking) or the bottomost 7 (bottom
blanking and vertical sync)
If the active display area uses more than 261 lines (assuming VSW and VDS
are zero) the interrupt will always occur on line 261, which is the next
to last line in the frame. So even if VDW was set to something unusual
like $1FF, the interrupt would occur on line 261 within the frame.
Bit 4 is set when a VRAM to VRAM DMA transfer has finished.
Bit 3 is set when a VRAM to SAT transfer has finished, which seems to
always happen four lines after the last line in the active display period.
It's not known when the transfer actually starts. The exact line affected
depends on the setting of VSW, VDS and VSW.
Bit 2 is set when the current scanline matches the value in register $06.
See the register reference section for more details.
Bit 1 is set when there are more than 16 sprites on the current scanline.
See the sprite section for more details.
Bit 0 is set when an opaque pixel in sprite #0 overlaps another opaque
pixel from any other sprite. See the sprite section for more details.
All interrupts caused by the VDC must be acknowledged by reading the
status flags once within the IRQ handler. If this is not done, the IRQ
line is not lowered and the interrupt occurs as soon as the handler RTIs.
----------------------------------------------------------------------------
5.1) Register reference
----------------------------------------------------------------------------
$00 - Memory Address Write Register (MAWR)
Bits 15-0 select a word offset in VRAM that will be used for VRAM writes.
$01 - Memory Address Read Register (MARR)
Bits 15-0 select a word offset in VRAM that will be used for VRAM reads.
After you have written the MSB of this value, a word from VRAM is read
and stored in the read buffer. On power-up, the contents of the read
buffer are indeterminate. (usually $FFFF)
$02 - VRAM Read Register / VRAM Write Register (VRR/VWR)
When you write to the LSB of this register, the CPU data is stored in a
temporary location called the write latch. When the MSB is written, the
entire 16-bit value composed of the write latch and MSB are written into
the VRAM address specified by MAWR. MAWR is then incremented by the
increment factor selected by register $05.
Writing to the LSB multiple times only updates the lower half of the
write latch and does not change MAWR or VRAM data. Writing to the MSB
multiple times will write the previously latched LSB data along with the
new MSB.
Reading the LSB will return the lower byte of the read buffer, and
reading the MSB will return the upper byte of the read buffer. MARR is
then incremented by the increment factor selected by register $05, and
a word of VRAM is read from the new address into the read buffer.
$03 - Unused
This register doesn't seem to have any effect when written to.
$04 - Unused
This register doesn't seem to have any effect when written to.
$05 - Control Register (CR)
D16-D13 : Unused
D12 : Increment width select (bit 1)
D11 : Increment width select (bit 0)
D10 : 1= DRAM refresh enable
D9 : DISP terminal output mode (bit 1)
D8 : DISP terminal output mode (bit 0)
D7 : 1= Background enable
D6 : 1= Sprite enable
D5 : External sync (bit 1)
D4 : External sync (bit 0)
D3 : 1= Enable interrupt for vertical blanking
D2 : 1= Enable interrupt for raster compare
D1 : 1= Enable interrupt for sprite overflow
D0 : 1= Enable interrupt for sprite #0 collision
Bits 7 and 6 enable and disable the background and sprite layers, and
can be changed at any time. Games use these to clip sprites within a region
of the display, or to give a letterboxed effect to the background layer.
Within a single frame, the overscan area outside of the active display
period is filled with color #0 from sprite palette #0, and the active
display area is filled with color #0 from background palette #0.
So even if only the sprites were enabled, they would still be drawn over
color #0 from background palette #0.
If bits 7 and 6 are cleared by the time the next frame starts (1), they are
locked for the duration of the frame. Changes to them will not be taken
into effect until the next frame. During this time, every line in the
active display area is filled with color #0 of sprite palette #0.
The VDC patent refers to this as "BURST" mode, where the VDC does not
read VRAM for background and sprite rendering. The CPU has unlimited
access to VRAM, and in addition VRAM to VRAM DMA can be done during
this time. Simply clearing both bits during the active display area does
not cause BURST mode to become enabled, it only happens as soon as the
active display period ends (which is incidentally when VRAM to SAT DMA
occurs), and remains effective in the next frame only if both bits remain
reset before the next frame starts.
1. I'm not sure if they are locked when the next frame starts, or when the
next active display period starts. I'm assuming the former for now.
Bits 3-0 will, when set, allow status flags to be set when a certain
condition occurs. In addition the VDC will generate an IRQ1 interrupt,
though interrupts can always be disabled through the CPUs IRQ control
registers or the P register's I flag.
$06 - Raster Compare Register (RCR)
The value stored in this register is compared to the current scanline.
If there is a match and the raster compare interrupt enable bit in register
$05 is set, then bit 2 of the status flags is set and an interrupt occurs.
The range of the RCR is 263 lines, relative to the start of the active
display period. (defined by VSW, VDS, and VCR) The VDC treats the first
scanline of the active display period as $0040, so the valid ranges for
the RCR register are $0040 to $0146.
For example, assume VSW=$02, VDS=$17. This positions the first line of
the active display period at line 25 of the frame. An RCR value of $0040
(zero) causes an interrupt at line 25, and a value of $0146 (262) causes an
interrupt at line 24 of the next frame.
Any other RCR values that are out of range ($00-$3F, $147-$3FF) will never
result in a successful line compare.
$07 - Background X-Scroll Register (BXR)
D15-D10 : unused
D9-D0 : when the background map is a larger virtual
size than the viewing screen shows, this is
the viewing screen's x-offset (in pixels)
from the origin of the virtual background map.
$08 - Background Y-Scroll Register (BYR)
D15-D9 : unused
D8-D0 : when the background map is a larger virtual
size than the viewing screen shows, this is
the viewing screen's y-offset (in pixels)
from the origin of the virtual background map.
$09 - Memory Width Register (MWR)
D15-D8 : Unused
D7 : CG mode
D6 : Virtual screen height
D5 : Virtual screen width (bit 1)
D4 : Virtual screen width (bit 0)
D3 : Sprite dot period (bit 1)
D2 : Sprite dot period (bit 0)
D1 : VRAM dot width (bit 1)
D0 : VRAM dot width (bit 0)
The VDC was designed to work with slower video memory. The TurboGrafx-16
happens to use the fastest kind available, but you can still set up the VDC
to handle VRAM as if it was slower.
During the active display period of a scanline, the VDC can do one 16-bit
access to VRAM on each cycle of the dot clock. Bits 1-0 of MWR tell the VDC
how to divide this amongst several sources:
1. CPU (reading or writing a word via register $02)
2. Background character pattern generator data (one read is for bitplanes
0 and 1, another is for bitplanes 2 and 3, either one or two are needed
per character)
3. BAT data (character name and palette, one fetch needed per character)
Bit Dot Dot cycles within an 8-dot unit
1 0 Width 0 1 2 3 4 5 6 7
-------------------------------------------
0 0 1 CPU BAT CPU ??? CPU CG0 CPU CG1
0 1 2 --BAT-- --CPU-- --CG0-- --CG1--
1 0 2 --BAT-- --CPU-- --CG0-- --CG1--
1 1 4 ------BAT------ ----CG0/CG1----
CPU - A read or write to register $02
BAT - The palette block and character name from the BAT
??? - Unknown, possibly an unused 'dummy' access
CG0 - Bitplanes 0, 1 from the character generator
CG1 - Bitplanes 2, 3 from the character generator
The default mode all games use is 0, as far as I can tell, modes 1, 2 are
identical, and mode 3 enables the CG mode bit as described later.
Bits 5-4 select the width of the virtual screen:
00 - 32 characters
01 - 64 characters
10 - 128 characters
11 - 128 characters
Bit 6 selects the height of the virtual screen:
0 - 32 rows
1 - 64 rows
There are no limits on the size of the BAT, at it's largest setting of
128x64 characters, the BAT is 16K.
Bit 7 selects which character generator bitplanes are read by the VDC
when the VRAM dot width is 3.
0 - Read bitplanes 0, 1, treat 2, 3 as zero
1 - Read bitplanes 2, 3, treat 0, 1 as zero
In either setting, background characters can only display four colors
out of 16 at any given time.
----------------------------------------------------------------------------
5.2) Background
----------------------------------------------------------------------------
The background generated by the VDC is a tiled layer composed of 8x8
characters. The background can be scrolled horizontally and vertically, and
it's size is definable in units of 32 characters in either direction,
from 32x32 up to 128x64.
The pattern data used by the characters is stored in a planar format.
Because the VDC always accesses VRAM in word units, the organization
of bitplanes reflect this. It takes 32 bytes of VRAM to define one tile;
the first eight words are bitplanes 0 and 1 for lines 0-7, and the next
eight words are bitplanes 0 and 1 for lines 0-7.
The background itself is defined by the block attribute table (BAT), which
starts at address zero in VRAM. Each word-wide entry in the BAT defines
a single character, and has the following layout:
MSB LSB
ppppnnnnnnnnnnnn
p : Color palette (0-15)
n : Character name (0-4095)
Notice that there are no provisions for tile flipping or priority control.
Because the TurboGrafx-16 only has 64K of VRAM, only patterns 0-2047 should
be used. Patterns 2048-4096 are filled with 'garbage' data.
The color palette selects one of sixteen 16-color palettes for the
character to use. The background layer always uses the first 256 colors
in the 512-color VCE palette.
The BAT doesn't necessarily have to match the same size of the display.
If the BAT is too small, (e.g. it's 32x32 and the display is 40x28), then
the offset into the BAT wraps around and the graphics are repeated. In
the same vein, you don't have to use up the entire BAT space if the display
won't show all of it (e.g. it's 64x32 and the display is 32x16, you
wouldn't need to define entries for rows 17-31).
For more information about scrolling, see registers $07 and $08 in
the register reference section.
----------------------------------------------------------------------------
5.3) Sprites
----------------------------------------------------------------------------
The VDC can control 64 sprites up to 32x64 in size, composed of 16x16
patterns.
Each sprite is defined by a four-word entry in the sprite attribute
table, (SAT) a memory area internal to the VDC that can only be written to
via VRAM to SAT DMA.
On power-up, the SAT is filled with garbage data and should either be
initialized or sprites should be turned off. See the register reference
section for more details about VRAM to SAT DMA.
Each SAT entry has the following format:
Word 0 : ------aaaaaaaaaa
Word 1 : ------bbbbbbbbbb
Word 2 : -----ccccccccccd
Word 3 : e-ffg--hi---jjjj
a = Sprite Y position (0-1023)
b = Sprite X position (0-1023)
c = Pattern index (0-1023)
d = CG mode bit (0= Read bitplanes 0/1, 1= Read bitplanes 2/3)
e = Vertical flip flag
f = Sprite height (CGY) (0=16 pixels, 1=32, 2/3=64)
g = Horizontal flip flag
h = Sprite width (CGX) (0=16 pixels, 1=32)
i = Sprite priority flag (1= high priority, 0= low priority)
j = Sprite palette (0-15)
Sprites are positioned in a virtual 1024x1024 space. The active display
area starts at offset (32, 64), allowing sprites to be partially shown
at the left and top edges, as well as giving sprites a place to be
hidden at when their coordinates are set to (0, 0).
The pattern index selects one of 1024 patterns, however the TurboGrafx-16
only has 64K of VRAM, so the first 512 should be used. Patterns 512-1023
are filled with 'garbage' data.
Each sprite pattern takes 128 bytes, and is arranged in four groups of
16 words. Each word corresponds to one 16-pixel line, and each group
corresponds to one bitplane. For example, words 0-15 define bitplane 0,
words 16-31 define bitplane 1, etc.
The CG mode bit is only valid when the sprite dot width field of the MWR
register is set to 2 or 3. When clear, bitplanes 0 and 1 are read, 2 and 3
are treated as zero. When set, bitplanes 2 and 3 are read, 0 and 1 are
treated as zero.
The vertical and horziontal flip flags flip an entire sprite (not just
one 16x16 pattern).
The CGX and CGY fields define the size of a sprite. Sprites larger than
16x16 use neighboring patterns to make up the rest of the sprite. Depending
on the size, the lower 3 bits of the pattern index are masked out. If CGX
is set, bit 0 of the pattern index is forced to zero. If CGY is 1, bit 1 of
the pattern index is forced to zero. If CGY is 2 or 3, bits 2 and 1 of the
pattern index are forced to zero. For example, a 16x16 sprite can use any
patterns, a 32x16 sprite can use every second pattern, and a 32x64 sprite
can only use every eighth pattern.
The priority and palette fields are discussed later on.
Sprite attribute table parsing:
During the horizontal blanking period of each scanline, the VDC parses the
SAT to collect information about what sprites will be displayed on the
next line. It progresses through the SAT one sprite at a time, working from
sprite #0 to #63. If a sprite is found that has the right Y coordinate
and height to make it fall on the next line, the sprite is added to an
internal 16-entry buffer. The VDC continues to parse the SAT until the
following conditions occur:
- All 64 sprite entries have been examined.
- All 16 buffer entries have been used.