<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.neogeodev.org//api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Frenchshark</id>
	<title>NeoGeo Development Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.neogeodev.org//api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Frenchshark"/>
	<link rel="alternate" type="text/html" href="https://wiki.neogeodev.org//index.php/Special:Contributions/Frenchshark"/>
	<updated>2026-09-09T22:30:31Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.40.0</generator>
	<entry>
		<id>https://wiki.neogeodev.org//index.php?title=Rendering_logic&amp;diff=4947</id>
		<title>Rendering logic</title>
		<link rel="alternate" type="text/html" href="https://wiki.neogeodev.org//index.php?title=Rendering_logic&amp;diff=4947"/>
		<updated>2016-08-09T09:45:28Z</updated>

		<summary type="html">&lt;p&gt;Frenchshark: /* Sprite parsing */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;On the NeoGeo hardware, the GPU (Graphics Processing Unit) a.k.a. VDP, may refer to a chip or a group of different chips used to generate the video signal.&lt;br /&gt;
&lt;br /&gt;
* [[LSPC-A0]], [[PRO-B0]] (early)&lt;br /&gt;
* [[LSPC2-A2]], [[NEO-B1]] (most common)&lt;br /&gt;
* [[NEO-GRC]], [[NEO-OFC]] (CD systems)&lt;br /&gt;
* [[NEO-GRZ]] (CDZ, MV-1C ?)&lt;br /&gt;
&lt;br /&gt;
See [[graphics pipeline]] for an overview of the interconnections between chips and cartridges.&lt;br /&gt;
&lt;br /&gt;
==Temporary notes==&lt;br /&gt;
&lt;br /&gt;
*Fix, then sprites (PCK1 then PCK2)&lt;br /&gt;
*Fix and sprite pixels are rendered at the same speed because sprite pixels are also written by pairs (reason for the odd/even buffers)&lt;br /&gt;
*Tile pixel lines are rendered in halves:&lt;br /&gt;
&lt;br /&gt;
*For the fix (32mclk = 8 pixels corresponds to 6MHz pixel clock):&lt;br /&gt;
**Full address is ...1**** (PCK2 pulse)&lt;br /&gt;
**2H1 is 0 for 2 pixels (columns 0 &amp;amp; 1), then 1 for 2 pixels (columns 2 &amp;amp; 3)&lt;br /&gt;
**Full address is ...0**** (PCK2 pulse)&lt;br /&gt;
**2H1 is 0 for 2 pixels (columns 4 &amp;amp; 5), then 1 for 2 pixels (columns 6 &amp;amp; 7)&lt;br /&gt;
&lt;br /&gt;
*For sprites (32mclk = 16 pixels):&lt;br /&gt;
**Full address is ...1***** (PCK1 pulse)&lt;br /&gt;
**CA4 is 0 for 4 pixels (columns 0~3), then 1 for 4 pixels (columns 4~7)&lt;br /&gt;
**Full address is ...0***** (PCK1 pulse)&lt;br /&gt;
**CA4 is 0 for 4 pixels (columns 8~11), then 1 for 4 pixels (columns 12~15)&lt;br /&gt;
&lt;br /&gt;
*As fix is rendered in realtime, the fix tile address is set before sprites (on a new line PCK1 pulses before PCK2)&lt;br /&gt;
*X position to B1, just before each PCK2 pulse (SP during 1mclk), for 20 sprites next to each other (X+16px each time):&lt;br /&gt;
** Start of line: 0000,0808,1010,1838,2000,2808,3010,3838,40C0,48E8,50F0,58F8,60C0,68E8,70F0,78F8,8000,8808,9010,9838,0,0,0...&lt;br /&gt;
&lt;br /&gt;
=Video generation=&lt;br /&gt;
&lt;br /&gt;
See [[Display timing]] for the sync signal&#039;s timing.&lt;br /&gt;
&lt;br /&gt;
[[NEO-B1]] is used for double-buffering scanlines. While a buffer is output to the screen, the other one is filled up. They&#039;re swapped each new scanline. Each of the two line buffers are actually 2 buffers of even/odd pixels. They will be named (1 &amp;amp; 2), and (3 &amp;amp; 4).&lt;br /&gt;
&lt;br /&gt;
*The TMS0 signal from LSPC tells B1 how the pair of buffers are used:&lt;br /&gt;
**0: Buffers 1&amp;amp;2 are output to the TV. Buffers 3&amp;amp;4 are written to.&lt;br /&gt;
**1: Buffers 1&amp;amp;2 are written to. Buffers 3&amp;amp;4 are output to the TV.&lt;br /&gt;
&lt;br /&gt;
*CSK1~4 signals are used to step to the next pixel (falling edge ?), periodic for video output, VRAM-dependent when filling up. Inactive during H-blank.&lt;br /&gt;
*WSE1~4 signals are used to indicate if the pixel color from GAD/GBD needs to be written to the buffer, matches CSK for video output (OE signal ?), depends on DOTA/DOTB (opaque pixel signal) when filling up.&lt;br /&gt;
*SS1~2 signals ?&lt;br /&gt;
*The rising edge of PCK1 and PCK2 stores fix or sprite pixels.&lt;br /&gt;
*The X position of the sprite (and something else in a byte ?) is latched by CSK falling edges when LD* is low.&lt;br /&gt;
*1H1 is probably used to switch pixels of FIXD between left and right.&lt;br /&gt;
&lt;br /&gt;
It seems that fix data is read 8 pixels in advance (confirms what Charles wrote in mvstech.txt). How is this handled in B1 ?&lt;br /&gt;
&lt;br /&gt;
=Sprite parsing=&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;color:#FF0000&amp;quot;&amp;gt;This is a draft. The following information shouldn&#039;t be considered as exact.&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To do: Edit waveforms, FP and SP windows of the P BUS start 0.5mclk earlier (1.5,5,1.5,1.5,5,1.5 = 16).&lt;br /&gt;
&lt;br /&gt;
*LSPC runs at 24MHz, but generates signals on rising and falling edges (&amp;quot;48MHz&amp;quot;)&lt;br /&gt;
*Fast VRAM is 35ns (&amp;lt;1mclk), slow VRAM is 100ns (&amp;lt;2.5mclk, 3 ?)&lt;br /&gt;
*The fast VRAM reads always occur 1mclk (41.6ns) after address is set. Smallest access window is 1.5mclk.&lt;br /&gt;
[[file:timing_gpu1.png]]&lt;br /&gt;
&lt;br /&gt;
*FIXT: P23~16 is 0, P15~0 is S ROM address (+ external 2H1)&lt;br /&gt;
*SPRT: P23~0 is C ROM address (+ external CA4)&lt;br /&gt;
*LO: P23~16 is [[LO]] ROM data, P15~0 is LO address&lt;br /&gt;
*FP: P19~16 is the fix tile palette, rest is 0&lt;br /&gt;
*SP: P23~16 is the sprite tile palette, P15~8 is X position, P7~0 is ?&lt;br /&gt;
&lt;br /&gt;
*LSPC always starts filling up active sprite list A ($8600) each new frame &lt;br /&gt;
&lt;br /&gt;
Read sequence:&lt;br /&gt;
&lt;br /&gt;
Timing diagram when no sprites fall in the next scanline (no writes to sprite list):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Parse        ################################                                ################################&lt;br /&gt;
Render                                       ##########################                                      ##########################&lt;br /&gt;
24M    |&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_&lt;br /&gt;
Addr   | 600 |  200  | 201 | 202 | 203 | 204 |  681  | 00E | 20E | 40E | 600 |  205  | 206 | 207 | 208 | 209 |  682  | 00F | 20F | 40F&lt;br /&gt;
PCK1   ______|&#039;&#039;&#039;|___________________________________________________________|&#039;&#039;&#039;|_____________________________________________________&lt;br /&gt;
PCK1B  &#039;&#039;&#039;&#039;&#039;&#039;&#039;|____|&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;|___|&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
LOAD   |&#039;&#039;&#039;&#039;&#039;&#039;&#039;|_______________________|&#039;&#039;&#039;&#039;&#039;&#039;&#039;|_______________________|&#039;&#039;&#039;&#039;&#039;&#039;&#039;|_______________________|&#039;&#039;&#039;&#039;&#039;&#039;&#039;|_______________________&lt;br /&gt;
12M    __|&#039;&#039;&#039;|___|&#039;&#039;&#039;|___|&#039;&#039;&#039;|___|&#039;&#039;&#039;|___|&#039;&#039;&#039;|___|&#039;&#039;&#039;|___|&#039;&#039;&#039;|___|&#039;&#039;&#039;|___|&#039;&#039;&#039;|___|&#039;&#039;&#039;|___|&#039;&#039;&#039;|___|&#039;&#039;&#039;|___|&#039;&#039;&#039;|___|&#039;&#039;&#039;|___|&#039;&#039;&#039;|___|&#039;&#039;&#039;|_&lt;br /&gt;
2Pixel       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |&lt;br /&gt;
Read       ?       !     !     !     !     !       !     !     !     !     ?       !     !     !     !     !       !     !     !     !&lt;br /&gt;
What      1      2      2     2     2     2      3      4     5     6     1      2      2     2     2     2      3      4     5     6...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*1: Probably CPU acces slot with last address latched ($600)&lt;br /&gt;
*2: Read sprite Y position from SCB3 ($200+) to see if it&#039;s in next scanline&lt;br /&gt;
*3: Read sprite list ($600+) to get sprite #&lt;br /&gt;
*4: Read SCB2 zoom values ($000+)&lt;br /&gt;
*5: Read SCB3 Y/size/chain ($200+)&lt;br /&gt;
*6: Read SCB4 X ($400+)&lt;br /&gt;
&lt;br /&gt;
10 states in 16 cycles (or 5 in 8 cycles: 4-3-3-3-3).&lt;br /&gt;
&lt;br /&gt;
One scanline contains 1536mclk cycles or 96 sequences of 16mclk cycles.&lt;br /&gt;
&lt;br /&gt;
Half of the mclk cycles are reserved for Sprite parsing, the other half is for sprite rendering and CPU access.&lt;br /&gt;
&lt;br /&gt;
Each half has 96 x 5 = 480 states.&lt;br /&gt;
&lt;br /&gt;
For the parsing :&lt;br /&gt;
-----------------&lt;br /&gt;
SCB3 is read (from $200 to $380), each time there is a sprite match, a write state to the sprite list is inserted (apparently 2 states after the corresponding SCB3 read).&lt;br /&gt;
&lt;br /&gt;
Once SCB3 address $380 is reached, only $0000 write states to the sprite list are possible (in order to fill the rest of the sprite list with zeros).&lt;br /&gt;
&lt;br /&gt;
No matter how many sprites are matched in the scanline, we will always have 384 SCB3 read states and 96 sprite list write states.&lt;br /&gt;
&lt;br /&gt;
That explains why sprite #0 cannot be used : this is the value used to terminate the sprite list. It would have been smarter for SNK to use the value 511 instead...&lt;br /&gt;
&lt;br /&gt;
According to Charles MacDonald&#039;s document, the GPU always renders 96 sprites : the &amp;quot;filler&amp;quot; sprite #0 can be rendered many times per scanline.&lt;br /&gt;
&lt;br /&gt;
For the rendering :&lt;br /&gt;
-------------------&lt;br /&gt;
The state order is :&lt;br /&gt;
*1: Read sprite list ($600+) to get sprite #&lt;br /&gt;
*2: Read SCB2 zoom values ($000+)&lt;br /&gt;
*3: Read SCB3 Y pos/size/sticky ($200+)&lt;br /&gt;
*4: Read SCB4 X pos ($400+)&lt;br /&gt;
*5: Read/write from CPU&lt;br /&gt;
One remark : it is more logical to have SCB3 read before SCB2 because we need the sticky bit to make the decision of keeping the previous vertical shrink value or not.&lt;br /&gt;
&lt;br /&gt;
Is the sticky bit written to the sprite list along with the sprite number or did SNK waste an additionnal 8-bit temporary register in their design ?&lt;br /&gt;
&lt;br /&gt;
CPU access to High VRAM :&lt;br /&gt;
-------------------------&lt;br /&gt;
SNK says min. 12 68kclk between writes (so 24mclk). 1 write every 24mclk = 64 per scanline.&lt;br /&gt;
&lt;br /&gt;
Why 12 and not 8 ?&lt;br /&gt;
&lt;br /&gt;
68000 DTACK# logic is apparently only tied to GPU registers access. So CPU access during state #5 occurs asynchronously with the 68000 bus.&lt;br /&gt;
&lt;br /&gt;
My guess on the HW implementation is that during state #5, the GPU always reads the memory content pointed by REG_VRAMADDR and updates the read latch of REG_VRAMRW.&lt;br /&gt;
&lt;br /&gt;
If write latch REG_VRAMRW is written by the 68000, the next state #5 becomes a write access and REG_VRAMADDR is incremented by REG_VRAMMOD value.&lt;br /&gt;
&lt;br /&gt;
Even if a theoretical limit of 16mclk or 8 68kclk is possible, some additionnal cycles are needed for the address and data to propagate through the chip.&lt;br /&gt;
&lt;br /&gt;
Or maybe SNK has given the worst case scenario between slow Low VRAM and fast High VRAM ?&lt;br /&gt;
&lt;br /&gt;
Timing diagram when the sprite list is being filled:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
+5/8:&lt;br /&gt;
&lt;br /&gt;
0   5   2   7   4   1   6   3&lt;br /&gt;
|       |       |   |       |&lt;br /&gt;
  5   2   7   4   1   6   3  0&lt;br /&gt;
      |       |   |       |  |&lt;br /&gt;
