2007-08-11 09:55:23 +02:00
|
|
|
|
|
|
|
Sega Genesis VDP documentation
|
|
|
|
Version 1.5f (08/10/00)
|
|
|
|
|
|
|
|
by Charles MacDonald
|
|
|
|
WWW: http://cgfm2.emuviews.com
|
|
|
|
|
|
|
|
Unpublished work Copyright 2000 Charles MacDonald
|
|
|
|
|
|
|
|
|
|
|
|
This document is very preliminary and subject to change.
|
|
|
|
|
|
|
|
|
|
|
|
Changes from the last version:
|
|
|
|
|
|
|
|
(v1.5f)
|
|
|
|
- Minor update on 68k -> VDP DMA transfers. (section 11)
|
|
|
|
(v1.5e)
|
|
|
|
- More information on VDP register sets.
|
|
|
|
- Added preliminary section numbers and table of contents.
|
|
|
|
- Revised todo list.
|
|
|
|
(v1.5d)
|
|
|
|
- Fixed sprite size byte layout.
|
|
|
|
- Listed a few games that overlap low and high priority sprites.
|
|
|
|
- Mentioned game problems regarding data port access.
|
|
|
|
(v1.5c)
|
|
|
|
- Verified DMA transfer wrapping.
|
|
|
|
- Fixed MSB/LSB mixup in DMA fill description.
|
|
|
|
- Added zero length DMA behavior.
|
|
|
|
- Mentioned results of writing during fills.
|
|
|
|
- Added VRAM address register wrapping.
|
|
|
|
(v1.5)
|
|
|
|
- Added shadow / hilight info.
|
|
|
|
- Added HV counter information.
|
|
|
|
- Added notes on odd byte access.
|
|
|
|
- Expanded register list.
|
|
|
|
- Rewrote DMA section.
|
|
|
|
- Did a little work on priority and patterns.
|
|
|
|
(v1.4)
|
|
|
|
- Described 8-bit port writes.
|
|
|
|
- Expanded VDP register programming section.
|
|
|
|
- Rewrote description of external interrupts.
|
|
|
|
- Removed 8-bit VRAM fill section.
|
|
|
|
- Added VDP pinout.
|
|
|
|
- Changed introduction and overview sections.
|
|
|
|
- Removed display size list.
|
|
|
|
- Added interrupts description.
|
|
|
|
(v1.3)
|
|
|
|
- Fixed sprite limitation count.
|
|
|
|
- Added details on blanking flags in status register.
|
|
|
|
- Added status register section.
|
|
|
|
- Added notes on VSRAM/CRAM fills.
|
|
|
|
- Added scrolling section.
|
|
|
|
- Added description of backdrop color register.
|
|
|
|
- Added description of mode set #3 register.
|
|
|
|
(v1.2)
|
|
|
|
- Completed most of the DMA section.
|
|
|
|
- Added sprite masking information.
|
|
|
|
- Verified bits 5-0 of reg 23 have no effect on VRAM fills.
|
|
|
|
- Fixed CD4 description.
|
|
|
|
(v1.1)
|
|
|
|
- Verified CD4 does not affect a 68K->VDP DMA transfer.
|
|
|
|
- Verified code and address registers retain their state after half of
|
|
|
|
a command word is written to the control port.
|
|
|
|
- Added display mode list.
|
|
|
|
- Added specific VDP RAM type info.
|
|
|
|
- Added sprite section.
|
|
|
|
- Verified register writes reset the pending flag.
|
|
|
|
- Added VDP port map.
|
|
|
|
- Verified byteswap behavior for VRAM writes.
|
|
|
|
- Verified no illegal codes allowed for CRAM writes.
|
|
|
|
- Verified A15-A7 of VSRAM address is ignored.
|
|
|
|
- Verified A0 of CRAM address is ignored.
|
|
|
|
- Verified A0 of VSRAM address is ignored.
|
|
|
|
- Verified writing to 'extra' VSRAM addresses has no effect.
|
|
|
|
- Verified ignored command word bits.
|
|
|
|
- Finalized info on palette control bit.
|
|
|
|
- Started basic control port decoding section.
|
|
|
|
- Added register #0, #1 descriptions.
|
|
|
|
- Mentioned plane A clipping in window register descriptions.
|
|
|
|
(v1.0)
|
|
|
|
- Initial draft.
|
|
|
|
|
|
|
|
Disclaimer:
|
|
|
|
|
|
|
|
If you use any information from this document, please credit me
|
|
|
|
(Charles MacDonald) and optionally provide a link to my webpage
|
|
|
|
(http://cgfm2.emuviews.com/) so interested parties can access it.
|
|
|
|
|
|
|
|
The credit text should be present in the accompanying documentation of
|
|
|
|
whatever project which used the information, or even in the program
|
|
|
|
itself (e.g. an about box)
|
|
|
|
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
Table of Contents
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
0.) Introduction
|
|
|
|
1.) Overview
|
|
|
|
2.) Display Modes
|
|
|
|
3.) VDP port map
|
|
|
|
4.) Interrupts
|
|
|
|
5.) HV Counter
|
|
|
|
6.) Status Register
|
|
|
|
7.) VDP ports
|
|
|
|
8.) VRAM
|
|
|
|
9.) CRAM
|
|
|
|
10.) VSRAM
|
|
|
|
11.) DMA
|
|
|
|
12.) Patterns
|
|
|
|
13.) Background Layers
|
|
|
|
14.) Priority
|
|
|
|
15.) Sprites
|
|
|
|
16.) Shadow / Hilight mode
|
|
|
|
17.) VDP registers
|
|
|
|
18.) VDP Pinout
|
|
|
|
|
|
|
|
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
0.) Introduction
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
This is a compilation of my notes on the Sega Genesis video display
|
|
|
|
processor.
|
|
|
|
|
|
|
|
I wanted to write this because the existing information on the VDP is
|
|
|
|
very inadequate. Many of the subtle quirks and bugs are not explained.
|
|
|
|
|
|
|
|
I'd like to thank the following people in alphabetical order for
|
|
|
|
providing information and being helpful:
|
|
|
|
|
|
|
|
Bart Trzynadlowski
|
|
|
|
Christian Schiller
|
|
|
|
Flavio Morsoletto
|
|
|
|
Omar Cornut
|
|
|
|
Stephane Dallongeville
|
|
|
|
|
|
|
|
Also thanks to Sardu and Steve Snake for genecyst and KGen98, which
|
|
|
|
were both used for devloping test programs.
|
|
|
|
|
|
|
|
I am interested in finding somebody who has a Sega Genesis copier and
|
|
|
|
would be willing to run some test programs and report the results.
|
|
|
|
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
1.) Overview
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
The Genesis VDP is derived from the Master System VDP, which in turn
|
|
|
|
was derived from the Texas Instruments TMS9918. As a result, the VDP is
|
|
|
|
programmed much like it's earlier counterparts.
|
|
|
|
|
|
|
|
Interestingly enough, none of the Sega produced VDP's are similar to the
|
|
|
|
later VDP models made by Yamaha, which were also based upon the TMS9918.
|
|
|
|
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
2.) Display Modes
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
To clarify naming conventions, here is a list of display modes used by
|
|
|
|
the various video chips mentioned:
|
|
|
|
|
|
|
|
Mode 0 - TMS9918 specific
|
|
|
|
Mode 1 - TMS9918 specific
|
|
|
|
Mode 2 - TMS9918 specific
|
|
|
|
Mode 3 - TMS9918 specific
|
|
|
|
Mode 4 - SMS mode
|
|
|
|
Mode 5 - Genesis mode
|
|
|
|
|
|
|
|
Supported mode list:
|
|
|
|
|
|
|
|
TMS9918 - Modes 0, 1, 2, 3
|
|
|
|
SMS - Modes 0, 1, 2, 3, 4
|
|
|
|
Genesis - Modes 4, 5 (possibly 0 and others as well)
|
|
|
|
|
|
|
|
If anybody has some information about how the Game Gear would fit into
|
|
|
|
this list, let me know. I assume it's identical to the SMS, but I don't
|
|
|
|
know about the TMS9918 compatability. (if any)
|
|
|
|
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
3.) VDP port map
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
The VDP occupies addresses C00000h to C0001Fh.
|
|
|
|
|
|
|
|
C00000h - Data port (8=r/w, 16=r/w)
|
|
|
|
C00002h - Data port (mirror)
|
|
|
|
C00004h - Control port (8=r/w, 16=r/w)
|
|
|
|
C00006h - Control port (mirror)
|
|
|
|
C00008h - HV counter (8/16=r/o)
|
|
|
|
C0000Ah - HV counter (mirror)
|
|
|
|
C0000Ch - HV counter (mirror)
|
|
|
|
C0000Eh - HV counter (mirror)
|
|
|
|
C00011h - SN76489 PSG (8=w/o)
|
|
|
|
C00013h - SN76489 PSG (mirror)
|
|
|
|
C00015h - SN76489 PSG (mirror)
|
|
|
|
C00017h - SN76489 PSG (mirror)
|
|
|
|
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
4.) Interrupts
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
The VDP generates all interrupts for the 68000. The only hardware
|
|
|
|
interrupts available are 2, 4, and 6.
|
|
|
|
|
|
|
|
Vertical interrupts
|
|
|
|
-------------------
|
|
|
|
|
|
|
|
If bit 5 (IE0) of register #1 is set, then a level 6 interrupt will occur
|
|
|
|
when the V counter is at line E0h, roughly at H counter cycle 08h. At this
|
|
|
|
point in time, the vertical interrupt occurance flag (bit 7) will be set
|
|
|
|
in the status register.
|
|
|
|
|
|
|
|
Line interrupts
|
|
|
|
---------------
|
|
|
|
|
|
|
|
The VDP has a counter that is decremented on every line. When the counter
|
|
|
|
has expired, and if bit 4 (IE1) of register #0 is set, then a level 4
|
|
|
|
interrupt will occur.
|
|
|
|
|
|
|
|
The counter is loaded with the contents of register #10 in the following
|
|
|
|
situations:
|
|
|
|
|
|
|
|
- Line zero of the frame.
|
|
|
|
- When the counter has expired.
|
|
|
|
- Lines 225 through 261. (note that line 224 is not included)
|
|
|
|
|
|
|
|
The counter is *not* loaded when register #10 is written to.
|
|
|
|
|
|
|
|
Note that line interrupts are processed *before* vertical interrupts
|
|
|
|
within a scanline. If you trigger a line interrupt to occur on line E0h,
|
|
|
|
then the level 4 line interrupt will be taken by the 68000, not the
|
|
|
|
level 6 vertical interrupt.
|
|
|
|
|
|
|
|
External interrupts
|
|
|
|
-------------------
|
|
|
|
|
|
|
|
Pin 7 of each of the three I/O ports is called the TH pin. It is connected
|
|
|
|
to the VDP. When the state of TH is changed, the following events happen:
|
|
|
|
|
|
|
|
- If bit 7 of the control register for the associated I/O port is set,
|
|
|
|
and bit 3 of register #11 (IE2) is set, then the VDP will generate
|
|
|
|
a level 2 interrupt.
|
|
|
|
|
|
|
|
- If bit 1 of VDP register #0 is set, the contents of the HV counter will
|
|
|
|
be latched.
|
|
|
|
|
|
|
|
A common use for these features are in a light gun game. The light gun
|
|
|
|
can change the state of TH, which will freeze the HV counter, and then
|
|
|
|
the level 2 interrupt subroutine can read the HV counter and also
|
|
|
|
communicate with the light gun to get information like what buttons
|
|
|
|
were pressed. This is pretty much how Sega's 'Menacer' device works.
|
|
|
|
The Konami 'Justifier' light gun works on similar principles.
|
|
|
|
|
|
|
|
I don't know if the HV counter resumes normal operation after the first
|
|
|
|
read, or if the HV counter latch bit has to be cleared and set again.
|
|
|
|
|
|
|
|
Some games will enable the HV counter latch and never read the HV counter,
|
|
|
|
let alone use any special peripherals. (Shadow of the Beast II)
|
|
|
|
|
|
|
|
You cannot force an external interrupt by changing the state of TH
|
|
|
|
through software. (e.g. in the normal sequence of reading the gamepad,
|
|
|
|
TH is set and cleared - this does not cause an interrupt if interrupts
|
|
|
|
are enabled)
|
|
|
|
|
|
|
|
The data direction for TH (which corresponds to bit 6 of the data and
|
|
|
|
control registers) must be configured as an input for a peripheral
|
|
|
|
to cause an interrupt.
|
|
|
|
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
5.) HV Counter
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
The HV counter returns the vertical and horizontal position of the
|
|
|
|
television's raster beam.
|
|
|
|
|
|
|
|
Reading the HV counter will return the following data:
|
|
|
|
|
|
|
|
VC7 VC6 VC5 VC4 VC3 VC2 VC1 VC0 (D15-D08)
|
|
|
|
HC8 HC7 HC6 HC5 HC4 HC3 HC2 HC1 (D07-D00)
|
|
|
|
|
|
|
|
VCx = Vertical position in lines.
|
|
|
|
HCx = Horizontal position in pixels.
|
|
|
|
|
|
|
|
According to the manual, VC0 is replaced with VC8 when in interlace mode 2.
|
|
|
|
|
|
|
|
For 8-bit reads, the even byte (e.g. C00008h) returns the V counter, and
|
|
|
|
the odd byte (e.g. C00009h) returns the H counter.
|
|
|
|
|
|
|
|
The V counter counts up from 00h to EAh, then it jumps back to E5h and
|
|
|
|
continues counting up to FFh. This allows it to cover the entire 262 line
|
|
|
|
display.
|
|
|
|
|
|
|
|
The H counter counts up from 00h to E9h, then it jumps back to 93h and
|
|
|
|
continues counting up to FFh. This allows it to cover an entire 342 pixel
|
|
|
|
line.
|
|
|
|
|
|
|
|
The H counter description is based upon known information about the SMS
|
|
|
|
VDP's H counter. The SMS has a 256x192 display, and each line consists of
|
|
|
|
342 pixels. (that includes blanking, retrace, etc.) The description
|
|
|
|
*could* be accurate for the Genesis' 32-cell display mode. This is not
|
|
|
|
the case for the 40-cell display, where I would assume the H counter jumps
|
|
|
|
back farther at a later point in the count-up.
|
|
|
|
|
|
|
|
In terms of emulation, I have found that turning the elapsed 68000 cycle
|
|
|
|
count into a pixel offset for the current scan line seems to provide a
|
|
|
|
reasonable return value for the H counter. However, I am quite positive
|
|
|
|
a single scanline consists of more pixels than just 256 or 320, and
|
|
|
|
in addition the 'jump-back' described above is not taken into account.
|
|
|
|
In all honesty this is a hack, not a solution.
|
|
|
|
|
|
|
|
I would appreciate any additional information on the HV counter.
|
|
|
|
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
6.) Status Register
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
Reading the control port returns a 16-bit word that allows you to observe
|
|
|
|
various states of the VDP and physical display.
|
|
|
|
|
|
|
|
d15 - Always 0
|
|
|
|
d14 - Always 0
|
|
|
|
d13 - Always 1
|
|
|
|
d12 - Always 1
|
|
|
|
d11 - Always 0
|
|
|
|
d10 - Always 1
|
|
|
|
d9 - FIFO Empty
|
|
|
|
d8 - FIFO Full
|
|
|
|
d7 - Vertical interrupt pending
|
|
|
|
d6 - Sprite overflow on current scan line
|
|
|
|
d5 - Sprite collision
|
|
|
|
d4 - Odd frame
|
|
|
|
d3 - Vertical blanking
|
|
|
|
d2 - Horizontal blanking
|
|
|
|
d1 - DMA in progress
|
|
|
|
d0 - PAL mode flag
|
|
|
|
|
|
|
|
Presumably bit 0 is set when the system display is PAL; however this same
|
|
|
|
information can be read from the version register (part of the I/O
|
|
|
|
register group - not the VDP), so maybe this bit reflects the state of
|
|
|
|
having a 240-line display enabled.
|
|
|
|
|
|
|
|
Bit 1 is set for the duration of a DMA operation. This is only useful for
|
|
|
|
fills and copies, since the 68000 is frozen during 68k -> VDP transfers.
|
|
|
|
|
|
|
|
Bit 2 returns the real-time status of the horizontal blanking signal.
|
|
|
|
It is set at H counter cycle E4h and cleared at H counter cycle 08h.
|
|
|
|
|
|
|
|
Bit 3 returns the real-time status of the vertical blanking signal.
|
|
|
|
It is set on line E0h, at H counter cycle AAh, and cleared on line FFh,
|
|
|
|
at H counter cycle AAh.
|
|
|
|
|
|
|
|
(Note: For both blanking flag descriptions, the H counter values are
|
|
|
|
very likely different for 32-cell and interlaced displays; they were
|
|
|
|
taken from a test with a 40-cell screen.)
|
|
|
|
|
|
|
|
Bit 4 is set when the display is interlaced, and in the odd frame,
|
|
|
|
otherwise it is cleared in the even frame. This applies to both
|
|
|
|
interlace modes.
|
|
|
|
|
|
|
|
Bit 5 is set when any sprites have non-transparent pixels overlapping one
|
|
|
|
another. This may hold true for sprites outside of the display area too.
|
|
|
|
This bit is most likely cleared at the end of the frame.
|
|
|
|
|
|
|
|
Bit 6 is set when too many sprites are on the current scan line, meaning
|
|
|
|
when the VDP parses the 21st sprite in 40-cell mode or the 17th sprite in
|
|
|
|
32-cell mode from the sprite list.
|
|
|
|
This bit is most likely cleared at the end of the frame.
|
|
|
|
|
|
|
|
Bit 7 is set when a vertical interrupt occurs. This happens at line E0h,
|
|
|
|
roughly after H counter cycle 08h. I do not know under what conditions
|
|
|
|
it is cleared, presumably at the end of the frame. Reading the control
|
|
|
|
port does not clear this bit. (this could be incorrect)
|
|
|
|
|
|
|
|
Bit 8 and 9 (FULL and EMPTY flags, respectively) are related to the FIFO;
|
|
|
|
here's what Flavio Morsoletto has to say about their use:
|
|
|
|
|
|
|
|
"The FIFO can hold up to four 16-bit words while the VDP's busy
|
|
|
|
parsing data from VRAM. Once the 68K has written the fourth word,
|
|
|
|
FULL is raised. If the processor attempts to write one more time, it
|
|
|
|
will be frozen (/DTACK high) until the FIFO unit manages to deliver
|
|
|
|
the first stacked word to its rightful owner. EMPTY only goes 1 when
|
|
|
|
there is nothing on the stack. The intermediate state is signaled by
|
|
|
|
both of them showing 0."
|
|
|
|
|
|
|
|
This situation only occurs during the active display period, as the
|
|
|
|
data port can be written to as many times as needed during blanking.
|
|
|
|
|
|
|
|
I've noticed most emulators keep the EMPTY flag set, so it appears as
|
|
|
|
if the FIFO was always empty instead of being in the neutral state. This
|
|
|
|
is probably for games that would normally check and find the FIFO in a
|
|
|
|
neutral state, then write data and expect to poll the FULL flag afterwards.
|
|
|
|
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
7.) VDP ports
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
The VDP is programmed entirely through the control and data ports.
|
|
|
|
Data written to the control port is formatted, so the VDP will know
|
|
|
|
how to interpret the data it recieves.
|
|
|
|
|
|
|
|
You can divide control port data into two categories; 16-bit register sets
|
|
|
|
and 32-bit command words.
|
|
|
|
|
|
|
|
Programming VDP registers
|
|
|
|
-------------------------
|
|
|
|
|
|
|
|
Any one of the 23 VDP registers can be programmed by writing 16 bits of
|
|
|
|
data to the control port. The data written has the following format:
|
|
|
|
|
|
|
|
1 0 ? R04 R03 R02 R01 R00 (D15-D8)
|
|
|
|
D07 D06 D05 D04 D03 D02 D01 D00 (D7-D0)
|
|
|
|
|
|
|
|
Rxx = VDP register select (00-1F)
|
|
|
|
Dxx = VDP register data (00-FF)
|
|
|
|
|
|
|
|
Writing to non-existant VDP registers has no effect.
|
|
|
|
|
|
|
|
Bits 15 and 14 must be set to 1 and 0 respectively, otherwise the write
|
|
|
|
will be treated as the first half of a command word. The state of bit 13
|
|
|
|
does not matter.
|
|
|
|
|
|
|
|
Here's an example of programming one register:
|
|
|
|
|
|
|
|
; Set border color to palette $3, index $F
|
|
|
|
move.w #$873F, $00C00004
|
|
|
|
|
|
|
|
Since the 68000 treats 32-bit memory access as two 16-bit operations,
|
|
|
|
you can program two registers at once:
|
|
|
|
|
|
|
|
; Set split point bits for both window registers
|
|
|
|
move.w #$91809280, $00C00004
|
|
|
|
|
|
|
|
Accessing VDP RAM
|
|
|
|
-----------------
|
|
|
|
|
|
|
|
You can access VRAM, CRAM, or VSRAM by writing a 32-bit command word to
|
|
|
|
the control port. The data written has the following format:
|
|
|
|
|
|
|
|
CD1 CD0 A13 A12 A11 A10 A09 A08 (D31-D24)
|
|
|
|
A07 A06 A05 A04 A03 A02 A01 A00 (D23-D16)
|
|
|
|
? ? ? ? ? ? ? ? (D15-D8)
|
|
|
|
CD5 CD4 CD3 CD2 ? ? A15 A14 (D7-D0)
|
|
|
|
|
|
|
|
CDx = VDP code (0-3F)
|
|
|
|
Axx = VDP address (00-FFFF)
|
|
|
|
|
|
|
|
The state of D15 through D8, D3, and D2 are ignored.
|
|
|
|
|
|
|
|
The VDP has an address and code register. They are used in conjunction to
|
|
|
|
handle data port accesses. The address register provides an offset into
|
|
|
|
VDP RAM to write or read data from. The code register specifies if
|
|
|
|
data port accesses will be reads or writes, and the kind of VDP RAM
|
|
|
|
to perform these operations on.
|
|
|
|
|
|
|
|
In order for the VDP to know if the first or second 16-bit half of the
|
|
|
|
command word has been written to the control port, it maintains an internal
|
|
|
|
write-pending flag. This flag is updated when these conditions are met:
|
|
|
|
|
|
|
|
- It is set when the first half of the command word is written.
|
|
|
|
- It is cleared when the second half of the command word is written.
|
|
|
|
- It is cleared when the data port is written to or read from.
|
|
|
|
- It is cleared when the control port is read.
|
|
|
|
|
|
|
|
It is perfectly valid to write the first half of the command word only.
|
|
|
|
In this case, _only_ A13-A00 and CD1-CD0 are updated to reflect the new
|
|
|
|
values, while the remaining address and code bits _retain_ their former
|
|
|
|
value.
|
|
|
|
|
|
|
|
You cannot write to a VDP register if the pending flag is set to one,
|
|
|
|
since the VDP is expecting the 2nd half of a command word.
|
|
|
|
|
|
|
|
Writing to a VDP register will clear the code register. Games that rely
|
|
|
|
on this are Golden Axe II (will display missing SEGA logo) and Sonic 3D.
|
|
|
|
(will show intro movie in wrong colors for a few frames) It is not known
|
|
|
|
if the address register is cleared as well, but the TMS9918 manual
|
|
|
|
indicates that this is so, perhaps it applies to the Genesis as well.
|
|
|
|
|
|
|
|
Here is a table of code register settings:
|
|
|
|
|
|
|
|
Bits CD3-CD0
|
|
|
|
0000b : VRAM read
|
|
|
|
0001b : VRAM write
|
|
|
|
0011b : CRAM write
|
|
|
|
0100b : VSRAM read
|
|
|
|
0101b : VSRAM write
|
|
|
|
1000b : CRAM read
|
|
|
|
|
|
|
|
You cannot write data after setting up a read operation, or read data
|
|
|
|
after setting up a write operation. The write or read is ignored.
|
|
|
|
|
|
|
|
CD4 is only set for the the VRAM copy DMA mode.
|
|
|
|
For data port accesses and 68k to VDP DMA, the state of CD4 is ignored.
|
|
|
|
|
|
|
|
Setting CD5 will trigger a DMA operation.
|
|
|
|
|
|
|
|
8-bit port access
|
|
|
|
-----------------
|
|
|
|
|
|
|
|
Doing an 8-bit write to the control or data port is interpreted by
|
|
|
|
the VDP as a 16-bit word, with the data written used for both halfs
|
|
|
|
of the word.
|
|
|
|
|
|
|
|
For instance, the following code writes 87h to VDP register #7:
|
|
|
|
|
|
|
|
; VDP sees data as 8787h
|
|
|
|
move.b #$87, $00C00004
|
|
|
|
|
|
|
|
This also applies to the data port. The following code sets CRAM entry
|
|
|
|
zero to pink:
|
|
|
|
|
|
|
|
; VDP sees data as 0E0Eh
|
|
|
|
move.l #$C0000000, $00C00004
|
|
|
|
move.b #$0E, $00C00000
|
|
|
|
|
|
|
|
It's important to realize that a distinction between 8-bit and 16-bit VRAM
|
|
|
|
fills do not exist. There is only one type of fill, and depending on how
|
|
|
|
you write to the data port will determine the kind of data used in the fill.
|
|
|
|
|
|
|
|
Odd addresses (e.g. C00007h, C00001h) function identically to even addresses
|
|
|
|
in the case of 8-bit writes. For instance, writing data to anywhere within
|
|
|
|
C00004h-C00007h would go to the control port.
|
|
|
|
|
|
|
|
Miscellaneous
|
|
|
|
-------------
|
|
|
|
|
|
|
|
I've found that some games will write VDP register data to the data port,
|
|
|
|
after the code and address registers have been set to zero. I've confirmed
|
|
|
|
that this is simply a bug on the programmer's behalf, you cannot program
|
|
|
|
the VDP registers in this way.
|
|
|
|
|
|
|
|
Games that do this include Alien Soldier and Eternal Champions.
|
|
|
|
|
|
|
|
Some games also set up VRAM reads and try to write to VRAM; this is
|
|
|
|
also pointless as data written after a VRAM read command is issued
|
|
|
|
is ignored by the VDP. The unlicensed version of Populous does
|
|
|
|
this, along with Golden Axe II.
|
|
|
|
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
8.) VRAM
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
The VDP is connected to 64K of video RAM. It is accessed as 65535 8-bit
|
|
|
|
bytes or 32768 16-bit words through the data port.
|
|
|
|
|
|
|
|
VRAM is multipurpose; it can store pattern data, horizontal scroll data,
|
|
|
|
sprite tables, and background name tables.
|
|
|
|
|
|
|
|
When writing 16-bit data to VRAM, having address bit 0 set will swap
|
|
|
|
the upper and lower bytes of the data written.
|
|
|
|
|
|
|
|
The address register wraps past address FFFFh.
|
|
|
|
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
9.) CRAM
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
The VDP has 64x9 bits of on-chip color RAM. It is accessed as 64 16-bit
|
|
|
|
words through the data port. Each word has the following format:
|
|
|
|
|
|
|
|
----bbb-ggg-rrr-
|
|
|
|
|
|
|
|
r = Red component (0-7)
|
|
|
|
g = Green component (0-7)
|
|
|
|
b = Blue component (0-7)
|
|
|
|
|
|
|
|
This allows for a total of 512 possible colors, with 64 colors stored
|
|
|
|
in CRAM at any given time.
|
|
|
|
|
|
|
|
When accessing CRAM, only address bits 6 through 1 are valid. The high-order
|
|
|
|
address bits are ignored. Since CRAM is word-wide, address bit zero has
|
|
|
|
no effect.
|
|
|
|
|
|
|
|
The address register wraps past address 7Fh.
|
|
|
|
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
10.) VSRAM
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
The VDP has 40x10 bits of on-chip vertical scroll RAM. It is accessed as
|
|
|
|
40 16-bit words through the data port. Each word has the following format:
|
|
|
|
|
|
|
|
------yyyyyyyyyy
|
|
|
|
|
|
|
|
y = Vertical scroll factor (0-3FFh)
|
|
|
|
|
|
|
|
When accessing VSRAM, only address bits 6 through 1 are valid.
|
|
|
|
The high-order address bits are ignored. Since VSRAM is word-wide, address
|
|
|
|
bit zero has no effect.
|
|
|
|
|
|
|
|
Even though there are 40 words of VSRAM, the address register will wrap
|
|
|
|
when it passes 7Fh. Writes to the addresses beyond 50h are ignored.
|
|
|
|
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
11.) DMA
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
The VDP can be programmed to move data into, copy, and fill sections of
|
|
|
|
VDP RAM, meaning VRAM, CRAM, and VSRAM. These functions are referred to
|
|
|
|
as Direct Memory Access. (DMA)
|
|
|
|
|
|
|
|
Overview
|
|
|
|
--------
|
|
|
|
|
|
|
|
Bits 7 and 6 of register #23 select the type of DMA operation:
|
|
|
|
|
|
|
|
D7 D6
|
|
|
|
0 ? : 68K -> VDP RAM transfer (D6 is bit 24 of source address)
|
|
|
|
1 0 : VRAM fill
|
|
|
|
1 1 : VRAM copy
|
|
|
|
|
|
|
|
Bit 4 of register #1 will enable DMA operations when set.
|
|
|
|
|
|
|
|
Some games will attempt to do DMA when it is disabled, including Phelios
|
|
|
|
and Rocket Knight Adventures.
|
|
|
|
|
|
|
|
When doing 68K -> VDP RAM transfers, the 68000 is frozen. For VRAM fills
|
|
|
|
and copies, the 68000 runs normally, but you can only read the control
|
|
|
|
port, HV counter, and write to the PSG register.
|
|
|
|
|
|
|
|
Writing to the control or data port during a VRAM fill seems to corrupt
|
|
|
|
the VDP registers and VRAM.
|
|
|
|
|
|
|
|
When the length field is set to zero, the length is treated as FFFFh.
|
|
|
|
|
|
|
|
68000 to VDP RAM
|
|
|
|
----------------
|
|
|
|
|
|
|
|
This is used to transfer data out of the 68000's address space into VRAM,
|
|
|
|
CRAM, or VSRAM.
|
|
|
|
|
|
|
|
Registers 19, 20, specify how many 16-bit words to transfer:
|
|
|
|
|
|
|
|
#19: L07 L06 L05 L04 L03 L02 L01 L00
|
|
|
|
#20: L15 L14 L13 L12 L11 L10 L08 L08
|
|
|
|
|
|
|
|
Note that a length of 7FFFh equals FFFFh bytes transferred, and a length
|
|
|
|
of FFFFh = 1FFFF bytes transferred.
|
|
|
|
|
|
|
|
Registers 21, 22, 23 specify the source address on the 68000 side:
|
|
|
|
|
|
|
|
#21: S08 S07 S06 S05 S04 S03 S02 S01
|
|
|
|
#22: S16 S15 S14 S13 S12 S11 S10 S09
|
|
|
|
#23: 0 S23 S22 S21 S20 S19 S18 S17
|
|
|
|
|
|
|
|
If the source address goes past FFFFFFh, it wraps to FF0000h.
|
|
|
|
(Actually, it probably wraps at E00000h, but there's no way to tell as
|
|
|
|
the two addresses are functionally equivelant)
|
|
|
|
|
|
|
|
When doing a transfer to CRAM, the operation is aborted once the address
|
|
|
|
register is larger than 7Fh. The only known game that requires this is
|
|
|
|
Batman & Robin, which will have palette corruption in levels 1 and 3
|
|
|
|
otherwise. This rule may possibly apply to VSRAM transfers as well.
|
|
|
|
|
|
|
|
A transfer is started when the following command word is written:
|
|
|
|
|
|
|
|
CD1 CD0 A13 A12 A11 A10 A09 A08 (D31-D24)
|
|
|
|
A07 A06 A05 A04 A03 A02 A01 A00 (D23-D16)
|
|
|
|
? ? ? ? ? ? ? ? (D15-D8)
|
|
|
|
1 0 0 CD2 ? ? A15 A14 (D7-D0)
|
|
|
|
|
|
|
|
CD2-CD0 specify the type of VDP RAM to write to:
|
|
|
|
|
|
|
|
001b - VRAM
|
|
|
|
011b - CRAM
|
|
|
|
101b - VSRAM
|
|
|
|
|
|
|
|
The following events occur after the command word is written:
|
|
|
|
|
|
|
|
- 68000 is frozen.
|
|
|
|
- VDP reads a word from source address.
|
|
|
|
- Source address is incremented by 2.
|
|
|
|
- VDP writes word to VRAM, CRAM, or VSRAM.
|
|
|
|
(For VRAM, the data is byteswapped if the address register has bit 0 set)
|
|
|
|
- Address register is incremented by the value in register #15.
|
|
|
|
- Repeat until length counter has expired.
|
|
|
|
- 68000 resumes operation.
|
|
|
|
|
|
|
|
When a transfer is done out of the ROM area ($000000-3FFFFF), the machine
|
|
|
|
will lock up unless the write that triggers the DMA operation is done
|
|
|
|
using RAM.
|
|
|
|
|
|
|
|
Usually this means putting the command word or the latter half of the
|
|
|
|
command word in RAM and moving that into the control port, putting
|
|
|
|
the command word on the stack and moving that into the control port,
|
|
|
|
or having the instruction that moves the command word into the control
|
|
|
|
port execute out of RAM.
|
|
|
|
|
|
|
|
VRAM fill
|
|
|
|
---------
|
|
|
|
|
|
|
|
VRAM fills are used to repeatedly write a given data value to multiple
|
|
|
|
sequential addresses in VRAM.
|
|
|
|
|
|
|
|
Registers 19, 20, specify how many 8-bit bytes to fill:
|
|
|
|
|
|
|
|
#19: L07 L06 L05 L04 L03 L02 L01 L00
|
|
|
|
#20: L15 L14 L13 L12 L11 L10 L08 L08
|
|
|
|
|
|
|
|
The address bits in registers 21, 22, 23 are ignored:
|
|
|
|
|
|
|
|
#21: ? ? ? ? ? ? ? ?
|
|
|
|
#22: ? ? ? ? ? ? ? ?
|
|
|
|
#23: 1 0 ? ? ? ? ? ?
|
|
|
|
|
|
|
|
A VRAM fill is started when the following command word is written:
|
|
|
|
|
|
|
|
0 1 A13 A12 A11 A10 A09 A08 (D31-D24)
|
|
|
|
A07 A06 A05 A04 A03 A02 A01 A00 (D23-D16)
|
|
|
|
? ? ? ? ? ? ? ? (D15-D8)
|
|
|
|
1 0 0 0 ? ? A15 A14 (D7-D0)
|
|
|
|
|
|
|
|
Any write to the data port will then start a VRAM fill. The LSB of the
|
|
|
|
data is written to the address specified, then the MSB is written to
|
|
|
|
the adjacent address. The address register is incremented by the value
|
|
|
|
in VDP register 15, and the upper 8 bits are written again to the next
|
|
|
|
adjacent address, and so on.
|
|
|
|
|
|
|
|
Here is some "C" pseudocode to illustrate a VRAM fill:
|
|
|
|
|
|
|
|
void vram_fill(int data)
|
|
|
|
{
|
|
|
|
/* Write lower byte to address specified */
|
|
|
|
vram[address] = (data >> 0) & 0xFF;
|
|
|
|
|
|
|
|
do {
|
|
|
|
/* Write upper byte to adjacent address */
|
|
|
|
vram[address ^ 1] = (data >> 8) & 0xFF;
|
|
|
|
|
|
|
|
/* Increment address register */
|
|
|
|
address += vdp_reg[15];
|
|
|
|
} while(--length)
|
|
|
|
}
|
|
|
|
|
|
|
|
Games that require accurate VRAM fill emulation include Thunder Force IV,
|
|
|
|
Contra Hard Corps, Revenge of Shinobi, Taiga Drama, and Sword of Vermillion.
|
|
|
|
|
|
|
|
VRAM copy
|
|
|
|
---------
|
|
|
|
|
|
|
|
VRAM copies are used to copy blocks of VRAM data.
|
|
|
|
|
|
|
|
Registers 19, 20, specify how many 8-bit bytes to copy:
|
|
|
|
|
|
|
|
#19: L07 L06 L05 L04 L03 L02 L01 L00
|
|
|
|
#20: L15 L14 L13 L12 L11 L10 L08 L08
|
|
|
|
|
|
|
|
The address bits in register 23 are ignored.
|
|
|
|
Registers 21, 22 specify the source address in VRAM:
|
|
|
|
|
|
|
|
#21: S07 S06 S05 S04 S03 S02 S01 S00
|
|
|
|
#22: S15 S14 S13 S12 S11 S10 S09 S08
|
|
|
|
#23: 1 1 ? ? ? ? ? ?
|
|
|
|
|
|
|
|
A VRAM copy is started when the following command word is written:
|
|
|
|
|
|
|
|
0 0 A13 A12 A11 A10 A09 A08 (D31-D24)
|
|
|
|
A07 A06 A05 A04 A03 A02 A01 A00 (D23-D16)
|
|
|
|
? ? ? ? ? ? ? ? (D15-D8)
|
|
|
|
1 1 0 0 ? ? A15 A14 (D7-D0)
|
|
|
|
|
|
|
|
The VDP will read a byte from the source address which is then incremented
|
|
|
|
by one. The data will then be written to the destination address, which
|
|
|
|
is incremented by register #15.
|
|
|
|
|
|
|
|
Games that use VRAM copies include Aleste, Bad Omen, and Viewpoint.
|
|
|
|
|
|
|
|
Transfer capacity
|
|
|
|
-----------------
|
|
|
|
|
|
|
|
The VDP can access memory a certain number of times on each line of the
|
|
|
|
display. This is severely limited during the active display period, since
|
|
|
|
the VDP also needs to update the screen, leaving less memory accesses
|
|
|
|
left for DMA.
|
|
|
|
|
|
|
|
According to the manual, here's a table that describes the transfer
|
|
|
|
rates of each of the three DMA types:
|
|
|
|
|
|
|
|
DMA Mode Width Display Transfer Count
|
|
|
|
-----------------------------------------------------
|
|
|
|
68K > VDP 32-cell Active 16
|
|
|
|
Blanking 167
|
|
|
|
40-cell Active 18
|
|
|
|
Blanking 205
|
|
|
|
VRAM Fill 32-cell Active 15
|
|
|
|
Blanking 166
|
|
|
|
40-cell Active 17
|
|
|
|
Blanking 204
|
|
|
|
VRAM Copy 32-cell Active 8
|
|
|
|
Blanking 83
|
|
|
|
40-cell Active 9
|
|
|
|
Blanking 102
|
|
|
|
|
|
|
|
'Active' is the active display period, 'Blanking' is either the vertical
|
|
|
|
blanking period or when the display is forcibly blanked via register #1.
|
|
|
|
|
|
|
|
The above transfer counts are all in bytes, unless the destination is
|
|
|
|
CRAM or VSRAM for a 68K > VDP transfer, in which case it is in words.
|
|
|
|
|
|
|
|
Miscellaneous
|
|
|
|
-------------
|
|
|
|
|
|
|
|
I don't know if the source address register and length counter are actually
|
|
|
|
updated during a DMA operation. I doubt it, but in theory you could have
|
|
|
|
a sequence like this:
|
|
|
|
|
|
|
|
move.l #$94109300, $00C00004 ; length = 4k words
|
|
|
|
move.l #$96009500, $00C00004
|
|
|
|
move.l #$97708F02, $00C00004 ; src = E00000h, inc = 02h
|
|
|
|
move.w #$40000003, $00C00004 ; VRAM write to C000h
|
|
|
|
; 8k is transferred to VRAM C000h from RAM E00000h
|
|
|
|
|
|
|
|
move.w #$60000003, $00C00004 ; VRAM write to E000h
|
|
|
|
; 8k is transferred to VRAM E000h from RAM E02000h
|
|
|
|
|
|
|
|
You can make VRAM fills affect CRAM or VSRAM by changing the CD2-CD0 bits
|
|
|
|
to the appropriate RAM type, just like how 68K -> VDP transfers work.
|
|
|
|
Due to the limited way this was tested, I can't say how exactly the fill
|
|
|
|
data is written; CRAM and VSRAM are word-wide, while VRAM is byte-wide,
|
|
|
|
to there's bound to be some differences. I'd assume the results are
|
|
|
|
the same as normal byte-wide data port access, where both the LSB and
|
|
|
|
MSB of each word are set to the same 8-bit data written.
|
|
|
|
|
|
|
|
In the case of a VRAM fill, the CD2-CD0 bits are set to the same value
|
|
|
|
required for a VRAM write. In the same vein, VRAM copies have the code
|
|
|
|
bits set to the same setting as a VRAM read. Maybe changing the code
|
|
|
|
register would allow copies within CRAM and VSRAM? Or perhaps the code
|
|
|
|
bits only select the source address? (Not that a CRAM > VRAM copy is
|
|
|
|
particularly useful, but there you go.)
|
|
|
|
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
12.) Patterns
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
All background and sprite graphics are made up of patterns. A pattern is
|
|
|
|
an 8x8 or 8x16 (interlace mode 2 only) pixel block that is made up of
|
|
|
|
fifteen colors.
|
|
|
|
|
|
|
|
Patterns are stored in VRAM. Each pixel is represented by four bits,
|
|
|
|
meaning there are four bytes (4 bits * 8 pixels = 4 bytes) per line of
|
|
|
|
the pattern, and 32 or 64 bytes per pattern, depending if the pattern
|
|
|
|
is 8x8 or 8x16.
|
|
|
|
|
|
|
|
Unlike other VRAM data that is stored in a specific table, patterns can
|
|
|
|
be placed anywhere. Even in the parts of other tables that aren't being
|
|
|
|
used, like a name table or sprite attribute table.
|
|
|
|
|
|
|
|
A pixel within a pattern that uses value zero isn't shown. It acts as a
|
|
|
|
transparency indicator.
|
|
|
|
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
13.) Background Layers
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
The VDP manages two background layers, called plane A and plane B.
|
|
|
|
|
|
|
|
Name Tables
|
|
|
|
-----------
|
|
|
|
|
|
|
|
There are three tables stored in video RAM that define the layout for
|
|
|
|
planes A, B, and W.
|
|
|
|
|
|
|
|
Each table is a matrix of 16-bit words. Each word has the following format:
|
|
|
|
|
|
|
|
pccvhnnnnnnnnnnn
|
|
|
|
|
|
|
|
p = Priority flag
|
|
|
|
c = Palette select
|
|
|
|
v = Vertical flip
|
|
|
|
h = Horizontal flip
|
|
|
|
n = Pattern name
|
|
|
|
|
|
|
|
The pattern name is the upper 11 bits of the physical address of pattern
|
|
|
|
in video RAM. Bit zero of the name is ignored in interlace mode 2.
|
|
|
|
|
|
|
|
The vertical and horizontal flip flags tell the VDP to draw the pattern
|
|
|
|
flipped in either direction.
|
|
|
|
|
|
|
|
The palette select allows the pattern to be shown in one of four
|
|
|
|
16-color palettes.
|
|
|
|
|
|
|
|
The priority flag is described later.
|
|
|
|
|
|
|
|
The name tables for plane A and B share the same dimensions. The name
|
|
|
|
table size cannot exceed 8192 bytes, so while a 64x64 or 128x32 name
|
|
|
|
table is allowed, a size of 128x128 or 64x128 is invalid.
|
|
|
|
|
|
|
|
The name table for plane W is 32x32 in 32-cell mode, and 64x32 in 40-cell
|
|
|
|
mode. This size is fixed and is entirely dependant on the display width.
|
|
|
|
|
|
|
|
Window
|
|
|
|
------
|
|
|
|
|
|
|
|
The window plane operates differently from plane A or B. It can be thought
|
|
|
|
of a 'replacement' for plane A which is used under certain conditions.
|
|
|
|
That said, plane A cannot be displayed in any area where plane W is
|
|
|
|
located, it is impossible for them to overlap.
|
|
|
|
|
|
|
|
Registers 17 and 18 define an area which the window is restricted to.
|
|
|
|
|
|
|
|
In terms of priority and intensity calculation for shadow / hilight mode,
|
|
|
|
plane W is treated _exactly_ the same as plane A.
|
|
|
|
|
|
|
|
Horizontal Scrolling
|
|
|
|
--------------------
|
|
|
|
|
|
|
|
The horizontal scroll table holds the scroll value for every line of
|
|
|
|
both planes A and B, that can be positioned anywhere within video RAM.
|
|
|
|
|
|
|
|
Each entry of the scroll table is a 16-bit word. It has the following
|
|
|
|
format:
|
|
|
|
|
|
|
|
------xxxxxxxxxx
|
|
|
|
|
|
|
|
x = Horizontal scroll value (0-3FFh)
|
|
|
|
|
|
|
|
Bits D15 through D10 are ignored by the VDP.
|
|
|
|
|
|
|
|
The lower three bits of the scroll value provide a pixel offset into each
|
|
|
|
column comprised of one pattern. The upper seven bits provide an offset
|
|
|
|
into each column of the name table (0-127).
|
|
|
|
|
|
|
|
When the scroll value is larger than the width of the playfield, the
|
|
|
|
display wraps horizontally.
|
|
|
|
|
|
|
|
Scroll values for planes A and B are stored in an interleaved fashion.
|
|
|
|
Entry #0 of the table is for plane A, entry #1 is for plane B, and this
|
|
|
|
repeats for the entire length of the table.
|
|
|
|
|
|
|
|
The manual says the scroll table is 960 bytes in size,
|
|
|
|
and this seems like an accurate figure, considering the scroll table
|
|
|
|
address bits suggest the table can be 1024 bytes.
|
|
|
|
|
|
|
|
However, I do not know what happens in double-resolution interlace
|
|
|
|
mode. To provide a scroll entry for both planes on every line, a total
|
|
|
|
of 1920 bytes would be needed. (480 lines x 2 planes x 2 bytes per line)
|
|
|
|
|
|
|
|
Here is some "C" pseudocode to illustrate how the VDP reads the scroll
|
|
|
|
table depnding on the settings of bits 0 and 1 of register #11:
|
|
|
|
|
|
|
|
void get_scroll(int line, int *scroll_a, int *scroll_b)
|
|
|
|
{
|
|
|
|
switch(vdp_reg[11] & 3)
|
|
|
|
{
|
|
|
|
case 0x00: /* Full screen */
|
|
|
|
*scroll_a = *(word *)vram[hscb + 0];
|
|
|
|
*scroll_b = *(word *)vram[hscb + 2];
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x01: /* First eight lines */
|
|
|
|
*scroll_a = *(word *)vram[hscb + ((line & 7) * 2) + 0];
|
|
|
|
*scroll_b = *(word *)vram[hscb + ((line & 7) * 2) + 2];
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x02: /* Every row */
|
|
|
|
*scroll_a = *(word *)vram[hscb + ((line & ~7) * 2) + 0];
|
|
|
|
*scroll_b = *(word *)vram[hscb + ((line & ~7) * 2) + 2];
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x03: /* Every line */
|
|
|
|
*scroll_a = *(word *)vram[hscb + (line * 2) + 0];
|
|
|
|
*scroll_b = *(word *)vram[hscb + (line * 2) + 2];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
A scroll mode setting of 01b is not valid; however the unlicensed version
|
|
|
|
of Populous uses it. This mode is identical to per-line scrolling, however
|
|
|
|
the VDP will only read the first sixteen entries in the scroll table for
|
|
|
|
every line of the display.
|
|
|
|
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
14.) Priority
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
The VDP manages a fairly complex system of priorities between the two
|
|
|
|
background layers and sprites. The basic ordering is:
|
|
|
|
|
|
|
|
|
|
|
|
(back) (front)
|
|
|
|
A > B > C > D > E' > F' > G'
|
|
|
|
|
|
|
|
' = Denotes high priority
|
|
|
|
A = Backdrop color
|
|
|
|
B = Low priority plane B
|
|
|
|
C = Low priority plane A
|
|
|
|
D = Low priority sprites
|
|
|
|
E = High priority plane B
|
|
|
|
F = High priority plane A
|
|
|
|
G = High priority sprites
|
|
|
|
|
|
|
|
The sprite priority bit does not affect inter-sprite priority, only the
|
|
|
|
relation between background data. Low priority sprites *can* overlap high
|
|
|
|
priority sprites. Games that do this to mask other sprites include
|
|
|
|
Castlevania Bloodlines, Raiden Trad, and Alien Soldier.
|
|
|
|
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
15.) Sprites
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
The Genesis can display a total of eighty 32x32 15-color sprites.
|
|
|
|
There are of course various restrictions on the display capacity, based
|
|
|
|
on current configuration of the VDP.
|
|
|
|
|
|
|
|
Sprite Attribute Table
|
|
|
|
----------------------
|
|
|
|
|
|
|
|
All sprite data is stored in a region of VRAM called sprite attribute table.
|
|
|
|
The table is 640 bytes in size. Each 8-byte entry has the following format:
|
|
|
|
|
|
|
|
Index + 0 : ------yy yyyyyyyy
|
|
|
|
Index + 2 : ----hhvv
|
|
|
|
Index + 3 : -lllllll
|
|
|
|
Index + 4 : pccvhnnn nnnnnnnn
|
|
|
|
Index + 6 : ------xx xxxxxxxx
|
|
|
|
|
|
|
|
y = Vertical coordinate of sprite
|
|
|
|
h = Horizontal size in cells (00b=1 cell, 11b=4 cells)
|
|
|
|
v = Vertical size in cells (00b=1 cell, 11b=4 cells)
|
|
|
|
l = Link field
|
|
|
|
p = Priority
|
|
|
|
c = Color palette
|
|
|
|
v = Vertical flip
|
|
|
|
h = Horizontal flip
|
|
|
|
n = Sprite pattern start index
|
|
|
|
x = Horizontal coordinate of sprite
|
|
|
|
|
|
|
|
Linking
|
|
|
|
-------
|
|
|
|
|
|
|
|
The VDP draws sprites in a front-to-back order, starting with sprite zero.
|
|
|
|
The 7-bit link field in each list entry is an index to the next entry of
|
|
|
|
the sprite that will be drawn. If the link field is zero, then no more
|
|
|
|
sprites will be drawn after the current sprite. Here's an example:
|
|
|
|
|
|
|
|
Sprite #00 has a link field of 2
|
|
|
|
Sprite #01 has a link field of 7
|
|
|
|
Sprite #02 has a link field of 4
|
|
|
|
Sprite #03 has a link field of 0
|
|
|
|
Sprite #04 has a link field of 3
|
|
|
|
Sprite #05 has a link field of 2
|
|
|
|
|
|
|
|
In this case, sprites #00, #02, #04, #03 will be drawn, in that order.
|
|
|
|
|
|
|
|
Coordinate System
|
|
|
|
-----------------
|
|
|
|
|
|
|
|
Sprites are positioned in a virtual 512x512 space. The display starts at
|
|
|
|
coordinate 128, 128, and takes up a space equal to the size of the physical
|
|
|
|
display. (usually 256x224 or 320x224) This system is convenient for
|
|
|
|
programmers; unwanted sprites can easily be hidden off screen, and sprites
|
|
|
|
can be shown partially at the top and left screen edges.
|
|
|
|
|
|
|
|
Sprite masking, mode 1
|
|
|
|
----------------------
|
|
|
|
|
|
|
|
If a sprite has an X coordinate of zero, and has a Y coordinate that
|
|
|
|
is within range of the display, then all sprites of lower priority
|
|
|
|
will not be displayed on the lines which the sprite takes up.
|
|
|
|
|
|
|
|
The height of the sprite is determined by the vertical size bits in
|
|
|
|
the sprite attributes; other factors like horizontal size, pattern data
|
|
|
|
used, priority bit, and color palette have no effect.
|
|
|
|
|
|
|
|
For instance, an 8x32 sprite at coordinates 0, 128, that was sprite #4
|
|
|
|
in the list would stop all sprites onwards for lines zero to 31 from
|
|
|
|
being shown. However, sprites #0 through #3 could still be displayed
|
|
|
|
in this area.
|
|
|
|
|
|
|
|
Sprite masking, mode 2
|
|
|
|
----------------------
|
|
|
|
|
|
|
|
If a sprite has an X coordinate of one, the former rule is invalid. Low
|
|
|
|
priority sprites will only be masked if a sprite with an X coordinate of
|
|
|
|
zero _also_ has a sprite with an X coordinate of one on the _same_ line.
|
|
|
|
|
|
|
|
This 'mode' is enabled when the VDP first parses a sprite with an X
|
|
|
|
coordinate of one. It is reset at the end of the frame.
|
|
|
|
|
|
|
|
To my knowledge, the only game which uses this masking mode is Galaxy
|
|
|
|
Force II. Because of this, I cannot ensure my description is accurate for
|
|
|
|
other games which may use it.
|
|
|
|
|
|
|
|
Sprite Drawing Limitations
|
|
|
|
--------------------------
|
|
|
|
|
|
|
|
The VDP will stop drawing sprites under the following conditions:
|
|
|
|
|
|
|
|
- The 80th sprite has been drawn in 40-cell mode.
|
|
|
|
- The 64th sprite has been drawn in 32-cell mode.
|
|
|
|
- Twenty sprites on the same scanline have been drawn in 40 cell mode.
|
|
|
|
- Sixteen sprites on the same scanline have been drawn in 32 cell mode.
|
|
|
|
- 320 pixels worth of sprite data has been drawn on the same scanline
|
|
|
|
in 40 cell mode.
|
|
|
|
- 256 pixels worth of sprite data has been drawn on the same scanline
|
|
|
|
in 32 cell mode.
|
|
|
|
- The currently drawn sprite has a link field of zero.
|
|
|
|
|
|
|
|
Sprites that are outside of the physical display area are still taken
|
|
|
|
into account.
|
|
|
|
|
|
|
|
Link settings that create an 'infinite loop' or that have self-referencing
|
|
|
|
will not cause any unforseen problem, these kinds of loops will be broken
|
|
|
|
out of when the above sprite limitations are eventually reached.
|
|
|
|
|
|
|
|
Because so many sprites can be shown on a single line, some games will
|
|
|
|
fill the entire display with sprites for a 'fake' third background layer.
|
|
|
|
Games that do this include 'Red Zone' by Zyrinx.
|
|
|
|
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
16.) Shadow / Hilight mode
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
Shadow / hilight mode allows more colors to be displayed on screen.
|
|
|
|
|
|
|
|
These additional colors are selected based upon the priority settings
|
|
|
|
for a background tile or sprite, and when certain kinds of sprite pixels
|
|
|
|
overlap background pixels.
|
|
|
|
|
|
|
|
Background
|
|
|
|
----------
|
|
|
|
|
|
|
|
Background tiles are shown at half intensity, or at normal intensity
|
|
|
|
if their priority bit is set.
|
|
|
|
|
|
|
|
In the latter case, this affects all other graphics elements in the
|
|
|
|
region (usually 8x8) that the tile takes up, regardless of the tile
|
|
|
|
containing opaque pixels or not. Meaning that the backdrop color, other
|
|
|
|
background plane, and sprites will be forcibly shown at normal intensity
|
|
|
|
in a given area if a background tile has it's priority bit set.
|
|
|
|
|
|
|
|
For instance, Ranger-X has both background layers and all sprites set
|
|
|
|
to low priority. A column in plane A uses high priority transparent
|
|
|
|
tiles. The result is that any sprites passing within that column, as
|
|
|
|
well as the plane B tiles that scroll behind it, are all shown at
|
|
|
|
normal intensity.
|
|
|
|
|
|
|
|
Sprites
|
|
|
|
-------
|
|
|
|
|
|
|
|
Depending on the priority setting, sprites are shown at normal or half
|
|
|
|
intensity just like background tiles.
|
|
|
|
|
|
|
|
Colors 0Eh, 1Eh, 2Eh, are always shown at normal intensity regardless of
|
|
|
|
priority. I'd say this a bug rather than a feature.
|
|
|
|
|
|
|
|
Any pixel in a sprite that uses colors 3Eh or 3Fh is treated specially:
|
|
|
|
|
|
|
|
Pixels with color 3Eh are not drawn. Instead, the underlying pixel
|
|
|
|
(from the backdrop or background) will be shown at half intensity.
|
|
|
|
|
|
|
|
If the pixel to be overwritten is already at half intensity, then
|
|
|
|
nothing will happen.
|
|
|
|
|
|
|
|
Pixels with color 3Fh are not drawn. Instead, the underlying pixel
|
|
|
|
(from the backdrop or background) will be shown at double intensity.
|
|
|
|
|
|
|
|
Backdrop
|
|
|
|
--------
|
|
|
|
|
|
|
|
The backdrop is shown at half intensity, while the overscan region outside
|
|
|
|
of the display area is always shown at normal intensity.
|
|
|
|
|
|
|
|
If a background tile has high priority, then the corresponding 8x8 block
|
|
|
|
of the backdrop color will be shown at normal intensity. It does not matter
|
|
|
|
if the background tile is fully opaque or not.
|
|
|
|
|
|
|
|
Operator sprites will affect the backdrop color as well.
|
|
|
|
|
|
|
|
Details
|
|
|
|
-------
|
|
|
|
|
|
|
|
It isn't known exactly how the colors are generated in shadow / hilight
|
|
|
|
mode. Currently all emulators make the half and double intensity colors
|
|
|
|
exactly half and double brightness of the current palette.
|
|
|
|
|
|
|
|
This is not correct. At least the double intensity palette is actually
|
|
|
|
not much brighter than the normal palette. You can see a good example
|
|
|
|
of this by running any game using hilight sprites in an emulator
|
|
|
|
side-by-side to the same game running on a real Genesis.
|
|
|
|
|
|
|
|
However, I haven't been able to figure out the colors exactly. In
|
|
|
|
particular, the introduction screen for Sonic 3D will only look correct
|
|
|
|
if the colors are exactly half and double, while this is certainly wrong
|
|
|
|
for other games.
|
|
|
|
|
|
|
|
I've verified that the contents of CRAM entries 3Eh and 3Fh do not
|
|
|
|
affect the intensity of shadow and hilight pixels used in sprites.
|
|
|
|
|
|
|
|
Any information on the colors used in shadow / hilight mode would be
|
|
|
|
appreciated.
|
|
|
|
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
17.) VDP registers
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
All the register names are taken from the manual.
|
|
|
|
|
|
|
|
$00 - Mode Set Register No. 1
|
|
|
|
-----------------------------
|
|
|
|
|
|
|
|
d7 - No effect
|
|
|
|
d6 - No effect
|
|
|
|
d5 - No effect
|
|
|
|
d4 - IE1 (Horizontal interrupt enable)
|
|
|
|
d3 - 1= Invalid display setting
|
|
|
|
d2 - Palette select
|
|
|
|
d1 - M3 (HV counter latch enable)
|
|
|
|
d0 - Display disable
|
|
|
|
|
|
|
|
Bit 4 will enable horizontal interrupts when set.
|
|
|
|
|
|
|
|
When bit 2 is cleared, only bits 1, 5, and 9 are taken into account
|
|
|
|
when the VDP generates color data from each word of CRAM. (meaning
|
|
|
|
the LSB of each RGB component is the only bit used) This reduces the
|
|
|
|
available color palette from 512 to 8 colors.
|
|
|
|
|
|
|
|
Bit 1 will cause the HV counter to be latched when a level 2 interrupt
|
|
|
|
is generated. The HV counter will resume normal operation when this bit
|
|
|
|
is cleared. (untested, need more info)
|
|
|
|
|
|
|
|
Setting bit 0 actually turns off all display generation, as opposed to the
|
|
|
|
screen blanking feature which simply shows the backdrop color.
|
|
|
|
|
|
|
|
$01 - Mode Set Register No. 2
|
|
|
|
-----------------------------
|
|
|
|
|
|
|
|
d7 - TMS9918 / Genesis display select
|
|
|
|
d6 - DISP (Display Enable)
|
|
|
|
d5 - IE0 (Vertical Interrupt Enable)
|
|
|
|
d4 - M1 (DMA Enable)
|
|
|
|
d3 - M2 (PAL / NTSC)
|
|
|
|
d2 - SMS / Genesis display select
|
|
|
|
d1 - 0 (No effect)
|
|
|
|
d0 - 0 (See notes)
|
|
|
|
|
|
|
|
Bit 7 seemingly puts the display in a TMS9918-like state when set, similar
|
|
|
|
to one of it's text display modes. Each 8x8 block is filled with a solid
|
|
|
|
color from the palette, and has no pattern data. The sprites seem to be
|
|
|
|
active. I couldn't select any of the other TMS9918 modes through the
|
|
|
|
usual TMS9918 mode bits. It would appear all the colors are actually
|
|
|
|
affected by CRAM, instead of using a fixed color set.
|
|
|
|
|
|
|
|
Bit 6 will blank the display when cleared. Any line that is blanked is
|
|
|
|
filled with the backdrop color. During this time, you can freely access
|
|
|
|
VDP memory with no limitations on the number of writes per line.
|
|
|
|
|
|
|
|
Bit 5 will enable vertical blanking interrupts when set.
|
|
|
|
|
|
|
|
Bit 4 will enable DMA operations when set. Otherwise, nothing will happen
|
|
|
|
when a DMA command is sent to the VDP.
|
|
|
|
|
|
|
|
Bit 3 will select between a PAL (240) and NTSC (224 lines) display.
|
|
|
|
|
|
|
|
Bit 2 toggles between the Master System (mode 4) and Genesis (mode 5)
|
|
|
|
display modes. While in mode 4, none of the registers which normally
|
|
|
|
affect the Genesis work; and the unused registers (8, 9 - can't test 6) now
|
|
|
|
function. The mode bits which select TMS9918 modes on a real SMS have
|
|
|
|
no function here. (This is why the SMS game F16 Fighter will not work
|
|
|
|
with a Power Base Converter, it uses some of the TMS9918 modes in-game)
|
|
|
|
|
|
|
|
The one exception is register $0C. You can set up a 320x192 display,
|
|
|
|
but the leftmost eight columns read 'garbage' data for the name table
|
|
|
|
attributes. Enabling interlace makes the display unstable. (and this
|
|
|
|
is partially true for a 320x192 picture, which shakes slightly) I'd
|
|
|
|
advise you set $0C to zero to enable a 256x192 display, which is the
|
|
|
|
normal SMS resolution. The Genesis always generates a 224 line picture;
|
|
|
|
the 192 lines in SMS mode are centered in the middle of the screen.
|
|
|
|
|
|
|
|
I could not get the top row or right column lock features to work while
|
|
|
|
in SMS mode. Apart from this bit, the M3 pin on the cartridge connector
|
|
|
|
also puts the machine into SMS mode, which may fully enable all video
|
|
|
|
features.
|
|
|
|
|
|
|
|
Bit 0 has an interesting effect; horizontal scrolling is disabled, and
|
|
|
|
it would almost seem like the horizontal scroll value modifies the
|
|
|
|
horizontal retrace / blanking / sync start and end positions around; the
|
|
|
|
middle of the display is blanked out, and will scroll left or right.
|
|
|
|
(note the blanked area scrolls - not the background) Moving too far in
|
|
|
|
one direction, so the blanked area is offscreen, totally corrupts the
|
|
|
|
display.
|
|
|
|
|
|
|
|
Combining bits 7 (TMS9918 mode) and 2 (SMS mode) have no effect.
|
|
|
|
|
|
|
|
$02 - Pattern Name Table Address for Scroll A
|
|
|
|
---------------------------------------------
|
|
|
|
|
|
|
|
Bits 5-3 of this register correspond to bits A15-A13 of the name table
|
|
|
|
address for plane A.
|
|
|
|
|
|
|
|
$03 - Pattern Name Table Address for Window
|
|
|
|
---------------------------------------------
|
|
|
|
|
|
|
|
Bits 5-1 of this register correspond to bits A15-A11 of the name table
|
|
|
|
address for the window.
|
|
|
|
|
|
|
|
In 40-cell mode, A11 is always forced to zero.
|
|
|
|
|
|
|
|
$04 - Pattern Name Table Address for Scroll B
|
|
|
|
---------------------------------------------
|
|
|
|
|
|
|
|
Bits 2-0 of this register correspond to bits A15-A11 of the name table
|
|
|
|
address for plane B.
|
|
|
|
|
|
|
|
$05 - Sprite Attribute Table Base Address
|
|
|
|
-----------------------------------------
|
|
|
|
|
|
|
|
Bits 6-0 of this register correspond to bits A15-A09 of the sprite
|
|
|
|
attribute table.
|
|
|
|
|
|
|
|
In 40-cell mode, A09 is always forced to zero.
|
|
|
|
|
|
|
|
$07 - Backdrop Color
|
|
|
|
--------------------
|
|
|
|
|
|
|
|
Bits 5-0 of this register select a palette entry to be used as the
|
|
|
|
backdrop color.
|
|
|
|
|
|
|
|
The backdrop color is displayed in the following places:
|
|
|
|
|
|
|
|
- The overscan area around the physical display
|
|
|
|
- Any line where the display enable bit has been cleared
|
|
|
|
|
|
|
|
You can think of the display being filled with the backdrop color, and
|
|
|
|
then everything else being drawn over it. Any gaps where no pixels were
|
|
|
|
drawn will show the backdrop color.
|
|
|
|
|
|
|
|
Even though palette entries 00h, 10h, 20h, and 30h cannot be used by
|
|
|
|
any patterns, these entries can be used for the backdrop color.
|
|
|
|
|
|
|
|
$0A - H Interrupt Register
|
|
|
|
--------------------------
|
|
|
|
|
|
|
|
Bits 7-0 specify the value to be loaded in the counter; for complete
|
|
|
|
details see the "Interrupts" section.
|
|
|
|
|
|
|
|
$0B - Mode Set Register No. 3
|
|
|
|
-----------------------------
|
|
|
|
|
|
|
|
d7 - 0 (No effect)
|
|
|
|
d6 - 0 (No effect)
|
|
|
|
d5 - 0 (No effect)
|
|
|
|
d4 - 0 (No effect)
|
|
|
|
d3 - IE2
|
|
|
|
d2 - VSCR
|
|
|
|
d1 - HSCR
|
|
|
|
d0 - LSCR
|
|
|
|
|
|
|
|
Bit 3 will enable external interrupts, caused by the TH pin being set
|
|
|
|
to input mode and having the TH interrupt enable bit set. (Both of these
|
|
|
|
are controlled by the Genesis' I/O registers)
|
|
|
|
|
|
|
|
Bit 2 selects between full screen vertical scrolling when clear, and
|
|
|
|
2-cell column based vertical scrolling when set.
|
|
|
|
|
|
|
|
Bits 1 and 0 determine how the VDP will parse the horizontal scroll table
|
|
|
|
when it is rendering display lines:
|
|
|
|
|
|
|
|
HSCR LSCR
|
|
|
|
0 0 - Full screen scroll
|
|
|
|
0 1 - Line scroll
|
|
|
|
1 0 - Cell scroll
|
|
|
|
1 1 - Line scroll
|
|
|
|
|
|
|
|
$0C - Mode Set Register No. 4
|
|
|
|
-----------------------------
|
|
|
|
|
|
|
|
d7 - RS0
|
|
|
|
d6 - 0 (No effect)
|
|
|
|
d5 - ? (See notes)
|
|
|
|
d4 - 0 (No effect)
|
|
|
|
d3 - S/TE
|
|
|
|
d2 - LSM1
|
|
|
|
d1 - LSM0
|
|
|
|
d0 - RS1
|
|
|
|
|
|
|
|
LSMx table:
|
|
|
|
00 : No interlace
|
|
|
|
01 : Interlace (Normal resolution)
|
|
|
|
10 : No interlace
|
|
|
|
11 : Interlace (Double resolution)
|
|
|
|
|
|
|
|
RSx table:
|
|
|
|
00 : Display is 32 cells wide
|
|
|
|
01 : Display is 40 cells wide
|
|
|
|
10 : Invalid display setting
|
|
|
|
11 : Display is 40 cells wide
|
|
|
|
|
|
|
|
Changes made to LSM0, LSM1 do not take effect until the display is no
|
|
|
|
longer in the active scan period.
|
|
|
|
|
|
|
|
All other bits can be modified with changes taking effect immediately at
|
|
|
|
any point in the display frame.
|
|
|
|
|
|
|
|
You should normally set the RSx bits to 00b or 11b. The unlicensed version
|
|
|
|
of Populous sets up a 40 cell display with a setting of 01b - technically
|
|
|
|
valid, but the display is distorted a bit.
|
|
|
|
|
|
|
|
Bit 5 seems to affect the display when used in conjunction with RS0, but
|
|
|
|
only in the same way as the display appears when using a setting of 01b.
|
|
|
|
I've tried every combination of bit 6 along with the RSx bits, and the
|
|
|
|
physical width of the display was never different from 32 or 40 cells.
|
|
|
|
|
|
|
|
$0D - H Scroll Data Table Base Address
|
|
|
|
--------------------------------------
|
|
|
|
|
|
|
|
Bits 5-0 of this register correspond to bits A15-A10 of the horizontal
|
|
|
|
scroll data table address.
|
|
|
|
|
|
|
|
$0F - Auto Increment Data
|
|
|
|
-------------------------
|
|
|
|
|
|
|
|
Bits 7-0 specify the value to be added to the VDP's address register
|
|
|
|
after every read or write to the data port.
|
|
|
|
|
|
|
|
A setting of zero means the address register is not incremented.
|
|
|
|
|
|
|
|
$10 - Scroll Size
|
|
|
|
-----------------
|
|
|
|
|
|
|
|
d7 - 0 (No effect)
|
|
|
|
d6 - 0 (No effect)
|
|
|
|
d5 - VSZ1
|
|
|
|
d4 - VSZ0
|
|
|
|
d3 - 0 (No effect)
|
|
|
|
d2 - 0 (No effect)
|
|
|
|
d1 - HSZ1
|
|
|
|
d0 - HSZ0
|
|
|
|
|
|
|
|
This register defines the size of the name tables for planes A and B.
|
|
|
|
Both fields can be set to the following values:
|
|
|
|
|
|
|
|
0 0 32 cells
|
|
|
|
0 1 64 cells
|
|
|
|
1 0 Invalid
|
|
|
|
1 1 128 cells
|
|
|
|
|
|
|
|
If the HSZ bits are set to 10b (invalid), then the first row of the name
|
|
|
|
table is shown for every line of the display.
|
|
|
|
|
|
|
|
|
|
|
|
$11 - Window H Position
|
|
|
|
-----------------------
|
|
|
|
|
|
|
|
d7 - RIGT
|
|
|
|
d6 - 0 (No effect)
|
|
|
|
d5 - 0 (No effect)
|
|
|
|
d4 - WHP5
|
|
|
|
d3 - WHP4
|
|
|
|
d2 - WHP3
|
|
|
|
d1 - WHP2
|
|
|
|
d0 - WHP1
|
|
|
|
|
|
|
|
This register will affect the window shown on the current line, if
|
|
|
|
the current line does not fall into the vertical range specified by
|
|
|
|
register $12.
|
|
|
|
|
|
|
|
The WHP field defines the horizontal range of the window, in units
|
|
|
|
of two cells. (16 pixels)
|
|
|
|
|
|
|
|
Setting the WHP field to zero disables the window.
|
|
|
|
Any nonzero value indicates how many 2-cell columns wide the window
|
|
|
|
plane is. (0=no window, 1=2 cells, 2=4 cells, 3=6 cells, etc.)
|
|
|
|
|
|
|
|
When RIGT=0, the window is shown from column zero to the column
|
|
|
|
specified by the WHP field.
|
|
|
|
|
|
|
|
For instance, if RIGT=0 and WHP=4, the window is displayed on columns
|
|
|
|
zero up to (and including) column seven.
|
|
|
|
|
|
|
|
When RIGHT=1, the window is shown from the column specified in the WHP
|
|
|
|
field up to the last column in the display meaning column 31 or 39
|
|
|
|
depending on the screen width setting.
|
|
|
|
|
|
|
|
For instance, if RIGT=1 and WHP=4, the window is displayed on columns
|
|
|
|
eight up to (and including) column 31 or 39.
|
|
|
|
|
|
|
|
Having WHP set to zero and RIGHT=1 is a legal setting; it means the
|
|
|
|
window is shown from column zero up to the last column in the display,
|
|
|
|
meaning the entire line is taken up by the window plane.
|
|
|
|
|
|
|
|
There is a bug in the window processing. This occurs when the window is
|
|
|
|
showing partially on the left side of the screen, specifically when
|
|
|
|
the VDP is drawing the 2-cell column from plane A that immediately proceeds
|
|
|
|
the last column the window was drawn on. (i.e. WHP+1)
|
|
|
|
|
|
|
|
If the lower four bits of the horizontal scroll value for the current
|
|
|
|
scan line are zero, then the name table attribute data for the current
|
|
|
|
column are fetched correctly.
|
|
|
|
|
|
|
|
If the lower four bits of the horizontal scroll value for the current
|
|
|
|
scan line are nonzero, the name table attribute data are fetched from
|
|
|
|
next column. (WHP+2)
|
|
|
|
|
|
|
|
In effect, you'll have N columns of the window plane, 1 column that has
|
|
|
|
identical patterns as the next column, then the remainder of the display
|
|
|
|
is drawn correctly.
|
|
|
|
|
|
|
|
Here's a diagram to illustrate this:
|
|
|
|
w = window tiles
|
|
|
|
abc = tile columns
|
|
|
|
|
|
|
|
D3-D0 of scroll value == 0
|
|
|
|
wwwwwwwwwwwwwwwwaabbccddeeffgghh
|
|
|
|
|
|
|
|
D3-D0 of scroll value != 0
|
|
|
|
wwwwwwwwwwwwwwwwbbbbccddeeffgghh
|
|
|
|
|
|
|
|
This register can be modified with changes taking effect immediately at
|
|
|
|
any point in the display frame.
|
|
|
|
|
|
|
|
Plane A is not shown in any column where plane W is shown; they cannot
|
|
|
|
overlap.
|
|
|
|
|
|
|
|
$12 - Window V Position
|
|
|
|
-----------------------
|
|
|
|
|
|
|
|
d7 - DOWN
|
|
|
|
d6 - 0 (No effect)
|
|
|
|
d5 - 0 (No effect)
|
|
|
|
d4 - WVP4
|
|
|
|
d3 - WVP3
|
|
|
|
d2 - WVP2
|
|
|
|
d1 - WVP1
|
|
|
|
d0 - WVP0
|
|
|
|
|
|
|
|
If the current scanline does not fall within the range specified by
|
|
|
|
register $12, then register $11 determines where the window is shown
|
|
|
|
for the remainder of the display.
|
|
|
|
|
|
|
|
The WVP field defines the vertical range of the window, in units
|
|
|
|
of eight lines.
|
|
|
|
|
|
|
|
Setting the WVP field to zero disables the window.
|
|
|
|
Any nonzero value indicates a vertical range for the window to appear
|
|
|
|
in. (0=no window, 1=lines 0-$7, 2= lines 0-$F, 3= lines 0-$17, etc.)
|
|
|
|
|
|
|
|
When DOWN=0, the window is shown from line zero to the line specified
|
|
|
|
by the WVP field.
|
|
|
|
|
|
|
|
For instance, if DOWN=0 and WVP=4, the window is displayed on lines
|
|
|
|
zero up to (and including) line $1F.
|
|
|
|
|
|
|
|
When DOWN=1, the window is shown from the line specified in the WVP
|
|
|
|
field up to the last line in the display.
|
|
|
|
|
|
|
|
For instance, if DOWN=1 and WVP=4, the window is displayed on lines $1F
|
|
|
|
up to (and including) the last line in the display.
|
|
|
|
|
|
|
|
Having WVP set to zero and DOWN=1 is a legal setting; it means the
|
|
|
|
window is shown from line zero up to the last line in the display,
|
|
|
|
meaning the entire screen is taken up by the window plane.
|
|
|
|
|
|
|
|
Plane A is not shown in any line where plane W is shown; they cannot
|
|
|
|
overlap.
|
|
|
|
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
18.) VDP Pinout
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
This is applicable to the 315-5313 chip used in the early versions of
|
|
|
|
the original Genesis model.
|
|
|
|
|
|
|
|
1-8 - SD0-SD7 (VRAM data bus)
|
|
|
|
10 - SE0 (VRAM)
|
|
|
|
11 - SC (VRAM)
|
|
|
|
12 - RAS (VRAM)
|
|
|
|
13 - CAS (VRAM)
|
|
|
|
15 - WE0 (VRAM)
|
|
|
|
16 - OE (VRAM)
|
|
|
|
17 - GND
|
|
|
|
26 - AGC
|
|
|
|
27 - R (Red video output)
|
|
|
|
28 - G (Green video output)
|
|
|
|
29 - B (Blue video output)
|
|
|
|
30 - AVC
|
|
|
|
31-38 - AD0-AD7 (VRAM address bus)
|
|
|
|
39 - YS
|
|
|
|
40 - SPA/8
|
|
|
|
41 - VSYNC (Vertical sync)
|
|
|
|
42 - C-SYNC (Composite sync)
|
|
|
|
43 - HSYNC (Horizontal sync)
|
|
|
|
44 - HL (from control port HL pin)
|
|
|
|
45 - SEL0 (from M3 on cartridge connector - forces mode 4 graphics)
|
|
|
|
46 - PAL (PAL / NTSC select)
|
|
|
|
47 - RESET (68000)
|
|
|
|
49 - CLK1 (68000)
|
|
|
|
48 - SEL1 (?)
|
|
|
|
50 - SBCR
|
|
|
|
51 - CLK0
|
|
|
|
52 - MCLK (53.64165 MHz)
|
|
|
|
53 - EDCLK
|
|
|
|
54 - VDD
|
|
|
|
54-70 - CD0-CD15 (68000 data bus)
|
|
|
|
71-93 - CA0-CA22 (68000 address bus (A23-A1))
|
|
|
|
94 - AVS
|
|
|
|
95 - PSG (SN76489 PSG sound output)
|
|
|
|
97 - GND
|
|
|
|
98 - INT
|
|
|
|
99 - BR
|
|
|
|
100 - BGAK
|
|
|
|
101 - BG
|
|
|
|
102 - MRE0
|
|
|
|
103 - INTAK
|
|
|
|
104 - IPL1 (68000)
|
|
|
|
105 - IPL2 (68000)
|
|
|
|
106 - IREQ
|
|
|
|
107 - RD (68000)
|
|
|
|
108 - WR (68000)
|
|
|
|
109 - MI
|
|
|
|
110 - AS (68000)
|
|
|
|
111 - UDS (68000)
|
|
|
|
112 - LDS (68000)
|
|
|
|
113 - R/W
|
|
|
|
114 - DTAK (68000)
|
|
|
|
115 - UWR (68000)
|
|
|
|
116 - LWR (68000)
|
|
|
|
118 - CAS0 (VRAM)
|
|
|
|
117 - OE0 (VRAM)
|
|
|
|
119 - RAS0 (VRAM)
|
|
|
|
128 - VDD
|
|
|
|
|
|
|
|
============================================================================
|
|
|
|
|
|
|
|
To-do test list
|
|
|
|
|
|
|
|
- Last column wrapping (40 cell mode only?)
|
|
|
|
- Columns being offset by the horizontal scroll value (d3-d0)
|
|
|
|
- How line scrolling is managed in interlace mode 2
|
|
|
|
- How sprite Y positions and VSRAM values are used in interlace mode 2
|
|
|
|
- Vertical interrupt supression via line interrupts
|
|
|
|
- Reverse sprite stage in CV
|
|
|
|
- Mid frame sprite table changes (can't be done I think; sprite table
|
|
|
|
changes aren't seen by VDP, though changing the sprite table address
|
|
|
|
will cause a switch)
|
|
|
|
- Large sprite pattern overflow
|
|
|
|
- Result of copy and fill operations overflowing
|
|
|
|
- Result of copy operations overlapping
|
|
|
|
- Use of HV counter latch in the 6-in-1 pak.
|
|
|
|
- Source and length registers being updated during DMA
|
|
|
|
- Confirm invalid code use
|
|
|
|
- Default status flag settings
|
|
|
|
- Sprite collisions and overflows outside of the physical display area
|
|
|
|
- How many times a sprite collision and overflow can occur (per line/frame?)
|
|
|
|
|
|
|
|
And for timing, based on the HV counter:
|
|
|
|
|
|
|
|
- Vertical counter increment
|
|
|
|
- Horizontal blanking flag clear and set
|
|
|
|
- Vertical blank flag clear and set
|
|
|
|
- Horizontal interrupt occurance
|
|
|
|
- Vertical interrupt occurance
|
|
|
|
- Invalid periods for vertical counter
|
|
|
|
- Invalid periods for horizontal counter (test with 6-in-1 pak)
|
|
|
|
- Start/stop of physical display (use sprite collision on either edge)
|