PowerBook reverse engineering for fun and no profit

(Drafts don't always save on here...I don't know if there's a maximum size or what, but I guess now this post is extra-refined.)

With Omaha2 complete, I moved on to capturing signals from GSC, starting in passive-matrix mode because I can use my singular display panel at the same time I'm running the logic analyzer. I made a few test programs that call SetDepth (to simulate choosing a color depth in the Control Strip) and write to the framebuffer in various repeating patterns. For anyone doing this in the future, make sure you have the system set to 32-bit addressing, or the OS will step in and rewrite the framebuffer with parts of windows while your program is running. The first, most notable thing about GSC is that it formats its output quite differently from passive-matrix mode Omaha2 (this is in 4-grey mode):
1781659454279.png
Instead of a continuous stream of byte clocks (CL2) with line clocks (CL1) at intervals, bursts of CL2 pulses are evenly spaced and ended with CL1 pulses, with just...nothing between them. FLM_F is also much longer, starting a few cycles into a block of CL2 pulses and lasting until the end of that block, and M_F is always high, instead of alternating.

I noticed that the first bits on D7-D4 of the line synchronous with FLM repeat every 4 lines. There are still 32000 CL2 clocks per frame, and 160 per line, they're just spaced differently. In hindsight, the explanation for this should have been obvious, but I didn't put the pieces together. In fact, I got so stumped that I went and Googled the screens.

If you Google for the Sharp LM64P51 or LM64P58, you get absolutely nothing except vintage Apple threads. If you Google for the Hitachi LMG5200, you get one result. From this result I found some leads that took me to lots of other Hitachi and Sharp monochrome passive matrix displays, all 320x240 or 640x480. The most enlightening of these was the Sharp LM64P839, whose datasheet I'm attaching here for the curious. From this datasheet we glean a lot of information: not only was I correct about the data format, but the dual nibble bus is actually a de facto standard! These are dual-scan passive-matrix displays, which means that two parts of the display are scanned at one time, just as I discovered. Here's the block diagram from the datasheet:

1781659961665.png
The data bits are called DU0-DU3 and DL0-DL3, for "display upper" and "display lower". The M signal is also internally generated (which explains why GSC doesn't send it), and is apparently used to prevent DC voltage from building up inside the liquid crystal and causing damage. Sharp calls the row drivers "COM" and the column drivers "SEG", which explains why the silkscreen on my LM64P58 says "STAB" and "CTAB" (though I don't know what "TAB" is for). Presumably GSC does send M when it's in active-matrix mode? We'll find out.

We also learn that FLM, which Sharp calls "S" but Hitachi calls "FLM", stands for "first line marker". The waveform recommended is:

1781660295902.png
and this exactly matches what we see. This waveform specification is identical between different manufacturers and different models, which explains why all the display revisions are electrically compatible.

At this point I realized what I was seeing on the logic analyzer. GSC is dithering the display by alternating pixel states between frames, so that the state of each greyscale pixel averages out to somewhere between on and off. We can confirm this by looking at some pixel data from a program called GreyScreen4GreyOneColor, which sets the whole screen to the byte 0x5f = 0b01_01_11_11, or two pixels light grey and then two black. With this, we see these values sent for the first 4 pixels of four consecutive frames:
D71000
D60010
D51111
D41111

This is obviously a dithered signal. For dark grey (0b10), two out of four frames are 1 and the others are zero. Clearly GSC's dithering algorithm has some complexity, because not only are adjacent pixels' dithering offset from one another, even when they're the same color, but it seems to intentionally not use some on-off combinations (3/4 frames set to 1 just isn't used, in this case).

GSC expands this for 16-grey mode by dithering across a 16-frame interval. It behaves the same otherwise. In each of the three color depths GSC supports, it changes the frequency and timing of the signals it generates. 4-color mode and 16-color mode behave the same as 4-grey and 16-grey mode, respectively. The display datasheets I found recommend certain frame clock frequencies for ideal monochrome results (minimal flicker, highest dynamic range, etc), so presumably GSC is using what Apple determined as the best timing for each mode. Here's the specs, measured on my oscilloscope:

Black and white: 61.0094 Hz FLM, 12.2019 kHz CL1
4 grey: 87.0400 Hz FLM, 17.4080 kHz CL1
16 grey: 119.415 Hz FLM, 23.8829 kHz CL1

I actually think that black and white mode looks a little flickery, but that might just be me. The CL1 frequency determines the amount of dead time between blocks of CL2 pulses, and in 16-grey mode there's almost no time between them. The period of a CL2 pulse is independent of the selected pixel depth, and one block of CL2 pulses is always 40.80 us long.

GSC also has registers, located at 0x50020000 in 32-bit mode, which is new since Omaha and Omaha2 were fully autonomous. These can be read by the CPU and mirror every 32 bytes. One byte near the end appears to be some kind of counter, and a few others change depending on the display mode selected. Other than that I have no idea what they do.
 

Attachments

At this point I realized what I was seeing on the logic analyzer. GSC is dithering the display by alternating pixel states between frames, so that the state of each greyscale pixel averages out to somewhere between on and off.
Makes sense - so greyscale is not really a ‘feature’ of these panels, it’s just how it’s being driven by GSC.

