The <gray.h> Header File

Routines for grayscale graphics

Functions

GrayAdjust
Adjusts grayscale support to make it flickerless.
GrayCheckRunning
Checks whether grayscale mode is active.
GrayDBufCleanup
Uninitializes grayscale double-buffering.
GrayDBufGetActiveIdx
Returns the index of the currently visible double buffer.
GrayDBufGetActivePlane
Returns a pointer to a specific plane of the currently visible buffer.
GrayDBufGetHiddenIdx
Returns the index of the currently invisible double buffer.
GrayDBufGetHiddenPlane
Returns a pointer to a specific plane of the currently hidden buffer.
GrayDBufGetPlane
Returns a pointer to a specific plane of a specific buffer.
GrayDBufInit
Initializes grayscale double-buffering mode.
GrayDBufSetActiveAMSPlane
Forces graphics routines to use selected plane of visible buffer.
GrayDBufSetActiveIdx
Sets the currently visible double buffer.
GrayDBufSetActiveIdxSync
Synchronizes and sets the currently visible double buffer.
GrayDBufSetAMSPlane
Forces graphics routines to use selected plane of a specific buffer.
GrayDBufSetHiddenAMSPlane
Forces graphics routines to use selected plane of hidden buffer.
GrayDBufToggle
Toggles the currently visible double buffer.
GrayDBufToggleSync
Synchronizes and toggles the currently visible double buffer.
GrayGetInt1Handler
Returns the interrupt handler executed by the grayscale algorithm.
GrayGetPlane
Gets the address of a grayscale plane.
GrayGetSwitchCount
Returns the current plane switch counter.
GrayGetVersionString
Returns the embedded grayscale support version string.
GrayMode
This function has become obsolete.
GrayOff
Deactivates grayscale mode.
GrayOn
Activates grayscale mode with four shades of gray.
GrayOnThrow
Activates grayscale mode, throwing an error if unsuccessful.
GraySetAMSPlane
Forces graphics routines to use selected plane.
GraySetInt1Handler
Sets the interrupt handler executed by the grayscale algorithm.
GraySetSwitchCount
Sets the current plane switch counter to a given value.
GrayWaitNSwitches
Waits for a given number of plane switches.

Constants

GRAYDBUFFER_SIZE
Specifies the necessary size of a user-allocated double-buffer.

Predefined Types

Bool
An enumeration to describe true or false values.
GrayModes
An enumeration to describe legal grayscale modes.
GrayPlanes
An enumeration to describe legal grayscale planes.
INT_HANDLER
A pointer to an interrupt handler.

See also: graph.h, sprites.h


GrayAdjust

void GrayAdjust (short adjustment);

Adjusts grayscale support to make it flickerless.

Note: This function has become more or less obsolete since the effect is hardly visible with the new HW2 grayscale support.

This function is introduced to improve grayscale support on HW2 calculators, i.e. to make it more flickerless (it can be used on HW1 calculators too, but HW1 grayscale support is usually satisfactorily flickerless by default). Namely, if the plane switching frequency is not well synchronized with the LCD refresh frequency, the flickering may be too ugly. Unfortunately, it is not possible to use hardwired values because this frequency drifts with the battery strength, so a value which is good if the batteries are full is not good if the batteries are worn out, and vice versa. So the only solution is to make the frequency ratio adjustable. This trick was used in Universal OS by Julien Muchembled to produce quite flickerless grayscale support on HW2 calculators (many thanks to him for telling to me about this). His grayscale support allows adjusting the LCD refresh frequency (by changing the logical height of the display) using the keys DIAMOND+LEFT/RIGHT.

This solution was a bit unflexible to me, so I decided to use slightly modified variant of Julien's method. I introduced the function GrayAdjust for fine adjusting of the grayscale quality. This function does exactly the same as pressing DIAMOND+LEFT/RIGHT in Universal OS, i.e. it adjusts the LCD refresh frequency. The default value for adjustment is 0, which means "factory settings". Values less than 0 increases and values greater than 0 decreases the LCD refresh frequency. Legal values for adjustment range from -28 to 127 on TI-89 and from 0 to 127 on TI-92+ (although only slight variations around 0 are meaningful, for example from -10 to +10). Note that values less than 0 are not allowed on the TI-92+, else strange things would happen with the screen (use macros from compat.h to check the calculator model).

