I took the time and used the venerable Visual2C02 to trace through 1.2 complete frames and see what kind of signal the PPU pushes out at all times.
Here's my results.
First, some terminology.
There are twelve signals that the PPU can produce. Some of those are identical. I will call them by the name that Visual2C02 uses.
So here's a scanline-by-scanline analysis of what the PPU produces.
Corollaries:
Colorburst signal is signal that toggles vid_burst_l and vid_burst_h after every 6 half-cycles.
Colors are produced as such (this is just a rehash of the information on http://wiki.nesdev.com/w/index.php/NTSC_video):
Missing tests:
Source material:
Here's my results.
First, some terminology.
There are twelve signals that the PPU can produce. Some of those are identical. I will call them by the name that Visual2C02 uses.
- vid_sync_l and vid_sync_h: aka. Sync and Black.
- vid_burst_l and vid_burst_h: Colorburst low and high.
- vid_luma0_l and vid_luma0_h: Signal levels equivalent to colors 0D and 00 respectively.
- vid_luma1_l and vid_luma1_h: Signal levels equivalent to colors 1D and 10 respectively.
- vid_luma2_l and vid_luma2_h: Signal levels equivalent to colors 2D and 20 respectively.
- vid_luma3_l and vid_luma3_h: Signal levels equivalent to colors 3D and 30 respectively.
- vid_luma3_h is identical to vid_luma2_h.
- vid_sync_h is identical to vid_luma1_l.
So here's a scanline-by-scanline analysis of what the PPU produces.
- hpos=4: On lines 0―239, the PPU starts producing color image (or grayscale image if $2001.0 was set).
- hpos=257: On lines 245―247, the PPU switches to vid_sync_h here. (Equalization pulses)
- hpos=260: On lines 0―239, the PPU starts producing background color again. (As on hpos=330.)
- hpos=271: On lines 0―241, the PPU switches to vid_sync_h here. (Front porch)
- hpos=280: On every line, the PPU switches to vid_sync_l here. (Horizontal sync)
- hpos=305: On lines 0―243 and 247―261, the PPU switches back to vid_sync_h here. (Begin of back porch)
- hpos=309: On lines 0―243 and 247―261, the PPU starts producing colorburst signal.
- hpos=324: On lines 0―243 and 247―261, the PPU switches back to vid_sync_h here. (End of back porch)
- hpos=329: On lines 0―240 and 261, the PPU starts producing background color in grayscale as if $2001.0 was set. This serves as the "pulse" that signifies the beginning of the image, as it is always different from the vid_sync_h in back porch.
- hpos=330: On lines 0―240 and 261, the PPU starts producing background color normally.
Corollaries:
- On scanlines 0―241, the PPU produces color image. This means 242 lines of image. The palette background color is rendered on all of them.
- On lines 242―244 and 248―261, the PPU produces long black lines (297 columns worth vid_sync_h). These black lines include the colorburst and sync as normal visible lines do, but they do not include the "pulse".
- On lines 245―247, the PPU produces very long sync pulses (318 columns).
Colorburst signal is signal that toggles vid_burst_l and vid_burst_h after every 6 half-cycles.
Colors are produced as such (this is just a rehash of the information on http://wiki.nesdev.com/w/index.php/NTSC_video):
- For colors $00,$10,$20,$30 (i.e. $X0), only vid_lumaX_h is produced.
- For colors $0D,$1D,$2D,$3D (i.e. $XD), only vid_lumaX_l is produced.
- For colors $XE and $XF, only vid_luma1_l is produced (i.e. treated as $1D; X is ignored).
- When grayscale is active, any color $XY is treated as if $X0, even if Y=D.
- For any other color $XY, vid_lumaX_l and vid_lumaX_h are alternated every 6 half-cycles with starting phase Y relative to colorburst.
Missing tests:
- I did not test whether the emphasis bits affect the background color or any other signals.
Source material:
Code:
Making the golden log:
http://www.qmtpro.com/~nes/chipimages/visual2c02/
Find "vid"
Select Post-render scanline, odd frame
Unclick Show sprite RAM contents
Unclick Animate during simulation
Trace these too: vid,vid_sync_l,vid_sync_h,vid_burst_l,vid_burst_h,vid_luma0_l,vid_luma0_h,vid_luma3_l,vid_luma3_h
Click RESET
Hide Chip Layout
Poke at 3F00: 3D (this causes SPIKE to be vid_luma3_h, BORDER to be vid_luma3_l, and GAME to be vid_luma0_h)
Poke at 0000: 16 * FF (ensures that game view will be rendered at color 00 rather than 3D)
NEXT SCANLINE
After every 3 scanlines or so, CTRL-A to mark everything, and paste it into a text file.
Clear log AND DON'T FORGET TO CLICK "Trace These Too" again!
The simulator becomes linearly slower and slower the longer the log is, so you have to clear it periodically.
Note: If you have to reset, you MUST poke the palette value again!
---
Signalwise summary:
vid_luma0_h (Game) at x= 4 on lines 0-239
vid_sync_h (Black) at x=257 on lines 245-247
vid_luma3_l (Border) at x=260 on lines 0-239
vid_sync_h (Black) at x=271 on lines 0-241
vid_sync_l (Sync) at x=280 on lines 0-261
vid_sync_h (Black) at x=305 on lines 0-243, 247-261
vid_burst_* (Burst) at x=309 on lines 0-243, 247-261
vid_sync_h (Black) at x=324 on lines 0-243, 247-261
vid_luma3_h (Pulse) at x=329 on lines 0-240, 261-261
vid_luma3_l (Border) at x=330 on lines 0-240, 261-261
---
Linewise summary (each "line" begins at column 280 on _previous_ scanline):
Line 240
[[ These six lines are manual simulation; extrapolation from next frame. ]]
row 239, column 280, half-cycle 4: vid_sync_l (Sync) (25 columns) - cycle -242
row 239, column 305, half-cycle 4: vid_sync_h (Black) (4 columns) - cycle -142
row 239, column 309, half-cycle 4: vid_burst_* (Burst) (15 columns) - cycle -126
row 239, column 324, half-cycle 4: vid_sync_h (Black) (5 columns) - cycle -66
row 239, column 329, half-cycle 4: vid_luma3_h (Pulse) (1 columns) - cycle -46
row 239, column 330, half-cycle 4: vid_luma3_l (Border) (282 columns) - cycle -42
[[Simulation begins here, using the "Post-render scanline, odd frame" option in Visual2c02.
These two first lines are garbage.]]
row 240, column 0, half-cycle 0: vid_sync_h (Black) (1.5 columns) - cycle 0
row 240, column 1, half-cycle 4: vid_luma3_l (Border) (270 columns) - cycle 6
row 240, column 271, half-cycle 4: vid_sync_h (Black) (9 columns) - cycle 1086
(341 cycles)
Line 241
row 240, column 280, half-cycle 4: vid_sync_l (Sync) (25 columns) - cycle 1122
row 240, column 305, half-cycle 4: vid_sync_h (Black) (4 columns) - cycle 1222
row 240, column 309, half-cycle 4: vid_burst_* (Burst) (15 columns) - cycle 1238
row 240, column 324, half-cycle 4: vid_sync_h (Black) (5 columns) - cycle 1298
row 240, column 329, half-cycle 4: vid_luma3_h (Pulse) (1 columns) - cycle 1318
row 240, column 330, half-cycle 4: vid_luma3_l (Border) (282 columns) - cycle 1322
row 241, column 271, half-cycle 4: vid_sync_h (Black) (9 columns) - cycle 2450
(341 cycles)
Lines 242-244
row 241, column 280, half-cycle 4: vid_sync_l (Sync) (25 columns) - cycle 2486
row 241, column 305, half-cycle 4: vid_sync_h (Black) (4 columns) - cycle 2586
row 241, column 309, half-cycle 4: vid_burst_* (Burst) (15 columns) - cycle 2602
row 241, column 324, half-cycle 4: vid_sync_h (Black) (297 columns) - cycle 2662
(341 cycles)
Lines 245-247
row 244, column 280, half-cycle 4: vid_sync_l (Sync) (318 columns) - cycle 6578
row 245, column 257, half-cycle 4: vid_sync_h (Black) (23 columns) - cycle 7850
(341 cycles)
Lines 248-261
row 247, column 280, half-cycle 4: vid_sync_l (Sync) (25 columns) - cycle 10670
row 247, column 305, half-cycle 4: vid_sync_h (Black) (4 columns) - cycle 10770
row 247, column 309, half-cycle 4: vid_burst_* (Burst) (15 columns) - cycle 10786
row 247, column 324, half-cycle 4: vid_sync_h (Black) (297 columns) - cycle 10846
(341 cycles)
Line 0
row 261, column 280, half-cycle 4: vid_sync_l (Sync) (25 columns) - cycle 29766
row 261, column 305, half-cycle 4: vid_sync_h (Black) (4 columns) - cycle 29866
row 261, column 309, half-cycle 4: vid_burst_* (Burst) (15 columns) - cycle 29882
row 261, column 324, half-cycle 4: vid_sync_h (Black) (5 columns) - cycle 29942
row 261, column 329, half-cycle 4: vid_luma3_h (Pulse) (1 columns) - cycle 29962
row 261, column 330, half-cycle 4: vid_luma3_l (Border) (14 columns) - cycle 29966
row 0, column 4, half-cycle 4: vid_luma0_h (Game) (256 columns) - cycle 30022
row 0, column 260, half-cycle 4: vid_luma3_l (Border) (11 columns) - cycle 31046
row 0, column 271, half-cycle 4: vid_sync_h (Black) (9 columns) - cycle 31090
(340 cycles; column 0 was skipped)
Lines 1-239
row 0, column 280, half-cycle 4: vid_sync_l (Sync) (25 columns) - cycle 31126
row 0, column 305, half-cycle 4: vid_sync_h (Black) (4 columns) - cycle 31226
row 0, column 309, half-cycle 4: vid_burst_* (Burst) (15 columns) - cycle 31242
row 0, column 324, half-cycle 4: vid_sync_h (Black) (5 columns) - cycle 31302
row 0, column 329, half-cycle 4: vid_luma3_h (Pulse) (1 columns) - cycle 31322
row 0, column 330, half-cycle 4: vid_luma3_l (Border) (15 columns) - cycle 31326
row 1, column 4, half-cycle 4: vid_luma0_h (Game) (256 columns) - cycle 31386
row 1, column 260, half-cycle 4: vid_luma3_l (Border) (11 columns) - cycle 32410
row 1, column 271, half-cycle 4: vid_sync_h (Black) (9 columns) - cycle 32454
(341 cycles)
Line 240-241
row 239, column 280, half-cycle 4: vid_sync_l (Sync) (25 columns) - cycle 357122
row 239, column 305, half-cycle 4: vid_sync_h (Black) (4 columns) - cycle 357222
row 239, column 309, half-cycle 4: vid_burst_* (Burst) (15 columns) - cycle 357238
row 239, column 324, half-cycle 4: vid_sync_h (Black) (5 columns) - cycle 357298
row 239, column 329, half-cycle 4: vid_luma3_h (Pulse) (1 columns) - cycle 357318
row 239, column 330, half-cycle 4: vid_luma3_l (Border) (282 columns) - cycle 357322
row 240, column 271, half-cycle 4: vid_sync_h (Black) (9 columns) - cycle 358450
(341 cycles)
Lines 242-244
row 241, column 280, half-cycle 4: vid_sync_l (Sync) (25 columns) - cycle
row 241, column 305, half-cycle 4: vid_sync_h (Black) (4 columns) - cycle
row 241, column 309, half-cycle 4: vid_burst_* (Burst) (15 columns) - cycle
row 241, column 324, half-cycle 4: vid_sync_h (Black) (297 columns) - cycle
(341 cycles)
Lines 245-247
row 244, column 280, half-cycle 4: vid_sync_l (Sync) (318 columns) - cycle
row 245, column 257, half-cycle 4: vid_sync_h (Black) (23 columns) - cycle
(341 cycles)
Lines 248-261
row 247, column 280, half-cycle 4: vid_sync_l (Sync) (25 columns) - cycle 368034
row 247, column 305, half-cycle 4: vid_sync_h (Black) (4 columns) - cycle 368134
row 247, column 309, half-cycle 4: vid_burst_* (Burst) (15 columns) - cycle 368150
row 247, column 324, half-cycle 4: vid_sync_h (Black) (297 columns) - cycle 368210
(341 cycles)
Lines 0-239
row 261, column 280, half-cycle 4: vid_sync_l (Sync) (25 columns) - cycle 387130
row 261, column 305, half-cycle 4: vid_sync_h (Black) (4 columns) - cycle 387230
row 261, column 309, half-cycle 4: vid_burst_* (Burst) (15 columns) - cycle 387246
row 261, column 324, half-cycle 4: vid_sync_h (Black) (5 columns) - cycle 387306
row 261, column 329, half-cycle 4: vid_luma3_h (Pulse) (1 columns) - cycle 387326
row 261, column 330, half-cycle 4: vid_luma3_l (Border) (15 columns) - cycle 387330
row 0, column 4, half-cycle 4: vid_luma0_h (Game) (256 columns) - cycle 387390
row 0, column 260, half-cycle 4: vid_luma3_l (Border) (11 columns) - cycle 388414
row 0, column 271, half-cycle 4: vid_sync_h (Black) (9 columns) - cycle 388458
(341 cycles)
http://www.qmtpro.com/~nes/chipimages/visual2c02/
Find "vid"
Select Post-render scanline, odd frame
Unclick Show sprite RAM contents
Unclick Animate during simulation
Trace these too: vid,vid_sync_l,vid_sync_h,vid_burst_l,vid_burst_h,vid_luma0_l,vid_luma0_h,vid_luma3_l,vid_luma3_h
Click RESET
Hide Chip Layout
Poke at 3F00: 3D (this causes SPIKE to be vid_luma3_h, BORDER to be vid_luma3_l, and GAME to be vid_luma0_h)
Poke at 0000: 16 * FF (ensures that game view will be rendered at color 00 rather than 3D)
NEXT SCANLINE
After every 3 scanlines or so, CTRL-A to mark everything, and paste it into a text file.
Clear log AND DON'T FORGET TO CLICK "Trace These Too" again!
The simulator becomes linearly slower and slower the longer the log is, so you have to clear it periodically.
Note: If you have to reset, you MUST poke the palette value again!
---
Signalwise summary:
vid_luma0_h (Game) at x= 4 on lines 0-239
vid_sync_h (Black) at x=257 on lines 245-247
vid_luma3_l (Border) at x=260 on lines 0-239
vid_sync_h (Black) at x=271 on lines 0-241
vid_sync_l (Sync) at x=280 on lines 0-261
vid_sync_h (Black) at x=305 on lines 0-243, 247-261
vid_burst_* (Burst) at x=309 on lines 0-243, 247-261
vid_sync_h (Black) at x=324 on lines 0-243, 247-261
vid_luma3_h (Pulse) at x=329 on lines 0-240, 261-261
vid_luma3_l (Border) at x=330 on lines 0-240, 261-261
---
Linewise summary (each "line" begins at column 280 on _previous_ scanline):
Line 240
[[ These six lines are manual simulation; extrapolation from next frame. ]]
row 239, column 280, half-cycle 4: vid_sync_l (Sync) (25 columns) - cycle -242
row 239, column 305, half-cycle 4: vid_sync_h (Black) (4 columns) - cycle -142
row 239, column 309, half-cycle 4: vid_burst_* (Burst) (15 columns) - cycle -126
row 239, column 324, half-cycle 4: vid_sync_h (Black) (5 columns) - cycle -66
row 239, column 329, half-cycle 4: vid_luma3_h (Pulse) (1 columns) - cycle -46
row 239, column 330, half-cycle 4: vid_luma3_l (Border) (282 columns) - cycle -42
[[Simulation begins here, using the "Post-render scanline, odd frame" option in Visual2c02.
These two first lines are garbage.]]
row 240, column 0, half-cycle 0: vid_sync_h (Black) (1.5 columns) - cycle 0
row 240, column 1, half-cycle 4: vid_luma3_l (Border) (270 columns) - cycle 6
row 240, column 271, half-cycle 4: vid_sync_h (Black) (9 columns) - cycle 1086
(341 cycles)
Line 241
row 240, column 280, half-cycle 4: vid_sync_l (Sync) (25 columns) - cycle 1122
row 240, column 305, half-cycle 4: vid_sync_h (Black) (4 columns) - cycle 1222
row 240, column 309, half-cycle 4: vid_burst_* (Burst) (15 columns) - cycle 1238
row 240, column 324, half-cycle 4: vid_sync_h (Black) (5 columns) - cycle 1298
row 240, column 329, half-cycle 4: vid_luma3_h (Pulse) (1 columns) - cycle 1318
row 240, column 330, half-cycle 4: vid_luma3_l (Border) (282 columns) - cycle 1322
row 241, column 271, half-cycle 4: vid_sync_h (Black) (9 columns) - cycle 2450
(341 cycles)
Lines 242-244
row 241, column 280, half-cycle 4: vid_sync_l (Sync) (25 columns) - cycle 2486
row 241, column 305, half-cycle 4: vid_sync_h (Black) (4 columns) - cycle 2586
row 241, column 309, half-cycle 4: vid_burst_* (Burst) (15 columns) - cycle 2602
row 241, column 324, half-cycle 4: vid_sync_h (Black) (297 columns) - cycle 2662
(341 cycles)
Lines 245-247
row 244, column 280, half-cycle 4: vid_sync_l (Sync) (318 columns) - cycle 6578
row 245, column 257, half-cycle 4: vid_sync_h (Black) (23 columns) - cycle 7850
(341 cycles)
Lines 248-261
row 247, column 280, half-cycle 4: vid_sync_l (Sync) (25 columns) - cycle 10670
row 247, column 305, half-cycle 4: vid_sync_h (Black) (4 columns) - cycle 10770
row 247, column 309, half-cycle 4: vid_burst_* (Burst) (15 columns) - cycle 10786
row 247, column 324, half-cycle 4: vid_sync_h (Black) (297 columns) - cycle 10846
(341 cycles)
Line 0
row 261, column 280, half-cycle 4: vid_sync_l (Sync) (25 columns) - cycle 29766
row 261, column 305, half-cycle 4: vid_sync_h (Black) (4 columns) - cycle 29866
row 261, column 309, half-cycle 4: vid_burst_* (Burst) (15 columns) - cycle 29882
row 261, column 324, half-cycle 4: vid_sync_h (Black) (5 columns) - cycle 29942
row 261, column 329, half-cycle 4: vid_luma3_h (Pulse) (1 columns) - cycle 29962
row 261, column 330, half-cycle 4: vid_luma3_l (Border) (14 columns) - cycle 29966
row 0, column 4, half-cycle 4: vid_luma0_h (Game) (256 columns) - cycle 30022
row 0, column 260, half-cycle 4: vid_luma3_l (Border) (11 columns) - cycle 31046
row 0, column 271, half-cycle 4: vid_sync_h (Black) (9 columns) - cycle 31090
(340 cycles; column 0 was skipped)
Lines 1-239
row 0, column 280, half-cycle 4: vid_sync_l (Sync) (25 columns) - cycle 31126
row 0, column 305, half-cycle 4: vid_sync_h (Black) (4 columns) - cycle 31226
row 0, column 309, half-cycle 4: vid_burst_* (Burst) (15 columns) - cycle 31242
row 0, column 324, half-cycle 4: vid_sync_h (Black) (5 columns) - cycle 31302
row 0, column 329, half-cycle 4: vid_luma3_h (Pulse) (1 columns) - cycle 31322
row 0, column 330, half-cycle 4: vid_luma3_l (Border) (15 columns) - cycle 31326
row 1, column 4, half-cycle 4: vid_luma0_h (Game) (256 columns) - cycle 31386
row 1, column 260, half-cycle 4: vid_luma3_l (Border) (11 columns) - cycle 32410
row 1, column 271, half-cycle 4: vid_sync_h (Black) (9 columns) - cycle 32454
(341 cycles)
Line 240-241
row 239, column 280, half-cycle 4: vid_sync_l (Sync) (25 columns) - cycle 357122
row 239, column 305, half-cycle 4: vid_sync_h (Black) (4 columns) - cycle 357222
row 239, column 309, half-cycle 4: vid_burst_* (Burst) (15 columns) - cycle 357238
row 239, column 324, half-cycle 4: vid_sync_h (Black) (5 columns) - cycle 357298
row 239, column 329, half-cycle 4: vid_luma3_h (Pulse) (1 columns) - cycle 357318
row 239, column 330, half-cycle 4: vid_luma3_l (Border) (282 columns) - cycle 357322
row 240, column 271, half-cycle 4: vid_sync_h (Black) (9 columns) - cycle 358450
(341 cycles)
Lines 242-244
row 241, column 280, half-cycle 4: vid_sync_l (Sync) (25 columns) - cycle
row 241, column 305, half-cycle 4: vid_sync_h (Black) (4 columns) - cycle
row 241, column 309, half-cycle 4: vid_burst_* (Burst) (15 columns) - cycle
row 241, column 324, half-cycle 4: vid_sync_h (Black) (297 columns) - cycle
(341 cycles)
Lines 245-247
row 244, column 280, half-cycle 4: vid_sync_l (Sync) (318 columns) - cycle
row 245, column 257, half-cycle 4: vid_sync_h (Black) (23 columns) - cycle
(341 cycles)
Lines 248-261
row 247, column 280, half-cycle 4: vid_sync_l (Sync) (25 columns) - cycle 368034
row 247, column 305, half-cycle 4: vid_sync_h (Black) (4 columns) - cycle 368134
row 247, column 309, half-cycle 4: vid_burst_* (Burst) (15 columns) - cycle 368150
row 247, column 324, half-cycle 4: vid_sync_h (Black) (297 columns) - cycle 368210
(341 cycles)
Lines 0-239
row 261, column 280, half-cycle 4: vid_sync_l (Sync) (25 columns) - cycle 387130
row 261, column 305, half-cycle 4: vid_sync_h (Black) (4 columns) - cycle 387230
row 261, column 309, half-cycle 4: vid_burst_* (Burst) (15 columns) - cycle 387246
row 261, column 324, half-cycle 4: vid_sync_h (Black) (5 columns) - cycle 387306
row 261, column 329, half-cycle 4: vid_luma3_h (Pulse) (1 columns) - cycle 387326
row 261, column 330, half-cycle 4: vid_luma3_l (Border) (15 columns) - cycle 387330
row 0, column 4, half-cycle 4: vid_luma0_h (Game) (256 columns) - cycle 387390
row 0, column 260, half-cycle 4: vid_luma3_l (Border) (11 columns) - cycle 388414
row 0, column 271, half-cycle 4: vid_sync_h (Black) (9 columns) - cycle 388458
(341 cycles)