40

On the Apple II there's an interesting way to add a little color to the bitmap, since the high bit select the palette used for the three-and-a-half pixels represented by the byte. Like this:

0: Black, green, purple, white
1: Black, blue,  orange, white

Now this leaves room for three whole pixels, plus one bit which presumably forms half a pixel, the other half being formed by bit 6 of the next byte. Like this:

0 00 01 10 1 
1 1 00 01 10

So that's a single pixel encoded by the 1 at the end of the first byte and the 1 adjacent to the MSB of the next byte. The two bytes encode seven pixels, I think they are black, purple, green, white, black, blue, orange.

Since it crosses the boundary between two bytes, what determines which colors that pixel in the middle can be?

4 Answers 4

55

A nice one - and coming up every now or then.


TL;DR

The Apple IIs video logic produces a B&W bitstream at the right frequency to bedazzle an NTSC TV set in a way to make it 'see' colour. The colours produced are based on the way the bitstream creates interferences that are detected by the TV set as colour information.

The encoding is rather a series of 7 bits that can be on or off, producing black when off and white when on. Due to the way colour is created in NTSC, fast switching a signal between black and white, at the right speed, creates an interference detected as colour signal. The effect is based on the pixel clock being exactly twice the NTSC colour carrier.

Bottom line, colour is not determinated by pixel groups at fixed positions, but by their sequence in the bitstream as a whole and its timing.


A More Detailed Look at Apple IIs Highres Graphics.

Let's start wth the most fundamental misunderstanding here:

The Highres Colour is NOT Based on Fixed Pixel Groups but a Bitstream

(The idea of fixed pixels forming colours will be called pixel theory)

It's All Black and White

Also, Apple II highres graphics aren't colour and never have been. It's just a precisely timed B&W signal that makes the TV set believe there is some colour information, and it tries to go along.

The primary data is stored in the lower 7 bits of each display byte. Combining the 40 bytes of each line gives 280 bits, forming a stream of 280 independent pixels. Try to fill the screen with values of $2A77 and it'll produce a series of 140 black and white columns. At least on a VBS (not CVBS) screen (monochrome composite fed CRT).

If the 7th (highest) bit of one byte is set, all its 7 displayed bits are delayed by half a pixel time. Again a VBS screen is a good way to prove it. Filling one line (40 bytes) for example with $05 and the next one with $85 and so on will give a set of vertical wavy vertical lines, every other clearly shifted by half a pixel to the right. Try different values, the effect will stay the same as long as the top bit is alternating between lines.

Feeding B&W to a Colour Screen isn't always B&W

The fun part comes when this signal gets fed into an NTSC TV set (after being modulated to some channel) or an NTSC CVBS monitor, which will now show colours as the sharp rising black to white flanks produce the right signal to make the colour circuit believe there is some colour information.

It is still only the flank of a black to white or white to black transition, but rising fast enough at the right moment to triggers the circuit. That's why only 01 or 10 combinations will produce a colour, while sequences of consecutive 0 or 1 will produce continous black or white. All of this is completely independent of any 'bitcell' or whatsoever and only based on the stream.

What Colour is Black and White?

What colour the circuitry sees (and displays) depends on the timing of the flank in relation to the colour burst signal at the start of each line. Since the pixel clock is exactly twice the NTSC colour carrier, a table can be made for all 4 states within the stream (*1) as they will be seen in the YPbPr space (*3):

(Y is luminiscance, Pb/Pr are the colour components)

`0` to `0` -> Y = 0.0; Pb = 0; Pr = 0
`0` to `1` -> Y = 0.5; Pb = 1; Pr = 1
`1` to `0` -> Y = 0.5; Pb = -1; Pr = -1
`1` to `1` -> Y = 1.0; Pb = 0; Pr = 0

As a result all sequences of zeros are black, all ones are white, only where the content changes is a colour produced. In case of 01 it's full blue plus full red creating violet, while 10 has no blue and no red, making it green (*2).

Now, if we want to have a green line, we need to produce a bit pattern of consecutive 0 to 1 changes, like 01010101010101.... Since there are only 7 bits used within a byte, this results in two alternating values:

0.0101010 and 0.1010101 or $2A and $55

