Retrochallenge 2018/04 (Now in COLOR)
Refraction for the Atari 2600

Episode 11: Color TV Systems

I haven't posted the game code in its entirety recently, because it has meanwhile grown to an extent, where it became unwieldlily to handle in a web page, like this one. However, you may recall that there was a section on the top to configure some parameters for screen geometry and colors for either NTSC or PAL 50Hz. Time, to put this to a test and explore, what it does mean to set up a game for multiple TV systems.

PAL/50 — Another 36 Visible Scan Lines

PAL doesn't just come with its own set of color definitions and just at 50 Hz refresh rate, it also comes at 312 lines. (48 lines of VBLANK, including VSYNC, 228 visible kernel scan lines and 36 lines of overscan, according to the Stella Developer's Guide). This means, we have to configure the game for this, otherwise, it'll be vertically squished, since the scan lines are stacked at a higher density. (That is, on a real, analog TV, not on your digital LCD emulation. The latter will probably scale up the image and increase the width to make up for the geometry in relation to its hardware pixels.)

Since the VCS / Atari 2600 doesn't have a concept of any vertical extent, it's impossible to have some kind of auto detection in the game ROM. We'll have to assemble dedicated binaries for each of the TV systems. The good news is that any adoptions are concerning the assembler code only and can be done by setting up some basic symbols in a few conditional branches. Notably, the horizontal resolution is the same on all systems. This is, because it's analog video and there are no hardware pixels at all. What are pixels on the screen, are just the color clocks of the TIA, and this is running on the same speed for all the various systems. Therefor, the number of pixels per line will be the same, regardless of the code being set up for NTSC, PAL/50, PAL/60, or even SECAM.

(In theory, individual color TVs do have a maximum color resolution, varying from model to model, which is due to the density of the color triplets which make up the phosphor coating, but this has only small effect on the luminance. B&W sets, on the other hand, have just a single, even coating of phosphor and therefor no limit to the theoretical resolution. In practis, there may be some limits set by the circuitry and its capability to quickly adjust the intensity of the electron gun in relation to the intensity of the incoming signal. However, while some detail may be lost in this case, the tube will still produce a viable image.)

Obviously, we'll have to account for the extra amount of visible scan lines, over wich we are iterating in our kernel routines. Less obviously, we'll also have to adjust some of the objects, especially those which are maintaining some kind of proportion to the horizontal extent of the screen image. This is the ball, which is meant to be roughly square, and the top and bottom borders of the playfield, which should maintain a certain proportion to the borders at the sides. Both of them will receive an extra line. And we'll have to adjust some of the ranges (bottom extent of the vertical positioning of the ships and the ball), but this can be easily done by a bit of math in the assembler. (E.g., "BallMaxY = PlayfieldHeight - BallHeight", where both right hand values are set up respectively to the screen geometry used by the TV system.)

So, let's put it to a test. Let's uncomment the line defining PAL and bravely hit enter on the comand line. Boom!

Just kidding. The good news is that it assembles at all. Since we are using extensive padding on the various sprites to implement our high-speed kernel, we were not really sure about this. However, our code doesn't hit the jump vectors for reset and interrupts on the top of the ROM. There's still ample space, or, at least, some.

So, let's have a look at it:

Refraction, PAL/50, color loss

First view of our title screen on PAL/50.

Refraction, PAL/50 playfield

Our playfield on PAL/50.
(Mind that the Stella emulator is using a cool plette for PAL, while using a warm one for NTSC.)

Pitfall II — Not by ActiVision

While it generally works, there is no color. However, if we contiue to the game itself, the playfield is drawn in color. Notably, the same assembler symbols are used for both of the screens/scenes. What is going on?

A bit of Google search provides the answer. Turns out, PAL color loss is a thing. If a field doesn't have an even number of scan lines in PAL, the image will degrade towards black and white, which is also recreated by the Stella emulator. (If this would happen to the extent mimicked by the emulator on a real, analog TV, may be open to debate, compare recent experiments. — Crazy idea: Could we generate an extended, desutarated palette for PAL by simply adding an extra scan line every second frame?)

As it happens, we do have a scan line too many in our title screen, which went unnoticed, since the image is trailing out in black at the bottom. So, correcting this, we eventually get:

Refraction, PAL/50, memory alignment failure

Second view of our title screen on PAL/50.

Here they are, our colors. — However, they are not exactly the colors of our NTSC version, while they are meant to be the same. In fact, the color definitions do match exactly in the color charts. As it happens, Stella is emulating a warm palette for NTSC and a cool one for PAL (the same one, the Javatari online-emulator is also using for NTSC). But these are mere trifles.

There's something other going on. Have a close look at the logo, there's some distortion. In fact, it's only happening from time to time, as some kind of irritating flicker, corresponing to the vertical position of the ball.

Remember what we said about the horizontal rendering being unaffected by the choice of the TV system? While this is true on the hardware level and as far as the TIA is concerned, it's not true for our software. Since we require some spefic memory alignements to keep our code up to speed and the cycle count just perfect, the extra amount of padding (which is proportional to the scan line count) added to our sprites in memory also introduces some crossings of page boundaries for indexed memory lookups, when our code is executed. So we'll have to adjust our memory alignments specifically for PAL/50, as well…

Overall, our code behaves much more pickier in PAL/50 than in NTSC.

Refraction, PAL/50

Finally, our title screen on PAL/50.

Pal/60