So how would one use this function? You can put an option into your program which displays a grayscale picture, and ask the user to adjust the quality. Here is a simplified example (called "Adjust Grayscale") of the program which displays the full screen filled with dark gray, then allows adjusting the quality using the '+' and '-' keys (use 'ESC' for exit):

// Adjust grayscale quality using + and - keys.

#define USE_TI89              // Compile for TI-89
#define USE_TI92PLUS          // Compile for TI-92 Plus
#define USE_V200              // Compile for V200

#define OPTIMIZE_ROM_CALLS    // Use ROM Call Optimization
#define MIN_AMS 100           // Compile for AMS 1.00 or higher
#define SAVE_SCREEN           // Save/Restore LCD Contents

#include <tigcclib.h>         // Include All Header Files

// Main Function
void _main(void)
{
  int key, value = 0;
  if (!GrayOn ())
    return;
  GrayAdjust (value);
  memset (GetPlane (DARK_PLANE), 255, LCD_SIZE);  // Fill the dark plane and
  memset (GetPlane (LIGHT_PLANE), 0, LCD_SIZE);   //  clear the light plane
  while ((key=ngetchx ()) != KEY_ESC)
    {
      if (key== '+' && value < 127)
        value++;
      if (key== '-' && value > (TI89 ? -28 : 0))
        value--;
      GrayAdjust(value);
    }
  GrayOff ();
}

This program does not have to be embedded in your program: as the LCD refresh frequency is kept in the hardware register, it will still be valid after exiting from the program, so this example may be used as a standalone adjusting program. However, the factory settings are restored each time the calculator is turned on. If you embed the adjustment code in your program, it is not a bad idea to use the same adjustment key as used in Universal OS (DIAMOND+LEFT/RIGHT), due to conformance. These keys may be checked easily using pseudoconstants from the compat.h header file, as in

if (key == KEY_DIAMOND + KEY_LEFT) ...

Note: Changing adjustment also has influence to the lightness of the display, but you always can change the contrast the usual way. Increasing adjustment makes the display lighter, and decreasing it makes the display darker. Anyway, do not use this function for adjusting the display lightness. Its purpose is just to estabilish precise synchronization.


GrayCheckRunning

short GrayCheckRunning (void);

Checks whether grayscale mode is active.

GrayCheckRunning returns TRUE if grayscale mode is active, else returns FALSE.

Deprecated alias: IsGrayMode


GrayDBufCleanup

void GrayDBufCleanup (void);

Uninitializes grayscale double-buffering.

GrayDBufCleanup turns off double-buffering mode, which should have been previously turned on with GrayDBufInit. After calling this function, you can operate in grayscale just like before calling GrayDBufInit. You do not need to call this function explicitly before GrayOff, as GrayOff will perform the necessary uninitialization itself.

Note: GrayDBufCleanup does not free the buffer passed to GrayDBufInit. You need to do this yourself afterwards, i.e. after calling this function or GrayOff.

See also: GrayDBufInit, GrayOff


GrayDBufGetActiveIdx

short GrayDBufGetActiveIdx (void);

Returns the index of the currently visible double buffer.

GrayDBufGetActiveIdx returns the index of the visible double buffer. This index will be either 0 or 1, and can be passed to GrayDBufSetActiveIdx or GrayDBufGetPlane. Usually, you do not need to call this function directly.

See also: GrayDBufGetHiddenIdx, GrayDBufSetActiveIdx, GrayDBufGetActivePlane, GrayDBufSetActiveAMSPlane


GrayDBufGetActivePlane

void *GrayDBufGetActivePlane (short plane);

Returns a pointer to a specific plane of the currently visible buffer.

GrayDBufGetActivePlane acts like GrayGetPlane for the currently visible buffer (it calls GrayDBufGetPlane with the index returned from GrayDBufGetActiveIdx). Drawing into this buffer has (almost) direct effect on the contents of the screen.

See also: GrayDBufGetHiddenPlane, GrayGetPlane, GrayDBufGetPlane, GrayDBufToggle


GrayDBufGetHiddenIdx