Does the active matrix panel also use dithering to generate greys? I’m guessing not, but…
 
GSC is a relabeled Chips & Technologies 65210, incidentally. Unfortunately no datasheets seem to exist online, although the System 7.1 source tree at least has register descriptions.
 
It's listed as a standard part in one of their databooks, but I've only seen Apple use it.
Interesting. If it was used in any other laptops, then I haven't seen one yet while making my database which currently has specs for 2500 models. Probably because a basic framebuffer chip that only supported grayscale displays was rather primitive by 1992 PC laptop standards? I wouldn't be surprised if it showed up in embedded/industrial applications though.

For reference, the typical C&T chip found in PC laptops released in 1992 was the 65530.
 
GSC in active-matrix mode is where everything comes together. To start with, GSC in active-matrix mode uses a similar waveform to GSC in passive-matrix mode:
1781897657425.png

The difference is that GSC in active-matrix mode does not change the shape or timing of its output signals depending on the color depth. In addition, it always sends 320 bytes per line, for 400 lines between FLM pulses. 400*320 = 128000 bytes, aka the size of the framebuffer when GSC is in 16-grey mode.

From the assumption that the data format is the same---8 bits map linearly to pixels, no dual scan---I first tested a screen in black and white. GreyScreenBWVertStripes sets the system to black & white mode, then sets 3 bytes black and 1 byte white, then repeats across the whole screen. With this, we see 12 CL2 cycles (bytes in active-matrix mode) high on the data bus, then 4 low. On Omaha2, this would equate to 12*8 = 96 pixels black and 4*8 = 32 pixels white, so obviously something is different. Since 320 bytes is 4 times 80 bytes, and 12*8/4 = 24 pixels, and 4*8/4 = 8 pixels, I figured I could assume that GSC sends each pixel as 4 consecutive bits instead of dithering 1-bit screens as it does in passive-matrix mode, and that the display understands greyscale video.

This turns out to be correct, but there is some complexity. The next program, GreyScreen4GreyVertStripes, sets the first 4 pixels to zero, the next 4 to 0b01, the 4 after that to 0b10, and the final four to 0b11, then repeats. With this, we see data that reconstructs to (8 bits at a time, line breaks every 16 bits):
0000000000000000
0100010001000100
1000100010001000
1110111011101110

Clearly GSC is sending data as 4 bits per pixel, which the display presumably understands as a greyscale framebuffer. However, GSC is also clearly adding some dithering or some variety of noise to the signal, since these don't map 1:1 to the gradient programmed to display on the screen. We'd like to see "01010101 01010101" in the second row, "10101010 10101010" in the third, and "11111111 11111111" in the fourth.

The final test is in 16-grey mode. GreyScreen16GreyVertStripes makes vertical columns from white to black at all 16 grey levels before repeating, just like 4GreyVertStripes. With this, we see that D7 is low for 8 CL2 cycles, then high for 8, D6 is low for 4 and high for 4, D5 is low for 2 and high for 2, and D4 is always low. If that's not almost a binary counter, I don't know what is. The fact that D4 is always low, however, means that GSC is purposefully not using the full dynamic range of the display as offered, which is reflected in the fact that sometimes D7 is low for 7 cycles and then high for 9, sometimes 8, D6 is low for 3 and then high for 4, sometimes 5, and D5 is low for 1 and high for 2, sometimes 3. The data at the start of the FLM line exhibits the same kind of repetition we saw in passive-matrix mode. This means that instead of sending straight greyscale data to the active-matrix panel, GSC is using a selected amount of the panel's available 16-bit range and dithering to get the rest of the way. Like in passive-matrix mode, I don't know what the dithering algorithm is.

Makes sense - so greyscale is not really a ‘feature’ of these panels, it’s just how it’s being driven by GSC.

Does the active matrix panel also use dithering to generate greys? I’m guessing not, but…
Evidently kind of. I wonder if C&T or Apple decided that the panel's greyscale wasn't good enough, or something, and decided to implement dithering in silicon in parallel. (How good is the greyscale on the active-matrix displays compared to passive-matrix? On my passive-matrix, 16GreyVertStripes looks more like vertical noise than a pleasant gradient---the white end has almost the same color from pixels 0 to 7, and the last two near-black and black columns look the same. I also noticed in passive-matrix mode that alternating columns of some colors were different brightnesses when the screen was displaying gradient rows, with nothing to cause horizontal/vertical shadowing.)

Interestingly, GSC cranks its clock speeds way up in active-matrix mode:
CL2: 8952.71 kHz
CL1: 27.9772 kHz
FLM: 87.013 Hz

CL2 was fast enough that my pitiful little 24 MHz Saelae clone couldn't keep up, and I was seeing about 170 cycles per line, which definitely doesn't make any sense. I used a Pi Pico 2 with sigrok-pico, which is fine except that its sample buffer is too small to capture a whole frame.

On a related note, Sharp recommends 85 Hz for best results on the passive-matrix LM64P839, and in 16-grey mode, GSC is pushing the screen near its limits:
1781912299403.png
I do think that 4-grey mode on GSC with a passive-matrix mode looks the best, and that runs at 87 Hz, so maybe they tuned for that. I have no idea about the active-matrix display, but I'm sure that was tuned as well.
 
Back
Top