TICT TUTORIAL SERIES 1 - Part V (c) TI-Chess Team 2001
Grayscale Programming - Technical Basics

Focus Of This Tutorial

This tutorial will explain the technical basics behind the Grayscales implementation (4 grays) on the TI-89 and TI-92plus.

You will not find any line of code within this document, but I think it maybe useful to know how grayscales works in general for every programmer who want to get the max out of his code (knowing your enemy is the first step to beat him, right? :-).

How to trick the Calculator Hardware to display grayscales?

To make things easy I will explain the technics behind the grayscales first in the way they are implemented on HW1 calculators. The differences on HW2 calculators are explained later.

Each TI-89 or TI-92+ features an LCD-Controller which is capable of displaying monochrome images on the LCD of the calculator. The data which is displayed on the LCD is hold by a piece of memory called the "LCD Display Memory" (LCD-Memory). This memory is part of the normal RAM and each bit within this memory maps directly to a pixel of the LCD. If a bit is set, the corresponding pixel on the LCD is black (turned on), otherwise it is white (turned off). The location of the LCD-Memory within the RAM is normally constant, but fortunately, the LCD controller can be programmed so that almost each part of the RAM can be used for as LCD-Memory.

Programming the LCD controller to use another part of the RAM is quite simple: a program has only to write the start address of the new LCD-Memory to a specifc port. That's enough.

Now let us "jump into the details of grayscales (4 different levels). For 4 grays we need two planes (You can think of a plane as being an separate LCD-Memory). Let as call the first plane Plane1 and the second plane Plane2. Here are the different grayscales we can produce with two planes:

Sorrily the LCD controller doesn't support grayscales by its own. Now: how to trick the hardware to support grayscales? The TI-89/92+ contains a timer chip which regulary triggers some interrupts (if you don't know what interrupts are, read tutorial S1P2). One of this interrupts is Interrupt1 which handles normally just the keyboard input. Therefore it queries the keyboard in very short intervals to guess if the user has pressed or released a key. The interrupt is that fast that we can use it for our grayscale purposes, too. The trick is that we install a special interrupt handler for Interrupt1, which programs the LCD controller to use Plane1 as LCD-Memory and a short time later (a few interrupts later) re-program it to use Plane2 as LCD-Memory. By switching in this way very fast between the planes the eye of the user gets tricked so that it believes it sees grays where only a monochrome picture will be displayed at each time!

But there is another trick necessary to get 4 grays. If we display each plane as long as the other one, we only get WHITE,GRAY and BLACK pixels. The user will not see any difference between LIGHTGRAY and DARKGRAY. Therefore we display Plane1 (the dark plane) about 66 percent of the time and Plane2 only 34 percent of the time. Now we have 4 levels of gray and it works perfectly and flicker-free on the Hardware1 version of TI-89/92-plus.

What is the difference on Hardware2?

Users with Hardware2 calculators may notice that the grayscales flickers much on their calculators (at least depending when the battery state is low). Now: what causes that flickers on Hardware2?

As we seen above grayscales on Hardware1 calcs are simply implemented by switching force and back between two planes. The switching itself can be performed quite quickly, because its done by just writing a new address to a specific point (which can be performed in almost zero time).

On Hardware2 the situation has completely changed. TI has introduced a new LCD controller which comes with an own (internal) RAM, the LCD-Controller RAM. To switch between the two grayscale planes we cannot only reprogram the LCD-Controller to use another part of the normal RAM as LCD-Memory, but we have copy (!!!) the complete content of the new plane to the LCD-Controller RAM. Due to the fact that the LCD-Memory is 3840 Bytes large (even on a TI-89), this amount of memory has to be copied multiple times per second between the normal RAM and the LCD-Controller RAM. This copy operation is performed again by an own interrupt handler for Interrupt1.

Let's say we need for almost flicker-free animations around 15 complete frames displayed each second. Due to the fact that one grayscale frame consists of 2 planes each of size 3840 the interrupt handler has to copy 3840*2*15 = 115.200 bytes = 115kBytes each second between the normal RAM and the LCD-Controller RAM. If you compare this to writing 60 bytes (2*15*2=60) per second to the LCD-Controller port on HW1, it is obviously why the Hardware2 grayscales is not complete flicker-free.

There are some methods to improve the grayscales on Hardware2 calcs, but for now I think its enough that you know the basic mechanisms for HW1 and HW2 grayscales.

Double-Buffering and Grayscale (Idea for a new Approach)

Double-Buffering is a method where a program displays one picture while another one is generated in another (invisible) memory buffer. If the generation of the new picture is finished the program displays the new one and the previous displayed one becomes invisible. If the switching between the two pictures can be performed fast, you won't see any flickers at all.

Sorrily double buffering is not supported directly by the grayscale implementations available for the TI-89/92plus (at least for grayscale programs which uses the TIGCCLIB grayscales). So most programs which implement double buffering on the TIs use the following approach:

  1. display Plane1/Plane2 of Picture1 and generate Picture2 in another memory buffer (2 planes in size)
  2. copy complete content of the invisible memory buffer into the visible Plane1/Plane2
As you can see this is extremely inefficiently on both Hardware versions. For each generated picture around 8kB are copied around, which is NOT really necessary !!

Suppose you have a slightly modified grayscale implementation which supplies us with 4 planes. Okay, this will take up around 16kB of RAM instead of 8kB, but if a program wants to do double buffering it will use that much of memory in any case. Suppose further that there are two new functions:

  1. void SetDisplayedPair(short x)
  2. short GetDisplayedPair()

The first function takes as argument 0 or 1. If the argument is set to 0 the interrupt handler which is responsible for the grayscale switching uses Plane1 and Plane2 for display. If the argument is 1 it will use Plane3 and Plane4 for display. If the other routines are also modified to support all four planes (instead of only 2) we will be able to do Double-Buffering without the overhead of copying 8kB around each time.

If everything will go right, I will present you an implementation of this new approach for double buffering in one of the next tutorials. The upcoming implementation will be a replacement for the grayscale implementation of TIGCCLIB. Turning on the double buffering variant should be easily performable by just adding a "#define DOUBLEBUFFER_GRAYS" at the beginning of your program. We will see. Stay tuned!

... And The Credits go to:

Contact TI-Chess Team Members

Check the TICT HQ Website at http://tict.ticalc.org for more tutorials and software.

More useful tips, tricks and hints can also be found at our Messageboard at: http://pub26.ezboard.com/btichessteamhq.

Suggestions, bug reports and similar are VERY welcome (use our Messageboard for this!!).

How to Thanx the Author?

Like Xavier from the Doors Team I like it to get postcards from all over the world. If you want to thank me, just send me an postcard with greetings on it. Thats enough.

My address is:

Thomas Nussbaumer
Heinrichstrasse 112a
A-8010 Graz

... and please: no mail bombs if one of my programs had crashed your calculator!

Thomas Nussbaumer Graz,Austria 08/01/2001