short GrayDBufGetHiddenIdx (void);

Returns the index of the currently invisible double buffer.

GrayDBufGetHiddenIdx returns the index of the visible double buffer. This index will be either 0 or 1, and can be passed to GrayDBufSetActiveIdx or GrayDBufGetPlane. Usually, you do not need to call this function directly.

See also: GrayDBufGetActiveIdx, GrayDBufSetActiveIdx, GrayDBufGetHiddenPlane, GrayDBufSetHiddenAMSPlane


GrayDBufGetHiddenPlane

void *GrayDBufGetHiddenPlane (short plane);

Returns a pointer to a specific plane of the currently hidden buffer.

GrayDBufGetHiddenPlane acts like GrayGetPlane for the currently hidden buffer (it calls GrayDBufGetPlane with the index returned from GrayDBufGetActiveIdx). Drawing into this buffer has no effect on the contents of the screen until GrayDBufSetActiveIdx or GrayDBufToggle is called.

See also: GrayDBufGetActivePlane, GrayGetPlane, GrayDBufGetPlane, GrayDBufToggle


GrayDBufGetPlane

void *GrayDBufGetPlane (short idx, short plane);

Returns a pointer to a specific plane of a specific buffer.

GrayDBufGetPlane acts like GrayGetPlane, but it has an additional parameter idx which contains the index of the buffer for which the plane pointer should be returned. idx should be 0 or 1; it is usually a value returned from GrayDBufGetActiveIdx or GrayDBufGetHiddenIdx. Usually, you do not need to call this function explicitly; call GrayDBufGetActivePlane or GrayDBufGetHiddenPlane instead.

Drawing into the currently visible buffer has (almost) direct effect on the contents of the screen. Drawing into the hidden buffer has no effect until GrayDBufSetActiveIdx or GrayDBufToggle is called.

See also: GrayGetPlane, GrayDBufGetActivePlane, GrayDBufGetHiddenPlane, GrayDBufGetActiveIdx, GrayDBufGetHiddenIdx, GrayDBufSetActiveIdx


GrayDBufInit

void GrayDBufInit (void *buf);

Initializes grayscale double-buffering mode.

GrayDBufInit initializes double-buffering mode. In double-buffering mode, you can switch between two buffers (using GrayDBufToggle) very quickly; much more quickly than using memcpy to achieve double-buffering. This function assumes that GrayOn has been called and its result was successful. To have as little extra double-buffering code in the grayscale implementation as possible, you need to allocate your own buffer and pass it to this function. You can do this with malloc, for example, but be sure to check its result before proceeding. The necessary size of the buffer (in bytes) is specified by the constant GRAYDBUFFER_SIZE.

You do not explicitly need to call GrayDBufCleanup to deactivate double-buffering mode; GrayOff will do the necessary uninitialization. However, be sure to free the buffer after calling GrayDBufCleanup or GrayOff.

The contents of the current grayscale buffer become the new contents of the plane with index 0 (see GrayDBufGetActiveIdx). The plane with index 1 is initialized from the new buffer and is filled either with random contents or with zeroes, depending on the contents of buf. Do not attempt to change the contents of buf directly after calling GrayDBufInit.

Note: After calling this function, you should not use the standard grayscale functions GrayGetPlane and GraySetAMSPlane any more. Instead, use the double-buffering functions GrayDBufGetPlane and GrayDBufSetAMSPlane or related ones.

See also: GrayDBufCleanup, GrayDBufSetActiveIdx, GrayDBufToggle


GrayDBufSetActiveAMSPlane

void GrayDBufSetActiveAMSPlane (short plane);

Forces graphics routines to use selected plane of visible buffer.

GrayDBufSetActiveAMSPlane acts like GraySetAMSPlane for the currently visible buffer (it calls GrayDBufSetAMSPlane with the index returned from GrayDBufGetActiveIdx). Drawing into this buffer has (almost) direct effect on the contents of the screen.

See also: GrayDBufSetHiddenAMSPlane, GraySetAMSPlane, GrayDBufSetAMSPlane, GrayDBufToggle


GrayDBufSetActiveIdx

void GrayDBufSetActiveIdx (short idx);

Sets the currently visible double buffer.