This gives a beautiful green line, doesn't it? And alternating black&white dots on a VBS screen.

Not all Colours Match

So, even before going into the second colour set (with bit 7 set), heck, even with just one byte, there's a problem when displaying two colours like green and violet next to each other. Let's take a value of 0.011001x (we're ignoring the last bit for simplicity). According to the 'pixel theory' this should result in two violet pixels with a green one in the middle. Right?

Well, no. It is a (bit)stream sent to an analogue circuit. This stream consists of five transitions, each changes the decoding done by the colour circuitry of an NTSC TV:

`01` -> Green
`11` -> White
`10` -> Violet
`00` -> Black
`01` -> Green

So what we get are not three but five coloured sections(*4) along the space of six B&W pixels. Usually this is called a 'fringe' or 'border' effect. Something well known from Apple games trying to produce filled graphics. Guess why so many games use kind of 'dithered' graphics?

It's important to note, that this effect is not tied to even or odd bytes, but will happen whenever the bit stream consists of any of these combinations.

A Second Set of Colours

Now for the high bit and its second colour set. As we've already seen, setting the high bit shifts the timing of the B&W pixels sent out by half a pixel clock, or fourth of an NTSC colour carrier. Now the change happens not half way through a colour cycle but three quarters, resulting in a different interpretation table:

`0` to `0` -> Y = 0.0; Pb = 0; Pr = 0
`0` to `1` -> Y = 0.5; Pb = 1; Pr = -1
`1` to `0` -> Y = 0.5; Pb = -1; Pr = 1
`1` to `1` -> Y = 1.0; Pb = 0; Pr = 0

As before, all sequences of zeros are black, all ones are white and only where the level changes, a colour is produced. In case of 01 it's now full blue plus no red creating a clear blue, and 10 having no blue but full red, making it orange.

A line of $2A and $55 will now be solid orange.

Doing the same 0.011001x will produce the same 5 coloured regions as before, just with blue and orange swapped with violet and green.

Flipping the Bit

Having gone so far, it should be obvious what a change of bit 7 between two bytes should do. Nothing unexpected, right? It's the same stream effect as before, just across a byte region.

No? Ok, let's just go through it once more, now using the example from the question. We've got two bytes, assuming at the begin of a line (or any even location):

0.0001101 and 1.1000110 producing a bitstream of 0001101.1000110 with a shift of half a pixel clock after the 7th bit. Result:

`00` -> Black
`00` -> Black
`01` -> Green
`11` -> White
`10` -> Violet
`01` -> Green
`11` -> White (shift happenes)
`10` -> Orange
`00` -> Black
`00` -> Black
`01` -> Blue
`11` -> White
`10` -> Orange

As expected, nothing happens, as the 11 sequence at the border will still be white. Just the resulting colour section will be a half a clock pixel longer. Similar if there would be two zeros. So what's with a 01 or a 10? Simple, it will always be the colour of the byte the 1 bit is in. Why? well, in case of 01 the rising flank will be shifted (or not) depending of the high bit of the second byte, and in case of 10 its flank is decoded according to the bit of the first byte.

Simple, isn't it?


Conclusion:

The Apple IIs highres graphics (and similar lowres) isn't anything like traditional bitmap data, but more of a digital made B&W stream with fast raising flanks, made to fool the analogue colour circuit of an NTSC TV.


*1 - Keep in mind, this is not a two bit pixel cell, but rather one position in a continous decoding - after all, TV is analogue, even when fed from a digital source.

*2 - Sounds confusing, but it's rather simple. We have 4 components, the mixture of the three colours and total brightness. Since they are connected we only need to transport three of them, as the fourth can be calculated from the others. So with a certain brightness and two of the colours given (here blue and red) the third can be calculated.

*3 - I'm using YPbPr here as this is today the more common, discrete notation. Classic documentation would use a colour circle, an analogue way to describe the same data with a single value..

*4 - Calling the colour sections 'pixels' at this stage isn't anymore appropriate, is it?

23
  • 3
    @hobbs: If a person on television is wearing a herringbone tweed jacket whose pattern sometimes hits the chroma frequency, does that mean that part of the he video frame "contains" color information there, or that it contains spectral content which is indistinguishable from color information [even though it is actually encoding luminance information]? Many composite-video systems generate separate chroma and luminance signals and mix them; the Apple just generates an all-in-one signal that from its perspective just represents luminance.
    – supercat
    Apr 17, 2018 at 17:42
  • 3
    @Raffzahn I was asking just about the generated signal, measured as it leaves the Apple II. But I think that amounts to a question hijack, as the real question is about colours produced, so it's more like effect on Apple + decoder, and I don't really care about the decoder. So I'll withdraw the question from here, and post it formally if I can't just figure out an answer on my own. Apologies for the digression.
    – Tommy
    Apr 17, 2018 at 18:03
  • 6
    @supercat but from its perspective it clearly doesn't just resemble luminance, since it carefully uses it to generate color. What we have here is akin to the world's crudest DC-to-3.58MHz SDR, generating CVBS at one bit per sample.
    – hobbs
    Apr 17, 2018 at 18:33
  • 3
    @supercat (as for the jacket thing, it's the broadcaster's job to make sure that luma information isn't encoded where the color should be, as it can't rightly be interpreted as luma. A correct color NTSC signal simply does not encode luminance above a certain frequency.)
    – hobbs
    Apr 17, 2018 at 18:35
  • 5
    "Any sufficiently old Apple technology is indistinguishable from magic."
    – xDaizu
    Apr 18, 2018 at 13:45
8

The simplest way to think about composite video on the Apple is to imagine that vertical colored stripes of red, blue, and green, and yellow are overlaid onto the picture. These strips are half a pixel wide in hi-res mode, and 1/14 pixel wide in lo-res mode. If memory serves, when the upper bit of a byte is clear, half the pixels will hit the red and blue sections (appearing purplish), and half will hit the green and yellow sections (appearing greenish). If the upper bit is set, all pixels get shifted to the right half a pixel, so half will hit blue and green, while half will hit yellow and red (appearing orange).

Most monitors have a circuit which amplifies the effect of these colors when the pattern repeats, but this is entirely under the control of the monitor, rather than the computer.

13
  • 1
    I've always had the follow-up question: what happens when a shifted byte abuts a non-shifted byte? Especially if a shifted byte ends in a 1, and is followed by a non-shifted byte that starts with a 0? I assume the 1 gets truncated as the decision is reload the shifter now or reload it in half a colour cycle?
    – Tommy
    Apr 17, 2018 at 15:01
  • @Tommy that is (a better phrased version of) my question. You could edit my post if you wanted
    – Lorraine
    Apr 17, 2018 at 15:06
  • 1
    @Tommy: I find it easier to think in terms of color stripes, since there are no fixed groups of four sub-pixels. A high-res screen can show black and white images at full resolution, provided that there are no white-black-white or black-white-black patterns anywhere.
    – supercat
    Apr 17, 2018 at 17:38
  • 1
    @supercat also, nerd attack!, many colour screens could show a black and white image at a decent resolution with tricksy programming because text mode doesn't output a colour burst. So if you raced the raster then each line you could toggle text mode on momentarily and then switch back to graphics, for graphics mode pixels with no colour burst. If there's no burst, a standards-compliant receiver should assume it's receiving a black and white broadcast and not process for colour.
    – Tommy
    Apr 17, 2018 at 17:49
  • 1
    @Tommy: Interestingly, I was just playing around this that last weekend at Midwest Gaming Classic. It seems that the particular monitor that happened to be there was sensitive to whether chroma was on more or less than about half the time, so unless one switched chroma on and off every scan line I don't think that trick would work. It's too bad Apple didn't tie chroma to the lores-enable soft switch to control chroma, while making the hi-res soft switch force pixel data to hi-res regardless of the lo-res control. That would have easily allowed hi-res color and hi-res B&W modes.
    – supercat
    Apr 17, 2018 at 17:55
7

No, not quite like that. Nobody pointed out to you that the pixel bits are stored in reverse order.

0 00 01 10 1 -> 0 1 01 10 00 = 58
1 1 00 01 10 -> 1 01 10 00 1 = B1

How can you see what it looks like? You could do it like this:

HGR
CALL-151
2000:58 B1 N 2002<2000.3FF6M

But then you'd see it doesn't look like what you expect, so I wrote you a little program. Twiddler:

8000:20 2F FB 20 B7 80 20 E2 F3 A9 00 A8 85 02 A9 20
8010:85 03 A2 20 A5 00 91 02 C8 A5 01 91 02 C8 D0 F4
8020:E6 03 CA D0 EF 86 24 A9 15 85 25 A5 00 20 92 80
8030:A5 01 20 92 80 AD 00 C0 10 FB 8D 10 C0 C9 9B D0
8040:03 4C 2F FB C9 89 B0 16 29 0F 0A AA F0 02 CA CA
8050:BD DD 80 85 00 E8 BD DD 80 85 01 4C 09 80 C9 A0
8060:D0 0B A5 00 A6 01 85 01 86 00 4C 09 80 C9 D9 B0
8070:C4 C9 B8 08 29 0F A8 90 01 88 A9 01 88 30 03 0A
8080:D0 FA 28 B0 06 45 00 85 00 90 04 45 01 85 01 4C
8090:09 80 85 04 A9 80 AA 25 04 F0 04 A9 B1 D0 02 A9
80A0:B0 20 ED FD 8A 4A D0 EE A9 A0 20 ED FD A5 04 20
80B0:DA FD A9 A0 4C ED FD A2 00 A0 17 BD C6 80 20 ED
80C0:FD E8 88 D0 F6 60 8D 8D B7 B6 B5 B4 B3 B2 B1 B0
80D0:A0 A0 A0 A0 C8 C7 C6 C5 C4 C3 C2 C1 8D 00 00 2A
80E0:55 55 2A 7F 7F 80 80 AA D5 D5 AA FF FF
0:58 B1
8000G

It edits the bytes in $00 and $01 and fills the hires screen with them. The bits are shown, and the keys to toggle the bits are printed above them. So 7 to 0 toggle the bits in byte 0, and H to A toggle the bits in byte 1. Ctrl-A to Ctrl-H set the bytes to hires colours 0 to 7, Space swaps the bytes, and Esc exits. Then if you like you can change $00 or $01 and restart with 8000G.

In AppleWin with NTSC emulation it looks like this:

Twiddler editing bytes 58 B1 in AppleWin

The main reason it doesn't look as you expect is because adjacent 1's produce white, but with fringes of colour at the edges or tints in short runs. In this case the high bit of byte 1 extends the white produced by the adjacent bit 6 in byte 0 and bit 1 in byte 1. Why is it extended? That is covered in some detail in James Sather's Understanding the Apple IIe. From page 8-33:

Interference Between Adjacent Delayed and Undelayed HIRES40 Patterns

The 7-dot, HIRES40 patterns fit snugly together if the adjacent patterns are all delayed or undelayed, but problems can be caused when they are mixed together. [...]

A delayed HIRES40 pattern extends the trailing dot or black space of a preceding undelayed pattern by half of a dot width. [...]

An undelayed HIRES pattern cuts off the trailing dot or black space of a preceding delayed pattern to half of a dot width.

The point of all this is that continuous undelayed or delayed patterns fit snugly together, but there is discontinuity between adjacent undelayed and delayed patterns.

Cutting off or extending a dot has the effect of slightly changing the dot pattern and, more noticeably, changing the coloring of the border dots. As a result, the HIRES programmer has one more thing that affects color to educate himself about and take into account. On the plus side, the programmer can draw vertical lines at pattern borders in eight colors that are not otherwise available in HIRES40 mode. He does this simply by turning on a right hand dot then extending or cutting it off via D7 of the following pattern. In some instances, no dots need be turned on in the following pattern.

Figure 8.14 (color section) is a photograph illustrating the generation of LORES colors at borders between delayed and undelayed 7-dot HIRES patterns.

The program which generated this display is listed in Figure 3.11. The mixed LORES/HIRES display is created by switching screen modes in a 8515-cycle loop as is discussed in an application note in Chapter 3. As the photo shows, any LORES color except dark blue-green (4) and dark magenta (1) can be produced at a limited number of screen positions.

Rather than posting the photo from Sather's book, I've taken Sather's code - which was on hand (with a screenshot - have a look) at the website of another Apple II emulator, Epple II - and run it in AppleWin to demonstrate that it's a genuine effect of NTSC decoding. It's the same behaviour that made bit-twiddling in double-hires so valuable. For example, just look at the fine detail in Airheart's graphics on an NTSC monitor or in an emulator that supports true NTSC emulation.

Sather's demo of LORES colors at borders between delayed and undelayed HIRES patterns, in AppleWin

In summary, it's not simple, and it's a lot more interesting than "nothing happens"!

3
  • Is there a hardware reason why the pixels are in reverse order? I imagine it makes for more awkward programming.
    – Lorraine
    Apr 23, 2018 at 7:50
  • That's an interesting question. I don't know for sure, but the best answer I can come up with is that lores and hires both use the same shift registers, and lores shifts out this way. The difference with hires is that bit 7 must not be shifted out, and the bit 7 delay (or not) feature to double the palette was only added in rev 1. It doesn't really complicate programming because when you need to turn a bit on programmatically shifting left or right is no harder. Likewise when you have shape data as bytes - either it's all preshifted or if done dynamically then left or right is no different. Apr 24, 2018 at 1:00
  • 2
    The reason is the shift registers go in opposite the “expected” direction is that Woz laid out the printed circuit board to minimize the amount of wire crossovers requiring extra plated feedthroughs (which can add to PCB costs).
    – hotpaw2
    May 8, 2018 at 1:17
5

The Apple II Reference Manual (Apple product number A2L0001A), published in 1979 by Apple Computer, Inc. for the Apple II and Apple II Plus computers, contains a few pages about the end-user data format of the high resolution graphics mode:

Each dot on the screen represents one bit from the picture buffer [a dedicated 8K region of memory]. Seven of the eight bits in each byte are displayed on the screen, with the remaining bit used to select the colors of the dots in that byte. Forty bytes are displayed on each line of the screen. The least significant bit (first bit) of the first byte in the line is displayed on the left edge of the screen, followed by the second bit, then the third, etc. The most significant (eighth) bit is not displayed. Then follows the first bit of the next byte, and so on. A total of 280 dots are displayed on each of the 192 lines of the screen.

On a black-and-white monitor or TV set, the dots whose corresponding bits are "on" (or equal to 1) appear white; the dots whose corresponding bits are "off" or (equal to 0) [sic] appear black. On a color monitor or TV, it is not so simple. If a bit is "off", its corresponding dot will always be black. If a bit is "on", however, its color will depend upon the position of that dot on the screen. If the dot is in the leftmost column on the screen, called "column 0", or in any even-numbered column, then it will appear violet. If the dot is in the rightmost column (column 279) or any odd-numbered column, then it will appear green. If two dots are placed side-by-side, they will both appear white. If the undisplayed bit of a byte is turned on, then the colors blue and red are substituted for violet and green, respectively. Thus, there are six colors available in the High Resolution Graphics mode, subject to the following limitations:

  1. Dots in even columns must be black, violet, or blue.

  2. Dots in odd columns must be black, green, or red.

  3. Each byte must be either a violet/green byte or a blue/red byte. It is not possible to mix green and blue, green and red, violet and blue, or violet and red in the same byte.

  4. Two colored dots side by side always appear white, even if they are in different bytes.

  5. On European-modified [PAL Region] Apples, these rules apply but the colors generated in the High-Resolution Graphics Mode may differ. (Apple Computer Inc. 19-20)

So, in summary, each pixel is 1 bit, and any color it has depends on its position, the state of the high bit, and any of its horizontal neighbors. Raffzahn's answer goes into great depth about how this was implemented by hacking the NTSC (or PAL) color format, which gives us the rules we see here.

2
  • @Wilson: Although monitors try to make things less blurry than this description would suggest, colored dots spread left and right into the adjoining columns, so that if a violet and green dot are placed adjacent to each other, they will overlap significantly. The hues of the violet and green are such that the places where they overlap visually appear as white, but there's no such thing as a "white" pixel.
    – supercat
    Apr 18, 2018 at 18:53
  • @Wilson: Perhaps by dots it's referring to single bit pixels i.e. with black on either side. In this case they can't be white. I disagree that there's no such thing as a white pixel though. With 2 adjacent 1 bits the picture signal stays high, so full luminance and no chrominance, though it really needs more than just 2 bits for the chrominance to settle down. Apr 21, 2018 at 13:11

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .