Routines for grayscale graphics
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.
short GrayCheckRunning (void); |
Checks whether grayscale mode is active.
GrayCheckRunning returns TRUE if grayscale mode is active, else returns FALSE.
Deprecated alias: IsGrayMode
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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.
#define GrayMode(x) ((x) ? GrayOn () : ({
|
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.
void GrayOff (void); |
Deactivates grayscale mode.
This function deactivates grayscale mode. If grayscale mode is not activated, this function does nothing.
See also: 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
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
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
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
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
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
#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
.
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.
enum GrayPlanes {LIGHT_PLANE = 0, DARK_PLANE = 1}; |
An enumeration to describe legal grayscale planes.