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?