GrayDBufSetActiveIdx sets the currently visible buffer to the one indexed by idx, which should be either 0 or 1. Afterwards, GrayDBufGetActiveIdx will return idx, and GrayDBufGetHiddenIdx will return the opposite. As the switch may happen during the time a plane is copied to the screen, it may be desirable to use GrayDBufSetActiveIdxSync instead.

See also: GrayDBufSetActiveIdxSync, GrayDBufToggle, GrayDBufToggleSync, GrayDBufGetActiveIdx, GrayDBufGetHiddenIdx


GrayDBufSetActiveIdxSync

void GrayDBufSetActiveIdxSync (short idx);

Synchronizes and sets the currently visible double buffer.

GrayDBufSetActiveIdxSync waits until the next plane switch occurs (using GrayWaitNSwitches), then calls GrayDBufSetActiveIdx. This way you can make sure that the switch is not performed during an update of the screen, which would cause unwanted distortion effects.

See also: GrayDBufSetActiveIdx, GrayDBufToggleSync, GrayDBufToggle, GrayDBufGetActiveIdx, GrayDBufGetHiddenIdx


GrayDBufSetAMSPlane

void GrayDBufSetAMSPlane (short idx, short plane);

Forces graphics routines to use selected plane of a specific buffer.

GrayDBufSetAMSPlane acts like GraySetAMSPlane, but it has an additional parameter idx which contains the index of the buffer for which the plane pointer should be returned. idx should be 0 or 1; it is usually a value returned from GrayDBufGetActiveIdx or GrayDBufGetHiddenIdx. Usually, you do not need to call this function explicitly; call GrayDBufSetActiveAMSPlane or GrayDBufSetHiddenAMSPlane instead.

Drawing into the currently visible buffer has (almost) direct effect on the contents of the screen. Drawing into the hidden buffer has no effect until GrayDBufSetActiveIdx or GrayDBufToggle is called.

See also: GraySetAMSPlane, GrayDBufSetActiveAMSPlane, GrayDBufSetHiddenAMSPlane, GrayDBufGetActiveIdx, GrayDBufGetHiddenIdx, GrayDBufSetActiveIdx


GrayDBufSetHiddenAMSPlane

void GrayDBufSetHiddenAMSPlane (short plane);

Forces graphics routines to use selected plane of hidden buffer.

GrayDBufSetHiddenAMSPlane acts like GraySetAMSPlane for the currently hidden buffer (it calls GrayDBufSetAMSPlane with the index returned from GrayDBufGetHiddenIdx). Drawing into this buffer has no effect on the contents of the screen until GrayDBufSetActiveIdx or GrayDBufToggle is called.

See also: GrayDBufSetActiveAMSPlane, GraySetAMSPlane, GrayDBufSetAMSPlane, GrayDBufToggle


GrayDBufToggle

void GrayDBufToggle (void);

Toggles the currently visible double buffer.

GrayDBufToggle sets the currently visible buffer to the one which was previously hidden. Afterwards, the return values of GrayDBufGetActiveIdx and GrayDBufGetHiddenIdx will be the exact opposite as before. As the switch may happen during the time a plane is copied to the screen, it may be desirable to use GrayDBufToggleSync instead.

See also: GrayDBufToggleSync, GrayDBufSetActiveIdx, GrayDBufSetActiveIdxSync


GrayDBufToggleSync

void GrayDBufToggleSync (void);

Synchronizes and toggles the currently visible double buffer.

GrayDBufToggleSync waits until the next plane switch occurs (using GrayWaitNSwitches) and calls GrayDBufToggle. Due to grayscale routine implementation differences, the order of the synchronizing and switching depends on the hardware version. On HW1, the toggles are effective only after a plane switch, so GrayDBufToggleSync toggles first and waits for synchronization afterwards so as to keep you from writing into the "hidden" planes before they are actually hidden. On HW2 and higher, GrayDBufToggleSync waits for synchronization before toggling. This way you can make sure that the switch is not performed during an update of the screen, which would cause unwanted distortion effects.

See also: GrayDBufToggle, GrayDBufSetActiveIdxSync, GrayDBufSetActiveIdx


GrayGetInt1Handler

INT_HANDLER GrayGetInt1Handler (void);