While we're at it, we also add some code for an option to compile for PAL/60. This is using PAL color definitions for PAL TV sets, but the same frequency (and hence geometry) as NTSC. In fact, TV sets are quite robust when it comes to the number of scan lines and the refresh rate, and most, if not all PAL TVs will happily churn along at 60Hz. This format is apparently popular for the Atari 2600, since it allows to run a game on a PAL console (maybe with minor vertical compressions) with the same timings as in NTSC. Also, it reduces the scaling problems on modern displays with hardware pixels, which will (presumedly) show exactly the same image as in NTSC.

By this, our configuration code has grown to the point of becoming unwiedly:


    SEG.U config

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Constants
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; tv standard specifics
; uncomment for PAL (0: NTSC, 60: PAL60, else PAL50)
;PAL = 60


    ifnconst  PAL          ; set default value
PAL = 0
    endif


; timing and geometry

; number of visible kernel lines

    if  PAL && PAL != 60   ; PAL50
ScanLines = 228
    else                   ; NTSC, PAL60
ScanLines = 192
    endif


    if ScanLines == 192
;----------------------------- NTSC, PAL60

; 262 lines: 3+37 VBlank, 192 kernel, 30 overscan
; timers (@ 64 cycles)
;  VBlank    43 * 64 = 2752 cycles = 36.21 lines
;  Overscan  35 * 64 = 2240 cycles = 29.47 lines

T64VBlank      =  43
T64Overscan    =  35

BorderHeight   =   6
BallHeight     =   7

;-----------------------------
    else
;----------------------------- PAL50

; 312 lines: 3+45 VBlank, 228 kernel, 36 overscan
; timers (@ 64 cycles)
;  VBlank    53 * 64 = 3392 cycles = 44.63 lines
;  Overscan  42 * 64 = 2688 cycles = 35.36 lines

T64VBlank      =  43
T64Overscan    =  35

BorderHeight   =   7
BallHeight     =   8

;-----------------------------
    endif


; colors

    if PAL == 0
;----------------------------- NTSC

BorderClr      = $64  ; purple
ScoreClr       = $1A  ; yellow
ScoreClrDk     = $18  ; medium gold
PlayerClr      = $0C  ; light grey
ResetClr       = $60  ; dark purple
SplashBallClr  = $8C  ; lt blue

;-----------------------------
    else
;----------------------------- PAL

BorderClr      = $C4
ScoreClr       = $2A
ScoreClrDk     = $28
PlayerClr      = $0C
ResetClr       = $C0
SplashBallClr  = $BC

;-----------------------------
    endif

; general definitions


;(...)

; splash geometry

    if  ScanLines ==  192
;----------------------------- default (NTSC, PAL60)

LogoVMargin = 36
SplashBounceHeight = LogoVMargin + 39
SplashBottomRoundOff = 10
;-----------------------------
    else
;----------------------------- PAL50
LogoVMargin = 44
SplashBounceHeight = LogoVMargin + 50
SplashBottomRoundOff = 6
;-----------------------------
    endif


;(...)


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Splash Visible Kernel

LogoTop = ScanLines/3 + 6 - LogoVMargin
LogoTopArea = LogoVMargin + Logo_height - 1
LogoReflectionHeight = 12
LogoBottomArea = LogoVMargin - LogoReflectionHeight
LogoAreaBottom = LogoTop + LogoTopArea + LogoBottomArea
SignatureTop = ScanLines - [ScanLines/3] + 2
SignatureVOffset = SignatureTop - LogoAreaBottom
YearVOffset = 5
SignatureAreaHeight = Signature_height-1 + YearVOffset + Year_height-1
SignatureBottom = SignatureTop + SignatureVOffset + SignatureAreaHeight
SplashTrailHeight = ScanLines - 1 - SignatureBottom + SplashBottomRoundOff


;(...)

In the end, I'm at the verge of deciding to strip support for PAL/50. It's probably not worth it. There are still other adjustments to be made, related to frequency. The game feels a bit sluggish in PAL/50, because it's running at a 6th less of the speed. We still have to adjust all the values for any vertical motions to this, by increasing them by a 6th. While we prepared for this by putting these values in symbols and tables, which may be adjusted easily, we also found that these motion deltas should come in steps of half a pixel in order to produce the impression of smooth motion. Adding a sixth of a pixel, on the other hand, will just produce a jump by an extra pixel every six pixels travelled. The resulting impression is not that much of increased speed, but of motion jitter. Moreover, considering image upscaling for modern displays would even increase the troubles. — We begin to embrace the idea of PAL/60 and postpone any further fiddling with memory alignments.

Refraction, PAL/60 and NTSC

Left PAL/60, right NTSC. Mind the differences in color temperature applied by the emulator.
Is the truth in the middle?

Trivia

The Atari VCS/2600 NTSC/PAL scanline list at Digital Press provides an extensive list of games and the scan lines per frame/field actually used by them.

Here are the overall statistics:

TVScan LinesObserved
SystemStandardMinMaxMost/Median
NTSC262238290262
PAL312258336312

As we may observe, there is some variety. Mind that these are the total numbers of scan lines (including VBLANK and overscan) and not just those of the visible kernel. Some of the games have even varying scan line counts, be it varying over frames or scenes. In practice, we haven't to adhere to the standard exactly, but we should try to maintain a consistent count for any of the screens/scenes of our game. However, make sure that there's an even scan line count for PAL, else your game may be subject to PAL color loss.

 

 

Next:  Episode 12: Sound (I) / Introducing “Studio2600”

Previous:   Episode 10: Title Graphics

Back to the index.

— This series is part of Retrochallenge 2018/04. —