0 1 2 3 4 5 6 7 8 9 A B C D E F&lt;br /&gt;
|       |     |     |     |    &lt;br /&gt;
&lt;br /&gt;
LLLLLLLLHHHHHHHHLLLLHHHHHHHHLLLL&lt;br /&gt;
HHHHHHLLLLLLLLHHHHLLLLLLLLHHHLLL&lt;br /&gt;
&lt;br /&gt;
             0 1 2 3 4 5 6 7 8 9 A B C D E F&lt;br /&gt;
             |       |     |     |     |&lt;br /&gt;
&lt;br /&gt;
Parse        ################################                                ################################&lt;br /&gt;
Render                                       ##########################                                      ##########################&lt;br /&gt;
24M    |&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_|&#039;|_&lt;br /&gt;
Addr   | 600 |  20F  | 210 | 211 | 600 | 601 |  684  | 005 | 205 | 405 | 600 |  212  | 213 | 602 | 603 | 214 |  685  | 006 | 206 | 406&lt;br /&gt;
PCK1   ______|&#039;&#039;&#039;|___________________________________________________________|&#039;&#039;&#039;|_____________________________________________________&lt;br /&gt;
PCK1B  &#039;&#039;&#039;&#039;&#039;&#039;&#039;|___|&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;|___|&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
LOAD   |&#039;&#039;&#039;&#039;&#039;&#039;&#039;|_______________________|&#039;&#039;&#039;&#039;&#039;&#039;&#039;|_______________________|&#039;&#039;&#039;&#039;&#039;&#039;&#039;|_______________________|&#039;&#039;&#039;&#039;&#039;&#039;&#039;|_______________________&lt;br /&gt;
12M    __|&#039;&#039;&#039;|___|&#039;&#039;&#039;|___|&#039;&#039;&#039;|___|&#039;&#039;&#039;|___|&#039;&#039;&#039;|___|&#039;&#039;&#039;|___|&#039;&#039;&#039;|___|&#039;&#039;&#039;|___|&#039;&#039;&#039;|___|&#039;&#039;&#039;|___|&#039;&#039;&#039;|___|&#039;&#039;&#039;|___|&#039;&#039;&#039;|___|&#039;&#039;&#039;|___|&#039;&#039;&#039;|___|&#039;&#039;&#039;|_&lt;br /&gt;
2Pixel       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |       |&lt;br /&gt;
/WE    &#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;|___|&#039;|___|&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;|___|&#039;|___|&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Read       ?       !     !     !                   !     !     !     !     ?       !     !                 !       !     !     !     !&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*R/W sequences: (2 write buffers ?)&lt;br /&gt;
*600 RRRWW... 600 RRWWR...&lt;br /&gt;
*600 WWRRW... 600 WRRWW... 600 RRWWR ... 600 RWWRR&lt;br /&gt;
*Even lines: Write to list A, Read from list B (Start of display)&lt;br /&gt;
*Odd lines: Write to list B, Read from list A&lt;br /&gt;
*In 16clk, 2 sprites SCB3 max. are checked to fill up sprite list , and 1 sprite&#039;s attributes are read for output&lt;br /&gt;
*384px * 4clk/px = 1536clk/line&lt;br /&gt;
*1536clk / 16clk = 96 sprites max/line&lt;br /&gt;
&lt;br /&gt;
*Available CPU R/W slots depending on parsing progress, safest is ? cycles&lt;br /&gt;
&lt;br /&gt;
==Slow (lower) VRAM==&lt;br /&gt;
&lt;br /&gt;
*Slow VRAM is 100ns (10MHz) and is read at ?&lt;br /&gt;
*4 slots per render cycle, 1 slot for CPU R/W (1 each 16 68k cycles)&lt;br /&gt;
&lt;br /&gt;
[[Category:Video system]]&lt;/div&gt;</summary>
		<author><name>Frenchshark</name></author>
	</entry>
</feed>