Returns the interrupt handler executed by the grayscale algorithm.

GrayGetInt1Handler returns the interrupt handler which is called internally by the grayscale support. Use this function to store the interrupt temporarily when you are using GraySetInt1Handler to change it.

The interrupt handler called by the grayscale routines looks something like this (pseudo-code):

DEFINE_INT_HANDLER (GrayInt1Handler)
{
  SwitchPlanes ();
  ExecuteHandler (OldInt1);
}

where OldInt1 is the previous AUTO_INT_1 handler. GrayGetInt1Handler returns the value of OldInt1. Note that this is just a C-style declaration of the AUTO_INT_1 handler for grayscale; the actual one is implemented in assembly.

Deprecated alias: GetGrayInt1Handler

See also: GraySetInt1Handler, intr.h


GrayGetPlane

void *GrayGetPlane (short plane);

Gets the address of a grayscale plane.

GrayGetPlane returns a pointer to the grayscale plane plane. Valid values for plane are LIGHT_PLANE and DARK_PLANE. To draw in black, draw in both planes.

Note: Do not assume that any plane is on 0x4C00 when in grayscale mode, due to hardware version 2 support. Also do not assume that the 2 grayscale planes are consecutive, this is not the case on hardware version 1.

Deprecated alias: GetPlane


GrayGetSwitchCount

unsigned long GrayGetSwitchCount (void);

Returns the current plane switch counter.

Antediluvian versions of grayscale support were lacking a mechanism to synchronize to the internal switching of the grayscales planes, which is quite necessary for almost any kind of "high-speed" games where the grayscale graphics change a lot.

This is why Thomas Nussbaumer implemented a plane switch counter which is increased after every processed plane switch.

A complete grayscale frame consists of 2 plane switches. GrayOn resets the switch counter value to 0.

A program which wants to synchronize to the plane switching within a loop can do this like this, for example:

unsigned long cur_count = GrayGetSwitchCount ();
do {
  unsigned long wait_for = cur_count + 2;
  while ((cur_count = GrayGetSwitchCount ()) < wait_for);
  // We'll always come here after the same plane
  // (dark plane or light plane) was switched.
  
  // Do something here ...
  
} while (some_condition);

Deprecated alias: GetGraySwitchCount

See also: GraySetSwitchCount


GrayGetVersionString

const char *GrayGetVersionString (void);

Returns the embedded grayscale support version string.

GrayGetVersionString returns the current version of the grayscale support in human-readable form. The version string is embedded in the program only if this macro is used at least once.


GrayMode

#define GrayMode(x) ((x) ? GrayOn () : ({
GrayOff ();
(short) 1;
}))

This function has become obsolete.

This function has become obsolete. You should use GrayOn and GrayOff now. GrayMode is implemented as a macro which calls one of these two functions; if mode is a constant, the compiler will optimize the code into a single function call.


GrayOff

void GrayOff (void);

Deactivates grayscale mode.

This function deactivates grayscale mode. If grayscale mode is not activated, this function does nothing.

See also: GrayOn


GrayOn

short GrayOn (void);

Activates grayscale mode with four shades of gray.

GrayOn activates grayscale mode. This works on both hardware version 1 and 2 calculators because the calculator type is detected automatically. See GrayAdjust for information on how to reduce flickering on HW2 calculators as much as possible.

The GrayMode function as well as the constants defined in the enum GrayModes still exist to maintain backwards compatibility with very old programs. In fact, GrayMode is now defined as a macro which optimizes into a call to GrayOn or GrayOff if you call it with a constant value.

GrayOn returns FALSE if there was an error in switching to grayscale mode, otherwise it returns TRUE. Don't forget to switch off grayscale mode before your program terminates, or your TI will crash very soon!

Here is an example of a program for testing grayscale mode (called "Gray Test Project"), which displays 3 squares on the screen, each with a different level of gray (see other functions from this header file and from the graph.h header file for an explanation about how it works):

// Grayscale test program for TIGCC

#define USE_TI89
#define USE_TI92PLUS
#define USE_V200

#define MIN_AMS 100
#define SAVE_SCREEN

#include <tigcclib.h>

void _main(void)
{
  if (!GrayOn ())
    return;
  GraySetAMSPlane (LIGHT_PLANE);
  ClrScr ();
  ScrRectFill (&(SCR_RECT){{20,20,40,40}}, ScrRect, A_NORMAL);
  ScrRectFill (&(SCR_RECT){{80,20,100,40}}, ScrRect, A_NORMAL);
  GraySetAMSPlane (DARK_PLANE);
  ClrScr ();
  ScrRectFill (&(SCR_RECT){{50,20,70,40}}, ScrRect, A_NORMAL);
  ScrRectFill (&(SCR_RECT){{80,20,100,40}}, ScrRect, A_NORMAL);
  ngetchx ();
  GrayOff ();
}

Starting from release 2.2 of the library, it is safe to call GrayOn even if grayscale mode is already on, and to call GrayOff even if grayscale mode is already off.

See also: GrayOff, GrayOnThrow


GrayOnThrow

void GrayOnThrow (void);

Activates grayscale mode, throwing an error if unsuccessful.

GrayOnThrow works like GrayOn, except that it throws an error if it could not turn on grayscale successfully.

See also: GrayOn, GrayOff, error.h


GraySetAMSPlane

void GraySetAMSPlane (short plane);

Forces graphics routines to use selected plane.

GraySetAMSPlane forces all graphics routines (from graph.h) to draw into the grayscale plane plane (valid values are LIGHT_PLANE and DARK_PLANE). This way you can use standard routines for drawing lines, circles, etc. in grayscale mode as well. In fact, GraySetAMSPlane is a macro which calls GrayGetPlane and PortSet.

Deprecated alias: SetPlane


GraySetInt1Handler

void GraySetInt1Handler (INT_HANDLER handler);

Sets the interrupt handler executed by the grayscale algorithm.

GraySetInt1Handler sets the interrupt handler which is called internally by the grayscale support to handler. Using this function, you can redirect this interrupt even in grayscale mode.

The interrupt handler called by the grayscale routines looks something like this (pseudo-code):

DEFINE_INT_HANDLER (GrayInt1Handler)
{
  SwitchPlanes ();
  ExecuteHandler (OldInt1);
}

where OldInt1 is the previous AUTO_INT_1 handler. GraySetInt1Handler sets the value of OldInt1 to handler. Note that this is just a C-style declaration of the AUTO_INT_1 handler for grayscale; the actual one is implemented in assembly.

Note: Always reset the handler to the previous value (returned by GrayGetInt1Handler) before turning off grayscale, otherwise it will be installed as a permanent interrupt handler.

Deprecated alias: SetGrayInt1Handler

See also: GrayGetInt1Handler, intr.h


GraySetSwitchCount

void GraySetSwitchCount (unsigned long val);

Sets the current plane switch counter to a given value.

See GrayGetSwitchCount for information on the switch counter.

Deprecated alias: SetGraySwitchCount

See also: GrayGetSwitchCount


GrayWaitNSwitches

void GrayWaitNSwitches (short wait);

Waits for a given number of plane switches.

GrayWaitNSwitches waits for wait plane switches to happen before returning. If wait is 1, the function waits until the next plane switch occurs, so you can synchronize your program to the grayscale plane switches. Since the switches happen periodically, you can be pretty sure that no switch will occur too soon after you call this function. Using GrayGetSwitchCount and GraySetSwitchCount, you can also choose exactly after which plane the function returns.

See also: GrayGetSwitchCount


GRAYDBUFFER_SIZE

#define GRAYDBUFFER_SIZE 7688

Specifies the necessary size of a user-allocated double-buffer.

As GrayDBufInit requires the user to pass a user-allocated buffer as a parameter, this constant has been introduced to specify the size of this buffer. Note that it is not exactly 2*LCD_SIZE.


GrayModes

enum GrayModes {GRAY_OFF = 0, GRAY_ON = 1, GRAY_HW1 = 1, GRAY_HW2 = 1};

An enumeration to describe legal grayscale modes.

This type has become obsolete upon the introduction of GrayOn and GrayOff.


GrayPlanes

enum GrayPlanes {LIGHT_PLANE = 0, DARK_PLANE = 1};

An enumeration to describe legal grayscale planes.


Return to the main index